=================
Calibration Loads
=================
The HOT/COLD load system provides the absolute flux reference for the
heterodyne calibration. This page describes how raw load data is
processed into the gain calibration factor :math:`\gamma`, receiver
temperature :math:`T_{rec}`, and bad channel mask.
Load Extraction
---------------
Calibration subscans are identified by their ``sobsmode`` string:
- **HOT** — hot load (ambient temperature blackbody)
- **COLD** / **COL** — cold load (typically 77 K LN₂)
- **SKY** — sky measurement
- **ZERO** — zero-level check
For each calibration subscan, the count data ``[C, D]`` is averaged over
the dump axis (NaN-aware, skipping padded values) to produce mean
hot/cold counts per channel.
**Implementation:** ``cal-core/src/math/dump_mean.rs`` → ``nan_mean_axis()``
Effective Load Temperature
--------------------------
The hot load is at ambient temperature, but spillover past the load
couples to the ambient environment. The effective load temperature
corrects for this:
.. math::
T_{hot,eff}^{sig} = \frac{T_{RJ}(\nu_{sig}, T_{hot})
- f_{amb} \cdot T_{RJ}(\nu_{sig}, T_{amb})}{1 - f_{amb}}
where :math:`f_{amb} = 1 - f_{eff}` is the ambient coupling fraction
and :math:`f_{eff}` is the forward efficiency.
The sky coupling coefficient relates ambient to effective temperature:
.. math::
a_{sig} = \frac{T_{RJ}(\nu_{sig}, T_{amb})}{T_{hot,eff}^{sig}}
**Implementation:** ``cal-io/src/resolve.rs``
Gamma (Gain Calibration Factor)
-------------------------------
The gamma factor converts count differences to temperature differences:
.. math::
\gamma(\nu) = \frac{C_{hot}(\nu) - C_{cold}(\nu)}{T'_{hot}(\nu)
- T'_{cold}(\nu)} \cdot (g_s x_s + g_i x_i)
This is the sensitivity in counts per Kelvin, including the sideband
gain weighting.
**Implementation:** ``cal-core/src/math/gamma.rs`` →
``gain_calibration_formula()``
Receiver Temperature
--------------------
The Y-factor method yields the primed receiver temperature:
.. math::
y = \frac{C_{hot}}{C_{cold}}
.. math::
T'_{rec} = \frac{T'_{hot} - y \cdot T'_{cold}}{y - 1}
Converting to single-sideband:
.. math::
T_{rec,SSB} = \left(T'_{rec} - T'_{term}\right)
\cdot \frac{g_s x_s + g_i x_i}{g_s x_s}
where :math:`T'_{term}` is the sideband-weighted termination
temperature (spillover).
**Implementation:** ``cal-core/src/math/temperature.rs`` →
``t_rec_formula()``
Bad Channel Detection
---------------------
A channel is flagged as bad if any of the following conditions hold:
1. :math:`C_{hot} \leq C_{cold}` — load signal not detected
2. :math:`(C_{hot} - C_{cold}) < \text{clip\_counts} \cdot \max(\text{smoothed}(C_{hot} - C_{cold}))` — weak relative signal
3. :math:`T_{rec,SSB} \leq 0` — unphysical receiver temperature
4. :math:`T_{rec,SSB} > \text{clip\_tsys} \cdot \frac{h \nu_{typ}}{k_B}` — unrealistically high
The ``clip_tsys`` (default 200 K) and ``clip_counts`` (default 0.01)
thresholds are configurable.
**Implementation:** ``cal-core/src/math/temperature.rs`` →
``compute_bad_channels()``
Per-Channel Load Temperature Tables
-------------------------------------
Some receivers provide per-channel RJ load temperatures via the
``LOAD_TEMP_ARRAY`` (pre-computed from load emission models). When
available, these replace the scalar :math:`T_{RJ}(T_{hot})` with
per-channel values, improving accuracy near band edges where the load
is not perfectly isothermal.
**Implementation:** ``cal-core/src/scan.rs`` →
``CalibrationLoad::new_with_load_temps()``
Data Flow
---------
.. mermaid::
graph LR
CS[CalibrationSnapshot
raw HOT/COLD] -->|extract & average| HC[hot_counts, cold_counts
per channel]
HC --> G[gamma]
HC --> TR[T_rec_prime, T_rec_SSB]
HC --> BC[bad_channels mask]
G --> CL[CalibrationLoad
all derived quantities]
TR --> CL
BC --> CL