Browse Source

autopause: smooth speed for resume, add quarter-window distance check

k4be 1 ngày trước cách đây
mục cha
commit
241b5fb20a
2 tập tin đã thay đổi với 66 bổ sung và 27 xóa
  1. 61 25
      soft/autopause.c
  2. 5 2
      soft/common.h

+ 61 - 25
soft/autopause.c

@@ -31,6 +31,29 @@ struct auto_pause_s auto_pause;
 #define AUTO_PAUSE_RESUME_HISTORY_MASK	((1U << AUTO_PAUSE_RESUME_HISTORY_LEN) - 1)
 #define AUTO_PAUSE_RESUME_MIN_COUNT	5
 
+/* Raw NMEA-reported ground speed (System.speed_x10) is single-epoch Doppler
+ * output with no smoothing - real recorded hikes show it read a few km/h for
+ * several consecutive seconds even while the receiver is stationary and
+ * HDOP/sat count are excellent (multipath/receiver noise, not position
+ * error, and correlated over several seconds rather than independent from
+ * sample to sample). A strict "N consecutive samples over threshold"
+ * debounce is exactly the shape most exposed to this - and even a plain
+ * majority-of-a-window vote on the raw value isn't enough on its own, since
+ * this noise is persistent enough to occasionally dominate a short window
+ * outright. Smooth the speed first (exponential smoothing, same idea as
+ * elevation_process()'s altitude smoothing) to knock down the bulk of the
+ * noise, *then* still require the smoothed value to clear the threshold for
+ * most of a window before resuming - so a single lingering noisy patch
+ * cannot itself resume a genuinely stationary logger, but sustained real
+ * movement (which pushes the smoothed value well past the threshold, not
+ * just brushing it) still resumes within several seconds. */
+#define SPEED_SMOOTH_ALPHA		0.25	/* exponential smoothing factor per sample (~1s apart) */
+#define SPEED_RESUME_HISTORY_LEN	7	/* same window length as the other resume-history checks */
+#define SPEED_RESUME_HISTORY_MASK	((1U << SPEED_RESUME_HISTORY_LEN) - 1)
+#define SPEED_RESUME_MIN_COUNT		6	/* stricter majority than distance/heading: speed alone has no
+						 * corroborating signal (no distance-from-anchor or heading check
+						 * backs it up), so it needs to be almost the whole window */
+
 /* Distance-from-anchor alone can't tell a real departure from a smooth
  * position drift with no real movement behind it: a satellite geometry
  * change (a few satellites dropping out/back in) can walk the computed fix
@@ -43,7 +66,7 @@ struct auto_pause_s auto_pause;
  * HEADING_STEP_MIN_DIST_M: bearing is meaningless noise over a sub-meter step
  * (the two fixes are within GPS jitter of each other), and would otherwise
  * make genuinely stationary jitter look "inconsistent" for the wrong reason. */
-#define HEADING_STEP_MIN_DIST_M		1.5	/* ignore bearing between two samples closer than this */
+#define HEADING_STEP_MIN_DIST_M		3	/* ignore bearing between two samples closer than this */
 #define HEADING_CONSISTENT_MAX_DEG	60	/* max angle from the previous step's bearing to still agree */
 #define HEADING_HISTORY_LEN		7	/* same window length as the distance resume history above */
 #define HEADING_HISTORY_MASK		((1U << HEADING_HISTORY_LEN) - 1)
