-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reference frame basics #102
Changes from all commits
9998c47
f9ee23d
9477e5d
2b1ec0b
4af02ff
4e6d599
7f88fe5
b93ea81
5d8d080
93ae426
2d4aba9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -47,6 +47,13 @@ | |||||||||||||||||
//! * "Transformation from Cartesian to Geodetic Coordinates Accelerated by | ||||||||||||||||||
//! Halley’s Method", T. Fukushima (2006), Journal of Geodesy. | ||||||||||||||||||
|
||||||||||||||||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; | ||||||||||||||||||
|
||||||||||||||||||
use crate::{ | ||||||||||||||||||
reference_frame::{get_transformation, ReferenceFrame, TransformationNotFound}, | ||||||||||||||||||
time::GpsTime, | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
/// WGS84 geodetic coordinates (Latitude, Longitude, Height) | ||||||||||||||||||
/// | ||||||||||||||||||
/// Internally stored as an array of 3 [f64](std::f64) values: latitude, longitude (both in the given angular units) and height above the geoid in meters | ||||||||||||||||||
|
@@ -327,6 +334,104 @@ impl AsMut<[f64; 3]> for ECEF { | |||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Add for ECEF { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn add(self, rhs: ECEF) -> ECEF { | ||||||||||||||||||
ECEF([self.x() + rhs.x(), self.y() + rhs.y(), self.z() + rhs.z()]) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Add<&ECEF> for ECEF { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn add(self, rhs: &ECEF) -> ECEF { | ||||||||||||||||||
self + *rhs | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Add<&ECEF> for &ECEF { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn add(self, rhs: &ECEF) -> ECEF { | ||||||||||||||||||
*self + *rhs | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl AddAssign for ECEF { | ||||||||||||||||||
fn add_assign(&mut self, rhs: ECEF) { | ||||||||||||||||||
*self += &rhs; | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl AddAssign<&ECEF> for ECEF { | ||||||||||||||||||
fn add_assign(&mut self, rhs: &ECEF) { | ||||||||||||||||||
self.0[0] += rhs.x(); | ||||||||||||||||||
self.0[1] += rhs.y(); | ||||||||||||||||||
self.0[2] += rhs.z(); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Sub for ECEF { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn sub(self, rhs: ECEF) -> ECEF { | ||||||||||||||||||
ECEF([self.x() - rhs.x(), self.y() - rhs.y(), self.z() - rhs.z()]) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Sub<&ECEF> for ECEF { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn sub(self, rhs: &ECEF) -> ECEF { | ||||||||||||||||||
self - *rhs | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Sub<&ECEF> for &ECEF { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn sub(self, rhs: &ECEF) -> ECEF { | ||||||||||||||||||
*self - *rhs | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl SubAssign for ECEF { | ||||||||||||||||||
fn sub_assign(&mut self, rhs: ECEF) { | ||||||||||||||||||
*self -= &rhs; | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl SubAssign<&ECEF> for ECEF { | ||||||||||||||||||
fn sub_assign(&mut self, rhs: &ECEF) { | ||||||||||||||||||
self.0[0] -= rhs.x(); | ||||||||||||||||||
self.0[1] -= rhs.y(); | ||||||||||||||||||
self.0[2] -= rhs.z(); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Mul<ECEF> for f64 { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn mul(self, rhs: ECEF) -> ECEF { | ||||||||||||||||||
ECEF([self * rhs.x(), self * rhs.y(), self * rhs.z()]) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Mul<&ECEF> for f64 { | ||||||||||||||||||
type Output = ECEF; | ||||||||||||||||||
fn mul(self, rhs: &ECEF) -> ECEF { | ||||||||||||||||||
self * *rhs | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl MulAssign<f64> for ECEF { | ||||||||||||||||||
fn mul_assign(&mut self, rhs: f64) { | ||||||||||||||||||
*self *= &rhs; | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl MulAssign<&f64> for ECEF { | ||||||||||||||||||
fn mul_assign(&mut self, rhs: &f64) { | ||||||||||||||||||
self.0[0] *= *rhs; | ||||||||||||||||||
self.0[1] *= *rhs; | ||||||||||||||||||
self.0[2] *= *rhs; | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// Local North East Down reference frame coordinates | ||||||||||||||||||
/// | ||||||||||||||||||
/// Internally stored as an array of 3 [f64](std::f64) values: N, E, D all in meters | ||||||||||||||||||
|
@@ -417,8 +522,102 @@ impl Default for AzimuthElevation { | |||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// Complete coordinate used for transforming between reference frames | ||||||||||||||||||
/// | ||||||||||||||||||
/// Velocities are optional, but when present they will be transformed | ||||||||||||||||||
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] | ||||||||||||||||||
pub struct Coordinate { | ||||||||||||||||||
reference_frame: ReferenceFrame, | ||||||||||||||||||
position: ECEF, | ||||||||||||||||||
velocity: Option<ECEF>, | ||||||||||||||||||
epoch: GpsTime, | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
impl Coordinate { | ||||||||||||||||||
pub fn new( | ||||||||||||||||||
reference_frame: ReferenceFrame, | ||||||||||||||||||
position: ECEF, | ||||||||||||||||||
velocity: Option<ECEF>, | ||||||||||||||||||
epoch: GpsTime, | ||||||||||||||||||
) -> Self { | ||||||||||||||||||
Coordinate { | ||||||||||||||||||
reference_frame, | ||||||||||||||||||
position, | ||||||||||||||||||
velocity, | ||||||||||||||||||
epoch, | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn without_velocity( | ||||||||||||||||||
reference_frame: ReferenceFrame, | ||||||||||||||||||
position: ECEF, | ||||||||||||||||||
epoch: GpsTime, | ||||||||||||||||||
) -> Self { | ||||||||||||||||||
Coordinate { | ||||||||||||||||||
reference_frame, | ||||||||||||||||||
position, | ||||||||||||||||||
velocity: None, | ||||||||||||||||||
epoch, | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn with_velocity( | ||||||||||||||||||
reference_frame: ReferenceFrame, | ||||||||||||||||||
position: ECEF, | ||||||||||||||||||
velocity: ECEF, | ||||||||||||||||||
epoch: GpsTime, | ||||||||||||||||||
) -> Self { | ||||||||||||||||||
Coordinate { | ||||||||||||||||||
reference_frame, | ||||||||||||||||||
position, | ||||||||||||||||||
velocity: Some(velocity), | ||||||||||||||||||
epoch, | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn reference_frame(&self) -> ReferenceFrame { | ||||||||||||||||||
self.reference_frame | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn position(&self) -> ECEF { | ||||||||||||||||||
self.position | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn velocity(&self) -> Option<ECEF> { | ||||||||||||||||||
self.velocity | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn epoch(&self) -> GpsTime { | ||||||||||||||||||
self.epoch | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// Use the velocity term to adjust the epoch of the coordinate. | ||||||||||||||||||
/// When a coordinate has no velocity the position won't be changed. | ||||||||||||||||||
pub fn adjust_epoch(&self, new_epoch: &GpsTime) -> Self { | ||||||||||||||||||
let dt = | ||||||||||||||||||
new_epoch.to_fractional_year_hardcoded() - self.epoch.to_fractional_year_hardcoded(); | ||||||||||||||||||
let v = self.velocity.unwrap_or_default(); | ||||||||||||||||||
|
||||||||||||||||||
Coordinate { | ||||||||||||||||||
position: self.position + dt * v, | ||||||||||||||||||
velocity: self.velocity, | ||||||||||||||||||
epoch: *new_epoch, | ||||||||||||||||||
reference_frame: self.reference_frame, | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+601
to
+606
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this is way to do in Rust, but why don't just modify the position and velocity alone? I guess self would will have to be mutable. That's the reason for this approach?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The style I typically write Rust code with definitely has more copies than is strictly necessary, and you're right that to change this method so it changes the coordinate in place without making a new copy would require making There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After sleeping on this some, I think this is a preferable form of the function. This produces cleaner code in the majority of cases but still allows someone to make the change in place if they desired. I will make sure to have let mut c = Coordinate::new(...);
// with &mut self
c.adjust_epoch(t);
// vs just re-assign
c = c.adjust_epoch(t); |
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pub fn transform_to(&self, new_frame: ReferenceFrame) -> Result<Self, TransformationNotFound> { | ||||||||||||||||||
let transformation = get_transformation(self.reference_frame, new_frame)?; | ||||||||||||||||||
Ok(transformation.transform(self)) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||
mod tests { | ||||||||||||||||||
use float_eq::assert_float_eq; | ||||||||||||||||||
|
||||||||||||||||||
use crate::time::UtcTime; | ||||||||||||||||||
|
||||||||||||||||||
use super::*; | ||||||||||||||||||
|
||||||||||||||||||
const D2R: f64 = std::f64::consts::PI / 180.0; | ||||||||||||||||||
|
@@ -516,4 +715,71 @@ mod tests { | |||||||||||||||||
assert!(height_err.abs() < MAX_DIST_ERROR_M); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
#[test] | ||||||||||||||||||
fn ecef_ops() { | ||||||||||||||||||
let a = ECEF::new(1.0, 2.0, 3.0); | ||||||||||||||||||
let b = ECEF::new(4.0, 5.0, 6.0); | ||||||||||||||||||
|
||||||||||||||||||
let result = a + b; | ||||||||||||||||||
assert_eq!(5.0, result.x()); | ||||||||||||||||||
assert_eq!(7.0, result.y()); | ||||||||||||||||||
assert_eq!(9.0, result.z()); | ||||||||||||||||||
|
||||||||||||||||||
let result = a + a + a; | ||||||||||||||||||
assert_eq!(3.0, result.x()); | ||||||||||||||||||
assert_eq!(6.0, result.y()); | ||||||||||||||||||
assert_eq!(9.0, result.z()); | ||||||||||||||||||
|
||||||||||||||||||
let result = a - b; | ||||||||||||||||||
assert_eq!(-3.0, result.x()); | ||||||||||||||||||
assert_eq!(-3.0, result.y()); | ||||||||||||||||||
assert_eq!(-3.0, result.z()); | ||||||||||||||||||
|
||||||||||||||||||
let result = 2.0 * a; | ||||||||||||||||||
assert_eq!(2.0, result.x()); | ||||||||||||||||||
assert_eq!(4.0, result.y()); | ||||||||||||||||||
assert_eq!(6.0, result.z()); | ||||||||||||||||||
|
||||||||||||||||||
let mut result = a; | ||||||||||||||||||
result += b; | ||||||||||||||||||
assert_eq!(5.0, result.x()); | ||||||||||||||||||
assert_eq!(7.0, result.y()); | ||||||||||||||||||
assert_eq!(9.0, result.z()); | ||||||||||||||||||
|
||||||||||||||||||
let mut result = a; | ||||||||||||||||||
result -= b; | ||||||||||||||||||
assert_eq!(-3.0, result.x()); | ||||||||||||||||||
assert_eq!(-3.0, result.y()); | ||||||||||||||||||
assert_eq!(-3.0, result.z()); | ||||||||||||||||||
|
||||||||||||||||||
let mut result = a; | ||||||||||||||||||
result *= 2.0; | ||||||||||||||||||
assert_eq!(2.0, result.x()); | ||||||||||||||||||
assert_eq!(4.0, result.y()); | ||||||||||||||||||
assert_eq!(6.0, result.z()); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
#[test] | ||||||||||||||||||
fn coordinate_epoch() { | ||||||||||||||||||
let initial_epoch = UtcTime::from_date(2020, 1, 1, 0, 0, 0.).to_gps_hardcoded(); | ||||||||||||||||||
let new_epoch = UtcTime::from_date(2021, 1, 1, 0, 0, 0.).to_gps_hardcoded(); | ||||||||||||||||||
let initial_coord = Coordinate::with_velocity( | ||||||||||||||||||
ReferenceFrame::ITRF2020, | ||||||||||||||||||
ECEF::new(0.0, 0.0, 0.0), | ||||||||||||||||||
ECEF::new(1.0, 2.0, 3.0), | ||||||||||||||||||
initial_epoch, | ||||||||||||||||||
); | ||||||||||||||||||
|
||||||||||||||||||
let new_coord = initial_coord.adjust_epoch(&new_epoch); | ||||||||||||||||||
|
||||||||||||||||||
assert_eq!(initial_coord.reference_frame, new_coord.reference_frame); | ||||||||||||||||||
assert_float_eq!(new_coord.position.x(), 1.0, abs <= 0.001); | ||||||||||||||||||
assert_float_eq!(new_coord.position.y(), 2.0, abs <= 0.001); | ||||||||||||||||||
assert_float_eq!(new_coord.position.z(), 3.0, abs <= 0.001); | ||||||||||||||||||
assert_float_eq!(new_coord.velocity.unwrap().x(), 1.0, abs <= 0.001); | ||||||||||||||||||
assert_float_eq!(new_coord.velocity.unwrap().y(), 2.0, abs <= 0.001); | ||||||||||||||||||
assert_float_eq!(new_coord.velocity.unwrap().z(), 3.0, abs <= 0.001); | ||||||||||||||||||
assert_eq!(new_epoch, new_coord.epoch()); | ||||||||||||||||||
} | ||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps
new_with_velocity
? Same fornew_without_velocity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least in the standard library it's the naming convention is to only have a single
new
function, and alternate constructor method names take the form ofwith_*
. See String::with_capacityThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in general i think you might be better off using the builder pattern if you find that you end up needing a lot of these. i think it is probably fine as is though