Kaynağa Gözat

Harden auto-pause against spurious resume/pause on a noisy or winding track

Speed-based resume no longer trusts a poor fix. A single noisy sample (low
satellites, high HDOP briefly reporting tens of km/h - the exact real
anomaly from an earlier hike log, HDOP 16.2 reporting 44.8 km/h) could
satisfy the existing "3 consecutive above-threshold" debounce on its own and
resume a logger that is actually sitting still. Untrustworthy samples (new
shared gps_fix_trustworthy(), reusing the same fix-validity/HDOP check
already used to gate the speed average) are now simply skipped - neither
counted towards, nor resetting, the debounce - so they can't trigger a
resume but also can't erase real progress from genuine movement.

Distance-based resume now needs more apparent movement (1.5x
auto_pause_dist) to resume than to enter pause: a false pause just costs one
quiet window before the speed check or the next window catches it, but a
false resume from position drift while genuinely stationary pollutes the
moving/stopped split and the odometer for as long as it isn't caught.

While testing this against a real hike log, found and fixed a real flicker
bug in the *existing* algorithm, unrelated to the above: a winding path can
have small net displacement over a whole auto_pause_time window despite
being covered at speed throughout it (out-and-backs, switchbacks), so the
distance check could pause a logger that was never really stopped - and,
since entering pause didn't reset the speed-counter debounce, a sample or
two later the still-live "was already moving" debounce would immediately
fire and resume it again. Now: (1) entering pause resets the speed counter,
so resuming needs fresh samples collected after the pause began, and (2)
entering pause is skipped entirely if the logger is already moving faster
than the resume threshold right now, since pausing it would be pointless.
On the real hike log this eliminated an 8-second pause/resume flicker while
leaving the other 21 track segments and the moving/stopped split unchanged
(one pause shifted by 30s, everything else identical).

These are forward-only behavior changes - GPX/log data already written by a
run is never revisited, matching the constraint that this application can't
retroactively revoke already-written output.
k4be 1 hafta önce
ebeveyn
işleme
2e5dc5e183
4 değiştirilmiş dosya ile 63 ekleme ve 18 silme
  1. 1 0
      gps-test-tool/main.h
  2. 47 11
      soft/autopause.c
  3. 14 7
      soft/nmea.c
  4. 1 0
      soft/nmea.h

+ 1 - 0
gps-test-tool/main.h

@@ -336,6 +336,7 @@ void gps_powered_on(void);
  * of what this tool's output looks like. It calls beep(), which is
  * meaningless on PC and stubbed out here. */
 void auto_pause_process(void);
+unsigned char gps_fix_trustworthy(void);
 static inline void beep(unsigned int len, unsigned char cnt) { (void)len; (void)cnt; }
 
 extern volatile unsigned char reset_cause;

+ 47 - 11
soft/autopause.c

@@ -12,6 +12,16 @@
 
 struct auto_pause_s auto_pause;
 
