# waveview — using it

Two programs, one renderer:

| | |
|---|---|
| `waveview <file.raw>` | interactive window |
| `wave-plot <file.raw> -o out.png` | the same plot, written to a PNG, **no display needed** |

Both read ngspice/Berkeley raw files, binary or ASCII, real (`.tran`, `.dc`) or
complex (`.ac`). Multi-gigabyte files open in well under a second.

```sh
waveview sim/out1.raw                 # look at it
waveview run_a.raw run_b.raw          # compare two runs in one window
waveview sim/out1.raw --size 1280x720 # smaller window
waveview sim/out1.raw --font-size 20  # bigger text (see below)
wave-plot sim/out1.raw -o out.png -s 'v(out)'      # or just get a picture
waveview --help ; wave-plot --help    # options
```

## The signal list

Opens with **one empty pane and nothing plotted** — click a signal to show it.

Hierarchical names become a tree: `v(xio.xser.rxp_a)` appears as
`xio ▸ xser ▸ rxp_a`. Click a subcircuit to expand it; collapsed
ones show how many children they hold. The plot legend always shows the full
path, so a trace is never ambiguous.

Clicking a signal shows it; clicking it again hides it, wherever it is.
Shift-click puts a second copy in the current pane.

## AC files: db, phase, and why `vdb()` is not in the file

ngspice stores the **complex** node voltage. `vdb(out)` is a display function,
not a stored quantity — asking the simulator to save it does not work
(`.probe ac vdb(out)` is HSPICE/Eldo syntax; ngspice rejects both the `ac`
keyword and `vdb`). Nothing is lost: dB, phase, magnitude, real and imag are
all exact projections of what *is* stored, and storing dB instead would throw
phase away for good.

So in the viewer every `.ac` signal **expands into its projections** —
`db`, `phase`, `phase unwrapped`, `mag`, `real`, `imag`. Click `db` under a
signal to get what `vdb()` would have given. In `wave-plot`, write them as
`-s 'db(v(out))'`, `-s 'cph(v(out))'`, and so on.

All six are checked against ngspice's own `db`/`ph`/`cph`/`mag`/`real`/`imag`
to the last digit of the reference file.

## Panes

`p` adds a pane, `P` deletes the current one, `d` clears it. Panes share the
time axis. **Click inside a pane to make it current** — that is how a signal
goes into an earlier pane rather than the newest one. The current pane has a
highlighted frame.

## Zoom

Wheel zooms time about the pointer. Right-drag zooms to a region, and *where
the drag starts decides the axes*: inside a pane zooms both, below the X axis
zooms time only, over the Y labels zooms that pane's amplitude only. A plain
right-click zooms out 2x. Shift-wheel (or the wheel over the Y labels) zooms
amplitude in and out. `f` fits both axes again.

## Cursors and measurements

`a` and `b` place cursors at the pointer, `A` and `B` remove them, `c` clears
both. **Drag a cursor with the mouse** to move it — every readout follows, and
each cursor labels the time or frequency it sits on. With two cursors down, **each pane
shows dy and the slope on the plot itself**, under the cursor labels, for that
pane's selected trace — so a gain/phase pair reports dB/decade above and
deg/decade below at the same time.  The status bar repeats it with **dx** between them for the selected trace — slew
rate on a transient edge (`12kV/s`), and **dB per decade** on a log axis
(`-20dB/dec` for a single pole), since per-hertz would be meaningless there.  This works the same in the time
domain and in AC; on a narrow window the status line drops the trace name and
any message before it drops the measurement.

`x` snaps cursor 1 to where the *selected* trace crosses zero (`X` for cursor
2). On a dB trace that is the unity-gain frequency, so with gain in one pane
and phase in the other you get phase margin directly: select the dB trace,
press `x`, read the phase pane. On a trace with no natural zero it snaps to
the mid-level crossing instead.

`s` opens a **statistics window**: one row per plotted trace, in the trace's
colour, with min, max, peak-to-peak, time-weighted average, RMS, and frequency
and duty cycle where the signal is periodic. It measures between the cursors
when both are placed, otherwise across the view.

`m` toggles the per-trace dy/slope labels in the panes — with many traces they
crowd the plot when all you want is the `c2-c1` delta above it.

Measurements read real samples, never the drawn envelope, so they are exact;
on a wide span over a large file that costs a moment, and narrowing with
cursors makes it instant.  A frequency is only reported when there are enough
samples per cycle for it to mean something, so noise does not masquerade as a
gigahertz signal.

Averages are **time-weighted**, not sample-weighted: SPICE timesteps are not
uniform, and a plain sample mean would over-weight the edges and disagree with
both `.measure` and an oscilloscope.

## Selecting traces

Click a trace or its legend label to select it — it is drawn a pixel thicker.
Shift-click adds to the selection, and `Del` removes everything selected.
`m` measures the selected trace.

