Skip to main content

Fusion

Enum Fusion 

Source
pub enum Fusion {
    Accepted {
        test_ratio: f32,
    },
    Reset,
    Rejected {
        test_ratio: f32,
    },
    NotInitialized,
    NoReference,
    Unobservable,
    NotFinite,
    InvalidNoise,
    StateInvalid,
    OutOfHorizon {
        age: Seconds,
    },
}
Expand description

The outcome of one measurement update.

Carries the dimensionless test ratio r = ε / γ of equation (38) in both the accepted and rejected cases, so a rejection is diagnosable rather than a bare failure. r > 1 means rejected, whatever the dimension of the observation.

Not #[must_use], unlike Propagation and the reset_* outcomes, because acceptance and rejection reach the caller by a second route: Diagnostics keeps the latest test ratio, the running counts and the timer for every source, so a loop that reads Eskf::state each cycle loses nothing by discarding this value.

The refusals keep their own counters there rather than a lint here: SourceHealth::refused and SourceHealth::last_refusal say how often a source was turned away and why, and SourceHealth::adopted separates a Reset from the ordinary acceptance it otherwise looks like. A refusal still moves no timer — it is not aiding, and pretending otherwise would let a stream of NaN hold off Status::DeadReckoning.

Variants§

§

Accepted

The measurement passed the gate and was fused.

Fields

§test_ratio: f32

Test ratio, in [0, 1].

§

Reset

The measurement was adopted outright rather than fused, because the filter had no estimate of that quantity to fuse it against, or none the gate would let it correct.

The second is recovery from gate lockout: a source whose measurements have been rejected for longer than Recovery allows has the next one adopted, counted in SourceHealth::recovered. For the barometer the adoption is of its reference α₀ rather than of any state, so nothing the estimate reports steps.

The first happens once per quantity, and for three of them. Position and velocity are adopted on the first GNSS fix after a coarse start: a vehicle that initialized while moving knows neither where it is nor how fast it is going, and no gate can judge a measurement against nothing. A static start and a seed both have an estimate of those two, so their first fix is fused normally, or, given as latitude and longitude, places the origin under that estimate; see Eskf::fuse_gnss_geodetic.

Heading is the third, and it is not a coarse start’s alone: stillness observes tilt and never yaw, so a static window carrying no magnetometer leaves yaw a prior nothing measured, and the first heading from fuse_mag_heading, fuse_gnss_heading or fuse_course is adopted there too. Only a seed escapes, having vouched for every quantity.

The state becomes the measurement and its covariance block becomes the measurement’s, which is what fusing against an infinitely uncertain prior converges to — the limit, taken exactly rather than approached with an invented variance.

Worth reacting to: position, velocity or attitude stepped, which a controller consuming the estimate may care about. Heading is the largest of the three — an adopted heading can turn the estimate by half a circle, where an adopted position moves a quantity no controller was flying on.

§

Rejected

The measurement failed the gate and was discarded. The state is unchanged.

Fields

§test_ratio: f32

Test ratio, greater than 1.

§

NotInitialized

No initialization has succeeded — Eskf::initialize, initialize_coarse or initialize_from — so there is no state to fuse against. The measurement was discarded.

§

NoReference

The measurement has nothing to be relative to. The measurement was discarded.

Barometer: no reference altitude α₀ is held, and position is not yet established to read one against — a start in motion before its first fix — or Config::baro_reference_from_estimate is off. Fusing it anyway would invent the origin from whichever sample happened to arrive first. Eskf::set_baro_reference names one.

Geodetic GNSS: no navigation origin is held and this fix cannot place one, because its latitude is beyond ±90°. The next usable fix will. See Eskf::fuse_gnss_geodetic.

Course: no GNSS velocity has been accepted within its own timeout, so the velocity the course would be taken along is dead-reckoned or was never established. Usually a call made before the first Eskf::fuse_gnss_velocity, or after the receiver stopped; see Eskf::fuse_course.

§

Unobservable

The geometry says nothing about the quantity measured. The measurement was discarded and no health timer moved.

A heading from Eskf::fuse_gnss_heading or Eskf::fuse_course, in two ways. The vehicle’s forward axis is within 30° of vertical, where the heading of that axis is undefined, and a tailsitter hovering is the case: PX4 refuses a GNSS yaw reset on the same bar (EKF/aid_sources/gnss/gnss_yaw_control.cpp:221 at c4e4ef98). Or, for the course, the vehicle is not moving fast enough for the direction of its estimated velocity to mean anything: a cross-track uncertainty over 15° of course, which is a hover or a slow taxi. Fusing either would hand the gate an angle drawn from noise.

