use crate::error::{Error, Result};
use crate::reader::BinaryReader;
use std::io::{Read, Seek, SeekFrom};
#[derive(Debug, Clone)]
pub struct Peak {
pub mz: f64,
pub abundance: f32,
}
#[derive(Debug)]
pub struct ProfileChunk {
pub first_bin: u32,
pub signal: Vec<f32>,
pub fudge: Option<f32>,
}
#[derive(Debug)]
pub struct Profile {
pub first_value: f64,
pub step: f64,
pub peak_count: u32,
pub nbins: u32,
pub chunks: Vec<ProfileChunk>,
}
#[derive(Debug)]
pub struct PacketHeader {
pub profile_size: u32,
pub peak_list_size: u32,
pub layout: u32,
pub descriptor_list_size: u32,
pub unknown_stream_size: u32,
pub triplet_stream_size: u32,
pub low_mz: f32,
pub high_mz: f32,
}
#[derive(Debug)]
pub struct ScanDataPacket {
pub header: PacketHeader,
pub profile: Option<Profile>,
pub peaks: Vec<Peak>,
}
impl ScanDataPacket {
pub(crate) fn read<R: Read + Seek>(r: &mut BinaryReader<R>) -> Result<Self> {
let header = PacketHeader::read(r)?;
let profile = if header.profile_size > 0 {
Some(Profile::read(r, header.layout)?)
} else {
None
};
let wide_mz = header.layout & 0x10000 != 0;
let peaks = if header.peak_list_size > 0 {
let count = r.read_u32()?;
let mut peaks = Vec::with_capacity(count as usize);
for _ in 0..count {
let mz = if wide_mz {
r.read_f64()?
} else {
r.read_f32()? as f64
};
let abundance = r.read_f32()?;
peaks.push(Peak { mz, abundance });
}
peaks
} else {
Vec::new()
};
if header.descriptor_list_size > 0 {
r.skip((header.descriptor_list_size * 4) as usize)?;
}
if header.unknown_stream_size > 0 {
r.skip((header.unknown_stream_size * 4) as usize)?;
}
if header.triplet_stream_size > 0 {
r.skip((header.triplet_stream_size * 4) as usize)?;
}
Ok(Self {
header,
profile,
peaks,
})
}
pub(crate) fn read_peaks_only<R: Read + Seek>(r: &mut BinaryReader<R>) -> Result<Vec<Peak>> {
let header = PacketHeader::read(r)?;
if header.profile_size > 0 {
r.skip((header.profile_size as usize) * 4)?;
}
let wide_mz = header.layout & 0x10000 != 0;
let peaks = if header.peak_list_size > 0 {
let count = r.read_u32()?;
let mut peaks = Vec::with_capacity(count as usize);
for _ in 0..count {
let mz = if wide_mz {
r.read_f64()?
} else {
r.read_f32()? as f64
};
let abundance = r.read_f32()?;
peaks.push(Peak { mz, abundance });
}
peaks
} else {
Vec::new()
};
Ok(peaks)
}
}
impl PacketHeader {
fn read<R: Read + Seek>(r: &mut BinaryReader<R>) -> Result<Self> {
let _unk1 = r.read_u32()?;
let profile_size = r.read_u32()?;
let peak_list_size = r.read_u32()?;
let layout = r.read_u32()?;
let descriptor_list_size = r.read_u32()?;
let unknown_stream_size = r.read_u32()?;
let triplet_stream_size = r.read_u32()?;
let _unk2 = r.read_u32()?;
let low_mz = r.read_f32()?;
let high_mz = r.read_f32()?;
Ok(Self {
profile_size,
peak_list_size,
layout,
descriptor_list_size,
unknown_stream_size,
triplet_stream_size,
low_mz,
high_mz,
})
}
}
impl Profile {
fn read<R: Read + Seek>(r: &mut BinaryReader<R>, layout: u32) -> Result<Self> {
let first_value = r.read_f64()?;
let step = r.read_f64()?;
let peak_count = r.read_u32()?;
let nbins = r.read_u32()?;
let has_fudge = layout & 0xFF != 0;
let mut chunks = Vec::with_capacity(peak_count as usize);
for _ in 0..peak_count {
let first_bin = r.read_u32()?;
let chunk_nbins = r.read_u32()?;
let fudge = if has_fudge { Some(r.read_f32()?) } else { None };
let mut signal = Vec::with_capacity(chunk_nbins as usize);
for _ in 0..chunk_nbins {
signal.push(r.read_f32()?);
}
chunks.push(ProfileChunk {
first_bin,
signal,
fudge,
});
}
Ok(Self {
first_value,
step,
peak_count,
nbins,
chunks,
})
}
}
impl Profile {
pub fn to_mz_intensity(&self, coefficients: &[f64]) -> Vec<(f64, f64)> {
let mut result = Vec::with_capacity(self.nbins as usize);
for chunk in &self.chunks {
for (i, &intensity) in chunk.signal.iter().enumerate() {
let bin_global = chunk.first_bin as f64 + i as f64;
let freq = self.first_value + bin_global * self.step;
let freq_adj = if let Some(fudge) = chunk.fudge {
freq + fudge as f64
} else {
freq
};
let mz = freq_to_mz(freq_adj, coefficients);
result.push((mz, intensity as f64));
}
}
result
}
}
pub fn freq_to_mz(freq: f64, coefficients: &[f64]) -> f64 {
if freq == 0.0 {
return 0.0;
}
match coefficients.len() {
0 => freq, 4 => {
let (a, b, c) = (coefficients[1], coefficients[2], coefficients[3]);
a + b / freq + c / (freq * freq)
}
5 => {
let (a, b, c) = (coefficients[2], coefficients[3], coefficients[4]);
let f2 = freq * freq;
a + b / f2 + c / (f2 * f2)
}
7 => {
let (a, b, c) = (coefficients[2], coefficients[3], coefficients[4]);
let f2 = freq * freq;
a + b / f2 + c / (f2 * f2)
}
_ => freq,
}
}
pub fn read_flat_peaks<R: Read + Seek>(
source: &mut R,
data_addr: u64,
cum_end: u64,
data_size: u32,
) -> Result<Vec<Peak>> {
if data_size <= 1 {
return Ok(Vec::new());
}
for subtract in [1u32, 2] {
if data_size <= subtract {
continue;
}
let peak_count = (data_size - subtract) as usize;
let peak_section_bytes = peak_count as u64 * 9;
if peak_section_bytes > cum_end {
continue;
}
let peaks_start = data_addr + cum_end - peak_section_bytes;
source.seek(SeekFrom::Start(peaks_start))?;
let mut r = BinaryReader::new(&mut *source);
let mut peaks = Vec::with_capacity(peak_count);
for _ in 0..peak_count {
let mz = r.read_f32()? as f64;
let abundance = r.read_f32()?;
peaks.push(Peak { mz, abundance });
}
let looks_valid = if let Some(first) = peaks.first() {
first.mz == 0.0 || (first.mz > 10.0 && first.mz < 10_000.0)
} else {
true
};
if looks_valid {
return Ok(peaks);
}
}
Err(Error::UnexpectedEof {
offset: data_addr + cum_end,
needed: 0,
})
}
pub fn read_scan_srm_v66<R: Read + Seek>(
source: &mut R,
data_addr: u64,
start_offset: u64,
_record_size: u32,
) -> Result<Vec<Peak>> {
let abs_start = data_addr + start_offset;
source.seek(SeekFrom::Start(abs_start))?;
let mut r = BinaryReader::new(source);
let n_peaks = r.read_u32()? as usize;
if n_peaks == 0 {
return Ok(Vec::new());
}
r.skip(28)?;
r.skip(n_peaks * 8)?;
let mut peaks = Vec::with_capacity(n_peaks);
for _ in 0..n_peaks {
let _channel = r.read_u32()?;
let mz = r.read_f32()? as f64;
let abundance = r.read_f32()?;
peaks.push(Peak { mz, abundance });
}
Ok(peaks)
}
pub fn read_scan_srm_v66_windows<R: Read + Seek>(
source: &mut R,
data_addr: u64,
start_offset: u64,
) -> Result<Vec<(f32, f32)>> {
let abs_start = data_addr + start_offset;
source.seek(SeekFrom::Start(abs_start))?;
let mut r = BinaryReader::new(source);
let n_peaks = r.read_u32()? as usize;
if n_peaks == 0 {
return Ok(Vec::new());
}
r.skip(28)?;
let mut windows = Vec::with_capacity(n_peaks);
for _ in 0..n_peaks {
let lo = r.read_f32()?;
let hi = r.read_f32()?;
windows.push((lo, hi));
}
Ok(windows)
}
pub fn search_v63_transition(data: &[u8], q3_center_target: f64) -> Option<(f64, f64, f64)> {
let end = data.len().saturating_sub(32);
for j in 8..end {
if j + 8 > data.len() {
break;
}
let v = f64::from_le_bytes(data[j..j + 8].try_into().ok()?);
if (v - q3_center_target).abs() > 0.002 {
continue;
}
let q1 = f64::from_le_bytes(data[j - 8..j].try_into().ok()?);
if !q1.is_finite() || !(50.0..=3000.0).contains(&q1) {
continue;
}
if j + 16 > data.len() {
continue;
}
let q3w = f64::from_le_bytes(data[j + 8..j + 16].try_into().ok()?);
if !q3w.is_finite() || !(0.01..=10.0).contains(&q3w) {
continue;
}
if j + 32 > data.len() {
continue;
}
let ce = f64::from_le_bytes(data[j + 24..j + 32].try_into().ok()?);
if !ce.is_finite() || !(0.1..=300.0).contains(&ce) {
continue;
}
return Some((q1, q3w, ce));
}
None
}