spectrometer3.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #!/usr/bin/env python3
  2. # -*- coding: UTF-8 -*-
  3. """
  4. OceanOptics Spectrometer python client
  5. (C) 2016 Mauro Lacy <mauro@lacy.com.ar>
  6. This is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this software. If not, see <http://www.gnu.org/licenses/>.
  16. """
  17. __version__ = '0.5'
  18. import socket
  19. from sys import exit
  20. #from time import sleep
  21. from datetime import datetime
  22. import os.path
  23. import struct
  24. # server address
  25. ip_address = '127.0.0.1'
  26. port = 1865
  27. class Spectrometer(object):
  28. # command constants
  29. cmd_set_integration_time = "\x00\x01"
  30. cmd_get_integration_time = "\x00\x02"
  31. cmd_set_boxcar_width = "\x00\x03"
  32. cmd_get_boxcar_width = "\x00\x04"
  33. cmd_set_scans_to_average = "\x00\x05"
  34. cmd_get_scans_to_average = "\x00\x06"
  35. cmd_set_target_url = "\x00\x07"
  36. cmd_get_target_url = "\x00\x08"
  37. cmd_get_spectrum = "\x00\x09"
  38. cmd_get_wavelengths = "\x00\x0A"
  39. cmd_get_serial_number = "\x00\x0B"
  40. cmd_get_name = "\x00\x0C"
  41. cmd_get_version = "\x00\x0D"
  42. cmd_get_calibration_coefficients_from_buffer = "\x00\x0E"
  43. cmd_set_calibration_coefficients_to_buffer = "\x00\x0F"
  44. cmd_get_calibration_coefficients_from_eeprom = "\x00\x10"
  45. cmd_set_calibration_coefficients_to_eeprom = "\x00\x11"
  46. cmd_get_pixel_binning_factor = "\x00\x12"
  47. cmd_set_pixel_binning_factor = "\x00\x13"
  48. cmd_get_integration_time_minimum = "\x00\x14"
  49. cmd_get_integration_time_maximum = "\x00\x15"
  50. cmd_get_intensity_maximum = "\x00\x16"
  51. cmd_get_electric_dark_correction = "\x00\x17"
  52. cmd_set_electric_dark_correction = "\x00\x18"
  53. cmd_set_tec_enable = "\x00\x1A"
  54. cmd_set_tec_temperature = "\x00\x1B"
  55. cmd_get_tec_temperature = "\x00\x1C"
  56. cmd_set_lamp_enable = "\x00\x1D"
  57. # OceanHandler commands
  58. cmd_get_current_status = "\x00\x20"
  59. cmd_get_current_spectrum = "\x00\x21"
  60. cmd_set_max_acquisitions = "\x00\x22"
  61. cmd_get_max_acquisitions = "\x00\x23"
  62. cmd_set_file_save_mode = "\x00\x24"
  63. cmd_get_file_save_mode = "\x00\x25"
  64. cmd_set_file_prefix = "\x00\x26"
  65. cmd_get_file_prefix = "\x00\x27"
  66. cmd_set_sequence_type = "\x00\x28"
  67. cmd_get_sequence_type = "\x00\x29"
  68. cmd_set_sequence_interval = "\x00\x2A"
  69. cmd_get_sequence_interval = "\x00\x2B"
  70. cmd_set_save_directory = "\x00\x2C"
  71. cmd_get_save_directory = "\x00\x2D"
  72. cmd_save_spectrum = "\x00\x2E"
  73. cmd_start_sequence = "\x00\x2F"
  74. cmd_pause_sequence = "\x00\x30"
  75. cmd_resume_sequence = "\x00\x31"
  76. cmd_stop_sequence = "\x00\x32"
  77. cmd_get_sequence_state = "\x00\x33"
  78. cmd_get_current_sequence_number = "\x00\x34"
  79. cmd_set_scope_mode = "\x00\x35"
  80. cmd_get_scope_mode = "\x00\x36"
  81. cmd_set_scope_interval = "\x00\x37"
  82. cmd_get_scope_interval = "\x00\x38"
  83. # convenience constant for null/no parameters to a command
  84. no_parameters = "\x00\x00"
  85. def __init__(self, ip_address, port=port, channel=0):
  86. # connect timeout
  87. self.timeout = 15
  88. # default refresh interval for scope mode
  89. self.default_scope_interval = 5
  90. self.ip_address = ip_address
  91. self.port = port
  92. self.channel=channel
  93. self.sock = self._connect_or_abort(ip_address, port)
  94. def _connect_or_abort(self, ip_address, port):
  95. sock = socket.create_connection((ip_address, port), timeout=self.timeout)
  96. if sock is None:
  97. print("socket: connect failed.")
  98. exit(1)
  99. return sock
  100. def _string_length_bytes(self, s):
  101. l = len(s)
  102. hi = l & 0xFF00
  103. hi = hi >> 8
  104. lo = l & 0xFF
  105. result = chr(hi) + chr(lo)
  106. return result
  107. def _socket_write_all(self, buffer):
  108. buffer_length = len(buffer)
  109. total_sent = 0
  110. while (total_sent < buffer_length):
  111. count = self.sock.send(buffer[total_sent:])
  112. total_sent += count
  113. if count == None:
  114. total_sent = None
  115. return total_sent
  116. def _socket_read_all(self):
  117. response_string = ''
  118. out = True
  119. while (out):
  120. out = self.sock.recv(2048)
  121. response_string += str(out, 'iso8859-1')
  122. return response_string
  123. def _socket_read_n(self, expected_length):
  124. remaining = expected_length
  125. response_string = ''
  126. chunk = True
  127. while (chunk):
  128. chunk = self.sock.recv(remaining)
  129. out = str(chunk, 'iso8859-1')
  130. response_string += out
  131. remaining -= len(out)
  132. return response_string, expected_length - remaining
  133. def _extract_result_code_from_response(self, response_string):
  134. result = ord(response_string[0]) * 256 + ord(response_string[1]);
  135. response_string = response_string[2:]
  136. return result, response_string
  137. def _close(self):
  138. self.sock.close()
  139. self.sock = None
  140. def _build_command(self, cmd, *args):
  141. msg = bytearray(cmd, 'iso8859-1')
  142. arguments = ''
  143. if len(args):
  144. for i, a in enumerate(args):
  145. arguments += str(a)
  146. if len(args) == 1 or i < len(args)-1:
  147. arguments += ';'
  148. msg += struct.pack('!H', len(arguments));
  149. else:
  150. arguments = self.no_parameters
  151. msg += bytearray(arguments, 'iso8859-1');
  152. return msg
  153. def _send_command(self, cmd, *args):
  154. if self.sock is None:
  155. self.sock = self._connect_or_abort(ip_address, port)
  156. msg = self._build_command(cmd, *args)
  157. self._socket_write_all(msg)
  158. result = self._socket_read_all()
  159. self._close()
  160. code = 0
  161. if len(result) >= 2:
  162. code, result = self._extract_result_code_from_response(result)
  163. return code if len(result) == 2 else result if code == 1 else None # FIXME?: what are valid code values?
  164. def _send_command_n(self, cmd, *args):
  165. if self.sock is None:
  166. self.sock = self._connect_or_abort(ip_address, port)
  167. msg = self._build_command(cmd, *args)
  168. self._socket_write_all(msg)
  169. output_string = None
  170. # we are expecting 2 bytes result code
  171. result_code = 0
  172. out, result = self._socket_read_n(2)
  173. if result == 2:
  174. res0 = ord(out[0]) << 8
  175. res1 = ord(out[1])
  176. result_code = res0 | res1
  177. # now we are expecting 4 bytes with the result length
  178. out, result = self._socket_read_n(4)
  179. if result == 4:
  180. b0 = ord(out[0]) << 24
  181. b1 = ord(out[1]) << 16
  182. b2 = ord(out[2]) << 8
  183. b3 = ord(out[3])
  184. result_length = b0 | b1 | b2 | b3
  185. output_string, _ = self._socket_read_n(result_length)
  186. self._close()
  187. return output_string, result_code
  188. def get_version(self):
  189. return self._send_command(self.cmd_get_version)
  190. def get_serial(self):
  191. return self._send_command(self.cmd_get_serial_number, self.channel)
  192. def get_current_status(self):
  193. return self._send_command(self.cmd_get_current_status, self.channel)
  194. def get_integration(self):
  195. return self._send_command(self.cmd_get_integration_time, self.channel)
  196. def get_get_boxcar(self):
  197. return self._send_command(self.cmd_get_boxcar_width, self.channel)
  198. def get_average(self):
  199. return self._send_command(self.cmd_get_scans_to_average, self.channel)
  200. def get_target(self):
  201. return self._send_command(self.cmd_get_target_url)
  202. def get_spectrum(self):
  203. return self._send_command_n(self.cmd_get_spectrum, self.channel)[0]
  204. def get_wavelengths(self):
  205. return self._send_command_n(self.cmd_get_wavelengths, self.channel)[0]
  206. def get_name(self):
  207. return self._send_command(self.cmd_get_name, self.channel)
  208. def get_calibration_buffer(self):
  209. return self._send_command_n(self.cmd_get_calibration_coefficients_from_buffer, self.channel)[0]
  210. def get_calibration_eeprom(self):
  211. return self._send_command_n(self.cmd_get_calibration_coefficients_from_eeprom, self.channel)[0]
  212. def get_binning(self):
  213. return self._send_command(self.cmd_get_pixel_binning_factor, self.channel)
  214. def get_min_integration(self):
  215. return self._send_command(self.cmd_get_integration_time_minimum, self.channel)
  216. def get_max_integration(self):
  217. return self._send_command(self.cmd_get_integration_time_maximum, self.channel)
  218. def get_max_intensity(self):
  219. return self._send_command(self.cmd_get_intensity_maximum, self.channel)
  220. def get_e_d_correct(self):
  221. return self._send_command(self.cmd_get_electric_dark_correction, self.channel)
  222. def get_tec_temperature(self):
  223. return self._send_command(self.cmd_get_tec_temperature, self.channel)
  224. def current_spectrum(self):
  225. return self._send_command_n(self.cmd_get_current_spectrum)[0]
  226. def get_max_acquisitions(self):
  227. return self._send_command(self.cmd_get_max_acquisitions, self.channel)
  228. def get_save_mode(self):
  229. return self._send_command(self.cmd_get_file_save_mode, self.channel)
  230. def get_prefix(self):
  231. return self._send_command(self.cmd_get_file_prefix, self.channel)
  232. def get_sequence_type(self):
  233. return self._send_command(self.cmd_get_sequence_type, self.channel)
  234. def get_sequence_interval(self):
  235. return self._send_command(self.cmd_get_sequence_interval, self.channel)
  236. def get_save_location(self):
  237. return self._send_command(self.cmd_get_save_directory, self.channel)
  238. def get_sequence_state(self):
  239. return self._send_command(self.cmd_get_sequence_state, self.channel)
  240. def get_sequence_number(self):
  241. return self._send_command(self.cmd_get_current_sequence_number, self.channel)
  242. def get_scope_mode(self):
  243. return self._send_command(self.cmd_get_scope_mode, self.channel)
  244. def get_scope_interval(self):
  245. return self._send_command(self.cmd_get_scope_interval, self.channel)
  246. def set_integration(self, microseconds):
  247. return self._send_command(self.cmd_set_integration_time, self.channel, int(microseconds))
  248. def set_boxcar(self, width):
  249. return self._send_command(self.cmd_set_boxcar_width, self.channel, width)
  250. def set_average(self, scans):
  251. return self._send_command(self.cmd_set_scans_to_average, self.channel, scans)
  252. def set_target(self, url):
  253. return self._send_command(self.cmd_set_target_url, url)
  254. def set_calibration_buffer(self, calibration):
  255. return self._send_command_n(self.cmd_set_calibration_coefficients_to_buffer,
  256. self.channel, self.calibration)[1]
  257. def set_calibration_eeprom(self, calibration):
  258. return self._send_command_n(self.cmd_set_calibration_coefficients_to_eeprom,
  259. self.channel, self.calibration)[1]
  260. def set_binning(self, bins):
  261. return self._send_command(self.cmd_set_pixel_binning_factor,
  262. self.channel, bins)
  263. def set_e_d_correct(self, electric):
  264. return self._send_command(self.cmd_set_electric_dark_correction, self.channel, electric)
  265. def set_tec_enable(self, enable):
  266. return self._send_command(self.cmd_set_tec_enable, self.channel, enable)
  267. def set_tec_temperature(self, temperature):
  268. return self._send_command(self.cmd_set_tec_temperature, self.channel, temperature)
  269. def set_lamp_enable(self, enable):
  270. return self._send_command(self.cmd_set_lamp_enable, self.channel, enable)
  271. def set_max_acquisitions(self, acquisitions):
  272. return self._send_command(self.cmd_set_max_acquisitions, self.channel, acquisitions)
  273. def set_save_mode(self, mode):
  274. return self._send_command(self.cmd_set_file_save_mode, self.channel, mode)
  275. def set_prefix(self, prefix):
  276. return self._send_command(self.cmd_set_file_prefix, self.channel, prefix)
  277. def set_sequence_type(self, t):
  278. return self._send_command(self.cmd_set_sequence_type, self.channel, t)
  279. def set_sequence_interval(self, interval):
  280. return self._send_command(self.cmd_set_sequence_interval, self.channel, interval)
  281. def set_save_location(self, location):
  282. return self._send_command(self.cmd_set_save_directory, self.channel, location)
  283. def set_scope_mode(self, mode):
  284. return self._send_command(self.cmd_set_scope_mode, self.channel, mode)
  285. def set_scope_interval(self, interval):
  286. return self._send_command(self.cmd_set_scope_interval, self.channel, interval)
  287. def save_spectrum(self, location):
  288. return self._send_command(self.cmd_save_spectrum, self.channel, location)
  289. def start_sequence(self):
  290. return self._send_command(self.cmd_start_sequence, self.channel)
  291. def pause_sequence(self):
  292. return self._send_command(self.cmd_pause_sequence, self.channel)
  293. def resume_sequence(self):
  294. return self._send_command(self.cmd_resume_sequence, self.channel)
  295. def stop_sequence(self):
  296. return self._send_command(self.cmd_stop_sequence, self.channel)
  297. if __name__ == '__main__':
  298. integration_time = 1 # [seconds]
  299. location = '/home/pi/spectrometer/spectrums'
  300. # prefix = 'spectrum'
  301. spectrometer = Spectrometer(ip_address, port)
  302. print('Version:', spectrometer.get_version())
  303. print('Serial:', spectrometer.get_serial())
  304. spectrometer.set_integration(integration_time*1e6)
  305. print('Integration time: %s µs' % spectrometer.get_integration())
  306. # 1) start/stop sequence
  307. # spectrometer.set_save_location(location)
  308. # print('Save location:', spectrometer.get_save_location())
  309. # spectrometer.set_prefix(prefix)
  310. # print('File prefix:', spectrometer.get_prefix())
  311. # print('Start Sequence:', spectrometer.start_sequence())
  312. # sleep(integration_time+1)
  313. # print('Stop Sequence:', spectrometer.stop_sequence())
  314. # 2) save_spectrum
  315. file = os.path.join(location, '%s.txt' % datetime.strftime(datetime.now(), '%d-%m-%Y_%H:%M:%S'))
  316. print('Saving spectrum:', file)
  317. spectrometer.save_spectrum(file)
  318. # 3) get_spectrum
  319. # print('Getting spectrum...')
  320. # print('Spectrum:')
  321. # spectrum = spectrometer.get_spectrum()
  322. # spectrum = [v for v in spectrum.split()]
  323. print('done.\nCurrent status:', spectrometer.get_current_status())