| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
@jsiegle Leaving this as a placeholder and describing what the current blockers are that must be resolved at the specification level before this can be merged. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Add harp.data.synchronization for aligning non-Harp devices to the Harp clock
Closes #14. Supersedes harp-tech/harp-python#38, and is the analysis-side counterpart to the spec discussion in harp-tech/protocol#128.
Important
Blocked by harp-tech/protocol#128. The downsampled clock transmission is not specified yet, so this decoder is written against what current emitters happen to do. The open question below (which edge of the packet the whole second belongs to) has to be settled in the spec before this merges — it decides the default and, on today's hardware, a fixed 40 ms of alignment error either way.
Why
Devices outside the Harp bus timestamp their data on their own clock. To make them alignable post-hoc, some emitters (the Behavior board today, White Rabbit's aux UART) mirror the Synchronization Clock on a digital output at a much lower baud rate — typically 1 kbps instead of 100 kbps — so that acquisition systems sampling at 5–30 kHz can record it on a spare digital or analog input. Reading that recording back is currently done with ad-hoc scripts, one per lab.
This PR adds the decoding half of that story to harp.data, in its own synchronization namespace.
What
Two entry points, one per shape of recording, each keyed on the axis that system already timestamps its own data on:
Time holds the decoded whole Harp second — the same name the readers give the Harp time axis. The rows are anchors: placing the device's own timestamps on the Harp axis is left to the caller, e.g. np.interp(spike_samples, clock.index, clock["Time"]), since the choice between interpolating neighbouring anchors (absorbs drift) and a global fit (rejects noise) belongs to the analysis, not the decoder.
The wire format decoded is plain RS-232 without parity — idle high, one low start bit, eight data bits LSB-first, one high stop bit — carrying the Harp time as a little-endian uint32. Packets are accepted with or without the 0xAA 0xAF header of the full protocol packet; the framing is detected per packet, so a future emitter that downsamples the complete 6-byte packet needs no flag.
Open question: which edge carries the second (needs resolving)
The 100 kbps protocol is explicit that the last byte of the packet is the synchronization event: its transmission starts 672 µs before the second lapses, and receivers align the next whole second to that. Both existing downsampled emitters do the opposite — per the Behavior board notes in protocol#128 and White Rabbit's aux UART docs, "the first falling edge occurs when a new second has elapsed", i.e. transmission starts on the boundary and the value describes the second that just began.
At 100 kbps the distinction is nearly free: the whole packet is 600 µs, comparable to the 672 µs offset the spec already names. Downsampled it is not — at 1 kbps a 4-byte packet spans 40 ms, so picking the wrong end puts every anchor 40 ms off. It is a constant offset, so recovered drift and interpolation are unaffected and any downstream analysis stays self-consistent; but absolute Harp time is wrong by ~40 ms, which is two orders of magnitude above the sub-millisecond accuracy the clock protocol exists to deliver, and it silently varies with the configured baud rate.
The decoder therefore exposes both, and does not pretend to know which is right: (tho this will change if we managed to standardize in the protocol)
What needs deciding in protocol#128: whether the downsampled transmission starts or ends on the second boundary, and whether the residual offset (the equivalent of the 672 µs) is specified or left to the receiver. Once settled, this PR needs at most a default flip and a docstring change — no API change. Related: if the answer is "ends on the boundary", emitters change, not this decoder, so existing recordings will need anchor="first_edge" indefinitely and that should be documented as a legacy mode.
Other decoding decisions worth reviewing
Tests
35 tests in tests/data/test_clock.py, driven by a synthetic emitter that renders packets to either transitions or a sampled waveform:
Docs
harp.data.synchronization entries on the Data API page, a "Align a non-Harp device to the Harp clock" section in the harp-data README, and a new example page, Aligning Local Timestamps to the Harp Clock, wired into the nav.
ruff format/ruff check, pyright and codespell are clean; the full suite passes (392 tests).