Skip to contents

Read QTOF stream from 'Shimadzu LabSolutions' .lcd files.

Usage

read_sz_qtof(
  path,
  format_out = c("matrix", "data.frame", "data.table"),
  data_format = "long",
  levels = c("MS1", "MS2"),
  read_metadata = TRUE,
  metadata_format = "shimadzu_lcd",
  scale = TRUE
)

Arguments

path

Path to .lcd file.

format_out

Class of output. Either matrix, data.frame, or data.table.

data_format

Format of output. Spectra have no wide representation, so this is always long.

levels

Which MS levels to return, spelled MS1 and MS2. Both are decoded either way, since the level of a scan is recorded in the scan.

read_metadata

Logical. Whether to read metadata from the file.

metadata_format

Format to output metadata in.

scale

Logical. Whether to scale raw detector counts to the intensities reported by 'LabSolutions'. Defaults to TRUE, which errors if the accumulation count is missing from the file, since there is no safe divisor to guess; FALSE returns the raw counts.

Value

A named list holding whichever of MS1 and MS2 the file contains, each a data.table, data.frame or matrix in long format with columns scan, rt, mz and intensity, preceded by precursor_mz for product-ion spectra. Each carries a scan_info attribute giving one row per spectrum of that level, including the ones that hold no peaks, with the acquisition event, MS level, DDA cycle, ion polarity, selected precursor and peak count.

Details

Data for each scan is stored in three contiguous blocks: a 64-byte header, a block of flight times, followed by a block of intensities.

Scan Header (64 bytes, little-endian):

OffsetTypeField
0–3uint32Data-dependent acquisition (DDA) cycle: sequential over survey scans, repeated by the product-ion scans acquired from each
4–7uint32Retention time (milliseconds)
8–15uint64Base peak flight time (0 if empty scan)
16–19uint32Base peak raw intensity (0 if empty scan)
20–23uint32Scan index (0-based; increments for every scan including empty ones)
24–27uint32Data block size in bytes (n_peaks * (8 + int_width); 0 if empty scan)
28–31uint32MS level in the upper 16 bits, acquisition event in the lower 16 bits (e.g. 0x00010001 = MS1 event 1, 0x00020003 = MS2 event 3)
32–35uint32Padding
36–39uint32Intensity width in bytes (int_width); 1, 2 and 4 all occur on scans with data
40–43uint32Instrument constant (scan window / detector setting)
44–47uint32Instrument constant
48–51uint32Instrument constant
52–55uint32Instrument constant
56–63—Padding

m/z block (n x 8 bytes) Each peak's flight time is an unsigned 64-bit little-endian integer. R has no 64-bit integer type, so it is read as two 32-bit halves and recombined as low + high * 2^32. The low half is unsigned and has to be corrected as such, since R has no unsigned 32-bit type either: read as signed, it shifts half of all flight times by 2^32 (~4 ppm of flight time, so ~8 ppm of m/z), and 0x80000000 comes back as NA. Conversion is $$mz = ((t-B)/A)^2$$

A and B are fitted per file from the TOF Calibration Table stream (see read_sz_qtof_calibration), which stores calibration points for both polarities; the polarity of the acquisition selects between them. This matters: the coefficients differ by ~1.5% between polarities on one instrument, which is ~3% in m/z, so fixed coefficients are not usable. The tuning calibration alone is still a few ppm out, because it is recorded before the run; folding in the mass correction the file caches for the run closes that gap (see read_sz_qtof_mass_correction). Measured against 'ProteoWizard' conversions of three files from two instruments — which read the file through Shimadzu's own library — the m/z then agree to 0.03-0.1 ppm (median). That figure is the resolution of the comparison rather than of this parser: the vendor reports m/z to four decimal places, which is 0.5 ppm at the bottom of the mass range and 0.05 ppm at the top.

Intensity block (n x m bytes) Each peak's intensity is stored as a little-endian unsigned integer of m bytes, where m is the int_width field from the scan header (2 for most scans, 4 for scans containing large values). The stored values are raw detector counts summed over every TOF accumulation in the scan — the individual time-of-flight spectra the instrument adds together to make one. LabSolutions (and the 'Shimadzu' library used by 'ProteoWizard') normalizes them to a nominal 100 accumulations: $$intensity = round(raw \times 100 / n)$$ where n is the number of accumulations per scan (rounding is half-up). n is read from the Status stream, which stores it once per acquisition event; it is 376 in the reference file, giving a divisor of 3.76.