• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hardbyte / python-can / 16362801995

18 Jul 2025 05:17AM UTC coverage: 70.862% (+0.1%) from 70.763%
16362801995

Pull #1920

github

web-flow
Merge f9e8a3c29 into 958fc64ed
Pull Request #1920: add FD support to slcan according to CANable 2.0 impementation

6 of 45 new or added lines in 1 file covered. (13.33%)

838 existing lines in 35 files now uncovered.

7770 of 10965 relevant lines covered (70.86%)

13.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.24
/can/bit_timing.py
1
# pylint: disable=too-many-lines
2
import math
21✔
3
from collections.abc import Iterator, Mapping
21✔
4
from typing import TYPE_CHECKING, cast
21✔
5

6
if TYPE_CHECKING:
21✔
UNCOV
7
    from can.typechecking import BitTimingDict, BitTimingFdDict
×
8

9

10
class BitTiming(Mapping):
21✔
11
    """Representation of a bit timing configuration for a CAN 2.0 bus.
21✔
12

13
    The class can be constructed in multiple ways, depending on the information
14
    available. The preferred way is using CAN clock frequency, prescaler, tseg1, tseg2 and sjw::
15

16
        can.BitTiming(f_clock=8_000_000, brp=1, tseg1=5, tseg2=1, sjw=1)
17

18
    Alternatively you can set the bitrate instead of the bit rate prescaler::
19

20
        can.BitTiming.from_bitrate_and_segments(
21
            f_clock=8_000_000, bitrate=1_000_000, tseg1=5, tseg2=1, sjw=1
22
        )
23

24
    It is also possible to specify BTR registers::
25

26
        can.BitTiming.from_registers(f_clock=8_000_000, btr0=0x00, btr1=0x14)
27

28
    or to calculate the timings for a given sample point::
29

30
        can.BitTiming.from_sample_point(f_clock=8_000_000, bitrate=1_000_000, sample_point=75.0)
31
    """
32

33
    def __init__(
21✔
34
        self,
35
        f_clock: int,
36
        brp: int,
37
        tseg1: int,
38
        tseg2: int,
39
        sjw: int,
40
        nof_samples: int = 1,
41
        strict: bool = False,
42
    ) -> None:
43
        """
44
        :param int f_clock:
45
            The CAN system clock frequency in Hz.
46
        :param int brp:
47
            Bit rate prescaler.
48
        :param int tseg1:
49
            Time segment 1, that is, the number of quanta from (but not including)
50
            the Sync Segment to the sampling point.
51
        :param int tseg2:
52
            Time segment 2, that is, the number of quanta from the sampling
53
            point to the end of the bit.
54
        :param int sjw:
55
            The Synchronization Jump Width. Decides the maximum number of time quanta
56
            that the controller can resynchronize every bit.
57
        :param int nof_samples:
58
            Either 1 or 3. Some CAN controllers can also sample each bit three times.
59
            In this case, the bit will be sampled three quanta in a row,
60
            with the last sample being taken in the edge between TSEG1 and TSEG2.
61
            Three samples should only be used for relatively slow baudrates.
62
        :param bool strict:
63
            If True, restrict bit timings to the minimum required range as defined in
64
            ISO 11898. This can be used to ensure compatibility across a wide variety
65
            of CAN hardware.
66
        :raises ValueError:
67
            if the arguments are invalid.
68
        """
69
        self._data: BitTimingDict = {
21✔
70
            "f_clock": f_clock,
71
            "brp": brp,
72
            "tseg1": tseg1,
73
            "tseg2": tseg2,
74
            "sjw": sjw,
75
            "nof_samples": nof_samples,
76
        }
77
        self._validate()
21✔
78
        if strict:
21✔
79
            self._restrict_to_minimum_range()
21✔
80

81
    def _validate(self) -> None:
21✔
82
        if not 1 <= self.brp <= 64:
21✔
83
            raise ValueError(f"bitrate prescaler (={self.brp}) must be in [1...64].")
21✔
84

85
        if not 1 <= self.tseg1 <= 16:
21✔
86
            raise ValueError(f"tseg1 (={self.tseg1}) must be in [1...16].")
21✔
87

88
        if not 1 <= self.tseg2 <= 8:
21✔
89
            raise ValueError(f"tseg2 (={self.tseg2}) must be in [1...8].")
21✔
90

91
        if not 1 <= self.sjw <= 4:
21✔
UNCOV
92
            raise ValueError(f"sjw (={self.sjw}) must be in [1...4].")
×
93

94
        if self.sjw > self.tseg2:
21✔
UNCOV
95
            raise ValueError(
×
96
                f"sjw (={self.sjw}) must not be greater than tseg2 (={self.tseg2})."
97
            )
98

99
        if self.sample_point < 50.0:
21✔
UNCOV
100
            raise ValueError(
×
101
                f"The sample point must be greater than or equal to 50% "
102
                f"(sample_point={self.sample_point:.2f}%)."
103
            )
104

105
        if self.nof_samples not in (1, 3):
21✔
UNCOV
106
            raise ValueError("nof_samples must be 1 or 3")
×
107

108
    def _restrict_to_minimum_range(self) -> None:
21✔
109
        if not 8 <= self.nbt <= 25:
21✔
UNCOV
110
            raise ValueError(f"nominal bit time (={self.nbt}) must be in [8...25].")
×
111

112
        if not 1 <= self.brp <= 32:
21✔
UNCOV
113
            raise ValueError(f"bitrate prescaler (={self.brp}) must be in [1...32].")
×
114

115
        if not 5_000 <= self.bitrate <= 1_000_000:
21✔
UNCOV
116
            raise ValueError(
×
117
                f"bitrate (={self.bitrate}) must be in [5,000...1,000,000]."
118
            )
119

120
    @classmethod
21✔
121
    def from_bitrate_and_segments(
21✔
122
        cls,
123
        f_clock: int,
124
        bitrate: int,
125
        tseg1: int,
126
        tseg2: int,
127
        sjw: int,
128
        nof_samples: int = 1,
129
        strict: bool = False,
130
    ) -> "BitTiming":
