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

# How current_probe_kit measures

This page gives the arithmetic of `current_probe_kit.py` and the source of every figure it uses. The function names
are the ones in the kit.

## Sources

The page numbers are each PDF's own page numbers (the page a PDF viewer opens), not the printed folio.

| figure | value | source |
|---|---|---|
| CP503B ranges and output | 5 A range 1 V/A, 30 A range 0.1 V/A | Micsig CP Series Product Introduction (2026-09-21) p.4; CP Series User Manual (2026-09-23) p.12 |
| CP503B accuracy, DC and 45-66 Hz | +-1 % +-1 mA (5 A), +-1 % +-10 mA (30 A) | Product Introduction p.4; User Manual p.15 (warranted) |
| CP503B bandwidth, rise time | DC-50 MHz, <= 7 ns | Product Introduction p.4; User Manual p.15 |
| CP503B noise (20 MHz limit) | < 4 mApp (5 A), < 30 mApp (30 A) | Product Introduction p.4; User Manual p.16 (typical) |
| CP503B overcurrent alarm | >= 7 Apk (5 A), >= 50 Apk (30 A) | User Manual p.16 (typical) |
| CP503B supply, input | DC 12 V from its adapter, never a scope's USB; the output into 1 MOhm | User Manual p.13, p.24 |
| warm-up, zero | 20 minutes; degauss and zero with the jaw closed and clicked; again after an overload | User Manual p.12, p.13, p.25 |
| polarity | the current in the direction of the arrow on the jaw | User Manual p.14 |
| small currents | the scope's 20 MHz band limit; several turns through the jaw, divided by the turns | User Manual p.26 |
| the 12 V adapter (Mean Well NGE18I12-P1J) | 12 V, 1.5 A rated, 18 W, 120 mVp-p ripple, +-3 % | Mean Well NGE18 series specification (2026-01-14) p.2 |
| `:CHANnel<n>:PROBe` in amperes | 0.001 to 10, "with the unit V/A" | MHO900 Programming Guide 3.6.9 |
| `:CHANnel<n>:PROBe` list | 0.001 ... 50000 | MHO900 Programming Guide 3.6.9; DHO800/DHO900 Programming Guide 3.6.8 |
| `:CHANnel<n>:UNITs` | WATT, AMPere, VOLTage, UNKNown | MHO900 guide 3.6.12; DHO guide 3.6.11 |
| scope DC gain accuracy | +-1 % of full scale (> 5 mV/div) | DHO900 data sheet p.10; MHO900 data sheet p.11 |
| DP2031 CH1 / CH2 | 0-32 V, 0-3 A; OCP 1 mA-3.3 A | DP2000 data sheet p.8 |
| DP2031 current readback | 0.15 % + 5 mA (CH1, CH2), 0.15 % + 10 mA (CH3) | DP2000 data sheet p.9 |
| DL3031 CC setting accuracy, 0-6 A | 0.1 % + 6 mA | DL3000 data sheet p.4 |
| DL3031 current readback | +-(0.1 % + 0.1 % FS) on 0-60 A | DL3000 data sheet p.5 |
| DL3031 transient frequency | 0.001 Hz-15 kHz | DL3000 data sheet p.4 |

The documents, from their makers:

- Micsig, High Frequency AC/DC Current Probe CP Series, Product Introduction (2026-09-21):
  https://www.micsig.com/uploads/20260921/5d7efa5cfb82958be2e7e27a42c95102.pdf
- Micsig, High Frequency AC/DC Current Probe CP Series User Manual (2026-09-23):
  https://www.micsig.com/uploads/20260923/d685e575dd1b53d8ac812a53b59dbad6.pdf
- Mean Well, NGE18 series specification (2026-01-14): https://www.meanwell.com/Upload/PDF/NGE18/NGE18-SPEC.PDF
- Rigol, MHO900 Programming Guide:
  https://www.rigol.com/dam/global/downloads/brochures/en/program-guide/oscilloscopes/MHO900-ProgrammingGuide.pdf
