|
|
@@ -107,9 +107,54 @@ unsigned int gp_val(const char *db, unsigned char count) {
|
|
|
return out;
|
|
|
}
|
|
|
|
|
|
+#define SPEED_SAMPLE_CAP_MULT 3 /* cap any single sample at this multiple of the running average... */
|
|
|
+#define SPEED_SAMPLE_CAP_MIN_SAMPLES 60 /* ...but only once the average actually means something, ... */
|
|
|
+#define SPEED_SAMPLE_CAP_FLOOR_X100 2000 /* ...and never cap below this (20 km/h): the cap is a backstop against
|
|
|
+ * a single wild outlier, not a speed limit - persistence (below) is what
|
|
|
+ * lets a genuinely sustained fast segment (car/train) through. */
|
|
|
+#define SPEED_SAMPLE_HIGH_HISTORY_LEN 7 /* consider this many of the most recent samples... */
|
|
|
+#define SPEED_SAMPLE_HIGH_HISTORY_MASK ((1U << SPEED_SAMPLE_HIGH_HISTORY_LEN) - 1)
|
|
|
+#define SPEED_SAMPLE_HIGH_MIN_COUNT 5 /* ...and once this many of them were above the cap, stop clamping:
|
|
|
+ * a single spike (e.g. the 200 km/h one-second glitch seen on a real
|
|
|
+ * hike) never reaches this, but a sustained fast segment does within a
|
|
|
+ * few seconds. */
|
|
|
+
|
|
|
+/* Bit history of "was this raw sample above the cap" for the last
|
|
|
+ * SPEED_SAMPLE_HIGH_HISTORY_LEN samples (bit 0: most recent). Static file
|
|
|
+ * scope, not part of System - it's working state for the cap logic only,
|
|
|
+ * nothing else needs it. */
|
|
|
+static unsigned char speed_sample_high_history;
|
|
|
+
|
|
|
+static void speed_sample_process(double speed_kmh) {
|
|
|
+ unsigned char paused = System.tracking_paused || System.tracking_auto_paused;
|
|
|
+ unsigned char fix_trustworthy = gps_fix_trustworthy();
|
|
|
+
|
|
|
+ System.speed = speed_kmh+0.5;
|
|
|
+
|
|
|
+ /* Average speed: fixed-point accumulation of instantaneous samples,
|
|
|
+ * excluding time spent paused, instead of dividing distance by time. */
|
|
|
+ if (!paused && fix_trustworthy) {
|
|
|
+ unsigned long int sample_x100 = (unsigned long int)(speed_kmh*100.0 + 0.5);
|
|
|
+ if (System.speed_sample_count >= SPEED_SAMPLE_CAP_MIN_SAMPLES) {
|
|
|
+ unsigned long int avg_x100 = System.speed_accum_x100 / System.speed_sample_count;
|
|
|
+ unsigned long int cap_x100 = avg_x100 * SPEED_SAMPLE_CAP_MULT;
|
|
|
+ unsigned char is_high;
|
|
|
+ if (cap_x100 < SPEED_SAMPLE_CAP_FLOOR_X100)
|
|
|
+ cap_x100 = SPEED_SAMPLE_CAP_FLOOR_X100;
|
|
|
+ is_high = sample_x100 > cap_x100;
|
|
|
+ speed_sample_high_history = (speed_sample_high_history << 1) | is_high;
|
|
|
+ if (is_high && __builtin_popcount(speed_sample_high_history & SPEED_SAMPLE_HIGH_HISTORY_MASK) < SPEED_SAMPLE_HIGH_MIN_COUNT)
|
|
|
+ sample_x100 = cap_x100; /* not (yet) persistent: clamp as a backstop against a lone spike */
|
|
|
+ }
|
|
|
+ System.speed_accum_x100 += sample_x100;
|
|
|
+ System.speed_sample_count++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static time_t gp_rmc_parse(const char *str) {
|
|
|
const char *p;
|
|
|
struct tm tmc;
|
|
|
+ double speed_knots;
|
|
|
|
|
|
p = gp_col(str, 1); /* Get h:m:s */
|
|
|
if (!p)
|
|
|
@@ -137,7 +182,17 @@ static time_t gp_rmc_parse(const char *str) {
|
|
|
}
|
|
|
|
|
|
FLAGS |= F_GPSOK;
|
|
|
-
|
|
|
+
|
|
|
+ /* Speed over ground, from this same sentence (and the same epoch whose
|
|
|
+ * status field was just checked above), in knots - converted to km/h.
|
|
|
+ * Not VTG: some receivers omit VTG or send a shorter form of it, and RMC
|
|
|
+ * is the one sentence essentially every NMEA GNSS module emits. */
|
|
|
+ p = gp_col(str, 7);
|
|
|
+ if (p && *p) {
|
|
|
+ xatof(&p, &speed_knots);
|
|
|
+ speed_sample_process(speed_knots * 1.852);
|
|
|
+ }
|
|
|
+
|
|
|
return utc;
|
|
|
}
|
|
|
|
|
|
@@ -212,45 +267,6 @@ unsigned char gps_fix_trustworthy(void) {
|
|
|
&& System.hdop_x100 <= FIX_QUALITY_HDOP_MAX_X100;
|
|
|
}
|
|
|
|
|
|
-#define SPEED_SAMPLE_CAP_MULT 3 /* cap any single sample at this multiple of the running average... */
|
|
|
-#define SPEED_SAMPLE_CAP_MIN_SAMPLES 60 /* ...but only once the average actually means something, ... */
|
|
|
-#define SPEED_SAMPLE_CAP_FLOOR_X100 2000 /* ...and never cap below this (20 km/h): the cap is a backstop against
|
|
|
- * wild outliers, not a speed limit - a car/train session must still
|
|
|
- * get through a genuinely fast segment. */
|
|
|
-
|
|
|
-static void gp_vtg_parse(const char *str) {
|
|
|
- const char *p;
|
|
|
- double speed;
|
|
|
- unsigned char paused = System.tracking_paused || System.tracking_auto_paused;
|
|
|
- unsigned char fix_trustworthy = gps_fix_trustworthy();
|
|
|
-
|
|
|
- p = gp_col(str, 9);
|
|
|
- if (!p || *p == 'N') /* Not valid, or field missing on this receiver's NMEA dialect */
|
|
|
- return;
|
|
|
-
|
|
|
- p = gp_col(str, 7); /* speed in km/h */
|
|
|
- if (!p)
|
|
|
- return;
|
|
|
- xatof(&p, &speed);
|
|
|
- System.speed = speed+0.5;
|
|
|
-
|
|
|
- /* Average speed: fixed-point accumulation of instantaneous samples,
|
|
|
- * excluding time spent paused, instead of dividing distance by time. */
|
|
|
- if (!paused && fix_trustworthy) {
|
|
|
- unsigned long int sample_x100 = (unsigned long int)(speed*100.0 + 0.5);
|
|
|
- if (System.speed_sample_count >= SPEED_SAMPLE_CAP_MIN_SAMPLES) {
|
|
|
- unsigned long int avg_x100 = System.speed_accum_x100 / System.speed_sample_count;
|
|
|
- unsigned long int cap_x100 = avg_x100 * SPEED_SAMPLE_CAP_MULT;
|
|
|
- if (cap_x100 < SPEED_SAMPLE_CAP_FLOOR_X100)
|
|
|
- cap_x100 = SPEED_SAMPLE_CAP_FLOOR_X100;
|
|
|
- if (sample_x100 > cap_x100)
|
|
|
- sample_x100 = cap_x100;
|
|
|
- }
|
|
|
- System.speed_accum_x100 += sample_x100;
|
|
|
- System.speed_sample_count++;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static void gp_gsa_parse(const char *str) {
|
|
|
const char *p;
|
|
|
double hdop;
|
|
|
@@ -346,10 +362,6 @@ time_t gps_parse(const char *str) { /* Get all required data from NMEA sentences
|
|
|
gp_gga_parse(str);
|
|
|
return 0;
|
|
|
}
|
|
|
- if (!gp_comp(str, PSTR("GPVTG")) || !gp_comp(str, PSTR("GNVTG"))) {
|
|
|
- gp_vtg_parse(str);
|
|
|
- return 0;
|
|
|
- }
|
|
|
if (!gp_comp(str, PSTR("GPGSA")) || !gp_comp(str, PSTR("GNGSA")) || !gp_comp(str, PSTR("BDGSA")) || !gp_comp(str, PSTR("GAGSA"))) {
|
|
|
gp_gsa_parse(str);
|
|
|
return 0;
|