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.