Skip to content

Commit f4365c6

Browse files
authored
Merge pull request #242 from gabrielbenedikt/HR2-support
Basic HR2 support
2 parents 319f690 + 52e4c0c commit f4365c6

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

os_support/10-oceanoptics.rules

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# udev rules file for Ocean Optics, Inc. spectrometers
22
# ====================================================
33
#
4-
# version: 2.6
5-
# updated: 2024-01-24
4+
# version: 2.7
5+
# updated: 2024-05-29
66
# maintainer: Andreas Poehlmann <[email protected]>
77
#
88
# [info] Previous versions are missing this header.
@@ -14,6 +14,7 @@
1414
# $ seabreeze_os_setup
1515
#
1616
# Changes:
17+
# - [2.7] added support for HR2 spectrometer
1718
# - [2.6] added support for SR6 spectrometer
1819
# - [2.5] added support for SR2 spectrometer
1920
# - [2.4] added support for SR4 spectrometer
@@ -95,5 +96,7 @@ ATTR{idVendor}=="0999", ATTR{idProduct}=="1001", SYMLINK+="sr2-%n", MODE:="0666"
9596
ATTR{idVendor}=="0999", ATTR{idProduct}=="1002", SYMLINK+="sr4-%n", MODE:="0666"
9697
# Ocean Insight Inc. SR6 Spectrometer
9798
ATTR{idVendor}=="0999", ATTR{idProduct}=="1005", SYMLINK+="sr6-%n", MODE:="0666"
99+
# Ocean Insight Inc. HR2 Spectrometer
100+
ATTR{idVendor}=="0999", ATTR{idProduct}=="1003", SYMLINK+="hr2-%n", MODE:="0666"
98101

99102
LABEL="oceanoptics_rules_end"

src/seabreeze/pyseabreeze/devices.py

+24
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,30 @@ class SR6(SeaBreezeDevice):
12761276
feature_classes = (sbf.spectrometer.SeaBreezeSpectrometerFeatureSR6,)
12771277

12781278

1279+
class HR2(SeaBreezeDevice):
1280+
model_name = "HR2"
1281+
1282+
# communication config
1283+
transport = (USBTransport,)
1284+
usb_vendor_id = 0x0999
1285+
usb_product_id = 0x1003
1286+
usb_endpoint_map = EndPointMap(ep_out=0x01, highspeed_in=0x81)
1287+
usb_protocol = OBP2Protocol
1288+
1289+
# spectrometer config
1290+
dark_pixel_indices = DarkPixelIndices.from_ranges()
1291+
integration_time_min = 1 # 1 us
1292+
integration_time_max = 2000000 # 2 s
1293+
integration_time_base = 1
1294+
spectrum_num_pixel = 2110
1295+
spectrum_raw_length = (2110 * 2) + 32 # XXX: Metadata
1296+
spectrum_max_value = 65535
1297+
trigger_modes = TriggerMode.supported("OBP_NORMAL")
1298+
1299+
# features
1300+
feature_classes = (sbf.spectrometer.SeaBreezeSpectrometerFeatureHR2,)
1301+
1302+
12791303
class ST(SeaBreezeDevice):
12801304
model_name = "ST"
12811305

src/seabreeze/pyseabreeze/features/spectrometer.py

+41
Original file line numberDiff line numberDiff line change
@@ -697,3 +697,44 @@ class SeaBreezeSpectrometerFeatureSR6(SeaBreezeSpectrometerFeatureOBP2):
697697

698698
class SeaBreezeSpectrometerFeatureST(SeaBreezeSpectrometerFeatureOBP2):
699699
pass
700+
701+
702+
class SeaBreezeSpectrometerFeatureHR2(SeaBreezeSpectrometerFeatureOBP):
703+
def _get_spectrum_raw(self) -> NDArray[np.uint8]:
704+
timeout = int(
705+
self._integration_time * 1e-3 + self.protocol.transport.default_timeout_ms
706+
)
707+
datastring = self.protocol.query(0x000_01C_00, timeout_ms=timeout)
708+
return numpy.frombuffer(datastring, dtype=numpy.uint8)
709+
710+
def get_intensities(self) -> NDArray[np.float_]:
711+
tmp = self._get_spectrum_raw()
712+
# 32byte metadata block at beginning
713+
ret = tmp[32:].view(numpy.dtype("<H")).astype(numpy.double)
714+
return ret * self._normalization_value
715+
716+
def set_trigger_mode(self, mode: int) -> None:
717+
if mode in self._trigger_modes:
718+
self.protocol.send(0x000_00D_01, mode, request_ack=True)
719+
else:
720+
raise SeaBreezeError("Only supports: %s" % str(self._trigger_modes))
721+
722+
def set_integration_time_micros(self, integration_time_micros: int) -> None:
723+
t_min = self._integration_time_min
724+
t_max = self._integration_time_max
725+
if t_min <= integration_time_micros < t_max:
726+
self._integration_time = integration_time_micros
727+
i_time = int(integration_time_micros / self._integration_time_base)
728+
self.protocol.send(0x000_00C_01, i_time)
729+
else:
730+
raise SeaBreezeError(f"Integration not in [{t_min:d}, {t_max:d}]")
731+
732+
def get_wavelengths(self) -> NDArray[np.float_]:
733+
data = self.protocol.query(0x000_011_00)
734+
num_coeffs = len(data) // 4
735+
assert len(data) % 4 == 0 # ???
736+
assert num_coeffs > 1 # ???
737+
coeffs = struct.unpack("<" + "f" * num_coeffs, data)[1:]
738+
# and generate the wavelength array
739+
indices = numpy.arange(self._spectrum_length, dtype=numpy.float64)
740+
return sum(wl * (indices**i) for i, wl in enumerate(coeffs)) # type: ignore

0 commit comments

Comments
 (0)