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
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": "src/__main__.py",
"module": "examples.dyscom.example_dyscom_send_file",
"program": "src/__main__.py",
// "module": "examples.dyscom.example_dyscom_send_file",
"justMyCode": false,
// "args": ["COM3"],
"console": "integratedTerminal"
Expand Down
24 changes: 1 addition & 23 deletions HINTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,10 @@ This page describes implementation details.
- https://www.reddit.com/r/macOSVMs/comments/1gb8egp/macos_sonoma_virtualbox_bootloop_afterduring/?rdt=48615


# Deviation from Instruction for Use

## Dyscom commands

### Common
- Datetime parameters have a different order
## I24 protocol hints

### 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
Expand All @@ -121,26 +115,10 @@ This page describes implementation details.
### 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)
- Undefined = 0
- Multiblock = 1
- Singleblock = 2

### DL_get for type battery
- Dl get types (table 23)
- 0 -> Battery (was Unused)

### DL_get_ack for type battery
- Energy state, 1 byte, is a flag, bit 1: cable connected, bit 2: device is loading
- Percentage, 1 byte, [0, 100] in percent
- Temperature, 1 byte, [-128, 127] in degrees
- Current, 4 bytes, [-327675, 327675] in milliampere
- Voltage, 4 bytes, [0, 65535] in millivolt

### DL_send_file_ack
- Block number, 4 byte, block number of DL_send_file

### DL_send_live_data
- SignalType for each sample is always 0
- Contains always 5 samples, regardless of selected signal types in DL_init
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Introduction

