import boule
import numpy as np
from numpy.typing import NDArray
[docs]
def eotvos_correction_glicken(
latitude: NDArray,
track: NDArray,
ground_speed: NDArray,
) -> NDArray:
"""
Compute the simple Eötvös correction from latitude, aircraft track and ground
speed using the Glicken 1962 simplified equation.
Parameters
----------
latitude : ndarray
Latitude in decimal degrees
track : ndarray
Aircraft track in decimal degrees from geographic north, positive clock-wise
ground_speed : array-like
Ground speed in meters per second
Returns
-------
ndarray
Eötvös correction in mGal
"""
# mps to knots
ground_speed = ground_speed * 1.94384
# degrees to radian
lat_rad = np.radians(latitude)
track_rad = np.radians(track)
return 7.503 * ground_speed * np.cos(lat_rad) * np.sin(track_rad) + (
0.004154 * ground_speed**2
)
[docs]
def eotvos_correction_harlan(
latitude: NDArray,
longitude: NDArray,
time: NDArray,
height: NDArray,
ground_speed: bool = True,
):
"""
Compute the Eötvös correction from latitude, longitude, time, and height from Harlan
1968. If ground_speed is True this will use equation 15, if ground_speed is False
this will use equation 17.
Parameters
----------
latitude : ndarray
Latitude in degrees
longitude : ndarray
Longitude in degrees
time : ndarray
Time in seconds for measurements need to compute time derivatives
height : ndarray
Aircraft height in meters
ground_speed : bool
If True, use equation 15, if False use equation 17.
Returns
-------
ndarray
Eötvös correction in mGal
"""
# define an ellipsoid
ell = boule.WGS84
# degrees to radian
lat_rad = np.radians(latitude)
lon_rad = np.radians(longitude)
lat_geocentric_rad = np.deg2rad(
ell.geodetic_to_spherical((None, latitude, np.zeros_like(latitude)))[1]
)
# angle between latitude and geocentric latitude
deviation = lat_rad - lat_geocentric_rad
# geocentric radius
r_geocentric = ell.geocentric_radius(latitude, coordinate_system="geodetic")
# time derivatives
lon_dot = np.gradient(lon_rad, time)
lat_geocentric_rad_dot = np.gradient(lat_geocentric_rad, time)
# pre-compute some trig
sin_lat_squared = np.sin(lat_rad) ** 2
cos_lat = np.cos(lat_rad)
if ground_speed:
# from Eq. A6
velocity_northing = (
r_geocentric * lat_geocentric_rad_dot * 1 / np.cos(deviation)
)
velocity_easting = r_geocentric * lon_dot * np.cos(lat_geocentric_rad)
# Eq. 15
term_northing = ((velocity_northing**2) / ell.semimajor_axis) * (
1
+ (height / ell.semimajor_axis)
+ ell.flattening * (2 - 3 * sin_lat_squared)
)
term_easting = ((velocity_easting**2) / ell.semimajor_axis) * (
1 + (height / ell.semimajor_axis) - ell.flattening * (sin_lat_squared)
)
term_coriolis = (
2
* velocity_easting
* ell.angular_velocity
* cos_lat
* (1 + (height / ell.semimajor_axis))
)
else:
lat_dot = np.gradient(lat_rad, time)
# Eq. A7
velocity_northing = (
r_geocentric * lat_geocentric_rad_dot * 1 / np.cos(deviation)
+ lat_dot * height
)
velocity_easting = (
r_geocentric * lon_dot * np.cos(lat_geocentric_rad)
+ height * lon_dot * cos_lat
)
# Eq. 17
term_northing = (velocity_northing**2 / ell.semimajor_axis) * (
1
- (height / ell.semimajor_axis)
+ ell.flattening * (2 - 3 * sin_lat_squared)
)
term_easting = (velocity_easting**2 / ell.semimajor_axis) * (
1 - (height / ell.semimajor_axis) - ell.flattening * sin_lat_squared
)
term_coriolis = 2 * velocity_easting * ell.angular_velocity * cos_lat
# eotvos correction in mGal
return 1e5 * (term_northing + term_easting + term_coriolis)
def eotvos_correction_full(
latitude: NDArray,
longitude: NDArray,
time: NDArray,
height: NDArray,
):
"""
Parameters
----------
TODO: for deviation, should height be 0 or observation height?
Parameters
----------
latitude : ndarray
Latitude in degrees
longitude : ndarray
Longitude in degrees
time : ndarray
Time in seconds for measurements need to compute time derivatives
height : ndarray
Aircraft height in meters
Returns
-------
ndarray
Eötvös correction in mGal
"""
# define an ellipsoid
ell = boule.WGS84
# convert degrees to radians, and unwrap
lat_rad = np.deg2rad(latitude)
lon_rad = np.deg2rad(longitude)
lon_rad = np.unwrap(lon_rad)
# compute 1st and 2nd time derivatives of lat, lon, and heights
# dot means time derivative
# dt = np.diff(time, prepend=np.nan)
# lat_dot = np.diff(lat_rad, prepend=np.nan) / dt
# lat_dotdot = np.diff(lat_dot, prepend=np.nan) / dt
# lon_dot = np.diff(lon_rad, prepend=np.nan) / dt
# lon_dotdot = np.diff(lon_dot, prepend=np.nan) / dt
# height_dot = np.diff(height, prepend=np.nan) / dt
# height_dotdot = np.diff(height_dot, prepend=np.nan) / dt
lat_dot = np.gradient(lat_rad, time)
lat_dotdot = np.gradient(lat_dot, time)
lon_dot = np.gradient(lon_rad, time)
lon_dotdot = np.gradient(lon_dot, time)
height_dot = np.gradient(height, time)
height_dotdot = np.gradient(height_dot, time)
# pre-compute trig functions
sin_lat = np.sin(lat_rad)
cos_lat = np.cos(lat_rad)
sin_2lat = np.sin(2 * lat_rad)
cos_2lat = np.cos(2 * lat_rad)
# calculate r' and its derivatives (Eqs. 6,7,8)
# r' is the vector from center of ellipsoid to point on ellipsoid directly (normal)
# below the aircraft
asquared_bsquared = ell.semimajor_axis**2 * ell.semiminor_axis**2
bsquared_minus_asquared = ell.semiminor_axis**2 - ell.semimajor_axis**2
k = asquared_bsquared * bsquared_minus_asquared
g = ell.semimajor_axis**2 * cos_lat**2 + ell.semiminor_axis**2 * sin_lat**2
r_prime = ell.geocentric_radius(latitude, coordinate_system="geodetic")
r_prime_dot = (1 / (2 * r_prime)) * lat_dot * ((k * sin_2lat) / g**2)
drdlat = (k * sin_2lat) / (2 * r_prime * g**2)
d2rdlat2 = (k / (2 * r_prime * g**2)) * (
(2 * cos_2lat)
- (
sin_2lat**2
/ (r_prime * g**2)
* ((k / (2 * r_prime)) + 2 * r_prime * g * bsquared_minus_asquared)
)
)
r_prime_dotdot = lat_dot**2 * d2rdlat2 + lat_dotdot * drdlat
# below are approximations
# r_prime = ell.semimajor_axis * (1 - (ell.flattening * sin_lat * sin_lat))
# r_prime_dot = -ell.semimajor_axis * lat_dot * ell.flattening * sin_2lat
# r_prime_dotdot = (-ell.semimajor_axis * lat_dotdot * ell.flattening * sin_2lat) - (2 * ell.semimajor_axis * lat_dot * lat_dot * ell.flattening * cos_2lat)
# calculate deviation between geodetic and geocentric latitudes and derivatives (Eqs. 10,11,12)
two_e_squared_minus_e_to_forth = 2 * ell.eccentricity**2 - ell.eccentricity**4
dev = (
latitude
- ell.geodetic_to_spherical((None, latitude, np.zeros_like(latitude)))[1]
) # Fig 1 from Harlan 1968 shows dev as the angle with a height of 0 ...
dev = np.deg2rad(dev)
devdot = lat_dot * (
(ell.eccentricity**2)
- two_e_squared_minus_e_to_forth
* sin_lat**2
/ (1 - two_e_squared_minus_e_to_forth * sin_lat**2)
)
term1 = (ell.eccentricity**2 - two_e_squared_minus_e_to_forth * sin_lat**2) / (
1 - two_e_squared_minus_e_to_forth * sin_lat**2
)
term2 = ((1 - ell.eccentricity**2) * two_e_squared_minus_e_to_forth * sin_2lat) / (
(1 - (two_e_squared_minus_e_to_forth * sin_lat**2)) ** 2
)
devdotdot = term1 * lat_dotdot - term2 * lat_dot**2
# below are approximations
# dev = np.arctan(ell.flattening * sin_2lat) # eq. 9 is D = arctan(flattening * sin2lat)
# devdot = 2 * lat_dot * ell.flattening * cos_2lat
# devdotdot = 2 * lat_dotdot * ell.flattening * cos_2lat - 4 * lat_dot * lat_dot * ell.flattening * sin_2lat
# pre-compute trig functions
sin_dev = np.sin(dev)
cos_dev = np.cos(dev)
# define r and its derivatives (Eq. 2)
r = np.vstack(
(
-r_prime * sin_dev,
np.zeros(len(r_prime)),
-r_prime * cos_dev - height,
)
).T
rdot = np.vstack(
(
-r_prime_dot * sin_dev - r_prime * devdot * cos_dev,
np.zeros(len(r_prime)),
-r_prime_dot * cos_dev + r_prime * devdot * sin_dev - height_dot,
)
).T
rdotdot_x = (
-r_prime_dotdot * sin_dev
- 2.0 * r_prime_dot * devdot * cos_dev
- r_prime * (devdotdot * cos_dev - devdot**2 * sin_dev)
)
rdotdot_z = (
-r_prime_dotdot * cos_dev
+ 2.0 * r_prime_dot * devdot * sin_dev
+ r_prime * (devdotdot * sin_dev + devdot**2 * cos_dev)
- height_dotdot
)
rdotdot = np.vstack(
(
rdotdot_x,
np.zeros(len(rdotdot_x)),
rdotdot_z,
)
).T
# define w and derivative (Eq. 3)
w = np.vstack(
(
(lon_dot + ell.angular_velocity) * cos_lat,
-lat_dot,
-(lon_dot + ell.angular_velocity) * sin_lat,
)
).T
wdot = np.vstack(
(
lon_dotdot * cos_lat - (lon_dot + ell.angular_velocity) * lat_dot * sin_lat,
-lat_dotdot,
-lon_dotdot * sin_lat
- (lon_dot + ell.angular_velocity) * lat_dot * cos_lat,
)
).T
# total acceleration of the aircraft Eq.1 from Harlan 1968
w2xrdot = np.cross(2 * w, rdot)
wdotxr = np.cross(wdot, r)
wxr = np.cross(w, r)
wxwxr = np.cross(w, wxr)
accel = rdotdot + w2xrdot + wdotxr + wxwxr
# calculate wexwexre, centrifugal acceleration due to the Earth
we = np.vstack(
(
ell.angular_velocity * cos_lat,
np.zeros(len(sin_lat)),
-ell.angular_velocity * sin_lat,
)
).T
wexr = np.cross(we, r)
wexwexr = np.cross(we, wexr)
# Eotvos correction is the vertical component of the total acceleration of
# the aircraft minus the centrifugal acceleration of the Earth
eotvos_corr = accel[:, 2] - wexwexr[:, 2]
# eotvos correction in mGal
return 1e5 * eotvos_corr
def eotvos_correction_approx(
latitude: NDArray,
longitude: NDArray,
time: NDArray,
height: NDArray,
):
"""
Parameters
----------
latitude : ndarray
Latitude in degrees
longitude : ndarray
Longitude in degrees
time : ndarray
Time in seconds for measurements need to compute time derivatives
height : ndarray
Aircraft height in meters
Returns
-------
ndarray
Eötvös correction in mGal
"""
# define an ellipsoid
ell = boule.WGS84
# convert degrees to radians, and unwrap
lat_rad = np.deg2rad(latitude)
lon_rad = np.deg2rad(longitude)
lon_rad = np.unwrap(lon_rad)
# compute 1st and 2nd time derivatives of lat, lon, and heights
# dot means time derivative
# dt = np.diff(time, prepend=np.nan)
# lat_dot = np.diff(lat_rad, prepend=np.nan) / dt
# lon_dot = np.diff(lon_rad, prepend=np.nan) / dt
# height_dot = np.diff(height, prepend=np.nan) / dt
# lat_dotdot = np.diff(lat_dot, prepend=np.nan) / dt
# lon_dotdot = np.diff(lon_dot, prepend=np.nan) / dt
# height_dotdot = np.diff(height_dot, prepend=np.nan) / dt
lat_dot = np.gradient(lat_rad, time)
lat_dotdot = np.gradient(lat_dot, time)
lon_dot = np.gradient(lon_rad, time)
lon_dotdot = np.gradient(lon_dot, time)
height_dot = np.gradient(height, time)
height_dotdot = np.gradient(height_dot, time)
# pre-compute trig functions
sin_lat = np.sin(lat_rad)
cos_lat = np.cos(lat_rad)
sin_2lat = np.sin(2 * lat_rad)
cos_2lat = np.cos(2 * lat_rad)
# calculate r' and its derivatives (Eqs. 6,7,8)
# r' is the vector from center of ellipsoid to point on ellipsoid directly (normal)
# below the aircraft
r_prime = ell.semimajor_axis * (
1 - ell.flattening * sin_lat**2
) # Eq 6, simplification
r_prime_dot = (
-ell.semimajor_axis * lat_dot * ell.flattening * sin_2lat
) # Eq 7, simplification
r_prime_dotdot = (
-ell.semimajor_axis * lat_dotdot * ell.flattening * sin_2lat
- 2 * ell.semimajor_axis * lat_dot**2 * ell.flattening * cos_2lat
) # Eq 8, simplification
# calculate deviation from normal and derivatives (Eqs. 10,11,12)
d = np.arctan(ell.flattening * sin_2lat) # Eq 10, simplification
ddot = 2 * lat_dot * ell.flattening * cos_2lat # Eq 11, simplification
ddotdot = (
2 * lat_dotdot * ell.flattening * cos_2lat
- 4 * lat_dot**2 * ell.flattening * sin_2lat
) # Eq 12, simplification
# pre-compute trig functions
sin_d = np.sin(d)
cos_d = np.cos(d)
# define r and its derivatives
# r is vector
r = np.vstack(
( # Eq 2
-r_prime * sin_d,
np.zeros(len(r_prime)),
-r_prime * cos_d - height,
)
).T
rdot = np.vstack(
( # this is just time derivative of above components
-r_prime_dot * sin_d - r_prime * ddot * cos_d,
np.zeros(len(r_prime)),
-r_prime_dot * cos_d + r_prime * ddot * sin_d - height_dot,
)
).T
ci = (
-r_prime_dotdot * sin_d
- 2.0 * r_prime_dot * ddot * cos_d
- r_prime * (ddotdot * cos_d - ddot * ddot * sin_d)
)
ck = (
-r_prime_dotdot * cos_d
+ 2.0 * r_prime_dot * ddot * sin_d
+ r_prime * (ddotdot * sin_d + ddot * ddot * cos_d)
- height_dotdot
)
rdotdot = np.vstack(
(ci, np.zeros(len(ci)), ck)
).T # this is just 2nd time derivative of above components
# define w and derivative (Eq. 3)
w = np.vstack(
(
(lon_dot + ell.angular_velocity) * cos_lat,
-lat_dot,
-(lon_dot + ell.angular_velocity) * sin_lat,
)
).T
wdot = np.vstack(
(
lon_dotdot * cos_lat # I think the first lon_dot should be lon_dotdot!
- (lon_dot + ell.angular_velocity) * lat_dot * sin_lat,
-lat_dotdot,
-lon_dotdot * sin_lat
- (lon_dot + ell.angular_velocity) * lat_dot * cos_lat,
)
).T
# total acceleration of the aircraft Eq.1 from Harlan 1968
w2xrdot = np.cross(2 * w, rdot)
wdotxr = np.cross(wdot, r)
wxr = np.cross(w, r)
wxwxr = np.cross(w, wxr)
accel = rdotdot + w2xrdot + wdotxr + wxwxr
# calculate wexwexre, centrifugal acceleration due to the Earth
we = np.vstack(
(
ell.angular_velocity * cos_lat,
np.zeros(len(sin_lat)),
-ell.angular_velocity * sin_lat,
)
).T
wexr = np.cross(we, r)
wexwexr = np.cross(we, wexr)
# Eotvos correction is the vertical component of the total acceleration of
# the aircraft minus the centrifugal acceleration of the Earth
eotvos_corr = accel[:, 2] - wexwexr[:, 2]
# eotvos correction in mGal
return 1e5 * eotvos_corr
# def central_difference(data_in, n=1, order=2, dt=0.1):
# """central difference differentiator"""
# if order == 2:
# # first derivative
# if n == 1:
# dy = (data_in[2:] - data_in[0:-2]) / (2 * dt)
# # second derivative
# elif n == 2:
# dy = (data_in[0:-2] - 2 * data_in[1:-1] + data_in[2:]) / np.power(dt, 2)
# else:
# raise ValueError("Invalid value for parameter n {1 or 2}")
# else:
# raise NotImplementedError
# return np.pad(dy, (1, 1), "edge")
# def eotvos_correction_dgs(
# latitude: NDArray,
# longitude: NDArray,
# height: NDArray,
# dt,
# differentiator=central_difference,
# ):
# """
# from https://github.com/DynamicGravitySystems/DGP/blob/5c0b566b846eb25f1e5ede64b2caaaa6a3352a29/dgp/lib/transform/gravity.py#L73
# I changed sampling rate to match my data
# final eotvos correction is column eotvos plus column kin_accel
# I changed variables names to match with the shipgrav implementation to compare
# I changed to output eotvos correction, which is columns eotvos + kin_accel
# I added sampling rate (dt) as parameter
# I changed inputs to be same as other functions
# Eotvos correction
# Parameters
# ----------
# data_in: DataFrame
# trajectory frame containing latitude, longitude, and
# height above the ellipsoid
# dt: float
# sample period
# Returns
# -------
# Series
# index taken from the input
# Notes
# -----
# Added to observed gravity when the positive direction of the sensitive axis is
# down, otherwise, subtracted.
# References
# ---------
# Harlan 1968, "Eotvos Corrections for Airborne Gravimetry" JGR 73,n14
# """
# # dt = 0.1
# # constants # added these to the function
# a = 6378137.0 # Default semi-major axis
# b = 6356752.3142 # Default semi-minor axis
# ecc = (a - b) / a # Eccentricity
# omega = 0.00007292115 # sidereal rotation rate, radians/sec
# mps2mgal = 100000 # m/s/s to mgal
# latr = np.deg2rad(latitude)
# lonr = np.deg2rad(longitude)
# # lonr = np.unwrap(lonr)
# dlat = differentiator(latr, n=1, dt=dt)
# ddlat = differentiator(latr, n=2, dt=dt)
# dlon = differentiator(lonr, n=1, dt=dt)
# ddlon = differentiator(lonr, n=2, dt=dt)
# dht = differentiator(height, n=1, dt=dt)
# ddht = differentiator(height, n=2, dt=dt)
# sin_lat = np.sin(latr)
# cos_lat = np.cos(latr)
# sin_2lat = np.sin(2.0 * latr)
# cos_2lat = np.cos(2.0 * latr)
# # Calculate the r' and its derivatives
# r_prime = a * (1.0 - ecc * sin_lat**2)
# dr_prime = -a * dlat * ecc * sin_2lat
# ddr_prime = -a * ddlat * ecc * sin_2lat - 2.0 * a * (dlat**2) * ecc * cos_2lat
# # Calculate the deviation from the normal and its derivatives
# d = np.arctan(ecc * sin_2lat)
# dd = 2.0 * dlat * ecc * cos_2lat
# ddd = 2.0 * ddlat * ecc * cos_2lat - 4.0 * dlat * dlat * ecc * sin_2lat
# # Calculate this value once (used many times)
# sinD = np.sin(d)
# cosD = np.cos(d)
# # Calculate r and its derivatives
# r = np.array([-r_prime * sinD, np.zeros(r_prime.size), -r_prime * cosD - height])
# rdot = np.array(
# [
# (-dr_prime * sinD - r_prime * dd * cosD),
# np.zeros(r_prime.size),
# (-dr_prime * cosD + r_prime * dd * sinD - dht),
# ]
# )
# ci = (
# -ddr_prime * sinD
# - 2.0 * dr_prime * dd * cosD
# - r_prime * (ddd * cosD - dd * dd * sinD)
# )
# ck = (
# -ddr_prime * cosD
# + 2.0 * dr_prime * dd * sinD
# + r_prime * (ddd * sinD + dd * dd * cosD)
# - ddht # here ddht is correctly outsite parenthesis, in shipgrap its inside
# )
# rdotdot = np.array([ci, np.zeros(ci.size), ck])
# # Define w and its derivative
# w = np.array([(dlon + omega) * cos_lat, -dlat, (-(dlon + omega)) * sin_lat])
# wdot = np.array(
# [
# ddlon * cos_lat # first term, dlon, should be ddlon
# - (dlon + omega) * dlat * sin_lat,
# -ddlat,
# (-ddlon * sin_lat - (dlon + omega) * dlat * cos_lat),
# ]
# )
# w2xrdot = np.cross(2.0 * w, rdot, axis=0)
# wdotxr = np.cross(wdot, r, axis=0)
# wxr = np.cross(w, r, axis=0)
# wxwxr = np.cross(w, wxr, axis=0)
# we = np.array([omega * cos_lat, np.zeros(sin_lat.shape), -omega * sin_lat])
# wexr = np.cross(we, r, axis=0)
# wexwexr = np.cross(we, wexr, axis=0)
# kin_accel = rdotdot * mps2mgal
# eotvos = (w2xrdot + wdotxr + wxwxr - wexwexr) * mps2mgal
# # acc = rdotdot + w2xrdot + wdotxr + wxwxr
# # eotvos = pd.Series(eotvos[2], index=data_in.index, name='eotvos')
# # kin_accel = pd.Series(kin_accel[2], index=data_in.index, name='kin_accel')
# # df = pd.concat([eotvos, kin_accel], axis=1, join='outer')
# return eotvos[2] + kin_accel[2]
# def eotvos_correction_shipgrav(
# latitude: NDArray,
# longitude: NDArray,
# height: NDArray,
# time: NDArray,
# ) -> pd.Series:
# """
# This is my re-writting of the below `eotvos_correction_shipgrav` function, which is
# directly from the shipgrav python package. They assume a constant sampling rate
# between points, but here I've re-written to allow varying sampling rates.
# I changed variable names to match with DGS package implementation
# ck and wdot both had mistakes I fixed
# I changed inputs to match format of other functions
# """
# a = 6378137.0
# b = 6356752.3142
# omega = 0.00007292115 # siderial rotation rate, radians/sec
# ecc = (a - b) / a # this is referred to as flattening in Boule
# latr = np.deg2rad(latitude)
# lonr = np.deg2rad(longitude)
# lonr = np.unwrap(lonr)
# # get time derivatives of position
# # dt = np.diff(time, prepend=np.nan)
# # dlat = np.diff(latr, prepend=np.nan) / dt
# # dlon = np.diff(lonr, prepend=np.nan) / dt
# # dht = np.diff(height, prepend=np.nan) / dt
# # ddlat = np.diff(dlat, prepend=np.nan) / dt
# # ddlon = np.diff(dlon, prepend=np.nan) / dt
# # ddht = np.diff(dht, prepend=np.nan) / dt
# dlat = np.gradient(latr, time)
# ddlat = np.gradient(dlat, time)
# dlon = np.gradient(lonr, time)
# ddlon = np.gradient(dlon, time)
# dht = np.gradient(height, time)
# ddht = np.gradient(dht, time)
# # sines and cosines etc
# sin_lat = np.sin(latr)
# cos_lat = np.cos(latr)
# sin_2lat = np.sin(2 * latr)
# cos_2lat = np.cos(2 * latr)
# # calculate r' and its derivatives
# r_prime = a * (1 - ecc * sin_lat * sin_lat)
# dr_prime = -a * dlat * ecc * sin_2lat
# ddr_prime = -a * ddlat * ecc * sin_2lat - 2 * a * dlat * dlat * ecc * cos_2lat
# # calculate deviation from normal and derivatives
# d = np.arctan(ecc * sin_2lat) # eq. 9 is D = arctan(flattening * sin2lat)
# dd = 2 * dlat * ecc * cos_2lat
# ddd = 2 * ddlat * ecc * cos_2lat - 4 * dlat * dlat * ecc * sin_2lat
# # define r and its derivatives
# # r is the vector from the earth's center to the aircraft
# r = np.vstack(
# (
# -r_prime * np.sin(d),
# np.zeros(len(r_prime)),
# -r_prime * np.cos(d) - height,
# )
# ).T
# rdot = np.vstack(
# (
# -dr_prime * np.sin(d) - r_prime * dd * np.cos(d),
# np.zeros(len(r_prime)),
# -dr_prime * np.cos(d) + r_prime * dd * np.sin(d) - dht,
# )
# ).T
# ci = (
# -ddr_prime * np.sin(d)
# - 2.0 * dr_prime * dd * np.cos(d)
# - r_prime * (ddd * np.cos(d) - dd * dd * np.sin(d))
# )
# ck = (
# -ddr_prime * np.cos(d)
# + 2.0 * dr_prime * dd * np.sin(d)
# # + r_prime * (ddd * np.sin(d) + dd * dd * np.cos(d) - ddht) # this is wrong, ddht should be outside parenthesis
# + r_prime * (ddd * np.sin(d) + dd * dd * np.cos(d))
# - ddht # this is correct
# )
# rdotdot = np.vstack((ci, np.zeros(len(ci)), ck)).T
# # define w and derivative
# w = np.vstack(
# (
# (dlon + omega) * cos_lat,
# -dlat,
# -(dlon + omega) * sin_lat,
# )
# ).T
# wdot = np.vstack(
# (
# ddlon * cos_lat
# - (dlon + omega)
# * dlat
# * sin_lat, # changed first term dlong to ddlon, no dicernable difference
# -ddlat,
# -ddlon * sin_lat - (dlon + omega) * dlat * cos_lat,
# )
# ).T
# w2xrdot = np.cross(2 * w, rdot)
# wdotxr = np.cross(wdot, r)
# wxr = np.cross(w, r)
# wxwxr = np.cross(w, wxr)
# # calculate wexwexre, centrifugal acceleration due to the Earth
# we = np.vstack(
# (
# omega * cos_lat,
# np.zeros(len(sin_lat)),
# -omega * sin_lat,
# )
# ).T
# wexr = np.cross(we, r)
# wexwexr = np.cross(we, wexr)
# # calculate total acceleration for the aircraft
# accel = rdotdot + w2xrdot + wdotxr + wxwxr
# # Eotvos correction is the vertical component of the total acceleration of
# # the aircraft minus the centrifugal acceleration of the Earth, convert to mGal
# return (accel[:, 2] - wexwexr[:, 2]) * 100e3
# def center_diff(y, n, samp):
# """ Numerical derivatives, central difference of nth order
# :param y: data to differentiate
# :type y: array_like
# :param n: order, either 1 or 2
# :type n: int
# :param samp: sampling rate
# :type samp: float
# :return: 1st or 2nd order derivative of **y**
# """
# if n == 1:
# return (y[2:] - y[:-2])*(samp/2)
# elif n == 2:
# return (y[:-2] - 2*y[1:-1] + y[2:])*(samp**2)
# else:
# print('bad order for derivative')
# return -999
# def eotvos_correction_shipgrav(lon, lat, ht, samp, a=6378137.0, b=6356752.3142):
# """ Full Eotvos correction in mGals.
# From https://github.com/PFPE/shipgrav/blob/e84b33cc6fcfddf0e18736cd1205620d82e16e91/shipgrav/grav.py#L194
# The Eotvos correction is the effect on measured gravity due to horizontal
# motion over the Earth's surface.
# This formulation is from Harlan (1968), "Eotvos Corrections for Airborne Gravimetry" in
# *Journal of Geophysical Research*, 73(14), DOI: 10.1029/JB073i014p04675
# Implementation modified from matlab script written by Sandra Preaux, NGS, NOAA August 24 2009
# Components of the correction:
# * rdoubledot
# * angular acceleration of the reference frame
# * corliolis
# * centrifugal
# * centrifugal acceleration of Earth
# :param lon: longitudes in degrees
# :type lon: array_like
# :param lat: latitudes in degrees
# :type lat: array_like
# :param ht: elevation (referenced to sea level)
# :type ht: array_like
# :param samp: sampling rate
# :type samp: float
# :param a: major axis of ellipsoid (default is WGS84)
# :type a: float, optional
# :param b: minor axis of ellipsoid (default is WGS84)
# :type b: float, optional
# :return: **E** (*ndarray*), Eotvos correction in mGal
# """
# We = 0.00007292115 # siderial rotation rate, radians/sec
# mps2mgal = 100000 # m/s/s to mgal
# ecc = (a-b)/a # this is referred to as flattening in Boule
# latr = np.deg2rad(lat)
# lonr = np.deg2rad(lon)
# # get time derivatives of position
# dlat = center_diff(latr, 1, samp)
# ddlat = center_diff(latr, 2, samp)
# dlon = center_diff(lonr, 1, samp)
# ddlon = center_diff(lonr, 2, samp)
# dht = center_diff(ht, 1, samp)
# ddht = center_diff(ht, 2, samp)
# # sines and cosines etc
# slat = np.sin(latr[1:-1])
# clat = np.cos(latr[1:-1])
# s2lat = np.sin(2*latr[1:-1])
# c2lat = np.cos(2*latr[1:-1])
# # calculate r' and its derivatives
# rp = a*(1 - ecc*slat*slat)
# drp = -a*dlat*ecc*s2lat
# ddrp = -a*ddlat*ecc*s2lat - 2*a*dlat*dlat*ecc*c2lat
# # calculate deviation from normal and derivatives
# D = np.arctan(ecc*s2lat)
# dD = 2*dlat*ecc*c2lat
# ddD = 2*ddlat*ecc*c2lat - 4*dlat*dlat*ecc*s2lat
# # define r and its derivatives
# r = np.vstack((-rp*np.sin(D), np.zeros(len(rp)), -
# rp*np.cos(D) - ht[1:-1])).T
# rdot = np.vstack((-drp*np.sin(D) - rp*dD*np.cos(D),
# np.zeros(len(rp)), -drp*np.cos(D) + rp*dD*np.sin(D) - dht)).T
# ci = -ddrp*np.sin(D) - 2.*drp*dD*np.cos(D) - rp * \
# (ddD*np.cos(D) - dD*dD*np.sin(D))
# ck = -ddrp*np.cos(D) + 2.*drp*dD*np.sin(D) + rp * \
# (ddD*np.sin(D) + dD*dD*np.cos(D) - ddht)
# rdotdot = np.vstack((ci, np.zeros(len(ci)), ck)).T
# # define w and derivative
# w = np.vstack(((dlon + We)*clat, -dlat, -(dlon + We)*slat)).T
# wdot = np.vstack((dlon*clat - (dlon + We)*dlat*slat, - # should 1st term be ddlon??
# ddlat, -ddlon*slat - (dlon + We)*dlat*clat)).T
# w2xrdot = np.cross(2*w, rdot)
# wdotxr = np.cross(wdot, r)
# wxr = np.cross(w, r)
# wxwxr = np.cross(w, wxr)
# # calculate wexwexre, centrifugal acceleration due to the Earth
# # re = np.vstack((-rp*np.sin(D), np.zeros(len(rp)), -rp*np.cos(D))).T # this is actually used, comment out?
# we = np.vstack((We*clat, np.zeros(len(slat)), -We*slat)).T
# # wexre = np.cross(we, re) # this is actually used, comment out?
# # wexwexre = np.cross(we, wexre) # this is actually used, comment out?
# wexr = np.cross(we, r)
# wexwexr = np.cross(we, wexr)
# # calculate total acceleration for the aircraft
# a = rdotdot + w2xrdot + wdotxr + wxwxr
# # Eotvos correction is the vertical component of the total acceleration of
# # the aircraft minus the centrifugal acceleration of the Earth, convert to mGal
# E = (a[:, 2] - wexwexr[:, 2])*mps2mgal
# E = np.hstack((E[0], E, E[-1]))
# return E