ソースを参照

Add '\n' for all SCPI commands

RoGeorge 7年前
コミット
570ad8ed5b
2個のファイルの変更121行の追加160行の削除
  1. 89
    106
      OscScreenGrabLAN.py
  2. 32
    54
      Rigol_functions.py

+ 89
- 106
OscScreenGrabLAN.py ファイルの表示

10
 import platform
10
 import platform
11
 import logging
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
 __author__ = 'RoGeorge'
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
 # TODO: Add message for csv data points: mdep (all) or 1200 (screen), depending on RUN/STOP state, MATH and WAV:MODE
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
 # TODO: Clarify info, warning, error, debug and print messages
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
 # TODO: Investigate scaling. Sometimes 3.0e-008 instead of expected 3.0e-000
37
 # TODO: Investigate scaling. Sometimes 3.0e-008 instead of expected 3.0e-000
34
 # TODO: Add timestamp and mark the trigger point as t0
38
 # TODO: Add timestamp and mark the trigger point as t0
35
 # TODO: Use channels label instead of chan1, chan2, chan3, chan4, math
39
 # TODO: Use channels label instead of chan1, chan2, chan3, chan4, math
43
 # Set the desired logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
47
 # Set the desired logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
44
 logging.basicConfig(level=logging.INFO,
48
 logging.basicConfig(level=logging.INFO,
45
                     format='%(asctime)s - %(levelname)s - %(message)s',
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
 # Update the next lines for your own default settings:
57
 # Update the next lines for your own default settings:
53
 path_to_save = "captures/"
58
 path_to_save = "captures/"
57
 # Rigol/LXI specific constants
62
 # Rigol/LXI specific constants
58
 port = 5555
63
 port = 5555
59
 
64
 
60
-expected_len = 1152068
61
-TMC_header_len = 11
62
-terminator_len = 3
63
-
64
 big_wait = 10
65
 big_wait = 10
65
-small_wait = 1
66
+smallWait = 1
66
 
67
 
67
 company = 0
68
 company = 0
68
 model = 1
69
 model = 1
110
 if len(sys.argv) > 1:
111
 if len(sys.argv) > 1:
111
     IP_DS1104Z = sys.argv[2]
112
     IP_DS1104Z = sys.argv[2]
112
 
113
 
113
-# Create/check if 'path' exists
114
-
115
 # Check network response (ping)
114
 # Check network response (ping)
116
 if platform.system() == "Windows":
115
 if platform.system() == "Windows":
117
     response = os.system("ping -n 1 " + IP_DS1104Z + " > nul")
116
     response = os.system("ping -n 1 " + IP_DS1104Z + " > nul")
120
 
119
 
121
 if response != 0:
120
 if response != 0:
122
     print
121
     print
123
-    print "No response pinging " + IP_DS1104Z
122
+    print "WARNING! No response pinging " + IP_DS1104Z
124
     print "Check network cables and settings."
123
     print "Check network cables and settings."
125
     print "You should be able to ping the oscilloscope."
124
     print "You should be able to ping the oscilloscope."
126
 
125
 
128
 # The default telnetlib drops 0x00 characters,
127
 # The default telnetlib drops 0x00 characters,
129
 #   so a modified library 'telnetlib_receive_all' is used instead
128
 #   so a modified library 'telnetlib_receive_all' is used instead
130
 tn = Telnet(IP_DS1104Z, port)
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
 # Check if instrument is set to accept LAN commands
132
 # Check if instrument is set to accept LAN commands
134
 if instrument_id == "command error":
133
 if instrument_id == "command error":
141
 id_fields = instrument_id.split(",")
140
 id_fields = instrument_id.split(",")
142
 if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
141
 if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
143
         (id_fields[model][:3] != "DS1") or (id_fields[model][-1] != "Z"):
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
 print "Instrument ID:",
150
 print "Instrument ID:",
149
 print instrument_id
151
 print instrument_id
155
 if file_format in ["png", "bmp"]:
157
 if file_format in ["png", "bmp"]:
156
     # Ask for an oscilloscope display print screen
158
     # Ask for an oscilloscope display print screen
157
     print "Receiving screen capture..."
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
         logging.warning("Received LESS data then expected! (" +
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
         if len(tmp) == 0:
168
         if len(tmp) == 0:
166
             break
169
             break
167
         buff += tmp
170
         buff += tmp
168
         logging.warning(str(len(tmp)) + " leftover bytes added to 'buff'.")
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
         sys.exit("ERROR")
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
     # Save as PNG or BMP according to file_format
183
     # Save as PNG or BMP according to file_format
179
     im = Image.open(StringIO.StringIO(buff))
184
     im = Image.open(StringIO.StringIO(buff))
180
     im.save(filename + "." + file_format, file_format)
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
 elif file_format == "csv":
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
     # tn.write("stop")
192
     # tn.write("stop")
186
     # response = tn.read_until("\n", 1)
193
     # response = tn.read_until("\n", 1)
187
 
194
 
188
     # Scan for displayed channels
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
     csv_buff = ""
213
     csv_buff = ""
199
-    depth = get_memory_depth(tn)
200
 
214
 
201
     # for each active channel
215
     # for each active channel
202
-    for channel in channel_list:
216
+    for channel in chanList:
203
         print
217
         print
204
 
218
 
205
         # Set WAVE parameters
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
         buff = ""
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
         buff = buff[:-1]
248
         buff = buff[:-1]
263
 
249
 
264
-        # Append each value to csv_buff
265
-
266
         # Process data
250
         # Process data
267
-
268
         buff_list = buff.split(",")
251
         buff_list = buff.split(",")
269
         buff_rows = len(buff_list)
252
         buff_rows = len(buff_list)
270
 
253
 
271
-        # Put red data into csv_buff
254
+        # Put read data into csv_buff
272
         csv_buff_list = csv_buff.split(os.linesep)
255
         csv_buff_list = csv_buff.split(os.linesep)
273
         csv_rows = len(csv_buff_list)
256
         csv_rows = len(csv_buff_list)
274
 
257
 
295
     scr_file.write(csv_buff)
278
     scr_file.write(csv_buff)
296
     scr_file.close()
279
     scr_file.close()
297
 
280
 
298
-    print "Saved file: '", filename + "." + file_format + "'"
281
+    print "Saved file:", "'" + filename + "." + file_format + "'"
299
 
282
 
300
 tn.close()
283
 tn.close()

+ 32
- 54
Rigol_functions.py ファイルの表示

1
-__author__ = 'RoGeorge'
2
-
3
-import time
4
 import pip
1
 import pip
5
 import sys
2
 import sys
6
 import logging
3
 import logging
7
 
4
 
5
+__author__ = 'RoGeorge'
8
 
6
 
9
-def log_running_Python_versions():
10
-    logging.info("***** Running Python version:")
11
-    logging.info(str(sys.version) + ", " + str(sys.version_info))         # parentheses required in python 3.
12
-    logging.info(sys.version_info)
7
+
8
+def log_running_python_versions():
9
+    logging.info("Python version: " + str(sys.version) + ", " + str(sys.version_info))  # () required in Python 3.
13
 
10
 
14
     installed_packages = pip.get_installed_distributions()
11
     installed_packages = pip.get_installed_distributions()
15
     installed_packages_list = sorted(["%s==%s" % (i.key, i.version) for i in installed_packages])
12
     installed_packages_list = sorted(["%s==%s" % (i.key, i.version) for i in installed_packages])
16
-    logging.info("Installed Python modules:" + str(installed_packages_list))
13
+    logging.info("Installed Python modules: " + str(installed_packages_list))
17
 
14
 
18
 
15
 
19
-def command(tn, SCPI):
20
-    logging.info("SCPI to be sent: " + SCPI)
16
+def command(tn, scpi):
17
+    logging.info("SCPI to be sent: " + scpi)
21
     answer_wait_s = 1
18
     answer_wait_s = 1
22
     response = ""
19
     response = ""
23
     while response != "1\n":
20
     while response != "1\n":
24
-        tn.write("*OPC?")  # previous operation(s) has completed ?
25
-        logging.info("Send SCPI: *OPC?")
21
+        tn.write("*OPC?\n")  # previous operation(s) has completed ?
22
+        logging.info("Send SCPI: *OPC? # May I send a command? 1==yes")
26
         response = tn.read_until("\n", 1)  # wait max 1s for an answer
23
         response = tn.read_until("\n", 1)  # wait max 1s for an answer
27
         logging.info("Received response: " + response)
24
         logging.info("Received response: " + response)
28
 
25
 
29
-    tn.write(SCPI)
30
-    logging.info("Sent SCPI: " + SCPI)
26
+    tn.write(scpi + "\n")
27
+    logging.info("Sent SCPI: " + scpi)
31
     response = tn.read_until("\n", answer_wait_s)
28
     response = tn.read_until("\n", answer_wait_s)
32
     logging.info("Received response: " + response)
29
     logging.info("Received response: " + response)
33
     return response
30
     return response
34
 
31
 
35
 
32
 
33
+# first TMC byte is '#'
34
+# second is '0'..'9', and tells how many of the next ASCII chars
35
+#   should be converted into an integer.
36
+#   The integer will be the length of the data stream (in bytes)
37
+# after all the data bytes, the last char is '\n'
38
+def tmc_header_bytes(buff):
39
+    return 2 + int(buff[1])
40
+
41
+
42
+def expected_data_bytes(buff):
43
+    return int(buff[2:tmc_header_bytes(buff)])
44
+
45
+
46
+def expected_buff_bytes(buff):
47
+    return tmc_header_bytes(buff) + expected_data_bytes(buff) + 1
48
+
49
+
36
 def get_memory_depth(tn):
50
 def get_memory_depth(tn):
37
     # Define number of horizontal grid divisions for DS1054Z
51
     # Define number of horizontal grid divisions for DS1054Z
38
     h_grid = 12
52
     h_grid = 12
39
 
53
 
40
     # ACQuire:MDEPth
54
     # ACQuire:MDEPth
41
-    mdep = command(tn, "ACQ:MDEP?")
55
+    mdep = command(tn, ":ACQ:MDEP?")
42
 
56
 
43
     # if mdep is "AUTO"
57
     # if mdep is "AUTO"
44
     if mdep == "AUTO\n":
58
     if mdep == "AUTO\n":
45
         # ACQuire:SRATe
59
         # ACQuire:SRATe
46
-        srate = command(tn, "ACQ:SRAT?")
60
+        srate = command(tn, ":ACQ:SRAT?")
47
 
61
 
48
         # TIMebase[:MAIN]:SCALe
62
         # TIMebase[:MAIN]:SCALe
49
-        scal = command(tn, "TIM:SCAL?")
63
+        scal = command(tn, ":TIM:SCAL?")
50
 
64
 
51
         # mdep = h_grid * scal * srate
65
         # mdep = h_grid * scal * srate
52
-        mdep = h_grid * scal * srate
66
+        mdep = h_grid * float(scal) * float(srate)
53
 
67
 
54
     # return mdep
68
     # return mdep
55
     return int(mdep)
69
     return int(mdep)
56
-
57
-
58
-# return maximum achieved stop point, or 0 for wrong input parameters
59
-# if achieved == requested, then set the start and stop waveform as n1_d and n2_d
60
-def is_waveform_from_to(tn, n1_d, n2_d):
61
-    # read current
62
-    # WAVeform:STARt
63
-    n1_c = int(command(tn, "WAV:STAR?"))
64
-
65
-    # WAVeform:STOP
66
-    n2_c = int(command(tn, "WAV:STOP?"))
67
-
68
-    if (n1_d > n2_d) or (n1_d < 1) or (n2_d < 1):
69
-        # wrong parameters
70
-        return 0
71
-
72
-    elif n2_d < n1_c:
73
-        # first set n1_d then set n2_d
74
-
75
-        command(tn, "WAV:STAR " + str(n1_d))
76
-        command(tn, "WAV:STOP " + str(n2_d))
77
-
78
-    else:
79
-        # first set n2_d then set n1_d
80
-        command(tn, "WAV:STOP " + str(n2_d))
81
-        command(tn, "WAV:STAR " + str(n1_d))
82
-
83
-    # read achieved n2
84
-    n2_a = int(command(tn, "WAV:STOP?"))
85
-
86
-    if n2_a < n2_d:
87
-        # restore n1_c, n2_c
88
-        is_waveform_from_to(tn, n1_c, n2_c)
89
-
90
-    # return n2_a
91
-    return n2_a

読み込み中…
キャンセル
保存