diff --git a/README.md b/README.md index f2591d15..fc13f93f 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ To use the pyseabreeze backend (requires `pyusb`) simply run this before importi | USB650 | | ap--/python-seabreeze#47 | | SPARK | x | x | | ADC1000 | | x | +| SR2 | | x | | SR4 | | x | | ST-VIS | | x | diff --git a/os_support/10-oceanoptics.rules b/os_support/10-oceanoptics.rules index b89becb5..16ee96ac 100644 --- a/os_support/10-oceanoptics.rules +++ b/os_support/10-oceanoptics.rules @@ -1,7 +1,7 @@ # udev rules file for Ocean Optics, Inc. spectrometers # ==================================================== # -# version: 2.4 +# version: 2.5 # updated: 2023-04-15 # maintainer: Andreas Poehlmann # @@ -14,6 +14,7 @@ # $ seabreeze_os_setup # # Changes: +# - [2.5] added support for SR2 spectrometer # - [2.4] added support for SR4 spectrometer # - added support for HDX spectrometer # - added support for QE-PRO class of OceanOptics spectrometers @@ -85,9 +86,11 @@ ATTR{idVendor}=="0547", ATTR{idProduct}=="2235", SYMLINK+="ezUSB-FX-%n", MODE:=" ATTR{idVendor}=="04b4", ATTR{idProduct}=="8613", SYMLINK+="ezUSB-FX2-%n", MODE:="0666" # Ocean Insight Inc. HDX spectrometer ATTR{idVendor}=="2457", ATTR{idProduct}=="2003", SYMLINK+="oceanhdx-%n", MODE:="0666" -# Ocean Insight Inc. SR4 spectrometer -ATTR{idVendor}=="0999", ATTR{idProduct}=="1002", SYMLINK+="sr4-%n", MODE:="0666" # Ocean Insight Inc. ST spectrometer ATTR{idVendor}=="0999", ATTR{idProduct}=="1000", SYMLINK+="st-%n", MODE:="0666" +# Ocean Insight Inc. SR4 spectrometer +ATTR{idVendor}=="0999", ATTR{idProduct}=="1001", SYMLINK+="sr2-%n", MODE:="0666" +# Ocean Insight Inc. SR4 spectrometer +ATTR{idVendor}=="0999", ATTR{idProduct}=="1002", SYMLINK+="sr4-%n", MODE:="0666" LABEL="oceanoptics_rules_end" diff --git a/src/seabreeze/pyseabreeze/devices.py b/src/seabreeze/pyseabreeze/devices.py index 02dcf0c7..adbd82d8 100644 --- a/src/seabreeze/pyseabreeze/devices.py +++ b/src/seabreeze/pyseabreeze/devices.py @@ -1202,6 +1202,30 @@ class ADC1000USB(SeaBreezeDevice): ) +class SR2(SeaBreezeDevice): + model_name = "SR2" + + # communication config + transport = (USBTransport,) + usb_vendor_id = 0x0999 + usb_product_id = 0x1001 + usb_endpoint_map = EndPointMap(ep_out=0x01, highspeed_in=0x81) + usb_protocol = OBP2Protocol + + # spectrometer config + dark_pixel_indices = DarkPixelIndices.from_ranges((0, 24)) + integration_time_min = 1 # 1 us + integration_time_max = 2000000 # 2 s + integration_time_base = 1 + spectrum_num_pixel = 2110 + spectrum_raw_length = (2110 * 2) + 32 # Metadata + spectrum_max_value = 65535 + trigger_modes = TriggerMode.supported("OBP_NORMAL") + + # features + feature_classes = (sbf.spectrometer.SeaBreezeSpectrometerFeatureSR2,) + + class SR4(SeaBreezeDevice): model_name = "SR4" diff --git a/src/seabreeze/pyseabreeze/features/spectrometer.py b/src/seabreeze/pyseabreeze/features/spectrometer.py index 8dfbcab7..681773e9 100644 --- a/src/seabreeze/pyseabreeze/features/spectrometer.py +++ b/src/seabreeze/pyseabreeze/features/spectrometer.py @@ -604,6 +604,47 @@ def get_intensities(self) -> NDArray[np.float_]: return ret * self._normalization_value +class SeaBreezeSpectrometerFeatureSR2(SeaBreezeSpectrometerFeatureOBP): + def _get_spectrum_raw(self) -> NDArray[np.uint8]: + timeout = int( + self._integration_time_max * 1e-3 + + self.protocol.transport.default_timeout_ms + ) + datastring = self.protocol.query(0x000_01C_00, timeout_ms=timeout) + return numpy.frombuffer(datastring, dtype=numpy.uint8) + + def get_intensities(self) -> NDArray[np.float_]: + tmp = self._get_spectrum_raw() + # 32byte metadata block at beginning + ret = tmp[32:].view(numpy.dtype(" None: + if mode in self._trigger_modes: + self.protocol.send(0x000_00D_01, mode, request_ack=True) + else: + raise SeaBreezeError("Only supports: %s" % str(self._trigger_modes)) + + def set_integration_time_micros(self, integration_time_micros: int) -> None: + t_min = self._integration_time_min + t_max = self._integration_time_max + if t_min <= integration_time_micros < t_max: + i_time = int(integration_time_micros / self._integration_time_base) + self.protocol.send(0x000_00C_01, i_time) + else: + raise SeaBreezeError(f"Integration not in [{t_min:d}, {t_max:d}]") + + def get_wavelengths(self) -> NDArray[np.float_]: + data = self.protocol.query(0x000_011_00) + num_coeffs = len(data) // 4 + assert len(data) % 4 == 0 # ??? + assert num_coeffs > 1 # ??? + coeffs = struct.unpack("<" + "f" * num_coeffs, data)[1:] + # and generate the wavelength array + indices = numpy.arange(self._spectrum_length, dtype=numpy.float64) + return sum(wl * (indices**i) for i, wl in enumerate(coeffs)) # type: ignore + + class SeaBreezeSpectrometerFeatureSR4(SeaBreezeSpectrometerFeatureOBP): def _get_spectrum_raw(self) -> NDArray[np.uint8]: timeout = int(