datalink 0.1.0

Decode aviation datalink traffic from payloads, SDR, files, and Airframes.io
use acars::demod::resample::ResampleAdapter;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub(crate) struct FrameContext {
    pub channel_index: usize,
    pub sample_index: u64,
    pub seconds_into_recording: f64,
    pub timestamp_unix: f64,
}

pub(crate) struct IqPipeline<'a, D> {
    adapter: &'a mut ResampleAdapter,
    demods: &'a mut [D],
    sample_index: &'a mut u64,
    sample_rate: u32,
    run_start: SystemTime,
}

impl<'a, D> IqPipeline<'a, D> {
    pub(crate) fn new(
        adapter: &'a mut ResampleAdapter,
        demods: &'a mut [D],
        sample_index: &'a mut u64,
        sample_rate: u32,
        run_start: SystemTime,
    ) -> Self {
        Self {
            adapter,
            demods,
            sample_index,
            sample_rate,
            run_start,
        }
    }
}

pub(crate) fn collect_iq_frames<D, Frame, Frames, Process>(
    pipeline: &mut IqPipeline<'_, D>,
    re: f32,
    im: f32,
    mut process: Process,
) -> anyhow::Result<Vec<(FrameContext, Frame)>>
where
    Process: FnMut(&mut D, f32, f32) -> anyhow::Result<Frames>,
    Frames: IntoIterator<Item = Frame>,
{
    let mut out = Vec::new();
    for sample in pipeline.adapter.feed(re, im) {
        *pipeline.sample_index = pipeline.sample_index.saturating_add(1);
        let seconds_into_recording = *pipeline.sample_index as f64 / pipeline.sample_rate as f64;
        let frame_ts = pipeline.run_start + Duration::from_secs_f64(seconds_into_recording);
        let timestamp_unix = frame_ts
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs_f64())
            .unwrap_or_default();

        for (channel_index, demod) in pipeline.demods.iter_mut().enumerate() {
            for frame in process(demod, sample.re, sample.im)? {
                out.push((
                    FrameContext {
                        channel_index,
                        sample_index: *pipeline.sample_index,
                        seconds_into_recording,
                        timestamp_unix,
                    },
                    frame,
                ));
            }
        }
    }
    Ok(out)
}