use std::collections::HashMap;
use amari_holographic::optical::BinaryHologram;
use super::now_timestamp;
#[derive(Clone, Debug)]
pub struct OpticalMeasurement {
pub mode_amplitudes: Vec<f32>,
pub total_intensity: f32,
pub timestamp: u64,
}
impl OpticalMeasurement {
pub fn new(mode_amplitudes: Vec<f32>) -> Self {
let total_intensity = mode_amplitudes.iter().sum();
Self {
mode_amplitudes,
total_intensity,
timestamp: now_timestamp(),
}
}
pub fn n_modes(&self) -> usize {
self.mode_amplitudes.len()
}
pub fn normalized_amplitudes(&self) -> Vec<f32> {
if self.total_intensity > 0.0 {
self.mode_amplitudes
.iter()
.map(|a| a / self.total_intensity)
.collect()
} else {
vec![0.0; self.mode_amplitudes.len()]
}
}
pub fn dominant_mode(&self) -> Option<usize> {
self.mode_amplitudes
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i)
}
}
#[derive(Clone, Debug)]
pub struct HardwareCalibration {
pub pattern_cache: HashMap<u64, BinaryHologram>,
pub calibration_temperature: f32,
pub calibrated_at: u64,
}
impl HardwareCalibration {
pub fn new(temperature: f32) -> Self {
Self {
pattern_cache: HashMap::new(),
calibration_temperature: temperature,
calibrated_at: now_timestamp(),
}
}
pub fn has_pattern(&self, hash: u64) -> bool {
self.pattern_cache.contains_key(&hash)
}
pub fn get_pattern(&self, hash: u64) -> Option<&BinaryHologram> {
self.pattern_cache.get(&hash)
}
pub fn cache_pattern(&mut self, hash: u64, hologram: BinaryHologram) {
self.pattern_cache.insert(hash, hologram);
}
pub fn age_seconds(&self) -> u64 {
let now = now_timestamp();
if now > self.calibrated_at {
(now - self.calibrated_at) / 1000
} else {
0
}
}
pub fn is_stale(&self, max_age_seconds: u64) -> bool {
self.age_seconds() > max_age_seconds
}
}
#[derive(Debug)]
pub enum HardwareError {
Communication(String),
NotReady,
CalibrationRequired,
MeasurementFailed(String),
DisplayFailed(String),
TemperatureOutOfRange {
current: f32,
min: f32,
max: f32,
},
Timeout(String),
Other(String),
}
impl std::fmt::Display for HardwareError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Communication(msg) => write!(f, "hardware communication error: {msg}"),
Self::NotReady => write!(f, "hardware not ready"),
Self::CalibrationRequired => write!(f, "calibration required"),
Self::MeasurementFailed(msg) => write!(f, "measurement failed: {msg}"),
Self::DisplayFailed(msg) => write!(f, "pattern display failed: {msg}"),
Self::TemperatureOutOfRange { current, min, max } => {
write!(f, "temperature {current} out of range [{min}, {max}]")
}
Self::Timeout(msg) => write!(f, "hardware timeout: {msg}"),
Self::Other(msg) => write!(f, "hardware error: {msg}"),
}
}
}
impl std::error::Error for HardwareError {}
pub trait OpticalHardware: Send {
fn id(&self) -> &str;
fn dimensions(&self) -> (usize, usize);
fn n_modes(&self) -> usize;
fn temperature(&self) -> Result<f32, HardwareError>;
fn display(&mut self, hologram: &BinaryHologram) -> Result<(), HardwareError>;
fn measure(&mut self) -> Result<OpticalMeasurement, HardwareError>;
fn quick_calibrate(&mut self) -> Result<HardwareCalibration, HardwareError>;
fn full_calibrate(&mut self) -> Result<HardwareCalibration, HardwareError>;
fn is_ready(&self) -> bool;
fn reset(&mut self) -> Result<(), HardwareError> {
Ok(())
}
fn diagnostics(&self) -> HashMap<String, String> {
HashMap::new()
}
}