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.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. 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 = ""
  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. sys.exit("ERROR")
  73. file_format = sys.argv[1].lower()
  74. # Read IP
  75. if len(sys.argv) > 1:
  76. IP_DS1104Z = sys.argv[2]
  77. # Create/check if 'path' exists
  78. # Check network response (ping)
  79. if platform.system() == "Windows":
  80. response = os.system("ping -n 1 " + IP_DS1104Z + " > nul")
  81. else:
  82. response = os.system("ping -c 1 " + IP_DS1104Z + " > /dev/null")
  83. if response != 0:
  84. print
  85. print "No response pinging " + IP_DS1104Z
  86. print "Check network cables and settings."
  87. print "You should be able to ping the oscilloscope."
  88. # Open a modified telnet session
  89. # The default telnetlib drops 0x00 characters,
  90. # so a modified library 'telnetlib_receive_all' is used instead
  91. tn = Telnet(IP_DS1104Z, port)
  92. tn.write("*idn?") # ask for instrument ID
  93. instrument_id = tn.read_until("\n", 1)
  94. # Check if instrument is set to accept LAN commands
  95. if instrument_id == "command error":
  96. print instrument_id
  97. print "Check the oscilloscope settings."
  98. print "Utility -> IO Setting -> RemoteIO -> LAN must be ON"
  99. sys.exit("ERROR")
  100. # Check if instrument is indeed a Rigol DS1000Z series
  101. id_fields = instrument_id.split(",")
  102. if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
  103. (id_fields[model][:3] != "DS1") or (id_fields[model][-1] != "Z"):
  104. print
  105. print "ERROR: No Rigol from series DS1000Z found at ", IP_DS1104Z
  106. sys.exit("ERROR")
  107. print "Instrument ID:",
  108. print instrument_id
  109. # Prepare filename as C:\MODEL_SERIAL_YYYY-MM-DD_HH.MM.SS
  110. timestamp = time.strftime("%Y-%m-%d_%H.%M.%S", time.localtime())
  111. filename = path_to_save + id_fields[model] + "_" + id_fields[serial] + "_" + timestamp
  112. if file_format in ["png", "bmp"]:
  113. # Ask for an oscilloscope display print screen
  114. tn.write("display:data?")
  115. print "Receiving screen capture..."
  116. buff = tn.read_until("\n", big_wait)
  117. # Just in case the transfer did not complete in the expected time
  118. while len(buff) < expected_len:
  119. tmp = tn.read_until("\n", small_wait)
  120. if len(tmp) == 0:
  121. break
  122. buff += tmp
  123. # Strip TMC Blockheader and terminator bytes
  124. buff = buff[TMC_header_len:-terminator_len]
  125. # Save as PNG or BMP according to file_format
  126. im = Image.open(StringIO.StringIO(buff))
  127. im.save(filename + "." + file_format, file_format)
  128. print "Saved file:", filename + "." + file_format
  129. elif file_format == "csv":
  130. # Put osc in STOP mode
  131. # tn.write("stop")
  132. # response = tn.read_until("\n", 1)
  133. # Scan for displayed channels
  134. channel_list = []
  135. for channel in ["chan1", "chan2", "chan3", "chan4", "math"]:
  136. tn.write(channel + ":display?")
  137. response = tn.read_until("\n", 1)
  138. # Strip '\n' terminator
  139. response = response[:-1]
  140. if response == '1':
  141. channel_list += [channel]
  142. print "Active channels on the display:", channel_list
  143. csv_buff = ""
  144. depth = get_memory_depth(tn)
  145. # for each active channel
  146. for channel in channel_list:
  147. print
  148. # Set WAVE parameters
  149. tn.write("waveform:source " + channel)
  150. time.sleep(0.2)
  151. tn.write("waveform:form asc")
  152. time.sleep(0.2)
  153. # Maximum - only displayed data when osc. in RUN mode, or full memory data when STOPed
  154. tn.write("waveform:mode max")
  155. time.sleep(0.2)
  156. # Get all possible data
  157. buff = ""
  158. data_available = True
  159. # max_chunk is dependent of the wav:mode and the oscilloscope type
  160. # if you get on the oscilloscope screen the error message
  161. # "Memory lack in waveform reading!", then decrease max_chunk value
  162. max_chunk = 100000.0 # tested for DS1104Z
  163. if max_chunk > depth:
  164. max_chunk = depth
  165. n1 = 1.0
  166. n2 = max_chunk
  167. data_available = True
  168. while data_available:
  169. display_n1 = n1
  170. stop_point = is_waveform_from_to(tn, n1, n2)
  171. if stop_point == 0:
  172. data_available = False
  173. print "ERROR: Stop data point index lower then start data point index"
  174. sys.exit("ERROR")
  175. elif stop_point < n1:
  176. break
  177. elif stop_point < n2:
  178. n2 = stop_point
  179. is_waveform_from_to(tn, n1, n2)
  180. data_available = False
  181. else:
  182. data_available = True
  183. n1 = n2 + 1
  184. n2 += max_chunk
  185. tn.write("waveform:data?")
  186. print "Data from channel " + str(channel) + ", points " +\
  187. str(int(display_n1)) + "-" + str(int(stop_point)) + ": Receiving..."
  188. buff_chunks = tn.read_until("\n", big_wait)
  189. # Just in case the transfer did not complete in the expected time
  190. while buff_chunks[-1] != "\n":
  191. tmp = tn.read_until("\n", small_wait)
  192. if len(tmp) == 0:
  193. break
  194. buff_chunks += tmp
  195. # Append data chunks
  196. # Strip TMC Blockheader and terminator bytes
  197. buff += buff_chunks[TMC_header_len:-1] + ","
  198. buff = buff[:-1]
  199. # Append each value to csv_buff
  200. # Process data
  201. buff_list = buff.split(",")
  202. buff_rows = len(buff_list)
  203. # Put red data into csv_buff
  204. csv_buff_list = csv_buff.split(os.linesep)
  205. csv_rows = len(csv_buff_list)
  206. current_row = 0
  207. if csv_buff == "":
  208. csv_first_column = True
  209. csv_buff = str(channel) + os.linesep
  210. else:
  211. csv_first_column = False
  212. csv_buff = str(csv_buff_list[current_row]) + "," + str(channel) + os.linesep
  213. for point in buff_list:
  214. current_row += 1
  215. if csv_first_column:
  216. csv_buff += str(point) + os.linesep
  217. else:
  218. if current_row < csv_rows:
  219. csv_buff += str(csv_buff_list[current_row]) + "," + str(point) + os.linesep
  220. else:
  221. csv_buff += "," + str(point) + os.linesep
  222. # Save data as CSV
  223. scr_file = open(filename + "." + file_format, "wb")
  224. scr_file.write(csv_buff)
  225. scr_file.close()
  226. print "Saved file:", filename + "." + file_format
  227. tn.close()