neurostream-edge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- neurostream_edge-0.1.0/LICENSE +21 -0
- neurostream_edge-0.1.0/PKG-INFO +345 -0
- neurostream_edge-0.1.0/README.md +308 -0
- neurostream_edge-0.1.0/neurostream/__init__.py +103 -0
- neurostream_edge-0.1.0/neurostream/acquisition/__init__.py +24 -0
- neurostream_edge-0.1.0/neurostream/acquisition/adapters/__init__.py +17 -0
- neurostream_edge-0.1.0/neurostream/acquisition/adapters/brainflow.py +130 -0
- neurostream_edge-0.1.0/neurostream/acquisition/adapters/lsl.py +123 -0
- neurostream_edge-0.1.0/neurostream/acquisition/base.py +98 -0
- neurostream_edge-0.1.0/neurostream/acquisition/simulator.py +151 -0
- neurostream_edge-0.1.0/neurostream/benchmarking/__init__.py +29 -0
- neurostream_edge-0.1.0/neurostream/benchmarking/benchmark.py +143 -0
- neurostream_edge-0.1.0/neurostream/benchmarking/latency.py +112 -0
- neurostream_edge-0.1.0/neurostream/models/__init__.py +29 -0
- neurostream_edge-0.1.0/neurostream/models/base.py +62 -0
- neurostream_edge-0.1.0/neurostream/models/engines.py +194 -0
- neurostream_edge-0.1.0/neurostream/models/factory.py +67 -0
- neurostream_edge-0.1.0/neurostream/observability/__init__.py +22 -0
- neurostream_edge-0.1.0/neurostream/observability/logger.py +62 -0
- neurostream_edge-0.1.0/neurostream/observability/metrics.py +100 -0
- neurostream_edge-0.1.0/neurostream/observability/profiler.py +88 -0
- neurostream_edge-0.1.0/neurostream/runtime/__init__.py +31 -0
- neurostream_edge-0.1.0/neurostream/runtime/events.py +183 -0
- neurostream_edge-0.1.0/neurostream/runtime/runtime.py +288 -0
- neurostream_edge-0.1.0/neurostream/storage/__init__.py +22 -0
- neurostream_edge-0.1.0/neurostream/storage/base.py +83 -0
- neurostream_edge-0.1.0/neurostream/storage/binary.py +134 -0
- neurostream_edge-0.1.0/neurostream/storage/memmap.py +206 -0
- neurostream_edge-0.1.0/neurostream/streaming/__init__.py +25 -0
- neurostream_edge-0.1.0/neurostream/streaming/base.py +154 -0
- neurostream_edge-0.1.0/neurostream/streaming/deque_buffer.py +107 -0
- neurostream_edge-0.1.0/neurostream/streaming/ring_buffer.py +196 -0
- neurostream_edge-0.1.0/neurostream/streaming/shared_memory_buffer.py +253 -0
- neurostream_edge-0.1.0/neurostream/windowing/__init__.py +18 -0
- neurostream_edge-0.1.0/neurostream/windowing/base.py +59 -0
- neurostream_edge-0.1.0/neurostream/windowing/sliding.py +107 -0
- neurostream_edge-0.1.0/neurostream_edge.egg-info/PKG-INFO +345 -0
- neurostream_edge-0.1.0/neurostream_edge.egg-info/SOURCES.txt +49 -0
- neurostream_edge-0.1.0/neurostream_edge.egg-info/dependency_links.txt +1 -0
- neurostream_edge-0.1.0/neurostream_edge.egg-info/requires.txt +17 -0
- neurostream_edge-0.1.0/neurostream_edge.egg-info/top_level.txt +1 -0
- neurostream_edge-0.1.0/pyproject.toml +63 -0
- neurostream_edge-0.1.0/setup.cfg +4 -0
- neurostream_edge-0.1.0/tests/test_acquisition.py +65 -0
- neurostream_edge-0.1.0/tests/test_benchmarking.py +61 -0
- neurostream_edge-0.1.0/tests/test_events_observability.py +133 -0
- neurostream_edge-0.1.0/tests/test_models.py +66 -0
- neurostream_edge-0.1.0/tests/test_runtime.py +83 -0
- neurostream_edge-0.1.0/tests/test_storage.py +87 -0
- neurostream_edge-0.1.0/tests/test_streaming.py +153 -0
- neurostream_edge-0.1.0/tests/test_windowing.py +122 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tarun Chilkur
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neurostream-edge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A modular runtime for continuous biosignal processing and real-time inference.
|
|
5
|
+
Author: Tarun Chilkur
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/tarunc997/NeuroStream-Edge
|
|
8
|
+
Project-URL: Repository, https://github.com/tarunc997/NeuroStream-Edge
|
|
9
|
+
Project-URL: Issues, https://github.com/tarunc997/NeuroStream-Edge/issues
|
|
10
|
+
Keywords: biosignal,streaming,real-time,inference,eeg,ecg,emg,ppg,edge
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: numpy>=1.21
|
|
25
|
+
Provides-Extra: onnx
|
|
26
|
+
Requires-Dist: onnxruntime>=1.14; extra == "onnx"
|
|
27
|
+
Provides-Extra: torch
|
|
28
|
+
Requires-Dist: torch>=2.0; extra == "torch"
|
|
29
|
+
Provides-Extra: brainflow
|
|
30
|
+
Requires-Dist: brainflow>=5.0; extra == "brainflow"
|
|
31
|
+
Provides-Extra: lsl
|
|
32
|
+
Requires-Dist: pylsl>=1.16; extra == "lsl"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# NeuroStream-Edge
|
|
39
|
+
|
|
40
|
+
> **A modular, interface-driven runtime for continuous biosignal processing and real-time inference.**
|
|
41
|
+
|
|
42
|
+
[](#status)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
[](https://www.python.org/downloads/)
|
|
45
|
+
[](https://pypi.org/project/neurostream-edge/)
|
|
46
|
+
[](#testing)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Overview
|
|
51
|
+
|
|
52
|
+
NeuroStream-Edge is an open-source runtime for building real-time biosignal
|
|
53
|
+
processing pipelines. It focuses on the **infrastructure between a continuous
|
|
54
|
+
sensor stream and machine learning inference** — providing modular components
|
|
55
|
+
for streaming, buffering, window generation, runtime orchestration, storage,
|
|
56
|
+
and performance analysis.
|
|
57
|
+
|
|
58
|
+
The runtime is designed to support multiple biosignal modalities — **EEG, ECG,
|
|
59
|
+
EMG, and PPG** — while remaining independent of any specific downstream task or
|
|
60
|
+
machine-learning architecture.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Highlights
|
|
65
|
+
|
|
66
|
+
- **Interface-driven architecture** — every layer exposes an abstract base class.
|
|
67
|
+
The runtime depends only on interfaces, never on concrete implementations.
|
|
68
|
+
- **Streaming-first, fixed-memory execution** — pre-allocated ring buffers with
|
|
69
|
+
O(1) writes and circular overwrite semantics. No growth, no GC pressure in the
|
|
70
|
+
hot path.
|
|
71
|
+
- **Model-agnostic & signal-agnostic** — bring your own ONNX / TorchScript model
|
|
72
|
+
or implement the `InferenceEngine` interface.
|
|
73
|
+
- **Minimal core dependencies** — the runtime requires only **NumPy**. Every
|
|
74
|
+
heavy backend (ONNX, PyTorch, BrainFlow, LSL) is an optional extra with a
|
|
75
|
+
clear `ImportError` and lazy import.
|
|
76
|
+
- **Observable by design** — a decoupled Observer-based event system feeds a
|
|
77
|
+
logger, profiler, and metrics collector without coupling into the execution path.
|
|
78
|
+
- **Online runtime and offline pipeline are intentionally separated** — reflecting
|
|
79
|
+
the different engineering requirements of low-latency inference and large-scale
|
|
80
|
+
dataset processing.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Status
|
|
85
|
+
|
|
86
|
+
**Alpha.** All eight core modules are implemented and covered by 95 passing tests
|
|
87
|
+
(~78% line coverage). The runtime is functional end-to-end with a reference
|
|
88
|
+
simulated pipeline, but is not yet benchmarked against external baselines and the
|
|
89
|
+
public API is still stabilising. See the [Roadmap](#roadmap) for what is planned.
|
|
90
|
+
|
|
91
|
+
| Module | Interface | Role | Status |
|
|
92
|
+
|--------|-----------|------|--------|
|
|
93
|
+
| `acquisition` | `SignalSource` | Continuous sample streams from sensors or simulators | ✅ |
|
|
94
|
+
| `streaming` | `Buffer` | Fixed-capacity, overwrite-on-full buffers | ✅ |
|
|
95
|
+
| `windowing` | `WindowGenerator` | Overlapping inference-ready windows | ✅ |
|
|
96
|
+
| `runtime` | — | Pipeline orchestration + event system | ✅ |
|
|
97
|
+
| `models` | `InferenceEngine` | Model-agnostic prediction backends | ✅ |
|
|
98
|
+
| `storage` | `StorageBackend` | Offline dataset recording / playback | ✅ |
|
|
99
|
+
| `observability` | `Observer` | Decoupled logging, profiling, metrics | ✅ |
|
|
100
|
+
| `benchmarking` | — | Throughput & latency characterisation | ✅ |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
### From PyPI
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install neurostream-edge
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### From source
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/tarunc997/NeuroStream-Edge.git
|
|
116
|
+
cd NeuroStream-Edge
|
|
117
|
+
pip install -e .
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Optional extras
|
|
121
|
+
|
|
122
|
+
Heavy backends are never installed automatically — install only what you need:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install "neurostream-edge[onnx]" # ONNX Runtime
|
|
126
|
+
pip install "neurostream-edge[torch]" # TorchScript
|
|
127
|
+
pip install "neurostream-edge[brainflow]" # BrainFlow hardware adapters
|
|
128
|
+
pip install "neurostream-edge[lsl]" # Lab Streaming Layer
|
|
129
|
+
pip install "neurostream-edge[dev]" # pytest, pytest-cov
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Extras can be combined, e.g. `pip install "neurostream-edge[onnx,dev]"`.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Quick start
|
|
137
|
+
|
|
138
|
+
A minimal online pipeline: **SimulatedSignalSource → RingBuffer → SlidingWindowGenerator → DummyInferenceEngine**.
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import time
|
|
142
|
+
import neurostream as ns
|
|
143
|
+
|
|
144
|
+
# 1. Build components (dependency injection)
|
|
145
|
+
source = ns.SimulatedSignalSource(sample_rate=250.0, n_channels=8, seed=42)
|
|
146
|
+
buffer = ns.RingBuffer(capacity=2500, n_channels=8) # 10 s @ 250 Hz
|
|
147
|
+
window = ns.SlidingWindowGenerator(buffer, window_size=250, # 1 s window
|
|
148
|
+
stride=50) # 0.2 s stride
|
|
149
|
+
engine = ns.DummyInferenceEngine(n_outputs=3)
|
|
150
|
+
|
|
151
|
+
# 2. Optional observability
|
|
152
|
+
bus = ns.EventBus()
|
|
153
|
+
metrics = ns.Metrics()
|
|
154
|
+
profiler = ns.Profiler()
|
|
155
|
+
bus.subscribe("*", metrics)
|
|
156
|
+
bus.subscribe("*", profiler)
|
|
157
|
+
|
|
158
|
+
# 3. Wire everything into the runtime
|
|
159
|
+
runtime = ns.Runtime(source=source, buffer=buffer, window=window,
|
|
160
|
+
inference=engine, event_bus=bus, read_chunk=10)
|
|
161
|
+
|
|
162
|
+
# 4. Run
|
|
163
|
+
runtime.start()
|
|
164
|
+
runtime.run_for(2.0) # run for 2 seconds
|
|
165
|
+
runtime.stop()
|
|
166
|
+
|
|
167
|
+
print(f"Predictions: {len(runtime.predictions)}")
|
|
168
|
+
print(f"Throughput : { {k: f'{v:.1f}/s' for k, v in metrics.throughput().items()} }")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
More examples live in [`examples/`](examples):
|
|
172
|
+
|
|
173
|
+
- [`online_pipeline.py`](examples/online_pipeline.py) — full online inference pipeline
|
|
174
|
+
- [`offline_recording.py`](examples/offline_recording.py) — record a stream to disk and replay it
|
|
175
|
+
- [`benchmark_buffers.py`](examples/benchmark_buffers.py) — compare buffer backends
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Architecture
|
|
180
|
+
|
|
181
|
+
The online inference runtime and offline data pipeline are intentionally
|
|
182
|
+
separated, reflecting the different engineering requirements of low-latency
|
|
183
|
+
inference and large-scale dataset processing.
|
|
184
|
+
|
|
185
|
+
### Online runtime
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
Continuous Signal
|
|
189
|
+
│
|
|
190
|
+
▼
|
|
191
|
+
Acquisition Layer
|
|
192
|
+
│
|
|
193
|
+
▼
|
|
194
|
+
Streaming Layer
|
|
195
|
+
(Ring Buffers)
|
|
196
|
+
│
|
|
197
|
+
▼
|
|
198
|
+
Windowing Layer
|
|
199
|
+
│
|
|
200
|
+
▼
|
|
201
|
+
Inference Runtime
|
|
202
|
+
│
|
|
203
|
+
▼
|
|
204
|
+
Model Interface
|
|
205
|
+
│
|
|
206
|
+
▼
|
|
207
|
+
Prediction
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Pipeline context
|
|
211
|
+
|
|
212
|
+
```text
|
|
213
|
+
Runtime (orchestrator)
|
|
214
|
+
│
|
|
215
|
+
┌────────────────┼────────────────┐
|
|
216
|
+
▼ ▼ ▼
|
|
217
|
+
Acquisition Streaming Windowing
|
|
218
|
+
(SignalSource) (Buffer) (WindowGenerator)
|
|
219
|
+
│ │ │
|
|
220
|
+
└────────────────┼────────────────┘
|
|
221
|
+
▼
|
|
222
|
+
Models (InferenceEngine)
|
|
223
|
+
│
|
|
224
|
+
Storage (offline)
|
|
225
|
+
|
|
226
|
+
Observability (Logger · Profiler · Metrics) ← Runtime events
|
|
227
|
+
Benchmarking (per-component measurement)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Design principles
|
|
231
|
+
|
|
232
|
+
- **Streaming-first** — designed around continuous signals, not static datasets.
|
|
233
|
+
- **Interface-driven** — the runtime depends only on abstractions; components are
|
|
234
|
+
interchangeable (Strategy pattern).
|
|
235
|
+
- **Composition over inheritance** — no per-modality `EEGRuntime` / `ECGRuntime`
|
|
236
|
+
classes; one runtime serves every signal type.
|
|
237
|
+
- **Fixed-memory execution** — storage is allocated once and never grows.
|
|
238
|
+
- **Storage-independent & model-agnostic** — backends are pluggable.
|
|
239
|
+
|
|
240
|
+
> **Allocation note:** the streaming write path is O(1) and allocation-free for
|
|
241
|
+
> contiguous writes. Read paths (`read()`, `latest()`) allocate copies to avoid
|
|
242
|
+
> exposing internal buffers.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Repository structure
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
docs/ # architectural & per-module reference
|
|
250
|
+
examples/ # runnable scripts
|
|
251
|
+
neurostream/
|
|
252
|
+
├── acquisition/ # signal sources & adapters
|
|
253
|
+
├── streaming/ # fixed-memory buffers
|
|
254
|
+
├── windowing/ # sliding-window generation
|
|
255
|
+
├── runtime/ # pipeline orchestration & events
|
|
256
|
+
├── models/ # inference engines & factory
|
|
257
|
+
├── storage/ # dataset backends
|
|
258
|
+
├── observability/ # logger, profiler, metrics
|
|
259
|
+
└── benchmarking/ # benchmarks & latency profiling
|
|
260
|
+
tests/ # 95 tests
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Documentation
|
|
266
|
+
|
|
267
|
+
Detailed per-module guides cover each layer's purpose, its abstract interface,
|
|
268
|
+
the concrete implementations and their trade-offs, and runnable usage examples.
|
|
269
|
+
|
|
270
|
+
| Module | Guide |
|
|
271
|
+
|--------|-------|
|
|
272
|
+
| Acquisition | [`docs/modules/acquisition.md`](docs/modules/acquisition.md) |
|
|
273
|
+
| Streaming | [`docs/modules/streaming.md`](docs/modules/streaming.md) |
|
|
274
|
+
| Windowing | [`docs/modules/windowing.md`](docs/modules/windowing.md) |
|
|
275
|
+
| Models | [`docs/modules/models.md`](docs/modules/models.md) |
|
|
276
|
+
| Storage | [`docs/modules/storage.md`](docs/modules/storage.md) |
|
|
277
|
+
| Runtime | [`docs/modules/runtime.md`](docs/modules/runtime.md) |
|
|
278
|
+
| Observability | [`docs/modules/observability.md`](docs/modules/observability.md) |
|
|
279
|
+
| Benchmarking | [`docs/modules/benchmarking.md`](docs/modules/benchmarking.md) |
|
|
280
|
+
|
|
281
|
+
Start at [`docs/index.md`](docs/index.md) for the architecture overview.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Testing
|
|
286
|
+
|
|
287
|
+
The suite uses **real concurrency** — actual background threads, real timing,
|
|
288
|
+
real predictions — rather than mocks, so integration behaviour is exercised
|
|
289
|
+
directly.
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
pip install "neurostream-edge[dev]"
|
|
293
|
+
pytest
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
With a coverage report:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
pytest --cov=neurostream --cov-report=term-missing
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Scope
|
|
305
|
+
|
|
306
|
+
NeuroStream-Edge is **infrastructure, not an end-user application.** It is not
|
|
307
|
+
tied to a specific machine-learning task such as seizure detection, sleep
|
|
308
|
+
staging, emotion recognition, or motor imagery. Instead, it provides the runtime
|
|
309
|
+
required to support continuous biosignal inference across multiple research and
|
|
310
|
+
deployment scenarios.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Roadmap
|
|
315
|
+
|
|
316
|
+
- [ ] End-to-end reference pipeline on a public dataset (e.g. TUH EEG, BCI Comp IV)
|
|
317
|
+
- [ ] Benchmark against a naive baseline using the built-in `benchmarking/` layer
|
|
318
|
+
- [ ] Convenience `quickstart()` constructor for low-friction onboarding
|
|
319
|
+
- [ ] Config-driven pipelines + `neurostream run` CLI (YAML → wired runtime)
|
|
320
|
+
- [ ] Plugin entry points (`[project.entry-points]`) for third-party engines/sources
|
|
321
|
+
- [ ] Explicit backpressure / drop policies (`block` / `drop-newest` / `drop-oldest`)
|
|
322
|
+
- [ ] Source & storage factories to match the existing `InferenceFactory`
|
|
323
|
+
- [ ] Async / queued EventBus option to fully decouple observers from the tick
|
|
324
|
+
- [ ] `CITATION.cff` and first tagged release
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Contributing
|
|
329
|
+
|
|
330
|
+
Contributions are welcome. The project follows an interface-driven architecture —
|
|
331
|
+
every layer exposes an abstract base class, and the runtime depends only on
|
|
332
|
+
interfaces. Before adding new functionality, open an issue to discuss scope.
|
|
333
|
+
|
|
334
|
+
When contributing, please:
|
|
335
|
+
|
|
336
|
+
1. Keep the runtime free of concrete-component dependencies (depend on interfaces).
|
|
337
|
+
2. Add or update tests for any behavioural change.
|
|
338
|
+
3. Run `pytest` and ensure the full suite passes before submitting a PR.
|
|
339
|
+
4. Follow the existing numpy-style docstring conventions.
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
[MIT License](LICENSE) © Tarun Chilkur
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# NeuroStream-Edge
|
|
2
|
+
|
|
3
|
+
> **A modular, interface-driven runtime for continuous biosignal processing and real-time inference.**
|
|
4
|
+
|
|
5
|
+
[](#status)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://pypi.org/project/neurostream-edge/)
|
|
9
|
+
[](#testing)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
NeuroStream-Edge is an open-source runtime for building real-time biosignal
|
|
16
|
+
processing pipelines. It focuses on the **infrastructure between a continuous
|
|
17
|
+
sensor stream and machine learning inference** — providing modular components
|
|
18
|
+
for streaming, buffering, window generation, runtime orchestration, storage,
|
|
19
|
+
and performance analysis.
|
|
20
|
+
|
|
21
|
+
The runtime is designed to support multiple biosignal modalities — **EEG, ECG,
|
|
22
|
+
EMG, and PPG** — while remaining independent of any specific downstream task or
|
|
23
|
+
machine-learning architecture.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Highlights
|
|
28
|
+
|
|
29
|
+
- **Interface-driven architecture** — every layer exposes an abstract base class.
|
|
30
|
+
The runtime depends only on interfaces, never on concrete implementations.
|
|
31
|
+
- **Streaming-first, fixed-memory execution** — pre-allocated ring buffers with
|
|
32
|
+
O(1) writes and circular overwrite semantics. No growth, no GC pressure in the
|
|
33
|
+
hot path.
|
|
34
|
+
- **Model-agnostic & signal-agnostic** — bring your own ONNX / TorchScript model
|
|
35
|
+
or implement the `InferenceEngine` interface.
|
|
36
|
+
- **Minimal core dependencies** — the runtime requires only **NumPy**. Every
|
|
37
|
+
heavy backend (ONNX, PyTorch, BrainFlow, LSL) is an optional extra with a
|
|
38
|
+
clear `ImportError` and lazy import.
|
|
39
|
+
- **Observable by design** — a decoupled Observer-based event system feeds a
|
|
40
|
+
logger, profiler, and metrics collector without coupling into the execution path.
|
|
41
|
+
- **Online runtime and offline pipeline are intentionally separated** — reflecting
|
|
42
|
+
the different engineering requirements of low-latency inference and large-scale
|
|
43
|
+
dataset processing.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Status
|
|
48
|
+
|
|
49
|
+
**Alpha.** All eight core modules are implemented and covered by 95 passing tests
|
|
50
|
+
(~78% line coverage). The runtime is functional end-to-end with a reference
|
|
51
|
+
simulated pipeline, but is not yet benchmarked against external baselines and the
|
|
52
|
+
public API is still stabilising. See the [Roadmap](#roadmap) for what is planned.
|
|
53
|
+
|
|
54
|
+
| Module | Interface | Role | Status |
|
|
55
|
+
|--------|-----------|------|--------|
|
|
56
|
+
| `acquisition` | `SignalSource` | Continuous sample streams from sensors or simulators | ✅ |
|
|
57
|
+
| `streaming` | `Buffer` | Fixed-capacity, overwrite-on-full buffers | ✅ |
|
|
58
|
+
| `windowing` | `WindowGenerator` | Overlapping inference-ready windows | ✅ |
|
|
59
|
+
| `runtime` | — | Pipeline orchestration + event system | ✅ |
|
|
60
|
+
| `models` | `InferenceEngine` | Model-agnostic prediction backends | ✅ |
|
|
61
|
+
| `storage` | `StorageBackend` | Offline dataset recording / playback | ✅ |
|
|
62
|
+
| `observability` | `Observer` | Decoupled logging, profiling, metrics | ✅ |
|
|
63
|
+
| `benchmarking` | — | Throughput & latency characterisation | ✅ |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
### From PyPI
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install neurostream-edge
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### From source
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/tarunc997/NeuroStream-Edge.git
|
|
79
|
+
cd NeuroStream-Edge
|
|
80
|
+
pip install -e .
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Optional extras
|
|
84
|
+
|
|
85
|
+
Heavy backends are never installed automatically — install only what you need:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install "neurostream-edge[onnx]" # ONNX Runtime
|
|
89
|
+
pip install "neurostream-edge[torch]" # TorchScript
|
|
90
|
+
pip install "neurostream-edge[brainflow]" # BrainFlow hardware adapters
|
|
91
|
+
pip install "neurostream-edge[lsl]" # Lab Streaming Layer
|
|
92
|
+
pip install "neurostream-edge[dev]" # pytest, pytest-cov
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Extras can be combined, e.g. `pip install "neurostream-edge[onnx,dev]"`.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Quick start
|
|
100
|
+
|
|
101
|
+
A minimal online pipeline: **SimulatedSignalSource → RingBuffer → SlidingWindowGenerator → DummyInferenceEngine**.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import time
|
|
105
|
+
import neurostream as ns
|
|
106
|
+
|
|
107
|
+
# 1. Build components (dependency injection)
|
|
108
|
+
source = ns.SimulatedSignalSource(sample_rate=250.0, n_channels=8, seed=42)
|
|
109
|
+
buffer = ns.RingBuffer(capacity=2500, n_channels=8) # 10 s @ 250 Hz
|
|
110
|
+
window = ns.SlidingWindowGenerator(buffer, window_size=250, # 1 s window
|
|
111
|
+
stride=50) # 0.2 s stride
|
|
112
|
+
engine = ns.DummyInferenceEngine(n_outputs=3)
|
|
113
|
+
|
|
114
|
+
# 2. Optional observability
|
|
115
|
+
bus = ns.EventBus()
|
|
116
|
+
metrics = ns.Metrics()
|
|
117
|
+
profiler = ns.Profiler()
|
|
118
|
+
bus.subscribe("*", metrics)
|
|
119
|
+
bus.subscribe("*", profiler)
|
|
120
|
+
|
|
121
|
+
# 3. Wire everything into the runtime
|
|
122
|
+
runtime = ns.Runtime(source=source, buffer=buffer, window=window,
|
|
123
|
+
inference=engine, event_bus=bus, read_chunk=10)
|
|
124
|
+
|
|
125
|
+
# 4. Run
|
|
126
|
+
runtime.start()
|
|
127
|
+
runtime.run_for(2.0) # run for 2 seconds
|
|
128
|
+
runtime.stop()
|
|
129
|
+
|
|
130
|
+
print(f"Predictions: {len(runtime.predictions)}")
|
|
131
|
+
print(f"Throughput : { {k: f'{v:.1f}/s' for k, v in metrics.throughput().items()} }")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
More examples live in [`examples/`](examples):
|
|
135
|
+
|
|
136
|
+
- [`online_pipeline.py`](examples/online_pipeline.py) — full online inference pipeline
|
|
137
|
+
- [`offline_recording.py`](examples/offline_recording.py) — record a stream to disk and replay it
|
|
138
|
+
- [`benchmark_buffers.py`](examples/benchmark_buffers.py) — compare buffer backends
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Architecture
|
|
143
|
+
|
|
144
|
+
The online inference runtime and offline data pipeline are intentionally
|
|
145
|
+
separated, reflecting the different engineering requirements of low-latency
|
|
146
|
+
inference and large-scale dataset processing.
|
|
147
|
+
|
|
148
|
+
### Online runtime
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
Continuous Signal
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
Acquisition Layer
|
|
155
|
+
│
|
|
156
|
+
▼
|
|
157
|
+
Streaming Layer
|
|
158
|
+
(Ring Buffers)
|
|
159
|
+
│
|
|
160
|
+
▼
|
|
161
|
+
Windowing Layer
|
|
162
|
+
│
|
|
163
|
+
▼
|
|
164
|
+
Inference Runtime
|
|
165
|
+
│
|
|
166
|
+
▼
|
|
167
|
+
Model Interface
|
|
168
|
+
│
|
|
169
|
+
▼
|
|
170
|
+
Prediction
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Pipeline context
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
Runtime (orchestrator)
|
|
177
|
+
│
|
|
178
|
+
┌────────────────┼────────────────┐
|
|
179
|
+
▼ ▼ ▼
|
|
180
|
+
Acquisition Streaming Windowing
|
|
181
|
+
(SignalSource) (Buffer) (WindowGenerator)
|
|
182
|
+
│ │ │
|
|
183
|
+
└────────────────┼────────────────┘
|
|
184
|
+
▼
|
|
185
|
+
Models (InferenceEngine)
|
|
186
|
+
│
|
|
187
|
+
Storage (offline)
|
|
188
|
+
|
|
189
|
+
Observability (Logger · Profiler · Metrics) ← Runtime events
|
|
190
|
+
Benchmarking (per-component measurement)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Design principles
|
|
194
|
+
|
|
195
|
+
- **Streaming-first** — designed around continuous signals, not static datasets.
|
|
196
|
+
- **Interface-driven** — the runtime depends only on abstractions; components are
|
|
197
|
+
interchangeable (Strategy pattern).
|
|
198
|
+
- **Composition over inheritance** — no per-modality `EEGRuntime` / `ECGRuntime`
|
|
199
|
+
classes; one runtime serves every signal type.
|
|
200
|
+
- **Fixed-memory execution** — storage is allocated once and never grows.
|
|
201
|
+
- **Storage-independent & model-agnostic** — backends are pluggable.
|
|
202
|
+
|
|
203
|
+
> **Allocation note:** the streaming write path is O(1) and allocation-free for
|
|
204
|
+
> contiguous writes. Read paths (`read()`, `latest()`) allocate copies to avoid
|
|
205
|
+
> exposing internal buffers.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Repository structure
|
|
210
|
+
|
|
211
|
+
```text
|
|
212
|
+
docs/ # architectural & per-module reference
|
|
213
|
+
examples/ # runnable scripts
|
|
214
|
+
neurostream/
|
|
215
|
+
├── acquisition/ # signal sources & adapters
|
|
216
|
+
├── streaming/ # fixed-memory buffers
|
|
217
|
+
├── windowing/ # sliding-window generation
|
|
218
|
+
├── runtime/ # pipeline orchestration & events
|
|
219
|
+
├── models/ # inference engines & factory
|
|
220
|
+
├── storage/ # dataset backends
|
|
221
|
+
├── observability/ # logger, profiler, metrics
|
|
222
|
+
└── benchmarking/ # benchmarks & latency profiling
|
|
223
|
+
tests/ # 95 tests
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Documentation
|
|
229
|
+
|
|
230
|
+
Detailed per-module guides cover each layer's purpose, its abstract interface,
|
|
231
|
+
the concrete implementations and their trade-offs, and runnable usage examples.
|
|
232
|
+
|
|
233
|
+
| Module | Guide |
|
|
234
|
+
|--------|-------|
|
|
235
|
+
| Acquisition | [`docs/modules/acquisition.md`](docs/modules/acquisition.md) |
|
|
236
|
+
| Streaming | [`docs/modules/streaming.md`](docs/modules/streaming.md) |
|
|
237
|
+
| Windowing | [`docs/modules/windowing.md`](docs/modules/windowing.md) |
|
|
238
|
+
| Models | [`docs/modules/models.md`](docs/modules/models.md) |
|
|
239
|
+
| Storage | [`docs/modules/storage.md`](docs/modules/storage.md) |
|
|
240
|
+
| Runtime | [`docs/modules/runtime.md`](docs/modules/runtime.md) |
|
|
241
|
+
| Observability | [`docs/modules/observability.md`](docs/modules/observability.md) |
|
|
242
|
+
| Benchmarking | [`docs/modules/benchmarking.md`](docs/modules/benchmarking.md) |
|
|
243
|
+
|
|
244
|
+
Start at [`docs/index.md`](docs/index.md) for the architecture overview.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Testing
|
|
249
|
+
|
|
250
|
+
The suite uses **real concurrency** — actual background threads, real timing,
|
|
251
|
+
real predictions — rather than mocks, so integration behaviour is exercised
|
|
252
|
+
directly.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
pip install "neurostream-edge[dev]"
|
|
256
|
+
pytest
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
With a coverage report:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
pytest --cov=neurostream --cov-report=term-missing
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Scope
|
|
268
|
+
|
|
269
|
+
NeuroStream-Edge is **infrastructure, not an end-user application.** It is not
|
|
270
|
+
tied to a specific machine-learning task such as seizure detection, sleep
|
|
271
|
+
staging, emotion recognition, or motor imagery. Instead, it provides the runtime
|
|
272
|
+
required to support continuous biosignal inference across multiple research and
|
|
273
|
+
deployment scenarios.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Roadmap
|
|
278
|
+
|
|
279
|
+
- [ ] End-to-end reference pipeline on a public dataset (e.g. TUH EEG, BCI Comp IV)
|
|
280
|
+
- [ ] Benchmark against a naive baseline using the built-in `benchmarking/` layer
|
|
281
|
+
- [ ] Convenience `quickstart()` constructor for low-friction onboarding
|
|
282
|
+
- [ ] Config-driven pipelines + `neurostream run` CLI (YAML → wired runtime)
|
|
283
|
+
- [ ] Plugin entry points (`[project.entry-points]`) for third-party engines/sources
|
|
284
|
+
- [ ] Explicit backpressure / drop policies (`block` / `drop-newest` / `drop-oldest`)
|
|
285
|
+
- [ ] Source & storage factories to match the existing `InferenceFactory`
|
|
286
|
+
- [ ] Async / queued EventBus option to fully decouple observers from the tick
|
|
287
|
+
- [ ] `CITATION.cff` and first tagged release
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Contributing
|
|
292
|
+
|
|
293
|
+
Contributions are welcome. The project follows an interface-driven architecture —
|
|
294
|
+
every layer exposes an abstract base class, and the runtime depends only on
|
|
295
|
+
interfaces. Before adding new functionality, open an issue to discuss scope.
|
|
296
|
+
|
|
297
|
+
When contributing, please:
|
|
298
|
+
|
|
299
|
+
1. Keep the runtime free of concrete-component dependencies (depend on interfaces).
|
|
300
|
+
2. Add or update tests for any behavioural change.
|
|
301
|
+
3. Run `pytest` and ensure the full suite passes before submitting a PR.
|
|
302
|
+
4. Follow the existing numpy-style docstring conventions.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
[MIT License](LICENSE) © Tarun Chilkur
|