use std::collections::VecDeque;
use thiserror::Error;
use super::messages::{
CsiReportPayload, SbpRequest, SbpResponse, SensingMeasurementInstance,
SensingMeasurementReport, SensingMeasurementSetupRequest, SensingMeasurementSetupResponse,
SensingSessionTermination,
};
use super::session::Action;
use super::types::{BfError, MeasurementInstanceId, MeasurementSetupId, MAX_REPORT_SUBCARRIERS};
use crate::csi_frame::CsiFrame;
#[derive(Debug, Clone, PartialEq)]
pub enum SensingFrame {
SetupRequest(SensingMeasurementSetupRequest),
SetupResponse(SensingMeasurementSetupResponse),
InstanceTrigger(SensingMeasurementInstance),
Report(SensingMeasurementReport),
SbpRequest(SbpRequest),
SbpResponse(SbpResponse),
SbpReport(SensingMeasurementReport),
Termination(SensingSessionTermination),
}
#[derive(Debug, Clone, PartialEq, Error)]
pub enum TransportError {
#[error("transport link down")]
LinkDown,
#[error("transport queue full (capacity {capacity})")]
QueueFull { capacity: usize },
}
pub trait SensingTransport {
fn send_frame(&mut self, frame: SensingFrame) -> Result<(), TransportError>;
fn poll_frame(&mut self) -> Option<SensingFrame>;
fn send_setup_request(
&mut self,
req: SensingMeasurementSetupRequest,
) -> Result<(), TransportError> {
self.send_frame(SensingFrame::SetupRequest(req))
}
fn send_setup_response(
&mut self,
resp: SensingMeasurementSetupResponse,
) -> Result<(), TransportError> {
self.send_frame(SensingFrame::SetupResponse(resp))
}
fn trigger_measurement_instance(
&mut self,
instance: SensingMeasurementInstance,
) -> Result<(), TransportError> {
self.send_frame(SensingFrame::InstanceTrigger(instance))
}
fn send_report(&mut self, report: SensingMeasurementReport) -> Result<(), TransportError> {
self.send_frame(SensingFrame::Report(report))
}
fn send_termination(
&mut self,
termination: SensingSessionTermination,
) -> Result<(), TransportError> {
self.send_frame(SensingFrame::Termination(termination))
}
}
pub fn action_to_frame(action: &Action) -> Option<SensingFrame> {
match action {
Action::SendSetupRequest(req) => Some(SensingFrame::SetupRequest(req.clone())),
Action::SendSetupResponse(resp) => Some(SensingFrame::SetupResponse(*resp)),
Action::SendSbpRequest(req) => Some(SensingFrame::SbpRequest(req.clone())),
Action::SendSbpResponse(resp) => Some(SensingFrame::SbpResponse(*resp)),
Action::TriggerInstance(instance) => Some(SensingFrame::InstanceTrigger(*instance)),
Action::SendReport(report) => Some(SensingFrame::Report(report.clone())),
Action::RelaySbpReport(report) => Some(SensingFrame::SbpReport(report.clone())),
Action::SendTermination(term) => Some(SensingFrame::Termination(*term)),
Action::DeliverReport(_) | Action::SessionClosed(_) => None,
}
}
pub fn frame_to_event(frame: SensingFrame) -> Option<super::session::SessionEvent> {
use super::session::SessionEvent as E;
match frame {
SensingFrame::SetupRequest(req) => Some(E::SetupRequestReceived(req)),
SensingFrame::SetupResponse(resp) => Some(E::SetupResponseReceived(resp)),
SensingFrame::Report(report) => Some(E::ReportReceived(report)),
SensingFrame::SbpReport(report) => Some(E::ReportReceived(report)),
SensingFrame::SbpRequest(req) => Some(E::SbpRequestReceived(req)),
SensingFrame::SbpResponse(resp) => Some(E::SbpResponseReceived(resp)),
SensingFrame::Termination(term) => Some(E::TerminationReceived(term)),
SensingFrame::InstanceTrigger(_) => None,
}
}
#[derive(Debug, Default)]
pub struct SimTransport {
sent: Vec<SensingFrame>,
inbound: VecDeque<SensingFrame>,
scripted: VecDeque<SensingFrame>,
link_down: bool,
capacity: usize,
}
impl SimTransport {
pub fn new() -> Self {
Self {
capacity: 1024,
..Default::default()
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
capacity,
..Default::default()
}
}
pub fn sent(&self) -> &[SensingFrame] {
&self.sent
}
pub fn drain_sent(&mut self) -> Vec<SensingFrame> {
std::mem::take(&mut self.sent)
}
pub fn push_inbound(&mut self, frame: SensingFrame) {
self.inbound.push_back(frame);
}
pub fn script_response(&mut self, frame: SensingFrame) {
self.scripted.push_back(frame);
}
pub fn set_link_down(&mut self, down: bool) {
self.link_down = down;
}
}
impl SensingTransport for SimTransport {
fn send_frame(&mut self, frame: SensingFrame) -> Result<(), TransportError> {
if self.link_down {
return Err(TransportError::LinkDown);
}
if self.sent.len() >= self.capacity {
return Err(TransportError::QueueFull {
capacity: self.capacity,
});
}
self.sent.push(frame);
if let Some(response) = self.scripted.pop_front() {
self.inbound.push_back(response);
}
Ok(())
}
fn poll_frame(&mut self) -> Option<SensingFrame> {
self.inbound.pop_front()
}
}
#[derive(Debug)]
pub struct OpportunisticCsiBridge {
setup_id: MeasurementSetupId,
batch_size: usize,
instance_counter: u32,
amp_accum: Vec<f64>,
phase_cos_accum: Vec<f64>,
phase_sin_accum: Vec<f64>,
frames_in_batch: usize,
}
impl OpportunisticCsiBridge {
pub fn new(setup_id: MeasurementSetupId, batch_size: usize) -> Result<Self, BfError> {
if batch_size == 0 {
return Err(BfError::InvalidBatchSize { got: 0 });
}
Ok(Self {
setup_id,
batch_size,
instance_counter: 0,
amp_accum: Vec::new(),
phase_cos_accum: Vec::new(),
phase_sin_accum: Vec::new(),
frames_in_batch: 0,
})
}
pub fn setup_id(&self) -> MeasurementSetupId {
self.setup_id
}
pub fn batch_size(&self) -> usize {
self.batch_size
}
pub fn ingest(&mut self, frame: &CsiFrame) -> Option<SensingMeasurementReport> {
if !frame.is_valid() || frame.subcarrier_count() > MAX_REPORT_SUBCARRIERS as usize {
return None;
}
let (amplitudes, phases) = frame.to_amplitude_phase();
if self.frames_in_batch == 0 || amplitudes.len() != self.amp_accum.len() {
self.amp_accum = vec![0.0; amplitudes.len()];
self.phase_cos_accum = vec![0.0; amplitudes.len()];
self.phase_sin_accum = vec![0.0; amplitudes.len()];
self.frames_in_batch = 0;
}
for (i, (a, p)) in amplitudes.iter().zip(phases.iter()).enumerate() {
self.amp_accum[i] += a;
self.phase_cos_accum[i] += p.cos();
self.phase_sin_accum[i] += p.sin();
}
self.frames_in_batch += 1;
if self.frames_in_batch < self.batch_size {
return None;
}
let scale = self.frames_in_batch as f64;
let n_subcarriers = u16::try_from(self.amp_accum.len()).ok()?;
let payload = CsiReportPayload {
n_subcarriers,
amplitudes: self.amp_accum.iter().map(|a| (a / scale) as f32).collect(),
phases: self
.phase_sin_accum
.iter()
.zip(self.phase_cos_accum.iter())
.map(|(s, c)| s.atan2(*c) as f32)
.collect(),
};
self.amp_accum.clear();
self.phase_cos_accum.clear();
self.phase_sin_accum.clear();
self.frames_in_batch = 0;
let n = self.instance_counter;
self.instance_counter = self.instance_counter.wrapping_add(1);
let report = SensingMeasurementReport {
setup_id: self.setup_id,
instance_id: MeasurementInstanceId::new((n % 256) as u8),
payload,
};
report.validate().ok()?;
Some(report)
}
}