Conversation
435511e to
56ac463
Compare
Update: validated against real measurements, three defects fixedI got access to more real-world data — two OEM CAN-FD bus logs (5.1 M and 2.3 M CAN frames) with three production ARXML databases, 15 real container messages — and ran the decoder against it before asking anyone to merge this. Notably, the same drive exists both message-based (raw CAN frames) and signal-based (decoded on the fly by the logger toolchain), which gives an oracle that shares no code with asammdf or canmatrix. That was worth doing: the in-memory tests were green, but real containers are messier than synthetic ones and turned up three defects that only real data reaches.
Results after the fixes:
The five signals in row 2 that do not agree everywhere are free-running sequence counters and CRCs whose sample instants differ between the two recordings by more than the comparison window — identical value range and cardinality. Two regression tests were added; defect 3 is covered by the existing oracle test now that it asserts absolute values. Full description updated above. One prerequisite fell out of this: reading these measurements at all needs #1307. |
07298b4 to
e9f758f
Compare
Container (multiplexed PDU) frames carry a variable sequence of contained
PDUs, each prefixed by a Header_ID/Header_DLC header, so a PDU's byte offset
depends on the lengths of the PDUs before it. extract_mux() only handled
is_multiplexed frames, so container frames were mis-decoded.
- extract_pdus() in bus_logging_utils.py walks the container headers per
frame (vectorized across frames, one pass per PDU slot), gathers each
contained PDU payload and extracts its signals. Each contained PDU becomes
its own channel group, reusing the existing channel-group machinery; a
PDU's signal start bits are PDU-relative so extract_signal applies
unchanged. canmatrix.Frame.unpack is the reference algorithm.
- route is_pdu_container frames through extract_pdus in _extract_can_logging;
all other frames keep using extract_mux.
- fix extract_signal signedness: a signed signal with a standard bit width
(8/16/32/64) at a non-byte-aligned offset was viewed as i{std_size} on the
padded width instead of being sign-extended from its real bit width (e.g. an
8-bit signed field at bit offset 1 returned 225 instead of -31). This
affected the normal extract_mux path too.
- add offline test/test_CAN_pdu_extraction.py validating extract_pdus against
canmatrix.unpack (big/little-endian headers, 0x00/0xFF padding, unique
multi-PDU frames, signed bit-packed signals) plus a full extract_bus_logging
end-to-end case and a static-container skip.
Static (header-less) AUTOSAR containers have a fixed layout: canmatrix rebases each contained PDU's signal start bits to be frame-relative and Frame.unpack itself refuses them, so they were previously skipped. Handle them by decoding every contained PDU straight from the full frame payload, one channel group each (pdu.id is None -> identity keyed on the PDU name). - factor the per-PDU signal emission out of extract_pdus into _emit_pdu_signals, shared by the dynamic (PDU-relative payload) and static (full-frame payload) paths; _contained_pdu_muxer builds the channel-group identity and tolerates a None header id. - test_extract_pdus_static_container validates static extraction against a flat frame carrying the same frame-relative signals (byte-aligned LE/BE plus a non-byte-aligned signed field). - test_extract_bus_logging_canfd_container_e2e runs the full pipeline on genuine CAN-FD container frames (64-byte payload, EDL flag + DataLength members set) to exercise the real-world container transport.
Validated `extract_pdus` against two real OEM CAN-FD bus logs (5.1 M and 2.3 M CAN frames) and three production ARXML databases, using `canmatrix.Frame.unpack` as the oracle. That uncovered three defects that the in-memory tests could not reach: 1. `OverflowError` aborted the whole extraction. Real containers carry opaque 216/288/400-bit blobs declared *signed*; those are kept as a byte matrix, so `as_non_byte_sized_signed_int` computed `1 << 216` on them. Signals wider than an integer dtype now skip two's complement, like unsigned ones already did. 2. Container padding was reported as measured data. A sender may transmit a contained PDU shorter than its declared size — the header DLC is the authority. Signals reaching past the transmitted length are now flagged through `invalidation_bits` instead of surfacing padding as a value. Real data hits this on 2.4 % of contained-PDU occurrences. 3. The header walk could run backwards. canmatrix's ARXML parser marks the synthetic `Header_ID`/`Header_DLC` signals *signed*, so a 0xFF padding byte decodes as a DLC of -1; the offset then moved backwards over a padded tail, rescanning the frame misaligned and inventing contained PDUs out of padding. Both header fields are now read unsigned. Two regression tests added for 1. and 2.; 3. is covered by the existing oracle test now that absolute values are asserted.
`doc/buslogging.rst` is the user-facing bus logging documentation and said nothing about container I-PDUs. Add a section covering what gets decoded (one channel group per contained PDU, addressed like any other bus logging signal), that both the dynamic and static layouts are handled, that samples whose bytes were not transmitted are marked through `invalidation_bits`, and the two known limitations (multiplexed contained PDUs, LIN). Also point the developer note at it, and note there that it is deliberately not part of the built docs — sphinx is configured for `.rst` only.
fa2e1be to
2190907
Compare
…container-extraction
AUTOSAR container-PDU extraction for CAN bus logging
Clean-room reimplementation of dynamic (and static) AUTOSAR container-PDU
extraction, superseding the approach in #912.
Why a rewrite of #912
#912's
extract_pdudecoded contained-PDU signals directly from the wholeframe with no per-PDU header walk, so it only produced correct values when
a PDU sat at a fixed frame offset. Dynamic containers — a variable sequence of
contained PDUs whose byte offsets shift per frame — were mis-decoded. It also
returned a
listwith a non-standard entry tuple instead of thedict[entry, dict[name, ExtractedSignal]]contract the channel-group machineryconsumes, and touched
utils.py/mdf.py/.coveragerc.What this does
Header_ID/Header_DLC, advances a per-frame byte offset, vectorized across frames.Reference algorithm is
canmatrix.Frame.unpack. Header geometry is derivedfrom the header signals (short 24+8, long 32+32), not hardcoded. Both header
fields are read unsigned: canmatrix's ARXML parser marks them signed, so a
0xFF padding byte decodes as a DLC of −1 and walks the offset backwards over
a padded tail, rescanning the frame misaligned and inventing contained PDUs
out of padding.
slice is isolated, then the existing
extract_signalapplies unchanged.its declared size — the header DLC is the authority. Signals reaching past the
transmitted length are flagged through
invalidation_bitsrather thanreported as measured data.
(canmatrix rebases their signal start bits to frame-relative via each PDU's
OFFSET;Frame.unpackitself refuses static containers).channel-group machinery — PDU identity is carried in the entry's
muxerslot; same return contract as
extract_mux.CAN_DataFrame.DataBytes-width driven, sothe FD flag has no functional effect on decoding.
extract_signal(shared with theextract_muxpath):offset was viewed as
i{std_size}on the padded width instead of beingsign-extended from its real bit width (e.g. an 8-bit signed field at bit
offset 1 returned 225 instead of −31);
two's complement cannot be applied to it. Real containers carry opaque
216/288/400-bit blobs declared signed, and those raised
OverflowErroron the
1 << bit_countmask.Footprint
Only
src/asammdf/blocks/bus_logging_utils.py(newextract_pdus,_emit_pdu_signals,_contained_pdu_muxer,_signal_byte_extent) plus routingin
mdf_v4.py(is_pdu_container->extract_pdus, elseextract_mux). Nochanges to
utils.py,mdf.py, or.coveragerc.Testing
Fully offline in
test/test_CAN_pdu_extraction.py:canmatrix.Frame.unpackoracle (BE/LE headers,0x00/0xFF padding, unique multi-PDU frames, bit-packed non-byte-aligned
signed signals);
frame-relative signals;
MDF.extract_bus_loggingend-to-end on a 32-byte container and on agenuine 64-byte CAN-FD container (EDL flag + DataLength set);
The existing
test/test_CAN_bus_logging.py(real OBD/J1939 data) still passes,confirming no regression to the
extract_muxpath.Validation on real measurements
Validated against two real OEM CAN-FD bus logs (5.1 M and 2.3 M CAN frames) and
three production ARXML databases covering three CAN channels — 15 real container
messages in total.
Against
canmatrix.Frame.unpackas the oracle, sample by sample:1 237 247 decoded values, 0 mismatches.
Message-based vs signal-based. The same drive was recorded twice by the
logger: once as raw CAN frames, once as signals decoded on the fly by the logger
toolchain. Cross-checking the container decode against that second recording is
an oracle that shares no code with asammdf or canmatrix. Of 303 comparable
signals, 298 agree on every one of 114 251 samples. The five that do not are
free-running sequence counters and CRCs whose sample instants differ between the
two recordings by more than the comparison window — same value range, same
cardinality.
No regression to the existing path. Running the full
MDF.extract_bus_loggingon the first measurement, onmasterand on thisbranch: all 541 non-container signals are bit-identical; on
masterthecontainer messages yield only
Header_ID/Header_DLC, this branch adds 789real contained-PDU signals in 58 additional channel groups. A further 676 008
non-container values were separately confirmed against the oracle.
Decoded values are physically plausible: HV DC-link 400 V mean / 794 V peak on an
800 V platform, inverter and coolant temperatures 25–33 °C, 14.5 V rail.
Related issues
extract_bus_logging()returned no PDU signals ("PDU signal extraction is not added yet").