"""
"""
from collections import defaultdict
import enum
import itertools
from future.utils import with_metaclass
from seabreeze.pyseabreeze.exceptions import SeaBreezeError
from seabreeze.pyseabreeze.features import SeaBreezeFeature
from seabreeze.pyseabreeze.protocol import OOIProtocol, OBPProtocol
from seabreeze.pyseabreeze import features as sbf
# spectrometer models for pyseabreeze
#
from seabreeze.pyseabreeze.transport import USBTransport, TransportInterface
_model_class_registry = {}
class _SeaBreezeDeviceMeta(type):
"""metaclass for pyseabreeze devices"""
def __new__(mcs, name, bases, attr_dict):
# This part of the metaclass magic is very opaque. It could be avoided by moving all
# of this logic to custom __init__ methods of the Spectrometer classes, or to factory
# functions. But I am using python-seabreeze as a playground to experiment with
# different ways of implementing extendable interfaces, so please forgive these design
# decisions ^^"
#
# I also like the concise way you can define the spectrometer functionality this way.
# It allows you to get a quick overview of the functionality implemented in pyseabreeze,
# without having to read through a lot of code. And I think that it's easy to understand
# what a spectrometer can do, by just looking at the spectrometer class definition, even
# if you don't understand the details of the implementation.
#
# Because it's quite opaque the code below tries hard to enforce strict typing in all
# defined spectrometer classes to minimize the amount of errors you can make when adding
# a new spectrometer to pyseabreeze.
#
if name != 'SeaBreezeDevice':
# This runs for all subclasses of SeaBreezeDevice, so for all defined spectrometers
# What might be unintuitive to the user is, that all defined attributes in the
# spectrometer classes are only used to configure the subclass and are NOT directly
# available in the instances later. (look at how attr_dict is modified below).
#
if 'model_name' not in attr_dict:
raise AttributeError("'model_name' not provided for class '{}'".format(name))
model_name = attr_dict.pop('model_name')
if not isinstance(model_name, str):
raise TypeError("{}.model_name not a str".format(name))
# gather the transport classes defined on the class
transport_classes = mcs._extract_transform_classes(model_name, class_name=name, attr_dict=attr_dict)
# gather the feature classes defined on the class
feature_classes = mcs._extract_feature_classes(model_name, class_name=name, attr_dict=attr_dict)
if any(not attr.startswith('_') for attr in attr_dict):
raise ValueError("can't define extra attrs on spectrometer classes: {}".format(
", ".join(attr for attr in attr_dict if not attr.startswith('_'))
))
attr_dict = {
'_model_name': model_name,
'_transport_classes': transport_classes,
'_feature_classes': feature_classes
}
return super(_SeaBreezeDeviceMeta, mcs).__new__(mcs, name, bases, attr_dict)
def __init__(cls, name, bases, attr_dict):
if name != 'SeaBreezeDevice':
# > model name
model_name = getattr(cls, '_model_name')
assert isinstance(model_name, str), "model name not a str"
_model_class_registry[model_name] = cls
super(_SeaBreezeDeviceMeta, cls).__init__(name, bases, attr_dict)
@staticmethod
def _extract_transform_classes(model_name, class_name, attr_dict):
visited_attrs = set()
transport_classes = []
try:
supported_transport_classes = attr_dict.pop('transport')
except KeyError:
raise AttributeError("{}.transport not provided")
if not isinstance(supported_transport_classes, tuple) or not supported_transport_classes:
raise TypeError("{}.transport not a tuple of len > 0")
for idx, transport_cls in enumerate(supported_transport_classes):
# for each supported transport of the spectrometer, gather the configuration from
# the spectrometer class and specialize the transport_cls with the provided settings.
#
if not issubclass(transport_cls, TransportInterface):
raise TypeError("{}.transport[{:d}] '{}' does not derive from TransportInterface".format(
class_name, idx, transport_cls.__name__
))
# noinspection PyProtectedMember
kwargs = transport_cls._required_init_kwargs
transport_init_kwargs = {}
for kw in kwargs:
if kw not in attr_dict:
raise AttributeError("{}.{} not provided for class but '{}' requires it.".format(
class_name, kw, transport_cls.__name__
))
transport_init_kwargs[kw] = attr_dict[kw]
visited_attrs.update(kwargs)
# specialize the transport class with the spectrometer's custom config
specialized_transport_cls = transport_cls.specialize(model_name, **transport_init_kwargs)
transport_classes.append(specialized_transport_cls)
for attr in visited_attrs:
del attr_dict[attr]
return tuple(transport_classes)
@staticmethod
def _extract_feature_classes(model_name, class_name, attr_dict):
visited_attrs = set()
feature_classes = defaultdict(list)
try:
supported_feature_classes = attr_dict.pop('feature_classes')
except KeyError:
raise AttributeError("{}.feature_classes not provided")
if not isinstance(supported_feature_classes, tuple) or not supported_feature_classes:
raise TypeError("{}.feature_classes not a tuple of len > 0")
for idx, feature_cls in enumerate(supported_feature_classes):
# for each supported feature of the spectrometer, gather the configuration
# from the spectrometer class and subclass the feature_cls with the provided
# settings. Also check if requirements are fulfilled, i.e. if the feature depends
# on other features or on specific protocols
#
if not issubclass(feature_cls, SeaBreezeFeature):
raise TypeError("{}.feature_classes[{:d}] '{}' does not derive from SeaBreezeFeature".format(
model_name, idx, feature_cls.__name__
))
# noinspection PyProtectedMember
required = set(feature_cls._required_features)
if not required.issubset(feature_classes):
raise KeyError("{}.feature_classes[{:d}] '{}' requires '{}'. To fix, re-order or add.".format(
model_name, idx, feature_cls.__name__, ", ".join(required - set(feature_classes))
))
# noinspection PyProtectedMember
kwargs = feature_cls._required_kwargs
feature_attrs = {}
for kw in kwargs:
if kw not in attr_dict:
raise AttributeError("{}.{} not provided for class but '{}' requires it.".format(
class_name, kw, feature_cls.__name__
))
feature_attrs[kw] = attr_dict[kw]
visited_attrs.update(kwargs)
# specialize the feature class with the spectrometer's custom config
specialized_feature_cls = feature_cls.specialize(model_name, **feature_attrs)
feature_classes[feature_cls.identifier].append(specialized_feature_cls)
for attr in visited_attrs:
del attr_dict[attr]
return feature_classes
class EndPointMap(object):
"""internal endpoint map for spectrometer classes"""
def __init__(self, ep_out=None, lowspeed_in=None, highspeed_in=None, highspeed_in2=None):
self.primary_out = self.ep_out = ep_out
self.primary_in = self.lowspeed_in = lowspeed_in
self.secondary_out = ep_out
self.secondary_in = self.highspeed_in = highspeed_in
self.secondary_in2 = self.highspeed_in2 = highspeed_in2
class DarkPixelIndices(tuple):
"""internal dark pixel range class"""
def __new__(cls, indices):
"""dark pixel indices
Parameters
----------
indices : iterable
index of electric dark pixel
"""
return super(DarkPixelIndices, cls).__new__(DarkPixelIndices, sorted(set(indices)))
@classmethod
def from_ranges(cls, *ranges):
"""return dark pixes indices from ranges
Parameters
----------
*ranges : (`int`, `int`)
ranges of electric dark pixels
"""
dp = itertools.chain(*(range(low, high) for (low, high) in ranges))
# noinspection PyArgumentList
return cls(dp)
class TriggerMode(enum.IntEnum):
"""internal trigger modes enum"""
NORMAL = 0x00
SOFTWARE = 0x01
LEVEL = 0x01
SYNCHRONIZATION = 0x02
HARDWARE = 0x03
EDGE = 0x03
SINGLE_SHOT = 0x04
SELF_NORMAL = 0x80
SELF_SOFTWARE = 0x81
SELF_SYNCHRONIZATION = 0x82
SELF_HARDWARE = 0x83
DISABLED = 0xFF
OBP_NORMAL = 0x00
OBP_EXTERNAL = 0x01
OBP_INTERNAL = 0x02
@classmethod
def supported(cls, *mode_strings):
return set(getattr(cls, mode_string) for mode_string in mode_strings)
[docs]class SeaBreezeDevice(with_metaclass(_SeaBreezeDeviceMeta)):
# internal attribute
_model_name = None
_serial_number = '?'
_cached_features = None
def __new__(cls, raw_device=None):
if raw_device is None:
raise SeaBreezeError("Don't instantiate SeaBreezeDevice directly. Use `SeabreezeAPI.list_devices()`.")
for transport in {USBTransport}:
supported_model = transport.supported_model(raw_device)
if supported_model is not None:
break
else:
raise TypeError("No transport supports device.")
specialized_cls = _model_class_registry[supported_model]
return super(SeaBreezeDevice, cls).__new__(specialized_cls)
def __init__(self, raw_device=None):
if raw_device is None:
raise SeaBreezeError("Don't instantiate SeaBreezeDevice directly. Use `SeabreezeAPI.list_devices()`.")
self._raw_device = raw_device
for transport in self._transport_classes:
if transport.supported_model(self._raw_device) is not None:
self._transport = transport()
break
else:
raise TypeError("No transport supports device.")
try:
self._serial_number = self.get_serial_number()
except SeaBreezeError:
pass
def __del__(self):
self.close()
@property
def model(self):
return self._model_name
@property
def serial_number(self):
return self._serial_number
def __repr__(self):
return "<SeaBreezeDevice %s:%s>" % (self.model, self.serial_number)
[docs] def open(self):
"""open the spectrometer usb connection
Returns
-------
None
"""
self._transport.open_device(self._raw_device)
# cache features
self._cached_features = {}
_ = self.features
# get serial
self._serial_number = self.get_serial_number()
[docs] def close(self):
"""close the spectrometer usb connection
Returns
-------
None
"""
self._transport.close_device()
@property
def is_open(self):
"""returns if the spectrometer device usb connection is opened
Returns
-------
bool
"""
return self._transport.is_open
[docs] def get_serial_number(self):
"""return the serial number string of the spectrometer
Returns
-------
serial_number: str
"""
try:
# noinspection PyProtectedMember
protocol = self._transport._protocol
if protocol is None:
raise AttributeError('transport not opened')
elif isinstance(protocol, OOIProtocol):
# The serial is stored in slot 0
# noinspection PyUnresolvedReferences
return str(self.f.eeprom.eeprom_read_slot(0))
elif isinstance(protocol, OBPProtocol):
return self.query(0x00000100, "")
else:
raise NotImplementedError("No serial number for protocol class {}".format(protocol.__class__.__name__))
except AttributeError:
raise SeaBreezeError("device not open")
@property
def features(self):
"""return a dictionary of all supported features
this returns a dictionary with all supported Features of the spectrometer
Returns
-------
features : `dict` [`str`, `seabreeze.cseabreeze.SeaBreezeFeature`]
"""
if not self._cached_features:
# noinspection PyProtectedMember
protocol = self._transport._protocol
self._cached_features = {}
for identifier in sbf.SeaBreezeFeature.get_feature_class_registry():
f_list = self._cached_features.setdefault(identifier, [])
for feature_cls in self._feature_classes[identifier]:
assert issubclass(feature_cls, sbf.SeaBreezeFeature) and identifier == feature_cls.identifier
if not feature_cls.supports_protocol(protocol):
continue
# noinspection PyProtectedMember
f_list.append(feature_cls(self._transport._protocol, len(f_list)))
return self._cached_features
@property
def f(self):
"""convenience access to features via attributes
this allows you to access a feature like this::
# via .features
device.features['spectrometer'][0].get_intensities()
# via .f
device.f.spectrometer.get_intensities()
"""
class FeatureAccessHandler(object):
def __init__(self, feature_dict):
for identifier, features in feature_dict.items():
setattr(self, identifier, features[0] if features else None) # TODO: raise FeatureNotAvailable?
return FeatureAccessHandler(self.features)
# SPECTROMETER DEFINITIONS
# ========================
#
class USB2000PLUS(SeaBreezeDevice):
model_name = 'USB2000PLUS'
# communication config
transport = (USBTransport, )
usb_product_id = 0x101E
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((6, 21)) # as in seabreeze-3.0.9
integration_time_min = 1000
integration_time_max = 655350000
integration_time_base = 1
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB2000PLUS,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class USB2000(SeaBreezeDevice):
model_name = 'USB2000'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1002
usb_endpoint_map = EndPointMap(ep_out=0x02, lowspeed_in=0x87, highspeed_in=0x82)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24))
integration_time_min = 3000
integration_time_max = 655350000
integration_time_base = 1000
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) + 1
spectrum_max_value = 4095
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB2000,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class HR2000(SeaBreezeDevice):
model_name = 'HR2000'
# communication config
transport = (USBTransport, )
usb_product_id = 0x100a
usb_endpoint_map = EndPointMap(ep_out=0x02, lowspeed_in=0x87, highspeed_in=0x82)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24))
integration_time_min = 3000
integration_time_max = 655350000
integration_time_base = 1000
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) + 1
spectrum_max_value = 4095
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureHR2000,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class HR4000(SeaBreezeDevice):
model_name = 'HR4000'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1012
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((2, 13))
integration_time_min = 10
integration_time_max = 655350000
integration_time_base = 1
spectrum_num_pixel = 3840
spectrum_raw_length = (3840 * 2) + 1
spectrum_max_value = 16383
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureHR4000,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class HR2000PLUS(SeaBreezeDevice):
model_name = 'HR2000PLUS'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1016
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24))
integration_time_min = 1000
integration_time_max = 655350000
integration_time_base = 1
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) + 1
spectrum_max_value = 16383
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureHR2000PLUS,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class USB650(SeaBreezeDevice):
model_name = 'USB650'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1014
usb_endpoint_map = EndPointMap(ep_out=0x02, lowspeed_in=0x87, highspeed_in=0x82)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges()
integration_time_min = 3000
integration_time_max = 655350000
integration_time_base = 1000
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) + 1
spectrum_max_value = 4095
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB650,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class QE65000(SeaBreezeDevice):
model_name = 'QE65000'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1018
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (1040, 1044)) # as in seabreeze-3.0.5
integration_time_min = 8000
integration_time_max = 1600000000
integration_time_base = 1000
spectrum_num_pixel = 1280
spectrum_raw_length = (1024 + 256)*2 + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureQE65000,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class USB4000(SeaBreezeDevice):
model_name = 'USB4000'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1022
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((5, 16)), # as in seabreeze-3.0.9
integration_time_min = 10
integration_time_max = 655350000
integration_time_base = 1
spectrum_num_pixel = 3840
spectrum_raw_length = (3840 * 2) + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB4000,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class NIRQUEST512(SeaBreezeDevice):
model_name = 'NIRQUEST512'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1026
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges(), # as in seabreeze-3.0.9
integration_time_min = 1000
integration_time_max = 1600000000
integration_time_base = 1000
spectrum_num_pixel = 512
spectrum_raw_length = (512 * 2) + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureNIRQUEST512,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class NIRQUEST256(SeaBreezeDevice):
model_name = 'NIRQUEST256'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1028
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges(), # as in seabreeze-3.0.9
integration_time_min = 1000
integration_time_max = 1600000000
integration_time_base = 1000
spectrum_num_pixel = 256
spectrum_raw_length = (256 * 2) + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureNIRQUEST256,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class MAYA2000PRO(SeaBreezeDevice):
model_name = 'MAYA2000PRO'
# communication config
transport = (USBTransport, )
usb_product_id = 0x102a
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (2064, 2068))
integration_time_min = 7200
integration_time_max = 65000000
integration_time_base = 1
spectrum_num_pixel = 2304
spectrum_raw_length = (2304 * 2) + 1
spectrum_max_value = 64000
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureMAYA2000PRO,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class MAYA2000(SeaBreezeDevice):
model_name = 'MAYA2000'
# communication config
transport = (USBTransport, )
usb_product_id = 0x102c
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((0, 8), (2072, 2080))
integration_time_min = 15000
integration_time_max = 1600000000
integration_time_base = 1
spectrum_num_pixel = 2304
spectrum_raw_length = (2304 * 2) + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureMAYA2000,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class TORUS(SeaBreezeDevice):
model_name = 'TORUS'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1040
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges()
integration_time_min = 1000
integration_time_max = 655350000
integration_time_base = 1
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) + 1
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureTORUS,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class APEX(SeaBreezeDevice):
model_name = 'APEX'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1044
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (2064, 2068))
integration_time_min = 15000
integration_time_max = 1600000000
integration_time_base = 1
spectrum_num_pixel = 2304
spectrum_raw_length = (2304 * 2) + 1
spectrum_max_value = 64000
trigger_modes = TriggerMode.supported('NORMAL', )
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureAPEX,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class MAYALSL(SeaBreezeDevice):
model_name = 'MAYALSL'
# communication config
transport = (USBTransport, )
usb_product_id = 0x1046
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (2064, 2068))
integration_time_min = 7200
integration_time_max = 65000000
integration_time_base = 1
spectrum_num_pixel = 2304
spectrum_raw_length = (2304 * 2) + 1
spectrum_max_value = 64000
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureMAYALSL,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class JAZ(SeaBreezeDevice):
model_name = 'JAZ'
# communication config
transport = (USBTransport, )
usb_product_id = 0x2000
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82)
usb_protocol = OOIProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24))
integration_time_min = 1000
integration_time_max = 655350000
integration_time_base = 1
spectrum_num_pixel = 2048
spectrum_raw_length = (2048 * 2) # XXX: No Sync byte!
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE')
# features
feature_classes = (
sbf.eeprom.SeaBreezeEEPromFeatureOOI,
sbf.spectrometer.SeaBreezeSpectrometerFeatureJAZ,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class STS(SeaBreezeDevice):
model_name = 'STS'
# communication config
transport = (USBTransport, )
usb_product_id = 0x4000
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81) # XXX: we'll ignore the alternative EPs
usb_protocol = OBPProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges()
integration_time_min = 10
integration_time_max = 85000000
integration_time_base = 1
spectrum_num_pixel = 1024
spectrum_raw_length = (1024 * 2)
spectrum_max_value = 16383
trigger_modes = TriggerMode.supported('OBP_NORMAL', 'OBP_EXTERNAL', 'OBP_INTERNAL')
# features
feature_classes = (
sbf.spectrometer.SeaBreezeSpectrometerFeatureSTS,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class QEPRO(SeaBreezeDevice):
model_name = 'QEPRO'
# communication config
transport = (USBTransport, )
usb_product_id = 0x4004
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81) # XXX: we'll ignore the alternative EPs
usb_protocol = OBPProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (1040, 1044))
integration_time_min = 10000
integration_time_max = 1600000000
integration_time_base = 1
spectrum_num_pixel = 1044
spectrum_raw_length = (1044 * 4) + 32 # XXX: Metadata
spectrum_max_value = (2**18)-1
trigger_modes = TriggerMode.supported('NORMAL', 'LEVEL', 'SYNCHRONIZATION', 'EDGE')
# features
feature_classes = (
sbf.spectrometer.SeaBreezeSpectrometerFeatureQEPRO,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
class VENTANA(SeaBreezeDevice):
model_name = 'VENTANA'
# communication config
transport = (USBTransport, )
usb_product_id = 0x5000
usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x82)
usb_protocol = OBPProtocol
# spectrometer config
dark_pixel_indices = DarkPixelIndices.from_ranges()
integration_time_min = 22000
integration_time_max = 60000000
integration_time_base = 1
spectrum_num_pixel = 1024
spectrum_raw_length = (1024 * 2) # XXX: No Sync byte!
spectrum_max_value = 65535
trigger_modes = TriggerMode.supported('NORMAL', 'LEVEL', 'SYNCHRONIZATION', 'EDGE')
# features
feature_classes = (
sbf.spectrometer.SeaBreezeSpectrometerFeatureVENTANA,
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)