use std::path::Path;
use crate::SphericalHarmonicsData;
#[derive(Debug, thiserror::Error)]
pub enum CoeffLoadError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("missing field '{field}' in {path}")]
MissingField {
field: &'static str,
path: String,
},
}
pub fn load_mu_from_jeod_cc(path: &Path) -> Result<f64, CoeffLoadError> {
let content = std::fs::read_to_string(path)?;
let path_str = path.display().to_string();
for line in content.lines() {
if let Some(val) = extract_assign_f64(line.trim(), "mu") {
return Ok(val);
}
}
Err(CoeffLoadError::MissingField {
field: "mu",
path: path_str,
})
}
pub fn load_from_jeod_cc(path: &Path) -> Result<SphericalHarmonicsData, CoeffLoadError> {
let content = std::fs::read_to_string(path)?;
let path_str = path.display().to_string();
let mut degree: Option<usize> = None;
let mut order: Option<usize> = None;
let mut mu: Option<f64> = None;
let mut radius: Option<f64> = None;
let mut tide_free: Option<bool> = None;
let mut tide_free_delta: Option<f64> = None;
for line in content.lines() {
let line = line.trim();
if let Some(val) = extract_assign_usize(line, "degree") {
degree = Some(val);
}
if let Some(val) = extract_assign_usize(line, "order") {
order = Some(val);
}
if let Some(val) = extract_assign_f64(line, "mu") {
mu = Some(val);
}
if let Some(val) = extract_assign_f64(line, "radius") {
radius = Some(val);
}
if line.contains("tide_free") && !line.contains("tide_free_delta") {
if line.contains("true") {
tide_free = Some(true);
} else if line.contains("false") {
tide_free = Some(false);
}
}
if let Some(val) = extract_assign_f64(line, "tide_free_delta") {
tide_free_delta = Some(val);
}
}
let degree = degree.ok_or_else(|| CoeffLoadError::MissingField {
field: "degree",
path: path_str.clone(),
})?;
let order = order.ok_or_else(|| CoeffLoadError::MissingField {
field: "order",
path: path_str.clone(),
})?;
let mu = mu.ok_or_else(|| CoeffLoadError::MissingField {
field: "mu",
path: path_str.clone(),
})?;
let radius = radius.ok_or(CoeffLoadError::MissingField {
field: "radius",
path: path_str,
})?;
let tide_free = tide_free.unwrap_or(true);
let tide_free_delta = tide_free_delta.unwrap_or(0.0);
let mut cnm: Vec<Vec<f64>> = Vec::with_capacity(degree + 1);
let mut snm: Vec<Vec<f64>> = Vec::with_capacity(degree + 1);
for n in 0..=degree {
cnm.push(vec![0.0; n + 1]);
snm.push(vec![0.0; n + 1]);
}
for line in content.lines() {
let line = line.trim();
if let Some((n, m, val)) = extract_coeff(line, "Cnm") {
if n <= degree && m <= n {
cnm[n][m] = val;
}
}
if let Some((n, m, val)) = extract_coeff(line, "Snm") {
if n <= degree && m <= n {
snm[n][m] = val;
}
}
}
Ok(SphericalHarmonicsData::new(
degree,
order,
radius,
mu,
cnm,
snm,
tide_free,
tide_free_delta,
))
}
fn extract_assign_usize(line: &str, key: &str) -> Option<usize> {
let pattern = format!("->{} = ", key);
if let Some(idx) = line.find(&pattern) {
let rest = &line[idx + pattern.len()..];
let val_str: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
val_str.parse().ok()
} else {
None
}
}
fn extract_assign_f64(line: &str, key: &str) -> Option<f64> {
let pattern = format!("->{} = ", key);
if let Some(idx) = line.find(&pattern) {
let rest = &line[idx + pattern.len()..];
let expr: String = rest.chars().take_while(|c| *c != ';').collect();
eval_simple_expr(&expr)
} else {
None
}
}
fn eval_simple_expr(expr: &str) -> Option<f64> {
let expr = expr.trim();
if let Ok(val) = expr.parse::<f64>() {
return Some(val);
}
if let Some(star_idx) = expr.find('*') {
let lhs = expr[..star_idx].trim();
let rhs = expr[star_idx + 1..]
.trim()
.trim_matches(|c| c == '(' || c == ')');
if let (Ok(a), Ok(b)) = (lhs.parse::<f64>(), rhs.trim().parse::<f64>()) {
return Some(a * b);
}
}
None
}
fn extract_coeff(line: &str, name: &str) -> Option<(usize, usize, f64)> {
let prefix = format!("{}[", name);
let start = line.find(&prefix)?;
let rest = &line[start + prefix.len()..];
let n_str: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
let n: usize = n_str.parse().ok()?;
let bracket_pos = rest.find("][")?;
let after_bracket = &rest[bracket_pos + 2..];
let m_str: String = after_bracket
.chars()
.take_while(|c| c.is_ascii_digit())
.collect();
let m: usize = m_str.parse().ok()?;
let eq_pos = after_bracket.find('=')?;
let val_rest = after_bracket[eq_pos + 1..].trim();
let val_str: String = val_rest
.chars()
.take_while(|c| {
*c == '-' || *c == '+' || *c == '.' || *c == 'E' || *c == 'e' || c.is_ascii_digit()
})
.collect();
let val: f64 = val_str.parse().ok()?;
Some((n, m, val))
}