Skip to contents

Remove fixes whose implied step speed exceeds a user-supplied cap, iteratively recomputing step speeds on the survivors so that peeling a single fix exposes its former neighbours' step speeds and the process can continue into coherent multi-fix error clusters. Unlike mt_flag_speed_cap, which applies the cap once to the original distribution, mt_peel_speed keeps peeling until no surviving fix has max(step_in, step_out) > v_max.

Usage

mt_peel_speed(
  x,
  v_max,
  aux_scores = NULL,
  max_iter = 1000L,
  remove = FALSE,
  silent = FALSE
)

Arguments

x

A move2 object (single-track or multi-track).

v_max

Numeric scalar, the physiological speed cap in m/s. Must be positive; no default.

aux_scores

Optional numeric vector of auxiliary per-fix outlier scores aligned to nrow(x). When supplied, the peel switches to asymmetric mode: instead of flagging both endpoints of every offending edge (step_speed > v_max), the function flags only the higher-scoring endpoint per edge. Higher score = more likely outlier. Typical sources: bridge_eta from mt_flag_outliers_bridge; detour_ratio from mt_flag_outliers_detour; -log(prob) from mt_flag_outliers; or any user-defined combination. On a 1-fix spike with reliable auxiliary scores, asymmetric mode removes the spike and preserves both clean neighbours where the default symmetric mode would remove all three. Default NULL (symmetric mode). See Asymmetric peel below.

max_iter

Integer. Hard safety cap on peel iterations. Default 1000; in practice convergence is reached in tens of iterations for typical error clusters.

remove

Logical. If TRUE, return only surviving fixes (is_outlier == FALSE). Default FALSE.

silent

Logical. If FALSE (default) the function prints a brief summary (cap used, fixes peeled, iterations). Set TRUE to suppress. Errors and warnings are always shown.

Value

The input x with added columns:

is_outlier

Logical. TRUE for fixes peeled at any iteration.

peel_iteration

Integer. The iteration at which a fix was peeled, or NA if it survived.

step_speed

Numeric, m/s. The outgoing step speed on the survivor sequence (NA where the fix was peeled or is at a track boundary).

Attributes:

v_max_used

The cap supplied.

n_peel_iterations

Integer, iterations performed.

converged

Logical. TRUE if the peel finished before hitting max_iter.

Details

This is the primitive that catches coherent multi-fix errors that per-fix detectors cannot resolve. A K02-style spoof of 175 fixes at a fake location has small step speeds internally (the fake trajectory is self-consistent) and only the jump-out / jump-back boundaries violate a physiological cap. Peeling those boundaries exposes the next interior fix as a new boundary (pre-spoof neighbour now connects to an interior spoof fix over the full jump distance), which also violates the cap and gets peeled. Iteration walks inward from both ends of the spoof until the clusters meet and the remaining track is clean.

v_max must be supplied by the user from species biology – typically the maximum sustained flight speed or a clear physiologically-impossible cap. A data-driven v_max is intentionally not offered here; see mt_suggest_speed_cap for a diagnostic helper that inspects the distribution and reports candidate cuts without choosing for you.

Distances are computed in the input CRS when projected, or in metres via per-track local AEQD projection when the input is longitude / latitude. The step_speed returned is always in m/s.

Asymmetric peel

The default symmetric peel labels every fix whose max(step_in, step_out) > v_max as an outlier in the current iteration. On a 1-fix spike both incident edges violate, so the spike fix plus its two clean neighbours all carry an edge-violation flag and are removed in the same pass. Only the spike is structurally implausible; the two neighbours are clean fixes whose only crime is being adjacent to the spike.

When aux_scores is supplied, the function instead iterates over the offending edges and flags only the higher-scoring endpoint per edge. A genuine spike (with two violating edges) gets flagged via either edge by virtue of its higher score and ends up in the is_outlier set exactly once; its neighbours, with lower scores, are spared. Convergence is still guaranteed (the offending edge is broken once one endpoint is removed) but may take more iterations than the symmetric variant on dense error clusters.

Tie-breaking: when score_left == score_right, the right (later-in-time) endpoint is flagged. This is arbitrary; users with frequent ties should pre-jitter their scores.

aux_scores are not modified or re-aligned during peel; the user supplies a vector aligned to the original input rows and the function indexes into it. Score reliability is the user's responsibility – supplying noisy or anti-correlated scores can produce worse results than the symmetric default.

Cluster-outlier caveat. Asymmetric peel is designed for 1-fix spikes where one endpoint of an offending edge is the true outlier and the other is a clean neighbour. On coherent multi-fix clusters (e.g. a long spoof segment that is locally self-consistent and only violates v_max at the jump-out / jump-back boundaries), the auxiliary score may not discriminate between the spoof-interior boundary fix and the clean-track boundary fix. The peel may then walk inward from one side only, or oscillate. For known cluster-outlier datasets (Argos PTT spoof, K02-style), the symmetric default is the robust choice.

See also

mt_suggest_speed_cap for diagnostic inspection of the speed distribution before choosing v_max. mt_flag_speed_cap for one-shot flagging without iteration. mt_clean_track for the full per-fix + speed pipeline, which uses mt_peel_speed internally when v_max is supplied.

Examples

if (FALSE) { # \dontrun{
## Eagle with a known spoof cluster; physiological cap ~30 m/s.
clean <- mt_peel_speed(eagle_track, v_max = 30)
summary(clean$is_outlier)
table(clean$peel_iteration, useNA = "ifany")
} # }