use ::Access;
use ::enu::ENU;
use ::nvector::NVector;
use ::wgs84::{ECCENTRICITY_SQ, SEMI_MAJOR_AXIS, SEMI_MINOR_AXIS, WGS84};
use na::{Matrix3, Point3, Transpose};
use num_traits::Float;
use std::convert::{From, Into};
use std::ops::{Add, AddAssign, Sub, SubAssign};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ECEF<N>(Point3<N>);
impl<N> ECEF<N> {
pub fn new(x: N, y: N, z: N) -> ECEF<N> {
ECEF(Point3::new(x, y, z))
}
}
impl<N: Copy> ECEF<N> {
pub fn x(&self) -> N {
self.0.x
}
pub fn y(&self) -> N {
self.0.y
}
pub fn z(&self) -> N {
self.0.z
}
}
impl<N: Float> ECEF<N> {
fn r_en(self) -> Matrix3<N> {
let wgs = WGS84::from(self);
Matrix3::new(-wgs.longitude().sin(),
wgs.longitude().cos(),
N::zero(),
-wgs.latitude().sin() * wgs.longitude().cos(),
-wgs.latitude().sin() * wgs.longitude().sin(),
wgs.latitude().cos(),
wgs.latitude().cos() * wgs.longitude().cos(),
wgs.latitude().cos() * wgs.longitude().sin(),
wgs.latitude().sin())
}
fn r_ne(self) -> Matrix3<N> {
self.r_en().transpose()
}
}
impl<N, T> Add<T> for ECEF<N>
where N: Float,
T: Into<ENU<N>>
{
type Output = ECEF<N>;
fn add(self, right: T) -> ECEF<N> {
let enu = T::into(right);
let pos = self.0 + self.r_en() * enu.access();
ECEF(pos)
}
}
impl<N, T> AddAssign<T> for ECEF<N>
where N: Float + AddAssign,
T: Into<ENU<N>>
{
fn add_assign(&mut self, right: T) {
let enu = T::into(right);
self.0 += self.r_en() * enu.access();
}
}
impl<N: Float> Sub<ECEF<N>> for ECEF<N> {
type Output = ENU<N>;
fn sub(self, right: ECEF<N>) -> ENU<N> {
let vec = self.0 - right.0;
let mat = right.r_ne();
let enu = mat * vec;
ENU::new(enu.x, enu.y, enu.z)
}
}
impl<N, T> Sub<T> for ECEF<N>
where N: Float,
T: Into<ENU<N>>
{
type Output = ECEF<N>;
fn sub(self, right: T) -> ECEF<N> {
let enu = T::into(right);
let vec_e = self.r_en() * enu.access();
ECEF(self.0 - vec_e)
}
}
impl<N, T> SubAssign<T> for ECEF<N>
where N: Float + SubAssign,
T: Into<ENU<N>>
{
fn sub_assign(&mut self, right: T) {
let enu = T::into(right);
let vec_e = self.r_en() * enu.access();
self.0 -= vec_e;
}
}
macro_rules! ecef_impl {
($T:ident) => (
impl<N, T> Add<T> for $T<N>
where N: Float, T: Into<ENU<N>> {
type Output = $T<N>;
fn add(self, right: T) -> Self {
$T::from(ECEF::from(self) + right)
}
}
impl<N, T> AddAssign<T> for $T<N>
where N: Float + AddAssign, T: Into<ENU<N>> {
fn add_assign(&mut self, right: T) {
*self = $T::from(ECEF::from(*self) + right);
}
}
impl<N, T> Sub<T> for $T<N>
where N: Float, T: Into<ENU<N>> {
type Output = $T<N>;
fn sub(self, right: T) -> $T<N> {
$T::from(ECEF::from(self) - right)
}
}
impl<N: Float> Sub<$T<N>> for $T<N> {
type Output = ENU<N>;
fn sub(self, right: $T<N>) -> ENU<N> {
ECEF::from(self) - ECEF::from(right)
}
}
impl<N, T> SubAssign<T> for $T<N>
where N: Float + SubAssign, T: Into<ENU<N>> {
fn sub_assign(&mut self, right: T) {
*self = $T::from(ECEF::from(*self) - right);
}
}
)
}
ecef_impl!(NVector);
ecef_impl!(WGS84);
impl<N: Float> From<WGS84<N>> for ECEF<N> {
fn from(f: WGS84<N>) -> ECEF<N> {
let a = N::from(SEMI_MAJOR_AXIS).unwrap();
let ecc_part = N::from(ECCENTRICITY_SQ).unwrap();
let sin_part = N::from(0.5).unwrap() *
(N::one() - (N::from(2.0).unwrap() * f.latitude()).cos());
let n = a / (N::one() - ecc_part * sin_part).sqrt();
let h = f.altitude();
let x = (h + n) * f.latitude().cos() * f.longitude().cos();
let y = (h + n) * f.latitude().cos() * f.longitude().sin();
let z = (h + n - N::from(ECCENTRICITY_SQ).unwrap() * n) * f.latitude().sin();
ECEF::new(x, y, z)
}
}
impl<N: Float> From<NVector<N>> for ECEF<N> {
fn from(f: NVector<N>) -> ECEF<N> {
let x = f.vector().z;
let y = f.vector().y;
let z = -f.vector().x;
let a_over_b = N::from(SEMI_MAJOR_AXIS).unwrap().powi(2) /
N::from(SEMI_MINOR_AXIS).unwrap().powi(2);
let mul = N::from(SEMI_MINOR_AXIS).unwrap() /
(x.powi(2) + a_over_b * y.powi(2) + a_over_b * z.powi(2)).sqrt();
ECEF::new(-(mul * a_over_b * z + z * f.altitude()),
mul * a_over_b * y + y * f.altitude(),
mul * x + x * f.altitude())
}
}
#[cfg(test)]
mod tests {
use ::Access;
use ::enu::ENU;
use ::ned::NED;
use ::nvector::NVector;
use ::wgs84::WGS84;
use assert::close;
use na::Norm;
use super::*;
fn ecef_close(a: ECEF<f64>, b: ECEF<f64>) {
close(a.x(), b.x(), 0.000001);
close(a.y(), b.y(), 0.000001);
close(a.z(), b.z(), 0.000001);
}
quickcheck! {
fn from_wgs84(wgs: WGS84<f64>) -> () {
let test = WGS84::from(ECEF::from(wgs));
close(wgs.latitude(), test.latitude(), 0.000001);
close(wgs.longitude(), test.longitude(), 0.000001);
close(wgs.altitude(), test.altitude(), 0.000001);
}
fn from_nvector(wgs: WGS84<f64>) -> () {
let ans = ECEF::from(wgs);
let test = ECEF::from(NVector::from(wgs));
ecef_close(ans, test);
}
fn rotation(a: WGS84<f64>, b: WGS84<f64>) -> () {
let ecef_a = ECEF::from(a);
let ecef_b = ECEF::from(b);
let vec_enu = ecef_b - ecef_a;
let vec_e = ecef_b.0 - ecef_a.0;
let r_en = ecef_a.r_en();
let vec_e2 = r_en * vec_enu.access();
close(vec_e.as_ref(), vec_e2.as_ref(), 0.0000001);
}
fn add_vector(a: WGS84<f64>, b: WGS84<f64>) -> () {
let ecef_a = ECEF::from(a);
let ecef_b = ECEF::from(b);
let vec_ab = ecef_b - ecef_a;
let ecef_b2 = ecef_a + vec_ab;
ecef_close(ecef_b, ecef_b2);
}
fn distance(a: WGS84<f64>, b: WGS84<f64>) -> () {
let dist_ab = (ECEF::from(b) - ECEF::from(a)).norm();
let dist_ba = (ECEF::from(a) - ECEF::from(b)).norm();
close(dist_ab, dist_ba, 0.000001);
}
fn add_ned(a: WGS84<f64>, n: f64, e: f64, d: f64) -> () {
let ecef = ECEF::from(a);
let enu_vec = ENU::new(e, n, -d);
let ned_vec = NED::new(n, e, d);
let ecef_enu = ecef + enu_vec;
let ecef_ned = ecef + ned_vec;
ecef_close(ecef_enu, ecef_ned);
}
fn sub_ned(a: WGS84<f64>, n: f64, e: f64, d: f64) -> () {
let ecef = ECEF::from(a);
let enu_vec = ENU::new(e, n, -d);
let ned_vec = NED::new(n, e, d);
let ecef_enu = ecef - enu_vec;
let ecef_ned = ecef - ned_vec;
ecef_close(ecef_enu, ecef_ned);
}
fn add_enu(a: WGS84<f64>, e: f64, n: f64, u: f64) -> () {
let ecef = ECEF::from(a);
let enu = ENU::new(e, n, u);
let ecef_2 = ecef + enu;
let enu_2 = ecef_2 - ecef;
close(enu.access().as_ref(), enu_2.access().as_ref(), 0.0000001);
}
}
}