131
        """Create a :class:`~can.BitTiming` instance from bitrate and segment lengths.
132

133
        :param int f_clock:
134
            The CAN system clock frequency in Hz.
135
        :param int bitrate:
136
            Bitrate in bit/s.
137
        :param int tseg1:
138
            Time segment 1, that is, the number of quanta from (but not including)
139
            the Sync Segment to the sampling point.
140
        :param int tseg2:
141
            Time segment 2, that is, the number of quanta from the sampling
142
            point to the end of the bit.
143
        :param int sjw:
144
            The Synchronization Jump Width. Decides the maximum number of time quanta
145
            that the controller can resynchronize every bit.
146
        :param int nof_samples:
147
            Either 1 or 3. Some CAN controllers can also sample each bit three times.
148
            In this case, the bit will be sampled three quanta in a row,
149
            with the last sample being taken in the edge between TSEG1 and TSEG2.
150
            Three samples should only be used for relatively slow baudrates.
151
        :param bool strict:
152
            If True, restrict bit timings to the minimum required range as defined in
153
            ISO 11898. This can be used to ensure compatibility across a wide variety
154
            of CAN hardware.
155
        :raises ValueError:
156
            if the arguments are invalid.
157
        """
158
        try:
21✔
159
            brp = round(f_clock / (bitrate * (1 + tseg1 + tseg2)))
21✔
160
        except ZeroDivisionError:
×
UNCOV
161
            raise ValueError("Invalid inputs") from None
×
162

163
        bt = cls(
21✔
164
            f_clock=f_clock,
165
            brp=brp,
166
            tseg1=tseg1,
167
            tseg2=tseg2,
168
            sjw=sjw,
169
            nof_samples=nof_samples,
170
            strict=strict,
171
        )
172
        if abs(bt.bitrate - bitrate) > bitrate / 256:
21✔
UNCOV
173
            raise ValueError(
×
174
                f"the effective bitrate (={bt.bitrate}) diverges "
175
                f"from the requested bitrate (={bitrate})"
176
            )
177
        return bt
21✔
178

179
    @classmethod
21✔
180
    def from_registers(
21✔
181
        cls,
182
        f_clock: int,
183
        btr0: int,
184
        btr1: int,
185
    ) -> "BitTiming":
186
        """Create a :class:`~can.BitTiming` instance from registers btr0 and btr1.
187

188
        :param int f_clock:
189
            The CAN system clock frequency in Hz.
190
        :param int btr0:
191
            The BTR0 register value used by many CAN controllers.
192
        :param int btr1:
193
            The BTR1 register value used by many CAN controllers.
194
        :raises ValueError:
195
            if the arguments are invalid.
196
        """
197
        if not 0 <= btr0 < 2**16:
21✔
UNCOV
198
            raise ValueError(f"Invalid btr0 value. ({btr0})")
×
199
        if not 0 <= btr1 < 2**16:
21✔
UNCOV
200
            raise ValueError(f"Invalid btr1 value. ({btr1})")
×
201

202
        brp = (btr0 & 0x3F) + 1
21✔
203
        sjw = (btr0 >> 6) + 1
21✔
204
        tseg1 = (btr1 & 0xF) + 1
21✔
205
        tseg2 = ((btr1 >> 4) & 0x7) + 1
21✔
206
        nof_samples = 3 if btr1 & 0x80 else 1
21✔
207
        return cls(
21✔
208
            brp=brp,
209
            f_clock=f_clock,
210
            tseg1=tseg1,
211
            tseg2=tseg2,
212
            sjw=sjw,
213
            nof_samples=nof_samples,
214
        )
215

216
    @classmethod
21✔
217
    def iterate_from_sample_point(
21✔
218
        cls, f_clock: int, bitrate: int, sample_point: float = 69.0
219
    ) -> Iterator["BitTiming"]:
220
        """Create a :class:`~can.BitTiming` iterator with all the solutions for a sample point.
221

222
        :param int f_clock:
223
            The CAN system clock frequency in Hz.
224
        :param int bitrate:
225
            Bitrate in bit/s.
226
        :param int sample_point:
227
            The sample point value in percent.
228
        :raises ValueError:
229
            if the arguments are invalid.
230
        """
231

232
        if sample_point < 50.0:
21✔
UNCOV
233
            raise ValueError(f"sample_point (={sample_point}) must not be below 50%.")
×
234

235
        for brp in range(1, 65):
21✔
236
            nbt = int(f_clock / (bitrate * brp))
21✔
237
            if nbt < 8:
21✔
238
                break
21✔
239

240
            effective_bitrate = f_clock / (nbt * brp)
21✔
241
            if abs(effective_bitrate - bitrate) > bitrate / 256:
21✔
242
                continue
21✔
243

244
            tseg1 = round(sample_point / 100 * nbt) - 1
21✔
245
            # limit tseg1, so tseg2 is at least 1 TQ
246
            tseg1 = min(tseg1, nbt - 2)
21✔
247

248
            tseg2 = nbt - tseg1 - 1
21✔
249
            sjw = min(tseg2, 4)
21✔
250

251
            try:
21✔
252
                bt = BitTiming(
21✔
253
                    f_clock=f_clock,
254
                    brp=brp,
255
                    tseg1=tseg1,
256
                    tseg2=tseg2,
257
                    sjw=sjw,
258
                    strict=True,
259
                )
260
                yield bt
21✔
261
            except ValueError:
21✔
262
                continue
21✔
263

264
    @classmethod
21✔
265
    def from_sample_point(
21✔
266
        cls, f_clock: int, bitrate: int, sample_point: float = 69.0
267
    ) -> "BitTiming":
268
        """Create a :class:`~can.BitTiming` instance for a sample point.
269

270
        This function tries to find bit timings, which are close to the requested
271
        sample point. It does not take physical bus properties into account, so the
272
        calculated bus timings might not work properly for you.
273

274
        The :func:`oscillator_tolerance` function might be helpful to evaluate the
275
        bus timings.
276

277
        :param int f_clock:
278
            The CAN system clock frequency in Hz.
279
        :param int bitrate:
280
            Bitrate in bit/s.
281
        :param int sample_point:
282
            The sample point value in percent.
283
        :raises ValueError:
284
            if the arguments are invalid.
285
        """
286

287
        if sample_point < 50.0:
21✔
UNCOV
288
            raise ValueError(f"sample_point (={sample_point}) must not be below 50%.")
×
289

290
        possible_solutions: list[BitTiming] = list(
21✔
291
            cls.iterate_from_sample_point(f_clock, bitrate, sample_point)
292
        )
293

294
        if not possible_solutions:
21✔
295
            raise ValueError("No suitable bit timings found.")
21✔
296

297
        # sort solutions
298
        for key, reverse in (
21✔
299
            # prefer low prescaler
300
            (lambda x: x.brp, False),
301
            # prefer low sample point deviation from requested values
302
            (lambda x: abs(x.sample_point - sample_point), False),
303
        ):
