use crate::{app::AppState, fpga::InterruptWaiter};
use anyhow::Result;
use bytes::Bytes;
use maia_json::SpectrometerMode;
use std::sync::Mutex;
use tokio::sync::broadcast;
const BASE_SCALE: f32 = 4e6;
#[derive(Debug)]
pub struct Spectrometer {
state: AppState,
sender: broadcast::Sender<Bytes>,
interrupt: InterruptWaiter,
}
#[derive(Debug)]
pub struct SpectrometerConfig(Mutex<Config>);
#[derive(Debug, Clone)]
struct Config {
samp_rate: f32,
mode: SpectrometerMode,
}
impl Spectrometer {
pub fn new(
state: AppState,
interrupt: InterruptWaiter,
sender: broadcast::Sender<Bytes>,
) -> Spectrometer {
Spectrometer {
state,
interrupt,
sender,
}
}
#[tracing::instrument(name = "spectrometer", skip_all)]
pub async fn run(self) -> Result<()> {
loop {
self.interrupt.wait().await;
let (samp_rate, mode) = self.state.spectrometer_config().samp_rate_mode();
let mut ip_core = self.state.ip_core().lock().unwrap();
let num_integrations = ip_core.spectrometer_number_integrations() as f32;
let scale = match mode {
SpectrometerMode::Average => BASE_SCALE / (num_integrations * samp_rate),
SpectrometerMode::PeakDetect => BASE_SCALE / samp_rate,
};
tracing::trace!(
last_buffer = ip_core.spectrometer_last_buffer(),
samp_rate,
num_integrations,
scale
);
for buffer in ip_core.get_spectrometer_buffers() {
if self.sender.receiver_count() > 0 {
let _ = self.sender.send(Self::buffer_u64fp_to_f32(buffer, scale));
}
}
}
}
fn buffer_u64fp_to_f32(buffer: &[u64], scale: f32) -> Bytes {
buffer
.iter()
.flat_map(|&x| {
let exponent = (x >> 56) as u8;
let value = x & ((1u64 << 56) - 1);
let y = value << (2 * exponent);
let z = y as f32 * scale;
z.to_ne_bytes().into_iter()
})
.collect()
}
}
impl SpectrometerConfig {
fn new() -> SpectrometerConfig {
SpectrometerConfig(Mutex::new(Config {
samp_rate: 0.0,
mode: SpectrometerMode::Average,
}))
}
pub fn samp_rate(&self) -> f32 {
self.0.lock().unwrap().samp_rate
}
pub fn mode(&self) -> SpectrometerMode {
self.0.lock().unwrap().mode
}
pub fn samp_rate_mode(&self) -> (f32, SpectrometerMode) {
let conf = self.0.lock().unwrap();
(conf.samp_rate, conf.mode)
}
pub fn set_samp_rate(&self, samp_rate: f32) {
self.0.lock().unwrap().samp_rate = samp_rate;
}
pub fn set_mode(&self, mode: SpectrometerMode) {
self.0.lock().unwrap().mode = mode;
}
pub fn set_samp_rate_mode(&self, samp_rate: f32, mode: SpectrometerMode) {
let mut conf = self.0.lock().unwrap();
conf.samp_rate = samp_rate;
conf.mode = mode;
}
}
impl Default for SpectrometerConfig {
fn default() -> SpectrometerConfig {
SpectrometerConfig::new()
}
}