<!-- SPDX-License-Identifier: MIT -->
<!-- Copyright (c) 2026 Olympian LED -->

# rate_kit: waveforms a second on a DHO800 / DHO900 / MHO900, counted from outside

`rate_kit.py` counts how many waveforms a second a Rigol DHO800, DHO900 or MHO900 acquires. It uses the same
method on any firmware: Rigol's stock firmware, Norbert Kiszka's mod, or NovaOS. It is one Python file (3.8 or
newer, standard library only) and talks only SCPI, over the raw socket on TCP 5555.

We wrote it so that others can run our method on their own units and tell us where it is wrong.
[METHOD.md](METHOD.md) has the runs we made on 2026-10-16, with the raw readings in `data/2026-10-16/`.

## What it measures

`:SYSTem:AOUTput TOUT` sets the rear AUX OUT connector to the trigger output. Rigol's MHO900 User Guide (section
4.4.2, Rear Panel) says that this output "can reflect the current capture rate of the oscilloscope at each
trigger", and that its measured frequency "is the same as the current capture rate". A counter in totalize mode
adds up its pulses:

    rate = (count1 - count0) / (t1 - t0)

`t0` and `t1` are the times at which the two `:COUNter:CURRent?` answers arrive, over a window of `--seconds`
(default 10).

- It counts triggers the scope accepted. It does not count waveforms drawn, and it does not measure dead time.
- A mode that records several triggers per pulse is counted once per pulse (the stock's UltraAcquire pulses once
  per sequence).

## Methods and wiring

Use a short BNC cable from AUX OUT to the counting input, with that input at 1 MOhm (`OMEG`).

| method | wiring | what counts | cost |
|---|---|---|---|
| `aux-self` | AUX OUT into one of the unit's own inputs (`--aux-input`, default CH4 on an MHO900, CH3 on a DHO). The signal on `--signal-input` (default CH1 on an MHO900, from G2; CH4 on a DHO). | the unit's own counter | The counter's input is digitised, shown or hidden, so the unit is no longer measuring one channel. On our stock MHO934 at 20 ns/div this halved the sample rate (4 to 2 GSa/s) and the rate (see Pitfalls). |
| `aux-external` | AUX OUT of the unit under test into an input of a second Rigol DHO/MHO (`--counter HOST`, `--counter-input`, default CH1). | the second scope's counter | Needs a second scope. The unit under test runs its signal channel alone, at its full sample rate. |

The signal comes from the built-in generator by default: a square wave from 0 to 3 V at 10 MHz (`--frequency`,
`--amplitude`), on `:SOURce2` on an MHO900 (`:SOURce` on a DHO900 "S"). With `--signal external` you set your own
generator and the kit leaves the generator alone. The trigger is an edge trigger, NORMal sweep, rising edge, at
1.5 V. The counting input's trigger level is also set to 1.5 V, because the counter counts crossings of its
input's trigger level.

## Running it

```sh
python3 rate_kit.py identify --host SCOPE                                  # read-only
python3 rate_kit.py measure  --host SCOPE --method aux-self                # 20 ns and 2 us/div; 1k, 10k, AUTO
python3 rate_kit.py measure  --host SCOPE --method aux-external --counter COUNTER --counter-input 3 \
                             --tdiv 20e-9 --depth 1k,10k,AUTO --repeats 3
python3 rate_kit.py restore  --host SCOPE --from rate-kit-<stamp>/snapshot-<stamp>.json   # after a hang or a kill
python3 rate_kit.py scpi-health --host SCOPE                               # read-only: latency, error queue
python3 rate_kit.py preset   --host SCOPE --yes-factory-default --method aux-external --counter COUNTER
python3 rate_kit.py trio --unit stock=STOCK,counter=COUNTER@1 --unit mod=MOD,counter=COUNTER@1 \
                         --unit novaos=NOVAOS,counter=COUNTER@1 --method aux-external --tdiv 20e-9 --depth 1k
python3 rate_kit.py measure --help                                         # every option, with each method's caveats
```

`SCOPE`, `COUNTER` and the others are your units' addresses (`HOST` or `HOST:PORT`).

Options you may want:

- `--channels one,all` adds a scene with every input on.
- `--counter-channel hidden` hides the AUX input while counting (`aux-self`). Its scale, offset and level are set
  while it is shown either way (see Pitfalls).
- `--display bench` sets vectors, infinite persistence and colour grade; `--display default` sets vectors, minimum
  persistence and colour grade off; the default `as-is` leaves the display alone.
