From d5eb6474ca2cba7b48b105a93af1e94d48dd73a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lasse=20Fr=C3=B6hner?= Date: Thu, 24 Jul 2025 14:03:39 +0200 Subject: [PATCH 1/4] Fix bitrate calculation for PCAN interfaces --- .../can/media/pythoncan/_pythoncan.py | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/pycyphal/transport/can/media/pythoncan/_pythoncan.py b/pycyphal/transport/can/media/pythoncan/_pythoncan.py index 28f0ce6f..584fdf26 100644 --- a/pycyphal/transport/can/media/pythoncan/_pythoncan.py +++ b/pycyphal/transport/can/media/pythoncan/_pythoncan.py @@ -491,32 +491,21 @@ def _construct_pcan(parameters: _InterfaceParameters) -> can.ThreadSafeBus: if isinstance(parameters, _FDInterfaceParameters): if parameters.bitrate[0] == 0 or parameters.bitrate[1] == 0: raise InvalidMediaConfigurationError("Bitrate must be non-zero") - # These magic numbers come from the settings of PCAN adapter. - # They don't allow any direct baudrate settings, you have to set all lengths and value of the main frequency. - # Bit lengths below are very universal and can be applied for almost every popular baudrate. - # There is probably a better solution here, but it needs significantly more time to implement it. - f_clock = 40000000 - nom_tseg1, nom_tseg2, nom_sjw = 3, 1, 1 - data_tseg1, data_tseg2, data_sjw = 3, 1, 1 - - nom_br = int(f_clock / parameters.bitrate[0] / (nom_tseg1 + nom_tseg2 + nom_sjw)) - data_br = int(f_clock / parameters.bitrate[1] / (data_tseg1 + data_tseg2 + data_sjw)) - # TODO: validate the result and see if it is within an acceptable range + timing = can.BitTimingFd.from_sample_point( + f_clock=80_000_000, # TODO: 80 MHz is a good choice for high data rates, what about lower ones? + nom_bitrate=parameters.bitrate[0], + nom_sample_point=87.5, + data_bitrate=parameters.bitrate[1], + data_sample_point=87.5, + ) + _logger.debug("PCAN timing solution: %s", timing) return ( PythonCANBusOptions(), can.ThreadSafeBus( interface=parameters.interface_name, channel=parameters.channel_name, - f_clock=f_clock, - nom_brp=nom_br, - data_brp=data_br, - nom_tseg1=nom_tseg1, - nom_tseg2=nom_tseg2, - nom_sjw=nom_sjw, - data_tseg1=data_tseg1, - data_tseg2=data_tseg2, - data_sjw=data_sjw, + timing=timing, fd=True, ), ) From 78bdf3d8d6dd08341e6fb8a3df4d788b009b6244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lasse=20Fr=C3=B6hner?= Date: Mon, 28 Jul 2025 10:26:59 +0200 Subject: [PATCH 2/4] Fix CAN FD bitrate switching Set the BRS bit for pythoncan transports when the interface operates in CAN FD mode. Fixes https://github.com/OpenCyphal/pycyphal/issues/368 --- pycyphal/transport/can/media/pythoncan/_pythoncan.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pycyphal/transport/can/media/pythoncan/_pythoncan.py b/pycyphal/transport/can/media/pythoncan/_pythoncan.py index 584fdf26..a62eb2cd 100644 --- a/pycyphal/transport/can/media/pythoncan/_pythoncan.py +++ b/pycyphal/transport/can/media/pythoncan/_pythoncan.py @@ -300,6 +300,7 @@ async def send(self, frames: typing.Iterable[Envelope], monotonic_deadline: floa is_extended_id=(f.frame.format == FrameFormat.EXTENDED), data=f.frame.data, is_fd=self._is_fd, + bitrate_switch=self._is_fd, ) try: desired_timeout = monotonic_deadline - loop.time() From 36bee2bde674368b2460aa70e9888ca4e37594a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lasse=20Fr=C3=B6hner?= Date: Mon, 4 Aug 2025 08:24:23 +0200 Subject: [PATCH 3/4] Bump patch version to 1.24.4 --- pycyphal/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycyphal/_version.py b/pycyphal/_version.py index 1a569d73..d0702e5a 100644 --- a/pycyphal/_version.py +++ b/pycyphal/_version.py @@ -1 +1 @@ -__version__ = "1.24.3" +__version__ = "1.24.4" From aa1b0884ff5524b58115aea7dcbb165a98cba66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lasse=20Fr=C3=B6hner?= Date: Mon, 4 Aug 2025 09:23:03 +0200 Subject: [PATCH 4/4] Set sample point location to 80% --- pycyphal/transport/can/media/pythoncan/_pythoncan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycyphal/transport/can/media/pythoncan/_pythoncan.py b/pycyphal/transport/can/media/pythoncan/_pythoncan.py index a62eb2cd..21b5c7d9 100644 --- a/pycyphal/transport/can/media/pythoncan/_pythoncan.py +++ b/pycyphal/transport/can/media/pythoncan/_pythoncan.py @@ -496,9 +496,9 @@ def _construct_pcan(parameters: _InterfaceParameters) -> can.ThreadSafeBus: timing = can.BitTimingFd.from_sample_point( f_clock=80_000_000, # TODO: 80 MHz is a good choice for high data rates, what about lower ones? nom_bitrate=parameters.bitrate[0], - nom_sample_point=87.5, + nom_sample_point=80, data_bitrate=parameters.bitrate[1], - data_sample_point=87.5, + data_sample_point=80, ) _logger.debug("PCAN timing solution: %s", timing) return (