Skip to content

[RFC] Fixed-wing in-flight detection fails during dead reckoning: replace GPS heading dependency with estimator-fused equivalent #11644

@daijoubu

Description

@daijoubu

Problem

INAV supports dead reckoning — continuing navigation after GPS loss using IMU and barometer. However, isProbablyStillFlying() for fixed-wing depends on isGPSHeadingValid(), which requires GPS fix, ≥6 satellites, and ground speed ≥300 cm/s. During dead reckoning, GPS is already absent, so this returns false unconditionally.

The consequence: a pilot flying on dead reckoning who accidentally disarms cannot use IN_FLIGHT_EMERG_REARM to recover. The emergency rearm path is inadvertently blocked by the same GPS failure that triggered dead reckoning in the first place.

This is one instance of a wider pattern. isGPSHeadingValid() appears nine times in the codebase; investigation identified four where it is being used as a flight proxy rather than a GPS protocol check:

File Use Problem
navigation.c:3604 isProbablyStillFlying() FW path Returns false during dead reckoning
navigation_fixedwing.c:767 isFixedWingFlying() — landing detector GPS gate overrides estimator velXY/alt signals already present
servos.c:643 Enable mixer flying mode Marked // TODO: proper flying detection
navigation.c:4686 Gate launch mode activation GPS failure mid-flight allows launch mode to activate while airborne

The remaining five call sites correctly depend on GPS data specifically (IMU attitude fusion, wind estimation, RTH trackback, launch detection, position estimation) and are not proposed for change.


Two approaches

Option A — isPosEstimatorHeadingValid() (surgical replacement)

A direct drop-in for isGPSHeadingValid() at the four call sites. Same proxy logic — valid heading implies forward motion implies flight — but sourced from the estimator's fused velocity rather than raw GPS:

bool isPosEstimatorHeadingValid(void)
{
    return isGPSHeadingValid() ||
           (posControl.actualState.velXY > HEADING_VALID_MIN_SPEED_CMS);
}

posControl.actualState.velXY is already computed by the position estimator from fused IMU/baro/GPS state. Under GPS loss it continues to be valid from IMU integration; it does not go stale when GPS drops. HEADING_VALID_MIN_SPEED_CMS would match the existing GPS threshold (300 cm/s) for consistency.

Change at each call site: replace isGPSHeadingValid() with isPosEstimatorHeadingValid(). No behavioural change when GPS is healthy.

Tradeoffs:

  • Minimal blast radius — one new function, four substitutions
  • Preserves the existing heading-as-flight-proxy pattern rather than fixing it
  • Does not address the servos.c TODO
  • If the IMU velocity estimate drifts significantly during dead reckoning, the threshold may need tuning

Option B — isEstimatedFlyingConfident() (architectural fix)

A new flight-detection function that explicitly uses baro altitude above takeoff as the primary GPS-independent signal, with estimator velocity and airspeed as supporting signals:

bool isEstimatedFlyingConfident(void)
{
    if (STATE(MULTIROTOR)) {
        return posControl.actualState.velXY > MC_LAND_CHECK_VEL_XY_MOVING ||
               averageAbsGyroRates() > 4.0f;
    } else {
        bool altCheck  = fabsf(posControl.actualState.abs.pos.z - getTakeoffAltitude()) > 500.0f;
        bool velCheck  = posControl.actualState.velXY > 350.0f;
#ifdef USE_PITOT
        bool airspeedCheck = sensors(SENSOR_PITOT) && pitotIsHealthy() &&
                             getAirspeedEstimate() > 350.0f;
#else
        bool airspeedCheck = false;
#endif
        return isGPSHeadingValid() || altCheck || (velCheck && airspeedCheck);
    }
}

Multirotor path is unchanged from current behaviour. Fixed-wing path adds baro altitude and estimator velocity/airspeed as fallbacks when GPS fails.

Tradeoffs:

  • Semantically correct — answers "are we flying?" rather than "do we have a heading?"
  • Fixes the servos.c TODO properly
  • Introduces threshold constants that will need justification and possible tuning per airframe
  • Larger change; more surface area for review

Comparison

Option A Option B
Change size ~5 lines + 4 substitutions ~20 lines + 4 substitutions
Concept introduced Better heading proxy New flight-detection concept
servos.c TODO Not addressed Addressed
Threshold tuning needed No (reuses existing 300 cm/s) Yes (altitude threshold, velocity threshold)
Regression risk Very low Low
Dead reckoning fix Yes Yes

Questions for discussion

  1. Option A or Option B — or a different approach entirely?
  2. For Option A: is 300 cm/s the right minimum estimator velocity for heading validity, or should it be lower given IMU velocity is less precise than GPS?
  3. For Option B: is 5 m the right altitude threshold above takeoff? Should it be configurable?
  4. Are there fixed-wing flight profiles where dead reckoning velocity estimate would be unreliable enough to cause false positives (e.g. prolonged soaring with engine off)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions