mod gain_stm_control_flags;
mod gain_stm_mode;
mod reduced_phase;
use std::collections::HashMap;
use crate::{
common::{Drive, LoopBehavior, Segment},
datagram::{Gain, GainFilter},
error::AUTDInternalError,
fpga::{FPGADrive, GAIN_STM_BUF_SIZE_MAX},
geometry::{Device, Geometry},
operation::{
cast,
stm::gain::reduced_phase::{PhaseFull, PhaseHalf},
Operation, TypeTag,
},
};
pub use gain_stm_mode::GainSTMMode;
use self::gain_stm_control_flags::GainSTMControlFlags;
#[repr(C)]
struct GainSTMHead {
tag: TypeTag,
flag: GainSTMControlFlags,
mode: GainSTMMode,
segment: u8,
freq_div: u32,
rep: u32,
}
#[repr(C)]
struct GainSTMSubseq {
tag: TypeTag,
flag: GainSTMControlFlags,
}
#[repr(C)]
struct GainSTMUpdate {
tag: TypeTag,
segment: u8,
}
pub struct GainSTMOp<G: Gain> {
gains: Vec<G>,
drives: Vec<HashMap<usize, Vec<Drive>>>,
remains: HashMap<usize, usize>,
sent: HashMap<usize, usize>,
mode: GainSTMMode,
freq_div: u32,
loop_behavior: LoopBehavior,
segment: Segment,
update_segment: bool,
}
impl<G: Gain> GainSTMOp<G> {
pub fn new(
gains: Vec<G>,
mode: GainSTMMode,
freq_div: u32,
loop_behavior: LoopBehavior,
segment: Segment,
update_segment: bool,
) -> Self {
Self {
gains,
drives: Default::default(),
remains: Default::default(),
sent: Default::default(),
mode,
freq_div,
loop_behavior,
segment,
update_segment,
}
}
}
impl<G: Gain> Operation for GainSTMOp<G> {
fn init(&mut self, geometry: &Geometry) -> Result<(), AUTDInternalError> {
self.drives = self
.gains
.iter()
.map(|g| g.calc(geometry, GainFilter::All))
.collect::<Result<_, _>>()?;
if !(2..=GAIN_STM_BUF_SIZE_MAX).contains(&self.drives.len()) {
return Err(AUTDInternalError::GainSTMSizeOutOfRange(self.drives.len()));
}
self.remains = geometry
.devices()
.map(|device| (device.idx(), self.drives.len()))
.collect();
self.sent = geometry.devices().map(|device| (device.idx(), 0)).collect();
Ok(())
}
fn required_size(&self, device: &Device) -> usize {
if self.sent[&device.idx()] == 0 {
std::mem::size_of::<GainSTMHead>()
+ device.num_transducers() * std::mem::size_of::<FPGADrive>()
} else {
std::mem::size_of::<GainSTMSubseq>()
+ device.num_transducers() * std::mem::size_of::<FPGADrive>()
}
}
fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result<usize, AUTDInternalError> {
assert!(self.remains[&device.idx()] > 0);
let sent = self.sent[&device.idx()];
let offset = if sent == 0 {
std::mem::size_of::<GainSTMHead>()
} else {
std::mem::size_of::<GainSTMSubseq>()
};
assert!(tx.len() >= offset + device.num_transducers() * std::mem::size_of::<FPGADrive>());
let mut send = 0;
match self.mode {
GainSTMMode::PhaseIntensityFull => {
let d = &self.drives[sent][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut FPGADrive,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
}
GainSTMMode::PhaseFull => {
let d = &self.drives[sent][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut PhaseFull<0>,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
if self.drives.len() > sent + 1 {
let d = &self.drives[sent + 1][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut PhaseFull<1>,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
}
}
GainSTMMode::PhaseHalf => {
let d = &self.drives[sent][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut PhaseHalf<0>,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
if self.drives.len() > sent + 1 {
let d = &self.drives[sent + 1][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut PhaseHalf<1>,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
}
if self.drives.len() > sent + 2 {
let d = &self.drives[sent + 2][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut PhaseHalf<2>,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
}
if self.drives.len() > sent + 3 {
let d = &self.drives[sent + 3][&device.idx()];
unsafe {
std::slice::from_raw_parts_mut(
tx[offset..].as_mut_ptr() as *mut PhaseHalf<3>,
d.len(),
)
.iter_mut()
.zip(d.iter())
.for_each(|(d, s)| d.set(s));
}
send += 1;
}
}
}
if sent == 0 {
let d = cast::<GainSTMHead>(tx);
d.tag = TypeTag::GainSTM;
d.flag = GainSTMControlFlags::BEGIN;
d.mode = self.mode;
d.segment = self.segment as u8;
d.freq_div = self.freq_div;
d.rep = self.loop_behavior.to_rep();
} else {
let d = cast::<GainSTMSubseq>(tx);
d.tag = TypeTag::GainSTM;
d.flag = GainSTMControlFlags::NONE;
}
let d = cast::<GainSTMSubseq>(tx);
d.flag.set(
GainSTMControlFlags::SEND_BIT0,
((send as u8 - 1) & 0x01) != 0,
);
d.flag.set(
GainSTMControlFlags::SEND_BIT1,
((send as u8 - 1) & 0x02) != 0,
);
if sent + send == self.drives.len() {
d.flag.set(GainSTMControlFlags::END, true);
d.flag.set(GainSTMControlFlags::UPDATE, self.update_segment);
}
self.sent.insert(device.idx(), sent + send);
if sent == 0 {
Ok(std::mem::size_of::<GainSTMHead>()
+ device.num_transducers() * std::mem::size_of::<FPGADrive>())
} else {
Ok(std::mem::size_of::<GainSTMSubseq>()
+ device.num_transducers() * std::mem::size_of::<FPGADrive>())
}
}
fn commit(&mut self, device: &Device) {
self.remains
.insert(device.idx(), self.drives.len() - self.sent[&device.idx()]);
}
fn remains(&self, device: &Device) -> usize {
self.remains[&device.idx()]
}
}
pub struct GainSTMChangeSegmentOp {
segment: Segment,
remains: HashMap<usize, usize>,
}
impl GainSTMChangeSegmentOp {
pub fn new(segment: Segment) -> Self {
Self {
segment,
remains: HashMap::new(),
}
}
}
impl Operation for GainSTMChangeSegmentOp {
fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result<usize, AUTDInternalError> {
assert_eq!(self.remains[&device.idx()], 1);
let d = cast::<GainSTMUpdate>(tx);
d.tag = TypeTag::GainSTMChangeSegment;
d.segment = self.segment as u8;
Ok(std::mem::size_of::<GainSTMUpdate>())
}
fn required_size(&self, _: &Device) -> usize {
std::mem::size_of::<GainSTMUpdate>()
}
fn init(&mut self, geometry: &Geometry) -> Result<(), AUTDInternalError> {
self.remains = geometry.devices().map(|device| (device.idx(), 1)).collect();
Ok(())
}
fn remains(&self, device: &Device) -> usize {
self.remains[&device.idx()]
}
fn commit(&mut self, device: &Device) {
self.remains.insert(device.idx(), 0);
}
}
#[cfg(test)]
mod tests {
use std::num::NonZeroU32;
use rand::prelude::*;
use super::*;
use crate::{
common::{EmitIntensity, Phase},
fpga::{SAMPLING_FREQ_DIV_MAX, SAMPLING_FREQ_DIV_MIN},
geometry::tests::create_geometry,
operation::tests::{NullGain, TestGain},
};
const NUM_TRANS_IN_UNIT: usize = 249;
const NUM_DEVICE: usize = 10;
#[test]
fn gain_stm_phase_intensity_full_op() {
const GAIN_STM_SIZE: usize = 3;
const FRAME_SIZE: usize = std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2;
let geometry = create_geometry(NUM_DEVICE, NUM_TRANS_IN_UNIT);
let mut tx = vec![0x00u8; FRAME_SIZE * NUM_DEVICE];
let mut rng = rand::thread_rng();
let gain_data: Vec<HashMap<usize, Vec<Drive>>> = (0..GAIN_STM_SIZE)
.map(|_| {
geometry
.devices()
.map(|dev| {
(
dev.idx(),
(0..dev.num_transducers())
.map(|_| {
Drive::new(
Phase::new(rng.gen_range(0x00..=0xFF)),
EmitIntensity::new(rng.gen_range(0..=0xFF)),
)
})
.collect(),
)
})
.collect()
})
.collect();
let gains: Vec<TestGain> = (0..GAIN_STM_SIZE)
.map(|i| TestGain {
data: gain_data[i].clone(),
})
.collect();
let freq_div: u32 = rng.gen_range(SAMPLING_FREQ_DIV_MIN..SAMPLING_FREQ_DIV_MAX);
let loop_behavior = LoopBehavior::Infinite;
let segment = Segment::S0;
let mut op = GainSTMOp::<_>::new(
gains,
GainSTMMode::PhaseIntensityFull,
freq_div,
loop_behavior,
segment,
true,
);
assert!(op.init(&geometry).is_ok());
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE));
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE - 1));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_ne!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 0);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 2],
GainSTMMode::PhaseIntensityFull as u8
);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 3], segment as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 4], (freq_div & 0xFF) as u8);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 5],
((freq_div >> 8) & 0xFF) as u8
);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 6],
((freq_div >> 16) & 0xFF) as u8
);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 7],
((freq_div >> 24) & 0xFF) as u8
);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 8], 0xFF);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 9], 0xFF);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 10], 0xFF);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 11], 0xFF);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMHead>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[0][&dev.idx()].iter())
.for_each(|(d, g)| {
assert_eq!(d[0], g.phase().value());
assert_eq!(d[1], g.intensity().value());
})
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE - 2));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_eq!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 0);
tx[FRAME_SIZE * dev.idx() + 2..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[1][&dev.idx()].iter())
.for_each(|(d, g)| {
assert_eq!(d[0], g.phase().value());
assert_eq!(d[1], g.intensity().value());
})
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), 0));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_eq!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_ne!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_ne!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 0);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMSubseq>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[2][&dev.idx()].iter())
.for_each(|(d, g)| {
assert_eq!(d[0], g.phase().value());
assert_eq!(d[1], g.intensity().value());
})
});
}
#[test]
fn gain_stm_phase_full_op() {
const GAIN_STM_SIZE: usize = 5;
const FRAME_SIZE: usize = std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2;
let geometry = create_geometry(NUM_DEVICE, NUM_TRANS_IN_UNIT);
let mut tx = vec![0x00u8; FRAME_SIZE * NUM_DEVICE];
let mut rng = rand::thread_rng();
let gain_data: Vec<HashMap<usize, Vec<Drive>>> = (0..GAIN_STM_SIZE)
.map(|_| {
geometry
.devices()
.map(|dev| {
(
dev.idx(),
(0..dev.num_transducers())
.map(|_| {
Drive::new(
Phase::new(rng.gen_range(0x00..=0xFF)),
EmitIntensity::new(rng.gen_range(0..=0xFF)),
)
})
.collect(),
)
})
.collect()
})
.collect();
let gains: Vec<TestGain> = (0..GAIN_STM_SIZE)
.map(|i| TestGain {
data: gain_data[i].clone(),
})
.collect();
let freq_div: u32 = rng.gen_range(SAMPLING_FREQ_DIV_MIN..SAMPLING_FREQ_DIV_MAX);
let loop_behavior = LoopBehavior::Finite(
NonZeroU32::new(rng.gen_range(0x0000001..=0xFFFFFFFF)).unwrap_or(NonZeroU32::MIN),
);
let rep = loop_behavior.to_rep();
let segment = Segment::S1;
let mut op = GainSTMOp::<_>::new(
gains,
GainSTMMode::PhaseFull,
freq_div,
loop_behavior,
segment,
false,
);
assert!(op.init(&geometry).is_ok());
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE));
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE - 2));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_ne!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 1);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 2], GainSTMMode::PhaseFull as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 3], segment as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 4], (freq_div & 0xFF) as u8);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 5],
((freq_div >> 8) & 0xFF) as u8
);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 6],
((freq_div >> 16) & 0xFF) as u8
);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 7],
((freq_div >> 24) & 0xFF) as u8
);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 8], (rep & 0xFF) as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 9], ((rep >> 8) & 0xFF) as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 10], ((rep >> 16) & 0xFF) as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 11], ((rep >> 24) & 0xFF) as u8);
tx[FRAME_SIZE * dev.idx() + 12..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[0][&dev.idx()].iter())
.zip(gain_data[1][&dev.idx()].iter())
.for_each(|((d, g0), g1)| {
assert_eq!(d[0], g0.phase().value());
assert_eq!(d[1], g1.phase().value());
})
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE - 4));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_eq!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 1);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMSubseq>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[2][&dev.idx()].iter())
.zip(gain_data[3][&dev.idx()].iter())
.for_each(|((d, g0), g1)| {
assert_eq!(d[0], g0.phase().value());
assert_eq!(d[1], g1.phase().value());
})
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), 0));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_eq!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_ne!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 0);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMSubseq>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[4][&dev.idx()].iter())
.for_each(|(d, g)| {
assert_eq!(d[0], g.phase().value());
})
});
}
#[test]
fn gain_stm_phase_half_op() {
const GAIN_STM_SIZE: usize = 11;
const FRAME_SIZE: usize = std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2;
let geometry = create_geometry(NUM_DEVICE, NUM_TRANS_IN_UNIT);
let mut tx = vec![0x00u8; FRAME_SIZE * NUM_DEVICE];
let mut rng = rand::thread_rng();
let gain_data: Vec<HashMap<usize, Vec<Drive>>> = (0..GAIN_STM_SIZE)
.map(|_| {
geometry
.devices()
.map(|dev| {
(
dev.idx(),
(0..dev.num_transducers())
.map(|_| {
Drive::new(
Phase::new(rng.gen_range(0x00..=0xFF)),
EmitIntensity::new(rng.gen_range(0..=0xFF)),
)
})
.collect(),
)
})
.collect()
})
.collect();
let gains: Vec<TestGain> = (0..GAIN_STM_SIZE)
.map(|i| TestGain {
data: gain_data[i].clone(),
})
.collect();
let freq_div: u32 = rng.gen_range(SAMPLING_FREQ_DIV_MIN..SAMPLING_FREQ_DIV_MAX);
let loop_behavior = LoopBehavior::Finite(
NonZeroU32::new(rng.gen_range(0x0000001..=0xFFFFFFFF)).unwrap_or(NonZeroU32::MIN),
);
let rep = loop_behavior.to_rep();
let segment = Segment::S0;
let mut op = GainSTMOp::<_>::new(
gains,
GainSTMMode::PhaseHalf,
freq_div,
loop_behavior,
segment,
true,
);
assert!(op.init(&geometry).is_ok());
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE));
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMHead>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE - 4));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_ne!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 3);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 2], GainSTMMode::PhaseHalf as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 3], segment as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 4], (freq_div & 0xFF) as u8);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 5],
((freq_div >> 8) & 0xFF) as u8
);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 6],
((freq_div >> 16) & 0xFF) as u8
);
assert_eq!(
tx[dev.idx() * FRAME_SIZE + 7],
((freq_div >> 24) & 0xFF) as u8
);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 8], (rep & 0xFF) as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 9], ((rep >> 8) & 0xFF) as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 10], ((rep >> 16) & 0xFF) as u8);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 11], ((rep >> 24) & 0xFF) as u8);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMHead>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[0][&dev.idx()].iter())
.zip(gain_data[1][&dev.idx()].iter())
.zip(gain_data[2][&dev.idx()].iter())
.zip(gain_data[3][&dev.idx()].iter())
.for_each(|((((d, g0), g1), g2), g3)| {
assert_eq!(d[0] & 0x0F, g0.phase().value() >> 4);
assert_eq!(d[0] >> 4, g1.phase().value() >> 4);
assert_eq!(d[1] & 0x0F, g2.phase().value() >> 4);
assert_eq!(d[1] >> 4, g3.phase().value() >> 4);
})
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), GAIN_STM_SIZE - 8));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_eq!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_eq!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 3);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMSubseq>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[4][&dev.idx()].iter())
.zip(gain_data[5][&dev.idx()].iter())
.zip(gain_data[6][&dev.idx()].iter())
.zip(gain_data[7][&dev.idx()].iter())
.for_each(|((((d, g0), g1), g2), g3)| {
assert_eq!(d[0] & 0x0F, g0.phase().value() >> 4);
assert_eq!(d[0] >> 4, g1.phase().value() >> 4);
assert_eq!(d[1] & 0x0F, g2.phase().value() >> 4);
assert_eq!(d[1] >> 4, g3.phase().value() >> 4);
})
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.required_size(dev),
std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2
)
});
geometry.devices().for_each(|dev| {
assert_eq!(
op.pack(dev, &mut tx[dev.idx() * FRAME_SIZE..]),
Ok(std::mem::size_of::<GainSTMSubseq>() + NUM_TRANS_IN_UNIT * 2)
);
op.commit(dev);
});
geometry
.devices()
.for_each(|dev| assert_eq!(op.remains(dev), 0));
geometry.devices().for_each(|dev| {
assert_eq!(tx[dev.idx() * FRAME_SIZE], TypeTag::GainSTM as u8);
let flag = tx[dev.idx() * FRAME_SIZE + 1];
assert_eq!(flag & GainSTMControlFlags::BEGIN.bits(), 0x00);
assert_ne!(flag & GainSTMControlFlags::END.bits(), 0x00);
assert_ne!(flag & GainSTMControlFlags::UPDATE.bits(), 0x00);
assert_eq!(tx[dev.idx() * FRAME_SIZE + 1] >> 6, 2);
tx[FRAME_SIZE * dev.idx() + std::mem::size_of::<GainSTMSubseq>()..]
.chunks(std::mem::size_of::<FPGADrive>())
.zip(gain_data[8][&dev.idx()].iter())
.for_each(|(d, g)| {
assert_eq!(d[0] & 0x0F, g.phase().value() >> 4);
})
});
}
#[test]
fn gain_stm_op_buffer_out_of_range() {
let geometry = create_geometry(NUM_DEVICE, NUM_TRANS_IN_UNIT);
let test = |n: usize| {
let gains: Vec<NullGain> = (0..n).map(|_| NullGain {}).collect();
let mut op = GainSTMOp::<_>::new(
gains,
GainSTMMode::PhaseIntensityFull,
SAMPLING_FREQ_DIV_MIN,
LoopBehavior::Infinite,
Segment::S0,
true,
);
op.init(&geometry)
};
assert_eq!(test(1), Err(AUTDInternalError::GainSTMSizeOutOfRange(1)));
assert_eq!(test(2), Ok(()));
assert_eq!(test(GAIN_STM_BUF_SIZE_MAX), Ok(()));
assert_eq!(
test(GAIN_STM_BUF_SIZE_MAX + 1),
Err(AUTDInternalError::GainSTMSizeOutOfRange(
GAIN_STM_BUF_SIZE_MAX + 1
))
);
}
}