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
@@ -89,7 +89,7 @@ src/channel.* ChannelBuffer (a ring pair as a plot::data::Series) and SlotS
|
||||
src/acquisition.* the UDPStreamer client, the producer thread, frame decoding
|
||||
src/trigger.* the trigger configuration and the producer-side state machine
|
||||
src/plot_host.* the one plot::Figure, its rows' traces and cursors, the x link group
|
||||
src/export.* the visible range as a table, and the .npy / .csv writers
|
||||
src/export.* the visible range as a table, and the .npy / .csv / .mf3 writers
|
||||
src/layout_store.* the per-source session layout file (TOML), written and read back
|
||||
src/event_series.hpp an immutable captured event as a plot::data::Series
|
||||
src/ui.* the GTK4 window: toolbar, trigger panel, signal panel, plot box
|
||||
@@ -2261,16 +2261,42 @@ magnitudes, so consecutive differences wobble by a few ULP of the stream's uptim
|
||||
(femtoseconds at any plausible uptime — note that the `f32` defect this used to guard against
|
||||
was *microseconds*, four orders of magnitude up).
|
||||
|
||||
Two formats, picked by the extension the save dialog ends up with:
|
||||
Three formats, picked by the extension the save dialog ends up with:
|
||||
|
||||
| file | what it is |
|
||||
|---|---|
|
||||
| `.npy` | one NumPy structured array, `time`/`abs_seconds` as `<f8` and every signal as `<f4` — the width the ring stores, so the file invents no precision and is about half an all-`f8` one. `pd.DataFrame(np.load(path))` is the whole reader. |
|
||||
| `.csv` | the same table as text, `%.17g` for the times and `%.9g` for the values: the shortest widths that round-trip each type exactly. |
|
||||
| `.mf3`, `.mdf` | **MDF 3.30**, one channel group with every channel record-interleaved, written by hand — see *The MDF export* below. |
|
||||
|
||||
Parquet (the original ask) would have meant linking Arrow — tens of megabytes of
|
||||
dependency for a feature whose output a three-line reader already handles.
|
||||
|
||||
**The MDF export** is the format for handing a window to another tool, and specifically to
|
||||
[ScryLab](https://docs.scrylab.de/docs/data-sources/), whose data-source contract is three
|
||||
rules this file is shaped by: *every data channel becomes a signal* — the channel name is the
|
||||
signal name — *the master channel of each channel group is the x axis*, and *the unit is taken
|
||||
from the channel metadata*, which in MDF 3 means the channel's conversion block (`CCBLOCK`) and
|
||||
nowhere else. A second occurrence of a name is dropped by that reader, so the channel names are
|
||||
de-duplicated against each other and the master's.
|
||||
|
||||
Two things follow from that, and both differ from what the `.npy`/`.csv` table holds:
|
||||
|
||||
- **The master is `abs_seconds`, named `time`**, not the axis the plot showed. The relative
|
||||
column is 0 at a different sample in each display mode, so a master off it would put a live
|
||||
export and a held event's export on two different timelines — the one thing that column
|
||||
exists to avoid. The relative axis goes out as an ordinary channel, **`time_relative`**,
|
||||
right after the master, and both carry the unit `s`. The signal columns follow in the same
|
||||
order the `.npy`/`.csv` file writes them.
|
||||
- **The unit travels with a column**, taken from the stream's CONFIG (or a math channel's own),
|
||||
and is written into the conversion block. The `.npy`/`.csv` writers are unchanged: their
|
||||
readers get the names and the numbers, and their bytes are what they always were.
|
||||
|
||||
There is **no MDF writer library here and no new dependency**: the blocks are spelled out in
|
||||
`src/export.cpp` against the field list the `asammdf` 8.8.27 reader walks the format with, and
|
||||
`docs/claude/invariants-session-and-export.md` records the layout, the version decision (MDF
|
||||
3.30, not MF4 — that is HDF5) and what was verified against a known-good reference file.
|
||||
|
||||
The table is collected when the menu item is clicked, not when the dialog is
|
||||
answered: a live view keeps scrolling while the file chooser is up. The live cut goes
|
||||
through `ScopeModel::live_window_locked()`, the same index range `publish_event()`
|
||||
|
||||
Reference in new issue
Block a user