|
@@ -10,26 +10,30 @@ import os
|
10
|
10
|
import platform
|
11
|
11
|
import logging
|
12
|
12
|
|
13
|
|
-__version__ = 'v1.0.0'
|
|
13
|
+__version__ = 'v1.1.0'
|
|
14
|
+# Added TMC Blockheader decoding
|
|
15
|
+# Added possibility to manually allow run for scopes other then DS1000Z
|
14
|
16
|
__author__ = 'RoGeorge'
|
15
|
17
|
|
16
|
18
|
#
|
17
|
|
-# TODO: Replace the fixed delay between commands with *OPC? (Operation Complete) query
|
18
|
|
-# TODO: Add debug mode
|
19
|
|
-# TODO: Add debug switch
|
20
|
|
-# TODO: Add Python and modules version
|
21
|
|
-# TODO: Add script version
|
|
19
|
+# TODO: Write all SCPI commands in their short name, with capitals
|
|
20
|
+# TODO: Add ignore instrument model switch instead of asking
|
|
21
|
+#
|
|
22
|
+# TODO: Detect if the scope is in RUN or in STOP mode (looking at the length of data extracted)
|
|
23
|
+# TODO: Add logic for 1200/mdep points to avoid displaying the 'Invalid Input!' message
|
22
|
24
|
# TODO: Add message for csv data points: mdep (all) or 1200 (screen), depending on RUN/STOP state, MATH and WAV:MODE
|
|
25
|
+# TODO: Add STOP scope switch
|
|
26
|
+#
|
|
27
|
+# TODO: Add debug switch
|
23
|
28
|
# TODO: Clarify info, warning, error, debug and print messages
|
24
|
|
-# TODO: Remove debugging print lines
|
25
|
|
-# TODO: Add .gitignore
|
26
|
29
|
#
|
27
|
|
-
|
28
|
|
-"""
|
29
|
|
-# TODO: Use "waveform:data?" multiple times to extract the whole 12M points
|
30
|
|
- in order to overcome the "Memory lack in waveform reading!" screen message
|
31
|
|
-"""
|
32
|
|
-# TODO: Detect if the osc is in RUN or in STOP mode (looking at the length of data extracted)
|
|
30
|
+# TODO: Add automated version increase
|
|
31
|
+#
|
|
32
|
+# TODO: Extract all memory datapoints. For the moment, CSV is limited to the displayed 1200 datapoints.
|
|
33
|
+# TODO: Use arrays instead of strings and lists for csv mode.
|
|
34
|
+#
|
|
35
|
+# TODO: variables/functions name refactoring
|
|
36
|
+# TODO: Fine tune maximum chunk size request
|
33
|
37
|
# TODO: Investigate scaling. Sometimes 3.0e-008 instead of expected 3.0e-000
|
34
|
38
|
# TODO: Add timestamp and mark the trigger point as t0
|
35
|
39
|
# TODO: Use channels label instead of chan1, chan2, chan3, chan4, math
|
|
@@ -43,11 +47,12 @@ __author__ = 'RoGeorge'
|
43
|
47
|
# Set the desired logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
44
|
48
|
logging.basicConfig(level=logging.INFO,
|
45
|
49
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
46
|
|
- filename=os.path.basename(sys.argv[0]) + '.log')
|
47
|
|
-logging.info("New run started...")
|
48
|
|
-logging.info("Log message: INFO level set.")
|
|
50
|
+ filename=os.path.basename(sys.argv[0]) + '.log',
|
|
51
|
+ filemode='w')
|
49
|
52
|
|
50
|
|
-log_running_Python_versions()
|
|
53
|
+logging.info("***** New run started...")
|
|
54
|
+logging.info("OS Platform: " + str(platform.uname()))
|
|
55
|
+log_running_python_versions()
|
51
|
56
|
|
52
|
57
|
# Update the next lines for your own default settings:
|
53
|
58
|
path_to_save = "captures/"
|
|
@@ -57,12 +62,8 @@ IP_DS1104Z = "192.168.1.3"
|
57
|
62
|
# Rigol/LXI specific constants
|
58
|
63
|
port = 5555
|
59
|
64
|
|
60
|
|
-expected_len = 1152068
|
61
|
|
-TMC_header_len = 11
|
62
|
|
-terminator_len = 3
|
63
|
|
-
|
64
|
65
|
big_wait = 10
|
65
|
|
-small_wait = 1
|
|
66
|
+smallWait = 1
|
66
|
67
|
|
67
|
68
|
company = 0
|
68
|
69
|
model = 1
|
|
@@ -110,8 +111,6 @@ file_format = sys.argv[1].lower()
|
110
|
111
|
if len(sys.argv) > 1:
|
111
|
112
|
IP_DS1104Z = sys.argv[2]
|
112
|
113
|
|
113
|
|
-# Create/check if 'path' exists
|
114
|
|
-
|
115
|
114
|
# Check network response (ping)
|
116
|
115
|
if platform.system() == "Windows":
|
117
|
116
|
response = os.system("ping -n 1 " + IP_DS1104Z + " > nul")
|
|
@@ -120,7 +119,7 @@ else:
|
120
|
119
|
|
121
|
120
|
if response != 0:
|
122
|
121
|
print
|
123
|
|
- print "No response pinging " + IP_DS1104Z
|
|
122
|
+ print "WARNING! No response pinging " + IP_DS1104Z
|
124
|
123
|
print "Check network cables and settings."
|
125
|
124
|
print "You should be able to ping the oscilloscope."
|
126
|
125
|
|
|
@@ -128,7 +127,7 @@ if response != 0:
|
128
|
127
|
# The default telnetlib drops 0x00 characters,
|
129
|
128
|
# so a modified library 'telnetlib_receive_all' is used instead
|
130
|
129
|
tn = Telnet(IP_DS1104Z, port)
|
131
|
|
-instrument_id = command(tn, "*idn?") # ask for instrument ID
|
|
130
|
+instrument_id = command(tn, "*IDN?") # ask for instrument ID
|
132
|
131
|
|
133
|
132
|
# Check if instrument is set to accept LAN commands
|
134
|
133
|
if instrument_id == "command error":
|
|
@@ -141,9 +140,12 @@ if instrument_id == "command error":
|
141
|
140
|
id_fields = instrument_id.split(",")
|
142
|
141
|
if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
|
143
|
142
|
(id_fields[model][:3] != "DS1") or (id_fields[model][-1] != "Z"):
|
144
|
|
- print "Found instrument model", id_fields[model], "instead of expected model, DS1*Z"
|
145
|
|
- print "ERROR: No Rigol from series DS1000Z found at ", IP_DS1104Z
|
146
|
|
- sys.exit("ERROR")
|
|
143
|
+ print "Found instrument model", "'" + id_fields[model] + "'", "from", "'" + id_fields[company] + "'"
|
|
144
|
+ print "WARNING: No Rigol from series DS1000Z found at", IP_DS1104Z
|
|
145
|
+ print
|
|
146
|
+ typed = raw_input("ARE YOU SURE YOU WANT TO CONTINUE? (No/Yes):")
|
|
147
|
+ if typed != 'Yes':
|
|
148
|
+ sys.exit('Nothing done. Bye!')
|
147
|
149
|
|
148
|
150
|
print "Instrument ID:",
|
149
|
151
|
print instrument_id
|
|
@@ -155,120 +157,101 @@ filename = path_to_save + id_fields[model] + "_" + id_fields[serial] + "_" + tim
|
155
|
157
|
if file_format in ["png", "bmp"]:
|
156
|
158
|
# Ask for an oscilloscope display print screen
|
157
|
159
|
print "Receiving screen capture..."
|
158
|
|
- buff = command(tn, "display:data?")
|
|
160
|
+ buff = command(tn, ":DISP:DATA?")
|
159
|
161
|
|
160
|
|
- # Just in case the transfer did not complete in the expected time
|
161
|
|
- while len(buff) < expected_len:
|
|
162
|
+ expectedBuffLen = expected_buff_bytes(buff)
|
|
163
|
+ # Just in case the transfer did not complete in the expected time, read the remaining 'buff' chunks
|
|
164
|
+ while len(buff) < expectedBuffLen:
|
162
|
165
|
logging.warning("Received LESS data then expected! (" +
|
163
|
|
- str(len(buff)) + " out of " + str(expected_len) + " expected raw BMP bytes.)")
|
164
|
|
- tmp = tn.read_until("\n", small_wait)
|
|
166
|
+ str(len(buff)) + " out of " + str(expectedBuffLen) + " expected 'buff' bytes.)")
|
|
167
|
+ tmp = tn.read_until("\n", smallWait)
|
165
|
168
|
if len(tmp) == 0:
|
166
|
169
|
break
|
167
|
170
|
buff += tmp
|
168
|
171
|
logging.warning(str(len(tmp)) + " leftover bytes added to 'buff'.")
|
169
|
172
|
|
170
|
|
- if len(buff) < expected_len:
|
171
|
|
- logging.error("Received LESS data then expected! (" +
|
172
|
|
- str(len(buff)) + " out of " + str(expected_len) + " expected raw BMP bytes.)")
|
|
173
|
+ if len(buff) < expectedBuffLen:
|
|
174
|
+ logging.error("After reading all data chunks, 'buff' is still shorter then expected! (" +
|
|
175
|
+ str(len(buff)) + " out of " + str(expectedBuffLen) + " expected 'buff' bytes.)")
|
173
|
176
|
sys.exit("ERROR")
|
174
|
177
|
|
175
|
|
- # Strip TMC Blockheader and terminator bytes
|
176
|
|
- buff = buff[TMC_header_len:-terminator_len]
|
|
178
|
+ # Strip TMC Blockheader and keep only the data
|
|
179
|
+ tmcHeaderLen = tmc_header_bytes(buff)
|
|
180
|
+ expectedDataLen = expected_data_bytes(buff)
|
|
181
|
+ buff = buff[tmcHeaderLen: tmcHeaderLen+expectedDataLen]
|
177
|
182
|
|
178
|
183
|
# Save as PNG or BMP according to file_format
|
179
|
184
|
im = Image.open(StringIO.StringIO(buff))
|
180
|
185
|
im.save(filename + "." + file_format, file_format)
|
181
|
|
- print "Saved file:", filename + "." + file_format
|
|
186
|
+ print "Saved file:", "'" + filename + "." + file_format + "'"
|
182
|
187
|
|
|
188
|
+# TODO: Change WAV:FORM from ASC to BYTE
|
183
|
189
|
elif file_format == "csv":
|
184
|
|
- # Put osc in STOP mode
|
|
190
|
+ # Put the scope in STOP mode - for the moment, deal with it by manually stopping the scope
|
|
191
|
+ # TODO: Add command line switch and code logic for 1200 vs ALL memory data points
|
185
|
192
|
# tn.write("stop")
|
186
|
193
|
# response = tn.read_until("\n", 1)
|
187
|
194
|
|
188
|
195
|
# Scan for displayed channels
|
189
|
|
- channel_list = []
|
190
|
|
- for channel in ["chan1", "chan2", "chan3", "chan4", "math"]:
|
191
|
|
- response = command(tn, channel + ":display?")
|
192
|
|
-
|
193
|
|
- # Strip '\n' terminator
|
194
|
|
- response = response[:-1]
|
195
|
|
- if response == '1':
|
196
|
|
- channel_list += [channel]
|
|
196
|
+ chanList = []
|
|
197
|
+ for channel in ["CHAN1", "CHAN2", "CHAN3", "CHAN4", "MATH"]:
|
|
198
|
+ response = command(tn, ":" + channel + ":DISP?")
|
|
199
|
+
|
|
200
|
+ # If channel is active
|
|
201
|
+ if response == '1\n':
|
|
202
|
+ chanList += [channel]
|
|
203
|
+
|
|
204
|
+ # the meaning of 'max' is - will read only the displayed data when the scope is in RUN mode,
|
|
205
|
+ # or when the MATH channel is selected
|
|
206
|
+ # - will read all the acquired data points when the scope is in STOP mode
|
|
207
|
+ # TODO: Change mode to MAX
|
|
208
|
+ # TODO: Add command line switch for MAX/NORM
|
|
209
|
+ command(tn, ":WAV:MODE NORM")
|
|
210
|
+ command(tn, ":WAV:STAR 0")
|
|
211
|
+ command(tn, ":WAV:MODE NORM")
|
197
|
212
|
|
198
|
213
|
csv_buff = ""
|
199
|
|
- depth = get_memory_depth(tn)
|
200
|
214
|
|
201
|
215
|
# for each active channel
|
202
|
|
- for channel in channel_list:
|
|
216
|
+ for channel in chanList:
|
203
|
217
|
print
|
204
|
218
|
|
205
|
219
|
# Set WAVE parameters
|
206
|
|
- command(tn, "waveform:source " + channel)
|
207
|
|
- command(tn, "waveform:form asc")
|
|
220
|
+ command(tn, ":WAV:SOUR " + channel)
|
|
221
|
+ command(tn, ":WAV:FORM ASC")
|
208
|
222
|
|
209
|
|
- # Maximum = only displayed data when osc. in RUN mode, or full memory data when STOPed
|
210
|
|
- # Always ONLY displayed data (1200 points) if MATH channel is selected
|
211
|
|
- command(tn, "waveform:mode max")
|
|
223
|
+ # MATH channel does not allow START and STOP to be set. They are always 0 and 1200
|
|
224
|
+ if channel != "MATH":
|
|
225
|
+ command(tn, ":WAV:STAR 1")
|
|
226
|
+ command(tn, ":WAV:STOP 1200")
|
212
|
227
|
|
213
|
|
- # Get all possible data
|
214
|
228
|
buff = ""
|
215
|
|
- data_available = True
|
216
|
|
-
|
217
|
|
- # max_chunk is dependent of the wav:mode and the oscilloscope type
|
218
|
|
- # if you get on the oscilloscope screen the error message
|
219
|
|
- # "Memory lack in waveform reading!", then decrease max_chunk value
|
220
|
|
- max_chunk = 100000 # tested for DS1104Z
|
221
|
|
- if max_chunk > depth:
|
222
|
|
- max_chunk = depth
|
223
|
|
-
|
224
|
|
- n1 = 1
|
225
|
|
- n2 = max_chunk
|
226
|
|
- while data_available:
|
227
|
|
- display_n1 = n1
|
228
|
|
- stop_point = is_waveform_from_to(tn, n1, n2)
|
229
|
|
- if stop_point == 0:
|
230
|
|
- logging.error("ERROR: Stop data point index is Zero while available data is True.")
|
231
|
|
- sys.exit("ERROR")
|
232
|
|
- elif stop_point < n1:
|
233
|
|
- break
|
234
|
|
- elif stop_point < n2:
|
235
|
|
- n2 = stop_point
|
236
|
|
- is_waveform_from_to(tn, n1, n2)
|
237
|
|
- data_available = False
|
238
|
|
- else:
|
239
|
|
- data_available = True
|
240
|
|
- n1 = n2 + 1
|
241
|
|
- n2 += max_chunk
|
|
229
|
+ print "Data from channel '" + str(channel) + "', points " + str(1) + "-" + str(1200) + ": Receiving..."
|
|
230
|
+ buffChunk = command(tn, ":WAV:DATA?")
|
242
|
231
|
|
243
|
|
- print "Data from channel '" + str(channel) + "', points " +\
|
244
|
|
- str(display_n1) + "-" + str(stop_point) + ": Receiving..."
|
245
|
|
- buff_chunks = command(tn, "waveform:data?")
|
|
232
|
+ # Just in case the transfer did not complete in the expected time
|
|
233
|
+ while buffChunk[-1] != "\n":
|
|
234
|
+ logging.warning("The data transfer did not complete in the expected time of " +
|
|
235
|
+ str(smallWait) + " second(s).")
|
246
|
236
|
|
247
|
|
- # Just in case the transfer did not complete in the expected time
|
248
|
|
- while buff_chunks[-1] != "\n":
|
249
|
|
- logging.warning("The data transfer did not complete in the expected time of " +
|
250
|
|
- str(small_wait) + " second(s).")
|
251
|
|
-
|
252
|
|
- tmp = tn.read_until("\n", small_wait)
|
253
|
|
- if len(tmp) == 0:
|
254
|
|
- break
|
255
|
|
- buff_chunks += tmp
|
256
|
|
- logging.warning(str(len(tmp)) + " leftover bytes added to 'buff_chunks'.")
|
|
237
|
+ tmp = tn.read_until("\n", smallWait)
|
|
238
|
+ if len(tmp) == 0:
|
|
239
|
+ break
|
|
240
|
+ buffChunk += tmp
|
|
241
|
+ logging.warning(str(len(tmp)) + " leftover bytes added to 'buff_chunks'.")
|
257
|
242
|
|
258
|
|
- # Append data chunks
|
259
|
|
- # Strip TMC Blockheader and terminator bytes
|
260
|
|
- buff += buff_chunks[TMC_header_len:-1] + ","
|
|
243
|
+ # Append data chunks
|
|
244
|
+ # Strip TMC Blockheader and terminator bytes
|
|
245
|
+ buff += buffChunk[tmc_header_bytes(buffChunk):-1] + ","
|
261
|
246
|
|
|
247
|
+ # Strip the last \n char
|
262
|
248
|
buff = buff[:-1]
|
263
|
249
|
|
264
|
|
- # Append each value to csv_buff
|
265
|
|
-
|
266
|
250
|
# Process data
|
267
|
|
-
|
268
|
251
|
buff_list = buff.split(",")
|
269
|
252
|
buff_rows = len(buff_list)
|
270
|
253
|
|
271
|
|
- # Put red data into csv_buff
|
|
254
|
+ # Put read data into csv_buff
|
272
|
255
|
csv_buff_list = csv_buff.split(os.linesep)
|
273
|
256
|
csv_rows = len(csv_buff_list)
|
274
|
257
|
|
|
@@ -295,6 +278,6 @@ elif file_format == "csv":
|
295
|
278
|
scr_file.write(csv_buff)
|
296
|
279
|
scr_file.close()
|
297
|
280
|
|
298
|
|
- print "Saved file: '", filename + "." + file_format + "'"
|
|
281
|
+ print "Saved file:", "'" + filename + "." + file_format + "'"
|
299
|
282
|
|
300
|
283
|
tn.close()
|