Browse Source

Add '\n' for all SCPI commands

RoGeorge 7 years ago
parent
commit
570ad8ed5b
2 changed files with 121 additions and 160 deletions
  1. 89
    106
      OscScreenGrabLAN.py
  2. 32
    54
      Rigol_functions.py

+ 89
- 106
OscScreenGrabLAN.py View File

@@ -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()

+ 32
- 54
Rigol_functions.py View File

@@ -1,91 +1,69 @@
1
-__author__ = 'RoGeorge'
2
-
3
-import time
4 1
 import pip
5 2
 import sys
6 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 11
     installed_packages = pip.get_installed_distributions()
15 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 18
     answer_wait_s = 1
22 19
     response = ""
23 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 23
         response = tn.read_until("\n", 1)  # wait max 1s for an answer
27 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 28
     response = tn.read_until("\n", answer_wait_s)
32 29
     logging.info("Received response: " + response)
33 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 50
 def get_memory_depth(tn):
37 51
     # Define number of horizontal grid divisions for DS1054Z
38 52
     h_grid = 12
39 53
 
40 54
     # ACQuire:MDEPth
41
-    mdep = command(tn, "ACQ:MDEP?")
55
+    mdep = command(tn, ":ACQ:MDEP?")
42 56
 
43 57
     # if mdep is "AUTO"
44 58
     if mdep == "AUTO\n":
45 59
         # ACQuire:SRATe
46
-        srate = command(tn, "ACQ:SRAT?")
60
+        srate = command(tn, ":ACQ:SRAT?")
47 61
 
48 62
         # TIMebase[:MAIN]:SCALe
49
-        scal = command(tn, "TIM:SCAL?")
63
+        scal = command(tn, ":TIM:SCAL?")
50 64
 
51 65
         # mdep = h_grid * scal * srate
52
-        mdep = h_grid * scal * srate
66
+        mdep = h_grid * float(scal) * float(srate)
53 67
 
54 68
     # return mdep
55 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

Loading…
Cancel
Save