- Rigol, DHO800/DHO900 Programming Guide:
  https://www.rigol.com/dam/global/downloads/brochures/en/program-guide/oscilloscopes/DHO800900_ProgrammingGuide_EN.pdf
- Rigol, MHO900 Series Data Sheet:
  https://www.rigol.com/dam/global/downloads/brochures/en/data-sheet/oscilloscopes/MHO900-DataSheet.pdf
- Rigol, DHO900 Data Sheet:
  https://www.rigol.com/dam/global/downloads/brochures/en/data-sheet/oscilloscopes/DHO900_DataSheet.pdf
- Rigol, DP2000 Data Sheet:
  https://www.rigol.com/dam/global/downloads/brochures/en/data-sheet/dc-powers/DP2000_DataSheet_en.pdf
- Rigol, DL3000 Series Data Sheet:
  https://www.rigol.com/dam/global/downloads/brochures/en/data-sheet/dc-load/DL3000_DataSheet_EN.pdf

## 1. A reading

`Reader.single` sends `:SINGle` and polls `:TRIGger:STATus?` until it reads `STOP`. In AUTO sweep the scope fires by
itself; in NORMal sweep (test 3) a single that does not stop within 2 s counts as a trigger that did not fire.
`Reader.record` reads the screen record:

    :WAVeform:SOURce CHANnel<n> | MATH1,  :WAVeform:MODE NORMal,  :WAVeform:FORMat WORD,  :WAVeform:PREamble?,
    :WAVeform:STARt 1,  :WAVeform:STOP <points>,  :WAVeform:DATA?

and turns each 16-bit code into the channel's own unit with the preamble:

    y = (code - yorigin - yreference) * yincrement

A reading (`Run.means`) is the mean of `--records` (5) such records, each the mean of its samples. The kit also keeps
the spread of the five means and the mean of their standard deviations (the noise). A record any of whose samples
sits 3.95 div or more from the offset's zero is clipped. Test 1 then takes the pair again at ten times the scale, at
most twice, and leaves a clipped point out of the factor.

## 2. Test 1: the channel in amperes

The channel is set to `:CHANnel<n>:UNITs AMPere` and to the probe's sensitivity S = turns x (1 V/A or 0.1 V/A), sent
as the guide says (`--ratio-convention guide`). Both settings are read back. A value not taken is a finding.

Each step `I` is a pair at one vertical setting, the scale being the 1-2-5 value at or above max(I, 3 x the smallest
scale) / 3, with the offset at 0:

    zero    = reading with the load's input off
    reading = reading with the load in CC at I, the reference read before and after (their mean is `ref`)
    corrected = reading - zero
    error     = corrected - ref
    pass      : |error| <= band(ref) and not clipped, and the supply in CV

