Skip to main content

_FUNCnnn.IDX

Binary scan index file. One file per function (e.g. _FUNC001.IDX, _FUNC002.IDX). Provides random access into the paired .DAT file.

Status: Mostly Decoded (Phase 4)​

Core structure and all critical access fields are confirmed. A few instrument-state fields remain without a confirmed interpretation.

Variant A: 22-byte record (non-IMS / simple TOF-MS)​

Observed in: PXD058812 (Waters QTOF), PXD041695 (QTof), and PXD081045 (Vion UNIFI export), and MTBLS701/MTBLS13770 (LCT Premier). Vion uses 8-byte DAT records; the LCT mass functions use 12-byte records.

Key facts:

  • File size = N x 22 bytes (exact, no header)
  • u32@0x00 = DAT byte offset - confirmed
  • Scan 0-2 are often zero-data "blank" scans (12 bytes each in DAT)
OffsetTypeConfirmedDescription
0x00u32YesByte offset into .DAT file
0x04u32YesLower 24 bits = DAT record count; high byte = format marker (0x18, or 0x4c/0x08 in the public LCT mass/lock functions)
0x08f32PartialNon-zero for data scans, 0 for blank scans. Correlates with scan signal level but no confirmed formula.
0x0Cf32YesRetention time (minutes)
0x10u16YesCentroid peak count (0 for blank scans, 17-196 per data scan in corpus)
0x12u16NoHardware register; varies over run (62496→34848 in corpus); purpose unknown
0x14u16NoHardware register; slow drift (~38446-38468 in corpus); purpose unknown

Validated: 22 x 197 = 4334 bytes (molecular_mass_P15_01.raw), 22 x 426 = 9372 bytes (MS_fragmentation_P29_01.raw)

Field +0x04: Packed type-code and record count​

Format: (marker << 24) | n_records.

The lower 24 bits count DAT records. PXD041695 has scans with more than 65,535 records, so reading only the lower 16 bits truncates them. The high byte is 0x18 in the other observed functions and 0x4c/0x08 in the public LCT mass/lock functions. DAT records may be 6, 8 or 12 bytes; consecutive DAT offsets establish the width. Those offsets also establish scan boundaries directly. At sizes divisible by both 22 and 30, these known markers and ordered DAT offsets disambiguate the two variants.

peak_count cannot be asserted for equality against the length returned by the current Encoding A decoder. In the corpus, a scan can contain thousands of non-zero 6-byte DAT records while the IDX field reports only tens or hundreds of centroid peaks (for example, 3,253 records and 47 peaks). The decoder emits the former after removing sentinel and zero-intensity records; it does not centroid, so its output count and peak_count measure different things and an equality check would reject valid corpus scans.

