11#if DETWS_ENABLE_NTRIP_CASTER
16#if DETWS_ENABLE_NMEA0183
24const double WGS84_A = 6378137.0;
25const double WGS84_F = 1.0 / 298.257223563;
26const double WGS84_E2 = WGS84_F * (2.0 - WGS84_F);
27const double DEG2RAD = 0.017453292519943295;
28const double RAD2DEG = 57.29577951308232;
31void gnss_geodetic_to_ecef(
const GnssGeodetic *g, GnssEcef *e)
33 double lat = g->lat_deg * DEG2RAD;
34 double lon = g->lon_deg * DEG2RAD;
35 double slat = sin(lat);
36 double clat = cos(lat);
37 double slon = sin(lon);
38 double clon = cos(lon);
39 double n = WGS84_A / sqrt(1.0 - WGS84_E2 * slat * slat);
40 e->x = (n + g->height_m) * clat * clon;
41 e->y = (n + g->height_m) * clat * slon;
42 e->z = (n * (1.0 - WGS84_E2) + g->height_m) * slat;
45void gnss_ecef_to_geodetic(
const GnssEcef *e, GnssGeodetic *g)
47 double p = sqrt(e->x * e->x + e->y * e->y);
48 g->lon_deg = atan2(e->y, e->x) * RAD2DEG;
51 double b = WGS84_A * (1.0 - WGS84_F);
52 g->lat_deg = (e->z >= 0.0) ? 90.0 : -90.0;
53 g->height_m = fabs(e->z) - b;
56 double lat = atan2(e->z, p * (1.0 - WGS84_E2));
58 for (
int i = 0; i < 6; i++)
60 double slat = sin(lat);
61 n = WGS84_A / sqrt(1.0 - WGS84_E2 * slat * slat);
62 double h = p / cos(lat) - n;
63 lat = atan2(e->z, p * (1.0 - WGS84_E2 * n / (n + h)));
65 g->lat_deg = lat * RAD2DEG;
66 g->height_m = p / cos(lat) - n;
69int64_t gnss_ecef_m_to_01mm(
double metres)
71 double v = metres * 10000.0;
72 return (int64_t)(v >= 0.0 ? v + 0.5 : v - 0.5);
79void gnss_survey_reset(GnssSurvey *s)
81 memset(s, 0,
sizeof(*s));
84void gnss_survey_add_ecef(GnssSurvey *s,
const GnssEcef *e)
93 double dx = e->x - s->ox;
94 double dy = e->y - s->oy;
95 double dz = e->z - s->oz;
105void gnss_survey_add_geodetic(GnssSurvey *s,
const GnssGeodetic *g)
108 gnss_geodetic_to_ecef(g, &e);
109 gnss_survey_add_ecef(s, &e);
112uint32_t gnss_survey_count(
const GnssSurvey *s)
117bool gnss_survey_mean(
const GnssSurvey *s, GnssEcef *out)
121 double n = (double)s->count;
122 out->x = s->ox + s->sdx / n;
123 out->y = s->oy + s->sdy / n;
124 out->z = s->oz + s->sdz / n;
128double gnss_survey_accuracy_m(
const GnssSurvey *s)
132 double n = (double)s->count;
133 double mx = s->sdx / n;
134 double my = s->sdy / n;
135 double mz = s->sdz / n;
136 double vx = s->sdx2 / n - mx * mx;
137 double vy = s->sdy2 / n - my * my;
138 double vz = s->sdz2 / n - mz * mz;
145 return sqrt(vx + vy + vz);
148bool gnss_survey_complete(
const GnssSurvey *s, uint32_t min_obs,
double acc_limit_m)
150 return s->count >= min_obs && s->count >= 2 && gnss_survey_accuracy_m(s) <= acc_limit_m;
157#if DETWS_ENABLE_NMEA0183
162bool dm_to_deg(
const char *field, uint8_t len,
double *out)
164 if (!field || len == 0)
166 const char *end = field;
170 double deg = (double)(
long)(dm / 100.0);
171 double minutes = dm - deg * 100.0;
172 *out = deg + minutes / 60.0;
177bool gnss_gga_to_geodetic(
const Nmea0183 *m, GnssGeodetic *out)
179 if (!m || !out || m->type[0] !=
'G' || m->type[1] !=
'G' || m->type[2] !=
'A')
181 if (m->field_count < 10)
185 if (!nmea0183_field_int(m, 6, &quality) || quality <= 0)
190 if (!dm_to_deg(m->fields[2], m->field_len[2], &lat))
192 if (!dm_to_deg(m->fields[4], m->field_len[4], &lon))
194 if (m->field_len[3] == 1 && (m->fields[3][0] ==
'S' || m->fields[3][0] ==
's'))
196 if (m->field_len[5] == 1 && (m->fields[5][0] ==
'W' || m->fields[5][0] ==
'w'))
200 if (!nmea0183_field_float(m, 9, &msl))
203 nmea0183_field_float(m, 11, &geoid);
207 out->height_m = (double)msl + (
double)geoid;
211bool gnss_survey_add_gga(GnssSurvey *s,
const Nmea0183 *m)
214 if (!gnss_gga_to_geodetic(m, &g))
216 gnss_survey_add_geodetic(s, &g);
GNSS survey-in: WGS84 geodetic <-> ECEF + fixed-position averaging (DETWS_ENABLE_NTRIP_CASTER).
NMEA 0183 sentence codec (DETWS_ENABLE_NMEA0183) - the marine / GPS ASCII protocol.
Tiny no-stdlib base-10 number parsers (strtol/strtoul/strtof replacements).
double det_strtod(const char *s, const char **end)
Parse a double (integer[.frac][e[+/-]exp]); sets end (or to s if none).