304
            possible_solutions.sort(key=key, reverse=reverse)
21✔
305

306
        return possible_solutions[0]
21✔
307

308
    @property
21✔
309
    def f_clock(self) -> int:
21✔
310
        """The CAN system clock frequency in Hz."""
311
        return self._data["f_clock"]
21✔
312

313
    @property
21✔
314
    def bitrate(self) -> int:
21✔
315
        """Bitrate in bits/s."""
316
        return round(self.f_clock / (self.nbt * self.brp))
21✔
317

318
    @property
21✔
319
    def brp(self) -> int:
21✔
320
        """Bit Rate Prescaler."""
321
        return self._data["brp"]
21✔
322

323
    @property
21✔
324
    def tq(self) -> int:
21✔
325
        """Time quantum in nanoseconds"""
326
        return round(self.brp / self.f_clock * 1e9)
21✔
327

328
    @property
21✔
329
    def nbt(self) -> int:
21✔
330
        """Nominal Bit Time."""
331
        return 1 + self.tseg1 + self.tseg2
21✔
332

333
    @property
21✔
334
    def tseg1(self) -> int:
21✔
335
        """Time segment 1.
336

337
        The number of quanta from (but not including) the Sync Segment to the sampling point.
338
        """
339
        return self._data["tseg1"]
21✔
340

341
    @property
21✔
342
    def tseg2(self) -> int:
21✔
343
        """Time segment 2.
344

345
        The number of quanta from the sampling point to the end of the bit.
346
        """
347
        return self._data["tseg2"]
21✔
348

349
    @property
21✔
350
    def sjw(self) -> int:
21✔
351
        """Synchronization Jump Width."""
352
        return self._data["sjw"]
21✔
353

354
    @property
21✔
355
    def nof_samples(self) -> int:
21✔
356
        """Number of samples (1 or 3)."""
357
        return self._data["nof_samples"]
21✔
358

359
    @property
21✔
360
    def sample_point(self) -> float:
21✔
361
        """Sample point in percent."""
362
        return 100.0 * (1 + self.tseg1) / (1 + self.tseg1 + self.tseg2)
21✔
363

364
    @property
21✔
365
    def btr0(self) -> int:
21✔
366
        """Bit timing register 0 for SJA1000."""
367
        return (self.sjw - 1) << 6 | self.brp - 1
21✔
368

369
    @property
21✔
370
    def btr1(self) -> int:
21✔
371
        """Bit timing register 1 for SJA1000."""
372
        sam = 1 if self.nof_samples == 3 else 0
21✔
373
        return sam << 7 | (self.tseg2 - 1) << 4 | self.tseg1 - 1
21✔
374

375
    def oscillator_tolerance(
21✔
376
        self,
377
        node_loop_delay_ns: float = 250.0,
378
        bus_length_m: float = 10.0,
379
    ) -> float:
380
        """Oscillator tolerance in percent according to ISO 11898-1.
381

382
        :param float node_loop_delay_ns:
383
            Transceiver loop delay in nanoseconds.
384
        :param float bus_length_m:
385
            Bus length in meters.
386
        """
387
        delay_per_meter = 5
21✔
388
        bidirectional_propagation_delay_ns = 2 * (
21✔
389
            node_loop_delay_ns + delay_per_meter * bus_length_m
390
        )
391

392
        prop_seg = math.ceil(bidirectional_propagation_delay_ns / self.tq)
21✔
393
        nom_phase_seg1 = self.tseg1 - prop_seg
21✔
394
        nom_phase_seg2 = self.tseg2
21✔
395
        df_clock_list = [
21✔
396
            _oscillator_tolerance_condition_1(nom_sjw=self.sjw, nbt=self.nbt),
397
            _oscillator_tolerance_condition_2(
398
                nbt=self.nbt,
399
                nom_phase_seg1=nom_phase_seg1,
400
                nom_phase_seg2=nom_phase_seg2,
401
            ),
402
        ]
403
        return max(0.0, min(df_clock_list) * 100)
21✔
404

405
    def recreate_with_f_clock(self, f_clock: int) -> "BitTiming":
21✔
406
        """Return a new :class:`~can.BitTiming` instance with the given *f_clock* but the same
407
        bit rate and sample point.
408

409
        :param int f_clock:
410
            The CAN system clock frequency in Hz.
411
        :raises ValueError:
412
            if no suitable bit timings were found.
413
        """
414
        # try the most simple solution first: another bitrate prescaler
415
        try:
21✔
416
            return BitTiming.from_bitrate_and_segments(
21✔
417
                f_clock=f_clock,
418
                bitrate=self.bitrate,
419
                tseg1=self.tseg1,
420
                tseg2=self.tseg2,
421
                sjw=self.sjw,
422
                nof_samples=self.nof_samples,
423
                strict=True,
424
            )
425
        except ValueError:
21✔
426
            pass
21✔
427

428
        # create a new timing instance with the same sample point
429
        bt = BitTiming.from_sample_point(
21✔
430
            f_clock=f_clock, bitrate=self.bitrate, sample_point=self.sample_point
431
        )
432
        if abs(bt.sample_point - self.sample_point) > 1.0:
×
UNCOV
433
            raise ValueError(
×
434
                "f_clock change failed because of sample point discrepancy."
435
            )
436
        # adapt synchronization jump width, so it has the same size relative to bit time as self
437
        sjw = round(self.sjw / self.nbt * bt.nbt)
×
438
        sjw = max(1, min(4, bt.tseg2, sjw))
×
439
        bt._data["sjw"] = sjw  # pylint: disable=protected-access
×
440
        bt._data["nof_samples"] = self.nof_samples  # pylint: disable=protected-access
×
441
        bt._validate()  # pylint: disable=protected-access
×
UNCOV
442
        return bt
×
443

444
    def __str__(self) -> str:
21✔
445
        segments = [
21✔
446
            f"BR: {self.bitrate:_} bit/s",
447
            f"SP: {self.sample_point:.2f}%",
448
            f"BRP: {self.brp}",
449
            f"TSEG1: {self.tseg1}",
450
            f"TSEG2: {self.tseg2}",
451
            f"SJW: {self.sjw}",
452
            f"BTR: {self.btr0:02X}{self.btr1:02X}h",
453
            f"CLK: {self.f_clock / 1e6:.0f}MHz",
454
        ]
455
        return ", ".join(segments)
21✔
456

457
    def __repr__(self) -> str:
21✔
458
        args = ", ".join(f"{key}={value}" for key, value in self.items())
21✔
459
        return f"can.{self.__class__.__name__}({args})"
