use crate::demod::ChannelDemod;
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::too_many_arguments
)]
pub(crate) fn decode_pd_line_pair(
spec: crate::modespec::ModeSpec,
pair_index: u32,
audio: &[f32],
skip_samples: i64,
pair_seconds: f64,
rate_hz: f64,
image: &mut crate::image::SstvImage,
demod: &mut ChannelDemod,
snr_est: &mut crate::snr::SnrEstimator,
hedr_shift_hz: f64,
) {
let sync_secs = spec.sync_seconds;
let porch_secs = spec.porch_seconds;
let pixel_secs = spec.pixel_seconds;
let septr_secs = spec.septr_seconds;
let width = spec.line_pixels;
let chan_len = f64::from(width) * pixel_secs;
let chan_starts_sec = [
sync_secs + porch_secs, sync_secs + porch_secs + chan_len + septr_secs, sync_secs + porch_secs + 2.0 * chan_len + 2.0 * septr_secs, sync_secs + porch_secs + 3.0 * chan_len + 3.0 * septr_secs, ];
let row0 = pair_index * 2;
let row1 = row0 + 1;
let width_us = width as usize;
let mut y_odd = vec![0_u8; width_us];
let mut cr = vec![0_u8; width_us];
let mut cb = vec![0_u8; width_us];
let mut y_even = vec![0_u8; width_us];
let ctx = crate::demod::ChannelDecodeCtx {
audio,
skip_samples,
rate_hz,
hedr_shift_hz,
spec,
};
let buffers: [&mut [u8]; 4] = [&mut y_odd, &mut cr, &mut cb, &mut y_even];
for (chan_idx, buf) in buffers.into_iter().enumerate() {
crate::demod::decode_one_channel_into(
buf,
chan_starts_sec[chan_idx],
pair_seconds,
&ctx,
&mut crate::demod::DemodState { demod, snr_est },
);
}
for x in 0..width_us {
let rgb_odd = crate::demod::ycbcr_to_rgb(y_odd[x], cr[x], cb[x]);
let rgb_even = crate::demod::ycbcr_to_rgb(y_even[x], cr[x], cb[x]);
image.put_pixel(x as u32, row0, rgb_odd);
image.put_pixel(x as u32, row1, rgb_even);
}
}
#[cfg(test)]
#[allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_possible_wrap
)]
mod tests {
#[test]
fn chan_starts_sec_septr_zero_is_numerically_equivalent_to_old_formula() {
for spec in [
crate::modespec::for_mode(crate::modespec::SstvMode::Pd120),
crate::modespec::for_mode(crate::modespec::SstvMode::Pd180),
crate::modespec::for_mode(crate::modespec::SstvMode::Pd240),
] {
let sync = spec.sync_seconds;
let porch = spec.porch_seconds;
let px = spec.pixel_seconds;
let septr = spec.septr_seconds;
let w = f64::from(spec.line_pixels);
let chan_len = w * px;
let new_starts = [
sync + porch,
sync + porch + chan_len + septr,
sync + porch + 2.0 * chan_len + 2.0 * septr,
sync + porch + 3.0 * chan_len + 3.0 * septr,
];
let old_starts = [
sync + porch,
sync + porch + w * px,
sync + porch + 2.0 * w * px,
sync + porch + 3.0 * w * px,
];
for (n, (n_val, o_val)) in new_starts.iter().zip(old_starts.iter()).enumerate() {
assert!(
(n_val - o_val).abs() < 1e-12,
"mode {:?} chan {} new={n_val} old={o_val}",
spec.mode,
n
);
}
}
}
}