116 lines
3.3 KiB
Rust
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;
|
|
}
|
|
}
|