Skip to content

Commit da422e9

Browse files
committed
fix(asc): apply timestamps_format to actual written timestamps, not just header
Previously, log_event() always subtracted self.started from every timestamp regardless of timestamps_format, meaning "relative" mode only changed the header line while writing identical data to "absolute" mode. Per the ASC format specification: - "absolute": each timestamp is an offset from the start of measurement - "relative": each timestamp is a delta from the preceding event Fix log_event() to compute per-event deltas when timestamps_format="relative", and update self.last_timestamp after each event so the next delta is correct. Also add two tests that verify the actual values written to the file differ between the two modes (3-message uneven spacing exposes the distinction at msg3: absolute writes 1.0, relative writes 0.7). Update changelog fragment to describe the semantic difference accurately.
1 parent 0ec90d7 commit da422e9

3 files changed

Lines changed: 67 additions & 6 deletions

File tree

can/io/asc.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,22 @@ def log_event(self, message: str, timestamp: float | None = None) -> None:
442442
# Use last known timestamp if unknown
443443
if timestamp is None:
444444
timestamp = self.last_timestamp
445-
# turn into relative timestamps if necessary
446-
if timestamp >= self.started:
447-
timestamp -= self.started
448-
line = self.FORMAT_EVENT.format(timestamp=timestamp, message=message)
445+
# Compute written timestamp based on configured format
446+
if self.timestamps_format == "absolute":
447+
# offsets from the start of measurement
448+
written_timestamp = (
449+
timestamp - self.started if timestamp >= self.started else timestamp
450+
)
451+
else:
452+
# deltas from the preceding event
453+
written_timestamp = (
454+
timestamp - self.last_timestamp
455+
if timestamp >= self.last_timestamp
456+
else 0.0
457+
)
458+
# Track last timestamp so the next event can compute its delta
459+
self.last_timestamp = timestamp
460+
line = self.FORMAT_EVENT.format(timestamp=written_timestamp, message=message)
449461
self.file.write(line)
450462

451463
def on_message_received(self, msg: Message) -> None:

doc/changelog.d/2022.added.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
Added `timestamps_format` parameter to `ASCWriter` to allow writing `relative` or `absolute` timestamps in the ASC file header.
1+
Added `timestamps_format` parameter to `ASCWriter` to support configurable timestamp
2+
format: `"absolute"` (default, timestamps are offsets from the start of measurement)
3+
or `"relative"` (each timestamp is the delta from the preceding event), matching the
4+
semantics described in the ASC format specification.

test/logformats_test.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,10 +718,56 @@ def test_write_relative_timestamp_roundtrip(self):
718718
result = list(reader)
719719

720720
self.assertEqual(len(result), len(msgs))
721-
# With relative_timestamp=True timestamps are offsets from the first message
721+
# Timestamps in file are per-event deltas; reader reads them as-is
722722
self.assertAlmostEqual(result[0].timestamp, 0.0, places=5)
723723
self.assertAlmostEqual(result[1].timestamp, 0.5, places=5)
724724

725+
def test_write_relative_timestamps_are_per_event_deltas(self):
726+
"""With timestamps_format='relative', each written timestamp is a delta from the
727+
preceding event (not an offset from measurement start)."""
728+
msgs = [
729+
can.Message(timestamp=100.0, arbitration_id=0x1, data=b"\x01"),
730+
can.Message(timestamp=100.3, arbitration_id=0x2, data=b"\x02"),
731+
can.Message(timestamp=101.0, arbitration_id=0x3, data=b"\x03"),
732+
]
733+
734+
with can.ASCWriter(self.test_file_name, timestamps_format="relative") as writer:
735+
for m in msgs:
736+
writer.on_message_received(m)
737+
738+
with can.ASCReader(self.test_file_name, relative_timestamp=True) as reader:
739+
result = list(reader)
740+
741+
self.assertEqual(len(result), len(msgs))
742+
# msg1: 0.0 (delta from "Start of measurement" at same time)
743+
# msg2: 0.3 (delta from msg1)
744+
# msg3: 0.7 (delta from msg2 — NOT 1.0, which would be absolute offset)
745+
self.assertAlmostEqual(result[0].timestamp, 0.0, places=5)
746+
self.assertAlmostEqual(result[1].timestamp, 0.3, places=5)
747+
self.assertAlmostEqual(result[2].timestamp, 0.7, places=5)
748+
749+
def test_write_absolute_timestamps_are_offsets_from_start(self):
750+
"""With timestamps_format='absolute' (default), each written timestamp is an
751+
offset from the measurement start, not a per-event delta."""
752+
msgs = [
753+
can.Message(timestamp=100.0, arbitration_id=0x1, data=b"\x01"),
754+
can.Message(timestamp=100.3, arbitration_id=0x2, data=b"\x02"),
755+
can.Message(timestamp=101.0, arbitration_id=0x3, data=b"\x03"),
756+
]
757+
758+
with can.ASCWriter(self.test_file_name, timestamps_format="absolute") as writer:
759+
for m in msgs:
760+
writer.on_message_received(m)
761+
762+
with can.ASCReader(self.test_file_name, relative_timestamp=True) as reader:
763+
result = list(reader)
764+
765+
self.assertEqual(len(result), len(msgs))
766+
# All timestamps are offsets from the measurement start (100.0):
767+
self.assertAlmostEqual(result[0].timestamp, 0.0, places=5)
768+
self.assertAlmostEqual(result[1].timestamp, 0.3, places=5)
769+
self.assertAlmostEqual(result[2].timestamp, 1.0, places=5)
770+
725771
@parameterized.expand(
726772
[
727773
(

0 commit comments

Comments
 (0)