Contributing Guide
If you want to contribute to seabreeze, please create a pull request on github. Documentation improvements are very welcome! Extending pyseabreeze is quite easy. Look at the examples below.
Tip
also feel free to create a PR improving the documentation if anything is unclear <3
Adding a new spectrometer
To add a new spectrometer with basic spectrometer functionality to pyseabreeze you first need to find a datasheet pdf of your spectrometer to get the necessary information and then you need to do two things:
Add a new device class to seabreeze/pyseabreeze/devices.py:
class SPARK(SeaBreezeDevice):
model_name = 'SPARK'
# communication config
transport = (USBTransport, )
usb_product_id = 0x4200
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) + 64 # XXX: Metadata
spectrum_max_value = 16383
trigger_modes = TriggerMode.supported('OBP_NORMAL', 'OBP_EXTERNAL', 'OBP_INTERNAL')
# features
feature_classes = (
sbf.spectrometer.SeaBreezeSpectrometerFeatureSPARK, # need to implement this
sbf.rawusb.SeaBreezeRawUSBBusAccessFeature,
)
And implement the specialized spectrometer feature class in seabreeze/pyseabreeze/features/spectrometer.py
# we're lucky here, and the Spark just uses the default implementation
# of the OBP Protocol. It's possible that for other spectrometers you
# need to override the `get_intensities` method or others dependent on how
# much they deviate from the default implementation
# (refer to the spectrometer's datasheet for this)
#
class SeaBreezeSpectrometerFeatureSPARK(SeaBreezeSpectrometerFeatureOBP):
pass
That’s it. These few lines add basic Spark support to pyseabreeze.
Testing spectrometer models
You can help too, by running the provided tests for your spectrometer model and posting the results in a github issue. To do this git clone the repository and:
pip install 'pytest-runner' 'pytest<5'
python setup.py pytest
running pytest
running egg_info
writing requirements to src/seabreeze.egg-info/requires.txt
writing src/seabreeze.egg-info/PKG-INFO
writing top-level names to src/seabreeze.egg-info/top_level.txt
writing dependency_links to src/seabreeze.egg-info/dependency_links.txt
writing entry points to src/seabreeze.egg-info/entry_points.txt
reading manifest template 'MANIFEST.in'
writing manifest file 'src/seabreeze.egg-info/SOURCES.txt'
running build_ext
skipping 'src/seabreeze/cseabreeze/c_seabreeze_wrapper.cpp' Cython extension (up-to-date)
copying build/lib.linux-x86_64-2.7/seabreeze/cseabreeze/_wrapper.so -> src/seabreeze/cseabreeze
========================================== test session starts ==========================================
platform linux2 -- Python 2.7.15+, pytest-4.6.5, py-1.8.0, pluggy-0.12.0 -- /python
cachedir: .pytest_cache
rootdir: /home/poehlmann/Development/python-seabreeze, inifile: pytest.ini
collected 23 items
tests/test_backends.py::test_backend_features_interface PASSED [ 4%]
tests/test_backends.py::test_cseabreeze_seabreezeapi PASSED [ 8%]
tests/test_backends.py::test_pyseabreeze_seabreezeapi PASSED [ 13%]
tests/test_spectrometers.py::test_read_model[cseabreeze:SPARK:00061] PASSED [ 17%]
tests/test_spectrometers.py::test_read_model[pyseabreeze:SPARK:00061] PASSED [ 21%]
tests/test_spectrometers.py::test_read_serial_number[cseabreeze:SPARK:00061] PASSED [ 26%]
tests/test_spectrometers.py::test_read_serial_number[pyseabreeze:SPARK:00061] PASSED [ 30%]
tests/test_spectrometers.py::test_read_intensities[cseabreeze:SPARK:00061] PASSED [ 34%]
tests/test_spectrometers.py::test_read_intensities[pyseabreeze:SPARK:00061] PASSED [ 39%]
tests/test_spectrometers.py::test_read_wavelengths[cseabreeze:SPARK:00061] PASSED [ 43%]
tests/test_spectrometers.py::test_read_wavelengths[pyseabreeze:SPARK:00061] PASSED [ 47%]
tests/test_spectrometers.py::test_read_spectrum[cseabreeze:SPARK:00061] PASSED [ 52%]
tests/test_spectrometers.py::test_read_spectrum[pyseabreeze:SPARK:00061] PASSED [ 56%]
tests/test_spectrometers.py::test_max_intensity[cseabreeze:SPARK:00061] PASSED [ 60%]
tests/test_spectrometers.py::test_max_intensity[pyseabreeze:SPARK:00061] PASSED [ 65%]
tests/test_spectrometers.py::test_integration_time_limits[cseabreeze:SPARK:00061] PASSED [ 69%]
tests/test_spectrometers.py::test_integration_time_limits[pyseabreeze:SPARK:00061] PASSED [ 73%]
tests/test_spectrometers.py::test_integration_time[cseabreeze:SPARK:00061] PASSED [ 78%]
tests/test_spectrometers.py::test_integration_time[pyseabreeze:SPARK:00061] PASSED [ 82%]
tests/test_spectrometers.py::test_trigger_mode[cseabreeze:SPARK:00061] PASSED [ 86%]
tests/test_spectrometers.py::test_trigger_mode[pyseabreeze:SPARK:00061] FAILED [ 91%]
tests/test_spectrometers.py::test_cant_find_serial[cseabreeze] PASSED [ 95%]
tests/test_spectrometers.py::test_cant_find_serial[pyseabreeze] PASSED [100%]
=============================================== FAILURES ================================================
______________________________ test_trigger_mode[pyseabreeze:SPARK:00061] _______________________________
backendlified_serial = '00061'
def test_trigger_mode(backendlified_serial):
devices = list(list_devices())
if len(devices) == 0:
pytest.skip("no supported device connected")
exc = Spectrometer._backend.SeaBreezeError
spec = Spectrometer.from_serial_number(backendlified_serial)
with pytest.raises(exc):
spec.trigger_mode(0xF0) # <- should be unsupported for all specs
> spec.trigger_mode(0x00) # <- normal mode
tests/test_spectrometers.py:177:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
src/seabreeze/spectrometers.py:269: in trigger_mode
self._dev.f.spectrometer.set_trigger_mode(mode)
src/seabreeze/pyseabreeze/features/spectrometer.py:298: in set_trigger_mode
self.protocol.send(0x00110110, mode)
src/seabreeze/pyseabreeze/protocol.py:256: in send
remaining_bytes, checksum_type = self._check_incoming_message_header(response[:44])
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <seabreeze.pyseabreeze.protocol.OBPProtocol object at 0x7f7ae94e4690>
header = array('B', [193, 192, 0, 17, 11, ...])
def _check_incoming_message_header(self, header):
"""check the incoming message header
Parameters
----------
header : `str`
a obp header of length 44
Returns
-------
bytes_and_checksum_type : tuple[`int`, `int`]
bytes_remaining after the header (returns 20 for a 64 byte message)
checksum_type only supports self.OBP.CHECKSUM_TYPE_MD5 for now
"""
if len(header) != 44:
raise SeaBreezeError("header has wrong length! len(header): %d" % len(header))
data = struct.unpack(self.OBP.HEADER_FMT, header)
if data[0] != self.OBP.HEADER_START_BYTES:
raise SeaBreezeError('Header start_bytes wrong: "%d"' % data[0])
if data[1] != self.OBP.HEADER_PROTOCOL_VERSION:
raise SeaBreezeError('Header protocol version wrong: %d' % data[1])
flags = data[2]
if flags == 0:
pass
if flags & self.OBP.FLAG_RESPONSE_TO_REQUEST:
pass # TODO: propagate?
if flags & self.OBP.FLAG_ACK:
pass # TODO: propagate?
if flags & self.OBP.FLAG_REQUEST_ACK:
pass # TODO: only the host should be able to set this?
if (flags & self.OBP.FLAG_NACK) or (flags & self.OBP.FLAG_HW_EXCEPTION):
error = data[3]
if error != 0: # != SUCCESS
> raise SeaBreezeError(self.OBP.ERROR_CODES[error])
E SeaBreezeError: Unknown message type
src/seabreeze/pyseabreeze/protocol.py:424: SeaBreezeError
=========================================== warnings summary ============================================
tests/test_spectrometers.py::test_read_intensities[pyseabreeze:SPARK:00061]
tests/test_spectrometers.py::test_read_spectrum[pyseabreeze:SPARK:00061]
/home/poehlmann/Development/python-seabreeze/src/seabreeze/pyseabreeze/features/spectrometer.py:345:
DeprecationWarning: The binary mode of fromstring is deprecated, as it behaves surprisingly on
unicode inputs. Use frombuffer instead return numpy.fromstring(datastring, dtype=numpy.uint8)
-- Docs: https://docs.pytest.org/en/latest/warnings.html
=========================== 1 failed, 22 passed, 2 warnings in 34.78 seconds ============================
Exception AttributeError: "'NoneType' object has no attribute '_ctx'" in
<bound method SPARK.__del__ of <SeaBreezeDevice SPARK:00061>> ignored