mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-16 00:21:35 +10:00
gps: replace GeoCoord::latLongToMeter's spherical trig with equirectangular approximation (#11184)
This commit is contained in:
@@ -88,6 +88,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MESHTASTIC_PREHOP_DROP 1
|
||||
#endif
|
||||
|
||||
// Use polynomial approximations for trigonometric functions to save flash.
|
||||
// Override with -D MESHTASTIC_TRIG_APPROX=0 for exact trig for special use cases e.g. close to Earth's poles.
|
||||
#ifndef MESHTASTIC_TRIG_APPROX
|
||||
#define MESHTASTIC_TRIG_APPROX 1
|
||||
#endif
|
||||
|
||||
// Debug/test only: let a wired client (serial/TCP) inject frames into the RX pipeline as if they had
|
||||
// arrived over LoRa - a SIMULATOR_APP ToRadio packet is delivered through the real receive path on real
|
||||
// hardware (see MeshService::injectAsReceived). This forges over-the-air traffic, so it MUST stay 0 in
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "GeoCoord.h"
|
||||
#include "configuration.h"
|
||||
#include <cmath>
|
||||
|
||||
// Narrow a UTM meter value to its unsigned field, clamping non-finite/negative/oversized inputs: an
|
||||
@@ -433,6 +434,43 @@ void GeoCoord::convertWGS84ToOSGB36(const double lat, const double lon, double &
|
||||
//(airyA*airyA/(airyA / sqrt(1 - airyEcc*sin(osgb.latitude)*sin(osgb.latitude)))); // Not used, no OSTN data
|
||||
}
|
||||
|
||||
#if MESHTASTIC_TRIG_APPROX
|
||||
// cos(x) minimax approx for x in [-pi/2, pi/2] ("cos_52"): https://www.ganssle.com/approx.htm
|
||||
static double cosLatitudeApprox(double latRad)
|
||||
{
|
||||
constexpr double c1 = 0.9999932946, c2 = -0.4999124376, c3 = 0.0414877472, c4 = -0.0012712095;
|
||||
double x2 = latRad * latRad;
|
||||
return c1 + x2 * (c2 + x2 * (c3 + c4 * x2));
|
||||
}
|
||||
|
||||
/// Approximate distance in meters via equirectangular projection (not exact spherical trig).
|
||||
/// <1% error to ~500km, degrading near the poles at long range (see test_geocoord_distance).
|
||||
float GeoCoord::latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
|
||||
{
|
||||
// Don't do math if the points are the same
|
||||
if (lat_a == lat_b && lng_a == lng_b)
|
||||
return 0.0;
|
||||
|
||||
double a1 = lat_a / DEG_CONVERT;
|
||||
double a2 = lng_a / DEG_CONVERT;
|
||||
double b1 = lat_b / DEG_CONVERT;
|
||||
double b2 = lng_b / DEG_CONVERT;
|
||||
|
||||
double meanLat = (a1 + b1) / 2;
|
||||
double dLng = b2 - a2;
|
||||
// Wrap to [-PI, PI]: unlike cos()/sin(), a raw longitude difference doesn't handle points that
|
||||
// straddle the antimeridian (e.g. 179.9 and -179.9 are ~0.2 degrees apart, not ~360).
|
||||
if (dLng > PI)
|
||||
dLng -= 2 * PI;
|
||||
else if (dLng < -PI)
|
||||
dLng += 2 * PI;
|
||||
double x = dLng * cosLatitudeApprox(meanLat);
|
||||
double y = b1 - a1;
|
||||
double tt = sqrt(x * x + y * y);
|
||||
|
||||
return (float)(6366000 * tt);
|
||||
}
|
||||
#else
|
||||
/// Ported from my old java code, returns distance in meters along the globe
|
||||
/// surface (by Haversine formula)
|
||||
float GeoCoord::latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
|
||||
@@ -456,6 +494,7 @@ float GeoCoord::latLongToMeter(double lat_a, double lng_a, double lat_b, double
|
||||
|
||||
return (float)(6366000 * tt);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Computes the bearing in degrees between two points on Earth. Ported from my
|
||||
|
||||
Reference in New Issue
Block a user