Calibration
OpenTimsTDF converts TOF indices to m/z with the calibration stored in the
bundle's MzCalibration table, and scan indices to 1/K0 with a linear
model from the acquisition mobility range.
API
let calib = reader.calibration()?;
let mz = calib.tof_to_mz(tof_index);
let one_over_k0 = calib.scan_to_inv_mobility(scan_index);
// Which m/z model is in use, and why if it is the fallback.
let status = reader.mz_calibration_status()?;
println!("{} {:?}", status.model, status.fallback_reason);
calibration() returns the calibration of the MzCalibration row that
most frames reference. Frames of different polarity reference different
rows; to convert any frame correctly, use the per-frame lookup:
let cal = reader.bundle_calibration()?;
for frame in reader.frames()? {
let c = cal.for_frame(&frame);
for p in reader.decode_peaks(&frame)? {
let mz = c.tof_to_mz(p.tof);
}
}
The mzML writer and the Python decode_spectrum already do this.
Model types
A Calibration holds an MzConversion (TOF to m/z) and a
MobilityConversion (scan to 1/K0), available through
mz_conversion() and mobility_conversion():
use opentimstdf::{MobilityConversion, MzConversion};
match calib.mz_conversion() {
MzConversion::Tables(m) => println!("C0={} C1={}", m.c0, m.c1),
MzConversion::RangeFallback { intercept, slope, .. } => {
println!("sqrt(mz) = {intercept} + {slope} * tof")
}
_ => println!("other m/z model"),
}
if let MobilityConversion::AcquisitionRange { intercept, slope, .. } =
calib.mobility_conversion()
{
println!("1/K0 = {intercept} + {slope} * scan");
}
These enums, MzCalibrationModel, MzCalibrationStatus and
TablesMzModel are #[non_exhaustive]: new models and coefficients can be
added in a minor release, so matches need a _ arm. Build values with
Calibration::new, TablesMzModel::new, MzConversion::range_fallback
and MobilityConversion::acquisition_range.
What is read
- m/z (tables model, the default):
MzCalibration.DigitizerDelay,DigitizerTimebase,C0,C1,C2,C4of each row referenced byFrames.MzCalibration. - m/z (range fallback):
GlobalMetadata.MzAcqRangeLower/UpperandDigitizerNumSamples. Used only when theMzCalibrationtable is absent or unusable (for exampleModelTypeother than 1). The reason is returned bymz_calibration_status(), written to run metadata asopentimstdf.mz_calibration_fallback_reason, and logged as a warning. - 1/K0:
GlobalMetadata.OneOverK0AcqRangeLower/Upperand the largestFrames.NumScans.
Run metadata extra always carries opentimstdf.mz_calibration with
tables or range_fallback.
Accuracy
On the public bundles tested, the tables model's median error against the precursor m/z values stored in the bundle is 0.8 to 0.9 ppm (up to 3.2 ppm on one test fixture). The range fallback ranges from 2 ppm to about 200 ppm depending on the bundle. No temperature correction is applied, and an instrument that drifted after calibration reads high or low by that drift (one tested bundle reads 9 to 18 ppm high). For accurate-mass work below about 5 ppm, recalibrate against known masses.
For the model, the validation method, and per-bundle figures, see:
- 04-calibration.md
- 01-tdf-sqlite-schema.md (
MzCalibration,TimsCalibration)