21✔
460

461
    def __getitem__(self, key: str) -> int:
21✔
462
        return cast("int", self._data.__getitem__(key))
21✔
463

464
    def __len__(self) -> int:
21✔
465
        return self._data.__len__()
21✔
466

467
    def __iter__(self) -> Iterator[str]:
21✔
468
        return self._data.__iter__()
21✔
469

470
    def __eq__(self, other: object) -> bool:
21✔
471
        if not isinstance(other, BitTiming):
21✔
472
            return False
21✔
473

474
        return self._data == other._data
21✔
475

476
    def __hash__(self) -> int:
21✔
477
        return tuple(self._data.values()).__hash__()
21✔
478

479

480
class BitTimingFd(Mapping):
21✔
481
    """Representation of a bit timing configuration for a CAN FD bus.
21✔
482

483
    The class can be constructed in multiple ways, depending on the information
484
    available. The preferred way is using CAN clock frequency, bit rate prescaler, tseg1,
485
    tseg2 and sjw for both the arbitration (nominal) and data phase::
486

487
        can.BitTimingFd(
488
            f_clock=80_000_000,
489
            nom_brp=1,
490
            nom_tseg1=59,
491
            nom_tseg2=20,
492
            nom_sjw=10,
493
            data_brp=1,
494
            data_tseg1=6,
495
            data_tseg2=3,
496
            data_sjw=2,
497
        )
498

499
    Alternatively you can set the bit rates instead of the bit rate prescalers::
500

501
        can.BitTimingFd.from_bitrate_and_segments(
502
            f_clock=80_000_000,
503
            nom_bitrate=1_000_000,
504
            nom_tseg1=59,
505
            nom_tseg2=20,
506
            nom_sjw=10,
507
            data_bitrate=8_000_000,
508
            data_tseg1=6,
509
            data_tseg2=3,
510
            data_sjw=2,
511
        )
512

513
    It is also possible to calculate the timings for a given
514
    pair of arbitration and data sample points::
515

516
        can.BitTimingFd.from_sample_point(
517
            f_clock=80_000_000,
518
            nom_bitrate=1_000_000,
519
            nom_sample_point=75.0,
520
            data_bitrate=8_000_000,
521
            data_sample_point=70.0,
522
        )
523
    """
524

525
    def __init__(  # pylint: disable=too-many-arguments
21✔
526
        self,
527
        f_clock: int,
528
        nom_brp: int,
529
        nom_tseg1: int,
530
        nom_tseg2: int,
531
        nom_sjw: int,
532
        data_brp: int,
533
        data_tseg1: int,
534
        data_tseg2: int,
535
        data_sjw: int,
536
        strict: bool = False,
537
    ) -> None:
538
        """
539
        Initialize a BitTimingFd instance with the specified parameters.
540

541
        :param int f_clock:
542
            The CAN system clock frequency in Hz.
543
        :param int nom_brp:
544
            Nominal (arbitration) phase bitrate prescaler.
545
        :param int nom_tseg1:
546
            Nominal phase Time segment 1, that is, the number of quanta from (but not including)
547
            the Sync Segment to the sampling point.
548
        :param int nom_tseg2:
549
            Nominal phase Time segment 2, that is, the number of quanta from the sampling
550
            point to the end of the bit.
551
        :param int nom_sjw:
552
            The Synchronization Jump Width for the nominal phase. This value determines
553
            the maximum number of time quanta that the controller can resynchronize every bit.
554
        :param int data_brp:
555
            Data phase bitrate prescaler.
556
        :param int data_tseg1:
557
            Data phase Time segment 1, that is, the number of quanta from (but not including)
558
            the Sync Segment to the sampling point.
559
        :param int data_tseg2:
560
            Data phase Time segment 2, that is, the number of quanta from the sampling
561
            point to the end of the bit.
562
        :param int data_sjw:
563
            The Synchronization Jump Width for the data phase. This value determines
564
            the maximum number of time quanta that the controller can resynchronize every bit.
565
        :param bool strict:
566
            If True, restrict bit timings to the minimum required range as defined in
567
            ISO 11898. This can be used to ensure compatibility across a wide variety
568
            of CAN hardware.
569
        :raises ValueError:
570
            if the arguments are invalid.
571
        """
572
        self._data: BitTimingFdDict = {
21✔
573
            "f_clock": f_clock,
574
            "nom_brp": nom_brp,
575
            "nom_tseg1": nom_tseg1,
576
            "nom_tseg2": nom_tseg2,
577
            "nom_sjw": nom_sjw,
578
            "data_brp": data_brp,
579
            "data_tseg1": data_tseg1,
580
            "data_tseg2": data_tseg2,
581
            "data_sjw": data_sjw,
582
        }
583
        self._validate()
21✔
584
        if strict:
21✔
585
            self._restrict_to_minimum_range()
21✔
586

587
    def _validate(self) -> None:
21✔
588
        for param, value in self._data.items():
21✔
589
            if value < 0:  # type: ignore[operator]
21✔
590
                err_msg = f"'{param}' (={value}) must not be negative."
21✔
591
                raise ValueError(err_msg)
21✔
592

593
        if self.nom_brp < 1:
21✔
594
            raise ValueError(
21✔
595
                f"nominal bitrate prescaler (={self.nom_brp}) must be at least 1."
596
            )
597

598
        if self.data_brp < 1:
21✔
UNCOV
599
            raise ValueError(
×
600
                f"data bitrate prescaler (={self.data_brp}) must be at least 1."
601
            )
602

603
        if self.data_bitrate < self.nom_bitrate:
21✔
UNCOV
604
            raise ValueError(
×
605
                f"data_bitrate (={self.data_bitrate}) must be greater than or "
606
                f"equal to nom_bitrate (={self.nom_bitrate})"
607
            )
608

609
        if self.nom_sjw > self.nom_tseg2:
21✔
UNCOV
610
            raise ValueError(
×
611
                f"nom_sjw (={self.nom_sjw}) must not be "
612
                f"greater than nom_tseg2 (={self.nom_tseg2})."
613
            )
614

615
        if self.data_sjw > self.data_tseg2:
21✔
UNCOV
616
            raise ValueError(
×
617
                f"data_sjw (={self.data_sjw}) must not be "
618
                f"greater than data_tseg2 (={self.data_tseg2})."
619
            )
620

621
        if self.nom_sample_point < 50.0:
21✔
622
            raise ValueError(
21✔
623
                f"The arbitration sample point must be greater than or equal to 50% "
624
                f"(nom_sample_point={self.nom_sample_point:.2f}%)."
625
            )
