Skip to content

Commit 0f89a20

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 4a31ba1 commit 0f89a20

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
@@ -462,10 +462,22 @@ def log_event(self, message: str, timestamp: float | None = None) -> None:
462462
# Use last known timestamp if unknown
463463
if timestamp is None:
464464
timestamp = self.last_timestamp
465-
# turn into relative timestamps if necessary
466-
if timestamp >= self.started:
467-
timestamp -= self.started
468-
line = self.FORMAT_EVENT.format(timestamp=timestamp, message=message)
465+
# Compute written timestamp based on configured format
466+
if self.timestamps_format == "absolute":
467+
# offsets from the start of measurement
468+
written_timestamp = (
469+
timestamp - self.started if timestamp >= self.started else timestamp
470+
)
471+
else:
472+
# deltas from the preceding event
473+
written_timestamp = (
474+
timestamp - self.last_timestamp
475+
if timestamp >= self.last_timestamp
476+
else 0.0
477+
)
478+
# Track last timestamp so the next event can compute its delta
479+
self.last_timestamp = timestamp
480+
line = self.FORMAT_EVENT.format(timestamp=written_timestamp, message=message)
469481
self.file.write(line)
470482

471483
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
@@ -725,10 +725,56 @@ def test_write_relative_timestamp_roundtrip(self):
725725
result = list(reader)
726726

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

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

0 commit comments

Comments
 (0)