natus-erd-reader 0.2.1__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.
- natus_erd_reader-0.2.1/CHANGELOG.md +58 -0
- natus_erd_reader-0.2.1/CONTRIBUTING.md +59 -0
- natus_erd_reader-0.2.1/LICENSE +21 -0
- natus_erd_reader-0.2.1/MANIFEST.in +11 -0
- natus_erd_reader-0.2.1/PKG-INFO +289 -0
- natus_erd_reader-0.2.1/README.md +255 -0
- natus_erd_reader-0.2.1/SECURITY.md +41 -0
- natus_erd_reader-0.2.1/THIRD_PARTY_NOTICES.md +42 -0
- natus_erd_reader-0.2.1/examples/read_window.py +33 -0
- natus_erd_reader-0.2.1/examples/validate_recording.py +580 -0
- natus_erd_reader-0.2.1/pyproject.toml +47 -0
- natus_erd_reader-0.2.1/setup.cfg +4 -0
- natus_erd_reader-0.2.1/src/natus_erd/__init__.py +26 -0
- natus_erd_reader-0.2.1/src/natus_erd/_paths.py +92 -0
- natus_erd_reader-0.2.1/src/natus_erd/binary.py +292 -0
- natus_erd_reader-0.2.1/src/natus_erd/decoder.py +207 -0
- natus_erd_reader-0.2.1/src/natus_erd/ent.py +474 -0
- natus_erd_reader-0.2.1/src/natus_erd/errors.py +21 -0
- natus_erd_reader-0.2.1/src/natus_erd/limits.py +60 -0
- natus_erd_reader-0.2.1/src/natus_erd/models.py +65 -0
- natus_erd_reader-0.2.1/src/natus_erd/py.typed +0 -0
- natus_erd_reader-0.2.1/src/natus_erd/reader.py +567 -0
- natus_erd_reader-0.2.1/src/natus_erd_reader.egg-info/PKG-INFO +289 -0
- natus_erd_reader-0.2.1/src/natus_erd_reader.egg-info/SOURCES.txt +41 -0
- natus_erd_reader-0.2.1/src/natus_erd_reader.egg-info/dependency_links.txt +1 -0
- natus_erd_reader-0.2.1/src/natus_erd_reader.egg-info/requires.txt +6 -0
- natus_erd_reader-0.2.1/src/natus_erd_reader.egg-info/top_level.txt +1 -0
- natus_erd_reader-0.2.1/tests/__init__.py +1 -0
- natus_erd_reader-0.2.1/tests/_fixture.py +204 -0
- natus_erd_reader-0.2.1/tests/test_channel_compat.py +143 -0
- natus_erd_reader-0.2.1/tests/test_decoder_limits.py +173 -0
- natus_erd_reader-0.2.1/tests/test_distribution_audit.py +131 -0
- natus_erd_reader-0.2.1/tests/test_ent_decoder.py +66 -0
- natus_erd_reader-0.2.1/tests/test_ent_safety.py +206 -0
- natus_erd_reader-0.2.1/tests/test_gap_compat.py +236 -0
- natus_erd_reader-0.2.1/tests/test_package.py +58 -0
- natus_erd_reader-0.2.1/tests/test_path_compat.py +168 -0
- natus_erd_reader-0.2.1/tests/test_read_limits.py +154 -0
- natus_erd_reader-0.2.1/tests/test_reader.py +121 -0
- natus_erd_reader-0.2.1/tests/test_sampling_compat.py +158 -0
- natus_erd_reader-0.2.1/tests/test_security_paths.py +138 -0
- natus_erd_reader-0.2.1/tests/test_validation_example.py +255 -0
- natus_erd_reader-0.2.1/tools/check_dist.py +132 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.1 — 2026-09-04
|
|
4
|
+
|
|
5
|
+
Compatibility fixes within the existing schema-9 Quantum layout; no new
|
|
6
|
+
application entry points, hardware calibration or resampling behavior.
|
|
7
|
+
|
|
8
|
+
- Read the finite positive sample rate from ERD headers instead of requiring
|
|
9
|
+
2048 Hz. Reject non-finite recording duration and inconsistent segment rates.
|
|
10
|
+
- Interpret STC stored counts separately from stamp spans, verify ETC counts,
|
|
11
|
+
and preserve leading, internal, trailing and fully empty gaps as NaN.
|
|
12
|
+
- Resolve complete recording filenames case-insensitively with ambiguity and
|
|
13
|
+
directory-boundary checks. Explicit STC or matching EEG can select among
|
|
14
|
+
multiple recordings; an unrelated ordinary `.stc` directory is ignored.
|
|
15
|
+
- Keep positional channel-label placeholders, avoid fallback-name collisions
|
|
16
|
+
and require index selection for genuinely duplicated vendor labels.
|
|
17
|
+
- Add bounded public-API validation examples without a fixed channel label;
|
|
18
|
+
adapt sample windows to the actual rate and redact unknown error details.
|
|
19
|
+
- Extend synthetic compatibility coverage, add Python 3.13 CI, and test the
|
|
20
|
+
minimum NumPy 1.24.x dependency on Python 3.10 for Windows and Linux.
|
|
21
|
+
- Preserve all existing read/parser budgets and Python-only runtime surface.
|
|
22
|
+
|
|
23
|
+
## 0.2.0 — source-only update
|
|
24
|
+
|
|
25
|
+
Python-library-only release with explicit resource limits. This is a breaking
|
|
26
|
+
change from 0.1.0, and remains experimental research software.
|
|
27
|
+
|
|
28
|
+
- Remove both installed command-line programs and `python -m natus_erd`.
|
|
29
|
+
- Remove EDF APIs, comparison tools, plotting extras and the local web viewer.
|
|
30
|
+
- Keep native ERD window reads, structural metadata, ENT events and validation.
|
|
31
|
+
- Add `ReadLimits` and `ResourceLimitError`; default output budget is 64 MiB
|
|
32
|
+
per read, with at most 131,072 samples per call. Reject oversized requests
|
|
33
|
+
before allocation and NumPy import.
|
|
34
|
+
- Add `iter_samples()` for chunked processing without retaining a full record.
|
|
35
|
+
- Bound metadata reads and ENT parsing; validate compressed-packet lengths and
|
|
36
|
+
decode through a bounded buffer instead of loading a whole packet.
|
|
37
|
+
- Parse ENT using an independent restricted grammar; never execute input.
|
|
38
|
+
- Load NumPy only when an accepted sample request needs an array. Do not
|
|
39
|
+
modify global numerical-backend thread settings.
|
|
40
|
+
- Require an explicit recording directory or entry-point file rather than
|
|
41
|
+
recursively searching broad parent directories.
|
|
42
|
+
- Extend synthetic resource-boundary tests and distribution checks. No
|
|
43
|
+
operating-system crash-prevention or clinical-safety claim is made.
|
|
44
|
+
|
|
45
|
+
### Migration from 0.1.0
|
|
46
|
+
|
|
47
|
+
Use `NatusERDReader` directly from Python; there is no replacement executable.
|
|
48
|
+
`EDFInfo`, `EDFReader` and `EDFSignal` are no longer exported or included.
|
|
49
|
+
Remove optional `plot`/`validation` extras from dependency declarations.
|
|
50
|
+
Split large `read_samples` calls with `iter_samples`, or choose an explicit
|
|
51
|
+
`ReadLimits` policy appropriate to the application. Raising the limits is an
|
|
52
|
+
application decision and requires its own memory planning.
|
|
53
|
+
|
|
54
|
+
## 0.1.0
|
|
55
|
+
|
|
56
|
+
Initial research release of the schema-9 Quantum reader, before the bounded
|
|
57
|
+
Python-only API introduced in 0.2.0. The legacy release did not sufficiently
|
|
58
|
+
constrain output allocation, metadata reads or compressed-packet reads.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use Python 3.10 or newer. A clean development environment is recommended:
|
|
4
|
+
|
|
5
|
+
```shell
|
|
6
|
+
python -m venv .venv
|
|
7
|
+
# Activate the environment using the command appropriate for your shell.
|
|
8
|
+
python -m pip install -e ".[dev]"
|
|
9
|
+
python -m unittest discover -v -s tests -t .
|
|
10
|
+
python -m build
|
|
11
|
+
python -m twine check dist/*
|
|
12
|
+
python tools/check_dist.py dist
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
All automated tests generate synthetic recordings at runtime. Do not add
|
|
16
|
+
clinical recordings, patient names, event text, exported waveforms, local
|
|
17
|
+
credentials or derived plots to issues, pull requests, commits or artifacts.
|
|
18
|
+
When reporting a format problem, supply sanitized structural metadata and a
|
|
19
|
+
synthetic reproducer where possible.
|
|
20
|
+
|
|
21
|
+
Keep unsupported schemas and headboxes explicit. Do not silently reuse the
|
|
22
|
+
Quantum calibration for unverified hardware. Changes to packet decoding need
|
|
23
|
+
tests for absolute values, delta sentinels, shorted channels and packet edges.
|
|
24
|
+
Resource-boundary tests must intercept oversized requests or use synthetic
|
|
25
|
+
bounded input; do not actually allocate gigabytes to test rejection paths.
|
|
26
|
+
Keep metadata-only imports free of NumPy and never alter the user's global
|
|
27
|
+
numerical-backend settings from package code.
|
|
28
|
+
|
|
29
|
+
CI sets `OPENBLAS_NUM_THREADS=1` and `OMP_NUM_THREADS=1` for the test runner
|
|
30
|
+
to keep backend initialization predictable. These are application-side test
|
|
31
|
+
settings, not package behavior or a substitute for an operating-system memory
|
|
32
|
+
limit. Backend-specific failure diagnosis should record the actual environment
|
|
33
|
+
and avoid unsafe reproduction on a user's machine.
|
|
34
|
+
|
|
35
|
+
The installed distribution exposes Python APIs only. Do not add executables,
|
|
36
|
+
web assets, EDF readers, optional plotting dependencies or real data to it.
|
|
37
|
+
`tools/check_dist.py` is a development audit script, not an installed command.
|
|
38
|
+
|
|
39
|
+
## Release checklist
|
|
40
|
+
|
|
41
|
+
1. Update the versions in `pyproject.toml` and `src/natus_erd/__init__.py`.
|
|
42
|
+
2. Update `CHANGELOG.md`; run the unit suite and distribution audit from a
|
|
43
|
+
clean staging tree so removed files cannot survive in build caches.
|
|
44
|
+
3. Confirm `git ls-files` contains no recordings, reports or credentials.
|
|
45
|
+
4. Merge to `main` and wait for CI to succeed.
|
|
46
|
+
5. Push an annotated `vX.Y.Z` tag. The release workflow builds and attaches a
|
|
47
|
+
wheel and source archive to the GitHub release.
|
|
48
|
+
|
|
49
|
+
6. For an explicitly authorized PyPI publication, configure a PyPI Trusted
|
|
50
|
+
Publisher for owner `ms903`, repository `natus-erd-reader`, workflow
|
|
51
|
+
`publish-pypi.yml`, and environment `pypi`. A new PyPI project requires a
|
|
52
|
+
pending publisher under the account's Publishing settings. See the
|
|
53
|
+
[official PyPI setup guide](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/).
|
|
54
|
+
7. Manually run `publish-pypi.yml` with the released tag. It audits the existing
|
|
55
|
+
GitHub Release archives and uploads those same files using short-lived OIDC
|
|
56
|
+
credentials. It does not rebuild them or require a stored PyPI API token.
|
|
57
|
+
|
|
58
|
+
PyPI publication is a separate, explicitly authorized step, not triggered by
|
|
59
|
+
ordinary pushes or by the GitHub Release workflow. Never commit credentials.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ms903 and natus-erd-reader contributors
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
include README.md LICENSE THIRD_PARTY_NOTICES.md CHANGELOG.md CONTRIBUTING.md SECURITY.md
|
|
2
|
+
include pyproject.toml MANIFEST.in
|
|
3
|
+
recursive-include src/natus_erd *.py py.typed
|
|
4
|
+
recursive-include tests *.py
|
|
5
|
+
recursive-include tools *.py
|
|
6
|
+
recursive-include examples *.py
|
|
7
|
+
prune data
|
|
8
|
+
prune figures
|
|
9
|
+
prune reports
|
|
10
|
+
prune .github
|
|
11
|
+
global-exclude __pycache__ *.py[cod] *.erd *.etc *.stc *.eeg *.ent *.edf *.bdf *.npy *.npz
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: natus-erd-reader
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Resource-bounded Python reader for Natus NeuroWorks schema-9 ERD recordings
|
|
5
|
+
Author: ms903 and natus-erd-reader contributors
|
|
6
|
+
License-Expression: MIT AND BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/ms903/natus-erd-reader
|
|
8
|
+
Project-URL: Repository, https://github.com/ms903/natus-erd-reader
|
|
9
|
+
Project-URL: Issues, https://github.com/ms903/natus-erd-reader/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/ms903/natus-erd-reader/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: eeg,seeg,natus,neuroworks,erd
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
License-File: THIRD_PARTY_NOTICES.md
|
|
28
|
+
Requires-Dist: numpy>=1.24
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# natus-erd-reader
|
|
36
|
+
|
|
37
|
+
[](https://github.com/ms903/natus-erd-reader/actions/workflows/ci.yml)
|
|
38
|
+
|
|
39
|
+
A small Python library for native Natus NeuroWorks ERD recordings. It reads
|
|
40
|
+
sample windows through the STC/ETC indexes, decodes schema-9 packets and
|
|
41
|
+
extracts ENT annotations. It has **no command-line application, web server,
|
|
42
|
+
EDF reader, plotting tool or vendor SDK dependency**.
|
|
43
|
+
|
|
44
|
+
Version 0.2.1 adds header-driven sample rates and compatibility fixes while
|
|
45
|
+
preserving bounded file reads and allocations. Python 3.10+ is
|
|
46
|
+
required; NumPy is the only runtime dependency and is imported only when a
|
|
47
|
+
valid sample-read request needs an array. Opening a recording and inspecting
|
|
48
|
+
metadata or events do not initialize NumPy.
|
|
49
|
+
|
|
50
|
+
This is **experimental research software**, not a certified diagnostic device.
|
|
51
|
+
It is not affiliated with or endorsed by Natus. The supported hardware/layout
|
|
52
|
+
is deliberately narrow, and no guarantee is made against operating-system,
|
|
53
|
+
driver, hardware or numerical-library failures.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
With Python 3.10 or newer, install **0.2.1** directly from GitHub Releases
|
|
58
|
+
(no Git installation or manual download required):
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
python -m pip install --upgrade "https://github.com/ms903/natus-erd-reader/releases/download/v0.2.1/natus_erd_reader-0.2.1-py3-none-any.whl"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run this command in the Python environment where you want to use the reader.
|
|
65
|
+
NumPy is installed automatically if needed. To preserve an existing,
|
|
66
|
+
compatible NumPy installation, add `--no-deps`.
|
|
67
|
+
|
|
68
|
+
Once the version is available on [PyPI](https://pypi.org/project/natus-erd-reader/),
|
|
69
|
+
it can also be installed by name:
|
|
70
|
+
|
|
71
|
+
```shell
|
|
72
|
+
python -m pip install --upgrade "natus-erd-reader==0.2.1"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The GitHub Release command above does not depend on PyPI availability.
|
|
76
|
+
|
|
77
|
+
Check the installed version without loading NumPy or opening a recording:
|
|
78
|
+
|
|
79
|
+
```shell
|
|
80
|
+
python -c "from importlib.metadata import version; print(version('natus-erd-reader'))"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For development source from the current `main` branch, or from a local checkout:
|
|
84
|
+
|
|
85
|
+
```shell
|
|
86
|
+
python -m pip install --upgrade "https://github.com/ms903/natus-erd-reader/archive/refs/heads/main.zip"
|
|
87
|
+
python -m pip install .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Tagged releases are listed in
|
|
91
|
+
[GitHub Releases](https://github.com/ms903/natus-erd-reader/releases).
|
|
92
|
+
The distribution name is
|
|
93
|
+
`natus-erd-reader`; the import name is `natus_erd`.
|
|
94
|
+
|
|
95
|
+
## Read a small window
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from math import ceil
|
|
99
|
+
|
|
100
|
+
from natus_erd import NatusERDReader
|
|
101
|
+
|
|
102
|
+
# Pass the recording directory itself, or its .stc, .eeg or .erd file.
|
|
103
|
+
reader = NatusERDReader.open(r"D:\path\to\recording")
|
|
104
|
+
print(reader.info) # Structural counts, not patient header fields.
|
|
105
|
+
|
|
106
|
+
# Start is inclusive; stop is exclusive. Both are sample numbers.
|
|
107
|
+
# Target one second, using the actual ERD header rate and recording length.
|
|
108
|
+
stop = min(reader.info.n_samples, ceil(reader.info.sample_rate))
|
|
109
|
+
data = reader.read_samples(0, stop, channels=[0, 1])
|
|
110
|
+
print(data.shape, data.dtype) # (2, stop), float64
|
|
111
|
+
|
|
112
|
+
digital = reader.read_samples(0, stop, channels=[0, 1], units="digital")
|
|
113
|
+
events = reader.read_events()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Arrays have shape `(channels, samples)`, always with `float64` dtype. The
|
|
117
|
+
default unit is microvolts (`units="uV"`); `"digital"` returns decoded native
|
|
118
|
+
counts without clipping to int16. Channel selection accepts zero-based
|
|
119
|
+
indices or names and preserves the requested order. Omitting `channels`
|
|
120
|
+
selects the first 256 signal channels. Shorted channels and known gaps are
|
|
121
|
+
returned as `NaN`, not zero.
|
|
122
|
+
STC stored-sample counts can be smaller than the stamp-defined time span;
|
|
123
|
+
the ETC packet stamps determine where samples are present. Leading, internal
|
|
124
|
+
and trailing gaps remain on the original time axis: they are not removed,
|
|
125
|
+
interpolated or compressed. Reads crossing a gap retain it as `NaN`.
|
|
126
|
+
|
|
127
|
+
Sample rates are read from the ERD header, not assumed to be 2048 Hz. For
|
|
128
|
+
example, 2048 samples cover four seconds at 512 Hz and one second at 2048 Hz.
|
|
129
|
+
The reader does not resample. A one-second request at an unusually high rate
|
|
130
|
+
can exceed the unchanged resource limits; use a shorter window or chunks.
|
|
131
|
+
|
|
132
|
+
Sample zero corresponds to the first STC stamp. `sample_to_stamp()` and
|
|
133
|
+
`stamp_to_sample()` convert between relative sample positions and native
|
|
134
|
+
stamps. `reader.info`, `reader.channels`, `reader.read_events()` and
|
|
135
|
+
`reader.validate()` expose structural metadata, channel descriptions, events
|
|
136
|
+
and a structural validation report, respectively. Validation is not a
|
|
137
|
+
complete decoding or clinical validation of every sample.
|
|
138
|
+
|
|
139
|
+
The reader does not recursively search arbitrary parent directories. Pass
|
|
140
|
+
the actual recording directory and keep its index and segment files together.
|
|
141
|
+
Filenames are matched case-insensitively, including their extensions, but
|
|
142
|
+
ambiguous case-folded duplicates are rejected. In a directory containing
|
|
143
|
+
multiple recordings, pass an explicit `.stc` or its matching `.eeg`; a
|
|
144
|
+
directory or `.erd` alone cannot select a unique recording. An ENT file is
|
|
145
|
+
optional; `.ent.old` is used only if `.ent` is absent, never to conceal a
|
|
146
|
+
corrupt `.ent`. Missing channel labels receive positional fallback names;
|
|
147
|
+
ambiguous vendor labels must be selected by index.
|
|
148
|
+
Only completed, static recordings are supported: do not modify the files or
|
|
149
|
+
continue acquisition into them while a reader is open. Size, timestamp and
|
|
150
|
+
file-identity checks detect some concurrent changes, but detection is
|
|
151
|
+
best-effort and does not provide a consistent snapshot of changing files.
|
|
152
|
+
|
|
153
|
+
## Keep larger reads bounded
|
|
154
|
+
|
|
155
|
+
The default output budget is **64 MiB per `read_samples` call**. A request
|
|
156
|
+
that would exceed it raises `ResourceLimitError` before allocating the output
|
|
157
|
+
or importing NumPy. An output needs `selected_channels × samples × 8` bytes.
|
|
158
|
+
Thus a one-second, two-channel read at 2048 Hz needs 32 KiB for its output;
|
|
159
|
+
reading an entire long recording at once is intentionally rejected.
|
|
160
|
+
There is also a default limit of **131,072 samples per call** (64 seconds at
|
|
161
|
+
2048 Hz), even when only one channel is selected, to bound decoding work.
|
|
162
|
+
|
|
163
|
+
Process long recordings incrementally:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
for chunk in reader.iter_samples(
|
|
167
|
+
start=0,
|
|
168
|
+
stop=reader.info.n_samples,
|
|
169
|
+
chunk_samples=20480,
|
|
170
|
+
channels=[0, 1],
|
|
171
|
+
units="uV",
|
|
172
|
+
):
|
|
173
|
+
# Analyze or write this chunk, then let it go before requesting the next.
|
|
174
|
+
print(chunk.shape)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Each chunk is checked against the same read budget. **Do not collect all
|
|
178
|
+
chunks into a list** unless you deliberately want to retain that memory.
|
|
179
|
+
The caller owns arrays returned by the reader; repeated reads retained by
|
|
180
|
+
the caller can still consume arbitrary memory.
|
|
181
|
+
|
|
182
|
+
For a stricter output policy:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from natus_erd import NatusERDReader, ReadLimits
|
|
186
|
+
|
|
187
|
+
reader = NatusERDReader.open(
|
|
188
|
+
r"D:\path\to\recording",
|
|
189
|
+
limits=ReadLimits(max_read_bytes=16 * 1024 * 1024),
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Metadata lengths, parser complexity and packet sizes are also checked before
|
|
194
|
+
large reads. Packet decoding uses a bounded input buffer rather than reading
|
|
195
|
+
the entire compressed packet into memory. Invalid offsets, truncated data,
|
|
196
|
+
overlap and unsupported layouts raise explicit errors. These checks limit
|
|
197
|
+
the reader's own resource requests; **they are not a process-wide memory
|
|
198
|
+
limit or a sandbox**.
|
|
199
|
+
|
|
200
|
+
Other defaults include 8 MiB per metadata file or compressed packet, 256 KiB
|
|
201
|
+
per ENT record, 4,096 ENT records, parser depth 32 and a cache of four segment
|
|
202
|
+
indexes. `ReadLimits` exposes these policies explicitly; increasing a limit
|
|
203
|
+
should follow an assessment of the input and available resources.
|
|
204
|
+
|
|
205
|
+
### NumPy backend resource use
|
|
206
|
+
|
|
207
|
+
Some NumPy builds initialize a multi-threaded numerical backend with a large
|
|
208
|
+
memory footprint, even though this reader does not need matrix multiplication.
|
|
209
|
+
The package does not modify process-wide thread settings or environment
|
|
210
|
+
variables. On a memory-constrained system, an application may explicitly set
|
|
211
|
+
its own backend policy **before importing NumPy or any library that imports
|
|
212
|
+
it**, for example at the very start of the application:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
import os
|
|
216
|
+
|
|
217
|
+
os.environ["OPENBLAS_NUM_THREADS"] = "1" # For an OpenBLAS-backed NumPy build.
|
|
218
|
+
|
|
219
|
+
from natus_erd import NatusERDReader
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
This setting is backend-specific and is not a remedy or guarantee for kernel
|
|
223
|
+
crashes. If a machine has frozen or restarted, do not retry an unbounded
|
|
224
|
+
workload; investigate the operating-system failure separately.
|
|
225
|
+
|
|
226
|
+
## Supported format and errors
|
|
227
|
+
|
|
228
|
+
The supported layout is ERD schema 9, base schema 1, Quantum headbox type 20:
|
|
229
|
+
276 recorded channels, 8-bit delta base and 6 discarded bits. Any finite
|
|
230
|
+
positive header sample rate is accepted if its recording duration is finite;
|
|
231
|
+
there is no sample-rate whitelist. All segments must have the same rate.
|
|
232
|
+
This does not extend support to other hardware or per-channel sampling rates.
|
|
233
|
+
STC schema 1, ETC schema 3 and ENT schema 3 are supported, each with base
|
|
234
|
+
schema 1. The ERD layout must contain a single headbox, an identity physical
|
|
235
|
+
channel mapping and a frequency-factor value of 32767 for every channel.
|
|
236
|
+
The first 256 channels use the Quantum AC calibration:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
uV = digital × (-8711 / (2**21 - 0.5)) × 2**discard_bits
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The last 20 auxiliary channels remain in `reader.channels` and can be read
|
|
243
|
+
as digital counts, but have no verified physical-unit conversion. Native
|
|
244
|
+
UTF-8 segment and montage names are supported. ENT is parsed as data with a
|
|
245
|
+
restricted, bounded grammar; it is never evaluated as Python code.
|
|
246
|
+
Notes with unsupported or malformed text syntax may be skipped; a restricted
|
|
247
|
+
fallback extracts channel names from recognized nonstandard montage fields.
|
|
248
|
+
Consequently, `read_events()` is not guaranteed to include every annotation
|
|
249
|
+
stored by the vendor. Invalid binary lengths, schemas and exceeded parser
|
|
250
|
+
budgets still raise errors rather than being silently skipped.
|
|
251
|
+
|
|
252
|
+
`.eeg` is an accepted entry point, not the sample-data source. Samples come
|
|
253
|
+
from `.stc`/`.etc` indexes and `.erd` payloads. Other schemas/headboxes,
|
|
254
|
+
`Decimated`, video, EDF and additional auxiliary calibration are out of scope.
|
|
255
|
+
|
|
256
|
+
- `UnsupportedFormatError`: unsupported format or hardware layout.
|
|
257
|
+
- `DataIntegrityError`: malformed, truncated or inconsistent input.
|
|
258
|
+
- `ResourceLimitError`: a configured resource limit would be exceeded.
|
|
259
|
+
- `NatusERDError`: common base class for these reader exceptions.
|
|
260
|
+
|
|
261
|
+
Ordinary invalid API arguments may raise `TypeError`, `ValueError` or
|
|
262
|
+
`IndexError`. Filesystem failures may propagate the corresponding `OSError`.
|
|
263
|
+
|
|
264
|
+
## Privacy, testing and maintenance
|
|
265
|
+
|
|
266
|
+
The library does not upload recordings or contact network services. Channel
|
|
267
|
+
labels and ENT event text can contain sensitive information; avoid printing
|
|
268
|
+
or sharing them without reviewing them. Only generic source and synthetic
|
|
269
|
+
tests belong in this repository. Real recordings, event text and derived
|
|
270
|
+
patient outputs must not be distributed.
|
|
271
|
+
|
|
272
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the test/build workflow and
|
|
273
|
+
[SECURITY.md](SECURITY.md) for resource-limit boundaries. Public API changes
|
|
274
|
+
from 0.1.0 are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
275
|
+
|
|
276
|
+
## 中文速览
|
|
277
|
+
|
|
278
|
+
这是只提供 Python API 的 Natus ERD 读取包,不再包含命令行、网页、EDF 或绘图功能。
|
|
279
|
+
支持的范围限定为 schema 9 / Quantum headbox 20。默认单次输出上限为 64 MiB;
|
|
280
|
+
采样率从 ERD 文件头读取,不再固定为 2048 Hz;采样空档保留原时间轴并返回 NaN。
|
|
281
|
+
长记录请使用 `iter_samples()` 分块处理,不要把所有块保留在内存中。
|
|
282
|
+
读取器会检查文件长度、索引和解析资源上限,但不能保证避免操作系统或硬件故障。
|
|
283
|
+
仅查看记录信息和事件不会初始化 NumPy;真正读取数组时才加载它。
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
See [LICENSE](LICENSE) and [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)
|
|
288
|
+
for applicable terms and retained attribution. Both are included in source
|
|
289
|
+
and wheel distributions.
|