Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/build-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ jobs:
build
pylint
-r src/science_mode_4/requirements.txt
-r examples/requirements.txt
--user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Run linter
run: pylint ./src/science_mode_4
- name: Run linter lib
run: pylint ./src
- name: Run linter examples
run: pylint ./examples
7 changes: 5 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[MAIN]
max-line-length=140
max-attributes=11
max-attributes=15

[DESIGN]
max-statements=100
Expand All @@ -9,4 +9,7 @@ max-statements=100
check-quote-consistency=yes

[MESSAGES CONTROL]
disable=too-few-public-methods
disable=too-few-public-methods

[SIMILARITIES]
min-similarity-lines=50
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "__main__.py",
// "module": "examples.dyscom.example_dyscom_write_csv",
// "program": "src/__main__.py",
"module": "examples.mid_level.example_mid_level",
"justMyCode": false,
// "args": ["COM3"],
"console": "integratedTerminal"
Expand Down
30 changes: 24 additions & 6 deletions HINTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This page describes implementation details.
- Starting point is using a _Device_ object matching attached hardware: _DeviceP24_ or _DeviceI24_
- If you have multiple devices, create multiple _Device_ instances
- With _capabilities_ it is possible to query for available layer
- To create a _Device_ object a _Connection_ object is required, use _SerialConnection_ to connect to a serial port
- To create a _Device_ object, a _Connection_ object is required, use _SerialConnection_ to connect to a serial port
- _Connection_ must be opened and closed
- Call _device.initialize()_ to get a defined state of the device (it stops any active stimulation/measurement)
- _Device_ object has layers to access commands
Expand All @@ -18,8 +18,16 @@ This page describes implementation details.
- Do not mix usage of layers because device has a single internal state, e.g. calling low level _init()_ and afterwards mid level _update()_ will not work

## Device communication
- Each device has an instance of a _PacketBuffer_
- Should be used to read packets from connection
- Handles extraction of packets from byte stream
- Most functions communicating with the device are async functions, because they wait for a matching acknowledge and return values from acknowledge
- If no matching acknowledge or no acknowledge arrives in time, an exception is raised
- The async functions connection buffer handling is always identical:
- Clear buffer
- Send command
- Process incoming data until the expected acknowledge arrives
- More data remains in connection buffer
- Additionally functions with naming schema _send_xxx_ are normal functions not waiting for acknowledge
- The acknowledge needs to handled manually by using _PacketBuffer_ object from device
- _PacketBuffer_ reads data from connection and separates packets from data stream
Expand All @@ -28,7 +36,9 @@ This page describes implementation details.
- Library creates a custom logger, see class _Logger_
- By default some information is logged to console
- Set log level to DEBUG to get more detailed information
- For more performance, disable logger
- `logger().setLevel(logging.DEBUG)`
- For better performance, disable logger
- `logger().disabled = True`

## General layer
- Contains functions to get common information like device serial or firmware version
Expand Down Expand Up @@ -63,19 +73,26 @@ This page describes implementation details.
- Device sends now _DlSendLiveData_ packets with measurement data
- Call _stop()_ to end measurement
- Call _power_module()_ to power off measurement module
- Important: all storage related functions are untested
- IMPORTANT: all storage related functions are untested

# Deviation from Instruction for Use

## Dyscom commands

### Common
- Strings are 1 byte less long (null termination is not an extra byte) in acknowledge packets
- Datetime parameters have a different order

### DL_init
- Init state seems always be UNUSED
- Strings are 1 byte longer than in other commands
- Output data rate depends on init params filter property
- Setting a filter overwrite other settings
- ADS129x register channel 1-4 settings
- ADS129x config register output data rate
- Maybe more register values are changed

### DL_get for type file system status and list of measurement info
- Return never meaningful values, probably not implemented on I24 side

### DL_get_ack for type file by name
- Additional parameter mode (1 byte)
Expand All @@ -98,5 +115,6 @@ This page describes implementation details.
- Block number, 4 byte, block number of DL_send_file

### DL_send_live_data
- Parameters are big endian?
- SignalType for each sample is always 0
- SignalType for each sample is always 0
- Contains always 5 samples, regardless of selected signal types in DL_init
- Fifth sample value seems always be zero
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,8 @@ Python 3.11 or higher
## 0.0.11
- Implemented UsbConnection class
- Alternative for SerialPortConnection, both share the same base class Connection
- Added _PyUSB_ and _libusb-package_ as dependencies
- Added _PyUSB_ and _libusb-package_ as dependencies

## 0.0.12
- Dyscom init
- Added channel settings register
110 changes: 0 additions & 110 deletions __main__.py

This file was deleted.

7 changes: 7 additions & 0 deletions examples/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Init file for utils"""

from .dyscom import *
from .general import *
from .low_level import *
from .mid_level import *
from .utils import *
6 changes: 6 additions & 0 deletions examples/dyscom/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Init file for utils"""

from .example_dyscom_fastplotlib import *
from .example_dyscom_get import *
from .example_dyscom_pyplot import *
from .example_dyscom_write_csv import *
20 changes: 6 additions & 14 deletions examples/dyscom/example_dyscom_fastplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def main():
"""Main function"""