- `--poll counter` also reads the count once a second, which adds a least-squares slope and shows how often the
  count changes. `--poll quiet` sends nothing between the two counter reads.
- `--scene minimal` writes only the generator, the signal input's display, scale and offset, the time base, the
  edge source and level, and NORMal sweep; everything else stays as you set it (we used it right after `*RST`).
  `--depth as-is` leaves the depth alone.
- `--load-counter-input N` (with `aux-external`) also runs the unit's own counter on its input N, unread, to measure
  what that costs; `--load-counter-channel shown|hidden`.
- `--clear-measurements` deletes every measurement item before counting (they cannot be restored).
- `--signal-check` sends `:MEASure:ITEM? VPP` on the signal input before counting. It is off by default, because
  that query leaves a live measurement on the screen (see Pitfalls).
- `--settle` (2 s) is the wait after `:RUN`; `--clear-wait` (3 s) is the wait between the counter's `CLEar` and its
  first read.

Each run writes three files to `--out`, default `./rate-kit-<UTC stamp>/`:

- `snapshot-<stamp>.json`: every setting the kit may touch, written before anything changes;
- `rate-kit-<stamp>.json`: everything, including every SCPI line with its time, reply and latency (`trace`), each
  window's raw counter readings with UTC timestamps, and the settings read back while counting;
- `rate-kit-<stamp>.md`: the same as tables.

The serial number and the addresses are replaced by `<serial hidden>` / `<address hidden>` unless you pass
`--show-serial` / `--show-address`.

Settings are put back on success, on an error and on Ctrl-C, including the run/stop state. Only settings that
differ are written back, and the kit then reads everything again and lists what still differs. Exit codes: 0
measured and restored; 1 a measurement failed, restored; 2 usage error, or not a DHO800, DHO900 or MHO900; 3 the
restore is incomplete (the report says what); 4 SCPI hung, nothing could be restored (run `restore --from` once it
answers again); 130 Ctrl-C, restored.

The kit never sends `:DISPlay:DATA?`, never saves files on the scope, and sends `*RST` and `:SYSTem:SETup` only in
`preset`, behind `--yes-factory-default`. Every read has a timeout. After a timeout it opens a new connection and
sends `*IDN?` before it goes on.

## The other commands

- **`scpi-health`** is read-only: it times `*IDN?` and `:TRIGger:STATus?` and reads the error queue.
- **`preset`** counts on a factory-default scope. It saves the `:SYSTem:SETup?` block, sends `*RST`, measures (with
  `--scene minimal` unless you choose otherwise), then writes the block back, with the generator and the run
  state, and compares the settings it read before. Measurement items cleared by the reset do not come back.
- **`trio`** runs the same scene on several units, one after another, and writes one table. `method=` and
  `counter=HOST@INPUT` can be set per unit.

## Every SCPI command the kit sends

Checked against Rigol's programming guides: the MHO900 Programming Guide (PDF dated 2026-02-06) and the
DHO800/DHO900 Programming Guide (PDF dated 2025-04-22). The section numbers are the guides' own; the page is the
PDF page, linked. `<n>` is a channel or generator number. "set" means the command with a value.

