# Observation Modes Different observing strategies require different reference selection and data preparation. This page describes the supported modes and how to add new ones. ## Mode Detection The engine auto-detects the observation mode from the data via `prepare()` in `cal-core/src/modes/prepare.rs`: 1. Check the `instmode` attribute and `sobsmode` strings 2. Detect OTF-chopped (dual beam switch) from SWA/SWB patterns 3. Detect OTF from OTF-ON/OTF-OFF patterns 4. Fall back to `TotalPower` if ambiguous This can be overridden with `--mode otf`, `--mode tp`, or `--mode otf-chopped` on the command line. **Implementation:** `cal-core/src/config.rs` -- `ObsMode` enum, `cal-core/src/modes/prepare.rs` -- `prepare()` dispatcher ## Implemented Modes ### TotalPower (TP) Single-point position-switched observations with discrete ON/OFF pairs. ```{eval-rst} .. mermaid:: graph LR OFF1[OFF] --> ON1[ON] ON1 --> OFF2[OFF] OFF2 --> ON2[ON] ON2 --> OFF3[OFF] style OFF1 fill:#bbdefb style OFF2 fill:#bbdefb style OFF3 fill:#bbdefb style ON1 fill:#c8e6c9 style ON2 fill:#c8e6c9 ``` - **ON subscans:** single dump per pointing - **Reference:** linear interpolation between bracketing OFFs (by MJD) - **Matching:** each ON is paired with its temporally bracketing OFFs ### OTF TotalPower On-the-fly scanning where multiple dumps trace a path across the source. ```{eval-rst} .. mermaid:: graph LR OFF1[OFF] --> OTF1[OTF-ON
D dumps] OTF1 --> OFF2[OFF] OFF2 --> OTF2[OTF-ON
D dumps] OTF2 --> OFF3[OFF] style OFF1 fill:#bbdefb style OFF2 fill:#bbdefb style OFF3 fill:#bbdefb style OTF1 fill:#c8e6c9 style OTF2 fill:#c8e6c9 ``` - **ON subscans:** multiple dumps along scan direction (D > 1) - **Reference:** linear interpolation between bracketing OFFs, averaged over all dumps (NaN-aware) - **OTF coordinates:** `otf_lon` and `otf_lat` are propagated to the output as `[D, S]` arrays ### OTF Chopped (Dual Beam Switch) On-the-fly scanning with beam switching (OTFSWA/OTFSWB patterns). Each ON subscan is paired with its same-beam OFF. Beams A and B are calibrated independently. - **ON subscans:** SWA-ON and SWB-ON dumps - **Reference:** per-dump OFF subtraction (matching kalibrate's per-dump subtraction for chopped data) - **DBS coupling:** optional adhesive coupling (`--dbs-coupling`) enables cross-beam OFF averaging and post-calibration A+B merge **Implementation:** `cal-core/src/modes/prepare.rs` -- `prepare_otf_chopped()`, `cal-io/src/pipeline/dbs.rs` -- `merge_dbs_pairs()` ## Data Preparation **File:** `cal-core/src/modes/prepare.rs` A single `prepare()` function dispatches to mode-specific preparation based on auto-detected `ObsMode`: ```rust pub fn prepare(scan: &ScanData) -> Result<(PreparedData, ObsMode), CalibrationError> { let mode = scan.detect_obs_mode(); let prepared = match mode { ObsMode::OtfTotalPower => prepare_otf(scan)?, ObsMode::OtfChopped => prepare_otf_chopped(scan)?, ObsMode::TotalPower => prepare_tp(scan)?, // ... }; Ok((prepared, mode)) } ``` The preparation phase decouples reference selection from calibration math, enabling vectorized processing: 1. Identify ON subscan indices from `sobsmode` 2. For each ON, find bracketing OFFs and interpolate linearly by MJD 3. Deduplicate OFF references (compute average once, reuse for all ONs sharing the same reference) 4. Build `ref_data[C, R, A, S_on]` from interpolated OFF averages 5. Compute integration time `t_int[S_on]` The ON data is **not copied** -- `PreparedData` stores indices into `ScanData.data`, and the calibration loop reads from there directly. This avoids a ~1 GB allocation for large OTF datasets. The calibration math (`calibrate_full`) is mode-agnostic -- it operates on the `PreparedData` output uniformly. ## Stubbed / Future Modes The `ObsMode` enum includes placeholders for: - **BeamSwitch** -- standalone (non-OTF) beam-switched reference - **FrequencySwitch** -- reference from frequency-shifted spectra These will be implemented when test data becomes available. See {doc}`/source/developer/adding-observation-mode` for the extension guide.