# Load-cell measurement chain

An executable calibration and signal-processing study for a 10 kg reference load cell. The package includes generated calibration data, noisy load traces, causal filters, a correlated calibration-parameter uncertainty calculation, headroom checks and tests. **All data are synthetic.** There is no physical calibration certificate or working ADC board.

## Run

Python 3.11 or later:

```sh
python -m venv .venv
# Linux/macOS: source .venv/bin/activate
# Windows: .venv\Scripts\activate
python -m pip install -r requirements.txt
python run.py
python -m unittest -v
```

Open `report.html` directly. `run.py` regenerates all CSV/JSON results, charts, test evidence and the offline report. It stops if tests fail. `verification.json` records test names and source hashes.

## Data and calculation

`inputs.json` contains each assumed reference value and the random seed. `data/synthetic_calibration.csv` has six mass levels × 30 generated readings. The least-squares fit models voltage as offset + slope × mass. Residual variance and the full 2 × 2 coefficient covariance matrix are saved in `results/calibration.json`.

The 60 s load trace samples a 5 kg step at 100 Hz, with 2 µV Gaussian input noise, a 0.02 g/s positive drift stress, ADC rounding and a bounded sinusoidal nonlinearity. It is saved before filtering. The repeatability comparison uses a separate 100,000-sample seeded white-noise trace. The noise-free version of the step supplies settling times; the first sample after the last excursion outside ±5 g counts as settled. Raw data, moving averages of 10/25/50 samples and EMA filters of 0.1/0.3 s all use the same inputs. No future samples enter a filter.

`results/uncertainty_budget.csv` is a spreadsheet-readable uncertainty budget at 5 kg. It propagates fitted slope/intercept covariance together, then combines it with independent assumed contributors. Rectangular half-widths are divided by √3; ADC rounding uses the interval/√12. A filtered reading's standard deviation is used directly, not incorrectly divided by the number of correlated output samples. The baseline k = 2 result is 3.560 g and assumes independent temperature-zero and temperature-span terms. The separate shared-temperature study reports 3.826 g at k = 2 and 5 kg; it uses a separate generated calibration and analytical filter noise. Both are illustrative model results, not calibrated coverage guarantees.

`results/headroom.csv` distinguishes ideal ADC code resolution from uncertainty. Differential headroom does not verify the ADC common-mode range or noise performance. Bridge mounting, analog filtering, shielding, reference tracking and actual input circuitry still require engineering on selected hardware.

The optional `ZeroTracker` requires an external empty-pan confirmation plus small stable measurements. It is disabled in the reported trace. A mistaken empty confirmation can still absorb a small real load; signal processing cannot resolve that ambiguity. Tests explicitly reject tracking an unconfirmed sustained load and confirmed large/unstable loads.

Method reference: [NIST TN 1297, Appendix A](https://www.nist.gov/pml/nist-technical-note-1297/nist-tn-1297-appendix-law-propagation-uncertainty). Numerical uncertainty bounds in this package are assumptions, not NIST values. `physical_calibration.md` lists hardware work that has **not** been executed.

## Completed virtual qualification

`python run.py` also runs 24 load/temperature/direction corners, biased-calibration rejection, 50,000 correlated uncertainty realizations, a 93 Hz interference/100 Hz sampling example, and false empty-pan confirmation. Open `virtual-validation.html` for assumptions, charts and acceptance results. The +/-5 g screening criterion is explicit; adverse thermal corners remain visible.

A 2 g group-mean residual gate rejects the injected 10 g and 50 g calibration-group errors. Shared zero/span temperature drift increases the calculated variance. A hypothetical pair of 10 Hz analog poles attenuates interference before it aliases to 7 Hz. Incorrect empty-pan confirmation absorbs a real 2 g load, demonstrating the limit of automatic zero tracking.

`completion.json` records twelve executed acceptance checks and source hashes. Four new regression tests bring the total to 18. This is the completed synthetic substitute for an instrument qualification run; it is not a calibration certificate.

The selected synthetic envelope limits temperature change since calibration to +/-2 C and time since zero to 30 s, with the filter settled for 0.25 s. Sixteen independently seeded corners have a maximum empirical 95th absolute error of 4.702 g, below the same 5 g screen. `selected-operating-envelope.json` and `results/virtual-selected-envelope.csv` record this accepted envelope; the wider failing cases remain separate.

The full 0-10 kg interval is also checked analytically: exact stationary points and endpoints of the declared error curve are evaluated, then 1.96 filtered-noise standard deviations are added. This bound applies to the generated calibration and stated sinusoidal error law, not to any arbitrary sensor that passes the residual gate.