626

627
        if self.data_sample_point < 50.0:
21✔
628
            raise ValueError(
21✔
629
                f"The data sample point must be greater than or equal to 50% "
630
                f"(data_sample_point={self.data_sample_point:.2f}%)."
631
            )
632

633
    def _restrict_to_minimum_range(self) -> None:
21✔
634
        # restrict to minimum required range as defined in ISO 11898
635
        if not 8 <= self.nbt <= 80:
21✔
636
            raise ValueError(f"Nominal bit time (={self.nbt}) must be in [8...80]")
21✔
637

638
        if not 5 <= self.dbt <= 25:
21✔
639
            raise ValueError(f"Nominal bit time (={self.dbt}) must be in [5...25]")
21✔
640

641
        if not 1 <= self.data_tseg1 <= 16:
21✔
642
            raise ValueError(f"data_tseg1 (={self.data_tseg1}) must be in [1...16].")
21✔
643

644
        if not 2 <= self.data_tseg2 <= 8:
21✔
645
            raise ValueError(f"data_tseg2 (={self.data_tseg2}) must be in [2...8].")
21✔
646

647
        if not 1 <= self.data_sjw <= 8:
21✔
UNCOV
648
            raise ValueError(f"data_sjw (={self.data_sjw}) must be in [1...8].")
×
649

650
        if self.nom_brp == self.data_brp:
21✔
651
            # shared prescaler
652
            if not 2 <= self.nom_tseg1 <= 128:
21✔
UNCOV
653
                raise ValueError(f"nom_tseg1 (={self.nom_tseg1}) must be in [2...128].")
×
654

655
            if not 2 <= self.nom_tseg2 <= 32:
21✔
656
                raise ValueError(f"nom_tseg2 (={self.nom_tseg2}) must be in [2...32].")
21✔
657

658
            if not 1 <= self.nom_sjw <= 32:
21✔
UNCOV
659
                raise ValueError(f"nom_sjw (={self.nom_sjw}) must be in [1...32].")
×
660
        else:
661
            # separate prescaler
662
            if not 2 <= self.nom_tseg1 <= 64:
21✔
663
                raise ValueError(f"nom_tseg1 (={self.nom_tseg1}) must be in [2...64].")
21✔
664

665
            if not 2 <= self.nom_tseg2 <= 16:
21✔
666
                raise ValueError(f"nom_tseg2 (={self.nom_tseg2}) must be in [2...16].")
21✔
667

668
            if not 1 <= self.nom_sjw <= 16:
21✔
UNCOV
669
                raise ValueError(f"nom_sjw (={self.nom_sjw}) must be in [1...16].")
×
670

671
    @classmethod
21✔
672
    def from_bitrate_and_segments(  # pylint: disable=too-many-arguments
21✔
673
        cls,
674
        f_clock: int,
675
        nom_bitrate: int,
676
        nom_tseg1: int,
677
        nom_tseg2: int,
678
        nom_sjw: int,
679
        data_bitrate: int,
680
        data_tseg1: int,
681
        data_tseg2: int,
682
        data_sjw: int,
683
        strict: bool = False,
684
    ) -> "BitTimingFd":
685
        """
686
        Create a :class:`~can.BitTimingFd` instance with the bitrates and segments lengths.
687

688
        :param int f_clock:
689
            The CAN system clock frequency in Hz.
690
        :param int nom_bitrate:
691
            Nominal (arbitration) phase bitrate in bit/s.
692
        :param int nom_tseg1:
693
            Nominal phase Time segment 1, that is, the number of quanta from (but not including)
694
            the Sync Segment to the sampling point.
695
        :param int nom_tseg2:
696
            Nominal phase Time segment 2, that is, the number of quanta from the sampling
697
            point to the end of the bit.
698
        :param int nom_sjw:
699
            The Synchronization Jump Width for the nominal phase. This value determines
700
            the maximum number of time quanta that the controller can resynchronize every bit.
701
        :param int data_bitrate:
702
            Data phase bitrate in bit/s.
703
        :param int data_tseg1:
704
            Data phase Time segment 1, that is, the number of quanta from (but not including)
705
            the Sync Segment to the sampling point.
706
        :param int data_tseg2:
707
            Data phase Time segment 2, that is, the number of quanta from the sampling
708
            point to the end of the bit.
709
        :param int data_sjw:
710
            The Synchronization Jump Width for the data phase. This value determines
711
            the maximum number of time quanta that the controller can resynchronize every bit.
712
        :param bool strict:
713
            If True, restrict bit timings to the minimum required range as defined in
714
            ISO 11898. This can be used to ensure compatibility across a wide variety
715
            of CAN hardware.
716
        :raises ValueError:
717
            if the arguments are invalid.
718
        """
719
        try:
21✔
720
            nom_brp = round(f_clock / (nom_bitrate * (1 + nom_tseg1 + nom_tseg2)))
21✔
721
            data_brp = round(f_clock / (data_bitrate * (1 + data_tseg1 + data_tseg2)))
21✔
UNCOV
722
        except ZeroDivisionError:
×
723
            raise ValueError("Invalid inputs.") from None
×
724

725
        bt = cls(
21✔
726
            f_clock=f_clock,
727
            nom_brp=nom_brp,
728
            nom_tseg1=nom_tseg1,
729
            nom_tseg2=nom_tseg2,
730
            nom_sjw=nom_sjw,
731
            data_brp=data_brp,
732
            data_tseg1=data_tseg1,
733
            data_tseg2=data_tseg2,
734
            data_sjw=data_sjw,
735
            strict=strict,
736
        )
737

738
        if abs(bt.nom_bitrate - nom_bitrate) > nom_bitrate / 256:
21✔
UNCOV
739
            raise ValueError(
×
740
                f"the effective nom. bitrate (={bt.nom_bitrate}) diverges "
741
                f"from the requested nom. bitrate (={nom_bitrate})"
742
            )
743

744
        if abs(bt.data_bitrate - data_bitrate) > data_bitrate / 256:
21✔
UNCOV
745
            raise ValueError(
×
746
                f"the effective data bitrate (={bt.data_bitrate}) diverges "
747
                f"from the requested data bitrate (={data_bitrate})"
748
            )
749

750
        return bt
21✔
751

752
    @classmethod
21✔
753
    def iterate_from_sample_point(
21✔
754
        cls,
755
        f_clock: int,
756
        nom_bitrate: int,
757
        nom_sample_point: float,
758
        data_bitrate: int,
759
        data_sample_point: float,
760
    ) -> Iterator["BitTimingFd"]:
