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 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #!/usr/bin/env python
  2. __author__ = 'RoGeorge'
  3. #
  4. # TODO: Add command line parameters file path
  5. # TODO: Add GUI
  6. # TODO: Add browse and custom filename selection
  7. # TODO: Create executable distributions
  8. #
  9. import telnetlib_receive_all
  10. import time
  11. import Image
  12. import StringIO
  13. import sys
  14. import os
  15. import platform
  16. # Update the next lines for your own default settings:
  17. path_to_save = ""
  18. save_format = "PNG"
  19. IP_DS1104Z = "192.168.1.3"
  20. # Rigol/LXI specific constants
  21. port = 5555
  22. expected_len = 1152068
  23. TMC_header_len = 11
  24. terminator_len = 3
  25. big_wait = 10
  26. small_wait = 1
  27. company = 0
  28. model = 1
  29. serial = 2
  30. # Check parameters
  31. script_name = os.path.basename(sys.argv[0])
  32. def print_help():
  33. # Print usage
  34. print
  35. print "Usage:"
  36. print " " + script_name + " png|bmp|csv [oscilloscope_IP [save_path]]"
  37. print
  38. print "Usage examples:"
  39. print " " + script_name + " png"
  40. print
  41. print "The following usage cases are not yet implemented:"
  42. print " " + script_name + " csv 192.168.1.3"
  43. print " " + script_name + " bmp 192.168.1.3 my_place_for_osc_bmp_captures"
  44. print
  45. print "This program captures either the waveform or the whole screen"
  46. print " of a Rigol DS1000Z series oscilloscope, then save it on the computer"
  47. print " as a CSV, PNG or BMP file with a timestamp in the file name."
  48. print
  49. print " The program is using LXI protocol, so the computer"
  50. print " must have LAN connection with the oscilloscope."
  51. print " USB and/or GPIB connections are not used by this software."
  52. print
  53. print " No VISA, IVI or Rigol drivers are needed."
  54. print
  55. if len(sys.argv) <= 1:
  56. print_help()
  57. sys.exit("Warning - wrong command line parameters.")
  58. elif sys.argv[1].lower() not in ["png", "bmp", "csv"]:
  59. print_help()
  60. print "This file type is not supported: ", sys.argv[1]
  61. sys.exit("ERROR")
  62. file_format = sys.argv[1].lower()
  63. # Create/check if 'path' exists
  64. # Check network response (ping)
  65. if platform.system() == "Windows":
  66. response = os.system("ping -n 1 " + IP_DS1104Z + " > nul")
  67. else:
  68. response = os.system("ping -c 1 " + IP_DS1104Z + " > /dev/null")
  69. if response != 0:
  70. print
  71. print "No response pinging " + IP_DS1104Z
  72. print "Check network cables and settings."
  73. print "You should be able to ping the oscilloscope."
  74. # Open a modified telnet session
  75. # The default telnetlib drops 0x00 characters,
  76. # so a modified library 'telnetlib_receive_all' is used instead
  77. tn = telnetlib_receive_all.Telnet(IP_DS1104Z, port)
  78. tn.write("*idn?") # ask for instrument ID
  79. instrument_id = tn.read_until("\n", 1)
  80. # Check if instrument is set to accept LAN commands
  81. if instrument_id == "command error":
  82. print instrument_id
  83. print "Check the oscilloscope settings."
  84. print "Utility -> IO Setting -> RemoteIO -> LAN must be ON"
  85. sys.exit("ERROR")
  86. # Check if instrument is indeed a Rigol DS1000Z series
  87. id_fields = instrument_id.split(",")
  88. if (id_fields[company] != "RIGOL TECHNOLOGIES") or \
  89. (id_fields[model][:3] != "DS1") or (id_fields[model][-1] != "Z"):
  90. print
  91. print "ERROR: No Rigol from series DS1000Z found at ", IP_DS1104Z
  92. sys.exit("ERROR")
  93. print "Instrument ID:"
  94. print instrument_id
  95. # Prepare filename as C:\MODEL_SERIAL_YYYY-MM-DD_HH.MM.SS
  96. timestamp = time.strftime("%Y-%m-%d_%H.%M.%S", time.localtime())
  97. filename = path_to_save + id_fields[model] + "_" + id_fields[serial] + "_" + timestamp
  98. if file_format in ["png", "bmp"]:
  99. # Ask for an oscilloscope display print screen
  100. tn.write("display:data?")
  101. print "Receiving..."
  102. buff = tn.read_until("\n", big_wait)
  103. # Just in case the transfer did not complete in the expected time
  104. while len(buff) < expected_len:
  105. tmp = tn.read_until("\n", small_wait)
  106. if len(tmp) == 0:
  107. break
  108. buff += tmp
  109. # Strip TMC Blockheader and terminator bytes
  110. buff = buff[TMC_header_len:-terminator_len]
  111. # Save as PNG or BMP according to file_format
  112. im = Image.open(StringIO.StringIO(buff))
  113. im.save(filename + "." + file_format, file_format)
  114. print "Saved file:", filename + "." + file_format
  115. elif file_format == "csv":
  116. print "csv mode"
  117. # Put osc in STOP mode
  118. # tn.write("stop")
  119. # response = tn.read_until("\n", 1)
  120. # Scan for displayed channels
  121. channel_list = []
  122. for channel in ["chan1", "chan2", "chan3", "chan4", "math"]:
  123. tn.write(channel + ":display?")
  124. response = tn.read_until("\n", 1)
  125. # Strip '\n' terminator
  126. response = response[:-1]
  127. if response == '1':
  128. channel_list += [channel]
  129. print channel_list
  130. csv_buff = ""
  131. # for each active channel
  132. for channel in channel_list:
  133. # Read WAVE parameters
  134. # Set WAVE parameters
  135. tn.write("waveform:source " + channel)
  136. response = tn.read_until("\n", 1)
  137. # Maximum - only displayed data when osc. in RUN mode, or full memory data when STOPed
  138. tn.write("waveform:mode maximum")
  139. response = tn.read_until("\n", 1)
  140. tn.write("waveform:format ASCII")
  141. response = tn.read_until("\n", 1)
  142. # Get data
  143. tn.write("waveform:data?")
  144. print "Receiving..."
  145. buff = tn.read_until("\n", big_wait)
  146. # Just in case the transfer did not complete in the expected time
  147. while buff[-1] != "\n":
  148. tmp = tn.read_until("\n", small_wait)
  149. if len(tmp) == 0:
  150. break
  151. buff += tmp
  152. # Append each value to csv_buff
  153. # Strip headers (TMC and points number)
  154. TMC_header = buff[:TMC_header_len]
  155. data_points = float(TMC_header[2:])
  156. # Strip TMC Blockheader and terminator bytes
  157. buff = buff[TMC_header_len:-1]
  158. # Process data
  159. print buff
  160. print
  161. print "data_points =", data_points, "from", channel
  162. buff_list = buff.split(",")
  163. buff_rows = len(buff_list)
  164. print buff_list
  165. # Put red data into csv_buff
  166. csv_buff_list = csv_buff.split(os.linesep)
  167. csv_rows = len(csv_buff_list)
  168. current_row = 0
  169. if csv_buff == "":
  170. csv_first_column = True
  171. csv_buff = str(channel) + os.linesep
  172. else:
  173. csv_first_column = False
  174. csv_buff = str(csv_buff_list[current_row]) + "," + str(channel) + os.linesep
  175. for point in buff_list:
  176. current_row += 1
  177. if csv_first_column:
  178. csv_buff += str(point) + os.linesep
  179. else:
  180. if current_row < csv_rows:
  181. csv_buff += str(csv_buff_list[current_row]) + "," + str(point) + os.linesep
  182. else:
  183. csv_buff += "," + str(point) + os.linesep
  184. print csv_buff
  185. # Save data as CSV
  186. scr_file = open(filename + "." + file_format, "wb")
  187. scr_file.write(csv_buff)
  188. scr_file.close()
  189. print "Saved file:", filename + "." + file_format
  190. tn.close()