Skip to content

canair hunt

usage: canair hunt [-h] <kind> ...

[UDS+CAN] Answer 'which byte carries a signal I already know?' Choose a domain kind:
  uds   sweep a diagnostic PID's bytes vs a known signal (domain A).
        A bare `canair hunt AAF 2181 --against …` is shorthand for this.
  can   sweep a raw broadcast-CAN frame ID's bytes vs a reference frame
        byte in the same log (domain B), bytes labelled 0xID:rN.

Read-only: analyses captures/ only, never talks to the device.

positional arguments:
  <kind>
    uds       Hunt a diagnostic PID's bytes vs a known signal (domain A)
    can       Hunt a raw broadcast-CAN frame ID's bytes vs a reference (domain
              B)

options:
  -h, --help  show this help message and exit

canair hunt uds

usage: canair hunt uds [-h]
                       (--against ECU:PID:PARAM | --against-file FILE | --physical)
                       [--min-n N] [--top N] [--transform MODE]
                       [--method {pearson,spearman}] [--join-tol SECONDS]
                       [--fill {auto,hold,none}] [--max-hold SECONDS] [--json]
                       [--all-interps] [--control ECU:PID:PARAM]
                       [--control-file FILE] [--promote NAME] [--per-session]
                       [--session-gap SECONDS] [--notation NAME]
                       [--since WHEN] [--until WHEN] [--date YYYY-MM-DD]
                       [--today] [--last-sessions [N]] [--last-session]
                       [--state STATE] [--label SUBSTR]
                       [ecu] [pid]

Sweeps every byte offset of the target PID under every interpretation
(u8/i16/u24/f32/... x endianness), time-aligns each candidate against a
known reference signal on another ECU/PID (--against), and ranks them by
|Pearson r|. Each top hit reports a linear fit (y=m·x+c), the residual,
and a physical-unit guess — so you learn not just which byte, but its
scale and offset.

This automates the 'which byte tracks vehicle speed / motor RPM?' scratch
work. Use --transform delta to hunt the byte tracking the reference's
*rate* (e.g. torque vs acceleration), and --promote to write the top hit
straight into ecus/ as an enabled, unverified candidate parameter.

Read-only: analyses captures/ only, never talks to the device.

positional arguments:
  ecu                   Target ECU to hunt on (e.g. AAF)
  pid                   Target PID to hunt on (e.g. 2181)