761
        """Create an :class:`~can.BitTimingFd` iterator with all the solutions for a sample point.
762

763
        :param int f_clock:
764
            The CAN system clock frequency in Hz.
765
        :param int nom_bitrate:
766
            Nominal bitrate in bit/s.
767
        :param int nom_sample_point:
768
            The sample point value of the arbitration phase in percent.
769
        :param int data_bitrate:
770
            Data bitrate in bit/s.
771
        :param int data_sample_point:
772
            The sample point value of the data phase in percent.
773
        :raises ValueError:
774
            if the arguments are invalid.
775
        """
776
        if nom_sample_point < 50.0:
21✔
UNCOV
777
            raise ValueError(
×
778
                f"nom_sample_point (={nom_sample_point}) must not be below 50%."
779
            )
780

781
        if data_sample_point < 50.0:
21✔
UNCOV
782
            raise ValueError(
×
783
                f"data_sample_point (={data_sample_point}) must not be below 50%."
784
            )
785

786
        sync_seg = 1
21✔
787

788
        for nom_brp in range(1, 257):
21✔
789
            nbt = int(f_clock / (nom_bitrate * nom_brp))
21✔
790
            if nbt < 1:
21✔
791
                break
21✔
792

793
            effective_nom_bitrate = f_clock / (nbt * nom_brp)
21✔
794
            if abs(effective_nom_bitrate - nom_bitrate) > nom_bitrate / 256:
21✔
795
                continue
21✔
796

797
            nom_tseg1 = round(nom_sample_point / 100 * nbt) - 1
21✔
798
            # limit tseg1, so tseg2 is at least 2 TQ
799
            nom_tseg1 = min(nom_tseg1, nbt - sync_seg - 2)
21✔
800
            nom_tseg2 = nbt - nom_tseg1 - 1
21✔
801

802
            nom_sjw = min(nom_tseg2, 128)
21✔
803

804
            for data_brp in range(1, 257):
21✔
805
                dbt = round(int(f_clock / (data_bitrate * data_brp)))
21✔
806
                if dbt < 1:
21✔
807
                    break
21✔
808

809
                effective_data_bitrate = f_clock / (dbt * data_brp)
21✔
810
                if abs(effective_data_bitrate - data_bitrate) > data_bitrate / 256:
21✔
811
                    continue
21✔
812

813
                data_tseg1 = round(data_sample_point / 100 * dbt) - 1
21✔
814
                # limit tseg1, so tseg2 is at least 2 TQ
815
                data_tseg1 = min(data_tseg1, dbt - sync_seg - 2)
21✔
816
                data_tseg2 = dbt - data_tseg1 - 1
21✔
817

818
                data_sjw = min(data_tseg2, 16)
21✔
819

820
                try:
21✔
821
                    bt = BitTimingFd(
21✔
822
                        f_clock=f_clock,
823
                        nom_brp=nom_brp,
824
                        nom_tseg1=nom_tseg1,
825
                        nom_tseg2=nom_tseg2,
826
                        nom_sjw=nom_sjw,
827
                        data_brp=data_brp,
828
                        data_tseg1=data_tseg1,
829
                        data_tseg2=data_tseg2,
830
                        data_sjw=data_sjw,
831
                        strict=True,
832
                    )
833
                    yield bt
21✔
834
                except ValueError:
21✔
835
                    continue
21✔
836

837
    @classmethod
21✔
838
    def from_sample_point(
21✔
839
        cls,
840
        f_clock: int,
841
        nom_bitrate: int,
842
        nom_sample_point: float,
843
        data_bitrate: int,
844
        data_sample_point: float,
845
    ) -> "BitTimingFd":
846
        """Create a :class:`~can.BitTimingFd` instance for a sample point.
847

848
        This function tries to find bit timings, which are close to the requested
849
        sample points. It does not take physical bus properties into account, so the
850
        calculated bus timings might not work properly for you.
851

852
        The :func:`oscillator_tolerance` function might be helpful to evaluate the
853
        bus timings.
854

855
        :param int f_clock:
856
            The CAN system clock frequency in Hz.
857
        :param int nom_bitrate:
858
            Nominal bitrate in bit/s.
859
        :param int nom_sample_point:
860
            The sample point value of the arbitration phase in percent.
861
        :param int data_bitrate:
862
            Data bitrate in bit/s.
863
        :param int data_sample_point:
864
            The sample point value of the data phase in percent.
865
        :raises ValueError:
866
            if the arguments are invalid.
867
        """
868
        if nom_sample_point < 50.0:
21✔
UNCOV
869
            raise ValueError(
×
870
                f"nom_sample_point (={nom_sample_point}) must not be below 50%."
871
            )
872

873
        if data_sample_point < 50.0:
21✔
UNCOV
874
            raise ValueError(
×
875
                f"data_sample_point (={data_sample_point}) must not be below 50%."
876
            )
877

878
        possible_solutions: list[BitTimingFd] = list(
21✔
879
            cls.iterate_from_sample_point(
880
                f_clock,
881
                nom_bitrate,
882
                nom_sample_point,
883
                data_bitrate,
884
                data_sample_point,
885
            )
886
        )
887

888
        if not possible_solutions:
21✔
889
            raise ValueError("No suitable bit timings found.")
21✔
890

891
        # prefer using the same prescaler for arbitration and data phase
892
        same_prescaler = list(
21✔
893
            filter(lambda x: x.nom_brp == x.data_brp, possible_solutions)
894
        )
895
        if same_prescaler:
21✔
896
            possible_solutions = same_prescaler
21✔
897

898
        # sort solutions
899
        for key, reverse in (
21✔
900
            # prefer low prescaler
901
            (lambda x: x.nom_brp + x.data_brp, False),
902
            # prefer same prescaler for arbitration and data
903
            (lambda x: abs(x.nom_brp - x.data_brp), False),
904
            # prefer low sample point deviation from requested values
905
            (
906
                lambda x: (
907
                    abs(x.nom_sample_point - nom_sample_point)
908
                    + abs(x.data_sample_point - data_sample_point)
909
                ),
910
                False,
911
            ),
912
        ):
913
            possible_solutions.sort(key=key, reverse=reverse)
21✔
914

915
        return possible_solutions[0]
21✔
916

917
    @property
21✔
918
    def f_clock(self) -> int:
21✔
919
        """The CAN system clock frequency in Hz."""
920
        return self._data["f_clock"]
21✔
921

922
    @property
