1use serde::{Deserialize, Serialize};
7use std::sync::OnceLock;
8
9const SENSORS_JSON: &str = include_str!("../data/sensors.json");
10
11static SENSOR_DB: OnceLock<Vec<SensorEntry>> = OnceLock::new();
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct SensorEntry {
16 pub model: String,
18 pub manufacturer: String,
20 pub sensor_type: String,
22 #[serde(default)]
24 pub description: Option<String>,
25 pub sensitivity: f64,
27 pub sensitivity_unit: String,
29 pub frequency_range: (f64, f64),
31 pub natural_period: Option<f64>,
33 pub damping: Option<f64>,
35}
36
37pub fn load_sensor_library() -> &'static [SensorEntry] {
42 SENSOR_DB
43 .get_or_init(|| serde_json::from_str(SENSORS_JSON).expect("embedded sensors.json is valid"))
44}
45
46pub fn find_sensor(model: &str) -> Option<&'static SensorEntry> {
50 let model_lower = model.to_lowercase();
51 load_sensor_library()
52 .iter()
53 .find(|s| s.model.to_lowercase() == model_lower)
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn load_library_count() {
62 let sensors = load_sensor_library();
63 assert_eq!(sensors.len(), 9);
64 }
65
66 #[test]
67 fn find_gs11d() {
68 let sensor = find_sensor("GS-11D").unwrap();
69 assert_eq!(sensor.manufacturer, "Geospace");
70 assert_eq!(sensor.sensitivity, 32.0);
71 assert_eq!(sensor.sensitivity_unit, "M/S");
72 }
73
74 #[test]
75 fn find_case_insensitive() {
76 assert!(find_sensor("gs-11d").is_some());
77 assert!(find_sensor("sts-2").is_some());
78 assert!(find_sensor("STS-2").is_some());
79 }
80
81 #[test]
82 fn find_nonexistent() {
83 assert!(find_sensor("NonExistentSensor").is_none());
84 }
85
86 #[test]
87 fn all_entries_valid() {
88 for sensor in load_sensor_library() {
89 assert!(sensor.sensitivity > 0.0, "sensitivity must be positive");
90 assert!(!sensor.model.is_empty(), "model must not be empty");
91 assert!(!sensor.manufacturer.is_empty());
92 assert!(
93 sensor.frequency_range.0 < sensor.frequency_range.1,
94 "freq range must be low < high"
95 );
96 }
97 }
98
99 #[test]
100 fn broadband_vs_geophone() {
101 let sts2 = find_sensor("STS-2").unwrap();
102 let gs11d = find_sensor("GS-11D").unwrap();
103
104 assert!(sts2.sensitivity > gs11d.sensitivity);
106 assert!(sts2.frequency_range.0 < gs11d.frequency_range.0);
108 }
109}