# 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.