1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
//! The system for converting readings from two antenna into one spherical
//! coordinate.
use std::f32::consts::PI;
use crate::hdm::Hdm;
use crate::saf::BufferMetadata;
use crate::update_accumulator::UpdateAccumulator;
// IDs of our particular antennas and tags
// Back antenna (base) = 118875763481542
// Front antenna (aux) = 118875763481510
// Tag 1 = 118875764010724
// Tag 2 = 118875764011634
const BACK_ANTENNA: usize = 118875763481542;
const FRONT_ANTENNA: usize = 118875763481510;
// A tuple of the gain and range of the tags
type TagSetting = (f32, f32);
/// Converts from raw antenna measurements into a spherical coordinate and bundles
/// range and gain into a [`BufferMetadata`] struct to pass into a [`Binauraliser`](crate::saf::Binauraliser).
pub struct Sphericalizer {
tag_settings: Vec<TagSetting>,
}
impl Sphericalizer {
/// Instantiates a new `Sphericalizer`, storing the gains and ranges of the
/// tags that we will be looking for.
pub fn new(tag_settings: Vec<TagSetting>) -> Self {
Self { tag_settings }
}
/// From observation, azimuth and elevation are in the range of -70 to 70 degrees (-1.22173 to 1.22173 rad)
/// This function scales them to the range -90 to 90 degrees (-PI/2 to PI/2 rad)
fn scale_angle(azm: f32) -> f32 {
let pi_2 = PI / 2.0;
let scaled = azm * pi_2 / 1.22173;
scaled.clamp(-pi_2, pi_2)
}
/// Pulls updates out of the [`UpdateAccumulator`], sphericalizes them, bundles
/// the associated gain and range, generating a vec of [`BufferMetadata`].
pub fn query(&self, acc: &mut UpdateAccumulator<Hdm>) -> Option<Vec<BufferMetadata>> {
let mut updates = acc.get_status();
// There should be two updates for each tag since there are two antennas
// If there are not, then we must wait until more updates come in
if updates.len() != self.tag_settings.len() * 2 {
return None;
}
// Sort by tag ID
updates.sort_by(|a, b| a.dst.cmp(&b.dst));
// Group updates into pairs, one for each tag, where each pair is from the back and front antennas
let grouped_updates = updates.chunks(2);
// For each pair, derive a single BufferMetadata
grouped_updates
.enumerate()
.map(|(i, pair)| {
let back_ant = pair
.iter()
.find(|u| u.src == BACK_ANTENNA)
.expect("Missing an update from the back antenna");
let front_ant = pair
.iter()
.find(|u| u.src == FRONT_ANTENNA)
.expect("Missing an update from the front antenna");
let (gain, range) = self.tag_settings[i];
let mut metadata = BufferMetadata {
azimuth: Sphericalizer::scale_angle(back_ant.azm as f32),
elevation: Sphericalizer::scale_angle(back_ant.elv as f32),
range,
gain,
};
// The front antenna informs whether the tag is in front or behind the base antenna, since the base itself cannot tell
if front_ant.azm > 0.0 {
metadata.azimuth = PI - metadata.azimuth;
};
metadata.azimuth -= 1.5 * PI;
if metadata.azimuth < 0.0 {
metadata.azimuth += 2.0 * PI;
}
metadata
})
.collect::<Vec<_>>()
.into()
}
}