Capture the display of a Rigol DS1000Z series oscilloscope by LAN using LXI SCPI commands
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

OscScreenGrabLAN.py 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. #!/usr/bin/env python
  2. __author__ = 'RoGeorge'
  3. #
  4. """
  5. # TODO: Use "waveform:data?" multiple times to extract the whole 12M points
  6. in order to overcome the "Memory lack in waveform reading!" screen message
  7. """
  8. # TODO: Detect if the osc is in RUN or in STOP mode (looking at the length of data extracted)
  9. # TODO: Investigate scaling. Sometimes 3.0e-008 instead of expected 3.0e-000
  10. # TODO: Add timestamp and mark the trigger point as t0
  11. # TODO: Use channels label instead of chan1, chan2, chan3, chan4, math
  12. # TODO: Add command line parameters file path
  13. # TODO: Speed-up the transfer, try to replace Telnet with direct TCP
  14. # TODO: Add GUI
  15. # TODO: Add browse and custom filename selection
  16. # TODO: Create executable distributions
  17. #
  18. from telnetlib_receive_all import Telnet
  19. from Rigol_functions import *
  20. import time
  21. from PIL import Image
  22. import StringIO
  23. import sys
  24. import os
  25. import platform
  26. # Update the next lines for your own default settings:
  27. path_to_save = "captures/"
  28. save_format = "PNG"
  29. IP_DS1104Z = "192.168.1.3"
  30. # Rigol/LXI specific constants
  31. port = 5555
  32. expected_len = 1152068
  33. TMC_header_len = 11
  34. terminator_len = 3
  35. big_wait = 10
  36. small_wait = 1
  37. company = 0
  38. model = 1
  39. serial = 2
  40. # Check command line parameters
  41. script_name = os.path.basename(sys.argv[0])
  42. def print_help():
  43. # Print usage
  44. print
  45. print "Usage:"
  46. print " " + "python " + script_name + " png|bmp|csv [oscilloscope_IP [save_path]]"
  47. print
  48. print "Usage examples:"
  49. print " " + "python " + script_name + " png"
  50. print " " + "python " + script_name + " csv 192.168.1.3"
  51. print
  52. print "The following usage cases are not yet implemented:"
  53. print " " + "python " + script_name + " bmp 192.168.1.3 my_place_for_captures"
  54. print
  55. print "This program captures either the waveform or the whole screen"
  56. print " of a Rigol DS1000Z series oscilloscope, then save it on the computer"
  57. print " as a CSV, PNG or BMP file with a timestamp in the file name."
  58. print
  59. print " The program is using LXI protocol, so the computer"
  60. print " must have LAN connection with the oscilloscope."
  61. print " USB and/or GPIB connections are not used by this software."
  62. print
  63. print " No VISA, IVI or Rigol drivers are needed."
  64. print
  65. # Read/verify file type
  66. if len(sys.argv) <= 1:
  67. print_help()
  68. sys.exit("Warning - wrong command line parameters.")
  69. elif sys.argv[1].lower() not in ["png", "bmp", "csv"]:
  70. print_help()
  71. print "This file type is not supported: ", sys.argv[1]
  72. print
  73. print_running_Python_versions()
  74. sys.exit("ERROR")
  75. file_format = sys.argv[1].lower()
  76. # Read IP
  77. if len(sys.argv) > 1:
  78. IP_DS1104Z = sys.argv[2]
  79. # Create/check if 'path' exists
  80. # Check network response (ping)
  81. if platform.system() == "Windows":
  82. response = os.system("ping -n 1 " + IP_DS1104Z + " > nul")
  83. else:
  84. response = os.system("ping -c 1 " + IP_DS1104Z + " > /dev/null")
  85. if response != 0:
  86. print
  87. print "No response pinging " + IP_DS1104Z
  88. print "Check network cables and settings."
  89. print "You should be able to ping the oscilloscope."
  90. # Open a modified telnet session
  91. # The default telnetlib drops 0x00 characters,
  92. # so a modified library 'telnetlib_receive_all' is used instead
  93. tn = Telnet(IP_DS1104Z, port)
  94. tn.write("*idn?") # ask for instrument ID
  95. instrument_id = tn.read_until("\n", 1)
  96. # Check if instrument is set to accept LAN commands
  97. if instrument_id == "command error":
  98. print instrument_id
  99. print "Check the oscilloscope settings."
  100. print "Utility -> IO Setting -> RemoteIO -> LAN must be ON"
  101. print
  102. print_running_Python_versions()
  103. sys.exit("ERROR")
  104. # Check if instrument is indeed a Rigol DS1000Z series
  105. id_fields = instrument_id.split(",")
  106. if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
  107. (id_fields[model][:3] != "DS1") or (id_fields[model][-1] != "Z"):
  108. print
  109. print "ERROR: No Rigol from series DS1000Z found at ", IP_DS1104Z
  110. print
  111. print_running_Python_versions()
  112. sys.exit("ERROR")
  113. print "Instrument ID:",
  114. print instrument_id
  115. # Prepare filename as C:\MODEL_SERIAL_YYYY-MM-DD_HH.MM.SS
  116. timestamp = time.strftime("%Y-%m-%d_%H.%M.%S", time.localtime())
  117. filename = path_to_save + id_fields[model] + "_" + id_fields[serial] + "_" + timestamp
  118. if file_format in ["png", "bmp"]:
  119. # Ask for an oscilloscope display print screen
  120. tn.write("display:data?")
  121. print "Receiving screen capture..."
  122. buff = tn.read_until("\n", big_wait)
  123. # Just in case the transfer did not complete in the expected time
  124. while len(buff) < expected_len:
  125. tmp = tn.read_until("\n", small_wait)
  126. if len(tmp) == 0:
  127. break
  128. buff += tmp
  129. # Strip TMC Blockheader and terminator bytes
  130. buff = buff[TMC_header_len:-terminator_len]
  131. # Save as PNG or BMP according to file_format
  132. im = Image.open(StringIO.StringIO(buff))
  133. im.save(filename + "." + file_format, file_format)
  134. print "Saved file:", filename + "." + file_format
  135. elif file_format == "csv":
  136. # Put osc in STOP mode
  137. # tn.write("stop")
  138. # response = tn.read_until("\n", 1)
  139. # Scan for displayed channels
  140. channel_list = []
  141. for channel in ["chan1", "chan2", "chan3", "chan4", "math"]:
  142. tn.write(channel + ":display?")
  143. response = tn.read_until("\n", 1)
  144. # Strip '\n' terminator
  145. response = response[:-1]
  146. if response == '1':
  147. channel_list += [channel]
  148. print "Active channels on the display:", channel_list
  149. csv_buff = ""
  150. depth = get_memory_depth(tn)
  151. # for each active channel
  152. for channel in channel_list:
  153. print
  154. # Set WAVE parameters
  155. tn.write("waveform:source " + channel)
  156. time.sleep(1)
  157. tn.write("waveform:form asc")
  158. time.sleep(1)
  159. # Maximum - only displayed data when osc. in RUN mode, or full memory data when STOPed
  160. tn.write("waveform:mode max")
  161. time.sleep(1)
  162. # Get all possible data
  163. buff = ""
  164. data_available = True
  165. # max_chunk is dependent of the wav:mode and the oscilloscope type
  166. # if you get on the oscilloscope screen the error message
  167. # "Memory lack in waveform reading!", then decrease max_chunk value
  168. max_chunk = 100000.0 # tested for DS1104Z
  169. if max_chunk > depth:
  170. max_chunk = depth
  171. n1 = 1.0
  172. n2 = max_chunk
  173. data_available = True
  174. while data_available:
  175. display_n1 = n1
  176. stop_point = is_waveform_from_to(tn, n1, n2)
  177. if stop_point == 0:
  178. data_available = False
  179. print "ERROR: Stop data point index lower then start data point index"
  180. print
  181. print_running_Python_versions()
  182. sys.exit("ERROR")
  183. elif stop_point < n1:
  184. break
  185. elif stop_point < n2:
  186. n2 = stop_point
  187. is_waveform_from_to(tn, n1, n2)
  188. data_available = False
  189. else:
  190. data_available = True
  191. n1 = n2 + 1
  192. n2 += max_chunk
  193. tn.write("waveform:data?")
  194. print "Data from channel " + str(channel) + ", points " +\
  195. str(int(display_n1)) + "-" + str(int(stop_point)) + ": Receiving..."
  196. buff_chunks = tn.read_until("\n", big_wait)
  197. # Just in case the transfer did not complete in the expected time
  198. while buff_chunks[-1] != "\n":
  199. tmp = tn.read_until("\n", small_wait)
  200. if len(tmp) == 0:
  201. break
  202. buff_chunks += tmp
  203. # Append data chunks
  204. # Strip TMC Blockheader and terminator bytes
  205. buff += buff_chunks[TMC_header_len:-1] + ","
  206. buff = buff[:-1]
  207. # Append each value to csv_buff
  208. # Process data
  209. buff_list = buff.split(",")
  210. buff_rows = len(buff_list)
  211. # Put red data into csv_buff
  212. csv_buff_list = csv_buff.split(os.linesep)
  213. csv_rows = len(csv_buff_list)
  214. current_row = 0
  215. if csv_buff == "":
  216. csv_first_column = True
  217. csv_buff = str(channel) + os.linesep
  218. else:
  219. csv_first_column = False
  220. csv_buff = str(csv_buff_list[current_row]) + "," + str(channel) + os.linesep
  221. for point in buff_list:
  222. current_row += 1
  223. if csv_first_column:
  224. csv_buff += str(point) + os.linesep
  225. else:
  226. if current_row < csv_rows:
  227. csv_buff += str(csv_buff_list[current_row]) + "," + str(point) + os.linesep
  228. else:
  229. csv_buff += "," + str(point) + os.linesep
  230. # Save data as CSV
  231. scr_file = open(filename + "." + file_format, "wb")
  232. scr_file.write(csv_buff)
  233. scr_file.close()
  234. print "Saved file:", filename + "." + file_format
  235. tn.close()