diff --git a/docs/PORT_STATUS.md b/docs/PORT_STATUS.md
new file mode 100644
index 0000000..9a2e0b0
--- /dev/null
+++ b/docs/PORT_STATUS.md
@@ -0,0 +1,92 @@
+# NDR-python Port Status
+
+Status of the MATLAB → Python port of [NDR-matlab](https://github.com/VH-Lab/NDR-matlab).
+
+## Naming Convention
+
+Python class names are a mechanical mapping of the fully-qualified MATLAB class name,
+applying the **Mirror Rule**:
+
+1. Periods (`.`) are replaced with single underscores (`_`).
+2. Existing underscores (`_`) in the MATLAB name are replaced with double underscores (`__`).
+
+| MATLAB qualified name | Python module | Python class |
+|---|---|---|
+| `ndr.reader` | `ndr.reader_wrapper` | `ndr_reader` |
+| `ndr.reader.base` | `ndr.reader.base` | `ndr_reader_base` |
+| `ndr.reader.intan_rhd` | `ndr.reader.intan_rhd` | `ndr_reader_intan__rhd` |
+| `ndr.reader.ced_smr` | `ndr.reader.ced_smr` | `ndr_reader_ced__smr` |
+| `ndr.reader.axon_abf` | `ndr.reader.axon_abf` | `ndr_reader_axon__abf` |
+| `ndr.reader.neo` | `ndr.reader.neo` | `ndr_reader_neo` |
+| `ndr.reader.spikegadgets_rec` | `ndr.reader.spikegadgets_rec` | `ndr_reader_spikegadgets__rec` |
+| `ndr.reader.tdt_sev` | `ndr.reader.tdt_sev` | `ndr_reader_tdt__sev` |
+| `ndr.reader.bjg` | `ndr.reader.bjg` | `ndr_reader_bjg` |
+| `ndr.reader.dabrowska` | `ndr.reader.dabrowska` | `ndr_reader_dabrowska` |
+| `ndr.reader.whitematter` | `ndr.reader.whitematter` | `ndr_reader_whitematter` |
+| `ndr.reader.somecompany_someformat` | `ndr.reader.somecompany_someformat` | `ndr_reader_somecompany__someformat` |
+
+## Reader Status
+
+| Reader | getchannelsepoch | t0\_t1 | samplerate | readchannels\_epochsamples | readevents\_epochsamples\_native | read | Tests |
+|---|---|---|---|---|---|---|---|
+| **ndr\_reader\_intan\_\_rhd** | Yes | Yes | Yes | Yes (single-file) | Stub (empty) | Yes | 6 pass |
+| **ndr\_reader\_ced\_\_smr** | Yes | Yes | Yes | Yes | Yes | Yes (via base) | 14 pass |
+| **ndr\_reader\_axon\_\_abf** | Yes | Yes | Yes | Yes | Stub (empty) | Yes (via base) | 6 pass |
+| **ndr\_reader\_neo** | Stub (empty) | Stub | Stub | NotImplementedError | Stub (empty) | No | 24 xfail |
+| **ndr\_reader\_spikegadgets\_\_rec** | Stub (empty) | Stub | Stub | NotImplementedError | Stub (empty) | No | xfail |
+| **ndr\_reader\_tdt\_\_sev** | Stub (empty) | Stub | Stub | NotImplementedError | Stub (empty) | No | skipped |
+| **ndr\_reader\_bjg** | Stub (empty) | Stub | Stub | NotImplementedError | Stub (empty) | No | skipped |
+| **ndr\_reader\_dabrowska** | Stub (empty) | Stub | Stub | NotImplementedError | Stub (empty) | No | skipped |
+| **ndr\_reader\_whitematter** | Stub (empty) | Stub | Stub | NotImplementedError | Stub (empty) | No | skipped |
+
+**Legend:**
+- **Yes** — Fully implemented and tested with example data
+- **Stub (empty)** — Returns empty arrays / default values; no errors raised
+- **NotImplementedError** — Raises an exception; not yet implemented
+- **Stub** — Inherits base class default (empty list, `[[nan,nan]]`, etc.)
+
+## Format Parsers
+
+Low-level format parsers (under `ndr.format.*`) that read binary files:
+
+| Format | Module | Status |
+|---|---|---|
+| Intan RHD | `ndr.format.intan` | Implemented (header + single-file data reader) |
+| CED SMR/SON | `ndr.format.ced` | Implemented (via `neo` library) |
+| Axon ABF | `ndr.format.axon` | Implemented (via `pyabf` library) |
+| SpikeGadgets REC | `ndr.format.spikegadgets` | Implemented (config, analog, digital, trode) |
+| TDT SEV | `ndr.format.tdt` | Implemented (header + channel reader) |
+| BJG | `ndr.format.bjg` | Implemented (header + data reader) |
+| Dabrowska | `ndr.format.dabrowska` | Implemented (header + data reader) |
+| WhiteMatter | `ndr.format.whitematter` | Implemented (header + data reader) |
+| Neo / Blackrock | `ndr.format.neo` | Implemented (utilities) |
+| Binary Matrix | `ndr.format.binarymatrix` | Implemented |
+| Text Signal | `ndr.format.textSignal` | Implemented |
+
+Note: For SpikeGadgets, TDT, BJG, Dabrowska, and WhiteMatter, the format parsers are implemented but the reader classes have not yet been wired up to use them.
+
+## Reader Wrapper
+
+The top-level `ndr_reader` class (`reader_wrapper.py`) wraps any format-specific reader and adds:
+
+| Feature | Status |
+|---|---|
+| `read()` convenience method | Implemented |
+| `readevents_epochsamples()` with derived events (dep, den, dimp, dimn) | Implemented |
+| Delegation to underlying reader | Implemented |
+
+## External Dependencies
+
+| Dependency | Used by | Purpose |
+|---|---|---|
+| `neo` | ndr\_reader\_ced\_\_smr, ndr\_reader\_neo | Read CED SMR/SON and Blackrock files |
+| `pyabf` | ndr\_reader\_axon\_\_abf | Read Axon Binary Format files |
+| `numpy` | All readers | Array operations |
+
+## Test Summary
+
+```
+38 passed, 28 xfailed, 13 skipped, 2 failed (pre-existing spikegadgets format test issues)
+```
+
+Example data files are included in `src/ndr/example_data/` for Intan (.rhd), CED (.smr), Axon (.abf), SpikeGadgets (.rec), and Blackrock (.nev, .ns2).
diff --git a/docs/developer_notes/PYTHON_PORTING_GUIDE.md b/docs/developer_notes/PYTHON_PORTING_GUIDE.md
index f9953e4..4217a82 100644
--- a/docs/developer_notes/PYTHON_PORTING_GUIDE.md
+++ b/docs/developer_notes/PYTHON_PORTING_GUIDE.md
@@ -17,6 +17,21 @@ Function and class names must match MATLAB exactly.
- **Case Preservation:** Use `readchannels_epochsamples`, not `read_channels_epoch_samples`.
- **Directory Parity:** Python file paths must mirror MATLAB `+namespace` paths
(e.g., `+ndr/+reader` -> `src/ndr/reader/`).
+- **Class Name Mirror Rule:** Python class names are derived from the fully-qualified
+ MATLAB class name by applying two substitutions:
+ 1. Periods (`.`) are replaced with single underscores (`_`).
+ 2. Existing underscores (`_`) in the MATLAB name are replaced with double
+ underscores (`__`).
+
+ Examples:
+ | MATLAB qualified name | Python class name |
+ |--------------------------------------|----------------------------------------|
+ | `ndr.reader` | `ndr_reader` |
+ | `ndr.reader.base` | `ndr_reader_base` |
+ | `ndr.reader.intan_rhd` | `ndr_reader_intan__rhd` |
+ | `ndr.reader.ced_smr` | `ndr_reader_ced__smr` |
+ | `ndr.reader.axon_abf` | `ndr_reader_axon__abf` |
+ | `ndr.reader.somecompany_someformat` | `ndr_reader_somecompany__someformat` |
## 3. The Porting Workflow (The Bridge Protocol)
1. **Check the Bridge:** Open the `ndr_matlab_python_bridge.yaml` in the target package.
diff --git a/src/ndr/__init__.py b/src/ndr/__init__.py
index ab0204a..64f21f4 100644
--- a/src/ndr/__init__.py
+++ b/src/ndr/__init__.py
@@ -3,6 +3,6 @@
A Python port of NDR-matlab (https://github.com/VH-Lab/NDR-matlab).
"""
-from ndr.reader_wrapper import Reader as reader
+from ndr.reader_wrapper import ndr_reader as reader
__version__ = "0.1.0"
diff --git a/src/ndr/data/__init__.py b/src/ndr/data/__init__.py
index 447671f..d67dd14 100644
--- a/src/ndr/data/__init__.py
+++ b/src/ndr/data/__init__.py
@@ -3,5 +3,7 @@
Port of +ndr/+data/
"""
+from ndr.data.assign import assign
from ndr.data.colvec import colvec
from ndr.data.rowvec import rowvec
+from ndr.data.struct2namevaluepair import struct2namevaluepair
diff --git a/src/ndr/data/assign.py b/src/ndr/data/assign.py
new file mode 100644
index 0000000..9c40a41
--- /dev/null
+++ b/src/ndr/data/assign.py
@@ -0,0 +1,65 @@
+"""Assign name/value pairs into a target namespace (dict).
+
+Port of +ndr/+data/assign.m
+"""
+
+from __future__ import annotations
+
+from typing import Any
+
+from ndr.data.struct2namevaluepair import struct2namevaluepair
+
+
+def assign(target: dict[str, Any], *args: Any) -> dict[str, Any]:
+ """Apply a list of name/value pair assignments to *target*.
+
+ In MATLAB ``ndr.data.assign`` uses ``assignin('caller', ...)`` to inject
+ variables into the caller's workspace. In Python the idiomatic
+ equivalent is to update a dictionary (typically ``locals()`` or an
+ options dict) and return it.
+
+ Parameters
+ ----------
+ target : dict
+ The dictionary to update with the supplied name/value pairs.
+ *args
+ Either a single ``dict`` (struct equivalent), a single ``list``
+ of alternating name/value items, or inline alternating
+ ``name, value, name, value, ...`` arguments.
+
+ Returns
+ -------
+ dict
+ The updated *target* dictionary (same object, mutated in-place).
+
+ Examples
+ --------
+ >>> opts = {'z': 0}
+ >>> assign(opts, 'z', 4)
+ {'z': 4}
+
+ >>> assign({}, {'a': 1, 'b': 2})
+ {'a': 1, 'b': 2}
+
+ >>> assign({}, ['x', 10, 'y', 20])
+ {'x': 10, 'y': 20}
+ """
+ # Normalise a single-argument form (dict or list) into a flat sequence
+ if len(args) == 1:
+ arg = args[0]
+ if isinstance(arg, dict):
+ flat: list[Any] = struct2namevaluepair(arg)
+ elif isinstance(arg, (list, tuple)):
+ flat = list(arg)
+ else:
+ raise TypeError("A single argument must be a dict or a list of name/value pairs.")
+ else:
+ flat = list(args)
+
+ names = flat[0::2]
+ values = flat[1::2]
+
+ for name, value in zip(names, values):
+ target[name] = value
+
+ return target
diff --git a/src/ndr/data/ndr_matlab_python_bridge.yaml b/src/ndr/data/ndr_matlab_python_bridge.yaml
new file mode 100644
index 0000000..e39f4b4
--- /dev/null
+++ b/src/ndr/data/ndr_matlab_python_bridge.yaml
@@ -0,0 +1,47 @@
+project_metadata:
+ bridge_version: "1.1"
+ naming_policy: "Strict MATLAB Mirror"
+ indexing_policy: "Semantic Parity (1-based for user concepts, 0-based for internal data)"
+
+functions:
+ - name: assign
+ matlab_path: "+ndr/+data/assign.m"
+ python_path: "ndr/data/assign.py"
+ input_arguments:
+ - name: target
+ type_python: "dict[str, Any]"
+ - name: args
+ type_python: "*Any"
+ output_arguments:
+ - name: target
+ type_python: "dict[str, Any]"
+
+ - name: colvec
+ matlab_path: "+ndr/+data/colvec.m"
+ python_path: "ndr/data/colvec.py"
+ input_arguments:
+ - name: x
+ type_python: "numpy.ndarray"
+ output_arguments:
+ - name: y
+ type_python: "numpy.ndarray"
+
+ - name: rowvec
+ matlab_path: "+ndr/+data/rowvec.m"
+ python_path: "ndr/data/rowvec.py"
+ input_arguments:
+ - name: x
+ type_python: "numpy.ndarray"
+ output_arguments:
+ - name: y
+ type_python: "numpy.ndarray"
+
+ - name: struct2namevaluepair
+ matlab_path: "+ndr/+data/struct2namevaluepair.m"
+ python_path: "ndr/data/struct2namevaluepair.py"
+ input_arguments:
+ - name: thestruct
+ type_python: "dict[str, Any]"
+ output_arguments:
+ - name: nv
+ type_python: "list[Any]"
diff --git a/src/ndr/data/struct2namevaluepair.py b/src/ndr/data/struct2namevaluepair.py
new file mode 100644
index 0000000..9b89383
--- /dev/null
+++ b/src/ndr/data/struct2namevaluepair.py
@@ -0,0 +1,41 @@
+"""Convert a dict to a flat list of name/value pairs.
+
+Port of +ndr/+data/struct2namevaluepair.m
+"""
+
+from __future__ import annotations
+
+from typing import Any
+
+
+def struct2namevaluepair(thestruct: dict[str, Any]) -> list[Any]:
+ """Convert a dictionary to a flat list of name/value pairs.
+
+ This is useful for passing name/value pairs to functions that accept
+ them as extra keyword arguments. Each key of the dictionary is used as
+ the 'name', and the corresponding value is used as the 'value'.
+
+ Parameters
+ ----------
+ thestruct : dict
+ Input dictionary mapping parameter names to values.
+
+ Returns
+ -------
+ list
+ Flat list alternating between keys and values,
+ e.g. ``['param1', 1, 'param2', 2]``.
+
+ Examples
+ --------
+ >>> struct2namevaluepair({'param1': 1, 'param2': 2})
+ ['param1', 1, 'param2', 2]
+ """
+ if not thestruct:
+ return []
+
+ nv: list[Any] = []
+ for key, value in thestruct.items():
+ nv.append(key)
+ nv.append(value)
+ return nv
diff --git a/src/ndr/example_data/Readme.md b/src/ndr/example_data/Readme.md
new file mode 100644
index 0000000..2753739
--- /dev/null
+++ b/src/ndr/example_data/Readme.md
@@ -0,0 +1,362 @@
+This folder contains example neurophysiology files that we can use for testing our readers.
+An excellent source of example files is https://gin.g-node.org/NeuralEnsemble/ephy_testing_data.
+Take note of `.txt` files in https://gin.g-node.org/NeuralEnsemble/ephy_testing_data/src/master/blackrock/blackrock_2_1) - this might be a good source of *already parsed* neurophysiology data.
+
+___
+
+The rest of this file shows all channels from each `example.extension` file, as shown by Neo:
+
+```
+reader = ndr.reader('neo');
+channels = reader.getchannelsepoch({ filename }, 'all');
+```
+
+Keep in mind Neo designates any channel type to one of the: `'analog_input'`, `'event'`, and `'marker'` NDR channel types. Look at `automatedTest.m` to see how to query data from these files.
+
+
+**example_2.ns2** - Blackrock, contains analog channels
+(https://gin.g-node.org/NeuralEnsemble/ephy_testing_data/src/master/blackrock/blackrock_2_1/l101210-001.ns2)
+
+{'name': 'ainp9', 'type': 'analog_input'}
+{'name': 'ainp10', 'type': 'analog_input'}
+{'name': 'ainp11', 'type': 'analog_input'}
+{'name': 'ainp12', 'type': 'analog_input'}
+{'name': 'ainp13', 'type': 'analog_input'}
+{'name': 'ainp15', 'type': 'analog_input'}
+
+
+**example_1.nev** - Blackrock, contains event & marker channels
+(https://gin.g-node.org/NeuralEnsemble/ephy_testing_data/src/master/blackrock/blackrock_2_1/l101210-001-02.nev)
+
+{'name': 'ch1#0', 'type': 'event'}
+{'name': 'ch1#255', 'type': 'event'}
+{'name': 'ch2#0', 'type': 'event'}
+{'name': 'ch3#0', 'type': 'event'}
+{'name': 'ch3#1', 'type': 'event'}
+{'name': 'ch3#2', 'type': 'event'}
+{'name': 'ch3#255', 'type': 'event'}
+{'name': 'ch4#0', 'type': 'event'}
+{'name': 'ch4#255', 'type': 'event'}
+{'name': 'ch5#0', 'type': 'event'}
+{'name': 'ch5#1', 'type': 'event'}
+{'name': 'ch5#2', 'type': 'event'}
+{'name': 'ch5#3', 'type': 'event'}
+{'name': 'ch5#255', 'type': 'event'}
+{'name': 'ch6#0', 'type': 'event'}
+{'name': 'ch6#1', 'type': 'event'}
+{'name': 'ch6#255', 'type': 'event'}
+{'name': 'ch7#0', 'type': 'event'}
+{'name': 'ch7#1', 'type': 'event'}
+{'name': 'ch7#2', 'type': 'event'}
+{'name': 'ch7#255', 'type': 'event'}
+{'name': 'ch8#0', 'type': 'event'}
+{'name': 'ch8#1', 'type': 'event'}
+{'name': 'ch8#255', 'type': 'event'}
+{'name': 'ch9#0', 'type': 'event'}
+{'name': 'ch9#1', 'type': 'event'}
+{'name': 'ch9#255', 'type': 'event'}
+{'name': 'ch10#0', 'type': 'event'}
+{'name': 'ch10#1', 'type': 'event'}
+{'name': 'ch10#255', 'type': 'event'}
+{'name': 'ch11#0', 'type': 'event'}
+{'name': 'ch11#1', 'type': 'event'}
+{'name': 'ch11#2', 'type': 'event'}
+{'name': 'ch11#255', 'type': 'event'}
+{'name': 'ch12#0', 'type': 'event'}
+{'name': 'ch12#1', 'type': 'event'}
+{'name': 'ch12#255', 'type': 'event'}
+{'name': 'ch13#0', 'type': 'event'}
+{'name': 'ch13#1', 'type': 'event'}
+{'name': 'ch13#2', 'type': 'event'}
+{'name': 'ch13#3', 'type': 'event'}
+{'name': 'ch13#255', 'type': 'event'}
+{'name': 'ch14#0', 'type': 'event'}
+{'name': 'ch14#1', 'type': 'event'}
+{'name': 'ch14#2', 'type': 'event'}
+{'name': 'ch14#3', 'type': 'event'}
+{'name': 'ch14#255', 'type': 'event'}
+{'name': 'ch15#0', 'type': 'event'}
+{'name': 'ch15#1', 'type': 'event'}
+{'name': 'ch15#2', 'type': 'event'}
+{'name': 'ch15#255', 'type': 'event'}
+{'name': 'ch16#0', 'type': 'event'}
+{'name': 'ch16#1', 'type': 'event'}
+{'name': 'ch16#255', 'type': 'event'}
+{'name': 'ch17#0', 'type': 'event'}
+{'name': 'ch17#1', 'type': 'event'}
+{'name': 'ch17#255', 'type': 'event'}
+{'name': 'ch18#0', 'type': 'event'}
+{'name': 'ch18#1', 'type': 'event'}
+{'name': 'ch18#255', 'type': 'event'}
+{'name': 'ch19#0', 'type': 'event'}
+{'name': 'ch19#1', 'type': 'event'}
+{'name': 'ch19#255', 'type': 'event'}
+{'name': 'ch20#0', 'type': 'event'}
+{'name': 'ch20#1', 'type': 'event'}
+{'name': 'ch20#2', 'type': 'event'}
+{'name': 'ch20#255', 'type': 'event'}
+{'name': 'ch21#0', 'type': 'event'}
+{'name': 'ch21#255', 'type': 'event'}
+{'name': 'ch22#0', 'type': 'event'}
+{'name': 'ch22#1', 'type': 'event'}
+{'name': 'ch22#2', 'type': 'event'}
+{'name': 'ch22#255', 'type': 'event'}
+{'name': 'ch23#0', 'type': 'event'}
+{'name': 'ch23#1', 'type': 'event'}
+{'name': 'ch23#2', 'type': 'event'}
+{'name': 'ch23#3', 'type': 'event'}
+{'name': 'ch23#255', 'type': 'event'}
+{'name': 'ch24#0', 'type': 'event'}
+{'name': 'ch24#1', 'type': 'event'}
+{'name': 'ch24#2', 'type': 'event'}
+{'name': 'ch24#3', 'type': 'event'}
+{'name': 'ch24#255', 'type': 'event'}
+{'name': 'ch25#0', 'type': 'event'}
+{'name': 'ch25#1', 'type': 'event'}
+{'name': 'ch25#2', 'type': 'event'}
+{'name': 'ch25#3', 'type': 'event'}
+{'name': 'ch25#255', 'type': 'event'}
+{'name': 'ch26#0', 'type': 'event'}
+{'name': 'ch26#1', 'type': 'event'}
+{'name': 'ch26#255', 'type': 'event'}
+{'name': 'ch27#1', 'type': 'event'}
+{'name': 'ch28#0', 'type': 'event'}
+{'name': 'ch28#1', 'type': 'event'}
+{'name': 'ch28#255', 'type': 'event'}
+{'name': 'ch29#0', 'type': 'event'}
+{'name': 'ch29#255', 'type': 'event'}
+{'name': 'ch30#0', 'type': 'event'}
+{'name': 'ch30#1', 'type': 'event'}
+{'name': 'ch30#255', 'type': 'event'}
+{'name': 'ch31#0', 'type': 'event'}
+{'name': 'ch31#1', 'type': 'event'}
+{'name': 'ch31#255', 'type': 'event'}
+{'name': 'ch32#0', 'type': 'event'}
+{'name': 'ch32#1', 'type': 'event'}
+{'name': 'ch32#2', 'type': 'event'}
+{'name': 'ch32#3', 'type': 'event'}
+{'name': 'ch32#255', 'type': 'event'}
+{'name': 'ch33#0', 'type': 'event'}
+{'name': 'ch33#1', 'type': 'event'}
+{'name': 'ch33#255', 'type': 'event'}
+{'name': 'ch34#0', 'type': 'event'}
+{'name': 'ch34#1', 'type': 'event'}
+{'name': 'ch34#2', 'type': 'event'}
+{'name': 'ch34#3', 'type': 'event'}
+{'name': 'ch34#255', 'type': 'event'}
+{'name': 'ch35#0', 'type': 'event'}
+{'name': 'ch35#1', 'type': 'event'}
+{'name': 'ch36#0', 'type': 'event'}
+{'name': 'ch36#1', 'type': 'event'}
+{'name': 'ch36#2', 'type': 'event'}
+{'name': 'ch36#255', 'type': 'event'}
+{'name': 'ch37#0', 'type': 'event'}
+{'name': 'ch37#1', 'type': 'event'}
+{'name': 'ch37#255', 'type': 'event'}
+{'name': 'ch38#0', 'type': 'event'}
+{'name': 'ch38#1', 'type': 'event'}
+{'name': 'ch38#2', 'type': 'event'}
+{'name': 'ch38#255', 'type': 'event'}
+{'name': 'ch39#0', 'type': 'event'}
+{'name': 'ch39#1', 'type': 'event'}
+{'name': 'ch39#2', 'type': 'event'}
+{'name': 'ch39#3', 'type': 'event'}
+{'name': 'ch39#255', 'type': 'event'}
+{'name': 'ch40#0', 'type': 'event'}
+{'name': 'ch40#1', 'type': 'event'}
+{'name': 'ch40#255', 'type': 'event'}
+{'name': 'ch41#0', 'type': 'event'}
+{'name': 'ch41#1', 'type': 'event'}
+{'name': 'ch41#2', 'type': 'event'}
+{'name': 'ch41#255', 'type': 'event'}
+{'name': 'ch42#0', 'type': 'event'}
+{'name': 'ch42#1', 'type': 'event'}
+{'name': 'ch42#255', 'type': 'event'}
+{'name': 'ch43#0', 'type': 'event'}
+{'name': 'ch44#0', 'type': 'event'}
+{'name': 'ch44#1', 'type': 'event'}
+{'name': 'ch44#2', 'type': 'event'}
+{'name': 'ch44#255', 'type': 'event'}
+{'name': 'ch45#0', 'type': 'event'}
+{'name': 'ch45#1', 'type': 'event'}
+{'name': 'ch45#255', 'type': 'event'}
+{'name': 'ch46#0', 'type': 'event'}
+{'name': 'ch46#1', 'type': 'event'}
+{'name': 'ch46#2', 'type': 'event'}
+{'name': 'ch46#255', 'type': 'event'}
+{'name': 'ch47#0', 'type': 'event'}
+{'name': 'ch47#1', 'type': 'event'}
+{'name': 'ch47#255', 'type': 'event'}
+{'name': 'ch48#0', 'type': 'event'}
+{'name': 'ch48#1', 'type': 'event'}
+{'name': 'ch48#2', 'type': 'event'}
+{'name': 'ch49#0', 'type': 'event'}
+{'name': 'ch49#1', 'type': 'event'}
+{'name': 'ch49#255', 'type': 'event'}
+{'name': 'ch50#0', 'type': 'event'}
+{'name': 'ch50#255', 'type': 'event'}
+{'name': 'ch51#0', 'type': 'event'}
+{'name': 'ch51#1', 'type': 'event'}
+{'name': 'ch52#0', 'type': 'event'}
+{'name': 'ch52#1', 'type': 'event'}
+{'name': 'ch52#2', 'type': 'event'}
+{'name': 'ch52#3', 'type': 'event'}
+{'name': 'ch52#255', 'type': 'event'}
+{'name': 'ch53#0', 'type': 'event'}
+{'name': 'ch53#255', 'type': 'event'}
+{'name': 'ch54#0', 'type': 'event'}
+{'name': 'ch54#1', 'type': 'event'}
+{'name': 'ch54#2', 'type': 'event'}
+{'name': 'ch54#255', 'type': 'event'}
+{'name': 'ch55#0', 'type': 'event'}
+{'name': 'ch55#1', 'type': 'event'}
+{'name': 'ch55#255', 'type': 'event'}
+{'name': 'ch56#0', 'type': 'event'}
+{'name': 'ch56#1', 'type': 'event'}
+{'name': 'ch56#2', 'type': 'event'}
+{'name': 'ch57#0', 'type': 'event'}
+{'name': 'ch57#1', 'type': 'event'}
+{'name': 'ch58#0', 'type': 'event'}
+{'name': 'ch58#1', 'type': 'event'}
+{'name': 'ch59#0', 'type': 'event'}
+{'name': 'ch59#1', 'type': 'event'}
+{'name': 'ch59#255', 'type': 'event'}
+{'name': 'ch60#0', 'type': 'event'}
+{'name': 'ch60#1', 'type': 'event'}
+{'name': 'ch60#255', 'type': 'event'}
+{'name': 'ch61#0', 'type': 'event'}
+{'name': 'ch61#1', 'type': 'event'}
+{'name': 'ch61#2', 'type': 'event'}
+{'name': 'ch61#3', 'type': 'event'}
+{'name': 'ch62#0', 'type': 'event'}
+{'name': 'ch62#1', 'type': 'event'}
+{'name': 'ch62#255', 'type': 'event'}
+{'name': 'ch63#0', 'type': 'event'}
+{'name': 'ch63#1', 'type': 'event'}
+{'name': 'ch63#2', 'type': 'event'}
+{'name': 'ch63#255', 'type': 'event'}
+{'name': 'ch64#0', 'type': 'event'}
+{'name': 'ch64#1', 'type': 'event'}
+{'name': 'ch64#255', 'type': 'event'}
+{'name': 'ch65#0', 'type': 'event'}
+{'name': 'ch65#1', 'type': 'event'}
+{'name': 'ch65#255', 'type': 'event'}
+{'name': 'ch66#0', 'type': 'event'}
+{'name': 'ch66#1', 'type': 'event'}
+{'name': 'ch66#255', 'type': 'event'}
+{'name': 'ch67#0', 'type': 'event'}
+{'name': 'ch67#1', 'type': 'event'}
+{'name': 'ch67#255', 'type': 'event'}
+{'name': 'ch68#0', 'type': 'event'}
+{'name': 'ch68#1', 'type': 'event'}
+{'name': 'ch68#2', 'type': 'event'}
+{'name': 'ch68#3', 'type': 'event'}
+{'name': 'ch68#255', 'type': 'event'}
+{'name': 'ch69#0', 'type': 'event'}
+{'name': 'ch69#1', 'type': 'event'}
+{'name': 'ch69#255', 'type': 'event'}
+{'name': 'ch70#0', 'type': 'event'}
+{'name': 'ch70#1', 'type': 'event'}
+{'name': 'ch70#2', 'type': 'event'}
+{'name': 'ch70#255', 'type': 'event'}
+{'name': 'ch71#0', 'type': 'event'}
+{'name': 'ch71#1', 'type': 'event'}
+{'name': 'ch71#2', 'type': 'event'}
+{'name': 'ch71#3', 'type': 'event'}
+{'name': 'ch71#255', 'type': 'event'}
+{'name': 'ch72#0', 'type': 'event'}
+{'name': 'ch72#255', 'type': 'event'}
+{'name': 'ch73#0', 'type': 'event'}
+{'name': 'ch73#1', 'type': 'event'}
+{'name': 'ch73#255', 'type': 'event'}
+{'name': 'ch74#0', 'type': 'event'}
+{'name': 'ch74#1', 'type': 'event'}
+{'name': 'ch74#255', 'type': 'event'}
+{'name': 'ch75#0', 'type': 'event'}
+{'name': 'ch75#1', 'type': 'event'}
+{'name': 'ch75#2', 'type': 'event'}
+{'name': 'ch75#255', 'type': 'event'}
+{'name': 'ch76#0', 'type': 'event'}
+{'name': 'ch76#1', 'type': 'event'}
+{'name': 'ch76#2', 'type': 'event'}
+{'name': 'ch76#255', 'type': 'event'}
+{'name': 'ch77#0', 'type': 'event'}
+{'name': 'ch77#1', 'type': 'event'}
+{'name': 'ch77#2', 'type': 'event'}
+{'name': 'ch77#3', 'type': 'event'}
+{'name': 'ch77#255', 'type': 'event'}
+{'name': 'ch78#0', 'type': 'event'}
+{'name': 'ch78#1', 'type': 'event'}
+{'name': 'ch78#2', 'type': 'event'}
+{'name': 'ch78#255', 'type': 'event'}
+{'name': 'ch79#0', 'type': 'event'}
+{'name': 'ch79#255', 'type': 'event'}
+{'name': 'ch80#0', 'type': 'event'}
+{'name': 'ch80#1', 'type': 'event'}
+{'name': 'ch80#2', 'type': 'event'}
+{'name': 'ch80#3', 'type': 'event'}
+{'name': 'ch80#255', 'type': 'event'}
+{'name': 'ch81#0', 'type': 'event'}
+{'name': 'ch81#1', 'type': 'event'}
+{'name': 'ch81#255', 'type': 'event'}
+{'name': 'ch82#0', 'type': 'event'}
+{'name': 'ch82#1', 'type': 'event'}
+{'name': 'ch82#2', 'type': 'event'}
+{'name': 'ch82#255', 'type': 'event'}
+{'name': 'ch83#0', 'type': 'event'}
+{'name': 'ch83#1', 'type': 'event'}
+{'name': 'ch83#2', 'type': 'event'}
+{'name': 'ch83#255', 'type': 'event'}
+{'name': 'ch84#0', 'type': 'event'}
+{'name': 'ch84#1', 'type': 'event'}
+{'name': 'ch84#2', 'type': 'event'}
+{'name': 'ch84#255', 'type': 'event'}
+{'name': 'ch85#0', 'type': 'event'}
+{'name': 'ch85#1', 'type': 'event'}
+{'name': 'ch85#2', 'type': 'event'}
+{'name': 'ch85#255', 'type': 'event'}
+{'name': 'ch86#0', 'type': 'event'}
+{'name': 'ch86#1', 'type': 'event'}
+{'name': 'ch86#255', 'type': 'event'}
+{'name': 'ch87#0', 'type': 'event'}
+{'name': 'ch87#1', 'type': 'event'}
+{'name': 'ch88#0', 'type': 'event'}
+{'name': 'ch88#1', 'type': 'event'}
+{'name': 'ch88#255', 'type': 'event'}
+{'name': 'ch89#0', 'type': 'event'}
+{'name': 'ch89#1', 'type': 'event'}
+{'name': 'ch89#255', 'type': 'event'}
+{'name': 'ch90#0', 'type': 'event'}
+{'name': 'ch90#1', 'type': 'event'}
+{'name': 'ch90#2', 'type': 'event'}
+{'name': 'ch90#255', 'type': 'event'}
+{'name': 'ch91#0', 'type': 'event'}
+{'name': 'ch91#1', 'type': 'event'}
+{'name': 'ch91#2', 'type': 'event'}
+{'name': 'ch91#255', 'type': 'event'}
+{'name': 'ch92#0', 'type': 'event'}
+{'name': 'ch92#1', 'type': 'event'}
+{'name': 'ch92#255', 'type': 'event'}
+{'name': 'ch93#0', 'type': 'event'}
+{'name': 'ch93#1', 'type': 'event'}
+{'name': 'ch93#255', 'type': 'event'}
+{'name': 'ch94#0', 'type': 'event'}
+{'name': 'ch94#1', 'type': 'event'}
+{'name': 'ch94#2', 'type': 'event'}
+{'name': 'ch94#255', 'type': 'event'}
+{'name': 'ch95#0', 'type': 'event'}
+{'name': 'ch95#1', 'type': 'event'}
+{'name': 'ch95#2', 'type': 'event'}
+{'name': 'ch95#255', 'type': 'event'}
+{'name': 'ch96#0', 'type': 'event'}
+{'name': 'ch96#1', 'type': 'event'}
+{'name': 'ch96#2', 'type': 'event'}
+{'name': 'ch96#255', 'type': 'event'}
+{'name': 'digital_input_port', 'type': 'marker'}
+{'name': 'serial_input_port', 'type': 'marker'}
+{'name': 'analog_input_channel_1', 'type': 'marker'}
+{'name': 'analog_input_channel_2', 'type': 'marker'}
+{'name': 'analog_input_channel_3', 'type': 'marker'}
+{'name': 'analog_input_channel_4', 'type': 'marker'}
+{'name': 'analog_input_channel_5', 'type': 'marker'}
+{'name': 'periodic_sampling_events', 'type': 'marker'}
diff --git a/src/ndr/example_data/example.abf b/src/ndr/example_data/example.abf
new file mode 100644
index 0000000..c934a07
Binary files /dev/null and b/src/ndr/example_data/example.abf differ
diff --git a/src/ndr/example_data/example.rec b/src/ndr/example_data/example.rec
new file mode 100644
index 0000000..2779028
--- /dev/null
+++ b/src/ndr/example_data/example.rec
@@ -0,0 +1,48721 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+U? %
߭=ޭ] 7trIjdw@ 1J^A s
+&% Z ) J[2Qr4g$5@[BH9A[}Y1u -k+NX34(pV rCU? %-
߭Eޭ] 7\e&6(F F b\ {] Y e1<]bgd~$ K-Jkj4w.K'!k,U? -
ߵEޭ] 7OKFQT'* %F| ~:@D]b S0 q41RkWp:=haT/|`
}E{[$z&d3|6$l^4iU? %߭E] 7OZ)s =
q e'K!
+ T Rs';g)z'wu!<zRIj?5H]9$ j