21✔
923
    def nom_bitrate(self) -> int:
21✔
924
        """Nominal (arbitration phase) bitrate."""
925
        return round(self.f_clock / (self.nbt * self.nom_brp))
21✔
926

927
    @property
21✔
928
    def nom_brp(self) -> int:
21✔
929
        """Prescaler value for the arbitration phase."""
930
        return self._data["nom_brp"]
21✔
931

932
    @property
21✔
933
    def nom_tq(self) -> int:
21✔
934
        """Nominal time quantum in nanoseconds"""
935
        return round(self.nom_brp / self.f_clock * 1e9)
21✔
936

937
    @property
21✔
938
    def nbt(self) -> int:
21✔
939
        """Number of time quanta in a bit of the arbitration phase."""
940
        return 1 + self.nom_tseg1 + self.nom_tseg2
21✔
941

942
    @property
21✔
943
    def nom_tseg1(self) -> int:
21✔
944
        """Time segment 1 value of the arbitration phase.
945

946
        This is the sum of the propagation time segment and the phase buffer segment 1.
947
        """
948
        return self._data["nom_tseg1"]
21✔
949

950
    @property
21✔
951
    def nom_tseg2(self) -> int:
21✔
952
        """Time segment 2 value of the arbitration phase. Also known as phase buffer segment 2."""
953
        return self._data["nom_tseg2"]
21✔
954

955
    @property
21✔
956
    def nom_sjw(self) -> int:
21✔
957
        """Synchronization jump width of the arbitration phase.
958

959
        The phase buffer segments may be shortened or lengthened by this value.
960
        """
961
        return self._data["nom_sjw"]
21✔
962

963
    @property
21✔
964
    def nom_sample_point(self) -> float:
21✔
965
        """Sample point of the arbitration phase in percent."""
966
        return 100.0 * (1 + self.nom_tseg1) / (1 + self.nom_tseg1 + self.nom_tseg2)
21✔
967

968
    @property
21✔
969
    def data_bitrate(self) -> int:
21✔
970
        """Bitrate of the data phase in bit/s."""
971
        return round(self.f_clock / (self.dbt * self.data_brp))
21✔
972

973
    @property
21✔
974
    def data_brp(self) -> int:
21✔
975
        """Prescaler value for the data phase."""
976
        return self._data["data_brp"]
21✔
977

978
    @property
21✔
979
    def data_tq(self) -> int:
21✔
980
        """Data time quantum in nanoseconds"""
UNCOV
981
        return round(self.data_brp / self.f_clock * 1e9)
×
982

983
    @property
21✔
984
    def dbt(self) -> int:
21✔
985
        """Number of time quanta in a bit of the data phase."""
986
        return 1 + self.data_tseg1 + self.data_tseg2
21✔
987

988
    @property
21✔
989
    def data_tseg1(self) -> int:
21✔
990
        """TSEG1 value of the data phase.
991

992
        This is the sum of the propagation time segment and the phase buffer segment 1.
993
        """
994
        return self._data["data_tseg1"]
21✔
995

996
    @property
21✔
997
    def data_tseg2(self) -> int:
21✔
998
        """TSEG2 value of the data phase. Also known as phase buffer segment 2."""
999
        return self._data["data_tseg2"]
21✔
1000

1001
    @property
21✔
1002
    def data_sjw(self) -> int:
21✔
1003
        """Synchronization jump width of the data phase.
1004

1005
        The phase buffer segments may be shortened or lengthened by this value.
1006
        """
1007
        return self._data["data_sjw"]
21✔
1008

1009
    @property
21✔
1010
    def data_sample_point(self) -> float:
21✔
1011
        """Sample point of the data phase in percent."""
1012
        return 100.0 * (1 + self.data_tseg1) / (1 + self.data_tseg1 + self.data_tseg2)
21✔
1013

1014
    def oscillator_tolerance(
21✔
1015
        self,
1016
        node_loop_delay_ns: float = 250.0,
1017
        bus_length_m: float = 10.0,
1018
    ) -> float:
1019
        """Oscillator tolerance in percent according to ISO 11898-1.
1020

1021
        :param float node_loop_delay_ns:
1022
            Transceiver loop delay in nanoseconds.
1023
        :param float bus_length_m:
1024
            Bus length in meters.
1025
        """
1026
        delay_per_meter = 5
21✔
1027
        bidirectional_propagation_delay_ns = 2 * (
21✔
1028
            node_loop_delay_ns + delay_per_meter * bus_length_m
1029
        )
1030

1031
        prop_seg = math.ceil(bidirectional_propagation_delay_ns / self.nom_tq)
21✔
1032
        nom_phase_seg1 = self.nom_tseg1 - prop_seg
21✔
1033
        nom_phase_seg2 = self.nom_tseg2
21✔
1034

1035
        data_phase_seg2 = self.data_tseg2
21✔
1036

1037
        df_clock_list = [
21✔
1038
            _oscillator_tolerance_condition_1(nom_sjw=self.nom_sjw, nbt=self.nbt),
1039
            _oscillator_tolerance_condition_2(
1040
                nbt=self.nbt,
1041
                nom_phase_seg1=nom_phase_seg1,
1042
                nom_phase_seg2=nom_phase_seg2,
1043
            ),
1044
            _oscillator_tolerance_condition_3(data_sjw=self.data_sjw, dbt=self.dbt),
1045
            _oscillator_tolerance_condition_4(
1046
                nom_phase_seg1=nom_phase_seg1,
1047
                nom_phase_seg2=nom_phase_seg2,
1048
                data_phase_seg2=data_phase_seg2,
1049
                nbt=self.nbt,
1050
                dbt=self.dbt,
1051
                data_brp=self.data_brp,
1052
                nom_brp=self.nom_brp,
1053
            ),
1054
            _oscillator_tolerance_condition_5(
1055
                data_sjw=self.data_sjw,
1056
                data_brp=self.data_brp,
1057
                nom_brp=self.nom_brp,
1058
                data_phase_seg2=data_phase_seg2,
1059
                nom_phase_seg2=nom_phase_seg2,
1060
                nbt=self.nbt,
1061
                dbt=self.dbt,
1062
            ),
1063
        ]
1064
        return max(0.0, min(df_clock_list) * 100)
21✔
1065

1066
    def recreate_with_f_clock(self, f_clock: int) -> "BitTimingFd":
21✔
1067
        """Return a new :class:`~can.BitTimingFd` instance with the given *f_clock* but the same
1068
        bit rates and sample points.
1069

1070
        :param int f_clock:
1071
            The CAN system clock frequency in Hz.
1072
        :raises ValueError:
1073
            if no suitable bit timings were found.
1074
        """
