Skip to content

canair correlate

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

[UDS+CAN] Find every strong cross-signal relationship across a drive/session.
Choose a domain kind:
  uds   diagnostic captures (default) — co-polled ECU/PID params/bytes
        (domain A). A bare `canair correlate …` is shorthand for this.
  can   a raw broadcast-CAN frame log's per-byte series (domain B),
        bytes labelled 0xID:rN.

Read-only: analyses captures/ only, never talks to the device. To pin
down *which byte* a relationship lives in, follow up with `canair hunt`.

positional arguments:
  <kind>
    uds       Correlate co-polled diagnostic captures (domain A)
    can       Correlate a raw broadcast-CAN frame log's per-byte series
              (domain B)

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

canair correlate uds

usage: canair correlate uds [-h] [--transform MODE] [--matrix]
                            [--against-file FILE] [--include-intra]
                            [--include-self] [--against ECU:PID:PARAM]
                            [--min-r R] [--min-n N] [--top N]
                            [--method {pearson,spearman,cramers_v,mutual_info}]
                            [--join-tol SECONDS] [--fill {auto,hold,none}]
                            [--max-hold SECONDS] [--bits] [--json]
                            [--per-session] [--session-gap SECONDS]
                            [--no-cluster] [--bytes] [--lag-scan N]
                            [--gate '[SIGNAL] OP VALUE']
                            [--control ECU:PID:PARAM] [--control-file FILE]
                            [--promote NAME] [--overlap] [--find-mirrors]
                            [--mirror-match FRACTION] [--allow-offset]
                            [--notation NAME] [--since WHEN] [--until WHEN]
                            [--date YYYY-MM-DD] [--today]
                            [--last-sessions [N]] [--last-session]
                            [--state STATE] [--label SUBSTR]
                            [query]

Show me every strong relationship across a whole drive.

Builds every decoded parameter (and, with --bytes, every varying raw
byte; --bits for toggling bits) across all co-polled ECU/PIDs in scope,
time-aligns them by nearest timestamp, and ranks the strongest
cross-signal correlations. This is how the AAF-speed and MCU-temp links
were originally found by hand.

Three ways to use it:
  (default)     ranked list of the strongest cross-ECU/PID pairs
  --against R   rank every signal against one reference R=ECU:PID:PARAM
  --matrix      a labelled correlation r-matrix

Use --overlap first to see which ECU:PID pairs actually share aligned
samples (so you pick a viable --against). --gate isolates a regime
(e.g. 'while moving'), --lag-scan reveals command->response ordering,
and --promote writes the top raw-byte hit into ecus/.

Read-only: analyses captures/ only, never talks to the device. To pin
down *which byte* a relationship lives in, follow up with `canair hunt`.

positional arguments:
  query                 Optional ECU[:PID] selector(s) to restrict the signals
                        (e.g. 'MCU VCU' or 'ESC:22C101'); default = all co-
                        polled in scope

