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
18 raise RuntimeError( 'Invalid arguments.' )
19
21 '''Class that encapsulates some Rigol DS1052E functionality.'''
23 self.obj = lib.Rigol_DS1000_new( c_int(0) )
24
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
34 """Test for an error having occurred."""
35 return( lib.Rigol_DS1000_error( self.obj ) )
36
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
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
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
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
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
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
164 (nch1, nch2, nmax) = o.acquire(1,1)
165 print nch1, nch2, nmax
166 if( nch1 > 0 ):
167
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
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
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