mod cache;
mod group;
mod transform;
pub use cache::Cache as GainCache;
pub use cache::IntoCache as IntoGainCache;
pub use group::Group;
pub use transform::IntoTransform as IntoGainTransform;
pub use transform::Transform as GainTransform;
use std::collections::HashMap;
use std::time::Duration;
use crate::{
common::{Drive, Segment},
error::AUTDInternalError,
geometry::{Device, Geometry, Transducer},
operation::{GainOp, NullOp},
};
use bitvec::prelude::*;
use super::with_segment::DatagramS;
use super::Datagram;
pub enum GainFilter<'a> {
All,
Filter(&'a HashMap<usize, BitVec<usize, Lsb0>>),
}
pub trait Gain {
fn calc(
&self,
geometry: &Geometry,
filter: GainFilter,
) -> Result<HashMap<usize, Vec<Drive>>, AUTDInternalError>;
fn transform(
geometry: &Geometry,
filter: GainFilter,
f: impl Fn(&Device, &Transducer) -> Drive,
) -> HashMap<usize, Vec<Drive>>
where
Self: Sized,
{
match filter {
GainFilter::All => geometry
.devices()
.map(|dev| (dev.idx(), dev.iter().map(|tr| f(dev, tr)).collect()))
.collect(),
GainFilter::Filter(filter) => geometry
.devices()
.filter_map(|dev| {
filter.get(&dev.idx()).map(|filter| {
(
dev.idx(),
dev.iter()
.map(|tr| {
if filter[tr.idx()] {
f(dev, tr)
} else {
Drive::null()
}
})
.collect(),
)
})
})
.collect(),
}
}
}
impl<'a> Gain for Box<dyn Gain + 'a> {
#[cfg_attr(coverage_nightly, coverage(off))]
fn calc(
&self,
geometry: &Geometry,
filter: GainFilter,
) -> Result<HashMap<usize, Vec<Drive>>, AUTDInternalError> {
self.as_ref().calc(geometry, filter)
}
}
impl<'a> Gain for Box<dyn Gain + Send + 'a> {
#[cfg_attr(coverage_nightly, coverage(off))]
fn calc(
&self,
geometry: &Geometry,
filter: GainFilter,
) -> Result<HashMap<usize, Vec<Drive>>, AUTDInternalError> {
self.as_ref().calc(geometry, filter)
}
}
impl DatagramS for Box<dyn Gain> {
type O1 = GainOp<Self>;
type O2 = NullOp;
#[cfg_attr(coverage_nightly, coverage(off))]
fn operation_with_segment(
self,
segment: Segment,
update_segment: bool,
) -> Result<(Self::O1, Self::O2), AUTDInternalError> {
Ok((
Self::O1::new(segment, update_segment, self),
Self::O2::default(),
))
}
fn timeout(&self) -> Option<Duration> {
Some(std::time::Duration::from_millis(200))
}
}
#[derive(Debug, Clone, Copy)]
pub struct ChangeGainSegment {
segment: Segment,
}
impl ChangeGainSegment {
pub const fn new(segment: Segment) -> Self {
Self { segment }
}
pub const fn segment(&self) -> Segment {
self.segment
}
}
impl Datagram for ChangeGainSegment {
type O1 = crate::operation::GainChangeSegmentOp;
type O2 = crate::operation::NullOp;
fn timeout(&self) -> Option<Duration> {
Some(Duration::from_millis(200))
}
fn operation(self) -> Result<(Self::O1, Self::O2), AUTDInternalError> {
Ok((Self::O1::new(self.segment), Self::O2::default()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{derive::*, geometry::tests::create_geometry};
#[derive(Gain, Clone, Copy, PartialEq, Debug)]
pub struct TestGain {
pub d: Drive,
}
impl Gain for TestGain {
fn calc(
&self,
geometry: &Geometry,
filter: GainFilter,
) -> Result<HashMap<usize, Vec<Drive>>, AUTDInternalError> {
Ok(Self::transform(geometry, filter, |_, _| self.d))
}
}
#[test]
fn test_gain_transform_all() -> anyhow::Result<()> {
let geometry = create_geometry(2, 249);
let d = Drive::random();
assert_eq!(
geometry
.devices()
.map(|dev| (dev.idx(), vec![d; dev.num_transducers()]))
.collect::<HashMap<_, _>>(),
TestGain { d }.calc(&geometry, GainFilter::All)?
);
Ok(())
}
#[test]
fn test_gain_transform_all_enabled() -> anyhow::Result<()> {
let mut geometry = create_geometry(2, 249);
geometry[0].enable = false;
let d = Drive::random();
assert_eq!(
geometry
.devices()
.map(|dev| (dev.idx(), vec![d; dev.num_transducers()]))
.collect::<HashMap<_, _>>(),
TestGain { d }.calc(&geometry, GainFilter::All)?
);
Ok(())
}
#[test]
fn test_gain_transform_filtered() -> anyhow::Result<()> {
let geometry = create_geometry(3, 249);
let filter = geometry
.iter()
.take(2)
.map(|dev| (dev.idx(), dev.iter().map(|tr| tr.idx() < 100).collect()))
.collect::<HashMap<_, _>>();
let d = Drive::random();
assert_eq!(
(0..2)
.map(|idx| (
geometry[idx].idx(),
(0..100)
.map(|_| d)
.chain((0..).map(|_| Drive::null()))
.take(geometry[idx].num_transducers())
.collect::<Vec<_>>()
))
.collect::<HashMap<_, _>>(),
TestGain { d }.calc(&geometry, GainFilter::Filter(&filter))?
);
Ok(())
}
#[test]
fn test_gain_transform_filtered_enabled() -> anyhow::Result<()> {
let mut geometry = create_geometry(2, 249);
geometry[0].enable = false;
let filter = geometry
.iter()
.map(|dev| (dev.idx(), dev.iter().map(|tr| tr.idx() < 100).collect()))
.collect::<HashMap<_, _>>();
let d = Drive::random();
assert_eq!(
geometry
.devices()
.map(|dev| (
dev.idx(),
(0..100)
.map(|_| d)
.chain((0..).map(|_| Drive::null()))
.take(dev.num_transducers())
.collect::<Vec<_>>()
))
.collect::<HashMap<_, _>>(),
TestGain { d }.calc(&geometry, GainFilter::Filter(&filter))?
);
Ok(())
}
#[test]
fn test_change_gain_segment() -> anyhow::Result<()> {
let d = ChangeGainSegment::new(Segment::S0);
assert_eq!(Segment::S0, d.segment());
assert_eq!(Some(Duration::from_millis(200)), d.timeout());
let _ = d.operation()?;
Ok(())
}
}