options:
  -h, --help            show this help message and exit
  --transform MODE      With --against: transform the reference before
                        aligning (e.g. delta to rank signals against the
                        reference's *rate*)
  --matrix              Print a labelled r-matrix instead of a ranked pair
                        list
  --against-file FILE   Rank every signal against an external CSV
                        (timestamp,value) reference 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)
  --include-intra       Include same-ECU+PID pairs (default: cross-PID/ECU
                        only)
  --include-self        With --against: keep the reference's own signal
                        (trivial r=1.0; dropped by default)
  --against ECU:PID:PARAM
                        Correlate every signal against this one reference
                        (e.g. ESC:22C101:REAL_SPEED_KMH) instead of the full
                        matrix
  --min-r R             Min |r| to report (default 0.6)
  --min-n N             Min aligned points (default 15)
  --top N               Max hits (default 40)
  --method {pearson,spearman,cramers_v,mutual_info}
                        Association coefficient: pearson (linear, default) or
                        spearman (rank — catches monotone-but-
                        nonlinear/quantized/saturating links), or the
                        categorical cramers_v / mutual_info (treat each value
                        as a nominal category — for mode/flag/enum bytes where
                        numeric spacing is meaningless)
  --bits                Include individual toggling bits (rN:k / Bn:k)
  --json                Machine-readable output
  --per-session         Remove each recording session's DC baseline before
                        correlating — makes slowly-varying absolute-level
                        signals (pack/12V/mains voltage, a held temperature)
                        rankable instead of dominated by cross-session
                        offsets. Ranks in-session variation, not the level (so
                        absolute scale is lost)
  --session-gap SECONDS
                        With --per-session: time gap that starts a new session
                        (default 300.0s)
  --no-cluster          Don't collapse near-perfectly-correlated (|r|≥0.995)
                        signal groups into a single summary line (e.g.
                        balanced cell voltages while charging)
  --bytes               Include raw varying bytes (Bn)
  --lag-scan N          With --against: shift each signal by ±N sample-
                        intervals and report the lag maximising |r| (apparent
                        lag incl. poll offset — not proven causality). Reveals
                        command→response ordering across ECUs
  --gate '[SIGNAL] OP VALUE'
                        With --against: only count points where a predicate
                        holds, e.g. '> 0' (reference itself — 'while moving')
                        or 'MCU:2102:MCU_MOTOR_RPM > 0' (a named signal).
                        Isolates a regime whole-history correlation dilutes
  --control ECU:PID:PARAM
                        With --against: regress out this nuisance signal and
                        rank by the PARTIAL correlation (what remains after
                        removing the control's linear influence) — surfaces
                        signals visible only once the dominant driver is
                        removed. --control-file takes an external
                        timestamp,value CSV instead
  --control-file FILE   Like --control, but the nuisance signal is an external
                        timestamp,value CSV (mutually exclusive with
                        --control)
  --promote NAME        With --against: write the top raw-byte hit to ecus/ as
                        an enabled, unverified candidate param NAME (via pids
                        upsert-param), with the correlation evidence auto-
                        filled into notes
  --overlap             Instead of correlating, report which ECU:PID pairs
                        share time-aligned samples (and how many) in scope —
                        pick a viable --against reference without trial and
                        error
  --find-mirrors        Instead of ranking correlations, report byte/bit
                        positions mirrored ACROSS co-polled ECU/PIDs (time-
                        aligned) — e.g. a door bit in IGPM also present in
                        BCM, or a temperature another ECU reports at a
                        different offset. Use with --bits for bit-level and
                        --allow-offset for offset/scale mirrors. Cross-ECU
                        companion to `decode --find-mirrors` (which is single-
                        PID)
  --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)

mirror matching:
  --mirror-match FRACTION
                        Fraction of compared rows that must agree (default
                        0.9; use 1 to demand every row, which round-robin poll
                        skew alone is enough to defeat)
  --allow-offset        Also accept a mirror at a constant offset or scale (a
                        == b + k, a == b × s) — real mirrors are frequently
                        the same quantity in different units or with a
                        different zero

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:
  # every strong relationship in the most recent drive
  canair correlate --state driving

  # which ECU:PID pairs even share aligned samples? (pick an --against target)
  canair correlate --overlap --state driving

  # rank every signal against a known speed reference
  canair correlate --against ESC:22C101:REAL_SPEED_KMH --state driving

  # include raw bytes + bits (finds undecoded status/relay signals)
  canair correlate --against ESC:22C101:REAL_SPEED_KMH --bytes --bits

  # only while moving (isolate a regime whole-history correlation dilutes)
  canair correlate --against ESC:22C101:REAL_SPEED_KMH --gate '> 0'

  # restrict to a couple of ECUs and show the full r-matrix
  canair correlate "MCU VCU" --matrix

  # rank every byte against an EXTERNAL log (meter/GPS/grid), nearest-timestamp join
  canair correlate --against-file grid_voltage.csv --bytes --state charging

  # partial correlation: rank every byte vs the grid with the charge current
  # regressed out — surfaces a signal only visible once the driver is removed
  canair correlate --against-file grid_voltage.csv --control OBC:2101:OBC_DC_A --bytes

  # spearman ranks catch monotone-but-nonlinear links
  canair correlate --against ESC:22C101:REAL_SPEED_KMH --method spearman

