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
77Set Massf323Precursor m/z of an MS/MS scan; 0 on MS1, MSe and lock-mass scans
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.

Set Mass (seq 77)​

In DDA bundles the _extern.inf Set Mass of each MS/MS function is 0 or a fixed method default, while this channel holds each scan's precursor m/z. Every MS/MS scan of every DDA function in the corpus has a non-zero value; every MS1, MSe and lock-mass scan has 0.

Cross-check against _auto.txt, a plain-text m/z,time (s) log of the precursors chosen during acquisition, present in 6 corpus bundles. Each run of consecutive MS/MS scans with one Set Mass value starting within 30 s after a log entry was matched to it:

Bundle_auto.txt entriesMS/MS runsMatched
PXD041695/JLG-1D-ISY-C18_Q0764.raw410410410 within 0.5 mDa
PXD041695/JLG-1D-ISY-C8_Q0753.raw412412412 within 0.5 mDa
PXD010569/20161025_AV_Dario_Banda_10.raw274274274 within 0.5 mDa
PXD010569/20161025_AV_Dario_Banda_3.raw273273273 within 0.5 mDa
PXD001123/SiRNA_21-2.raw882865865 within 0.5 mDa
PXD029515/blast_young_0h_H1__MSMS.raw121712171217 within 100 ppm (median +18 ppm)

In PXD001123, 17 logged precursors have no MS/MS scan. In PXD029515 the log and the channel differ by a constant offset of about 18 ppm; the channel value is the one reported. PXD003126 and PXD073126 (mobility DDA) have no _auto.txt, but their MS/MS scans carry the channel the same way. For the targeted MS/MS sample PXD035818 the channel reads 884.92 on every scan, where _extern.inf gives Set Mass 884.9.

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, and FuncSts::set_mass(scan_idx) reads the "Set Mass" channel (seq 77), with a stored 0 returned as None. Reader::precursor_mz uses it as the precursor m/z of each scan, falling back to the _extern.inf Set Mass (also None when 0), and the mzML records write that value as both the isolation window target m/z and the selected ion m/z. 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)