## Files

`r` reloads the current file after a re-run, keeping your signals, zoom,
cursors and expanded subcircuits. Signals are re-bound **by name**, so if the
deck changed, anything that no longer exists is dropped and reported rather
than silently becoming a different trace.

`o` opens another file in place; `O` adds one **alongside** the current ones.
Type the path in the status bar, Enter opens, Esc cancels.

## Comparing runs

Give several files on the command line, or add one with `O`.  Each file
becomes its own root in the signal list, so the same node name in two runs
stays distinct, and the legend prefixes traces with the file they came from
(`run_1p.raw:v(out)`).  Plot them into the same pane and the cursors,
dy/slope readouts and the statistics window all cover both.

The time axis is the union of the runs, so files of different length line up
on real time rather than on sample number.  `r` reloads every open file.

## Batch plotting

`wave-plot` needs no display, so it runs over ssh and in scripts:

```sh
wave-plot out.raw -o a.png -s 'v(xafe.xi0.outp)' -s 'v(xafe.xi0.outn)'
wave-plot out.raw -o z.png -s 'v(clk)' --from 4.99e-5 --to 5.01e-5
wave-plot ac.raw  -o b.png -s 'v(out)' --bode
wave-plot out.raw -o d.png -s 'v(a)' --pane -s 'v(b)'      # stacked panes
for f in sweep_*.raw; do wave-plot $f -o ${f%.raw}.png -s 'v(out)'; done
```

## Keys and mouse

Also available with `h` in the viewer, or `waveview --keys`.

| | |
|---|---|
| **SIGNALS** | |
| `click a subcircuit` | expand / collapse it |
| `expand an .ac signal` | pick db / phase / mag / real / imag |
| `click name` | show / hide in the current pane |
| `shift-click name` | also show it here (keep it elsewhere too) |
| `click a trace or its label` | select it (highlighted) |
| `shift-click a trace` | add / remove it from the selection |
| `Del` | remove every selected trace |
| **PANES** | |
| `click in a pane` | make it the current pane |
| `p` | new pane |
| `P` | delete the current pane |
| `d` | clear the current pane |
| `n` | next pane |
| **ZOOM AND PAN** | |
| `wheel` | zoom time about the pointer |
| `shift-wheel` | zoom that pane's amplitude |
| `wheel on Y labels` | same - zoom amplitude |
| `middle-drag` | pan |
| `right-drag in a pane` | zoom to the box, both axes |
| `right-drag below X axis` | zoom X only |
| `right-drag on Y labels` | zoom that pane's Y only |
| `right-click` | zoom out 2x (amplitude if on Y labels) |
| `f` | fit both axes |
| `arrows / PgUp / PgDn` | pan, and scroll the signal list |
| **CURSORS** | |
| `drag a cursor` | move it; readouts follow |
| `a  /  b` | place cursor 1 / cursor 2 at the pointer |
| `x  /  X` | snap cursor 1 / 2 to the selected trace's zero |
| `A  /  B` | remove cursor 1 / cursor 2 |
| `c` | clear both cursors |
| `two cursors` | dy and slope per trace, in its own colour |
| **MEASURE** | |
| `m` | toggle the dy/slope labels in the panes |
| `s` | statistics for every trace, in its own window |
| **OTHER** | |
| `r` | reload the file (after a re-run) |
| `o  /  O` | open a file / add one alongside (compare runs) |
| `w` | write a PNG of the window |
| `status bar` | frame time and KB pushed to the display |
| `h  or  ?` | this help |
| `q  or  Esc` | quit |

## Text size

`--font-size <px>` sets the text size, in both `waveview` and `wave-plot`:

```sh
waveview sim/out1.raw --font-size 20
wave-plot sim/out1.raw -o out.png --font-size 20   # for a slide or a report
```

Four sizes are baked in — **13** (the default), **16**, **20** and **26** —
and the nearest one to what you ask for is used. They are real bitmaps
rendered at each size, not one bitmap scaled up, so the large sizes stay
sharp; that is also why the list is fixed rather than any number you like.

Everything else follows the text: the signal list, the margins, the legend,
the cursor readouts and the statistics window all take their spacing from the
font, so nothing overlaps at any size. The one exception is the help window,
which drops to two columns, and then to a smaller size, if it would otherwise
not fit on the screen.

## Known limits

- Overlapping traces paint over each other — no transparency between signals.
- Only one file at a time; comparing two runs in one window is not built yet.
- No expression parser: `wave-plot --sub a b` is the only derived curve.
- Bus signals (`v(ctl<0>)`) are separate leaves, not grouped.
- On files with ~1000 saved signals there is one band of zoom that pauses
  briefly while it reads real samples.

For the design and the measurements behind it, see `../README.md`.
