diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..2846ec1
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,27 @@
+name: Build and deploy docs
+
+on:
+ push:
+ branches: [ main, master, develop ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.12'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r docs/requirements.txt
+ pip install -e .
+
+ - name: Build Sphinx docs
+ working-directory: docs
+ run: make html
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index e534a22..7d89378 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,5 @@
-__pycache__/
manuals
error_device_logs
-build/
-dist/
.update_package.sh.swp
.eggs/
sciopy.egg-info/
@@ -22,3 +19,40 @@ examples/measuremet_16/*
examples/measuremet_32/*
sciopy/eth_*
Driver
+ISX3-dev.ipynb
+update_docu.md
+
+# Python
+__pycache__/
+*.py[cod]
+*.egg-info/
+dist/
+build/
+
+# virtualenvs
+.env/
+.venv/
+.docs-venv/
+docs-venv/
+venv/
+venv*/
+
+# Sphinx build outputs & doctrees
+docs/_build/
+docs/_doctrees/
+_build/
+.doctrees/
+
+# Sphinx cache / temp
+docs/.doctrees/
+*.doctree
+
+# OS / editor files
+.DS_Store
+Thumbs.db
+.idea/
+.vscode/
+
+# pip / wheel cache (optional)
+pip-wheel-metadata/
+*.whl
\ No newline at end of file
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..1ce954d
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,12 @@
+version: 2
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.12"
+python:
+ install:
+ - method: pip
+ path: .
+ - requirements: docs/requirements.txt
+requests:
+ - type: github
diff --git a/README.md b/README.md
index 28e7b50..ae2d8ee 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
This package offers the serial interface for communication with an EIT device from ScioSpec. Commands can be written serially and the system response can be read out. With the current version, it is possible to start and stop measurements with defined burst counts and to read out the measurement data. In addition, the measurement data is packed into a data class for better further processing.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..a8e594a
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,18 @@
+# Minimal Makefile for Sphinx
+SPHINXBUILD = sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+.PHONY: help html clean
+
+help:
+ @echo "Please use 'make ' where is one of"
+ @echo " html to build the documentation"
+ @echo " clean to remove build artifacts"
+
+html:
+ $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" -a -E
+
+clean:
+ @rm -rf $(BUILDDIR)/*
+ @echo "Cleaned build artifacts"
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.EIT_16_32_64_128.rst b/docs/_autosummary/sciopy.EIT_16_32_64_128.rst
new file mode 100644
index 0000000..4cad4f7
--- /dev/null
+++ b/docs/_autosummary/sciopy.EIT_16_32_64_128.rst
@@ -0,0 +1,44 @@
+sciopy.EIT\_16\_32\_64\_128
+===========================
+
+.. currentmodule:: sciopy
+
+.. autoclass:: EIT_16_32_64_128
+
+
+ .. automethod:: __init__
+
+
+ .. rubric:: Methods
+
+ .. autosummary::
+
+ ~EIT_16_32_64_128.GetDeviceInfo
+ ~EIT_16_32_64_128.GetFirmwareIDs
+ ~EIT_16_32_64_128.GetMeasurementSetup
+ ~EIT_16_32_64_128.GetOutputConfiguration
+ ~EIT_16_32_64_128.PowerPlugDetect
+ ~EIT_16_32_64_128.ResetMeasurementSetup
+ ~EIT_16_32_64_128.SaveSettings
+ ~EIT_16_32_64_128.SetMeasurementSetup
+ ~EIT_16_32_64_128.SetOutputConfiguration
+ ~EIT_16_32_64_128.SoftwareReset
+ ~EIT_16_32_64_128.StartStopMeasurement
+ ~EIT_16_32_64_128.SystemMessageCallback
+ ~EIT_16_32_64_128.SystemMessageCallback_usb_fs
+ ~EIT_16_32_64_128.SystemMessageCallback_usb_hs
+ ~EIT_16_32_64_128.__init__
+ ~EIT_16_32_64_128.connect_device_FS
+ ~EIT_16_32_64_128.connect_device_HS
+ ~EIT_16_32_64_128.disconnect_device
+ ~EIT_16_32_64_128.get_data_as_matrix
+ ~EIT_16_32_64_128.init_channel_group
+ ~EIT_16_32_64_128.update_BurstCount
+ ~EIT_16_32_64_128.update_FrameRate
+ ~EIT_16_32_64_128.write_command_string
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.ISX_3.rst b/docs/_autosummary/sciopy.ISX_3.rst
new file mode 100644
index 0000000..6c86a51
--- /dev/null
+++ b/docs/_autosummary/sciopy.ISX_3.rst
@@ -0,0 +1,43 @@
+sciopy.ISX\_3
+=============
+
+.. currentmodule:: sciopy
+
+.. autoclass:: ISX_3
+
+
+ .. automethod:: __init__
+
+
+ .. rubric:: Methods
+
+ .. autosummary::
+
+ ~ISX_3.Action
+ ~ISX_3.GetDeviceID
+ ~ISX_3.GetExtensionPortChannel
+ ~ISX_3.GetExtensionPortModule
+ ~ISX_3.GetFE_Settings
+ ~ISX_3.GetFPGAfirmwareID
+ ~ISX_3.GetOptions
+ ~ISX_3.GetSetup
+ ~ISX_3.GetSyncTime
+ ~ISX_3.ResetSystem
+ ~ISX_3.SetExtensionPortChannel
+ ~ISX_3.SetFE_Settings
+ ~ISX_3.SetMeasurementSetup
+ ~ISX_3.SetOptions
+ ~ISX_3.SetSetup
+ ~ISX_3.SetSyncTime
+ ~ISX_3.StartMeasure
+ ~ISX_3.SystemMessageCallback
+ ~ISX_3.__init__
+ ~ISX_3.connect_device_USB2
+ ~ISX_3.disconnect_device_USB2
+ ~ISX_3.write_command_string
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.com_util.rst b/docs/_autosummary/sciopy.com_util.rst
new file mode 100644
index 0000000..111110a
--- /dev/null
+++ b/docs/_autosummary/sciopy.com_util.rst
@@ -0,0 +1,22 @@
+sciopy.com\_util
+================
+
+.. automodule:: sciopy.com_util
+
+
+ .. rubric:: Functions
+
+ .. autosummary::
+
+ available_serial_ports
+ bytesarray_to_byteslist
+ bytesarray_to_float
+ bytesarray_to_int
+ clTbt_dp
+ clTbt_sp
+ del_hex_in_list
+ parse_single_frame
+ reshape_full_message_in_bursts
+ single_hex_to_int
+ split_bursts_in_frames
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.doteit.rst b/docs/_autosummary/sciopy.doteit.rst
new file mode 100644
index 0000000..1c56144
--- /dev/null
+++ b/docs/_autosummary/sciopy.doteit.rst
@@ -0,0 +1,18 @@
+sciopy.doteit
+=============
+
+.. automodule:: sciopy.doteit
+
+
+ .. rubric:: Functions
+
+ .. autosummary::
+
+ convert_fulldir_doteit_to_npz
+ convert_fulldir_doteit_to_pickle
+ doteit_in_SingleEitFrame
+ list_all_files
+ list_eit_files
+ load_pickle_to_dict
+ single_eit_in_pickle
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.meshing.rst b/docs/_autosummary/sciopy.meshing.rst
new file mode 100644
index 0000000..2143bcb
--- /dev/null
+++ b/docs/_autosummary/sciopy.meshing.rst
@@ -0,0 +1,15 @@
+sciopy.meshing
+==============
+
+.. automodule:: sciopy.meshing
+
+
+ .. rubric:: Functions
+
+ .. autosummary::
+
+ add_circle_anomaly
+ create_empty_2d_mesh
+ mesh_sample
+ plot_mesh
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.rst b/docs/_autosummary/sciopy.rst
new file mode 100644
index 0000000..851bc18
--- /dev/null
+++ b/docs/_autosummary/sciopy.rst
@@ -0,0 +1,6 @@
+sciopy
+======
+
+.. automodule:: sciopy
+
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.sciopy_dataclasses.rst b/docs/_autosummary/sciopy.sciopy_dataclasses.rst
new file mode 100644
index 0000000..742ec51
--- /dev/null
+++ b/docs/_autosummary/sciopy.sciopy_dataclasses.rst
@@ -0,0 +1,17 @@
+sciopy.sciopy\_dataclasses
+==========================
+
+.. automodule:: sciopy.sciopy_dataclasses
+
+
+ .. rubric:: Classes
+
+ .. autosummary::
+
+ EisMeasurementSetup
+ EitMeasurementSetup
+ PreperationConfig
+ ScioSpecMeasurementConfig
+ SingleEitFrame
+ SingleFrame
+
\ No newline at end of file
diff --git a/docs/_autosummary/sciopy.visualization.rst b/docs/_autosummary/sciopy.visualization.rst
new file mode 100644
index 0000000..3cf4965
--- /dev/null
+++ b/docs/_autosummary/sciopy.visualization.rst
@@ -0,0 +1,14 @@
+sciopy.visualization
+====================
+
+.. automodule:: sciopy.visualization
+
+
+ .. rubric:: Functions
+
+ .. autosummary::
+
+ norm_data
+ plot_el_sign
+ plot_potential_matrix
+
\ No newline at end of file
diff --git a/doc/images/logo_sciopy.jpg b/docs/_static/logo_sciopy.jpg
similarity index 100%
rename from doc/images/logo_sciopy.jpg
rename to docs/_static/logo_sciopy.jpg
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 0000000..d6c7171
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,14 @@
+API reference
+=============
+
+.. autosummary::
+ :toctree: _autosummary
+
+ sciopy
+ sciopy.com_util
+ sciopy.doteit
+ sciopy.EIT_16_32_64_128
+ sciopy.ISX_3
+ sciopy.meshing
+ sciopy.sciopy_dataclasses
+ sciopy.visualization
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..c4611c7
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,59 @@
+import os
+import sys
+from datetime import datetime
+
+# Allow importing the package from repo root
+sys.path.insert(0, os.path.abspath(".."))
+
+project = "sciopy"
+author = "Jacob P. Thönes"
+release = "0.8.2.2"
+
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.autosummary",
+ "sphinx.ext.viewcode",
+ "sphinx_autodoc_typehints",
+ "sphinx.ext.intersphinx",
+ "nbsphinx",
+]
+
+autosummary_generate = True
+autodoc_member_order = "bysource"
+autodoc_typehints = "description"
+
+templates_path = ["_templates"]
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+html_theme = "sphinx_rtd_theme"
+html_static_path = ["_static"]
+
+# Project logo shown in the theme header
+html_logo = "_static/logo_sciopy.jpg"
+
+# Intersphinx mapping to useful external docs
+intersphinx_mapping = {
+ "python": ("https://docs.python.org/3/", None),
+ "numpy": ("https://numpy.org/doc/stable/", None),
+ "pandas": ("https://pandas.pydata.org/docs/", None),
+}
+
+# Linkcheck settings (tolerate some known external issues)
+linkcheck_ignore = [r"https://localhost:\d+/"]
+linkcheck_timeout = 10
+
+# Small helper for copyright line
+copyright = f"{datetime.now().year}, {author}"
+
+# Mock imports if building on CI without installing heavy deps (adjust as needed)
+autodoc_mock_imports = [
+ "numpy",
+ "pandas",
+ "matplotlib",
+ "serial",
+ "pyeit",
+ "pyftdi",
+ "pyserial",
+ "tqdm",
+]
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..6ba2772
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,20 @@
+sciopy documentation master file
+=================================
+
+Welcome to sciopy's documentation!
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ api
+
+
+Indices and tables
+------------------
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..648943d
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,10 @@
+@echo off
+set SPHINXBUILD=sphinx-build
+set SOURCEDIR=.
+set BUILDDIR=_build
+if "%1"=="clean" (
+ if exist %BUILDDIR% rd /s /q %BUILDDIR%
+ echo Cleaned build artifacts
+ goto :eof
+)
+%SPHINXBUILD% -M html %SOURCEDIR% %BUILDDIR% -a -E
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..54bff3a
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,5 @@
+sphinx
+sphinx-rtd-theme
+sphinx-autodoc-typehints
+sphinxcontrib-napoleon
+nbsphinx
diff --git a/examples/example_notebook.ipynb b/examples/EIT-16-256-Ch.ipynb
similarity index 100%
rename from examples/example_notebook.ipynb
rename to examples/EIT-16-256-Ch.ipynb
diff --git a/examples/ISX-3.ipynb b/examples/ISX-3.ipynb
new file mode 100644
index 0000000..e91d9f8
--- /dev/null
+++ b/examples/ISX-3.ipynb
@@ -0,0 +1,145 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "2727ee29",
+ "metadata": {},
+ "source": [
+ "# Example code for connecting the Sciospec ISX-3 device"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "18bd09f0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "\n",
+ "from sciopy import ISX_3, EisMeasurementSetup"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "88778348",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "isx = ISX_3()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "c21b7642",
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "SerialException",
+ "evalue": "[Errno 2] could not open port COM1: [Errno 2] No such file or directory: 'COM1'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
+ "File \u001b[0;32m~/miniconda3/lib/python3.12/site-packages/serial/serialposix.py:322\u001b[0m, in \u001b[0;36mSerial.open\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 322\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfd \u001b[38;5;241m=\u001b[39m \u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mportstr\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mO_RDWR\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m|\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mO_NOCTTY\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m|\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mO_NONBLOCK\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 323\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m msg:\n",
+ "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'COM1'",
+ "\nDuring handling of the above exception, another exception occurred:\n",
+ "\u001b[0;31mSerialException\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[3], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# connect to the device (adjust the port as necessary)\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m \u001b[43misx\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconnect_device_USB2\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mCOM1\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/Schreibtisch/Uni/Forschung/PyPI_Packages/sciopy/sciopy/ISX_3.py:63\u001b[0m, in \u001b[0;36mISX_3.connect_device_USB2\u001b[0;34m(self, port, baudrate, timeout)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 62\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mserial_protocol \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUSB-FS\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m---> 63\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdevice \u001b[38;5;241m=\u001b[39m \u001b[43mserial\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mSerial\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 64\u001b[0m \u001b[43m \u001b[49m\u001b[43mport\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mport\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 65\u001b[0m \u001b[43m \u001b[49m\u001b[43mbaudrate\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mbaudrate\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 66\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 67\u001b[0m \u001b[43m \u001b[49m\u001b[43mparity\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mserial\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mPARITY_NONE\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 68\u001b[0m \u001b[43m \u001b[49m\u001b[43mstopbits\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mserial\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mSTOPBITS_ONE\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 69\u001b[0m \u001b[43m \u001b[49m\u001b[43mbytesize\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mserial\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mEIGHTBITS\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 70\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 71\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mConnection to\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdevice\u001b[38;5;241m.\u001b[39mname, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mis established.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
+ "File \u001b[0;32m~/miniconda3/lib/python3.12/site-packages/serial/serialutil.py:244\u001b[0m, in \u001b[0;36mSerialBase.__init__\u001b[0;34m(self, port, baudrate, bytesize, parity, stopbits, timeout, xonxoff, rtscts, write_timeout, dsrdtr, inter_byte_timeout, exclusive, **kwargs)\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124munexpected keyword arguments: \u001b[39m\u001b[38;5;132;01m{!r}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;241m.\u001b[39mformat(kwargs))\n\u001b[1;32m 243\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m port \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 244\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/miniconda3/lib/python3.12/site-packages/serial/serialposix.py:325\u001b[0m, in \u001b[0;36mSerial.open\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 323\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m msg:\n\u001b[1;32m 324\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfd \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m--> 325\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m SerialException(msg\u001b[38;5;241m.\u001b[39merrno, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcould not open port \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_port, msg))\n\u001b[1;32m 326\u001b[0m \u001b[38;5;66;03m#~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking\u001b[39;00m\n\u001b[1;32m 328\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpipe_abort_read_r, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpipe_abort_read_w \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m\n",
+ "\u001b[0;31mSerialException\u001b[0m: [Errno 2] could not open port COM1: [Errno 2] No such file or directory: 'COM1'"
+ ]
+ }
+ ],
+ "source": [
+ "# connect to the device (adjust the port as necessary)\n",
+ "isx.connect_device_USB2(\"COM1\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fa54e5a6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# reset the system (clear previous settings)\n",
+ "isx.ResetSystem()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "28c54b12",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "setup = EisMeasurementSetup(\n",
+ " start=10,\n",
+ " stop=100000,\n",
+ " step=20,\n",
+ " stepmode=\"log\",\n",
+ " avg=1,\n",
+ " amplitude=100,\n",
+ " precision=1,\n",
+ " measurement_time=1,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e8cf6f6b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "isx.SetMeasurementSetup(setup)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2a8c956e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# disconnect device from serial port\n",
+ "isx.disconnect_device_USB2()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1cf1c447",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "base",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/requirements.txt b/requirements.txt
index 75965ca..3af6f5a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,4 @@ pyeit==1.2.4
pyftdi==0.55.0
pyserial==3.5
setuptools==78.1.1
-tqdm==4.66.3
\ No newline at end of file
+tqdm==4.66.3
diff --git a/sciopy/EIT_16_32_64_128.py b/sciopy/EIT_16_32_64_128.py
index 7329359..fc4d0f7 100644
--- a/sciopy/EIT_16_32_64_128.py
+++ b/sciopy/EIT_16_32_64_128.py
@@ -1,3 +1,5 @@
+"""Module for interfacing with the Sciospec EIT devices via serial communication"""
+
try:
import serial
except ImportError:
@@ -32,6 +34,10 @@
class EIT_16_32_64_128:
+ """
+ A class for interfacing with the Sciospec EIT 16/32/64/128 devices.
+ """
+
def __init__(self, n_el: int) -> None:
"""
__init__
@@ -48,7 +54,19 @@ def __init__(self, n_el: int) -> None:
self.ret_hex_int = None
def init_channel_group(self):
- if self.n_el in [16, 32, 48, 64]:
+ """
+ Initializes and returns a list representing the channel group based on the number of electrodes.
+ Returns:
+ list: A list of channel group indices, where each index corresponds to a channel.
+ The number of channels is determined by dividing `self.n_el` by 16.
+ Raises:
+ ValueError: If `self.n_el` is not one of the allowed values (16, 32, 48, 64, or 128).
+ Notes:
+ - Allowed values for `self.n_el` are 16, 32, 48, 64, and 128.
+ - The returned list contains consecutive integers starting from 1 up to `self.n_el // 16`.
+ """
+
+ if self.n_el in [16, 32, 48, 64, 128]:
return [ch + 1 for ch in range(self.n_el // 16)]
else:
raise ValueError(
@@ -57,7 +75,22 @@ def init_channel_group(self):
def connect_device_HS(self, url: str = "ftdi://ftdi:232h/1", baudrate: int = 9000):
"""
- Connect to high speed
+ Establishes a high-speed serial connection to an FTDI device.
+
+ This method initializes the FTDI device using the specified URL and baudrate,
+ configures the device for synchronous FIFO mode, and sets up the serial protocol.
+ If a serial connection is already defined, it notifies the user.
+
+ Args:
+ url (str): The FTDI device URL. Defaults to "ftdi://ftdi:232h/1".
+ baudrate (int): The baud rate for the serial connection. Defaults to 9000.
+
+ Side Effects:
+ Sets the `self.serial_protocol` attribute to "HS" if not already defined.
+ Initializes and configures the FTDI device, assigning it to `self.device`.
+
+ Raises:
+ Any exceptions raised by the FTDI library during device initialization or configuration.
"""
if hasattr(self, "serial_protocol"):
print(
@@ -78,7 +111,18 @@ def connect_device_HS(self, url: str = "ftdi://ftdi:232h/1", baudrate: int = 900
def connect_device_FS(self, port: str, baudrate: int = 9600, timeout: int = 1):
"""
- Connect to full speed
+ Establishes a serial connection to a device using the FS protocol.
+
+ Parameters:
+ port (str): The serial port to connect to (e.g., 'COM3' or '/dev/ttyUSB0').
+ baudrate (int, optional): The baud rate for the serial connection. Defaults to 9600.
+ timeout (int, optional): The timeout value for the serial connection in seconds. Defaults to 1.
+
+ Notes:
+ - If a serial connection is already defined in 'self.serial_protocol', a message is printed.
+ - Sets 'self.serial_protocol' to "FS" if not already defined.
+ - Initializes 'self.device' as a serial.Serial object with specified parameters.
+ - Prints a confirmation message upon successful connection.
"""
if hasattr(self, "serial_protocol"):
print(
@@ -99,15 +143,27 @@ def connect_device_FS(self, port: str, baudrate: int = 9600, timeout: int = 1):
def disconnect_device(self):
"""
- Disconnect serial device
+ Disconnects the currently connected device by closing its connection.
+
+ This method should be called to safely terminate communication with the device.
"""
self.device.close()
def SystemMessageCallback_usb_fs(self):
"""
- !Only used if a full-speed connection is established!
+ Reads data from a USB device, processes received messages, and returns the data in the specified format.
+
+ The method continuously reads from the device until no more data is received, then processes the received bytes.
+ It converts the received data to hexadecimal format and attempts to identify a specific message index.
+ Depending on the value of `self.ret_hex_int`, it returns the data as hexadecimal, integer, or both.
- Reads the message buffer of a serial connection. Also prints out the general system message.
+ Prints diagnostic messages if `self.print_msg` is True.
+
+ Returns:
+ list[str]: List of received data in hexadecimal format if `self.ret_hex_int == "hex"`.
+ list[int]: List of received data as integers if `self.ret_hex_int == "int"`.
+ tuple: Both integer and hexadecimal lists if `self.ret_hex_int == "both"`.
+ None: If `self.ret_hex_int` is None.
"""
timeout_count = 0
received = []
@@ -151,11 +207,21 @@ def SystemMessageCallback_usb_fs(self):
def SystemMessageCallback_usb_hs(self):
"""
- !Only used if a high-speed connection is established!
+ Reads data from a USB high-speed device, processes received messages, and returns the data in various formats.
- Reads the message buffer of a serial connection. Also prints out the general system message.
- """
+ The method continuously reads data from the device until no more data is received. It converts the received bytes to hexadecimal format,
+ searches for a specific message index, and prints corresponding messages if enabled. The returned data format depends on the value of
+ `self.ret_hex_int` ("hex", "int", "both", or None).
+ Returns:
+ list[str]: List of received data in hexadecimal format if `self.ret_hex_int == "hex"`.
+ list[int]: List of received data as integers if `self.ret_hex_int == "int"`.
+ tuple[list[int], list[str]]: Both integer and hexadecimal lists if `self.ret_hex_int == "both"`.
+ None: If `self.ret_hex_int` is None.
+
+ Raises:
+ BaseException: If message index is not found in the received data.
+ """
timeout_count = 0
received = []
received_hex = []
@@ -198,7 +264,15 @@ def SystemMessageCallback_usb_hs(self):
def SystemMessageCallback(self):
"""
- SystemMessageCallback
+ Handles system messages based on the selected serial protocol.
+
+ Depending on the value of `self.serial_protocol`, this method delegates
+ the handling of system messages to the appropriate callback:
+ - If `self.serial_protocol` is "HS", calls `SystemMessageCallback_usb_hs()`.
+ - If `self.serial_protocol` is "FS", calls `SystemMessageCallback_usb_fs()`.
+
+ Raises:
+ AttributeError: If the required callback methods are not defined.
"""
if self.serial_protocol == "HS":
self.SystemMessageCallback_usb_hs()
@@ -207,7 +281,17 @@ def SystemMessageCallback(self):
def write_command_string(self, command):
"""
- Function for writing a command 'bytearray(...)' to the serial port
+ Sends a command string to the device using the appropriate serial protocol.
+
+ Depending on the value of `self.serial_protocol`, the command is sent using either
+ high-speed ("HS") or full-speed ("FS") protocol. After sending the command, the
+ system message callback is triggered.
+
+ Args:
+ command (str): The command string to be sent to the device.
+
+ Raises:
+ AttributeError: If `self.device` does not have the required method for the selected protocol.
"""
if self.serial_protocol == "HS":
self.device.write_data(command)
@@ -218,12 +302,29 @@ def write_command_string(self, command):
# --- sciospec device commands
def SoftwareReset(self):
+ """
+ Performs a software reset of the device.
+
+ This method sends a specific command sequence to the device to initiate a software reset.
+ """
self.print_msg = True
self.write_command_string(bytearray([0xA1, 0x00, 0xA1]))
self.print_msg = False
def update_BurstCount(self, burst_count):
+ """
+ Updates the burst count setting and sends the corresponding command to the device.
+
+ Args:
+ burst_count (int): The new burst count value to set.
+
+ Side Effects:
+ - Sets `self.setup.burst_count` to the provided value.
+ - Sends a command to the device using `write_command_string`.
+ - Temporarily sets `self.print_msg` to True during the operation.
self.print_msg = True
+
+ """
self.setup.burst_count = burst_count
self.write_command_string(
bytearray([0xB0, 0x03, 0x02, 0x00, self.setup.burst_count, 0xB0])
@@ -231,6 +332,20 @@ def update_BurstCount(self, burst_count):
self.print_msg = False
def update_FrameRate(self, framerate):
+ """
+ Updates the frame rate setting for the device and sends the corresponding command.
+
+ Args:
+ framerate (int): The desired frame rate to set.
+
+ Side Effects:
+ - Sets `self.setup.framerate` to the provided value.
+ - Sends a command to the device to update the frame rate.
+ - Temporarily sets `self.print_msg` to True during the operation.
+
+ Note:
+ The command sent is constructed using the `clTbt_sp` function and numpy's `concatenate`.
+ """
self.print_msg = True
self.setup.framerate = framerate
self.write_command_string(
@@ -244,7 +359,29 @@ def update_FrameRate(self, framerate):
def SetMeasurementSetup(self, setup: EitMeasurementSetup):
"""
- set_measurement_config sets the ScioSpec device configuration depending on the EitMeasurementSetup configuration dataclass.
+ Configures the ScioSpec device measurement setup according to the provided EitMeasurementSetup dataclass.
+
+ This method sets various device parameters including burst count, excitation amplitude, ADC range, gain,
+ single-ended mode, excitation switch type, framerate, excitation frequencies, and electrode injection configuration.
+ It also enables output configuration options such as excitation setting, frequency stack row, and timestamp.
+
+ Parameters
+ ----------
+ setup : EitMeasurementSetup
+ The measurement setup configuration containing parameters such as burst count, amplitude, ADC range, gain,
+ framerate, excitation frequency, number of electrodes, and injection skip.
+
+ Raises
+ ------
+ AssertionError
+ If the number of electrodes in the setup does not match the device initialization.
+
+ Notes
+ -----
+ - Amplitude is limited to a maximum of 10mA.
+ - ADC range and gain are set according to predefined device commands.
+ - Electrode injection configuration is set for all electrodes based on the provided setup.
+ - Output configuration is enabled for excitation, frequency stack, and timestamp.
"""
self.setup = setup
@@ -346,14 +483,40 @@ def SaveSettings(self):
self.print_msg = False
def ResetMeasurementSetup(self):
+ """
+ Resets the measurement setup by sending a specific command to the device.
+
+ This method sets the `print_msg` flag to True, sends a reset command via
+ `write_command_string`, and then sets `print_msg` back to False.
+
+ The command sent is a bytearray: [0xB0, 0x01, 0x01, 0xB0].
+
+ Returns:
+ None
+ """
self.print_msg = True
self.write_command_string(bytearray([0xB0, 0x01, 0x01, 0xB0]))
self.print_msg = False
def GetMeasurementSetup(self, setup_of: str):
"""
- GetMeasurementSetup
-
+ Retrieves and configures the measurement setup for the device based on the specified setup option.
+
+ Parameters:
+ setup_of (str): A string identifier for the desired measurement setup option.
+ Supported options include:
+ - 'Burst Count' (0x02)
+ - 'Frame Rate' (0x03)
+ - 'Excitation Frequencies' (0x04)
+ - 'Excitation Amplitude' (0x05)
+ - 'Excitation Sequence' (0x06)
+ - 'Single-Ended or Differential Measure Mode' (0x08)
+ - 'Gain Settings' (0x09)
+ - 'Excitation Switch Type' (0x0C)
+
+ Note:
+ This method sends a command to the device to retrieve or configure the specified measurement setup.
+ The actual translation and handling of the response is TBD (to be implemented).
Burst Count 2 -> 0x02
Frame Rate 3 -> 0x03
Excitation Frequencies 4 -> 0x04
@@ -371,6 +534,21 @@ def GetMeasurementSetup(self, setup_of: str):
self.print_msg = False
def StartStopMeasurement(self, return_as="pot_mat"):
+ """
+ Starts and stops a measurement process using the configured serial protocol (HS or FS).
+ Sends appropriate commands to the device to initiate and terminate measurement.
+ Processes the received data by removing hexadecimal values, reshaping messages into bursts,
+ and splitting bursts into frames. Stores the processed data in `self.data`.
+
+ Args:
+ return_as (str, optional): Specifies the format of the returned data.
+ - "hex": Returns the processed data as a list of hexadecimal values.
+ - "pot_mat": Returns the processed data as a matrix using `get_data_as_matrix()`.
+ Default is "pot_mat".
+
+ Returns:
+ list or matrix: The measurement data in the format specified by `return_as`.
+ """
if self.serial_protocol == "HS":
self.device.write_data(bytearray([0xB4, 0x01, 0x01, 0xB4]))
self.ret_hex_int = "hex"
@@ -404,6 +582,22 @@ def StartStopMeasurement(self, return_as="pot_mat"):
return self.get_data_as_matrix()
def get_data_as_matrix(self):
+ """
+ Converts the raw EIT data into a 3D matrix of potentials.
+
+ The resulting matrix has the shape (burst_count, n_el, n_el), where:
+ - burst_count: Number of bursts in the measurement setup.
+ - n_el: Number of electrodes.
+
+ For each burst, the method iterates through its frames, grouping channel data
+ into electrode signals and arranging them in the matrix according to their channel group.
+
+ After processing, self.data is replaced with the resulting matrix.
+
+ Returns:
+ np.ndarray: A 3D complex-valued matrix containing the electrode potentials
+ for each burst and channel group.
+ """
pot_matrix = np.empty(
(self.setup.burst_count, self.n_el, self.n_el), dtype=complex
)
@@ -442,16 +636,51 @@ def GetOutputConfiguration(self):
self.print_msg = False
def GetDeviceInfo(self):
+ """
+ Retrieves device information by sending a specific command to the device.
+
+ This method sets the print_msg flag to True, sends the device info command
+ using `write_command_string`, and then resets the print_msg flag to False.
+
+ Returns:
+ None
+ """
self.print_msg = True
self.write_command_string(bytearray([0xD1, 0x00, 0xD1]))
self.print_msg = False
def GetFirmwareIDs(self):
+ """
+ Sends a command to retrieve firmware IDs from the device.
+
+ This method sets the print_msg flag to True, sends a specific command
+ to the device to request firmware identification, and then resets the
+ print_msg flag to False.
+
+ Returns:
+ None
+ """
self.print_msg = True
self.write_command_string(bytearray([0xD2, 0x00, 0xD2]))
self.print_msg = False
def PowerPlugDetect(self):
+ """
+ Detects the presence of a power plug by sending a specific command to the device.
+
+ This method sets the print_msg flag to True, sends a command to check for power plug detection,
+ and then resets the print_msg flag to False.
+
+ Command sent:
+ - [0xCC, 0x01, 0x81, 0xCC]: Power plug detection command.
+
+ Note:
+ The method does not return any value. It is assumed that the result of the detection
+ is handled elsewhere in the class or by a callback.
+
+ Side Effects:
+ Modifies the self.print_msg attribute.
+ """
self.print_msg = True
self.write_command_string(bytearray([0xCC, 0x01, 0x81, 0xCC]))
self.print_msg = False
diff --git a/sciopy/ISX_3.py b/sciopy/ISX_3.py
index 2bc7a34..cc4f3f9 100644
--- a/sciopy/ISX_3.py
+++ b/sciopy/ISX_3.py
@@ -1,3 +1,5 @@
+"""Module for interfacing with the Sciospec ISX-3 EIT device via serial communication"""
+
try:
import serial
except ImportError:
@@ -5,36 +7,153 @@
from dataclasses import dataclass
+from .sciopy_dataclasses import EisMeasurementSetup
+
+
+msg_dict = {
+ "0x01": "No message inside the message buffer",
+ "0x02": "Timeout: Communication-timeout (less data than expected)",
+ "0x04": "Wake-Up Message: System boot ready",
+ "0x11": "TCP-Socket: Valid TCP client-socket connection",
+ "0x81": "Not-Acknowledge: Command has not been executed",
+ "0x82": "Not-Acknowledge: Command could not be recognized",
+ "0x83": "Command-Acknowledge: Command has been executed successfully",
+ "0x84": "System-Ready Message: System is operational and ready to receive data",
+ "0x92": "Data holdup: Measurement data could not be sent via the master interface",
+}
-@dataclass
-class EisMeasurementSetup:
- pass
+error_msg_dict = {
+ "0x01": "init setup failed",
+ "0x02": "add frequency block failed",
+ "0x03": "set parasitic parameters failed",
+ "0x04": "set acceleration settings failed",
+ "0x05": "set sync time failed",
+ "0x06": "set channel settings failed",
+ "0x07": "set calibration data failed",
+ "0x08": "set timestamp failed",
+ "0x09": "start measurement failed",
+ "0x22": "set amplitude failed",
+}
+
+
+acknowledge_msg_dict = {
+ "0x01": "Frame-Not-Acknowledge: Incorrect syntax",
+ "0x02": "Timeout: Communication-timeout (less data than expected)",
+ "0x04": "Wake-Up Message: System boot ready",
+ "0x81": "Not-Acknowledge: Command has not been executed",
+ "0x82": "Not-Acknowledge: Command could not be recognized",
+ "0x83": "Command-Acknowledge: Command has been executed successfully",
+ "0x84": "System-Ready Message: System is operational and ready to receive data",
+}
class ISX_3:
- def __init__(self, n_el) -> None:
- # number of electrodes used
- self.n_el = n_el
+ """
+ A class for interfacing with the Sciospec ISX-3 EIT device.
+ """
+
+ def __init__(self) -> None:
+ self.print_msg = True
+ self.ret_hex_int = None
- def connect_device_FS(self, port: str, baudrate: int = 9600, timeout: int = 1):
+ def connect_device_USB2(self, port: str, baudrate: int = 9600, timeout: int = 1):
"""
- Connect to full speed
+ Connect to USB 2.0 Type B
"""
- if hasattr(self, "serial_protocol"):
+ if hasattr(self, "USB-FS"):
print(
- "Serial connection 'self.serial_protocol' already defined as {self.serial_protocol}."
+ f"Serial connection 'self.serial_protocol' already defined as {self.serial_protocol}."
)
else:
- self.serial_protocol = "FS"
- self.device = serial.Serial(
- port=port,
- baudrate=baudrate,
- timeout=timeout,
- parity=serial.PARITY_NONE,
- stopbits=serial.STOPBITS_ONE,
- bytesize=serial.EIGHTBITS,
- )
- print("Connection to", self.device.name, "is established.")
+ self.serial_protocol = "USB-FS"
+ self.device = serial.Serial(
+ port=port,
+ baudrate=baudrate,
+ timeout=timeout,
+ parity=serial.PARITY_NONE,
+ stopbits=serial.STOPBITS_ONE,
+ bytesize=serial.EIGHTBITS,
+ )
+ print("Connection to", self.device.name, "is established.")
+
+ def disconnect_device_USB2(self):
+ self.device.close()
+ print("Connection to", self.device.name, "is closed.")
+
+ def SystemMessageCallback(self):
+ """
+ Reads the message buffer of a serial connection. Also prints out the general system message.
+ """
+ timeout_count = 0
+ received = []
+ received_hex = []
+ data_count = 0
+
+ while True:
+ buffer = self.device.read()
+ if buffer:
+ received.extend(buffer)
+ data_count += len(buffer)
+ timeout_count = 0
+ continue
+ timeout_count += 1
+ if timeout_count >= 1:
+ # Break if we haven't received any data
+ break
+
+ received = "".join(str(received)) # If you need all the data
+ received_hex = [hex(receive) for receive in received]
+ try:
+ msg_idx = received_hex.index("0x18")
+ if self.print_msg:
+ print(msg_dict[received_hex[msg_idx + 2]])
+ except BaseException:
+ if self.print_msg:
+ print(msg_dict["0x01"])
+ # self.print_msg = False
+ if self.print_msg:
+ print("message buffer:\n", received_hex)
+ print("message length:\t", data_count)
+
+ if self.ret_hex_int is None:
+ return
+ elif self.ret_hex_int == "hex":
+ return received_hex
+ elif self.ret_hex_int == "int":
+ return received
+ elif self.ret_hex_int == "both":
+ return received, received_hex
+
+ def write_command_string(self, command):
+ """
+ Function for writing a command 'bytearray(...)' to the serial port
+ """
+ self.device.write(command)
+ self.SystemMessageCallback()
+
+ def ResetSystem(self):
+ self.print_msg = True
+ self.write_command_string(bytearray([0xA1, 0x00, 0xA1]))
+ self.print_msg = False
+
+ def SetMeasurementSetup(self, setup: EisMeasurementSetup):
+ """
+ Configures the measurement setup for the device.
+
+ Parameters
+ ----------
+ setup : EisMeasurementSetup
+ An instance of EisMeasurementSetup containing the measurement parameters.
+ """
+
+ self.print_msg = True
+ # self.write_command_string(command)
+ self.print_msg = False
+
+ def StartMeasure(self):
+ self.print_msg = True
+ self.write_command_string(bytearray([0xB8, 0x01, 0x01, 0x01, 0xB8]))
+ self.print_msg = False
def SetOptions(self):
# 0x97
@@ -44,17 +163,62 @@ def GetOptions(self):
# 0x98
pass
- def ResetSystem(self):
- # 0xA1
- pass
+ def SetFE_Settings(self, PP, CH, RA):
+ """
+ Configures the frontend measurement settings for the device.
- def SetFE_Settings(self):
- # 0xB0
- pass
+ PP : int
+ Measurement mode (see above for options).
+ CH : int
+ Measurement channel (see above for options).
+ RA : int
+ Range setting (see above for options).
+
+ Frontend configuration:
+ - PP (Measurement mode):
+ - 0x02: 4 point configuration
+ - CH (Measurement channel):
+ - 0x01: BNC Port (ISX-3mini: Port 1)
+ - 0x02: ExtensionPort
+ - 0x03: ExtensionPort2 (ISX-3mini: Port 2, ISX-3v2: optional, InternalMux)
+ - RA (Range Settings):
+ - 0x01: 100 Ohm
+ - 0x02: 10 kOhm
+ - 0x04: 1 MOhm
+ """
+ self.print_msg = True
+ self.write_command_string(bytearray([0xB0, PP, CH, RA, 0xB0]))
+ self.print_msg = False
def GetFE_Settings(self):
- # 0xB1
- pass
+ """
+ Retrieves the frontend measurement settings from the device.
+
+ Returns:
+ dict: Dictionary containing PP (Measurement mode), CH (Measurement channel), RA (Range setting).
+ """
+ self.print_msg = True
+ # TBD: write_command_string needs to return response
+ self.write_command_string(bytearray([0xB1, 0x00, 0xB1]))
+ response = self.read_response()
+ self.print_msg = False
+
+ if (
+ response
+ and len(response) >= 6
+ and response[0] == 0xB1
+ and response[-1] == 0xB1
+ ):
+ self.PP = response[2]
+ self.CH = response[3]
+ self.RA = response[4]
+ print("Frontend Settings:")
+ print(f"Measurement Mode (PP): {self.PP}")
+ print(f"Measurement Channel (CH): {self.CH}")
+ print(f"Range Setting (RA): {self.RA}")
+ else:
+ print("Failed to get FE settings or invalid response.")
+ return None
def SetExtensionPortChannel(self):
# 0xB2
@@ -76,10 +240,6 @@ def SetSetup(self):
# 0xB7
pass
- def StartMeasure(self):
- # 0xB8
- pass
-
def SetSyncTime(self):
# 0xB9
pass
@@ -100,8 +260,32 @@ def GetExtensionPortChannel(self):
# 0xD3
pass
+ def Action(self):
+ self.print_msg = True
+ self.write_command_string(bytearray([0xD2, 0x00, 0xD2]))
+ self.print_msg = False
+
-# 0xBD - Set Ethernet Configuration
-# 0xBE - Get Ethernet Configuration
-# 0xCF - TCP connection watchdog
-# 0xD0 - Get ARM firmware ID
+# - 0x90 - Save Settings
+# - 0x97 - Set Options
+# - 0x98 - Get Options
+# - 0x99 - Set IOPort Configuration
+# - 0x9A - Get IOPort Configuration
+# - 0x9B - Set NTC Parameter 1
+# - 0x9D - Set NTC Parameter 2
+# - 0x9C - Get NTC Parameter 1
+# - 0x9E - Get NTC Parameter 2
+# - 0xA1 - Reset System
+# - 0xB0 - Set FE Settings
+# - 0xB1 - Get FE Settings
+# - 0xB2 - Set ExtensionPort Channel
+# - 0xB3 - Get ExtPort Channel
+# - 0xB5 - Get ExtPort Module
+# - 0xB6 - Set Setup
+# - 0xB7 - Get Setup
+# - 0xB8 - Start Measure
+# - 0xB9 - Set Sync Time
+# - 0xBA - Get Sync Time
+# - 0xBD - Set Ethernet Configuration
+# - 0xBE - Get Ethernet Configur
+# - 0xD0 - Get ARM firmware ID
diff --git a/sciopy/__init__.py b/sciopy/__init__.py
index 4699715..1bda5a2 100644
--- a/sciopy/__init__.py
+++ b/sciopy/__init__.py
@@ -1,8 +1,17 @@
+"""A Python package for Sciospec device communication"""
+
from .com_util import (
available_serial_ports,
)
from .EIT_16_32_64_128 import EIT_16_32_64_128, EitMeasurementSetup
+from .ISX_3 import ISX_3, EisMeasurementSetup
-__all__ = ["available_serial_ports", "EIT_16_32_64_128", "EitMeasurementSetup"]
+__all__ = [
+ "available_serial_ports",
+ "EIT_16_32_64_128",
+ "EitMeasurementSetup",
+ "ISX_3",
+ "EisMeasurementSetup",
+]
diff --git a/sciopy/com_util.py b/sciopy/com_util.py
index 0664ceb..0477c19 100644
--- a/sciopy/com_util.py
+++ b/sciopy/com_util.py
@@ -1,3 +1,5 @@
+"""Serial data handling"""
+
from typing import Union
try:
diff --git a/sciopy/doteit.py b/sciopy/doteit.py
index d74464a..021c4c3 100644
--- a/sciopy/doteit.py
+++ b/sciopy/doteit.py
@@ -1,4 +1,4 @@
-""" Convert a .eit file to python sctructured data"""
+"""Convert a .eit file to python sctructured data"""
import os
import numpy as np
diff --git a/sciopy/meshing.py b/sciopy/meshing.py
index bb31fc1..fcebaf4 100644
--- a/sciopy/meshing.py
+++ b/sciopy/meshing.py
@@ -1,3 +1,5 @@
+"""*WIP* module for mesh generation and plotting"""
+
from typing import Union
import matplotlib.pyplot as plt
import numpy as np
diff --git a/sciopy/sciopy_dataclasses.py b/sciopy/sciopy_dataclasses.py
index c28e14b..ee2c079 100644
--- a/sciopy/sciopy_dataclasses.py
+++ b/sciopy/sciopy_dataclasses.py
@@ -1,9 +1,25 @@
+"""Dataclasses for sciopy package."""
+
from dataclasses import dataclass
from typing import List, Tuple, Union
@dataclass
class EitMeasurementSetup:
+ """
+ Represents the setup parameters for Electrical Impedance Tomography (EIT) measurements.
+
+ Attributes:
+ burst_count (int): Number of bursts per measurement cycle.
+ n_el (int): Number of electrodes used in the measurement.
+ exc_freq (int or float): Excitation frequency in Hz.
+ framerate (int or float): Frame rate of the measurement in Hz.
+ amplitude (int or float): Amplitude of the excitation signal.
+ inj_skip (int or list): Electrode(s) to skip during current injection.
+ gain (int): Amplifier gain setting.
+ adc_range (int): Analog-to-digital converter range setting.
+ """
+
burst_count: int
n_el: int
exc_freq: Union[int, float]
@@ -17,7 +33,34 @@ class EitMeasurementSetup:
@dataclass
class EisMeasurementSetup:
- pass
+ """
+ Represents the setup parameters for an Electrochemical Impedance Spectroscopy (EIS) measurement.
+
+ Attributes:
+ start (int | float): Start frequency in Hz (e.g., 500000 for 500kHz).
+ stop (int | float): Stop frequency in Hz.
+ step (int | float): Number of frequency steps.
+ stepmode (str): Type of frequency distribution over the interval ('lin' for linear, 'log' for logarithmic).
+ AVG (int | float): Number of averages taken per measurement.
+ Amplitude (int | float): Amplitude of the excitation signal in mV.
+ Precision (int): Desired precision configuration (≥0):
+ - 1: Standard configuration (max relative deviation < 0.1%)
+ - <1: Faster measurement, less precise
+ - >1: More precise, slower measurement
+ MeasurementTime (int | float): Duration of the measurement in seconds.
+
+ Note:
+ Additional parameters may include measurement channel settings and other hardware-specific configurations.
+ """
+
+ start: Union[int, float] # min 100mHz
+ stop: Union[int, float] # max 10MHz
+ step: Union[int, float]
+ stepmode: str # 'lin', 'log'
+ avg: Union[int, float]
+ amplitude: Union[int, float]
+ precision: int
+ measurement_time: Union[int, float]
@dataclass
diff --git a/sciopy/visualization.py b/sciopy/visualization.py
index 11c4ed1..c6e831e 100644
--- a/sciopy/visualization.py
+++ b/sciopy/visualization.py
@@ -1,10 +1,24 @@
+"""Visualize recorded samples"""
+
import matplotlib.pyplot as plt
import numpy as np
-import math
-import os
-from typing import Tuple
-from .prepare_data import comp_tank_relative_r_phi
-from sciopy.prepare_data import norm_data
+
+
+def norm_data(data: np.ndarray) -> np.ndarray:
+ """
+ Normalize data between 0 and 1.
+
+ Parameters
+ ----------
+ data : np.ndarray
+ data to normalize
+
+ Returns
+ -------
+ np.ndarray
+ normalized data
+ """
+ return (data - np.min(data)) / (np.max(data) - np.min(data))
def plot_potential_matrix(sample: np.lib.npyio.NpzFile) -> None:
diff --git a/setup.py b/setup.py
index f250a9b..4e1c4fa 100644
--- a/setup.py
+++ b/setup.py
@@ -2,11 +2,11 @@
setup(
name="sciopy",
- version="0.8.2",
+ version="0.8.2.2",
packages=find_packages(),
- author="Jacob Peter Thönes",
+ author="Jacob P. Thönes",
author_email="jacob.thoenes@uni-rostock.de",
- description="Python based interface module for communication with the Sciospec Electrical Impedance Tomography (EIT) device.",
+ description="Python based interface module for communication with Sciospec devices.",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
keywords="Sciospec EIT EIS".split(),
@@ -16,5 +16,5 @@
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
- url="https://github.com/spatialaudio/sciopy.git",
+ url="https://github.com/EITLabworks/sciopy",
)