humpback-dds/src/attenuator.rs

130 lines
3.8 KiB
Rust

use core::assert;
use embedded_hal::blocking::spi::Transfer;
use crate::urukul::Error;
pub struct Attenuator<SPI> {
spi: SPI,
data: [u8; 4],
}
impl<SPI, E> Attenuator<SPI>
where
SPI: Transfer<u8, Error = E>,
{
pub fn new(spi: SPI) -> Self {
Attenuator {
spi,
// data[y] refers to the yth byte for SPI communication
data: [0, 0, 0, 0],
}
}
/*
* Set attenuations of all attenuators
* att[x] refers to the attenuation for channel x
*/
pub fn set_attenuation(&mut self, att: [f32; 4]) -> Result<(), Error<E>> {
for i in 0..4 {
let mut atten = att[i];
if att[i] > 31.5 {
atten = 31.5;
}
if att[i] < 0.0 {
atten = 0.0;
}
// Set data as attenuation * 2
// Flip data using bitwise XOR, active low data
// Data is most signifant attenuator first
self.data[3 - i] = (((atten * 2.0) as u8) ^ 0xFF) << 2
}
let mut clone = self.data.clone();
// Transmit SPI once to set attenuation
self.spi
.transfer(&mut clone)
.map(|_| ())
.map_err(|_| Error::AttenuatorError)
}
pub fn set_channel_attenuation(
&mut self,
channel: u8,
attenuation: f32,
) -> Result<(), Error<E>> {
assert!(channel < 4);
let mut arr: [f32; 4] = self.get_attenuation()?;
arr[channel as usize] = attenuation;
self.set_attenuation(arr).map(|_| ())
}
pub fn get_channel_attenuation(&mut self, channel: u8) -> Result<f32, Error<E>> {
assert!(channel < 4);
match self.get_attenuation() {
Ok(arr) => Ok(arr[channel as usize]),
Err(e) => Err(e),
}
}
pub fn get_attenuation(&mut self) -> Result<[f32; 4], Error<E>> {
let mut clone = self.data.clone();
match self.spi.transfer(&mut clone).map_err(Error::SPI) {
Ok(arr) => {
let mut ret: [f32; 4] = [0.0; 4];
for index in 0..4 {
ret[index] = ((arr[3 - index] ^ 0xFC) as f32) / 8.0;
}
Ok(ret)
}
Err(e) => Err(e),
}
}
/*
* Test method for Attenuators.
* Return the number of test failed.
*/
pub fn test(&mut self) -> Result<u32, Error<E>> {
// Test attenuators by getting back the attenuation
let mut error_count = 0;
// Convert cached SPI data into attenuation floats
let att_floats: [f32; 4] = [
((self.data[3] ^ 0xFC) as f32) / 8.0,
((self.data[2] ^ 0xFC) as f32) / 8.0,
((self.data[1] ^ 0xFC) as f32) / 8.0,
((self.data[0] ^ 0xFC) as f32) / 8.0,
];
// Set the attenuation to an arbitrary value, then read the attenuation
self.set_attenuation([3.5, 9.5, 20.0, 28.5])?;
match self.get_attenuation() {
Ok(arr) => {
if arr[0] != 3.5 {
error_count += 1;
}
if arr[1] != 9.5 {
error_count += 1;
}
if arr[2] != 20.0 {
error_count += 1;
}
if arr[3] != 28.5 {
error_count += 1;
}
}
Err(_) => return Err(Error::AttenuatorError),
};
self.set_attenuation(att_floats)?;
Ok(error_count)
}
}
impl<SPI, E> Transfer<u8> for Attenuator<SPI>
where
SPI: Transfer<u8, Error = E>,
{
type Error = Error<E>;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
self.spi.transfer(words).map_err(Error::SPI)
}
}