Not the magnetometer’s: (34) levels the field rather than reading the heading of body x, and (36′) prices the leveling at any tilt, so a magnetic heading is fused through 125° of tilt on 285ee2e7.

§

NotFinite

A number in the measurement or its noise is NaN or infinite. The measurement was discarded and no health timer moved.

Refused before anything else, including the adoption of a quantity initialization left unestablished: one NaN in the state or covariance spreads to every quantity at the next update and never leaves.

§

InvalidNoise

A variance in the measurement noise is zero or negative. The measurement was discarded and no health timer moved.

R has to be a variance some sensor could have. Zero makes the innovation covariance S = H P Hᵀ + R of equation (24) singular as soon as the state it observes is itself certain, and a negative one is worse: it claims a measurement better than perfect, and where the measurement is adopted outright it writes that negative variance straight into P, which Validity then reads as an excellent estimate.

Usually a floor or a unit mistake at the boundary — a receiver reporting eph = 0 while it has no fix, or a variance arrived at by subtracting one σ² from another. Refused alongside NotFinite, before any adoption.

One bad component refuses the measurement it belongs to. A GNSS fix is two, so a receiver in 2D-fix mode reporting a good eph with epv = 0 keeps its horizontal aiding and has its height refused; see GnssFusion. The filter reports rather than repairs: PX4 and ArduPilot clamp such a value into range, and a caller that wants that behavior floors noise before the call, where the policy is visible. See Eskf::fuse_gnss_position.

§

StateInvalid

The filter’s own numbers could not support an update, so nothing was committed. The state and covariance are unchanged, no health timer moved, and the measurement is not at fault.

Two ways to get here, one meaning. The innovation covariance S = H P Hᵀ + R of equation (24) was not positive-definite, which with a positive R means P itself had stopped being a covariance; or the correction of (26) and (39)–(41) overflowed f32 on finite inputs, as (11)–(14) can. Either way a caller learns the same thing: the estimate, not the sensor, is what needs attention.

State-qualified by the rule Propagation::StateNotFinite follows, and not merged into it: NotFinite is always the input handed in, and this is what the filter produced. Kept out of Rejected because the gate never gave a verdict — a rejection says the measurement disagreed with a sound estimate, and counting this as one would put a filter fault in the column that times out a sensor.

§

OutOfHorizon

The measurement’s time is one the filter cannot place it at: older than LATENCY_HORIZON, later than the state by more than Config::max_predict_dt, or before a start that did not show the vehicle at rest, whose motion before it nothing describes. The measurement was discarded and no health timer moved.

Inside those bounds the measurement is fused at its own time, equation (23′): against the state as it was, or, for one timed between the last IMU sample and the next, as it will be, carried forward on the estimated velocity and the last sample’s rate.

Fused anyway, an old measurement is a statement about where the vehicle was, applied to where it is: at 20 m/s a fix a second old is 20 m of error handed to the gate as truth. One later than the state is a clock the IMU and the sensor do not share, or a sample the caller has not yet handed to predict. Either way it is the timestamp that is wrong, which is a refusal rather than a verdict on the value.

Fields

§age: Seconds

How long before the state the measurement was taken, negative for one after it: what tells a latency longer than the horizon from a clock with another epoch.

Implementations§

Source§

impl Fusion

Source

pub const fn is_accepted(self) -> bool

Whether the filter took the measurement, whether by fusing it or by adopting it outright. See is_reset to tell those apart.

Source

pub const fn is_reset(self) -> bool

Whether the measurement replaced the estimate rather than correcting it, stepping the state.

Source

pub const fn refusal(self) -> Option<Refusal>

Why the measurement was turned away, or None if it reached the gate.

Reset is not a refusal: the measurement was taken, just adopted rather than fused.

Source

pub const fn test_ratio(self) -> Option<f32>

The test ratio, where the gate ran at all.

Trait Implementations§

Source§

impl Clone for Fusion

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Fusion

Source§

impl Debug for Fusion

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Fusion

The verdict without the source, which the outcome does not know: "gnss position {}" reads gnss position rejected, ratio 2.70. A refusal carries its Refusal’s words, and Reset reads adopted, which is what it did.

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Format for Fusion
where Seconds: Format,

Available on crate feature defmt only.
Source§

fn format(&self, f: Formatter<'_>)

Writes the defmt representation of self to fmt.
Source§

impl PartialEq for Fusion

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Fusion

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.