feat: the export writes an MDF 3.30 file, so a window loads in ScryLab
A third format beside `.npy` and `.csv`, written by hand in `src/export.cpp` -- MF4 is HDF5 and would mean a library, MF3 is a handful of fixed blocks. The layout is the asammdf 8.8.27 reader's own field list, byte-diffed against a known-good 3.30 file it wrote; the shape is the one ScryLab's data-source contract asks for: one channel group, record-interleaved, the master as the x axis, and the unit in the channel's conversion block. So in MDF the master is `abs_seconds`, named `time` -- the clock that is the same in every display mode -- and the plotted axis goes out as an ordinary channel, `time_relative`. `ExportTable::Column` gained the CONFIG's unit, which only this writer spells out; the `.npy`/`.csv` bytes are unchanged, and `write_export()` now picks the writer for both the dialog and the probe.
This commit is contained in:
1 parent
761615c2fe
commit
908286ee89
10 files changed
+505
-28
No files matched your search
@@ -126,10 +126,27 @@ Two lines of the self-check output are assertions in disguise:
|
||||
An export taken **mid-capture** reports `mode=live` with `time=[-pre..e]` where `e` is how far
|
||||
the post window has filled — the export is what is on screen, and during a capture that is a
|
||||
partly filled window. Pin the probe outside a capture when checking the `rows=` count.
|
||||
The two `bytes=` lines are the writers: the `.npy` is
|
||||
The three `bytes=` lines are the writers: the `.npy` is
|
||||
`64·⌈(10+len(header)+1)/64⌉ + rows·(16 + 4·cols)`, so a size that is not that is a
|
||||
mispacked record. Read it back with `pd.DataFrame(np.load(path))` after changing
|
||||
either writer.
|
||||
The `.mf3` line's size is the MDF 3.30 layout the writer spells out, and every term of it is
|
||||
observable: `272 + comment + rows·record + 28 + channels·274 + 30`, where the ID block is 64,
|
||||
the HD block 208, the comment is a `TX` block of its text plus 5 bytes, `record` is
|
||||
`16 + 4·cols` (two `f64` time columns then one `f32` per signal), the data group is 28, each of
|
||||
`channels = cols + 2` gets a 46-byte conversion block and a 228-byte channel block, and the
|
||||
channel group is 30. Read the file back with asammdf: `MDF(path).info()` must show one group
|
||||
whose `channel 0` is `name="time" type=master`, and `get(name, group=0).unit` must be the unit
|
||||
the next line reports.
|
||||
`[export-probe] mdf master=time master_unit=s channels=N record=B units=['s' 's' 'V' …]`
|
||||
is the other half, and it is the assertion on the units: `channels` and `record` are the two
|
||||
numbers the `.mf3` size above is built from (so `record` must be `16 + 4·cols` and `channels`
|
||||
`cols + 2`), and `units` must be **the CONFIG's**, in the table's column order, behind the
|
||||
master's and the relative axis's two `s` — one `'s' 's'` pair plus one entry per exported
|
||||
column, each exactly the unit the stream sent (an empty one reads `''`). A `V` where the mock
|
||||
sent `s`, or a missing entry, is the unit not reaching the conversion block, which is the only
|
||||
thing ScryLab reads a signal's unit from; `write_npy`/`write_csv` must be unchanged either way
|
||||
(their bytes are pinned by the two lines above).
|
||||
- `[window-probe] <action> label=.. entry=.. unit=U window=W event_span=E cap=C decim=M
|
||||
stored=R aa=F buffered=B` (stderr, `UDPSCOPE_PROBE_WINDOW_MS`) — **`window` must equal
|
||||
`event_span`** on every line (the trigger's event span is the display window; a capture
|
||||
|
||||
Reference in new issue
Block a user