What does hold, and is what Reader::decode_scan checks (check_peak_count_sanity in reader.rs, which logs a warning when it fails): a centroid count can never exceed the raw decoded record count, since every centroid is built from at least one raw point. peak_count <= decoded_len is therefore a cheap decode sanity check - it catches a decode that produced implausibly few points for the peak count the index claims - without depending on the unimplemented centroiding relationship (Sigilweaver/OpenWRaw#24).

n_records = u32@0x04 & 0x00FFFFFF provides the record count when the next offset is unavailable. When the IDX length is divisible by both 22 and 30, the repeated 0x18 marker and monotonic offsets distinguish this variant from Variant B.

Variant B: 30-byte record (IMS / HDMS and non-IMS QTof)​

Observed in:

  • PXD066594 (WANG.raw, SYNAPT G2-Si, IMS)
  • PXD068881 (CtpA, SYNAPT G2-Si, IMS)
  • PXD075602 (DHPR_11257-1.raw, Xevo G2-XS QTof, non-IMS)

Key facts:

  • File size = N x 30 bytes (exact, no header)
  • DAT byte offset stored at +0x16 (NOT +0x00) - confirmed for all three datasets
  • Scan sizes vary depending on ion density
  • No record count: a scan runs from its offset to the next record's offset (the last scan runs to the end of the DAT)
  • The index does not determine the DAT record layout. Behind this variant the corpus holds 8-byte Encoding D records and, in some Xevo G2-XS lock-mass functions, 12-byte Encoding E records; the width is judged from the data (see _FUNCnnn.DAT, "Choosing the record width")
  • Both SYNAPT (mobility-capable) and Xevo QTof instruments use this variant
OffsetTypeConfirmedDescription
0x00u32NoFlags (always 0 in tested datasets)
0x04u32YesHardware scan-duration counter; see note below
0x08u32PartialHardware event counter; scan-varying; see note below
0x0Cf32YesRetention time (minutes)
0x10u32No4-byte field; interpreted as f32 gives scan-varying values (~18577 in WANG) but some scans have non-physical f32 garbage; purpose unclear
0x14u16NoHardware register; large values (35124-65208 in WANG); purpose unknown
0x16u32YesByte offset into .DAT file, low 32 bits
0x1Au32YesByte offset into .DAT file, high 32 bits (0 while the DAT is under 4 GiB)

The DAT offset is a 64-bit little-endian value at +0x16..+0x1E. Most DAT files are under 4 GiB, so the high word at +0x1A is usually 0. Functions with a larger DAT set it once the offset crosses 4 GiB: in PXD045625 Abu_190520_Sha11.raw, _FUNC001.IDX scan 2699 has low word 1,626,312 and high word 1 (offset 4,296,593,608). Three corpus functions (7.0 GB, 5.9 GB and 4.5 GB DAT files) have offsets past 4 GiB, covering 2,708 scans. Reading only the low word wraps the offset back to the start of the file.

Validated: sum of (IDX[i+1].offset - IDX[i].offset) for all i = DAT file size exactly. Offsets are non-decreasing and never exceed the DAT size in the corpus; the reader rejects a scan whose offsets go backwards or past the end of the DAT.

Field +0x04: Hardware tick counter​

Observed range: approximately 402.6M-402.8M across all corpus instruments. Varies by +/-50K between consecutive scans (jitter in scan execution time).

Hypothesis: total 800-MHz reference-clock ticks elapsed during the scan. Predicted value = n_pushes x (pusher_period_us x 800):

Datasetn_pushesIDX+0x04 / n_pushesImplied period at 800 MHz
DHPR82994852060.6 us
WANG143892799035.0 us
CtpA431793299116.6 us

DHPR result (60.6 us) is consistent with the FUNCTNS.INF-derived 60.25 us pusher period. WANG result (35.0 us) differs from the 69 us implied by tof_depth alone. CtpA (116.6 us) is anomalously long; may reflect a different acquisition mode.

The 800 MHz figure is inferred; the actual clock rate is not confirmed from available data.

Field +0x08: Scan counter (purpose unclear)​

Observed range: approximately 1.17B-1.29B across corpus. Fluctuates by up to 7% from first to last scan of a run. The variation pattern across scans is consistent with a TIC chromatogram (rises and falls with elution) but the absolute value does not match the sum of intensity values from the DAT records by any simple factor. May encode raw TDC hit counts from the detector hardware before per-scan intensity processing.

Distinguishing Variant A from B​

  • Check file_size mod 22 == 0 (Variant A) or mod 30 == 0 (Variant B)
  • Both should be mutually exclusive in practice
  • Variant B is used by both SYNAPT and Xevo QTof instruments, and does not by itself indicate an ion mobility acquisition

Fields Under Investigation​

  • Variant A: exact formula for +0x08 f32 (confirmed non-zero for data scans; correlated with TIC but no clean factor)
  • Variant A: +0x12 and +0x14 hardware register semantics
  • Variant B: +0x04 clock-rate confirmation (800 MHz assumed)
  • Variant B: +0x08 raw TDC hit count vs processed intensity relationship
  • Variant B: +0x10 4-byte field type and semantics
  • Whether a 32-byte variant exists for other instrument generations

Reference Sources​

  • Empirical hex analysis: re/src/analysis/inspect.py records
  • Corpus samples:
    • PXD066594/WANG.raw (Variant B, SYNAPT G2-Si, 590 scans)
    • PXD068881/20220517_CtpA_1076_2h_1.raw (Variant B, SYNAPT G2-Si, 1138 scans)
    • PXD058812/molecular_mass_P15_01.raw (Variant A, QTOF non-IMS, 197 scans)
    • PXD058812/MS_fragmentation_P29_01.raw (Variant A, QTOF non-IMS, 426 scans)
    • PXD075602/DHPR_11257-1.raw (Variant B, Xevo G2-XS QTof non-IMS, 1150 scans)