options:
  -h, --help            show this help message and exit
  --against ECU:PID:PARAM
                        Reference signal: a diagnostic ECU:PID:PARAM (or
                        ECU:PID:EXPR)
  --against-file FILE   Reference from an external CSV (timestamp,value)
                        instead of a bus signal — a calibrated meter log, GPS
                        track, grid-voltage export. Joined by nearest
                        timestamp; the file must be on the same absolute clock
                        as the captures (relative/zero-based logs won't align)
  --physical            No reference: flag bytes whose (scaled) value lands in
                        a named physical band (mains RMS/peak, line freq, 12V
                        rail, HV pack) at some scaling (/1 /10 /100 ×2 ×√2).
                        Finds anchorless signals by plausibility
  --min-n N             Min aligned points (default 10)
  --top N               Max hits (default 12)
  --transform MODE      Transform the reference before aligning (e.g. delta to
                        hunt the byte that tracks the reference's *rate* —
                        torque vs acceleration)
  --method {pearson,spearman}
                        Ranking coefficient: pearson (linear, default) or
                        spearman (rank)
  --json                Machine-readable output
  --all-interps         Show every interpretation per offset (u8/i16/u24/…);
                        default collapses to the best interpretation per byte
                        offset
  --control ECU:PID:PARAM
                        Confounder control: regress out this nuisance signal
                        and rank by the PARTIAL correlation (what remains
                        after removing the control's linear influence).
                        Surfaces a byte whose link to --against only shows
                        once the dominant driver is removed (e.g. AC voltage
                        behind the IR-drop current)
  --control-file FILE   Like --control, but the nuisance signal is an external
                        timestamp,value CSV (mutually exclusive with
                        --control)
  --promote NAME        Write the top hit's expression to ecus/ as an enabled,
                        unverified candidate param NAME (via pids upsert-
                        param), with the correlation evidence auto-filled into
                        notes
  --per-session         Remove each recording session's DC baseline before
                        ranking — makes a slowly-varying absolute-level
                        reference/byte (pack/12V/mains voltage, a held
                        temperature) rankable by --against instead of being
                        dominated by cross-session offsets. Ranks the in-
                        session *variation*, not the level
  --session-gap SECONDS
                        With --per-session: time gap that starts a new session
                        (default 300.0s)
  --notation NAME       byte-index notation for output labels: wican
                        (default), isotp, torque, bix. Overrides the
                        display.byte_notation config key.

time joining:
  --join-tol SECONDS    Nearest-timestamp join window (default 5s)
  --fill {auto,hold,none}
                        Carry a run-length (keep:changes) value forward to
                        reference instants it has no sample at: 'auto'
                        (default) fills only keep:changes sessions, 'hold'
                        forces it everywhere, 'none' keeps strict point
                        semantics
  --max-hold SECONDS    Cap how long a filled value may be carried (default:
                        until the next sample or the end of its recording
                        session)

scoping:
  Restrict to captures within a date/time range (inclusive) and/or by session state (token-matched) or label substring. --since/--until accept a date (YYYY-MM-DD) or a timestamp (YYYY-MM-DD HH:MM[:SS[.ffffff]])

  --since WHEN          Only captures on or after this date/time (YYYY-MM-DD[
                        HH:MM:SS])
  --until WHEN          Only captures on or before this date/time (YYYY-MM-DD[
                        HH:MM:SS])
  --date YYYY-MM-DD     Only captures on this exact date (shorthand for
                        --since X --until X)
  --today               Only captures recorded today (shorthand for --date
                        <today>)
  --last-sessions [N]   Only the most recent N recorded sessions in scope (N
                        defaults to 1)
  --last-session        Only the most recent recorded session in scope (alias
                        for --last-sessions 1)
  --state STATE         Only captures recorded in STATE, matched by token and
                        widened by the profile's implies: hierarchy (--state
                        ready also matches DRIVING). Comma-separate
                        alternatives (--state ready,driving); repeat the flag
                        to require several at once (--state charging --state
                        parked)
  --label SUBSTR        Only captures whose session/capture label contains
                        SUBSTR (case-insensitive)

examples:
  # which byte on AAF 2181 is vehicle speed (known on ESC 22C101)?
  canair hunt AAF 2181 --against ESC:22C101:REAL_SPEED_KMH

  # restrict to drive captures and only strong linear fits
  canair hunt AAF 2181 --against ESC:22C101:REAL_SPEED_KMH --state driving --min-n 30

  # hunt the byte that tracks the reference's *rate* (acceleration, not speed)
  canair hunt MCU 2102 --against ESC:22C101:REAL_SPEED_KMH --transform delta

  # rank by rank-correlation (catches quantized / saturating links)
  canair hunt MCU 2102 --against ESC:22C101:REAL_SPEED_KMH --method spearman

  # write the winning byte into ecus/ as an unverified candidate param
  canair hunt AAF 2181 --against ESC:22C101:REAL_SPEED_KMH --promote WHEEL_SPEED_KMH

  # reference an EXTERNAL log (calibrated meter / GPS / grid-voltage export),
  # joined by nearest timestamp on the captures' absolute clock
  canair hunt OBC 2101 --against-file grid_voltage.csv --state charging

  # confounder control: which byte tracks the grid once the IR-drop current is
  # regressed out? (partial correlation — surfaces links a dominant driver hides)
  canair hunt OBC 2101 --against-file grid_voltage.csv --control OBC:2101:OBC_DC_A

  # no reference at all: flag bytes whose scaled value lands in a physical band
  # (mains RMS/peak, line freq, 12V rail, HV pack) — finds an anchorless signal
  canair hunt OBC 2101 --physical --state charging

tip: --against takes a known signal ECU:PID:PARAM (or a raw ECU:PID:EXPR). Use
     `canair correlate --overlap` first to find a reference that actually shares
     time-aligned samples with your target.

canair hunt can

usage: canair hunt can [-h] [--can-format {auto,asc,blf,csv,log,gvret}] --id
                       ID --against 0xID:rN [--min-n N] [--top N]
                       [--transform MODE] [--method {pearson,spearman}]
                       [--join-tol SECONDS] [--fill {auto,hold,none}]
                       [--max-hold SECONDS] [--json] [--all-interps]
                       [--notation NAME]
                       FILE

Hunt on a raw broadcast-CAN frame log: sweep every byte/interpretation of --id's frames vs --against (a frame byte 0xID:rN in the same log). Hits are raw-CAN rN labels (no WiCAN expr); --promote is not supported for frames yet (frame signals are defined in signals/).

positional arguments:
  FILE                  Path to a raw broadcast-CAN frame log
                        (.asc/.blf/candump .log/.trc/GVRET .csv)

options:
  -h, --help            show this help message and exit
  --can-format {auto,asc,blf,csv,log,gvret}
                        Log format (default: auto-detect by extension)
  --id ID               The arbitration ID to hunt on (e.g. 0x220)
  --against 0xID:rN     Reference frame byte in the same log (e.g. 0x386:r0)
  --min-n N             Min aligned points (default 10)
  --top N               Max hits (default 12)
  --transform MODE      Transform the reference before aligning (e.g. delta to
                        hunt the byte that tracks the reference's *rate* —
                        torque vs acceleration)
  --method {pearson,spearman}
                        Ranking coefficient: pearson (linear, default) or
                        spearman (rank)
  --json                Machine-readable output
  --all-interps         Show every interpretation per offset (u8/i16/u24/…);
                        default collapses to the best interpretation per byte
                        offset
  --notation NAME       byte-index notation for output labels: wican
                        (default), isotp, torque, bix. Overrides the
                        display.byte_notation config key.

time joining:
  --join-tol SECONDS    Nearest-timestamp join window (default 5s)
  --fill {auto,hold,none}
                        Carry a run-length (keep:changes) value forward to
                        reference instants it has no sample at: 'auto'
                        (default) fills only keep:changes sessions, 'hold'
                        forces it everywhere, 'none' keeps strict point
                        semantics
  --max-hold SECONDS    Cap how long a filled value may be carried (default:
                        until the next sample or the end of its recording
                        session)