The smallest scale is 5 mA/div on the 5 A range and 50 mA/div on the 30 A range (5 mV/div at the probe's output).

    band(I) = 1 % x I + 5 mA      (5 A range)
            = 1 % x I + 20 mA     (30 A range)

The band is the bench plan's: the probe's own accuracy with room for the reference and the scope. Beside it the JSON
gives the datasheets' worst case (`budget`): the probe (1 % x I + 1 mA or 10 mA, divided by the turns), the reference
(0.15 % x I + 5 mA) and the scope's gain (1 % of 8 divisions at the scale used). The zero removes the scope's offset
error (up to 0.1 div + 2 mV) and the probe's residual zero; `raw_error` is the error without it.

At the 1 A step `cursor_check` puts manual cursor A at the reading and B at the zero, with
`:CURSor:MANual:TYPE AMPLitude` on the channel. `:CURSor:MANual:AYValue?` must read the reading back within
1 % or 2 mA. Before the steps `:TRIGger:EDGE:LEVel 1` must read back 1 (in A) within 0.02.

### What a whole factor means (`nearest_factor`)

`factor` is the median of corrected / ref over the steps of 0.4 A or more. With the ratio sent as the guide's
sensitivity S:

| factor | meaning |
|---|---|
| 1 | the channel reads amperes |
| S^2 (0.01 on the 30 A range) | the firmware multiplies by the value, as for a voltage probe's X, instead of dividing by V/A |
| 10 (S = 0.1) or 0.1 (S = 1) | the probe is on its other range, or the channel stayed in volts |

On the 5 A range S = 1, so the two readings of the ratio cannot be told apart there; the 30 A range tells them
apart. When the factor is off by more than 5 % and S is not 1, `other_convention` sends the inverse value and
measures the top step once more. If that reads amperes, the finding `ratio-convention` says that this firmware takes
the ratio in amperes as an X.

`--turns N` passes the lead N times through the jaw: the probe sees N x I, the ratio sent is N x S, and the channel
should still read I. Three turns on the 5 A range is 3 V/A, which is not on the guide's list, so it tests a custom
ratio: taken or not, read back or not.

## 3. Test 2: power

CH2 is set to volts with a 10x ratio, and Math1 to `MULTiply` of CH1 and CH2. At each step one single is read three
ways (CH1, CH2, MATH1). The math mean is held to the supply's own `:MEASure:SCALar:POWEr:DC?` (the mean of a read
before and after) within 2 %. The JSON also gives mean(CH1) x mean(CH2), the same check done in the kit, since for a
DC current the product of the means is the mean of the product. Math1's scale is set to the 1-2-5 value at or above
P / 3 at each step.

## 4. Test 3: the step

The load runs its continuous transient in CC: level A 0.5 A, level B 1.5 A, 1 kHz, 50 % duty. The channel is at
0.5 A/div, 200 us/div, with an edge trigger on the channel, rising, at 1 A, in NORMal sweep. Each of 20 singles must
stop within 2 s. For each record (`step_shape`):

    lo, hi      = the medians of the samples below and at or above 1 A
    rise, fall  = the time from 10 % to 90 % of (hi - lo), and back, the median over the edges in the record
    at_trigger  = the sample at t = 0 (it should sit at the trigger level)

The medians over the records must hold `|lo - 0.5| <= band(0.5) + 0.1 % x 0.5 + 6 mA` and the same for hi at 1.5 A,
the load's own setting accuracy added. The records' mean must sit within band(ref) of the supply's readback. At 50 %
duty that readback is the step's mean.

## 5. Test 4: drift

Nothing in the jaws, after Zero. The channel is on the 5 A range at 5 mA/div. A reading every `--interval` seconds for
`--minutes`. The kit reports `drift` (last minus first), `span` (highest minus lowest) and the least-squares slope in
A per minute. Neither the probe's documents nor this kit give a pass mark.

## 6. Persistence

`persist-setup`: the channel in amperes with the ratio, both read back. Then `:SAVE:OVERlap ON` and
`:SAVE:SETup C:/cpkit.stp`. Then the unit is changed to volts and the ratio to 1, and the kit sends
`:LOAD:SETup C:/cpkit.stp` and reads both again. Both must come back.

`persist-arm` sets the same, writes every setting it found to `--state`, and puts nothing back. After the person
power-cycles the scope, `persist-check` reads the unit and the ratio and compares them with what was armed. Then it
puts back every setting persist-arm found.

## 7. The tests of the kit

`test_current_probe_kit.py` runs every test against `SimScope`, a Rigol-dialect scope on 127.0.0.1 whose firmware's
habits are parameters:

- it reads the ratio in amperes as V/A or as an X;
- it takes `AMPere` or refuses it;
- it takes a custom ratio or refuses it;
- it keeps the unit across a power cycle or loses it;
- it saves setup files or not.

For each case the tests check the verdict, the findings and the factor. They check that every setting is back to
what it was, that the scope was sent nothing on the never list, that every command sent is in the trace and that no
serial reaches the JSON.
