Metadata-Version: 2.5
Name: signalino
Version: 0.1.0
Summary: Python API for Signalino EEG devices, powered by BrainFlow
Project-URL: Homepage, https://github.com/JABarios/signalino-python
Project-URL: Repository, https://github.com/JABarios/signalino-python
Project-URL: Issues, https://github.com/JABarios/signalino-python/issues
Author: Scignals
License-Expression: MIT
License-File: LICENSE
Keywords: BCI,BrainFlow,EEG,LSL,MNE,Signalino
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Requires-Dist: brainflow
Requires-Dist: numpy>=1.26
Requires-Dist: pyserial<4,>=3.5
Provides-Extra: all
Requires-Dist: mne<2,>=1.12; extra == 'all'
Requires-Dist: pylsl<2,>=1.18.2; extra == 'all'
Provides-Extra: dev
Requires-Dist: build<2,>=1.3; extra == 'dev'
Requires-Dist: pytest-cov<8,>=6; extra == 'dev'
Requires-Dist: pytest<10,>=8.4; extra == 'dev'
Requires-Dist: ruff<1,>=0.12; extra == 'dev'
Requires-Dist: twine<8,>=7; extra == 'dev'
Provides-Extra: lsl
Requires-Dist: pylsl<2,>=1.18.2; extra == 'lsl'
Provides-Extra: mne
Requires-Dist: mne<2,>=1.12; extra == 'mne'
Description-Content-Type: text/markdown

# signalino

`signalino` is the high-level Python API for Signalino 4 EEG devices. It wraps
the Signalino board implemented in BrainFlow and adds a non-destructive sample
buffer, typed battery and impedance results, Lab Streaming Layer publishing,
and conversion to MNE-Python.

> This is research software. It is not a medical device and must not be used
> for diagnosis or patient monitoring.

## Status

The package is ready to build and test but has not been published to PyPI.
Signalino support currently lives in the
[Signalino-enabled BrainFlow repository](https://github.com/JABarios/brainflow-signalino). The
official `brainflow` wheel does not yet contain board ID 69, so install the matching
Signalino-enabled BrainFlow Python package and native library before connecting
hardware. The wrapper verifies both the Python binding and native library and
reports a clear error when they do not match.

## Installation

Build BrainFlow first, then install its Python package and this wrapper:

```bash
cmake -S ../brainflow-signalino -B ../brainflow-signalino/build \
  -DCMAKE_BUILD_TYPE=Release -DBUILD_BLE=ON
cmake --build ../brainflow-signalino/build --parallel
python -m pip install ../brainflow-signalino/python_package
python -m pip install .
```

The BrainFlow fork currently uses a development package version (`0.0.1`). The
`signalino` dependency intentionally does not impose a misleading BrainFlow
version floor; runtime capability checks are authoritative until board 69 is
available in an official BrainFlow release.

Install optional integrations as needed:

```bash
python -m pip install ".[lsl]"
python -m pip install ".[mne]"
python -m pip install ".[all]"
```

For development:

```bash
python -m pip install -e ".[dev,all]"
pytest
```

## USB

```python
import time

from signalino import Signalino

with Signalino.usb() as device:  # Automatically chooses the most probable port
    device.start_streaming()
    time.sleep(2)
    eeg_uv = device.get_data()

print(eeg_uv.shape)  # (8, approximately 500)
```

Use `COM3`-style names on Windows and `/dev/ttyACM0`-style names on Linux.
Pass one explicitly as `Signalino.usb("/dev/cu.usbmodem1101")` when needed.
Discovery only examines port names and USB descriptors; it does not open ports.
Use `find_usb_ports()` to display every probable candidate. If two devices are
equally likely, automatic selection refuses to guess.

## Bluetooth LE

```python
from signalino import Signalino

device = Signalino.ble("Signalino-852960")
device.connect()
device.start_streaming()
```

When only one Signalino is advertising, `Signalino.ble()` lets BrainFlow choose
it automatically. Provide the advertised name whenever multiple devices may be
present.

## Data

`get_data()` returns a NumPy array in microvolts with shape
`(channels, samples)`. Data is consumed oldest first by default:

```python
latest_copy = device.get_data(250, clear=False)
oldest_consumed = device.get_data(250)
```

For timestamps, use `get_data_batch()`:

```python
batch = device.get_data_batch()
print(batch.samples_uv.shape)
print(batch.timestamps)
```

The package continuously drains BrainFlow into its own bounded buffer. LSL and
`get_data()` therefore receive the same samples without stealing data from one
another.

## Public API

The stable top-level API is:

```text
Signalino.usb(...) / Signalino.ble(...)
find_usb_port() / find_usb_ports()
connect() / disconnect()
start_streaming() / stop_streaming()
get_data() / get_data_batch() / clear_data()
battery() / impedance()
start_lsl() / stop_lsl()
to_mne()
```

Public result types and exceptions are importable directly from `signalino`.
Implementation modules whose names begin with an underscore are private.

## Battery

```python
status = device.battery()
print(status.volts, status.percent, status.charging)
```

With the current USB BrainFlow bridge, battery replies cannot be collected
while binary EEG is streaming. Stop USB streaming before refreshing the value.
BLE uses a separate control characteristic and can refresh battery state while
EEG is active.

## Impedance

```python
reading = device.impedance()
print(reading.kiloohms)
```

The ADS1299 cannot emit normal EEG while measuring impedance. If streaming is
active, `impedance()` pauses EEG, takes one reading, exits impedance mode, and
restores both EEG acquisition and the previous LSL outlet. This produces a
short, timestamp-visible gap by design.

## LSL

```python
device.start_streaming()
stream = device.start_lsl()
print(stream.name, stream.source_id)
```

The outlet contains eight `float32` EEG channels in microvolts. It is closed
automatically before acquisition stops, so Signalino never leaves an advertised
but empty LSL stream behind.

## MNE

```python
raw = device.to_mne(clear=False)
print(raw.info["sfreq"])
```

MNE stores EEG in volts; conversion from Signalino's microvolts is automatic.
Pass an MNE montage with `device.to_mne(montage=montage)` when channel names have
been assigned to physical electrode positions.

## Development and release checks

```bash
ruff check .
ruff format --check .
pytest --cov=signalino
python -m build
python -m twine check dist/*
```

Building creates an sdist and a platform-independent wheel. Publishing is
deliberately not part of the build process.

## License

MIT
