## Record and calculate paired low-resistance readings

Use `blank-readings.csv` to retain actual readings and their measurement context. Replace `FILL_REAL_ID` with the sample identifier. The three rows in `demo-readings.csv` are constructed examples, not MTTJ measurements or acceptance samples. `kelvin_pair_calc.py` calculates a resistance and offset from each pair of signed current and voltage readings.

The Python script runs offline with the standard library and requires no additional packages. It does not control an instrument, validate dry-circuit settings or decide whether a product passes. Confirm the measurement assumptions and use the applicable product specification separately, as described in the article.

## The 16 input columns

Keep the column names unchanged. The script reads the four signed-reading columns for its calculation and retains the other input columns as context. It does not check the values of those contextual fields.

| Column | Meaning and unit |
| --- | --- |
| `sample_id` | Identifier for the sample represented by the row |
| `sample_state` | Sample condition at the time of measurement |
| `spec_revision` | Product specification revision used for the measurement |
| `sense_A_B_drawing_ref` | Reference to the diagram locating the A/B sensing points |
| `fixture_id` | Identifier for the fixture used |
| `method` | Recorded measurement method; the supplied templates use `four_wire_current_pair` |
| `range` | Instrument measurement range |
| `source_limit_mV` | Recorded source voltage limit, in mV |
| `sample_history` | Sample history, including preceding mechanical, environmental or electrical tests |
| `i_plus_mA` | Signed measured current for the positive-current reading, in mA; must be greater than 0 for calculation |
| `i_minus_mA` | Signed measured current for the negative-current reading, in mA; must be less than 0 for calculation |
| `v_plus_uV` | Signed sensed voltage paired with `i_plus_mA`, in μV |
| `v_minus_uV` | Signed sensed voltage paired with `i_minus_mA`, in μV |
| `sampled_plus_at` | Sampling time for the positive-current reading; retained as recorded text |
| `sampled_minus_at` | Sampling time for the negative-current reading; retained as recorded text |
| `note` | Notes retained with the original row |

Retain the signs of both currents and voltages. A voltage value of zero is a numeric reading; a blank is a missing reading and must remain blank. If any of the four signed-reading fields is absent, empty or contains only whitespace, the script does not substitute zero. Empty contextual fields remain empty.

## Calculation and four output columns

For each complete, valid pair, the script uses:

`R = (V+ − V−) / (I+ − I−)`

`E = V+ − I+ R`

The second formula is the script's calculation of the offset from `V = IR + E`. Inputs in μV and mA give `R` in mΩ and `E` in μV. These calculations assume linear resistance and a stable offset during the pair; producing a result does not check those assumptions. Numerical outputs are formatted with up to 12 significant digits.

| Output column | Meaning |
| --- | --- |
| `r_pair_mohm` | Calculated paired-reading resistance in mΩ; blank for a missing or invalid pair |
| `offset_pair_uV` | Calculated paired-reading offset in μV; blank for a missing or invalid pair |
| `calculation_status` | The script's calculation status: `calculated`, `missing` or `invalid` |
| `calculation_note` | The script's explanation of the status and calculation assumptions |

| Status token | When the script writes it |
| --- | --- |
| `calculated` | All four inputs are numeric and finite, the positive current is greater than 0, the negative current is less than 0, and both calculated outputs are finite. The note is `Assumes a linear resistor and stable offset. No acceptance judgment.` |
| `missing` | At least one of the four signed-reading fields is absent or blank. Both numerical outputs remain blank. The note is `Missing readings; no zero imputation.` |
| `invalid` | An input is not numeric or finite, the current signs do not meet the required conditions, or a calculated output is not finite. Both numerical outputs remain blank. The note identifies the reason. |

For `invalid`, the script's notes are `Readings must be numeric.`, `Require finite readings and Iplus>0, Iminus<0.`, or `Non-finite calculated output.` The status tokens and notes describe calculation handling; they are not product acceptance results.

## Read the constructed examples

| Demonstration row | I+ / I− (mA) | V+ / V− (μV) | `r_pair_mohm` (mΩ) | `offset_pair_uV` (μV) | `calculation_status` |
| --- | --- | --- | ---: | ---: | --- |
| `DEMO_STABLE` | +10 / −10 | +110 / −90 | 10 | 10 | `calculated` |
| `DEMO_DRIFT` | +10 / −10 | +110 / −86 | 9.8 | 12 | `calculated` |
| `DEMO_UNEQUAL` | +12 / −8 | +130 / −70 | 10 | 10 | `calculated` |

The drift example changes the assumed offset from 10 to 14 μV between the two readings. Its paired calculation returns 9.8 mΩ and an offset of 12 μV. The `calculated` status does not detect or remove that drift. The unequal-current example uses the actual signed values +12 and −8 mA and returns 10 mΩ. With the signed readings left blank in the blank template, the two numerical output fields stay blank and the status is `missing`.

## Run the offline calculation and preserve the input

Keep the script and the chosen CSV available locally, then run `python kelvin_pair_calc.py demo-readings.csv result.csv`. For actual readings, use the filled blank template as the input and choose a separate output file. The script rejects input and output paths that resolve to the same path so that the raw input is retained.

The output retains the input columns and appends the four calculation columns. If the input already contains columns with those four output names, the script replaces those columns with the new calculation outputs. It writes the result as UTF-8 CSV with a byte-order mark. The calculation continues to preserve missing readings as blanks and does not issue a pass/fail decision.
