Skip to content

i_corrector.hpp

ICorrector — the WRITE seam: one source of ABSOLUTE position fixes (V5 GPS, AprilTag PnP, LIDAR scan-match).

This header declares 2 types (10 members).

Extracted from include/shulib/localization/i_corrector.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.

Contents

class ICorrector

class ICorrector

One source of ABSOLUTE field-pose fixes — V5 GPS, AprilTag PnP, LIDAR scan-match. PULL, not push: the Localizer calls propose() once per tick with its odom-predicted pose, and nothing here ever writes into the estimator. An implementation owns ALL of its own mess — HAL access, frame/lever-arm/PnP reduction, latency, staleness, gating — so the Localizer stays geometry-free and the trust math stays in one place. Pure with respect to its injected HAL handle, which is what makes a corrector host-testable against a fake.

class, declared at include/shulib/localization/i_corrector.hpp:25.

ICorrector::~ICorrector

virtual ~ICorrector() = default

Polymorphic-base boilerplate: the destructor is virtual so a concrete corrector held as ICorrector&/ICorrector* destroys correctly, and DECLARING it is what suppresses the implicit copy/move, which are re-defaulted below. The base carries no state of its own. Ownership stays with the CALLER either way: the Localizer takes a NON-OWNING span<ICorrector* const> (at most kMaxCorrectors, each checked non-null at construction), so every corrector must outlive the Localizer it was handed to.

function, declared at include/shulib/localization/i_corrector.hpp:33.

ICorrector::ICorrector

ICorrector() = default

Covered by the comment on ~ICorrector — one comment documents this run of special members.

function, declared at include/shulib/localization/i_corrector.hpp:34.

ICorrector::ICorrector (overload 2)

ICorrector(const ICorrector&) = default

Covered by the comment on ~ICorrector — one comment documents this run of special members.

function, declared at include/shulib/localization/i_corrector.hpp:35.

ICorrector::ICorrector (overload 3)

ICorrector(ICorrector&&) = default

Covered by the comment on ~ICorrector — one comment documents this run of special members.

function, declared at include/shulib/localization/i_corrector.hpp:36.

ICorrector::operator=

ICorrector& operator=(const ICorrector&) = default

Covered by the comment on ~ICorrector — one comment documents this run of special members.

function, declared at include/shulib/localization/i_corrector.hpp:37.

ICorrector::operator= (overload 2)

ICorrector& operator=(ICorrector&&) = default

Covered by the comment on ~ICorrector — one comment documents this run of special members.

function, declared at include/shulib/localization/i_corrector.hpp:38.

ICorrector::propose

[[nodiscard]] virtual CorrectionProposal propose(const math::Pose2d& predicted, units::Time dt) = 0

Propose an absolute fix given the odom-predicted pose this tick. MUST be non-throwing and MUST return {valid=false} when it has no usable fix (off-strip GPS, no tag) — never a zero-confidence pull. dt is the tick duration (seconds).

function, declared at include/shulib/localization/i_corrector.hpp:43.

ICorrector::name

[[nodiscard]] virtual const char* name() const noexcept = 0

Stable id for telemetry / per-source dead-reckon accounting.

function, declared at include/shulib/localization/i_corrector.hpp:47.

class NullCorrector

class NullCorrector final : public ICorrector

The M2 placeholder: a registered source that never has a fix. Lets the fusion pipeline run and be tested end-to-end (it just always dead-reckons) before any real corrector exists, and keeps the seam visibly wired for telemetry. M3 replaces it with GpsCorrector/AprilTagCorrector.

class, declared at include/shulib/localization/i_corrector.hpp:53.

NullCorrector::propose

[[nodiscard]] CorrectionProposal propose(const math::Pose2d& /*predicted*/, units::Time /*dt*/) override

Always declines — a default-constructed proposal, so valid == false and selfAudit.reason == None. Both arguments are ignored, and the estimator dead-reckons this tick exactly as it would with no corrector registered at all.

function, declared at include/shulib/localization/i_corrector.hpp:58.

NullCorrector::name

[[nodiscard]] const char* name() const noexcept override

"null". Because this corrector never proposes and never self-audits, the Localizer never reads it — the id exists so the seam is visibly wired, not to label a record.

function, declared at include/shulib/localization/i_corrector.hpp:64.

Design commentary, from the header

The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.

The header’s own reasoning — 10 lines
 ICorrector — the WRITE seam: one source of ABSOLUTE position fixes (V5 GPS, AprilTag PnP, LIDAR
 scan-match) (master plan §6/§8). It is PULL, not push: the Localizer calls propose() each tick
 with the odom-PREDICTED pose and the tick dt, and the corrector returns a CorrectionProposal
 (or {valid=false}). The corrector owns ALL of its mess — HAL access, frame/lever-arm/PnP
 reduction, latency, staleness, gating — so the Localizer stays geometry-free and the trust math
 stays in one place. Pure w.r.t. its injected HAL handle; host-testable with a fake.

 At M2 the registered list is empty or holds only NullCorrector (below); GpsCorrector /
 AprilTagCorrector are concrete at M3 behind THIS exact signature, with no caller change.

Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.