Skip to content

Commit 5feec51

Browse files
ozgesolidkeyclaude
andcommitted
Docs: MF4 signal visualization (stage 2) design draft
Design doc for the flexible MF4 signal viz: Signals browser + multi-signal overlay on a shared t-axis with click-to-line. Covers reuse of the Trends discovery/series engine, a new aligned+downsampled signalSeries backend, the t-not-wallclock x-axis nuance, a P1/P2/P3 phasing, and open questions for review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 65cf5ed commit 5feec51

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

docs/MF4_VISUALIZATION.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# MF4 Signal Visualization — Stage 2 Design
2+
3+
**Status:** Draft for review · **Owner:** (Özge) · **Created:** 2026-06-30
4+
5+
Stage 1 (done, on `main`) made MF4/MDF4 files open: the adapter decodes channels
6+
into normalized `t=<master> <name>=<value> …` text lines, parsed off-thread in a
7+
worker. You can already chart *one* signal at a time via the Trends panel.
8+
9+
Stage 2 is the **flexible visualization**: browse all signals and overlay several
10+
on a shared time axis, with LOGAN's click-to-line tie-back to the raw records.
11+
12+
---
13+
14+
## 1. Goals
15+
16+
- See **many signals at once**, overlaid on a shared **time (`t`) axis**.
17+
- **Browse/pick** signals from a searchable list (a file can have hundreds).
18+
- Keep LOGAN's signature: **click a point → jump to that line** in the viewer.
19+
- Handle different scales gracefully (e.g. `rpm` 0–8000 vs `throttle` 0–1).
20+
- Reuse the existing Trends discovery + series engine; minimal new surface.
21+
22+
### Non-goals (this stage)
23+
- Channel-conversion (`##CC`) application, units/engineering scaling.
24+
- Math channels / derived signals, FFT, XY (signal-vs-signal) plots.
25+
- Multi-file / baseline overlay.
26+
- Export of the plot (PNG/CSV) — fast follow, not core.
27+
28+
---
29+
30+
## 2. Current state (what we reuse)
31+
32+
| Piece | Where | Reuse |
33+
|-------|-------|-------|
34+
| Field discovery | `trendDiscoverFields()``TrendFieldSpec[]` (`name,type,occurrences,distinct`) | populate the signal list |
35+
| Single series | `trendSeries({field})``TrendSeriesResult` (`points[]` = `{viewerLine, epochMs, num, raw}`) | per-signal samples + click-to-line |
36+
| Canvas chart | `drawTrendChart()` (single series) | generalize to multi-series |
37+
| Click→line | `jumpToTrendLine()` / `goToLine()` | unchanged |
38+
| Bottom panel tabs | `data-bottom-tab="…"` system | host the new view |
39+
40+
**Key nuance — the X axis is `t`, not wall-clock.** MF4's master is a *relative*
41+
float (seconds from start), so `TrendPoint.epochMs` is `null` for these files.
42+
The overlay must plot against the **`t` value** (or record index when `t` is
43+
absent), not the timestamp buckets Trends uses for normal logs. This is the main
44+
reason we need a small new data path rather than just looping `trendSeries`.
45+
46+
---
47+
48+
## 3. UX
49+
50+
A new **"Signals"** tab in the bottom panel (shown for files that expose numeric
51+
fields; surfaced prominently for MF4). Two regions:
52+
53+
```
54+
┌──────────────────────────────────────────────────────────────────────┐
55+
│ Signals [search ▢ "rpm" ] [▣ shared Y] [⊟ fit] [⛶ zoom] │
56+
├───────────────┬──────────────────────────────────────────────────────┤
57+
│ ▣ ● rpm │ ╱╲ ╱╲ │
58+
│ ▣ ● throttle │ ╱╲ ╱ ╲ ╱ ╲ ← overlay plot, x = t │
59+
│ ▢ ● speed │ ╱╲╱ ╲╱ ╲╱ ╲ │
60+
│ ▢ ● coolantT │ ╱ ╲ │
61+
│ … (filterable)│ t=0 t=12.4s cursor: t=6.2 rpm=4100 │
62+
└───────────────┴──────────────────────────────────────────────────────┘
63+
```
64+
65+
- **Left — signal browser:** searchable, scrollable checkbox list. Each row: a
66+
color swatch (auto-assigned, editable later), name, and a sparkline/count hint.
67+
Check to add to the plot, uncheck to remove. "Numeric only" by default.
68+
- **Right — overlay plot:** all checked signals on one canvas, shared `t` axis.
69+
- **Shared Y** (normalized 0–1 per signal) by default so differently-scaled
70+
signals are comparable; toggle to **real Y** with a left axis (and optionally
71+
a second right axis for one selected signal).
72+
- **Cursor readout:** hovering shows `t` and each visible signal's value at the
73+
nearest sample.
74+
- **Zoom/pan:** drag-select to zoom X; double-click to reset; wheel to zoom.
75+
- **Click a point → jump** to that record's line in the viewer (via `viewerLine`).
76+
77+
---
78+
79+
## 4. Data model & backend
80+
81+
Add one main-process method + IPC: **`signalSeries`** — given selected field
82+
names, read the normalized file once and return **aligned** samples:
83+
84+
```ts
85+
// request
86+
{ fields: string[], xField?: string /* default 't' */, maxPoints?: number,
87+
startLine?: number, endLine?: number }
88+
89+
// response
90+
{
91+
x: { field: string; values: number[] }, // the t (or index) axis
92+
series: Array<{
93+
field: string;
94+
values: (number | null)[]; // aligned 1:1 with x (null = absent)
95+
viewerLines: number[]; // for click→line
96+
min: number; max: number; // for autoscale / normalize
97+
}>,
98+
totalRecords: number; truncated: boolean;
99+
}
100+
```
101+
102+
Why a dedicated call instead of N × `trendSeries`:
103+
- **Alignment by `t`:** every record line carries `t=` + the signals; reading once
104+
yields a single shared X with each signal aligned, no cross-join guesswork.
105+
- **One pass, downsampled:** decimate to `maxPoints` (e.g. ~2–4k per signal,
106+
min/max-preserving) so the canvas stays fast on million-record files.
107+
- The line is `t=… name=value …`; parsing is the same regex Trends already uses.
108+
109+
`trendDiscoverFields` is reused as-is for the browser list (it already returns
110+
type so we can default to numeric).
111+
112+
**Surfacing MF4-ness to the renderer:** extend the `OPEN_FILE` result with the
113+
resolved adapter id / capabilities (already tracked in `sourceRegistry` via
114+
`getSourceCapabilities`) so the renderer can auto-open/feature the Signals tab for
115+
MF4. Generic numeric logs can opt in manually.
116+
117+
---
118+
119+
## 5. Phased plan
120+
121+
**P1 — Minimal overlay (the core ask).**
122+
- "Signals" tab + searchable checkbox list (`trendDiscoverFields`).
123+
- `signalSeries` backend (aligned, downsampled).
124+
- Multi-series canvas (generalized `drawTrendChart`), shared-Y normalize, legend,
125+
click→line. Auto colors.
126+
127+
**P2 — Comfort.**
128+
- Zoom/pan + double-click reset, cursor readout/crosshair.
129+
- Real-Y mode with left axis + optional second (right) axis for one signal.
130+
- Per-signal color edit + show/hide without unchecking.
131+
132+
**P3 — Nice-to-have.**
133+
- Stacked **lanes** (separate plots sharing the X cursor) for unrelated scales.
134+
- Export PNG/CSV; persist the chosen signal set per file in `.logan/` sidecar.
135+
136+
Ship P1 first, get it in front of real MF4 files, iterate.
137+
138+
---
139+
140+
## 6. Open questions (need decisions)
141+
142+
1. **Placement** — bottom-panel **"Signals" tab** (consistent with Trends), or a
143+
larger dedicated view? Default: tab.
144+
2. **Default Y mode** — start in **shared/normalized** (compare shapes) or real
145+
units? Default: normalized, with a one-click toggle.
146+
3. **Second axis** — is a single right-hand axis enough for P2, or do you want
147+
full per-signal axis assignment (→ pushes toward P3 lanes)?
148+
4. **Auto-open for MF4** — when an MF4 file opens, should the Signals tab pop
149+
automatically, or stay opt-in like other panels?
150+
5. **Scale limits** — biggest real file (record count) we must stay smooth on?
151+
Drives the downsample budget.
152+
153+
---
154+
155+
## 7. Risks
156+
157+
- **Performance** on huge recordings → mitigated by one-pass min/max decimation
158+
and a hard `maxPoints` cap; never feed raw points to canvas.
159+
- **No timestamp** → X is relative `t`; if a file lacks a master, fall back to
160+
record index and label it clearly.
161+
- **Sparse signals** (different channel groups sampled at different rates) → P1
162+
reads the first channel group only (Stage-1 limitation); multi-group alignment
163+
is a later item, called out so results aren't silently partial.

0 commit comments

Comments
 (0)