# initialize plot helper the handle plot specific things
plot_helper = FastPlotLibHelper({0: ["BI", "b"], 1: ["EMG1", "r"], 2: ["EMG2", "y"],\
3: ["Breathing", "g"], 4: ["Temperature", "w"]}, 1000)
plot_helper = FastPlotLibHelper({0: ["Channel 1", "b"], 1: ["Channel 2", "r"], 2: ["Channel 3", "y"],\
3: ["Channel 4", "g"]}, 1000)
# flag to indicate
is_window_open: bool = True

Expand Down Expand Up @@ -53,7 +53,7 @@ async def device_communication() -> int:
# call init with lowest sample rate and enable signal types
init_params = DyscomInitParams()
init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1,\
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING, DyscomSignalType.TEMPERATURE]
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING]
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_500_SPS__LP_MODE_250_SPS
init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.LOW_POWER
await dyscom.init(init_params)
Expand Down Expand Up @@ -93,23 +93,15 @@ async def device_communication() -> int:
print(f"SendLiveData status error {sld.samples}")
break

# these samples match signal types from dl_init command
plot_helper.append_value(0, sld.samples[0].value)
plot_helper.append_value(1, sld.samples[1].value)
plot_helper.append_value(2, sld.samples[2].value)
plot_helper.append_value(3, sld.samples[3].value)
plot_helper.append_value(4, sld.samples[4].value)

# signal type seems to be always 0, so we can't use it
# for s in sld.samples:
# ph.append_value(int(s.signal_type), s.value)
for x in range(4):
plot_helper.append_value(x, sld.samples[x].value)

plot_helper.update()
else:
# print(f"Live data acknowledges per iteration {live_data_counter}")
break

await asyncio.sleep(0.01)
await asyncio.sleep(0.001)

# stop measurement
await dyscom.stop()
Expand Down
8 changes: 8 additions & 0 deletions examples/dyscom/example_dyscom_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ async def main() -> int:
file_system_status = await dyscom.get_file_system_status()
print(file_system_status)

####
calibration_filename = f"rehaingest_{device_id}.cal"
# get calibration file info
await dyscom.get_file_info(calibration_filename)
# get calibration file -> does not work
# there should be DL_Send_File commands afterwards
await dyscom.get_file_by_name(calibration_filename)

# close serial port connection
connection.close()
return 0
Expand Down
5 changes: 2 additions & 3 deletions examples/dyscom/example_dyscom_pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ async def main() -> int:
"""Main function"""

plot_helper = PyPlotHelper({0: ["BI", "blue"]}, 250)

# disable logger to increase performance
logger().disabled = True

Expand All @@ -42,7 +41,7 @@ async def main() -> int:
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
# call init with lowest sample rate (because of performance issues with plotting values)
init_params = DyscomInitParams()
init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1]
init_params.signal_type = [DyscomSignalType.BI]
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_500_SPS__LP_MODE_250_SPS
init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.LOW_POWER
await dyscom.init(init_params)
Expand Down Expand Up @@ -79,7 +78,7 @@ async def main() -> int:
# print(f"Live data acknowledges per iteration {live_data_counter}")
break

await asyncio.sleep(0.01)
await asyncio.sleep(0.001)

# stop measurement
await dyscom.stop()
Expand Down
9 changes: 4 additions & 5 deletions examples/dyscom/example_dyscom_write_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from science_mode_4.dyscom.ads129x.ads129x_config_register_1 import Ads129xOutputDataRate, Ads129xPowerMode
from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode
from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData
from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType,\
from science_mode_4.dyscom.dyscom_types import DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType,\
DyscomPowerModuleType, DyscomSignalType
from science_mode_4.protocol.types import ResultAndError
from science_mode_4.utils.logger import logger
Expand All @@ -22,7 +22,7 @@
def main():
"""Main function"""

csv_helper = CsvHelper("values.csv", ["package_nr", "bi", "emg_1", "emg_2", "breathing", "temperature", "time_delta"])
csv_helper = CsvHelper("values.csv", ["package_nr", "Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "time_delta"])
csv_helper.start()

async def device_communication() -> int:
Expand Down Expand Up @@ -51,9 +51,8 @@ async def device_communication() -> int:
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
# call init with 4k sample rate and enable signal types
init_params = DyscomInitParams()
init_params.filter = DyscomFilterType.PREDEFINED_FILTER_3
init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1,\
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING, DyscomSignalType.TEMPERATURE]
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING]
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_4_KSPS__LP_MODE_2_KSPS
init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.HIGH_RESOLUTION
await dyscom.init(init_params)
Expand Down Expand Up @@ -101,7 +100,7 @@ async def device_communication() -> int:
# print(f"Live data acknowledges per iteration {live_data_counter}")
break

# await asyncio.sleep(0.01)
# await asyncio.sleep(0.001)

# print stats
end_time = timer()
Expand Down
3 changes: 3 additions & 0 deletions examples/general/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Init file for utils"""

from .example_general import *
4 changes: 4 additions & 0 deletions examples/low_level/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Init file for utils"""

from .example_low_level import *
from .example_low_level_plot import *
4 changes: 4 additions & 0 deletions examples/mid_level/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Init file for utils"""

from .example_mid_level import *
from .example_mid_level_simple import *
Loading