--method cheat sheet (which coefficient when):
  pearson      linear correlation of two continuous signals (DEFAULT). Use for
               analog values that scale together — speed vs wheel-rpm, current
               vs power. Misses nonlinear/curved links. Reports signed r (±1).
  spearman     rank correlation — catches any MONOTONE relationship, even
               nonlinear/quantized/saturating (a signal that rises then flattens,
               or is coarsely stepped). Reach for it when pearson looks weak but
               the plot clearly tracks. Reports signed r (±1).
  cramers_v    categorical association [0..1]: treats each distinct value as an
               unordered CATEGORY. Use for mode/gear/flag/enum bytes where the
               numeric spacing is meaningless (state 3 isn't "more" than state 1).
  mutual_info  categorical too — normalized mutual information [0..1]. Like
               cramers_v but detects ANY statistical dependence between two
               enum/flag signals, not just a table-association pattern.

  Rule of thumb: numeric-and-proportional → pearson; numeric-but-curved/stepped
  → spearman; a code/mode/flag (not a magnitude) → cramers_v / mutual_info.
  The two categorical methods rank by association strength only — they have no
  sign (no ±direction) and no linear fit.

canair correlate can

usage: canair correlate can [-h] [--can-format {auto,asc,blf,csv,log,gvret}]
                            [--id IDS] [--include-intra] [--find-mirrors]
                            [--mirror-match FRACTION] [--allow-offset]
                            [--no-cluster] [--against ECU:PID:PARAM]
                            [--min-r R] [--min-n N] [--top N]
                            [--method {pearson,spearman,cramers_v,mutual_info}]
                            [--join-tol SECONDS] [--fill {auto,hold,none}]
                            [--max-hold SECONDS] [--bits] [--json]
                            [--notation NAME]
                            FILE

Correlate the per-byte series of a raw broadcast-CAN frame log (.asc/.blf/candump .log/.trc/GVRET .csv) — bytes are labelled 0xID:rN. --against/--bits/--id/--min-r/--top/--find-mirrors all apply.

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 IDS              Restrict to comma-separated arbitration IDs (e.g.
                        0x220,0x386)
  --include-intra       Include same-arbitration-ID pairs (default: cross-ID
                        only)
  --find-mirrors        Instead of ranking correlations, report byte/bit
                        positions mirrored ACROSS arbitration IDs (time-
                        aligned) — a signal broadcast on two IDs (e.g. wheel
                        speed on 0x386 and 0x331). Use with --bits for bit-
                        level and --allow-offset for offset/scale mirrors
  --no-cluster          Don't collapse near-perfectly-correlated (|r|≥0.995)
                        byte groups into one line
  --against ECU:PID:PARAM
                        Correlate every signal against this one reference
                        (e.g. ESC:22C101:REAL_SPEED_KMH) instead of the full
                        matrix
  --min-r R             Min |r| to report (default 0.6)
  --min-n N             Min aligned points (default 15)
  --top N               Max hits (default 40)
  --method {pearson,spearman,cramers_v,mutual_info}
                        Association coefficient: pearson (linear, default) or
                        spearman (rank — catches monotone-but-
                        nonlinear/quantized/saturating links), or the
                        categorical cramers_v / mutual_info (treat each value
                        as a nominal category — for mode/flag/enum bytes where
                        numeric spacing is meaningless)
  --bits                Include individual toggling bits (rN:k / Bn:k)
  --json                Machine-readable output
  --notation NAME       byte-index notation for output labels: wican
                        (default), isotp, torque, bix. Overrides the
                        display.byte_notation config key.

mirror matching:
  --mirror-match FRACTION
                        Fraction of compared rows that must agree (default
                        0.9; use 1 to demand every row, which round-robin poll
                        skew alone is enough to defeat)
  --allow-offset        Also accept a mirror at a constant offset or scale (a
                        == b + k, a == b × s) — real mirrors are frequently
                        the same quantity in different units or with a
                        different zero

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)

--method cheat sheet (which coefficient when):
  pearson      linear correlation of two continuous signals (DEFAULT). Use for
               analog values that scale together — speed vs wheel-rpm, current
               vs power. Misses nonlinear/curved links. Reports signed r (±1).
  spearman     rank correlation — catches any MONOTONE relationship, even
               nonlinear/quantized/saturating (a signal that rises then flattens,
               or is coarsely stepped). Reach for it when pearson looks weak but
               the plot clearly tracks. Reports signed r (±1).
  cramers_v    categorical association [0..1]: treats each distinct value as an
               unordered CATEGORY. Use for mode/gear/flag/enum bytes where the
               numeric spacing is meaningless (state 3 isn't "more" than state 1).
  mutual_info  categorical too — normalized mutual information [0..1]. Like
               cramers_v but detects ANY statistical dependence between two
               enum/flag signals, not just a table-association pattern.

  Rule of thumb: numeric-and-proportional → pearson; numeric-but-curved/stepped
  → spearman; a code/mode/flag (not a magnitude) → cramers_v / mutual_info.
  The two categorical methods rank by association strength only — they have no
  sign (no ±direction) and no linear fit.