@@ -62,12 +85,11 @@ static void auto_unpause(void) {
 static void auto_pause_activate(void) {
 	System.tracking_auto_paused = 1;
 	pause_time_start();
-	/* 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;
+	/* Otherwise, speed-history progress from just before this decision could
+	 * carry over and complete on the very next few samples, instantly
+	 * flip-flopping straight back out of the pause this just activated. */
+	auto_pause.speed_high_history = 0;
+	auto_pause.smoothed_speed_valid = 0;
 	/* Anchor the resume-by-distance check on where the pause actually
 	 * started, then let it drift towards the running average of positions
 	 * seen while paused (steadier than any single fix - see
@@ -96,7 +118,8 @@ void auto_pause_clear_manual_resume(void) {
 	System.tracking_auto_paused = 0;
 	auto_pause.prev_distance = System.distance;
 	auto_pause.point_counter = 0;
-	auto_pause.speed_counter = 0;
+	auto_pause.speed_high_history = 0;
+	auto_pause.smoothed_speed_valid = 0;
 }
 
 /* Call once, whenever fix_gap_detected() reports a gap on the point about to
@@ -108,7 +131,8 @@ void auto_pause_clear_manual_resume(void) {
  * threshold is meant to require (persistent time actually stationary). */
 void auto_pause_reset_gap(void) {
 	auto_pause.point_counter = 0;
-	auto_pause.speed_counter = 0;
+	auto_pause.speed_high_history = 0;
+	auto_pause.smoothed_speed_valid = 0;
 	auto_pause.prev_distance = System.distance;
 	if (System.tracking_auto_paused) {
 		/* Same reasoning as auto_pause_activate()'s anchor init: re-anchor on
@@ -130,27 +154,33 @@ void auto_pause_process(void) {
 		System.tracking_auto_paused = 0;
 		auto_pause.prev_distance = System.distance;
 		auto_pause.point_counter = 0;
-		auto_pause.speed_counter = 0;
+		auto_pause.speed_high_history = 0;
+		auto_pause.smoothed_speed_valid = 0;
 		return;
 	}
 	/* 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. */
+	 * could otherwise satisfy the persistence check below on its own and
+	 * resume a logger that is actually sitting still. Such a sample is
+	 * simply ignored - it shifts nothing into the history below - 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_x10 >= (unsigned int)System.conf.auto_pause_speed*10) { /* 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;
-			}
+		unsigned char speed_high;
+		if (!auto_pause.smoothed_speed_valid) {
+			auto_pause.smoothed_speed_x10 = (float)System.speed_x10;
+			auto_pause.smoothed_speed_valid = 1;
 		} else {
-			auto_pause.speed_counter = 0;
+			auto_pause.smoothed_speed_x10 += SPEED_SMOOTH_ALPHA * ((float)System.speed_x10 - auto_pause.smoothed_speed_x10);
+		}
+		speed_high = auto_pause.smoothed_speed_x10 >= (float)((unsigned int)System.conf.auto_pause_speed*10);
+		auto_pause.speed_high_history = (auto_pause.speed_high_history << 1) | speed_high;
+		if (__builtin_popcount(auto_pause.speed_high_history & SPEED_RESUME_HISTORY_MASK) >= SPEED_RESUME_MIN_COUNT) {
+			auto_pause.point_counter = 0;
+			auto_pause.speed_high_history = 0;
+			auto_unpause();
+			return;
 		}
 	}
 	/* Resume-by-distance: compared against an anchor updated to the running
@@ -203,13 +233,16 @@ void auto_pause_process(void) {
 				}
 				auto_pause.prev_bearing = this_bearing;
 				auto_pause.prev_bearing_valid = 1;
+				auto_pause.prev_location = location;
 			}
 			/* else: too small a step to trust its bearing - leave prev_bearing
 			 * and heading_consistent_history untouched, neither confirming nor
 			 * breaking the run of consistent headings. */
 		}
-		auto_pause.prev_location = location;
-		auto_pause.prev_location_valid = 1;
+		if (!auto_pause.prev_location_valid) {
+			auto_pause.prev_location = location;
+			auto_pause.prev_location_valid = 1;
+		}
 
 		if (__builtin_popcount(auto_pause.resume_high_history & AUTO_PAUSE_RESUME_HISTORY_MASK) >= AUTO_PAUSE_RESUME_MIN_COUNT
 			&& __builtin_popcount(auto_pause.heading_consistent_history & HEADING_HISTORY_MASK) >= HEADING_MIN_COUNT) {
@@ -218,6 +251,8 @@ void auto_pause_process(void) {
 			return;
 		}
 	}
+	if (auto_pause.point_counter == System.conf.auto_pause_time - System.conf.auto_pause_time/4)
+		auto_pause.quarter_distance = System.distance;
 	if (++auto_pause.point_counter < System.conf.auto_pause_time)
 		return;
 	auto_pause.point_counter = 0;
@@ -235,6 +270,7 @@ void auto_pause_process(void) {
 		 * a run of untrustworthy fixes must not be allowed to decide a pause
 		 * transition either way here - it's simply skipped for this window. */
 		if ((System.distance - auto_pause.prev_distance)/100 <= System.conf.auto_pause_dist
+			&& (System.distance - auto_pause.quarter_distance)/100 <= System.conf.auto_pause_dist/4
 			&& System.speed_x10 < (unsigned int)System.conf.auto_pause_speed*10)
 			auto_pause_activate(); /* pause: not enough net movement, and not currently moving fast */
 	}

+ 5 - 2
soft/common.h

@@ -152,10 +152,13 @@ struct system_s {
 struct auto_pause_s {
 	unsigned char point_counter;
 	unsigned long int prev_distance;
-	unsigned char speed_counter;
+	unsigned long int quarter_distance; /* System.distance at 3/4 of the current auto-pause window */
+	float smoothed_speed_x10; /* exponentially-smoothed System.speed_x10, to reject correlated Doppler noise */
+	unsigned char smoothed_speed_valid:1;
+	unsigned char speed_high_history; /* bit history: was smoothed speed over the auto-pause-speed threshold */
 	struct location_s pause_anchor; /* running average position while paused */
 	unsigned int anchor_sample_count;
-	unsigned char resume_high_history; /* bit history: was the anchor-distance/speed resume condition met */
+	unsigned char resume_high_history; /* bit history: was the anchor-distance resume condition met */
 	struct location_s prev_location; /* last sample seen while paused, for step-to-step bearing */
 	unsigned char prev_location_valid:1;
 	unsigned char prev_bearing_valid:1;