Module RigolDSWrap
[hide private]
[frames] | no frames]

Source Code for Module RigolDSWrap

  1  '''RigolDSWrap.py - A Python Interface to Rigol DS1000 Oscilloscope Library. 
  2     ========================================================================= 
  3   
  4     Acquire and read waveform data from a Rigol DS1052E (all DS 1000 series perhaps) 
  5     oscilloscope. Provide basic mechanisms for issuing all other commands and 
  6     queries. 
  7   
  8     Author: Nick Glazzard 2013''' 
  9   
 10  from ctypes import * 
 11  from numpy import * 
 12  from time import sleep 
 13  import matplotlib.pyplot as plt 
 14   
 15  lib = cdll.LoadLibrary( './rigol_ds1000.so' ) 
 16   
17 -def argerr():
18 raise RuntimeError( 'Invalid arguments.' )
19
20 -class RigolDS(object):
21 '''Class that encapsulates some Rigol DS1052E functionality.'''
22 - def __init__(self):
23 self.obj = lib.Rigol_DS1000_new( c_int(0) )
24
25 - def callerr(self):
26 """If a call fails, get an explanatory error message and show it.""" 27 lib.Rigol_DS1000_error_string.restype = c_char_p 28 cp = lib.Rigol_DS1000_error_string( self.obj ) 29 if( not ( cp is None ) ): 30 print 'Error: ' + cp 31 raise RuntimeError( 'rigol_ds1000 call failed.' )
32
33 - def error(self):
34 """Test for an error having occurred.""" 35 return( lib.Rigol_DS1000_error( self.obj ) )
36
37 - def get_error_info(self):
38 """Get an error message.""" 39 lib.Rigol_DS1000_error_string.restype = c_char_p 40 return( lib.Rigol_DS1000_error_string( self.obj ) )
41
42 - def command(self,c):
43 """Send a low level command to the oscilloscope. 44 All commands begin with a colon.""" 45 if( not lib.Rigol_DS1000_command( self.obj, c_char_p(c) ) ): 46 self.callerr() 47 return( 1 )
48
49 - def multi_command(self,c):
50 """Send multiple commands, separated by semi-colons, to the oscilloscope.""" 51 if( not lib.Rigol_DS1000_command( self.obj, c_char_p(c) ) ): 52 self.callerr() 53 return( 1 )
54
55 - def query(self,q):
56 """Send a query command to the oscilloscope and get its response 57 back as a string. Query commands should contain a ?""" 58 lib.Rigol_DS1000_string_query.restype = c_char_p 59 cp = lib.Rigol_DS1000_string_query(self.obj,c_char_p(q)) 60 if( cp is None ): 61 self.callerr() 62 return( cp )
63
64 - def acquire(self,stopfirst,localafter):
65 """Acquire waveform data from the oscilloscope. 66 Return the number of data points available in each channel. 67 Return 0 if a channel is not available. 68 Also return the maximum of the two channels sample counts. 69 If stopfirst is not 0, stop sampling before acquiring, then restart. 70 If localafter is not 0, leave remote mode after acquisition. 71 This function does the hard work of getting the measurements. 72 Return tuple (chan1_samples, chan2_samples, maximum_samples)""" 73 chan1_samples = c_int(0) 74 chan2_samples = c_int(0) 75 max_samples = c_int(0) 76 if( not lib.Rigol_DS1000_acquire_channels( self.obj,byref(chan1_samples), byref(chan2_samples), 77 byref(max_samples),c_int(stopfirst),c_int(localafter) ) ): 78 self.callerr() 79 return( chan1_samples.value, chan2_samples.value, max_samples.value )
80
81 - def read_channel(self,channel,samples):
82 """Return samples number of waveform samples for channel (1 or 2). 83 The sampling rate (time between samples), horizontal (time) and vertical (voltage) 84 offsets are also returned. 85 Return tuple (number_of_samples, sample_data, sample_rate, hoffset, voffset)""" 86 sample_buffer = (c_float*samples)() 87 deltat = c_float(0.0) 88 hoffset = c_float(0.0) 89 voffset = c_float(0.0) 90 read_samples = lib.Rigol_DS1000_get_channel(self.obj,c_int(channel),byref(sample_buffer), 91 byref(deltat),byref(hoffset),byref(voffset) ) 92 if( read_samples == 0 ): 93 self.callerr() 94 data = fromiter( sample_buffer, dtype=float, count=read_samples ) 95 return( read_samples, data, deltat.value, hoffset.value, voffset.value )
96
97 -def fourier_spectrum( nsamples, data, deltat, logdb, power, rms ):
98 """Given nsamples of real voltage data spaced deltat seconds apart, find the spectrum of the 99 data (its frequency components). If logdb, return in dBV, otherwise linear volts. 100 If power, return the power spectrum, otherwise the amplitude spectrum. 101 If rms, use RMS volts, otherwise use peak-peak volts. 102 Also return the number of frequency samples, the frequency sample spacing and maximum frequency. 103 Note: The results from this agree pretty much with my HP 3582A FFT Spectrum Analyzer, 104 although that has higher dynamic range than the 8 bit scope.""" 105 data_freq = fft.rfft(data * hanning( nsamples )) 106 nfreqs = data_freq.size 107 data_freq = data_freq / nfreqs 108 ascale = 4 109 if( rms ): 110 ascale = ascale / ( 2 * sqrt(2) ) 111 if( power ): 112 spectrum = ( ascale * absolute(data_freq) )**2 113 if( logdb ): 114 spectrum = 10.0 * log10( spectrum ) 115 else: 116 spectrum = ascale * absolute(data_freq) 117 if( logdb ): 118 spectrum = 20.0 * log10( spectrum ) 119 freq_step = 1.0 / (deltat * 2 * nfreqs); 120 max_freq = nfreqs * freq_step 121 return( nfreqs, freq_step, max_freq, spectrum )
122
123 -def time_plot( nsamples, data, deltat, hoff, title=None ):
124 """Create a simple amplitude versus time plot using Matplotlib.""" 125 times = arange( hoff, hoff+deltat*(nsamples-1), deltat ) 126 plt.plot( times, data[0:nsamples-1] ) 127 if( title == None ): 128 plt.title( 'Time domain data. Volts.' ) 129 else: 130 plt.title( title ) 131 plt.xlabel( 'Time (s)' ) 132 plt.ylabel( 'Volts' ) 133 plt.grid( True ) 134 plt.show()
135
136 -def freq_plot( nfreqs, spectrum, freq_step, max_freq, 137 title=None, logdb=False, 138 fmin=0.0, fmax=10000.0, ylo=-60.0, yhi=0.0 ):
139 """Create an amplitude versus frequncy plot for data analysed with fourier_spectrum().""" 140 freqs = arange( 0, max_freq, freq_step ) 141 plt.xlim( fmin, fmax ) 142 plt.plot( freqs, spectrum ) 143 if( title == None ): 144 if( logdb ): 145 plt.title( 'Frequency domain. Log RMS Volts.' ) 146 else: 147 plt.title( 'Frequency domain. Linear RMS Volts.' ) 148 else: 149 plt.title( title ) 150 plt.xlabel( 'Freq (Hz)' ) 151 if( logdb ): 152 plt.ylabel( 'dBV RMS' ) 153 plt.ylim( -60, 0 ) 154 else: 155 plt.ylabel( 'Volts RMS' ) 156 plt.grid( True ) 157 plt.show()
158 159 if __name__ == '__main__': 160 o = RigolDS() 161 print o.query( '*IDN?' ) 162 o.command( ':TIM:SCAL 0.0005' ) 163 #o.command( ':ACQ:MEMD NORM' ) 164 (nch1, nch2, nmax) = o.acquire(1,1) 165 print nch1, nch2, nmax 166 if( nch1 > 0 ): 167 # Read data and plot it. 168 (nsamples, data, deltat, hoff, voff ) = o.read_channel(1,nch1) 169 print "Time domain data. Volts." 170 print "samples", nsamples, "time step", deltat, "H offset", hoff, "V offset", voff 171 time_plot( nsamples, data, deltat, hoff, 'Channel 1 Time Data' ) 172 # Amplitude spectrum and plot it. 173 print "Frequency domain. Linear RMS Volts." 174 ( nfreqs, freq_step, max_freq, spectrum ) = \ 175 fourier_spectrum( nsamples, data, deltat, False, False, True ) 176 print "Freq step", freq_step, "Max freq", max_freq, "Freq bins", nfreqs 177 freq_plot( nfreqs, spectrum, freq_step, max_freq ) 178 # Log amplitude spectrum and plot it. 179 print "Frequency domain. Log RMS Volts." 180 ( nfreqs, freq_step, max_freq, spectrum ) = \ 181 fourier_spectrum( nsamples, data, deltat, True, False, True ) 182 freq_plot( nfreqs, spectrum, freq_step, max_freq, None, True ) 183 if( nch2 > 0 ): 184 (nsamples, data, deltat, hoff, voff ) = o.read_channel(2,nch2) 185 print nsamples, deltat, hoff, voff 186 time_plot( nsamples, data, deltat, hoff, 'Channel 2 Time Data' ) 187