Pārlūkot izejas kodu

Read speed over ground from RMC instead of VTG

Some receivers omit VTG entirely or send a shorter form of it, while
RMC is the one sentence essentially every NMEA GNSS module emits, so
sourcing speed from it is more robust. The outlier-capping logic
(speed_sample_process()) is unchanged, just reused from the new call
site.
k4be 1 nedēļu atpakaļ
vecāks
revīzija
4486aa8438
1 mainītis faili ar 56 papildinājumiem un 44 dzēšanām
  1. 56 44
      soft/nmea.c

+ 56 - 44
soft/nmea.c

@@ -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;