Skip to content

Commit 919e738

Browse files
authored
Dev (#15)
* new example * v=0.4.3 * inserted n_el 16 and 32 save dicts * updated example scripts * new example for converting measurement directorys to csv * first implementation of npz to csv * corrected plot angle * fixed position error of measurement * 0.4.4 * added logo * deleted section * new release of 0.4.2 * added 0.5.0 * linting * appended ScioSpecMeasurementConfig * update examples * linting * flake * Update README.md * updated docstring and resolved import error * linting * deleted old docs * added excitation frequency * Inserted exc_freq to dependent files. * v==0.5.2 * prep for pytest publish * pctr * updated picture * v==0.6.0 * changed logo * Update README.md * v==0.6.1 * Update README.md * angle correction * inserted daytime and temperature * docstring and data format * temperature visualization * linting * new visualization and black linting==23.1.0 * updated black==23.1.0 * pypitest==0.6.1.4 * pypi test == 0.6.1.5 * pypi test == 0.6.1.5 * v==0.6.2 * implemented visualization, not added to init * linting * linting#2 * linting #3 * meshing * inserted citation cff * added citation cff * zenodo doi * inserted absolute valued potential data * Implemented mesh generation * gitignore .vscode * inserted raw string for fixing error. * changed function names and inserted v computation * linting * docstring and function descriptions. * linting * skipping first measured value * changed absolute label at potential matrix * init. mesh for import * first try to use pyvista * pyvisa changes * changed prepare for ml routine * merge dev to 3d * updated function * added file in ignore * modified configurations and added an example for 32 electrodes measurement * modified burst count * deleted prototyping functions. * ignored .vsc settings * updated reqs * fixed npz to csv conversion error * changed names of example measurements * deleted unspecified measurement example. * Adjusting the configurations * Ready for test measurements using 32 electrodes. * Inserted Issue marker in npz_to_csv.npz * working example measurement scripts * updated setup_m * linting and ignore * linting and updates * new example script * pypi test release * new release v==0.6.4 * updated readme * Updated Readme TDB * preperations for Ethernet connection * new pytest version and typos * linting * updated and undo some changes from pytest 0.6.4.7 * release==0.6.4.8 * channel group repeated bug resolved.
1 parent 2220be8 commit 919e738

9 files changed

Lines changed: 260 additions & 22 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ tst_smpls
1616
examples/npz_to_csv_enderstat.py
1717
examples/npz_to_csv.py
1818
examples/measuremet_16/*
19-
examples/measuremet_32/*
19+
examples/measuremet_32/*
20+
sciopy/eth_*
21+
Driver

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ All requirements are provided inside the `requirements.txt`. To install them nav
1111

1212
## Run Example Script
1313

14-
For a single measurement, you can simply run the `measurement.py` script using the command:
14+
For a single measurement, you can simply run the `example` scripts using the command:
1515

1616
python measurement_n_el_16.py
1717

@@ -21,10 +21,27 @@ measurement with a burst count of 10. For a successful measurement, you have to
2121
The second provided example script `prep_data_for_ml.py` can be used for the conversion of a finished measurement.
2222
This script creates a new folder with the ending `_prepared` and puts together the potential values and object positions for all measurements. This could be useful for later application of machine learning.
2323

24+
## Explanation of stored files (.npz)
25+
26+
27+
- `potential matrix` (e.g. variable P) is a 16x16 matrix (n_el=16). If you visualize it using `from sciopy import plot_potential_matrix` you can recognize the used excitation pattern or if an electrode is a defect.
28+
- `p_with_exc` is the matrix P with the excitation electrodes
29+
- `p_without_exc` is the matrix P without the excitation electrodes
30+
- `abs_p_norm_without_ext` is the matrix, normalized between (I think) 0-1 without the excitation electrodes.
31+
- `v_with_ext` is the computed voltage from the potential values containing the excitation electrodes
32+
- `v_without_ext` is the computed voltage from the potential values without the excitation electrodes
33+
- `abs_v_norm_without_ext` is the voltage data, normalized between 0-1 without the excitation electrodes.
34+
- `config` contains some information regarding the measurement procedure.
2435

2536
## TBD
2637

27-
- Measurement using 48, 64 electrodes.
38+
- [ ] Communication using LAN connection.
39+
- [ ] Measurements using 48, 64 electrodes.
40+
- [ ] Adjacent and opposite injection pattern combination.
41+
- [x] Measurements using 32 electrodes.
42+
- [x] Parsing measurements of 32 electrodes.
43+
- [x] Adjacent and opposite drive patterns for 32 electrodes.
44+
- [x] Measurement using 16 electrodes in opposite and adjacent injection pattern.
2845

2946
## Contact
3047

examples/custom_measurement.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import numpy as np
2+
from datetime import datetime
3+
4+
from sciopy import (
5+
SystemMessageCallback,
6+
set_measurement_config,
7+
connect_COM_port,
8+
StartStopMeasurement,
9+
reshape_full_message_in_bursts,
10+
del_hex_in_list,
11+
split_bursts_in_frames,
12+
)
13+
14+
from sciopy.sciopy_dataclasses import ScioSpecMeasurementSetup
15+
16+
s_path = ""
17+
files_offset = 0
18+
19+
sciospec_measurement_setup = ScioSpecMeasurementSetup(
20+
burst_count=1,
21+
total_meas_num=10,
22+
n_el=16,
23+
channel_group=[1],
24+
exc_freq=10_000,
25+
framerate=5,
26+
amplitude=10,
27+
inj_skip=0,
28+
gain=1,
29+
adc_range=1,
30+
notes="None",
31+
configured=False,
32+
)
33+
34+
# Connect ScioSpec device
35+
COM_ScioSpec = connect_COM_port(port="/dev/ttyACM0")
36+
37+
# Send configuration
38+
set_measurement_config(serial=COM_ScioSpec, ssms=sciospec_measurement_setup)
39+
40+
# Read out system callback
41+
SystemMessageCallback(COM_ScioSpec, prnt_msg=True)
42+
43+
# Start and stop single measurement
44+
measurement_data_hex = StartStopMeasurement(COM_ScioSpec)
45+
# Delete hex in mesured buffer
46+
measurement_data = del_hex_in_list(measurement_data_hex)
47+
# Reshape the full mesaurement buffer. Depending on number of electrodes
48+
split_measurement_data = reshape_full_message_in_bursts(
49+
measurement_data, sciospec_measurement_setup
50+
)
51+
measurement_data = split_bursts_in_frames(
52+
split_measurement_data, sciospec_measurement_setup
53+
)
54+
55+
# Set to "True" to save single measurement
56+
save = True
57+
58+
if save:
59+
for bursts in measurement_data:
60+
np.savez(
61+
s_path + "sample_{0:06d}.npz".format(files_offset),
62+
config=sciospec_measurement_setup,
63+
data=bursts,
64+
)
65+
files_offset += 1
66+
SystemMessageCallback(COM_ScioSpec, prnt_msg=False)

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
matplotlib==3.3.3
2-
numpy==1.22.3
3-
pandas==1.2.4
1+
matplotlib==3.7.1
2+
numpy==1.24.3
3+
pandas==2.0.1
44
pyeit==1.2.4
55
pyserial==3.5
66
setuptools==67.7.2
7-
tqdm==4.60.0
7+
tqdm==4.65.0

sciopy/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,12 @@
6969
DisableLED_AutoMode,
7070
EnableLED_AutoMode,
7171
PowerPlugDetect,
72-
GetDevideInfo,
72+
GetDeviceInfo,
7373
GetFirmwareIDs,
7474
)
7575

7676
from .configurations import (
77+
set_measurement_config,
7778
conf_n_el_16_adjacent,
7879
conf_n_el_32_adjacent,
7980
conf_n_el_16_opposite,
@@ -143,9 +144,10 @@
143144
"DisableLED_AutoMode",
144145
"EnableLED_AutoMode",
145146
"PowerPlugDetect",
146-
"GetDevideInfo",
147+
"GetDeviceInfo",
147148
"GetFirmwareIDs",
148149
# .configurations
150+
"set_measurement_config",
149151
"conf_n_el_16_adjacent",
150152
"conf_n_el_32_adjacent",
151153
"conf_n_el_16_opposite",

sciopy/configurations.py

Lines changed: 144 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,141 @@
11
# TBD: https://stackoverflow.com/questions/3898572/what-are-the-most-common-python-docstring-formats
2-
from .sciopy_dataclasses import ScioSpecMeasurementConfig
2+
from .sciopy_dataclasses import (
3+
ScioSpecMeasurementConfig,
4+
ScioSpecMeasurementSetup,
5+
)
36
from datetime import datetime
7+
import numpy as np
8+
from typing import Union
9+
import struct
10+
11+
12+
def set_measurement_config(serial, ssms: ScioSpecMeasurementSetup) -> None:
13+
"""
14+
set_measurement_config sets the ScioSpec device configuration depending on the ssms configuration dataclass.
15+
16+
Parameters
17+
----------
18+
serial : _type_
19+
serial connection
20+
ssms : ScioSpecMeasurementSetup
21+
dataclass with the measurement setup settings.
22+
"""
23+
24+
def clTbt_sp(val: Union[int, float]) -> list:
25+
"""
26+
clTbt_sp converts an integer or float value to a list of single precision bytes.
27+
28+
Parameters
29+
----------
30+
val : Union[int, float]
31+
Value that has to be converted
32+
33+
Returns
34+
-------
35+
list
36+
list with single precision byte respresentation
37+
"""
38+
return [int(bt) for bt in struct.pack(">f", val)]
39+
40+
def clTbt_dp(val: float) -> list:
41+
"""
42+
clTbt_dp converts an integer or float value to a list of double precision bytes.
43+
44+
Parameters
45+
----------
46+
val : float
47+
value that has to be converted
48+
49+
Returns
50+
-------
51+
list
52+
list with double precision byte respresentation
53+
"""
54+
return [int(ele) for ele in struct.pack(">d", val)]
55+
56+
# Set measurement setup:
57+
serial.write(bytearray([0xB0, 0x01, 0x01, 0xB0]))
58+
# Set burst count: "B0 03 02 00 03 B0" = 3
59+
serial.write(bytearray([0xB0, 0x03, 0x02, 0x00, ssms.burst_count, 0xB0]))
60+
61+
# Excitation amplitude double precision
62+
# A_min = 100nA
63+
# A_max = 10mA
64+
if ssms.amplitude > 0.001:
65+
print(f"Divide {ssms.amplitude}/1000. Out of available range")
66+
ssms.amplitude = ssms.amplitude / 1000
67+
serial.write(
68+
bytearray(list(np.concatenate([[176, 9, 5], clTbt_dp(ssms.amplitude), [176]])))
69+
)
70+
71+
# ADC range settings: [+/-1, +/-5, +/-10]
72+
# ADC range = +/-1 : B0 02 0D 01 B0
73+
# ADC range = +/-5 : B0 02 0D 02 B0
74+
# ADC range = +/-10 : B0 02 0D 03 B0
75+
if ssms.adc_range == 1:
76+
serial.write(bytearray([0xB0, 0x02, 0x0D, 0x01, 0xB0]))
77+
elif ssms.adc_range == 5:
78+
serial.write(bytearray([0xB0, 0x02, 0x0D, 0x02, 0xB0]))
79+
elif ssms.adc_range == 10:
80+
serial.write(bytearray([0xB0, 0x02, 0x0D, 0x03, 0xB0]))
81+
82+
# Gain settings:
83+
# Gain = 1 : B0 03 09 01 00 B0
84+
# Gain = 10 : B0 03 09 01 01 B0
85+
# Gain = 100 : B0 03 09 01 02 B0
86+
# Gain = 1_000 : B0 03 09 01 03 B0
87+
if ssms.gain == 1:
88+
serial.write(bytearray([0xB0, 0x03, 0x09, 0x01, 0x00, 0xB0]))
89+
elif ssms.gain == 10:
90+
serial.write(bytearray([0xB0, 0x03, 0x09, 0x01, 0x01, 0xB0]))
91+
elif ssms.gain == 100:
92+
serial.write(bytearray([0xB0, 0x03, 0x09, 0x01, 0x02, 0xB0]))
93+
elif ssms.gain == 1_000:
94+
serial.write(bytearray([0xB0, 0x03, 0x09, 0x01, 0x03, 0xB0]))
95+
96+
# Single ended mode:
97+
serial.write(bytearray([0xB0, 0x03, 0x08, 0x01, 0x01, 0xB0]))
98+
99+
# Excitation switch type:
100+
serial.write(bytearray([0xB0, 0x02, 0x0C, 0x01, 0xB0]))
101+
102+
# Set framerate:
103+
serial.write(
104+
bytearray(list(np.concatenate([[176, 5, 3], clTbt_sp(ssms.framerate), [176]])))
105+
)
106+
107+
# Set frequencies:
108+
# [CT] 0C 04 [fmin] [fmax] [fcount] [ftype] [CT]
109+
f_min = clTbt_sp(ssms.exc_freq)
110+
f_max = clTbt_sp(ssms.exc_freq)
111+
f_count = [0, 1]
112+
f_type = [0]
113+
# bytearray
114+
serial.write(
115+
bytearray(
116+
list(np.concatenate([[176, 12, 4], f_min, f_max, f_count, f_type, [176]]))
117+
)
118+
)
119+
120+
# Set injection config
121+
122+
el_inj = np.arange(1, ssms.n_el + 1)
123+
el_gnd = np.roll(el_inj, -(ssms.inj_skip + 1))
124+
125+
for v_el, g_el in zip(el_inj, el_gnd):
126+
serial.write(bytearray([0xB0, 0x03, 0x06, v_el, g_el, 0xB0]))
127+
128+
# Get measurement setup
129+
serial.write(bytearray([0xB1, 0x01, 0x03, 0xB1]))
130+
# Set output configuration
131+
serial.write(bytearray([0xB2, 0x02, 0x01, 0x01, 0xB2]))
132+
serial.write(bytearray([0xB2, 0x02, 0x03, 0x01, 0xB2]))
133+
serial.write(bytearray([0xB2, 0x02, 0x02, 0x01, 0xB2]))
134+
135+
## start measurement
136+
# serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
137+
# stop measurement
138+
# serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
4139

5140

6141
def conf_n_el_16_adjacent(
@@ -86,8 +221,8 @@ def conf_n_el_16_adjacent(
86221
serial.write(bytearray([0xB2, 0x02, 0x01, 0x01, 0xB2]))
87222
serial.write(bytearray([0xB2, 0x02, 0x03, 0x01, 0xB2]))
88223
serial.write(bytearray([0xB2, 0x02, 0x02, 0x01, 0xB2]))
89-
serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
90-
serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
224+
# serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
225+
# serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
91226
return ScioSpecMeasurementConfig(
92227
com_port=serial.name,
93228
burst_count=10,
@@ -188,8 +323,8 @@ def conf_n_el_16_opposite(
188323
serial.write(bytearray([0xB2, 0x02, 0x01, 0x01, 0xB2]))
189324
serial.write(bytearray([0xB2, 0x02, 0x03, 0x01, 0xB2]))
190325
serial.write(bytearray([0xB2, 0x02, 0x02, 0x01, 0xB2]))
191-
serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
192-
serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
326+
# serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
327+
# serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
193328
return ScioSpecMeasurementConfig(
194329
serial.name,
195330
burst_count=10,
@@ -306,8 +441,8 @@ def conf_n_el_32_adjacent(
306441
serial.write(bytearray([0xB2, 0x02, 0x01, 0x01, 0xB2]))
307442
serial.write(bytearray([0xB2, 0x02, 0x03, 0x01, 0xB2]))
308443
serial.write(bytearray([0xB2, 0x02, 0x02, 0x01, 0xB2]))
309-
serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
310-
serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
444+
# serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
445+
# serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
311446
return ScioSpecMeasurementConfig(
312447
serial.name,
313448
burst_count=1,
@@ -424,8 +559,8 @@ def conf_n_el_32_opposite(
424559
serial.write(bytearray([0xB2, 0x02, 0x01, 0x01, 0xB2]))
425560
serial.write(bytearray([0xB2, 0x02, 0x03, 0x01, 0xB2]))
426561
serial.write(bytearray([0xB2, 0x02, 0x02, 0x01, 0xB2]))
427-
serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
428-
serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
562+
# serial.write(bytearray([0xB4, 0x01, 0x01, 0xB4]))
563+
# serial.write(bytearray([0xB4, 0x01, 0x00, 0xB4]))
429564
return ScioSpecMeasurementConfig(
430565
serial.name,
431566
burst_count=1,

sciopy/sciopy_dataclasses.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
from dataclasses import dataclass
2-
from typing import List, Tuple
2+
from typing import List, Tuple, Union
3+
4+
5+
@dataclass
6+
class ScioSpecMeasurementSetup:
7+
burst_count: int
8+
total_meas_num: int
9+
n_el: int
10+
channel_group: list
11+
exc_freq: Union[int, float]
12+
framerate: Union[int, float]
13+
amplitude: Union[int, float]
14+
inj_skip: str
15+
gain: int
16+
adc_range: int
17+
notes: str
18+
configured: bool
319

420

521
@dataclass

sciopy/setup_m.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ def PowerPlugDetect(serial) -> None:
788788
SystemMessageCallback(serial)
789789

790790

791-
def GetDevideInfo(serial) -> None:
791+
def GetDeviceInfo(serial) -> None:
792792
"""
793793
Print device information.
794794

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
setuptools.setup(
44
name="sciopy",
5-
version="0.6.4",
5+
version="0.6.4.8",
66
author="Jacob Peter Thönes",
77
author_email="jacob.thoenes@uni-rostock.de",
8-
description="Python based interface module for serial communication with the ScioSpec Electrical Impedance Tomography (EIT) device.",
8+
description="Python based interface module for communication with the ScioSpec Electrical Impedance Tomography (EIT) device.",
99
long_description=open("README.md").read(),
1010
long_description_content_type="text/markdown",
1111
keywords="ScioSpec EIT".split(),

0 commit comments

Comments
 (0)