1075
        # try the most simple solution first: another bitrate prescaler
1076
        try:
21✔
1077
            return BitTimingFd.from_bitrate_and_segments(
21✔
1078
                f_clock=f_clock,
1079
                nom_bitrate=self.nom_bitrate,
1080
                nom_tseg1=self.nom_tseg1,
1081
                nom_tseg2=self.nom_tseg2,
1082
                nom_sjw=self.nom_sjw,
1083
                data_bitrate=self.data_bitrate,
1084
                data_tseg1=self.data_tseg1,
1085
                data_tseg2=self.data_tseg2,
1086
                data_sjw=self.data_sjw,
1087
                strict=True,
1088
            )
1089
        except ValueError:
21✔
1090
            pass
21✔
1091

1092
        # create a new timing instance with the same sample points
1093
        bt = BitTimingFd.from_sample_point(
21✔
1094
            f_clock=f_clock,
1095
            nom_bitrate=self.nom_bitrate,
1096
            nom_sample_point=self.nom_sample_point,
1097
            data_bitrate=self.data_bitrate,
1098
            data_sample_point=self.data_sample_point,
1099
        )
1100
        if (
21✔
1101
            abs(bt.nom_sample_point - self.nom_sample_point) > 1.0
1102
            or abs(bt.data_sample_point - self.data_sample_point) > 1.0
1103
        ):
UNCOV
1104
            raise ValueError(
×
1105
                "f_clock change failed because of sample point discrepancy."
1106
            )
1107
        # adapt synchronization jump width, so it has the same size relative to bit time as self
1108
        nom_sjw = round(self.nom_sjw / self.nbt * bt.nbt)
21✔
1109
        nom_sjw = max(1, min(bt.nom_tseg2, nom_sjw))
21✔
1110
        bt._data["nom_sjw"] = nom_sjw  # pylint: disable=protected-access
21✔
1111
        data_sjw = round(self.data_sjw / self.dbt * bt.dbt)
21✔
1112
        data_sjw = max(1, min(bt.data_tseg2, data_sjw))
21✔
1113
        bt._data["data_sjw"] = data_sjw  # pylint: disable=protected-access
21✔
1114
        bt._validate()  # pylint: disable=protected-access
21✔
1115
        return bt
21✔
1116

1117
    def __str__(self) -> str:
21✔
1118
        segments = [
21✔
1119
            f"NBR: {self.nom_bitrate:_} bit/s",
1120
            f"NSP: {self.nom_sample_point:.2f}%",
1121
            f"NBRP: {self.nom_brp}",
1122
            f"NTSEG1: {self.nom_tseg1}",
1123
            f"NTSEG2: {self.nom_tseg2}",
1124
            f"NSJW: {self.nom_sjw}",
1125
            f"DBR: {self.data_bitrate:_} bit/s",
1126
            f"DSP: {self.data_sample_point:.2f}%",
1127
            f"DBRP: {self.data_brp}",
1128
            f"DTSEG1: {self.data_tseg1}",
1129
            f"DTSEG2: {self.data_tseg2}",
1130
            f"DSJW: {self.data_sjw}",
1131
            f"CLK: {self.f_clock / 1e6:.0f}MHz",
1132
        ]
1133
        return ", ".join(segments)
21✔
1134

1135
    def __repr__(self) -> str:
21✔
1136
        args = ", ".join(f"{key}={value}" for key, value in self.items())
21✔
1137
        return f"can.{self.__class__.__name__}({args})"
21✔
1138

1139
    def __getitem__(self, key: str) -> int:
21✔
1140
        return cast("int", self._data.__getitem__(key))
21✔
1141

1142
    def __len__(self) -> int:
21✔
1143
        return self._data.__len__()
21✔
1144

1145
    def __iter__(self) -> Iterator[str]:
21✔
1146
        return self._data.__iter__()
21✔
1147

1148
    def __eq__(self, other: object) -> bool:
21✔
1149
        if not isinstance(other, BitTimingFd):
21✔
1150
            return False
21✔
1151

1152
        return self._data == other._data
21✔
1153

1154
    def __hash__(self) -> int:
21✔
1155
        return tuple(self._data.values()).__hash__()
21✔
1156

1157

1158
def _oscillator_tolerance_condition_1(nom_sjw: int, nbt: int) -> float:
21✔
1159
    """Arbitration phase - resynchronization"""
1160
    return nom_sjw / (2 * 10 * nbt)
21✔
1161

1162

1163
def _oscillator_tolerance_condition_2(
21✔
1164
    nbt: int, nom_phase_seg1: int, nom_phase_seg2: int
1165
) -> float:
1166
    """Arbitration phase - sampling of bit after error flag"""
1167
    return min(nom_phase_seg1, nom_phase_seg2) / (2 * (13 * nbt - nom_phase_seg2))
21✔
1168

1169

1170
def _oscillator_tolerance_condition_3(data_sjw: int, dbt: int) -> float:
21✔
1171
    """Data phase - resynchronization"""
1172
    return data_sjw / (2 * 10 * dbt)
21✔
1173

1174

1175
def _oscillator_tolerance_condition_4(
21✔
1176
    nom_phase_seg1: int,
1177
    nom_phase_seg2: int,
1178
    data_phase_seg2: int,
1179
    nbt: int,
1180
    dbt: int,
1181
    data_brp: int,
1182
    nom_brp: int,
1183
) -> float:
1184
    """Data phase - sampling of bit after error flag"""
1185
    return min(nom_phase_seg1, nom_phase_seg2) / (
21✔
1186
        2 * ((6 * dbt - data_phase_seg2) * data_brp / nom_brp + 7 * nbt)
1187
    )
1188

1189

1190
def _oscillator_tolerance_condition_5(
21✔
1191
    data_sjw: int,
1192
    data_brp: int,
1193
    nom_brp: int,
1194
    nom_phase_seg2: int,
1195
    data_phase_seg2: int,
1196
    nbt: int,
1197
    dbt: int,
1198
) -> float:
1199
    """Data phase - bit rate switch"""
1200
    max_correctable_phase_shift = data_sjw - max(0.0, nom_brp / data_brp - 1)
21✔
1201
    time_between_resync = 2 * (
21✔
1202
        (2 * nbt - nom_phase_seg2) * nom_brp / data_brp + data_phase_seg2 + 4 * dbt
1203
    )
1204
    return max_correctable_phase_shift / time_between_resync
21✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc