Skip to content

Commit f1fc5e2

Browse files
author
Marc Hofmann
committed
wip
1 parent 04ad412 commit f1fc5e2

4 files changed

Lines changed: 103 additions & 19 deletions

File tree

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"type": "debugpy",
1010
"request": "launch",
1111
"program": "__main__.py",
12-
// "module": "examples.dyscom.example_dyscom_get",
12+
// "module": "examples.dyscom.example_dyscom_write_csv",
1313
"justMyCode": false,
1414
// "args": ["COM3"],
1515
"console": "integratedTerminal"

__main__.py

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
"""Test program how to use library without installing the library,
22
DO NOT USE THIS FILE, USE EXAMPLES INSTEAD"""
33

4+
from timeit import default_timer as timer
5+
46
import logging
57
import sys
68
import asyncio
79

810
from science_mode_4.device_i24 import DeviceI24
911
from science_mode_4.device_p24 import DeviceP24
12+
from science_mode_4.dyscom.ads129x.ads129x_config_register_1 import Ads129xOutputDataRate, Ads129xPowerMode
13+
from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode
14+
from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData
15+
from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType, DyscomPowerModuleType, DyscomSignalType
16+
from science_mode_4.protocol.commands import Commands
17+
from science_mode_4.protocol.types import ResultAndError
1018
from science_mode_4.utils import logger
1119
from science_mode_4.utils.serial_port_connection import SerialPortConnection
1220
from science_mode_4.utils.usb_connection import UsbConnection
@@ -16,12 +24,8 @@
1624
async def main() -> int:
1725
"""Main function"""
1826

19-
# Serial
20-
# 2025-04-22 15:38 - DEBUG - Outgoing data, length: 12, bytes: F0 81 55 81 59 81 4E 81 0C 04 3E 0F
21-
# 2025-04-22 15:38 - DEBUG - Incoming data, length: 13, bytes: F0 81 55 81 58 81 D1 81 0A 04 43 00 0F
22-
# USB
23-
# 2025-04-22 15:45 - DEBUG - Outgoing data, length: 12, bytes: F0 81 55 81 59 81 4E 81 0C 04 3E 0F
24-
# 2025-04-22 15:45 - DEBUG - Incoming data, length: 13, bytes: F0 81 55 81 58 81 D1 81 0A 04 43 00 0F
27+
logger().disabled = True
28+
2529
logger().setLevel(logging.DEBUG)
2630
# devices = SerialPortConnection.list_science_mode_device_ports()
2731
# connection = SerialPortConnection(devices[0].device)
@@ -32,10 +36,69 @@ async def main() -> int:
3236

3337
device = DeviceI24(connection)
3438
await device.initialize()
35-
general = device.get_layer_general()
36-
print(f"Device id: {general.device_id}")
37-
print(f"Firmware version: {general.firmware_version}")
38-
print(f"Science mode version: {general.science_mode_version}")
39+
# get dyscom layer to call dyscom level commands
40+
dyscom = device.get_layer_dyscom()
41+
42+
# call enable measurement power module for measurement
43+
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
44+
# call init with 4k sample rate and enable signal types
45+
init_params = DyscomInitParams()
46+
init_params.filter = DyscomFilterType.PREDEFINED_FILTER_2
47+
init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1,\
48+
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING, DyscomSignalType.TEMPERATURE]
49+
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_4_KSPS__LP_MODE_2_KSPS
50+
init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.HIGH_RESOLUTION
51+
await dyscom.init(init_params)
52+
53+
# start dyscom measurement
54+
await dyscom.start()
55+
56+
start_time = timer()
57+
total_count = 0
58+
59+
# loop for some time
60+
for x in range(1000):
61+
# check operation mode from time to time, this function is not waiting for response
62+
# so we have to handle it by ourself later
63+
if x % 100 == 0:
64+
dyscom.send_get_operation_mode()
65+
66+
live_data_counter = 0
67+
while True:
68+
# process all available packages
69+
ack = dyscom.packet_buffer.get_packet_from_buffer(live_data_counter == 0)
70+
if ack:
71+
# because there are multiple get commands, we need to additionally check kind,
72+
# which is always associated DyscomGetType
73+
if ack.command == Commands.DL_GET_ACK and ack.kind == DyscomGetType.OPERATION_MODE:
74+
om_ack: PacketDyscomGetAckOperationMode = ack
75+
print(f"Operation mode {om_ack.operation_mode.name}")
76+
# check if measurement is still active
77+
if om_ack.result_error != ResultAndError.NO_ERROR:
78+
break
79+
elif ack.command == Commands.DL_SEND_LIVE_DATA:
80+
live_data_counter += 1
81+
total_count += 1
82+
83+
sld: PacketDyscomSendLiveData = ack
84+
if sld.status_error:
85+
print(f"SendLiveData status error {sld.samples}")
86+
break
87+
88+
else:
89+
# print(f"Live data acknowledges per iteration {live_data_counter}")
90+
break
91+
92+
# await asyncio.sleep(0.01)
93+
94+
# print stats
95+
end_time = timer()
96+
print(f"Samples: {total_count}, duration: {end_time - start_time}, sample rate: {total_count / (end_time - start_time)}")
97+
98+
# stop measurement
99+
await dyscom.stop()
100+
# turn power module off
101+
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_OFF)
39102

40103
connection.close()
41104

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ license-files = [
2020
"LICENSE"
2121
]
2222
dependencies = [
23-
"pyserial >= 3.5",
23+
"pyserial",
24+
"pyusb"
2425
]
2526

2627
[project.urls]

src/science_mode_4/utils/usb_connection.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(self, device: usb.core.Device):
3232
self._device = device
3333
self._out_endpoint: usb.core.Endpoint = None
3434
self._in_endpoint: usb.core.Endpoint = None
35+
self._in_endpoint_buffer_size = 128 * 64
3536
self._is_open = False
3637

3738

@@ -40,16 +41,18 @@ def open(self):
4041
self._device.set_configuration()
4142
# get an endpoint instance
4243
cfg = self._device.get_active_configuration()
43-
intf = cfg[(1,0)]
44+
# Interface 0: CDC Communication
45+
# Interface 1: CDC Data
46+
interface: usb.core.Interface = cfg[(1,0)]
4447

4548
# match the first OUT endpoint
4649
self._out_endpoint = usb.util.find_descriptor(
47-
intf,
48-
custom_match = lambda e: (print(e) or usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT))
50+
interface,
51+
custom_match = lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)
4952
# match the first IN endpoint
5053
self._in_endpoint = usb.util.find_descriptor(
51-
intf,
52-
custom_match = lambda e: (print(e) or usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN))
54+
interface,
55+
custom_match = lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)
5356

5457
self._is_open = True
5558

@@ -68,8 +71,25 @@ def write(self, data: bytes):
6871

6972

7073
def clear_buffer(self):
71-
pass
74+
# there is no way to clear the buffer from in endpoint, so
75+
# read all data and discard it
76+
self._read_intern()
7277

7378

7479
def _read_intern(self) -> bytes:
75-
return bytes(self._in_endpoint.read(self._in_endpoint.wMaxPacketSize))
80+
data = bytes()
81+
try:
82+
while True:
83+
# Read up to endpoint's max packet size (64 bytes in this case)
84+
tmp = self._in_endpoint.read(self._in_endpoint_buffer_size)
85+
data += bytes(tmp)
86+
if len(tmp) < self._in_endpoint_buffer_size:
87+
break
88+
except usb.core.USBError as e:
89+
if e.errno == 10060:
90+
# Timeout error (no data available)
91+
pass
92+
else:
93+
raise
94+
95+
return data

0 commit comments

Comments
 (0)