Adding an Observation Mode#
This guide walks through adding a new observation mode (e.g., FrequencySwitch) to the calibration engine.
Architecture Note#
There is no CalibrationMode trait or per-mode calibration
function. All modes share a single calibration path
(calibrate_full). Mode-specific logic lives exclusively in the
preparation phase (cal-core/src/modes/prepare.rs), which
produces a PreparedData struct consumed uniformly by the
mode-agnostic calibration math.
Step 1: Add ObsMode Variant#
In cal-core/src/config.rs, add the variant to the ObsMode enum:
pub enum ObsMode {
Auto,
TotalPower,
OtfTotalPower,
OtfChopped,
FrequencySwitch, // <- new
BeamSwitch,
}
Step 2: Implement Preparation#
Add a preparation function in cal-core/src/modes/prepare.rs:
fn prepare_frequency_switch(
scan: &ScanData,
) -> Result<PreparedData, CalibrationError> {
// 1. Identify ON subscan indices from sobsmode
// 2. Build reference from frequency-shifted spectra
// 3. Return PreparedData { ref_data, on_subscan_indices, ... }
todo!()
}
The key contract: PreparedData must provide:
ref_data[C, R, A, S_on]– reference spectra for each ONon_subscan_indices– which entries inScanData.dataare ONoff_index_per_on– OFF subscan index for per-subscan atmospheret_int[S_on]– integration time per ON subscan
Step 3: Wire into the Dispatcher#
In cal-core/src/modes/prepare.rs -> prepare(), add a match arm
for the new mode:
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)?,
ObsMode::FrequencySwitch => prepare_frequency_switch(scan)?, // <- new
_ => return Err(CalibrationError::InvalidMode { ... }),
};
Ok((prepared, mode))
}
The calibration math (calibrate_full) is mode-agnostic – it
operates on the PreparedData output uniformly. No changes needed
in calibrate.rs.
Step 4: Add Detection Logic#
In ScanData::detect_obs_mode() (cal-core/src/scan/scan_data.rs),
add pattern matching for the new mode’s sobsmode strings or
instmode attribute.
Step 5: Add CLI/Python Parsing#
In cal-cli/src/main.rs -> parse_obs_mode():
"freqswitch" | "fs" => Ok(ObsMode::FrequencySwitch),
In cal-py/src/lib.rs -> parse_config():
"frequency_switch" | "freq_switch" | "fs" => ObsMode::FrequencySwitch,
Step 6: Add Parity Test#
Obtain reference output from the legacy kalibrate for a test dataset
Place the reference CSV in
kalibrate_test_lib/reference_data/Add a test case to the parity suite
Step 7: Update Documentation#
Add the mode to /source/physics/observation-modes
Update the
ObsModetable in /source/reference/configuration
Checklist#
[ ] ObsMode variant added to config.rs
[ ] Preparation function added to modes/prepare.rs
[ ] Dispatcher match arm in prepare()
[ ] Mode detection logic in scan_data.rs
[ ] CLI parsing added (main.rs)
[ ] Python parsing added (cal-py)
[ ] Parity test with reference data
[ ] Documentation updated