60template<
typename Derived>
61Eigen::Vector3<typename Derived::Scalar>
lla2ecef(
const Eigen::MatrixBase<Derived>& lla_position,
double a,
double e_squared)
63 const typename Derived::Scalar& latitude = lla_position(0);
64 const typename Derived::Scalar& longitude = lla_position(1);
65 const typename Derived::Scalar& altitude = lla_position(2);
72 return { (R_E + altitude) * std::cos(latitude) * std::cos(longitude),
73 (R_E + altitude) * std::cos(latitude) * std::sin(longitude),
74 (R_E * (1 - e_squared) + altitude) * std::sin(latitude) };
84template<
typename Derived>
85Eigen::Vector3<typename Derived::Scalar>
ecef2lla(
const Eigen::MatrixBase<Derived>& e_position,
86 double a,
double b,
double e_squared)
88 if (e_position.isZero())
91 typename Derived::Scalar(0.0),
92 typename Derived::Scalar(0.0),
93 typename Derived::Scalar(-a)
97 const auto& x = e_position(0);
98 const auto& y = e_position(1);
99 const auto& z = e_position(2);
103 auto lon = std::atan2(y, x);
107 auto p = e_position.head(2).norm();
108 auto E = std::sqrt(a * a - b * b);
109 auto F = 54.0 * std::pow(b * z, 2.0);
110 auto G = p * p + (1.0 - e_squared) * z * z - e_squared * E * E;
111 auto c = e_squared * e_squared * F * p * p / std::pow(G, 3.0);
112 auto s = std::pow(1.0 + c + std::sqrt(c * c + 2.0 * c), 1.0 / 3.0);
113 auto P = (F / (3.0 * G * G)) / std::pow(s + (1.0 / s) + 1.0, 2.0);
114 auto Q = std::sqrt(1.0 + 2.0 * e_squared * e_squared * P);
115 auto k_1 = -P * e_squared * p / (1.0 + Q);
116 auto k_2 = 0.5 * a * a * (1.0 + 1.0 / Q);
117 auto k_3 = -P * (1.0 - e_squared) * z * z / (Q * (1.0 + Q));
118 auto k_4 = -0.5 * P * p * p;
119 auto r_0 = k_1 + std::sqrt(k_2 + k_3 + k_4);
120 auto k_5 = (p - e_squared * r_0);
121 auto U = std::sqrt(k_5 * k_5 + z * z);
122 auto V = std::sqrt(k_5 * k_5 + (1.0 - e_squared) * z * z);
124 auto alt = U * (1.0 - (b * b / (a * V)));
128 auto z_0 = (b * b * z) / (a * V);
129 auto e_p = (a / b) * std::sqrt(e_squared);
131 auto lat = std::atan((z + z_0 * (e_p * e_p)) / p);
133 return { lat, lon, alt };
140template<
typename Derived>
141[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
quat2eulerZYX(
const Eigen::QuaternionBase<Derived>& q)
144 Eigen::Vector3<typename Derived::Scalar> XYZ = q.toRotationMatrix().eulerAngles(2, 1, 0).reverse();
147 if (XYZ.y() >= M_PI / 2.0 || XYZ.y() <= -M_PI / 2.0)
149 typename Derived::Scalar x = XYZ.x() > 0 ? XYZ.x() - M_PI : XYZ.x() + M_PI;
150 typename Derived::Scalar y = XYZ.y() >= M_PI / 2.0 ? -(XYZ.y() - M_PI) : -(XYZ.y() + M_PI);
151 typename Derived::Scalar z = XYZ.z() - M_PI;
165template<
typename Derived>
166[[nodiscard]] Eigen::Matrix<typename Derived::Scalar, 4, 3>
covRPY2quatJacobian(
const Eigen::QuaternionBase<Derived>& n_quat_b)
170 if (std::abs(RPY.y()) >
typename Derived::Scalar(
deg2rad(90 - 1e-9)))
172 Eigen::Quaternion<typename Derived::Scalar> n_quat2_b = n_quat_b * Eigen::AngleAxis<typename Derived::Scalar>(
typename Derived::Scalar(
deg2rad(1e-8)), Eigen::Vector3<typename Derived::Scalar>::UnitY());
175 auto ccc = std::cos(RPY(0) / 2.0) * std::cos(RPY(1) / 2.0) * std::cos(RPY(2) / 2.0);
176 auto scc = std::sin(RPY(0) / 2.0) * std::cos(RPY(1) / 2.0) * std::cos(RPY(2) / 2.0);
177 auto csc = std::cos(RPY(0) / 2.0) * std::sin(RPY(1) / 2.0) * std::cos(RPY(2) / 2.0);
178 auto ccs = std::cos(RPY(0) / 2.0) * std::cos(RPY(1) / 2.0) * std::sin(RPY(2) / 2.0);
179 auto ssc = std::sin(RPY(0) / 2.0) * std::cos(RPY(1) / 2.0) * std::cos(RPY(2) / 2.0);
180 auto scs = std::sin(RPY(0) / 2.0) * std::cos(RPY(1) / 2.0) * std::sin(RPY(2) / 2.0);
181 auto css = std::cos(RPY(0) / 2.0) * std::sin(RPY(1) / 2.0) * std::sin(RPY(2) / 2.0);
182 auto sss = std::sin(RPY(0) / 2.0) * std::sin(RPY(1) / 2.0) * std::sin(RPY(2) / 2.0);
184 Eigen::Matrix<typename Derived::Scalar, 4, 3> J;
186 J << (ccc + sss) / 2.0, -(ccs + ssc) / 2.0, -(csc + scs) / 2.0,
187 (ccs - ssc) / 2.0, (ccc - sss) / 2.0, (scc - css) / 2.0,
188 -(csc + scs) / 2.0, -(css + scc) / 2.0, (ccc + sss) / 2.0,
189 (css - scc) / 2.0, (scs - csc) / 2.0, (ssc - ccs) / 2.0;
198template<
typename Derived,
typename DerivedQ>
199[[nodiscard]] Eigen::Matrix4<typename Derived::Scalar>
covRPY2quat(
const Eigen::MatrixBase<Derived>& covRPY,
const Eigen::QuaternionBase<DerivedQ>& n_quat_b)
202 return J * covRPY * J.transpose();
207template<
typename Derived>
208[[nodiscard]] Eigen::Matrix<typename Derived::Scalar, 3, 4>
covQuat2RPYJacobian(
const Eigen::QuaternionBase<Derived>& n_quat_b)
210 auto disc = n_quat_b.w() * n_quat_b.y() - n_quat_b.x() * n_quat_b.z();
211 auto a = 2.0 * std::pow(n_quat_b.y(), 2) + 2.0 * std::pow(n_quat_b.z(), 2) - 1.0;
212 auto b = 2.0 * std::pow(n_quat_b.x(), 2) + 2.0 * std::pow(n_quat_b.y(), 2) - 1.0;
213 auto c = n_quat_b.w() * n_quat_b.z() + n_quat_b.x() * n_quat_b.y();
214 auto d = n_quat_b.w() * n_quat_b.x() + n_quat_b.y() * n_quat_b.z();
215 auto e = std::pow(b, 2) + 4.0 * std::pow(d, 2);
216 auto f = std::sqrt(1.0 - 4.0 * std::pow(disc, 2));
217 auto g = std::pow(a, 2) + 4.0 * std::pow(c, 2);
219 Eigen::Matrix<typename Derived::Scalar, 3, 4> J;
221 (2.0 * (-b * n_quat_b.w() + 4.0 * d * n_quat_b.x())) / e,
222 (2.0 * (-b * n_quat_b.z() + 4.0 * d * n_quat_b.y())) / e,
223 -(2.0 * b * n_quat_b.y()) / e,
224 -(2.0 * b * n_quat_b.x()) / e,
226 -2.0 * n_quat_b.z() / f,
227 2.0 * n_quat_b.w() / f,
228 -2.0 * n_quat_b.x() / f,
229 2.0 * n_quat_b.y() / f,
231 -(2.0 * a * n_quat_b.y()) / g,
232 (2.0 * (-a * n_quat_b.x() + 4.0 * c * n_quat_b.y())) / g,
233 (2.0 * (-a * n_quat_b.w() + 4.0 * c * n_quat_b.z())) / g,
234 -(2.0 * a * n_quat_b.z()) / g;
244template<
typename Derived,
typename DerivedQ>
245[[nodiscard]] Eigen::Matrix3<typename Derived::Scalar>
covQuat2RPY(
const Eigen::MatrixBase<Derived>& covQuat,
const Eigen::QuaternionBase<DerivedQ>& n_quat_b)
248 return J * covQuat * J.transpose();
253template<
typename Derived>
254[[nodiscard]] Eigen::Matrix<typename Derived::Scalar, 4, 4>
covQuat2QuatJacobian(
const Eigen::QuaternionBase<Derived>& beta_quat_alpha)
256 Eigen::Matrix<typename Derived::Scalar, 4, 4> J;
258 beta_quat_alpha.w(), -beta_quat_alpha.z(), beta_quat_alpha.y(), beta_quat_alpha.x(),
259 beta_quat_alpha.z(), beta_quat_alpha.w(), beta_quat_alpha.x(), beta_quat_alpha.y(),
260 -beta_quat_alpha.y(), beta_quat_alpha.x(), beta_quat_alpha.w(), beta_quat_alpha.z(),
261 -beta_quat_alpha.x(), -beta_quat_alpha.y(), -beta_quat_alpha.z(), beta_quat_alpha.w();
269template<
typename Derived>
270[[nodiscard]] Eigen::Matrix<typename Derived::Scalar, 3, 4>
covQuatDiffJacobian(
const Eigen::QuaternionBase<Derived>& quat)
272 Eigen::Matrix<typename Derived::Scalar, 3, 4> J;
274 2.0 * quat.w(), -2.0 * quat.z(), 2.0 * quat.y(), -2.0 * quat.x(),
275 2.0 * quat.z(), 2.0 * quat.w(), -2.0 * quat.x(), -2.0 * quat.y(),
276 -2.0 * quat.y(), 2.0 * quat.x(), 2.0 * quat.w(), -2.0 * quat.z();
285template<
typename Scalar>
289 Eigen::AngleAxis<Scalar> zAngle(-omega_ie * time, Eigen::Vector3<Scalar>::UnitZ());
291 return Eigen::Quaternion<Scalar>(zAngle).normalized();
298template<
typename Scalar>
301 return e_Quat_i(time, omega_ie).conjugate();
308template<
typename Scalar>
309[[nodiscard]] Eigen::Quaternion<Scalar>
e_Quat_n(
const Scalar& latitude,
const Scalar& longitude)
313 Eigen::AngleAxis<Scalar> longitudeAngle(longitude, Eigen::Vector3<Scalar>::UnitZ());
314 Eigen::AngleAxis<Scalar> latitudeAngle(-M_PI_2 - latitude, Eigen::Vector3<Scalar>::UnitY());
316 return (longitudeAngle * latitudeAngle).normalized();
323template<
typename Scalar>
324[[nodiscard]] Eigen::Quaternion<Scalar>
n_Quat_e(
const Scalar& latitude,
const Scalar& longitude)
326 return e_Quat_n(latitude, longitude).conjugate();
334template<
typename Scalar>
335[[nodiscard]] Eigen::Quaternion<Scalar>
b_Quat_n(
const Scalar& roll,
const Scalar& pitch,
const Scalar& yaw)
339 Eigen::AngleAxis<Scalar> rollAngle(-roll, Eigen::Vector3<Scalar>::UnitX());
340 Eigen::AngleAxis<Scalar> pitchAngle(-pitch, Eigen::Vector3<Scalar>::UnitY());
341 Eigen::AngleAxis<Scalar> yawAngle(-yaw, Eigen::Vector3<Scalar>::UnitZ());
343 return (rollAngle * pitchAngle * yawAngle).normalized();
349template<
typename Derived>
350[[nodiscard]] Eigen::Quaternion<typename Derived::Scalar>
b_Quat_n(
const Eigen::MatrixBase<Derived>& rollPitchYaw)
352 return b_Quat_n(rollPitchYaw(0), rollPitchYaw(1), rollPitchYaw(2));
360template<
typename Scalar>
361[[nodiscard]] Eigen::Quaternion<Scalar>
n_Quat_b(Scalar roll, Scalar pitch, Scalar yaw)
363 return b_Quat_n(roll, pitch, yaw).conjugate();
369template<
typename Derived>
370[[nodiscard]] Eigen::Quaternion<typename Derived::Scalar>
n_Quat_b(
const Eigen::MatrixBase<Derived>& rollPitchYaw)
372 return n_Quat_b(rollPitchYaw(0), rollPitchYaw(1), rollPitchYaw(2));
380template<
typename Scalar>
381[[nodiscard]] Eigen::Quaternion<Scalar>
b_Quat_p(Scalar mountingAngleX, Scalar mountingAngleY, Scalar mountingAngleZ)
384 Eigen::AngleAxis<Scalar> xAngle(-mountingAngleX, Eigen::Vector3<Scalar>::UnitX());
385 Eigen::AngleAxis<Scalar> yAngle(-mountingAngleY, Eigen::Vector3<Scalar>::UnitY());
386 Eigen::AngleAxis<Scalar> zAngle(-mountingAngleZ, Eigen::Vector3<Scalar>::UnitZ());
388 return (xAngle * yAngle * zAngle).normalized();
396template<
typename Scalar>
397[[nodiscard]] Eigen::Quaternion<Scalar>
p_Quat_b(Scalar mountingAngleX, Scalar mountingAngleY, Scalar mountingAngleZ)
399 return b_Quat_p(mountingAngleX, mountingAngleY, mountingAngleZ).conjugate();
405template<
typename Derived>
406[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
lla2ecef_WGS84(
const Eigen::MatrixBase<Derived>& lla_position)
414template<
typename Derived>
415[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
lla2ecef_GRS80(
const Eigen::MatrixBase<Derived>& lla_position)
423template<
typename Derived>
424[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
ecef2lla_WGS84(
const Eigen::MatrixBase<Derived>& e_position)
435template<
typename Derived>
436[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
ecef2lla_GRS80(
const Eigen::MatrixBase<Derived>& e_position)
450template<
typename DerivedA,
typename DerivedB>
451[[nodiscard]] Eigen::Vector3<typename DerivedA::Scalar>
ecef2ned(
const Eigen::MatrixBase<DerivedA>& e_position,
const Eigen::MatrixBase<DerivedB>& lla_position_ref)
453 const auto& latitude_ref = lla_position_ref(0);
454 const auto& longitude_ref = lla_position_ref(1);
458 Eigen::Matrix3<typename DerivedA::Scalar> R_ne;
460 R_ne << -std::sin(latitude_ref) * std::cos(longitude_ref), -std::sin(latitude_ref) * std::sin(longitude_ref), std::cos(latitude_ref),
461 -std::sin(longitude_ref) , std::cos(longitude_ref) , 0 ,
462 -std::cos(latitude_ref) * std::cos(longitude_ref), -std::cos(latitude_ref) * std::sin(longitude_ref), -std::sin(latitude_ref);
465 return R_ne * (e_position - e_position_ref);
474template<
typename DerivedA,
typename DerivedB>
475[[nodiscard]] Eigen::Vector3<typename DerivedA::Scalar>
ned2ecef(
const Eigen::MatrixBase<DerivedA>& n_position,
const Eigen::MatrixBase<DerivedB>& lla_position_ref)
477 const auto& latitude_ref = lla_position_ref(0);
478 const auto& longitude_ref = lla_position_ref(1);
482 Eigen::Matrix3<typename DerivedA::Scalar> R_en;
484 R_en << -std::sin(latitude_ref) * std::cos(longitude_ref), -std::sin(longitude_ref), -std::cos(latitude_ref) * std::cos(longitude_ref),
485 -std::sin(latitude_ref) * std::sin(longitude_ref), std::cos(longitude_ref), -std::cos(latitude_ref) * std::sin(longitude_ref),
486 std::cos(latitude_ref) , 0 , -std::sin(latitude_ref) ;
489 return e_position_ref + R_en * n_position;
496template<
typename Derived>
497[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
pz90toWGS84_pos(
const Eigen::MatrixBase<Derived>& pz90_pos)
499 typename Derived::Scalar m = -0.008e-6;
500 auto omega_x =
static_cast<typename Derived::Scalar
>(-2.3_mas);
501 auto omega_y =
static_cast<typename Derived::Scalar
>(3.54_mas);
502 auto omega_z =
static_cast<typename Derived::Scalar
>(-4.21_mas);
503 Eigen::Vector3<typename Derived::Scalar> dX{ -0.013, 0.106, 0.022 };
505 Eigen::Matrix3<typename Derived::Scalar> T;
506 T << 1, -omega_z, omega_y,
507 omega_z, 1, -omega_x,
508 -omega_y, omega_x, 1;
510 return 1.0 / (1.0 + m) * T * (pz90_pos - dX);
517template<
typename DerivedA,
typename DerivedB>
518[[nodiscard]] Eigen::Vector3<typename DerivedA::Scalar>
pz90toWGS84(
const Eigen::MatrixBase<DerivedA>& pz90,
const Eigen::MatrixBase<DerivedB>& pz90_pos)
528template<
typename Derived>
529[[nodiscard]] Eigen::Vector3<typename Derived::Scalar>
sph2ecef(
const Eigen::MatrixBase<Derived>& position_s,
530 const typename Derived::Scalar& elevation,
531 const typename Derived::Scalar& azimuth)
533 Eigen::Matrix3<typename Derived::Scalar> R_se;
534 R_se << std::sin(elevation) * std::cos(azimuth), std::cos(elevation) * std::cos(azimuth), -std::sin(azimuth),
535 std::sin(elevation) * std::sin(azimuth), std::cos(elevation) * std::sin(azimuth), std::cos(azimuth),
536 std::cos(elevation), -std::sin(elevation), 0.0;
538 return R_se * position_s;
Functions concerning the ellipsoid model.
Utility class for logging to console and file.
static constexpr double a
Semi-major axis = equatorial radius.
static constexpr double b
Semi-minor axis = polar radius.
static constexpr double e_squared
Square of the first eccentricity of the ellipsoid.
static constexpr double b
Semi-minor axis = polar radius.
static constexpr double a
Semi-major axis = equatorial radius.
static constexpr double e_squared
Square of the first eccentricity of the ellipsoid.
static constexpr double omega_ie
Nominal mean angular velocity of the Earth in [rad/s].
Eigen::Vector3< typename Derived::Scalar > ecef2lla(const Eigen::MatrixBase< Derived > &e_position, double a, double b, double e_squared)
Converts Earth-centered-Earth-fixed coordinates into latitude, longitude and altitude.
Eigen::Vector3< typename Derived::Scalar > lla2ecef(const Eigen::MatrixBase< Derived > &lla_position, double a, double e_squared)
Converts latitude, longitude and altitude into Earth-centered-Earth-fixed coordinates.
Eigen::Matrix< typename Derived::Scalar, 3, 4 > covQuatDiffJacobian(const Eigen::QuaternionBase< Derived > &quat)
Calculates the Jacobian to convert an attitude represented in quaternions into the difference of the ...
Eigen::Vector3< typename Derived::Scalar > ecef2lla_WGS84(const Eigen::MatrixBase< Derived > &e_position)
Converts Earth-centered-Earth-fixed coordinates into latitude, longitude and altitude using WGS84.
Eigen::Matrix3< typename Derived::Scalar > covQuat2RPY(const Eigen::MatrixBase< Derived > &covQuat, const Eigen::QuaternionBase< DerivedQ > &n_quat_b)
Converts a covariance for an attitude represented in quaternion form into a covariance for Euler ange...
Eigen::Quaternion< Scalar > i_Quat_e(Scalar time, Scalar omega_ie=InsConst::omega_ie)
Quaternion for rotations from Earth-centered-Earth-fixed to inertial frame.
Eigen::Matrix< typename Derived::Scalar, 4, 4 > covQuat2QuatJacobian(const Eigen::QuaternionBase< Derived > &beta_quat_alpha)
Calculates the Jacobian to convert an attitude represented in quaternions from one frame into another...
Eigen::Vector3< typename DerivedA::Scalar > ecef2ned(const Eigen::MatrixBase< DerivedA > &e_position, const Eigen::MatrixBase< DerivedB > &lla_position_ref)
Converts ECEF coordinates into local NED coordinates.
Eigen::Quaternion< Scalar > b_Quat_n(const Scalar &roll, const Scalar &pitch, const Scalar &yaw)
Quaternion for rotations from navigation to body frame.
Eigen::Quaternion< Scalar > e_Quat_i(Scalar time, Scalar omega_ie=InsConst::omega_ie)
Quaternion for rotations from inertial to Earth-centered-Earth-fixed frame.
Eigen::Vector3< typename Derived::Scalar > pz90toWGS84_pos(const Eigen::MatrixBase< Derived > &pz90_pos)
Converts PZ-90.11 coordinates to WGS84 coordinates.
Eigen::Quaternion< Scalar > b_Quat_p(Scalar mountingAngleX, Scalar mountingAngleY, Scalar mountingAngleZ)
Quaternion for rotations from platform to body frame.
Eigen::Vector3< typename DerivedA::Scalar > pz90toWGS84(const Eigen::MatrixBase< DerivedA > &pz90, const Eigen::MatrixBase< DerivedB > &pz90_pos)
Converts PZ-90.11 vectors to WGS84 frame.
Eigen::Vector3< typename Derived::Scalar > sph2ecef(const Eigen::MatrixBase< Derived > &position_s, const typename Derived::Scalar &elevation, const typename Derived::Scalar &azimuth)
Converts spherical Earth-centered-Earth-fixed coordinates into cartesian coordinates.
Eigen::Quaternion< Scalar > e_Quat_n(const Scalar &latitude, const Scalar &longitude)
Quaternion for rotations from navigation to Earth-fixed frame.
Eigen::Matrix< typename Derived::Scalar, 4, 3 > covRPY2quatJacobian(const Eigen::QuaternionBase< Derived > &n_quat_b)
Calculates the Jacobian to convert an attitude represented in Euler angels (roll, pitch,...
Eigen::Quaternion< Scalar > n_Quat_e(const Scalar &latitude, const Scalar &longitude)
Quaternion for rotations from Earth-fixed to navigation frame.
Eigen::Quaternion< Scalar > p_Quat_b(Scalar mountingAngleX, Scalar mountingAngleY, Scalar mountingAngleZ)
Quaternion for rotations from body to platform frame.
Eigen::Vector3< typename Derived::Scalar > quat2eulerZYX(const Eigen::QuaternionBase< Derived > &q)
Converts the quaternion to Euler rotation angles with rotation sequence ZYX.
Eigen::Matrix< typename Derived::Scalar, 3, 4 > covQuat2RPYJacobian(const Eigen::QuaternionBase< Derived > &n_quat_b)
Calculates the Jacobian to convert an attitude represented in quaternions into a covariance in Euler ...
Eigen::Vector3< typename Derived::Scalar > lla2ecef_GRS80(const Eigen::MatrixBase< Derived > &lla_position)
Converts latitude, longitude and altitude into Earth-centered-Earth-fixed coordinates using GRS90.
Eigen::Matrix4< typename Derived::Scalar > covRPY2quat(const Eigen::MatrixBase< Derived > &covRPY, const Eigen::QuaternionBase< DerivedQ > &n_quat_b)
Converts a covariance for an attitude represented in Euler angels (roll, pitch, yaw) into a covarianc...
Eigen::Vector3< typename DerivedA::Scalar > ned2ecef(const Eigen::MatrixBase< DerivedA > &n_position, const Eigen::MatrixBase< DerivedB > &lla_position_ref)
Converts local NED coordinates into ECEF coordinates.
Eigen::Vector3< typename Derived::Scalar > lla2ecef_WGS84(const Eigen::MatrixBase< Derived > &lla_position)
Converts latitude, longitude and altitude into Earth-centered-Earth-fixed coordinates using WGS84.
Eigen::Vector3< typename Derived::Scalar > ecef2lla_GRS80(const Eigen::MatrixBase< Derived > &e_position)
Converts Earth-centered-Earth-fixed coordinates into latitude, longitude and altitude using GRS90.
Eigen::Quaternion< Scalar > n_Quat_b(Scalar roll, Scalar pitch, Scalar yaw)
Quaternion for rotations from body to navigation frame.
constexpr auto deg2rad(const T °)
Convert Degree to Radians.
Scalar calcEarthRadius_E(const Scalar &latitude, const Scalar &a=InsConst::WGS84::a, const Scalar &e_squared=InsConst::WGS84::e_squared)
Calculates the East/West (prime vertical) earth radius.