pub enum DeviceFamily {
LcqIonTrap,
LtqIonTrap,
LtqFt,
LtqOrbitrap,
QOrbitrap,
Tribrid,
ExplorisOrbitrap,
OrbitrapAstral,
TripleQuad,
Unknown,
}Expand description
Coarse instrument-family classification.
Variants§
LcqIonTrap
LCQ Classic/Deca/Advantage/Fleet - 3D ion trap (legacy).
LtqIonTrap
LTQ / LTQ XL / LTQ Velos / LTQ Velos Pro - 2D linear ion trap.
LtqFt
LTQ FT - ion trap coupled with FTICR (pre-Orbitrap era).
LtqOrbitrap
LTQ Orbitrap family - ion trap + Orbitrap hybrids (Classic / XL / Discovery / Velos / Elite).
QOrbitrap
Q Exactive family - quadrupole + C-trap + Orbitrap (Q Exactive / Plus / HF / HF-X / UHMR). No ion trap.
Tribrid
Tribrid Orbitrap - quadrupole + linear ion trap + Orbitrap (Fusion / Fusion Lumos / Eclipse / Ascend).
ExplorisOrbitrap
Single-stage Q-Orbitrap with advanced scan modes (Orbitrap Exploris 120 / 240 / 480).
OrbitrapAstral
Orbitrap Astral hybrid - Orbitrap plus asymmetric-track lossless analyzer.
TripleQuad
Triple quadrupole - TSQ Vantage / Quantum / Quantiva / Altis / Endura.
Unknown
Unknown / undetected.
Implementations§
Source§impl DeviceFamily
impl DeviceFamily
Sourcepub fn display_name(self) -> &'static str
pub fn display_name(self) -> &'static str
Human-readable family name.
Examples found in repository?
3fn main() {
4 let args: Vec<String> = env::args().collect();
5 if args.len() < 2 {
6 eprintln!("Usage: dump <file.raw> [--max-scans N]");
7 std::process::exit(1);
8 }
9
10 let path = &args[1];
11
12 // Parse optional --max-scans flag
13 let max_scans: Option<u32> = args
14 .windows(2)
15 .find(|w| w[0] == "--max-scans")
16 .and_then(|w| w[1].parse().ok());
17 match opentfraw::RawFileReader::open_path(path) {
18 Ok(raw) => {
19 println!("=== Thermo RAW File ===");
20 println!("Version: {}", raw.version);
21 println!(
22 "Instrument: {} ({})",
23 raw.instrument_model.unwrap_or("unknown model"),
24 raw.device_family.display_name()
25 );
26 println!(
27 "Scans: {} ({} to {})",
28 raw.num_scans,
29 raw.run_header.sample_info.first_scan_number,
30 raw.run_header.sample_info.last_scan_number,
31 );
32 println!();
33
34 println!("--- Header ---");
35 println!("Signature: {}", raw.header.signature);
36 println!(
37 "Audit start: {} (unix ts: {:.0})",
38 raw.header.audit_start.tag1, raw.header.audit_start.time
39 );
40 println!("Audit tag2: {}", raw.header.audit_start.tag2);
41 println!();
42
43 println!("--- Acquisition Date ---");
44 let p = &raw.raw_file_info.preamble;
45 println!(
46 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
47 p.year, p.month, p.day, p.hour, p.minute, p.second, p.millisecond
48 );
49 println!("Controllers: {}", p.controller_count);
50 println!("Data addr: {:#x}", p.data_addr);
51 println!("RunHdr addr: {:#x}", p.run_header_addr);
52 println!();
53
54 println!("--- Sequence Row ---");
55 println!("Comment: {}", raw.seq_row.comment);
56 println!("Inst method: {}", raw.seq_row.inst_method);
57 println!("File name: {}", raw.seq_row.file_name);
58 println!();
59
60 println!("--- RawFileInfo ---");
61 println!("Computer: {}", raw.raw_file_info.computer_name);
62 for (i, h) in raw.raw_file_info.label_headings.iter().enumerate() {
63 if !h.is_empty() {
64 println!("Label[{}]: {}", i + 1, h);
65 }
66 }
67 println!();
68
69 println!("--- Sample Info ---");
70 let si = &raw.run_header.sample_info;
71 println!("M/z range: {:.2} - {:.2}", si.low_mz, si.high_mz);
72 println!(
73 "RT range: {:.2} - {:.2} min",
74 si.start_time, si.end_time
75 );
76 println!("Max TIC: {:.2e}", si.max_ion_current);
77 println!("Error log: {} entries", si.error_log_length);
78 println!("Inst log: {} entries", si.inst_log_length);
79 println!();
80
81 println!("--- Run Header ---");
82 let rh = &raw.run_header;
83 println!("Scan index: {:#x}", rh.scan_index_addr);
84 println!("Data: {:#x}", rh.data_addr);
85 println!("Trailer: {:#x}", rh.scan_trailer_addr);
86 println!("Params: {:#x}", rh.scan_params_addr);
87 println!("Inst log: {:#x}", rh.inst_log_addr);
88 println!("Error log: {:#x}", rh.error_log_addr);
89 println!("Self addr: {:#x}", rh.own_addr);
90 println!("ntrailer: {}", rh.ntrailer);
91 println!("nparams: {}", rh.nparams);
92 println!("nsegs: {}", rh.nsegs);
93 println!();
94
95 // First few scans
96 let n_show = std::cmp::min(5, raw.scan_index.len());
97 println!("--- First {} Scan Index Entries ---", n_show);
98 for entry in &raw.scan_index[..n_show] {
99 println!(" Scan {}: RT={:.4} min, TIC={:.2e}, base={:.2} @ {:.4} m/z, range=[{:.2}-{:.2}], offset={:#x}, size={}",
100 entry.index + 1,
101 entry.start_time,
102 entry.total_current,
103 entry.base_intensity,
104 entry.base_mz,
105 entry.low_mz,
106 entry.high_mz,
107 entry.offset,
108 entry.data_size,
109 );
110 }
111 println!();
112
113 // First few scan events
114 let n_events = std::cmp::min(3, raw.scan_events.len());
115 println!("--- First {} Scan Events ---", n_events);
116 for (i, evt) in raw.scan_events[..n_events].iter().enumerate() {
117 let p = &evt.preamble;
118 println!(" Event {}: analyzer={:?}, polarity={:?}, mode={:?}, ms_power={:?}, dependent={}, ionization={:?}, activation={:?}",
119 i,
120 p.analyzer(),
121 p.polarity(),
122 p.scan_mode(),
123 p.ms_power(),
124 p.is_dependent(),
125 p.ionization(),
126 p.activation(),
127 );
128 if !evt.reactions.is_empty() {
129 for rx in &evt.reactions {
130 println!(" Precursor: {:.4} @ {:.1}", rx.precursor_mz, rx.energy);
131 }
132 }
133 println!(" Coefficients: {} params", evt.coefficients.len());
134 for fc in &evt.fraction_collectors {
135 println!(" Range: [{:.2}-{:.2}]", fc.low_mz, fc.high_mz);
136 }
137 }
138 println!();
139
140 // Scan parameters header (trailer extra schema)
141 println!(
142 "--- Scan Parameters Schema ({} fields) ---",
143 raw.scan_parameters_header.fields.len()
144 );
145 for desc in &raw.scan_parameters_header.fields {
146 println!(
147 " {:?}: \"{}\" (len={})",
148 desc.field_type, desc.label, desc.length
149 );
150 }
151 println!();
152
153 // First scan's parameters
154 if let Some(first_params) = raw.scan_parameters.first() {
155 println!("--- Scan 1 Parameters ---");
156 for (label, value) in &first_params.values {
157 match value {
158 opentfraw::generic_data::GenericValue::Gap => {}
159 _ => println!(" {}: {:?}", label, value),
160 }
161 }
162 }
163 println!();
164
165 // Typed accessor summary for scan 1
166 let first = raw.run_header.sample_info.first_scan_number;
167 if let Some(p) = raw.scan_params(first) {
168 println!("--- Scan 1 Typed Summary ---");
169 println!(" injection_time_ms : {:?}", p.ion_injection_time_ms());
170 println!(" charge_state : {:?}", p.charge_state());
171 println!(" monoisotopic_mz : {:?}", p.monoisotopic_mz());
172 println!(" micro_scan_count : {:?}", p.micro_scan_count());
173 println!(" ft_resolution : {:?}", p.ft_resolution());
174 println!(" hcd_energy : {:?}", p.hcd_energy());
175 println!(" master_scan_number : {:?}", p.master_scan_number());
176 println!(" agc_enabled : {:?}", p.agc_enabled());
177 println!(" agc_target : {:?}", p.agc_target());
178 println!(" max_ion_time_ms : {:?}", p.max_ion_time_ms());
179 println!(" elapsed_scan_time_s: {:?}", p.elapsed_scan_time_s());
180 println!(" lm_correction_ppm : {:?}", p.lm_correction_ppm());
181 }
182 println!();
183
184 // Error log
185 if !raw.error_log.is_empty() {
186 println!("--- Error Log ({} entries) ---", raw.error_log.len());
187 for (i, e) in raw.error_log.iter().enumerate().take(5) {
188 println!(" [{}] RT={:.2}: {}", i, e.time, e.message);
189 }
190 }
191
192 // Instrument log schema
193 println!(
194 "--- Instrument Log Schema ({} fields) ---",
195 raw.inst_log_header.fields.len()
196 );
197 for desc in &raw.inst_log_header.fields {
198 println!(" {:?}: \"{}\"", desc.field_type, desc.label);
199 }
200 println!();
201
202 // Scan data validation: read first few scans and cross-check against index
203 println!(
204 "--- Scan Data Validation (device={}, format={}) ---",
205 raw.device_family.display_name(),
206 raw.scan_format.display_name()
207 );
208 let mut file = std::fs::File::open(path).expect("reopen file");
209 let first_scan = raw.run_header.sample_info.first_scan_number;
210 let cap = max_scans.unwrap_or(5);
211 let n_validate = std::cmp::min(cap, raw.num_scans);
212 for i in 0..n_validate {
213 let scan_num = first_scan + i;
214 let idx_entry = &raw.scan_index[i as usize];
215
216 if raw.flat_peaks {
217 // Flat-peak (TSQ/SRM) format - use unified router
218 match raw.read_scan_peaks(&mut file, scan_num) {
219 Ok(peaks) => {
220 let n_peaks = peaks.len();
221 let peak_tic: f64 = peaks.iter().map(|p| p.abundance as f64).sum();
222 let nonzero: Vec<_> =
223 peaks.iter().filter(|p| p.abundance != 0.0).collect();
224 println!(" Scan {} (evt={}): {} peaks ({} nonzero) | peak_tic={:.2e} vs index_tic={:.2e}",
225 scan_num, idx_entry.scan_event, n_peaks, nonzero.len(),
226 peak_tic, idx_entry.total_current);
227 if i == 0 {
228 for (j, pk) in peaks.iter().enumerate().take(5) {
229 println!(
230 " Peak {}: m/z={:.4}, abundance={:.4}",
231 j, pk.mz, pk.abundance
232 );
233 }
234 println!(
235 " Index base peak: m/z={:.4}, intensity={:.2e}",
236 idx_entry.base_mz, idx_entry.base_intensity
237 );
238 }
239 }
240 Err(e) => {
241 println!(" Scan {}: ERROR - {}", scan_num, e);
242 }
243 }
244 } else {
245 // PacketHeader format
246 match raw.read_scan(&mut file, scan_num) {
247 Ok(pkt) => {
248 let h = &pkt.header;
249 let profile_bins: usize = pkt
250 .profile
251 .as_ref()
252 .map(|p| p.chunks.iter().map(|c| c.signal.len()).sum())
253 .unwrap_or(0);
254 let n_peaks = pkt.peaks.len();
255
256 // Cross-check: scan index says range [low-high]
257 let range_ok = if n_peaks > 0 {
258 let first_mz = pkt.peaks[0].mz;
259 let last_mz = pkt.peaks[n_peaks - 1].mz;
260 // Peaks should be within the declared range (with some tolerance)
261 first_mz >= idx_entry.low_mz * 0.99
262 && last_mz <= idx_entry.high_mz * 1.01
263 } else {
264 true
265 };
266
267 // Compute TIC from centroid peaks and compare
268 let peak_tic: f64 = pkt.peaks.iter().map(|p| p.abundance as f64).sum();
269
270 // Show scan event info
271 let evt = raw.scan_events.get(i as usize);
272 let mode_str = evt
273 .and_then(|e| e.preamble.scan_mode())
274 .map(|m| format!("{:?}", m))
275 .unwrap_or_else(|| "?".into());
276
277 println!(" Scan {}: {} | profile={} bins, peaks={}, layout={} | mz=[{:.2}-{:.2}] | range_ok={} | peak_tic={:.2e} vs index_tic={:.2e}",
278 scan_num, mode_str, profile_bins, n_peaks, h.layout,
279 h.low_mz, h.high_mz,
280 range_ok, peak_tic, idx_entry.total_current,
281 );
282
283 // Show top 3 peaks for first scan
284 if i == 0 && !pkt.peaks.is_empty() {
285 let mut sorted: Vec<_> = pkt.peaks.iter().collect();
286 sorted
287 .sort_by(|a, b| b.abundance.partial_cmp(&a.abundance).unwrap());
288 let n_top = std::cmp::min(3, sorted.len());
289 for (j, pk) in sorted[..n_top].iter().enumerate() {
290 println!(
291 " Top {}: m/z={:.4}, abundance={:.2e}",
292 j + 1,
293 pk.mz,
294 pk.abundance
295 );
296 }
297 println!(
298 " Index base peak: m/z={:.4}, intensity={:.2e}",
299 idx_entry.base_mz, idx_entry.base_intensity
300 );
301 }
302
303 // Show profile info for first scan
304 if i == 0 {
305 if let Some(ref prof) = pkt.profile {
306 println!(" Profile: first_value={:.6e}, step={:.6e}, {} chunks, {} total bins",
307 prof.first_value, prof.step, prof.chunks.len(), profile_bins);
308 // Convert and show top signal
309 let coeffs: Vec<f64> =
310 evt.map(|e| e.coefficients.clone()).unwrap_or_default();
311 if !coeffs.is_empty() {
312 let mz_int = prof.to_mz_intensity(&coeffs);
313 if let Some(max_pt) = mz_int
314 .iter()
315 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
316 {
317 println!(" Profile max: m/z={:.4}, intensity={:.2e} (coeffs={})",
318 max_pt.0, max_pt.1, coeffs.len());
319 }
320 }
321 }
322 }
323 }
324 Err(e) => {
325 println!(" Scan {}: ERROR - {}", scan_num, e);
326 }
327 }
328 } // end else (PacketHeader)
329 }
330 }
331 Err(e) => {
332 eprintln!("Error: {}", e);
333 std::process::exit(1);
334 }
335 }
336}Sourcepub fn uses_flat_peaks(self) -> bool
pub fn uses_flat_peaks(self) -> bool
Whether the device is expected to use the flat-peaks (SRM) scan layout.
Source§impl DeviceFamily
impl DeviceFamily
Sourcepub fn detect_instrument(
metadata_bytes: &[u8],
tag2: &str,
inst_method: &str,
first_analyzer: Option<Analyzer>,
) -> DetectedInstrument
pub fn detect_instrument( metadata_bytes: &[u8], tag2: &str, inst_method: &str, first_analyzer: Option<Analyzer>, ) -> DetectedInstrument
Scan metadata_bytes (a raw prefix of the RAW file) for a canonical
Thermo instrument model encoded as UTF-16LE, then fall back to the
heuristic over tag2 + inst_method + first_analyzer.
Trait Implementations§
Source§impl Clone for DeviceFamily
impl Clone for DeviceFamily
Source§fn clone(&self) -> DeviceFamily
fn clone(&self) -> DeviceFamily
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more