11#if PC_ENABLE_NTRIP_CASTER
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 pc_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 pc_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 pc_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 pc_gnss_survey_reset(GnssSurvey *s)
81 memset(s, 0,
sizeof(*s));
84void pc_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 pc_gnss_survey_add_geodetic(GnssSurvey *s,
const GnssGeodetic *g)
108 pc_gnss_geodetic_to_ecef(g, &e);
109 pc_gnss_survey_add_ecef(s, &e);
112uint32_t pc_gnss_survey_count(
const GnssSurvey *s)
117bool pc_gnss_survey_mean(
const GnssSurvey *s, GnssEcef *out)
123 double n = (double)s->count;
124 out->x = s->ox + s->sdx / n;
125 out->y = s->oy + s->sdy / n;
126 out->z = s->oz + s->sdz / n;
130double pc_gnss_survey_accuracy_m(
const GnssSurvey *s)
136 double n = (double)s->count;
137 double mx = s->sdx / n;
138 double my = s->sdy / n;
139 double mz = s->sdz / n;
140 double vx = s->sdx2 / n - mx * mx;
141 double vy = s->sdy2 / n - my * my;
142 double vz = s->sdz2 / n - mz * mz;
155 return sqrt(vx + vy + vz);
158bool pc_gnss_survey_complete(
const GnssSurvey *s, uint32_t min_obs,
double acc_limit_m)
160 return s->count >= min_obs && s->count >= 2 && pc_gnss_survey_accuracy_m(s) <= acc_limit_m;
172bool dm_to_deg(
const char *field, uint8_t len,
double *out)
174 if (!field || len == 0)
178 const char *end = field;
184 double deg = (double)(
long)(dm / 100.0);
185 double minutes = dm - deg * 100.0;
186 *out = deg + minutes / 60.0;
191bool pc_gnss_gga_to_geodetic(
const Nmea0183 *m, GnssGeodetic *out)
193 if (!m || !out || m->type[0] !=
'G' || m->type[1] !=
'G' || m->type[2] !=
'A')
197 if (m->field_count < 10)
203 if (!pc_nmea0183_field_int(m, 6, &quality) || quality <= 0)
210 if (!dm_to_deg(m->fields[2], m->field_len[2], &lat))
214 if (!dm_to_deg(m->fields[4], m->field_len[4], &lon))
218 if (m->field_len[3] == 1 && (m->fields[3][0] ==
'S' || m->fields[3][0] ==
's'))
222 if (m->field_len[5] == 1 && (m->fields[5][0] ==
'W' || m->fields[5][0] ==
'w'))
228 if (!pc_nmea0183_field_float(m, 9, &msl))
233 pc_nmea0183_field_float(m, 11, &geoid);
237 out->height_m = (double)msl + (
double)geoid;
241bool pc_gnss_survey_add_gga(GnssSurvey *s,
const Nmea0183 *m)
244 if (!pc_gnss_gga_to_geodetic(m, &g))
248 pc_gnss_survey_add_geodetic(s, &g);
NMEA 0183 sentence codec (PC_ENABLE_NMEA0183) - the marine / GPS ASCII protocol.
Tiny no-stdlib base-10 number parsers (strtol/strtoul/strtof replacements).
double pc_strtod(const char *s, const char **end)
Parse a double (integer[.frac][e[+/-]exp]); sets end (or to s if none).