+/* Resuming from a period of auto-pause because of apparent distance needs
+ * more apparent movement than triggering the pause did in the first place:
+ * position noise while genuinely stationary (multipath, poor sky view) can
+ * still occasionally clear the plain auto_pause_dist threshold over enough
+ * auto_pause_time windows, and unlike a false pause (which just costs one
+ * quiet window before the speed-based check or the next window catches the
+ * resume), a false resume pollutes the moving/stopped split and the
+ * odometer for as long as it isn't caught. */
+#define AUTO_PAUSE_RESUME_DIST_MULT_X100	150	/* 1.5x the configured distance threshold */
+
 static void auto_unpause(void) {
 	if (!System.tracking_auto_paused)
 		return;
@@ -22,6 +32,12 @@ static void auto_unpause(void) {
 
 static void auto_pause_activate(void) {
 	System.tracking_auto_paused = 1;
+	/* Otherwise, speed-counter progress from just before this decision (e.g.
+	 * 2 of the 3 consecutive above-threshold samples the resume check below
+	 * wants) could carry over and complete on the very next sample,
+	 * instantly flip-flopping straight back out of the pause this just
+	 * activated. */
+	auto_pause.speed_counter = 0;
 	log_pause_event(1);
 	beep(50, 3);
 }
@@ -34,25 +50,45 @@ void auto_pause_process(void) {
 		auto_pause.speed_counter = 0;
 		return;
 	}
-	if (System.speed >= System.conf.auto_pause_speed) { /* unpause when set speed is exceeded for 3 consecutive measurements */
-		if (++auto_pause.speed_counter >= 3) {
-			auto_pause.point_counter = 0;
+	/* A poor/invalid fix must not be read as "moving": a single noisy sample
+	 * (e.g. a low-satellite, high-HDOP fix briefly reporting tens of km/h,
+	 * as seen on a real hike where reception dropped for a few seconds)
+	 * could otherwise satisfy the "3 consecutive" debounce below on its own
+	 * and resume a logger that is actually sitting still. Such a sample is
+	 * simply ignored - neither counted towards, nor resetting, the
+	 * consecutive-samples debounce - so it can't itself trigger a resume,
+	 * but also can't erase real progress made by genuine movement. */
+	if (gps_fix_trustworthy()) {
+		if (System.speed >= System.conf.auto_pause_speed) { /* unpause when set speed is exceeded for 3 consecutive measurements */
+			if (++auto_pause.speed_counter >= 3) {
+				auto_pause.point_counter = 0;
+				auto_pause.speed_counter = 0;
+				auto_unpause();
+				return;
+			}
+		} else {
 			auto_pause.speed_counter = 0;
-			auto_unpause();
-			return;
 		}
-	} else {
-		auto_pause.speed_counter = 0;
 	}
 	if (++auto_pause.point_counter < System.conf.auto_pause_time)
 		return;
 	auto_pause.point_counter = 0;
-	if ((System.distance - auto_pause.prev_distance)/100 > System.conf.auto_pause_dist) {
-		if (System.tracking_auto_paused)
+	if (System.tracking_auto_paused) {
+		unsigned long int resume_threshold = (unsigned long int)System.conf.auto_pause_dist
+			* AUTO_PAUSE_RESUME_DIST_MULT_X100 / 100;
+		if ((System.distance - auto_pause.prev_distance)/100 > resume_threshold)
 			auto_unpause(); /* unpause when distance exceeded */
 	} else {
-		if (!System.tracking_auto_paused)
-			auto_pause_activate(); /* pause otherwise */
+		/* Net displacement over this window can be small even while genuinely
+		 * moving throughout it - a winding path folding back on itself, or a
+		 * short out-and-back - and pausing on that alone, only to have the
+		 * very next sample or two immediately satisfy the speed-based resume
+		 * above, is itself a spurious (if brief) pause. If the logger is
+		 * already moving fast enough to resume on its own right now, don't
+		 * pause it in the first place. */
+		if ((System.distance - auto_pause.prev_distance)/100 <= System.conf.auto_pause_dist
+			&& System.speed < System.conf.auto_pause_speed)
+			auto_pause_activate(); /* pause: not enough net movement, and not currently moving fast */
 	}
 	auto_pause.prev_distance = System.distance;
 }

+ 14 - 7
soft/nmea.c

@@ -200,11 +200,19 @@ static void gp_gga_parse(const char *str) {
 	location.time = utc; /* parsed from RMC */
 }
 
-/* Speed samples are only trusted from a valid, reasonably accurate fix:
- * a single sample from a poor fix (e.g. HDOP 16 with only a few satellites)
- * would otherwise distort the whole-session average by itself. */
-#define SPEED_SAMPLE_HDOP_MAX_X100	500	/* reject samples with HDOP > 5.00 */
-#define SPEED_SAMPLE_CAP_MULT		3	/* also cap any single sample at this multiple of the running average... */
+/* A fix is only trusted (for the speed average, and for the auto-pause
+ * speed-based unpause check below) when it is valid, of a known type, and
+ * reasonably accurate: a single sample from a poor fix (e.g. HDOP 16 with
+ * only a few satellites) would otherwise distort the whole-session average,
+ * or - worse - talk the auto-pause logic into resuming a stationary logger. */
+#define FIX_QUALITY_HDOP_MAX_X100	500	/* a fix is untrustworthy once HDOP > 5.00 */
+
+unsigned char gps_fix_trustworthy(void) {
+	return (FLAGS & F_GPSOK) && System.fix_type != FIX_TYPE_UNKNOWN
+		&& 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
@@ -214,8 +222,7 @@ 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 = (FLAGS & F_GPSOK) && System.fix_type != FIX_TYPE_UNKNOWN
-		&& System.hdop_x100 <= SPEED_SAMPLE_HDOP_MAX_X100;
+	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 */

+ 1 - 0
soft/nmea.h

@@ -6,4 +6,5 @@ time_t gps_parse(const char *str);
 UINT get_line(char *buff, UINT sz_buf);
 void gps_initialize(void);
 void check_min_sat_limit(void);
+unsigned char gps_fix_trustworthy(void);