wxbox/crates/ar2/src/sites/wsr88d.rs
core e4dcb48878
Some checks are pending
Verify Latest Dependencies / Verify Latest Dependencies (push) Waiting to run
build and test / wxbox - latest (push) Waiting to run
chore: praise be the clippy gods
2025-05-19 10:38:23 -04:00

116 lines
3.3 KiB
Rust

use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use std::sync::LazyLock;
#[derive(Deserialize, Debug)]
pub struct Wsr88dSite {
pub id: String,
pub name: String,
pub agency: String,
pub equipment: String,
pub city: String,
pub state: String,
pub county: String,
#[serde(deserialize_with = "from_elevation_str")]
pub elevation: f64,
#[serde(deserialize_with = "from_deg_str")]
pub lat: f64,
#[serde(deserialize_with = "from_deg_str")]
pub long: f64,
}
#[derive(Deserialize)]
pub struct Wsr88dSites {
pub sites: HashMap<String, Wsr88dSite>,
}
fn from_elevation_str<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
let elevation = s.split(" ").next().unwrap();
Ok(elevation.parse().unwrap())
}
fn from_deg_str<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
let mut split = s.split(" ");
let mut hr = split.next().unwrap();
let positive = hr.starts_with('+');
if positive {
hr = hr.strip_prefix('+').unwrap();
} else {
hr = hr.strip_prefix('-').unwrap();
}
let hr: f64 = hr.parse().unwrap();
let min = split.next().unwrap();
let min: f64 = min.parse().unwrap();
let sec = split.next().unwrap();
let sec: f64 = sec.parse().unwrap();
let mut degrees = hr + min / 60.0 + sec / 3600.0;
if !positive {
degrees *= -1.0;
}
Ok(degrees)
}
pub static SITES: LazyLock<Wsr88dSites> =
LazyLock::new(|| toml::from_str(include_str!("wsr88d.toml")).unwrap());
#[cfg(test)]
mod tests {
use crate::sites::wsr88d::SITES;
const A: f64 = 6_378.1370 * 1000.0; // m
const B: f64 = 6_356.7523 * 1000.0; // m
#[test]
fn azimuth_configurations() {
let radar = SITES.sites.get("KCRP").unwrap();
let radar_theta = radar.long;
let radar_phi = radar.lat;
let radar_r = ((A.powi(2) * radar_phi.cos()).powi(2)
+ (B.powi(2) * radar_phi.sin()).powi(2) / (A * radar_phi.cos()).powi(2)
+ (B * radar_phi.sin()).powi(2))
.sqrt();
let radar_x = radar_r * radar_theta.cos() * radar_phi.sin();
let radar_y = radar_r * radar_theta.sin() * radar_phi.sin();
let radar_z = radar_r * radar_theta.cos();
let measurement_theta = radar_theta + 0.0001;
let measurement_phi = radar_phi;
let measurement_r = radar_r;
let measurement_x = measurement_r * measurement_theta.cos() * measurement_phi.sin();
let measurement_y = measurement_r * measurement_theta.sin() * measurement_phi.sin();
let measurement_z = measurement_r * measurement_theta.cos();
let radar_local_x = measurement_x - radar_x;
let radar_local_y = measurement_y - radar_y;
let radar_local_z = measurement_z - radar_z;
let radar_local_r =
(radar_local_x.powi(2) + radar_local_y.powi(2) + radar_local_z.powi(2)).sqrt();
let radar_local_theta = (radar_local_y / radar_local_x).atan();
let radar_local_phi = (radar_local_z / radar_local_r).acos();
let azimuth = radar_local_theta;
let elevation = radar_local_phi;
let distance = radar_local_r;
}
}