Python implementation of HasomedScience ScienceMode 4 protocol for P24 (https://github.com/ScienceMode/ScienceMode4_P24) and I24 (https://github.com/ScienceMode/ScienceMode4_I24) devices. To use this library see section [Installation](#installation).
Pure Python implementation of HasomedScience ScienceMode 4 protocol for P24 (https://github.com/ScienceMode/ScienceMode4_P24) and I24 (https://github.com/ScienceMode/ScienceMode4_I24) devices. To use this library see section [Installation](#installation). Library and examples are tested under Windows, Linux and MacOS.

## Requirements

Expand Down Expand Up @@ -125,4 +125,7 @@ Python 3.11 or higher
- Fixed error with PacketLowLevelChannelConfigAck result

## 0.0.17
- Added sample that demonstrates how to read measurement data files from I24 devices
- Added sample that demonstrates how to read measurement data files from I24 devices

## 0.0.18
- More documentation
12 changes: 11 additions & 1 deletion examples/dyscom/example_dyscom_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,20 @@ async def main() -> int:
file_system_status = await dyscom.get_file_system_status()
print(file_system_status)

####
# this is the default calibration filename
calibration_filename = f"rehaingest_{device_id}.cal"
####
# get calibration file content
calibration_content = await dyscom.get_file_content(calibration_filename)
print(f"Calibration content length: {len(calibration_content)}")
# I24 devices calculates checksum from first 2 bytes of the file content
calculated_checksum = (calibration_content[0] << 8) | calibration_content[1]
print(f"Calculated calibration content checksum: {calculated_checksum}")

####
# get file info for calibration file
file_info = await dyscom.get_file_info(calibration_filename)
print(f"Calibration file info checksum: {file_info.checksum}")

# close serial port connection
connection.close()
Expand Down
18 changes: 17 additions & 1 deletion src/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from science_mode_4.device_i24 import DeviceI24
from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomInitFlag, DyscomInitParams, DyscomPowerModulePowerType,\
DyscomPowerModuleType, DyscomSignalType
DyscomPowerModuleType, DyscomSignalType, DyscomSysType
from science_mode_4.utils.serial_port_connection import SerialPortConnection


Expand All @@ -24,8 +24,24 @@ async def main() -> int:
# to have a defined state
await device.initialize()


# get dyscom layer to call low level commands
dyscom = device.get_layer_dyscom()
device_id = await dyscom.get_device_id()

####
calibration_filename = f"rehaingest_{device_id}.cal"
calibration_content = await dyscom.get_file_content(calibration_filename)
print(f"Calibration content length: {len(calibration_content)}")
calculated_checksum = (calibration_content[0] << 8) | calibration_content[1]
print(f"Calculated calibration content checksum: {calculated_checksum}")

####
file_info = await dyscom.get_file_info(calibration_filename)
print(f"Calibration file info checksum: {file_info.checksum}")

s_y_s = await dyscom.sys(DyscomSysType.DEVICE_STORAGE)
print(f"Sys {s_y_s.state.name}")

# call enable measurement power module and memory card for measurement
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
Expand Down
3 changes: 2 additions & 1 deletion src/science_mode_4/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ async def initialize(self):
if DeviceCapability.DYSCOM in self._capabilities:
# get operation mode to see if dyscom measurement is running
operation_mode = await self.get_layer_dyscom().get_operation_mode()
if operation_mode in [DyscomGetOperationModeType.LIVE_MEASURING_PRE,
if operation_mode in [DyscomGetOperationModeType.UNDEFINED,
DyscomGetOperationModeType.LIVE_MEASURING_PRE,
DyscomGetOperationModeType.LIVE_MEASURING,
DyscomGetOperationModeType.RECORD_PRE,
DyscomGetOperationModeType.RECORD,
Expand Down
2 changes: 1 addition & 1 deletion src/science_mode_4/dyscom/ads129x/ads129x.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Ads129x:
channel_4_setting_register = Ads129xChannelSettingsRegister() # CH4SET

positive_signal_derivation_register = 0x02 # RLD_SENSP
negative_signal_derivation_register = 0xEA # RLD_SENSN
negative_signal_derivation_register = 0x02 # RLD_SENSN
positive_signal_lead_off_detection_register = 0 # LOFF_SENSP
negative_signal_lead_off_detection_register = 0 # LOFF_SENSN

Expand Down
6 changes: 4 additions & 2 deletions src/science_mode_4/dyscom/dyscom_get_file_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ def get_data(self) -> bytes:
bb = ByteBuilder()
bb.append_bytes(super().get_data())
bb.append_bytes(DyscomHelper.str_to_bytes(self._filename, 128))
# maybe more parameters are necessary here
# file_size, file_checksum
# file size
bb.append_value(0, 4, True)
# checksum
bb.append_value(0, 2, True)
return bb.get_bytes()


Expand Down
10 changes: 8 additions & 2 deletions src/science_mode_4/dyscom/dyscom_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from science_mode_4.layer import Layer
from science_mode_4.protocol.commands import Commands
from science_mode_4.utils.logger import logger
from .dyscom_types import DyscomFrequencyOut, DyscomGetOperationModeType, DyscomPowerModuleType,\
DyscomPowerModulePowerType, DyscomSignalType, DyscomSysType
from .dyscom_types import DyscomFrequencyOut, DyscomGetOperationModeType, DyscomInitState, DyscomPowerModuleType,\
DyscomPowerModulePowerType, DyscomSignalType, DyscomSysState, DyscomSysType
from .dyscom_init import DyscomInitResult, PacketDyscomInit, PacketDyscomInitAck, DyscomInitParams
from .dyscom_get_file_system_status import PacketDyscomGetFileSystemStatus, PacketDyscomGetAckFileSystemStatus,\
DyscomGetFileSystemStatusResult
Expand Down Expand Up @@ -36,6 +36,9 @@ async def init(self, params: DyscomInitParams) -> DyscomInitResult:
p = PacketDyscomInit(params)
ack: PacketDyscomInitAck = await self.send_packet_and_wait(p)
self._check_result_error(ack.result_error, "DyscomInit")
if ack.init_state not in [DyscomInitState.UNUSED, DyscomInitState.SUCCESS]:
raise ValueError(f"Dyscom error init {ack.init_state.name}")

logger().info("Dyscom init, measurement_file_id: %s, state: %s, frequency: %s",\
ack.measurement_file_id, ack.init_state.name, ack.frequency_out.name)
return DyscomInitResult(ack.register_map_ads129x, ack.measurement_file_id, ack.init_state, ack.frequency_out)
Expand Down Expand Up @@ -139,6 +142,9 @@ async def sys(self, sys_type: DyscomSysType, filename: str = "") -> DyscomSysRes
p = PacketDyscomSys(sys_type, filename)
ack: PacketDyscomSysAck = await self.send_packet_and_wait(p)
self._check_result_error(ack.result_error, "DyscomSys")
if ack.state not in [DyscomSysState.SUCCESSFUL]:
raise ValueError(f"Dyscom error sys {ack.state.name}")

logger().info("Dyscom sys, type: %s, state: %s, filename: %s", ack.sys_type.name, ack.state.name, ack.filename)
return DyscomSysResult(ack.sys_type, ack.state, ack.filename)

Expand Down