Skip to main content

_FUNCnnn.STS

Binary scan statistics file. One file per function (e.g. _FUNC001.STS, _FUNC002.STS). Records per-scan instrument housekeeping values: electrode voltages, collision energies, push counts, lock-mass corrections, and TIC traces.

Status: Fully Decoded

File Layout

[32-byte file preamble]
[n_desc × 48-byte descriptor records] (starting at offset 0x20)
[n_scans × scan_record_size bytes of data]

Total header size = 32 + n_desc × 48. This equals the data_offset stored at preamble byte 0.

File Preamble (32 bytes)

OffsetTypeDescription
0x00u16data_offset - byte offset where per-scan data begins
0x02u16Format version (always 1 observed)
0x04u16scan_record_size - bytes per scan in the data section
0x06u16n_desc - number of descriptor records
0x08-0x1F-Zeroes (padding to 32 bytes)

Equations:

  • data_offset = 32 + n_desc * 48
  • n_scans = (file_size - data_offset) / scan_record_size

Validated:

  • PXD058812 P15: data_offset=0x4D0=1232, scan_sz=63, n_desc=25, n_scans=197
  • PXD075602 DHPR: data_offset=0x0AA0=2720, scan_sz=167, n_desc=56, n_scans=1150

Descriptor Records (48 bytes each)

Each descriptor defines one channel stored in the per-scan data section.

Offset within recordTypeDescription
0x00u16Channel sequence number (not always contiguous; some skip)
0x02u16Encoding type (see table below)
0x04u16Byte offset of this channel within each scan record
0x06-0x2FbytesNull-padded ASCII channel name (42 bytes)

Encoding Types

Type codeC typeSizeNotes
0u81 byteBoolean flag / small integer
1i162 bytesSigned 16-bit integer
2u324 bytesUnsigned 32-bit integer
3f324 bytesIEEE 754 single-precision float

The byte offset in the descriptor (0x04) is used directly as an index into the per-scan record. Channels are packed without explicit gaps; the offset of channel k+1 equals offset k + size(type k).

Per-Scan Data

The data section starts at data_offset and contains n_scans consecutive records, each scan_record_size bytes. To read channel X for scan S:

channel_value = scan_data[S * scan_record_size + descriptor[X].offset]

decoded using the descriptor's encoding type.

Known Channels

Observed across all four corpus datasets. Channels available depend on instrument generation; newer firmware adds channels.

SeqNameTypeCodeNotes
13Segment Numberi16varies0 for normal scans
52Conei16variesCone voltage (V); matches _extern.inf Sampling Cone
62Collision Energyf32variesPer-scan collision energy (eV)
76/78Pusher Frequency / Collision Energy2variesvariesFunction-dependent
100Reference Scanu8varies1 if this scan is a lock-mass reference
101Use lock mass correctionu8variesBoolean
102Lock mass correctionf32variesMass correction applied (Da)
103Use temp correctionu8variesBoolean
104Temperature correctionf32variesTemperature-derived TOF correction (µs)
107TIC Trace Af32variesTotal Ion Current (a.u.) for function A
108TIC Trace Bf32variesTotal Ion Current (a.u.) for function B
116Scan Push Countu32variesNumber of ADC pushes summed in this scan
117Scanwave Normalisation Factorf32variesIMS normalisation factor
121ETD Fragmentation Modei16varies0 = CID; non-zero = ETD

Not all channels are present in every file. Parse by looking up the descriptor list rather than assuming fixed offsets.

Observed Values

Datasetn_descscan_szConeCollision EnergyPush Count
PXD058812 P15 (non-IMS QTOF)256330 V10.0 eV-
PXD075602 DHPR (Xevo G2-XS)5616730 V4.0 eV8299

Push Count = 8299 in DHPR: 8299 × 60.25 µs ≈ 0.50 s per scan, consistent with the 0.50 s scan time recorded in _FUNCTNS.INF.

Relationship to IDX Fields

The IDX +0x08 field (30-byte Variant B, value labelled "Large value / TIC?") likely corresponds to one of the TIC Trace channels here. This cross-check has not yet been confirmed empirically.

Rust Implementation

crate::raw::func_sts::FuncSts parses this file and exposes per-scan channel lookups by name; FuncSts::collision_energy(scan_idx) reads the "Collision Energy" channel specifically, and FuncSts::etd_fragmentation_mode(scan_idx) reads the "ETD Fragmentation Mode" channel (seq 121) specifically. Reader::open parses every function's _FUNCnnn.STS when present (silently None if absent or malformed - this file is supplementary, not required to decode peak data), and mzml::precursor_info_for uses it to populate PrecursorInfo::collision_energy for every MS2 function, including MSe/HDMSe (which has no discrete precursor target_mz but still reports a real per-scan collision energy) and targeted MS/MS (Sigilweaver/OpenWRaw#8, #13), and to populate PrecursorInfo::activation from the ETD Fragmentation Mode channel: 0 maps to Activation::CID, non-zero to Activation::ETD (Sigilweaver/OpenWRaw#22). Every ETD Fragmentation Mode value seen in the repository corpus is 0; the non-zero branch follows directly from this table's documented encoding but has not been exercised against a real non-zero fixture, only synthetically (mzml::tests::activation_for_maps_zero_to_cid_and_nonzero_to_etd).

Channels 101 and 102 remain available through the generic channel / value_at API but are deliberately not applied downstream. Every sample in the repository corpus has Use lock mass correction = 0 and Lock mass correction = 0.0. Consequently the corpus cannot establish how a non-zero lock-mass value modifies the calibrated TOF-to-m/z conversion. Applying an interpretation would require a fixture with a non-zero value and an independently checkable in-repository invariant (Sigilweaver/OpenWRaw#23).

Reference Sources

  • Empirical analysis: all _FUNC001.STS files in corpus
  • Corpus samples:
    • PXD058812/molecular_mass_P15_01.raw (25 channels, 63 bytes/scan)
    • PXD075602/DHPR_11257-1.raw (56 channels, 167 bytes/scan)
    • PXD035818/17122018_TNFA_PEPTIDE_GSHH_MSMS_884.raw (targeted MS/MS, confirms the same descriptor/channel layout on a non-MSe function)