use crate::conversions::z_from_p;
use crate::gsw_internal_const::{DEG2RAD, GAMMA};
use crate::{Error, Result};
pub fn coriollis_parameter(lat: f64) -> Result<f64> {
if !(-90.0..=90.0).contains(&lat) {
if lat.is_nan() {
return Ok(f64::NAN);
} else {
return Err(Error::OutOfBounds);
}
}
let omega = 7.292115e-5;
Ok(2.0 * omega * libm::sin(lat * DEG2RAD))
}
#[cfg(test)]
mod test_coriollis_parameter {
use super::{coriollis_parameter, Error};
#[test]
fn poles() {
let f = coriollis_parameter(90.0).unwrap();
assert!((f - 0.0001458423).abs() <= f64::EPSILON);
let f = coriollis_parameter(-90.0).unwrap();
assert!((f + 0.0001458423).abs() <= f64::EPSILON);
}
#[test]
fn equator() {
let f = coriollis_parameter(0.0).unwrap();
assert!((f).abs() <= f64::EPSILON);
}
#[test]
fn nan() {
let f = coriollis_parameter(f64::NAN).unwrap();
assert!(f.is_nan());
}
#[test]
fn out_of_limites() {
for lat in [-91.0, 91.0] {
let f = coriollis_parameter(lat);
match f {
Err(Error::OutOfBounds) => (),
_ => panic!("Out of limits. It should be Error::OutOfBounds"),
}
}
}
}
pub fn gravity(lat: f64, p: f64) -> Result<f64> {
if !(-90.0..=90.0).contains(&lat) {
if lat.is_nan() {
return Ok(f64::NAN);
} else {
return Err(Error::OutOfBounds);
}
}
let sinlat = libm::sin(lat * DEG2RAD);
let sin2 = sinlat * sinlat;
let gs = 9.780327 * (1.0 + (5.2792e-3 + (2.32e-5 * sin2)) * sin2);
if cfg!(feature = "nodgdz") {
return Ok(gs);
}
let z = z_from_p(p, lat, 0.0, 0.0);
Ok(gs * (1.0 - GAMMA * z))
}
#[cfg(test)]
mod test_gravity {
use super::{gravity, Error};
#[test]
fn nan() {
let g = gravity(f64::NAN, 0.0).unwrap();
assert!(g.is_nan());
let g = gravity(0.0, f64::NAN).unwrap();
assert!(g.is_nan());
}
#[test]
fn out_of_limites() {
for lat in [-91.0, 91.0] {
let g = gravity(lat, 0.0);
match g {
Err(Error::OutOfBounds) => (),
_ => panic!("Out of limits. It should be Error::OutOfBounds"),
}
}
}
}
pub fn distance(lon1: f64, lat1: f64, p1: f64, lon2: f64, lat2: f64, p2: f64) -> Result<f64> {
if !(-90.0..=90.0).contains(&lat1) | !(-90.0..=90.0).contains(&lat2) {
if lat1.is_nan() || lat2.is_nan() {
return Ok(f64::NAN);
} else {
return Err(Error::OutOfBounds);
}
}
let earth_radius = 6371000.0;
let dlon = (lon2 - lon1) * DEG2RAD;
let dlat = (lat2 - lat1) * DEG2RAD;
let sdlat = libm::sin(0.5 * dlat);
let sdlon = libm::sin(0.5 * dlon);
let a =
(sdlat * sdlat) + libm::cos(lat1 * DEG2RAD) * libm::cos(lat2 * DEG2RAD) * (sdlon * sdlon);
let angles = 2.0 * libm::atan2(libm::sqrt(a), libm::sqrt(1.0 - a));
let p_mid = 0.5 * (p1 + p2);
let lat_mid = 0.5 * (lat1 + lat2);
let z = z_from_p(p_mid, lat_mid, 0.0, 0.0);
Ok((earth_radius + z) * angles)
}
#[cfg(test)]
mod test_distance {
use super::distance;
#[test]
fn distance_along_equator() {
let d = distance(0.0, 0.0, 0.0, 1.0, 0.0, 0.0).unwrap();
assert!((d - 111194.92664455874).abs() <= f64::EPSILON);
}
#[test]
fn distance_high_latitude() {
let d = distance(0.0, 75.0, 0.0, 1.0, 75.0, 0.0).unwrap();
assert!((d - 28779.023924964815).abs() <= f64::EPSILON);
}
#[test]
fn distance_with_depth() {
let d = distance(0.0, 0.0, 0.0, 0.0, 1.0, 1000.0).unwrap();
assert!((d - 111186.2584129753).abs() <= f64::EPSILON);
}
#[test]
fn distance_on_depth() {
let d = distance(0.0, 0.0, 0.0, 0.0, 0.0, 1000.0).unwrap();
assert!((d - 0.0).abs() <= f64::EPSILON);
}
#[test]
fn distance_depth_effect() {
let d_surf = distance(0.0, 0.0, 0.0, 0.0, 0.1, 0.0).unwrap();
let d_deep = distance(0.0, 0.0, 0.0, 0.0, 0.1, 4000.0).unwrap();
let diff = d_surf - d_deep;
assert!((diff - 3.4549677662762406).abs() <= f64::EPSILON);
}
}