## Zapis i obliczanie par odczytów małej rezystancji

W `blank-readings.csv` zachowaj realne odczyty i kontekst; zastąp `FILL_REAL_ID` identyfikatorem. Trzy wiersze `demo-readings.csv` są wymyślone, nie są pomiarami MTTJ ani próbkami odbiorczymi. `kelvin_pair_calc.py` oblicza rezystancję i offset każdej pary prądów i napięć ze znakiem.

Python działa offline, tylko z biblioteką standardową, bez dodatkowych pakietów. Nie steruje przyrządem, nie sprawdza suchego obwodu i nie decyduje o odbiorze. Potwierdź założenia i specyfikację oddzielnie, zgodnie z artykułem.

## 16 kolumn wejściowych

Zachowaj nazwy. Skrypt liczy z czterech kolumn odczytu; pozostałe zachowuje jako kontekst, bez sprawdzania wartości.

| Kolumna | Znaczenie i jednostka |
| --- | --- |
| `sample_id` | Identyfikator próbki wiersza |
| `sample_state` | Stan podczas pomiaru |
| `spec_revision` | Rewizja użytej specyfikacji |
| `sense_A_B_drawing_ref` | Odnośnik do schematu Sense A/B |
| `fixture_id` | Identyfikator przyrządu badawczego |
| `method` | Zapisana metoda; szablony używają `four_wire_current_pair` |
| `range` | Zakres przyrządu |
| `source_limit_mV` | Zapisany limit napięcia źródła w mV |
| `sample_history` | Historia, w tym poprzednie badania mechaniczne, środowiskowe i elektryczne |
| `i_plus_mA` | Zmierzony dodatni prąd ze znakiem w mA; większy od 0 do obliczeń |
| `i_minus_mA` | Zmierzony ujemny prąd ze znakiem w mA; mniejszy od 0 do obliczeń |
| `v_plus_uV` | Napięcie ze znakiem sparowane z `i_plus_mA`, w μV |
| `v_minus_uV` | Napięcie ze znakiem sparowane z `i_minus_mA`, w μV |
| `sampled_plus_at` | Czas dodatniego odczytu, zachowany jako tekst |
| `sampled_minus_at` | Czas ujemnego odczytu, zachowany jako tekst |
| `note` | Uwagi zachowane z oryginalnym wierszem |

Zachowaj wszystkie znaki. Zero napięcia jest liczbą; pusty odczyt jest brakiem i pozostaje pusty. Nieobecność pola, pustka lub same białe znaki w którymkolwiek z czterech pól nie są zastępowane zerem. Pusty kontekst też pozostaje pusty.

## Obliczenia i cztery wyjścia

Każda pełna poprawna para używa:

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

`E = V+ − I+ R`

Drugi wzór wyznacza offset z `V = IR + E`. μV i mA dają `R` w mΩ oraz `E` w μV. Zakłada liniowość i stabilny offset w parze; wynik nie sprawdza założeń. Wyjścia mają do 12 cyfr znaczących.

| Wyjście | Znaczenie |
| --- | --- |
| `r_pair_mohm` | Obliczona rezystancja w mΩ; pusta dla brakującej lub błędnej pary |
| `offset_pair_uV` | Obliczony offset w μV; pusty dla brakującej lub błędnej pary |
| `calculation_status` | Status `calculated`, `missing` albo `invalid` |
| `calculation_note` | Wyjaśnienie statusu i założeń |

| Status | Kiedy zapisany |
| --- | --- |
| `calculated` | Cztery skończone liczby, dodatni prąd większy od 0, ujemny mniejszy od 0 i oba wyniki skończone. Oryginał `Assumes a linear resistor and stable offset. No acceptance judgment.` zakłada liniowość i stabilność, bez decyzji odbiorczej. |
| `missing` | Odczyt nieobecny lub pusty; oba wyniki liczbowe puste. `Missing readings; no zero imputation.` oznacza brak bez podstawiania zera. |
| `invalid` | Dane nie są liczbą lub są nieskończone/NaN, niewłaściwe znaki prądów albo nieskończony/NaN wynik. Wyjścia puste, uwaga podaje przyczynę. |

Oryginalne uwagi: `Readings must be numeric.`, `Require finite readings and Iplus>0, Iminus<0.` lub `Non-finite calculated output.` Wymagają liczb, skończoności i podanych znaków albo zgłaszają wynik nieskończony/NaN. Statusy dotyczą obliczeń, nie odbioru.

## Odczytaj wymyślone przykłady

| Wiersz | 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` |

Dryft zmienia założony offset z 10 na 14 μV i daje 9.8 mΩ oraz 12 μV. `calculated` nie wykrywa ani nie usuwa dryftu. Rzeczywiste nierówne prądy +12 i −8 mA dają 10 mΩ. Puste pola szablonu dają puste dwa wyniki i `missing`.

## Uruchom offline i zachowaj wejście

Z lokalnymi plikami uruchom `python kelvin_pair_calc.py demo-readings.csv result.csv`. Dla realnych danych użyj wypełnionego szablonu i osobnego wyjścia. Skrypt odrzuca ścieżki wejścia i wyjścia rozwiązujące się do tej samej ścieżki, chroniąc oryginał.

Zachowuje kolumny wejściowe i dodaje cztery obliczeniowe. Jeśli już istnieją pod tymi nazwami, zastępuje je nowymi wynikami. Zapisuje UTF-8 CSV ze znacznikiem kolejności bajtów. Braki pozostają puste, bez decyzji zaliczone/niezaliczone.
