#![allow(dead_code)]
pub const RA_SAMPLE_COUNT: usize = 8;
#[derive(Debug, Clone, Copy)]
pub struct CriSampleResult {
pub sample_index: u8,
pub special_ri: f32,
}
#[derive(Debug, Clone)]
pub struct CriData {
pub light_name: String,
pub cct_kelvin: f32,
pub ra: f32,
pub samples: Vec<CriSampleResult>,
}
impl CriData {
pub fn new(light_name: impl Into<String>, cct_kelvin: f32, ra: f32) -> Self {
Self {
light_name: light_name.into(),
cct_kelvin,
ra,
samples: Vec::new(),
}
}
pub fn add_sample(&mut self, sample: CriSampleResult) {
self.samples.push(sample);
}
pub fn min_special_ri(&self) -> f32 {
self.samples
.iter()
.map(|s| s.special_ri)
.fold(f32::INFINITY, f32::min)
}
}
pub fn passes_ra_threshold(data: &CriData, min_ra: f32) -> bool {
data.ra >= min_ra
}
pub fn export_cri_report(data: &CriData) -> String {
let mut out = format!(
"Light: {}\nCCT: {} K\nCRI Ra: {:.1}\n",
data.light_name, data.cct_kelvin, data.ra
);
for s in &data.samples {
out.push_str(&format!(" R{}: {:.1}\n", s.sample_index, s.special_ri));
}
out
}
pub fn validate_cri_data(data: &CriData) -> bool {
(0.0..=100.0).contains(&data.ra) && data.cct_kelvin > 0.0
}
pub fn compute_ra(special_ris: &[f32; 8]) -> f32 {
let sum: f32 = special_ris.iter().sum();
(sum / 8.0).clamp(0.0, 100.0)
}
pub fn ra_classification(ra: f32) -> &'static str {
if ra >= 90.0 {
"Excellent"
} else if ra >= 80.0 {
"Good"
} else if ra >= 60.0 {
"Fair"
} else {
"Poor"
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_cri() -> CriData {
CriData::new("LED A", 4000.0, 95.0)
}
#[test]
fn test_new_cri_data() {
assert!((make_cri().cct_kelvin - 4000.0).abs() < f32::EPSILON);
}
#[test]
fn test_passes_ra_threshold_true() {
assert!(passes_ra_threshold(&make_cri(), 80.0));
}
#[test]
fn test_passes_ra_threshold_false() {
assert!(!passes_ra_threshold(&make_cri(), 98.0));
}
#[test]
fn test_validate_cri_data_valid() {
assert!(validate_cri_data(&make_cri()));
}
#[test]
fn test_validate_cri_data_negative_cct() {
let mut d = make_cri();
d.cct_kelvin = -100.0;
assert!(!validate_cri_data(&d));
}
#[test]
fn test_export_cri_report_contains_name() {
assert!(export_cri_report(&make_cri()).contains("LED A"));
}
#[test]
fn test_compute_ra_uniform() {
let ris = [100.0f32; 8];
assert!((compute_ra(&ris) - 100.0).abs() < f32::EPSILON);
}
#[test]
fn test_compute_ra_clamps() {
let ris = [110.0f32; 8];
assert_eq!(compute_ra(&ris), 100.0);
}
#[test]
fn test_ra_classification_excellent() {
assert_eq!(ra_classification(95.0), "Excellent");
}
#[test]
fn test_ra_classification_poor() {
assert_eq!(ra_classification(50.0), "Poor");
}
}