|
|
@@ -35,7 +35,14 @@ struct kalman_s {
|
|
|
#define ELEV_SMOOTH_TAU 30.0 /* seconds */
|
|
|
#define ELEV_DEADBAND 5.0 /* meters */
|
|
|
|
|
|
-/* Large-jump detection thresholds (informational logging only, points are not rejected because of these) */
|
|
|
+/* Large-jump detection thresholds. POS_JUMP_THRESHOLD is informational
|
|
|
+ * logging only - the distance-diff spike check above already rejects
|
|
|
+ * inconsistent position jumps regardless of size. ALT_JUMP_THRESHOLD both
|
|
|
+ * logs and (combined with the same spike shape) rejects an inconsistent
|
|
|
+ * altitude jump - unlike position there was previously no altitude
|
|
|
+ * consistency check at all, letting a poor-geometry fix's bogus altitude
|
|
|
+ * (right sats/HDOP, wrong solution - e.g. just after reacquiring signal)
|
|
|
+ * straight into the accepted track. */
|
|
|
#define POS_JUMP_THRESHOLD 100.0 /* meters between consecutive raw fixes */
|
|
|
#define ALT_JUMP_THRESHOLD 50.0 /* meters between consecutive raw fixes */
|
|
|
|
|
|
@@ -63,6 +70,7 @@ static struct gpx_s {
|
|
|
unsigned char avg_count;
|
|
|
unsigned char paused;
|
|
|
unsigned char point_count;
|
|
|
+ time_t last_point_time; /* time of the last point seen by gpx_process_point(), to detect a fix gap */
|
|
|
struct avg_store_s avg_store;
|
|
|
struct location_s last_saved;
|
|
|
struct location_s last_distance_point; /* Last accepted point for distance calculation */
|
|
|
@@ -108,6 +116,8 @@ unsigned char gpx_init(FIL *file) {
|
|
|
gpx.last_distance_point.lat = 0;
|
|
|
gpx.last_distance_point.time = 0;
|
|
|
gpx.elevation.initialized = 0;
|
|
|
+ gpx.last_point_time = 0;
|
|
|
+ gpx.point_count = 0;
|
|
|
|
|
|
gpx.paused = 1; /* make it add a <trkseg> tag */
|
|
|
|
|
|
@@ -190,12 +200,38 @@ unsigned char gpx_close(FIL *file) {
|
|
|
return f_close(file);
|
|
|
}
|
|
|
|
|
|
+/* A fix normally arrives about once a second; treat a longer gap between
|
|
|
+ * accepted points as a fix having been lost and reacquired (regardless of
|
|
|
+ * why the epochs in between were rejected/absent - fix loss, a 2D fix, poor
|
|
|
+ * HDOP, or the receiver simply not producing lines), even without an
|
|
|
+ * explicit auto-pause. A freshly reacquired fix can take a few seconds to
|
|
|
+ * converge (seen after a real signal loss: a nominally-3D, in-range-HDOP fix
|
|
|
+ * still a couple hundred meters off in altitude until more satellites come
|
|
|
+ * in), the same way a fix has never been trusted right after power-on -
|
|
|
+ * so re-run the existing skip_points warm-up, and reset the Kalman/window
|
|
|
+ * state that would otherwise splice the old and new fixes together. */
|
|
|
+#define FIX_GAP_SECONDS 3
|
|
|
+
|
|
|
void gpx_process_point(struct location_s *loc, FIL *file){
|
|
|
float lon_est, lon_err, lat_est, lat_err, dist;
|
|
|
struct location_s *ptr;
|
|
|
static struct location_s nloc;
|
|
|
static struct location_s filtered_loc;
|
|
|
|
|
|
+ if (gpx.last_point_time && loc->time - gpx.last_point_time > FIX_GAP_SECONDS) {
|
|
|
+ if (get_flag(CONFFLAG_VERBOSE_LOG))
|
|
|
+ xputs_P(PSTR("FIX GAP: resetting warm-up/filter state\r\n"));
|
|
|
+ kalman_init(&gpx.kalman[0]);
|
|
|
+ kalman_init(&gpx.kalman[1]);
|
|
|
+ gpx.prev_points.count = 0;
|
|
|
+ gpx.avg_count = 0;
|
|
|
+ gpx.avg_store.lat = 0;
|
|
|
+ gpx.avg_store.lon = 0;
|
|
|
+ gpx.avg_store.time = 0;
|
|
|
+ gpx.point_count = 0;
|
|
|
+ }
|
|
|
+ gpx.last_point_time = loc->time;
|
|
|
+
|
|
|
if (gpx.point_count < System.conf.skip_points) { /* Skipping initial points */
|
|
|
gpx.point_count++;
|
|
|
return;
|
|
|
@@ -251,6 +287,19 @@ void gpx_process_point(struct location_s *loc, FIL *file){
|
|
|
float dist12 = distance(prev_points_get(0), prev_points_get(1));
|
|
|
float dist34 = distance(prev_points_get(2), prev_points_get(3));
|
|
|
float dist32 = distance(prev_points_get(2), prev_points_get(1));
|
|
|
+ /* Same spike test as the distance check below, applied to altitude:
|
|
|
+ * a poor-geometry fix (e.g. right after reacquiring signal) can pass
|
|
|
+ * the satellite-count/HDOP gate in nmea.c yet still carry a wildly
|
|
|
+ * wrong altitude, so catch it here the same way a position spike is
|
|
|
+ * caught - a jump surrounded by two much smaller ones on both sides.
|
|
|
+ * TODO: GSA's VDOP would be a more direct altitude-quality gate than
|
|
|
+ * this shape heuristic, but VDOP is only in GSA, which is parsed
|
|
|
+ * after GGA within the epoch (see nmea.c's gp_gga_parse HDOP
|
|
|
+ * comment) - using it here would need buffering a point across the
|
|
|
+ * epoch boundary until VDOP arrives. Left as a future refinement. */
|
|
|
+ float alt12 = fabs(prev_points_get(0)->alt - prev_points_get(1)->alt);
|
|
|
+ float alt34 = fabs(prev_points_get(2)->alt - prev_points_get(3)->alt);
|
|
|
+ float alt32 = fabs(prev_points_get(2)->alt - prev_points_get(1)->alt);
|
|
|
if (get_flag(CONFFLAG_VERBOSE_LOG))
|
|
|
xprintf(PSTR("New distance: %fm\r\n"), dist32);
|
|
|
if(dist34 > dist12 && dist32 > dist12){
|
|
|
@@ -259,6 +308,12 @@ void gpx_process_point(struct location_s *loc, FIL *file){
|
|
|
log_reject(REJECT_REASON_DISTDIFF);
|
|
|
return;
|
|
|
}
|
|
|
+ if(alt32 > ALT_JUMP_THRESHOLD && alt34 > alt12 && alt32 > alt12){
|
|
|
+ if (get_flag(CONFFLAG_VERBOSE_LOG))
|
|
|
+ xputs_P(PSTR("ALTITUDE DIFF REJECT\r\n"));
|
|
|
+ log_reject(REJECT_REASON_ALTDIFF);
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (dist32 > POS_JUMP_THRESHOLD)
|
|
|
log_jump(0, dist32);
|
|
|
ptr = prev_points_get(PREV_POINTS_LENGTH - 2);
|