CogniPilot

MODELICA LIBRARY · ENGINEERING REFERENCE

Estimation/FusionHorizon/package.mo

Download original .moGitHub source

Documentation: Estimation.FusionHorizon

within Estimation;

package FusionHorizon
  "Estimator-agnostic delayed fusion horizon and SE_2(3) output predictor"
  constant Integer DeltaLength = 56
    "Flat storage width of one Estimation.FusionHorizon.Delta:
     3 position, 3 velocity, 4 quaternion, 1 span, and five 3x3 bias
     Jacobians stored row-major.";

  constant Real TimestampMagnitudeLimit = 1.0e30
    "A timestamp whose magnitude reaches this is not a time. The delayed
     measurement queues order themselves by timestamp, so a not-a-number or an
     unbounded one does not merely produce a wrong answer, it destroys the
     ordering the whole horizon rests on: every comparison against a
     not-a-number is false, so such a packet is never ripe, never late, and
     never leaves the queue. It is refused at arrival instead.

     The same magnitude the filters use, restated here rather than imported,
     because nothing in this package may depend on a filter.";

  constant Integer MocapMeasurementLength = 26
    "Flat storage width of one Avionics.MocapSample as a queue holds it:
     1 timestamp, 3 position, 4 quaternion, 9 position covariance, 9 attitude
     covariance. valid and fresh are NOT stored: a queued packet is by
     construction one that was valid when it arrived, and both flags are
     asserted on the tick it is delivered.";
  constant Integer GpsMeasurementLength = 30
    "1 timestamp, positionValid, velocityValid, 3 geodetic, 3 position,
     3 velocity, 9 position covariance, 9 velocity covariance. The two
     per-solution validity flags ARE stored, because unlike valid and fresh
     they say which half of the fix is usable, and that is a property of the
     measurement rather than of its delivery.";
  constant Integer MagnetometerMeasurementLength = 13
    "1 timestamp, 3 field, 9 covariance";
  constant Integer BarometerMeasurementLength = 3
    "1 timestamp, 1 altitude, 1 variance";
  constant Integer OpticalFlowMeasurementLength = 23
    "1 timestamp, 2 line of sight, 4 line-of-sight covariance, 3 gyroscope
     integral, 9 gyroscope covariance, 1 integration time, 1 ground distance,
     1 ground-distance variance, 1 quality";

  // ---- arrival outcomes, one per aiding source per inertial tick ----------
  // Every one of these is NAMED. Inferring what happened to a measurement from
  // the absence of a delivery is the predicate exhaustion the estimator status
  // boundary already refuses elsewhere: a packet that never arrived and a
  // packet refused for being older than the horizon are different failures,
  // and a supervisor has to be able to tell them apart.
  constant Integer AidingNoArrival = 0
    "No novel, valid, finitely stamped sample was presented on this tick";
  constant Integer AidingQueued = 1
    "Stored, to be fused when the fusion instant reaches its timestamp";
  constant Integer AidingDeliveredOnArrival = 5
    "Delivered directly at an empty queue's current release";
  constant Integer AidingRefusedLate = 2
    "Refused at arrival: the fusion instant had already passed this timestamp
     by more than the residual alignment covers, so there is no fusion instant
     left to fuse it at. This outcome is what replaces transporting a
     measurement Jacobian a quarter of a second backwards to meet it";
  constant Integer AidingRefusedOverflow = 3
    "Refused at arrival because the queue was full. The queue keeps what it
     already holds and the NEW measurement is the one that is lost.

     That is the opposite of the right answer for a live-edge buffer, and the
     reason it is right here is worth stating because the intuition runs the
     other way. In a delayed queue the OLDEST entry is the one closest to
     being fusable: it is the next one the fusion instant will reach. Dropping
     it to make room for a newer one throws away the entry that was about to
     be used and replaces it with one that cannot be used for another horizon.
     Do that on every arrival and a queue too short for its source never
     ripens ANY entry -- the contents are a sliding window of measurements
     that are all still in the filter's future -- so the source goes silent
     for the whole flight while every arrival is dutifully stored. Measured on
     an oversampled source before this was corrected: zero deliveries, and
     every assertion about ordering and residuals still passing, because a
     queue that delivers nothing violates none of them.

     Refusing the arrival cannot deadlock, because the delivery path always
     drains: on every release the oldest entry is either delivered or, if the
     fusion instant has passed it, discarded as stale. One slot therefore
     frees per release whatever the source does, and the queue degrades to
     delivering at the release rate rather than to delivering nothing";
  constant Integer AidingBeforeHorizon = 4
    "Presented before the first release, so there is no fusion instant for it
     to name yet. NOT a refusal and not counted as one: the horizon costs one
     horizon of start-up, during which the filter has had no inertial packet
     either and could not have fused anything. The sample is left unconsumed,
     so a source that holds its packet between pulses has it admitted on the
     first tick after the horizon becomes real";

  // ---- delivery outcomes -------------------------------------------------
  constant Integer AidingNoDelivery = 0
    "Nothing was ripe on this tick, or this tick released no window";
  constant Integer AidingDeliveredAtHorizon = 1
    "The oldest entry's timestamp has been reached by the fusion instant, and
     the entry was handed to the filter";
  constant Integer AidingDroppedStale = 2
    "The oldest queued entry exceeded the residual alignment bound";

  annotation(Documentation(info = "<html>
    <p>Delayed aiding queues, preintegrated IMU increments and current-time
    SE_2(3) prediction. Start with
    <a href=\"modelica://Estimation.FusionHorizon.HorizonEstimator\">HorizonEstimator</a>;
    its replaceable filter implements
    <a href=\"modelica://Estimation.StrapdownINS.PartialEstimator\">PartialEstimator</a>.</p>
    <p>Aiding packets wait until their capture timestamps are reached by the
    delayed filter. The output predictor reapplies buffered increments and
    first-order bias-Jacobian corrections to carry that state to the present.
    Buffered increments use a fixed bias anchor; corrections beyond the
    configured range raise <code>biasMoveExceeded</code>.</p>
    <p>Choose the fusion interval as an integral number of IMU intervals and
    the horizon as an integral number of fusion intervals. The horizon must
    cover the maximum source delay plus jitter margin. These values size fixed
    arrays and are set at translation. Supply an appropriately band-limited
    IMU stream; this package does not perform sensor anti-alias filtering.</p>
    <p>Monitor readiness, late/overflow refusals and the delivered residual age.
    Budget buffer folds using generated-code measurements on the intended
    target. Host replay timings do not establish target WCET.</p>
    </html>"));
end FusionHorizon;