| command | sent as | when | MHO900 guide | DHO800/DHO900 guide |
|---|---|---|---|---|
| `*IDN?` | query | every command; again after any timeout | [3.12.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=165) (PDF p. 165) | [3.12.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=148) (PDF p. 148) |
| `*OPC?` | query | `preset` only, after `*RST` and after the setup goes back | [3.12.6](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=168) (PDF p. 168) | [3.12.6](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=151) (PDF p. 151) |
| `*RST` | command | `preset` only, with `--yes-factory-default` | [3.12.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=166) (PDF p. 166) | [3.12.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=149) (PDF p. 149) |
| `:ACQuire:MDEPth` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.3.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=42) (PDF p. 42) | [3.3.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=42) (PDF p. 42) |
| `:ACQuire:SRATe?` | query | measuring (`measure`, `preset`, `trio`) and `restore` | [3.3.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=44) (PDF p. 44) | [3.3.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=44) (PDF p. 44) |
| `:ACQuire:TYPE` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.3.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=43) (PDF p. 43) | [3.3.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=43) (PDF p. 43) |
| `:BUS<n>:DISPlay?` | query | read in `preset` only, before and after | [3.4.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=46) (PDF p. 46) | [3.4.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=48) (PDF p. 48) |
| `:CHANnel<n>:BWLimit` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.6.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=106) (PDF p. 106) | [3.6.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=93) (PDF p. 93) |
| `:CHANnel<n>:COUPling` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.6.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=106) (PDF p. 106) | [3.6.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=94) (PDF p. 94) |
| `:CHANnel<n>:DISPlay` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.6.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=107) (PDF p. 107) | [3.6.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=94) (PDF p. 94) |
| `:CHANnel<n>:IMPedance?` | query | read in `preset`, on an MHO900 only | [3.6.7](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=110) (PDF p. 110) | **not in it** |
| `:CHANnel<n>:INVert?` | query | read in `preset` only, before and after | [3.6.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=108) (PDF p. 108) | [3.6.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=95) (PDF p. 95) |
| `:CHANnel<n>:OFFSet` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.6.5](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=108) (PDF p. 108) | [3.6.5](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=96) (PDF p. 96) |
| `:CHANnel<n>:PROBe` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.6.9](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=112) (PDF p. 112) | [3.6.8](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=98) (PDF p. 98) |
| `:CHANnel<n>:SCALe` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.6.8](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=111) (PDF p. 111) | [3.6.7](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=97) (PDF p. 97) |
| `:CHANnel<n>:VERNier?` | query | read in `preset` only, before and after | [3.6.13](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=115) (PDF p. 115) | [3.6.12](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=101) (PDF p. 101) |
| `:COUNter:CURRent?` | query | at each end of a window (each second too with `--poll counter`) | [3.7.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=116) (PDF p. 116) | [3.7.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=102) (PDF p. 102) |
| `:COUNter:ENABle` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.7.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=117) (PDF p. 117) | [3.7.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=103) (PDF p. 103) |
| `:COUNter:MODE` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.7.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=118) (PDF p. 118) | [3.7.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=104) (PDF p. 104) |
| `:COUNter:SOURce` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.7.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=117) (PDF p. 117) | [3.7.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=103) (PDF p. 103) |
| `:COUNter:TOTalize:CLEar` | command | before each window, on the counting scope | [3.7.7](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=120) (PDF p. 120) | [3.7.7](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=106) (PDF p. 106) |
| `:COUNter:TOTalize:ENABle` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.7.6](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=119) (PDF p. 119) | [3.7.6](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=105) (PDF p. 105) |
| `:DISPlay:COLor` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.9.11](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=152) (PDF p. 152) | [3.9.9](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=136) (PDF p. 136) |
| `:DISPlay:GBRightness?` | query | read in `preset` only, before and after | [3.9.6](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=149) (PDF p. 149) | [3.9.6](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=134) (PDF p. 134) |
| `:DISPlay:GRADing:TIME` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.9.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=147) (PDF p. 147) | [3.9.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=132) (PDF p. 132) |
| `:DISPlay:GRID?` | query | read in `preset` only, before and after | [3.9.5](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=148) (PDF p. 148) | [3.9.5](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=133) (PDF p. 133) |
| `:DISPlay:RULers?` | query | read in `preset` only, before and after | [3.9.9](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=151) (PDF p. 151) | [3.9.8](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=135) (PDF p. 135) |
| `:DISPlay:TYPE` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.9.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=146) (PDF p. 146) | [3.9.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=131) (PDF p. 131) |
| `:DISPlay:WBRightness?` | query | read in `preset` only, before and after | [3.9.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=148) (PDF p. 148) | [3.9.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=133) (PDF p. 133) |
| `:HISTogram:ENABle?` | query | read in `preset` only, before and after | [3.11.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=156) (PDF p. 156) | [3.11.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=140) (PDF p. 140) |
| `:MASK:ENABle?` | query | read in `preset` only, before and after | [3.15.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=186) (PDF p. 186) | [3.15.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=170) (PDF p. 170) |
| `:MATH<n>:DISPlay?` | query | read in `preset` only, before and after | [3.16.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=197) (PDF p. 197) | [3.16.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=181) (PDF p. 181) |
| `:MEASure:AMSource?` | query | read in `preset` only, before and after | [3.17.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=237) (PDF p. 237) | [3.17.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=219) (PDF p. 219) |
| `:MEASure:CLEar` | command | only with `--clear-measurements`, on a DHO800/DHO900 | **not in it** | [3.17.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=218) (PDF p. 218) |
| `:MEASure:DELete` | command | only with `--clear-measurements`, on an MHO900 | [3.17.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=236) (PDF p. 236) | **not in it** |
| `:MEASure:ITEM?` | query | only with `--signal-check` (off by default) | [3.17.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=235) (PDF p. 235) | [3.17.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=217) (PDF p. 217) |
| `:MEASure:STATistic:DISPlay?` | query | read in `preset` only, before and after | [3.17.6](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=238) (PDF p. 238) | [3.17.6](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=220) (PDF p. 220) |
| `:NOVAos:DENSity:RATE?` | query | NovaOS's own query, sent only to a unit that answered `:NOVAos:VERSion?` | **not in it** | **not in it** |
| `:NOVAos:VERSion?` | query | NovaOS's own query, sent once at the start to any unit whose maker string is not Kiszka's mod; stock does not answer it (2 s timeout) and queues -100, which the kit reads off the error queue | **not in it** | **not in it** |
| `:RUN` | command | measuring (`measure`, `preset`, `trio`) and `restore` | [3.1.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=33) (PDF p. 33) | [3.1.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=33) (PDF p. 33) |
| `:SOURce<n>:FREQuency` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.25.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=326) (PDF p. 326) | [3.25.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=298) (PDF p. 298) |
| `:SOURce<n>:FUNCtion` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.25.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=324) (PDF p. 324) | [3.25.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=298) (PDF p. 298) |
| `:SOURce<n>:OUTPut:STATe` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.25.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=324) (PDF p. 324) | [3.25.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=297) (PDF p. 297) |
| `:SOURce<n>:VOLTage:AMPLitude` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.25.10](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=330) (PDF p. 330) | [3.25.7](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=301) (PDF p. 301) |
| `:SOURce<n>:VOLTage:OFFSet` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.25.11](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=331) (PDF p. 331) | [3.25.8](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=302) (PDF p. 302) |
| `:STOP` | command | measuring (`measure`, `preset`, `trio`) and `restore` | [3.1.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=34) (PDF p. 34) | [3.1.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=34) (PDF p. 34) |
| `:SYSTem:AOUTput` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.24.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=307) (PDF p. 307) | [3.24.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=289) (PDF p. 289) |
| `:SYSTem:ERRor?` | query | every command (the error queue) | [3.24.21](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=321) (PDF p. 321) | [3.24.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=290) (PDF p. 290) |
| `:SYSTem:SETup` | query, set | `preset` only: the setup saved before `*RST` and written back after | [3.24.20](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=320) (PDF p. 320) | [3.24.8](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=292) (PDF p. 292) |
| `:TIMebase:MAIN:OFFSet` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.26.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=346) (PDF p. 346) | [3.26.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=313) (PDF p. 313) |
| `:TIMebase:MAIN:SCALe` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.26.5](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=347) (PDF p. 347) | [3.26.5](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=314) (PDF p. 314) |
| `:TIMebase:MODE?` | query | read in `preset` only, before and after | [3.26.6](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=347) (PDF p. 347) | [3.26.6](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=315) (PDF p. 315) |
| `:TRIGger:COUPling?` | query | read in `preset` only, before and after | [3.27.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=354) (PDF p. 354) | [3.27.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=323) (PDF p. 323) |
| `:TRIGger:EDGE:LEVel` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.27.8.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=360) (PDF p. 360) | [3.27.8.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=328) (PDF p. 328) |
| `:TRIGger:EDGE:SLOPe` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.27.8.2](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=359) (PDF p. 359) | [3.27.8.2](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=328) (PDF p. 328) |
| `:TRIGger:EDGE:SOURce` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.27.8.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=358) (PDF p. 358) | [3.27.8.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=327) (PDF p. 327) |
| `:TRIGger:HOLDoff?` | query | read in `preset` only, before and after | [3.27.5](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=356) (PDF p. 356) | [3.27.5](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=325) (PDF p. 325) |
| `:TRIGger:MODE` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.27.1](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=354) (PDF p. 354) | [3.27.1](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=322) (PDF p. 322) |
| `:TRIGger:NREJect?` | query | read in `preset` only, before and after | [3.27.6](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=357) (PDF p. 357) | [3.27.6](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=326) (PDF p. 326) |
| `:TRIGger:STATus?` | query | measuring (`measure`, `preset`, `trio`) and `restore` | [3.27.3](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=355) (PDF p. 355) | [3.27.3](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=324) (PDF p. 324) |
| `:TRIGger:SWEep` | query, set | measuring (`measure`, `preset`, `trio`) and `restore` | [3.27.4](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900-ProgrammingGuide.pdf#page=356) (PDF p. 356) | [3.27.4](https://aioscilloscope.com/bench/rigol-dho924s-oscilloscope/docs/DHO800900_ProgrammingGuide_EN.pdf#page=324) (PDF p. 324) |

Not in Rigol's guides:

- `:NOVAos:VERSion?` and `:NOVAos:DENSity:RATE?` are NovaOS's own queries. The first is how the kit tells NovaOS
  from the stock firmware, whose `*IDN?` looks the same. The stock firmware does not answer it: the kit waits 2 s,
  then reads the `-100` it queued off the error queue. The kit does not send it to a unit whose `*IDN?` maker is
  Kiszka's mod.
- `:CHANnel<n>:IMPedance?` is in the MHO900 guide only, and the kit sends it to an MHO900 only.
- `:MEASure:DELete` is in the MHO900 guide and `:MEASure:CLEar` in the DHO800/DHO900 guide; the kit sends each only
  to its own family.

The User Guide quoted above is the MHO900 User Guide, section 4.4.2,
[PDF p. 44](https://aioscilloscope.com/bench/rigol-mho934-oscilloscope/docs/MHO900_UserGuide_EN.pdf#page=44).

## Pitfalls we found

All on our bench on 2026-10-16, on MHO934s at 20 ns/div with a 10 MHz square from G2 on CH1. METHOD.md has the
readings.

- **A measurement item on the screen costs the stock firmware about a fifth of its rate.** One
  `:MEASure:ITEM? VPP,CHANnel1` leaves a live VPP measurement running. Right after `*RST`, at 1k, our stock unit
  read 24,370 and 25,537 wfm/s (20:20Z) and, with that one item on, 19,490 and 19,875 (20:23Z): 21 % less. Do not
  leave `:MEASure` items running while you count. The kit sends no measurement query unless you pass
  `--signal-check`, and leaves items that are already on unless you pass `--clear-measurements`.
- **Counting on the unit's own input halves the rate.** With CH1 alone, `:ACQuire:SRATe?` read 4 GSa/s; with the
  unit's own counter in totalize on its CH4, 2 GSa/s, whether CH4 was shown or hidden. Counted by a second scope,
  our stock unit after `*RST` read 25,361 wfm/s with CH1 alone, 13,167 with its own counter on its hidden CH4
  (-48 %), and 9,060 with CH4 shown (-64 %) (mean of nine 10 s windows each, 21:18-21:27Z). So `aux-self` is a
  lower bound; `aux-external` gives the one-channel figure.
- **Kiszka's mod v0.1.5 ignores a hidden channel's scale, offset and trigger level, with no error.** The trigger
  level is the counter's threshold, so a counter on a channel hidden before its setup stayed at 0. Set a channel up
  while it is shown, then hide it, and the mod counts it. The kit always shows the counting input before it sets it
  up, and before it restores it.
- **An abandoned `:DISPlay:DATA? PNG` read left SCPI on our mod v0.1.5 unit unanswered until a reboot.** The client
  had timed out before the transfer finished. The same read on the stock unit completed. The kit never sends
  `:DISPlay:DATA?`.
- **The stock's count moves in steps.** 75 of the 90 counter readings in our published windows are multiples of
  500, so the readout appears to update in chunks. A step of 500 at each end of a 10 s window of about 260,000
  counts is up to about 0.4 %. `--poll counter` shows the steps; longer windows make the error smaller.
- **The stock app aborted once while its counter went back to FREQ** with a shown, unconnected channel as its
  source (21:26:47 on the unit's clock; it was started again by itself about 30 s later). The kit now switches the
  counter off before it changes the counter's mode or source.
- **The edge trigger level belongs to the selected source.** The kit selects the counting input, sets its level,
  then selects the signal input and sets that one, and reads and restores the level of each source separately.

## What we would like to learn

1. How do you measure waveforms a second, and how do you measure dead time? How do you tell triggers the scope
   accepted from waveforms it drew?
2. The scene behind your own figures: memory depth, display (vectors or dots, persistence, colour grade), channels
   on, input termination, signal and frequency, sweep, and whether anything else on the screen was on (measurements,
   counter, menus).
3. How do you check that every acquired waveform was drawn? Do you take the trigger-out count as the acquired count?
4. What is wrong with our method? Look in particular at the counter's own channel, the 10 s window against the
   count's steps, NORMal at 1.5 V on a 10 MHz square, and the 3 s wait after the clear.

Corrections are welcome. Send the `rate-kit-<stamp>.json` from your run: it holds the full command trace, so we can
follow your run command by command.

## Files

- `rate_kit.py`: the kit.
- `test_rate_kit.py`: tests against a fake SCPI server. Run them with `python3 -I test_rate_kit.py`.
- `METHOD.md`: how we measured on 2026-10-16, with the raw numbers.
- `data/2026-10-16/`: the runs as files (`windows.csv` has every window), with serial numbers and addresses removed.
- `LICENSE`: the MIT licence.

MIT licence. Copyright (c) 2026 Olympian LED. No third-party code is included.
