Skip to content

Commit b2689ae

Browse files
authored
Add BitTiming.iterate_from_sample_point static methods (#1671)
* add the possibility to return all the possible solutions using the from_sample_point static methods * Format code with black * add iterators for timings from sample point * update docstrings * make ruff happy * add a small test for iterate_from_sample_point * Format code with black --------- Co-authored-by: danielhrisca <[email protected]>
1 parent b0ba12f commit b2689ae

File tree

2 files changed

+115
-25
lines changed

2 files changed

+115
-25
lines changed

can/bit_timing.py

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,10 @@ def from_registers(
213213
)
214214

215215
@classmethod
216-
def from_sample_point(
216+
def iterate_from_sample_point(
217217
cls, f_clock: int, bitrate: int, sample_point: float = 69.0
218-
) -> "BitTiming":
219-
"""Create a :class:`~can.BitTiming` instance for a sample point.
220-
221-
This function tries to find bit timings, which are close to the requested
222-
sample point. It does not take physical bus properties into account, so the
223-
calculated bus timings might not work properly for you.
224-
225-
The :func:`oscillator_tolerance` function might be helpful to evaluate the
226-
bus timings.
218+
) -> Iterator["BitTiming"]:
219+
"""Create a :class:`~can.BitTiming` iterator with all the solutions for a sample point.
227220
228221
:param int f_clock:
229222
The CAN system clock frequency in Hz.
@@ -238,7 +231,6 @@ def from_sample_point(
238231
if sample_point < 50.0:
239232
raise ValueError(f"sample_point (={sample_point}) must not be below 50%.")
240233

241-
possible_solutions: List[BitTiming] = []
242234
for brp in range(1, 65):
243235
nbt = round(int(f_clock / (bitrate * brp)))
244236
if nbt < 8:
@@ -264,10 +256,40 @@ def from_sample_point(
264256
sjw=sjw,
265257
strict=True,
266258
)
267-
possible_solutions.append(bt)
259+
yield bt
268260
except ValueError:
269261
continue
270262

263+
@classmethod
264+
def from_sample_point(
265+
cls, f_clock: int, bitrate: int, sample_point: float = 69.0
266+
) -> "BitTiming":
267+
"""Create a :class:`~can.BitTiming` instance for a sample point.
268+
269+
This function tries to find bit timings, which are close to the requested
270+
sample point. It does not take physical bus properties into account, so the
271+
calculated bus timings might not work properly for you.
272+
273+
The :func:`oscillator_tolerance` function might be helpful to evaluate the
274+
bus timings.
275+
276+
:param int f_clock:
277+
The CAN system clock frequency in Hz.
278+
:param int bitrate:
279+
Bitrate in bit/s.
280+
:param int sample_point:
281+
The sample point value in percent.
282+
:raises ValueError:
283+
if the arguments are invalid.
284+
"""
285+
286+
if sample_point < 50.0:
287+
raise ValueError(f"sample_point (={sample_point}) must not be below 50%.")
288+
289+
possible_solutions: List[BitTiming] = list(
290+
cls.iterate_from_sample_point(f_clock, bitrate, sample_point)
291+
)
292+
271293
if not possible_solutions:
272294
raise ValueError("No suitable bit timings found.")
273295

@@ -729,22 +751,15 @@ def from_bitrate_and_segments( # pylint: disable=too-many-arguments
729751
return bt
730752

731753
@classmethod
732-
def from_sample_point(
754+
def iterate_from_sample_point(
733755
cls,
734756
f_clock: int,
735757
nom_bitrate: int,
736758
nom_sample_point: float,
737759
data_bitrate: int,
738760
data_sample_point: float,
739-
) -> "BitTimingFd":
740-
"""Create a :class:`~can.BitTimingFd` instance for a given nominal/data sample point pair.
741-
742-
This function tries to find bit timings, which are close to the requested
743-
sample points. It does not take physical bus properties into account, so the
744-
calculated bus timings might not work properly for you.
745-
746-
The :func:`oscillator_tolerance` function might be helpful to evaluate the
747-
bus timings.
761+
) -> Iterator["BitTimingFd"]:
762+
"""Create an :class:`~can.BitTimingFd` iterator with all the solutions for a sample point.
748763
749764
:param int f_clock:
750765
The CAN system clock frequency in Hz.
@@ -769,8 +784,6 @@ def from_sample_point(
769784
f"data_sample_point (={data_sample_point}) must not be below 50%."
770785
)
771786

772-
possible_solutions: List[BitTimingFd] = []
773-
774787
sync_seg = 1
775788

776789
for nom_brp in range(1, 257):
@@ -818,10 +831,61 @@ def from_sample_point(
818831
data_sjw=data_sjw,
819832
strict=True,
820833
)
821-
possible_solutions.append(bt)
834+
yield bt
822835
except ValueError:
823836
continue
824837

838+
@classmethod
839+
def from_sample_point(
840+
cls,
841+
f_clock: int,
842+
nom_bitrate: int,
843+
nom_sample_point: float,
844+
data_bitrate: int,
845+
data_sample_point: float,
846+
) -> "BitTimingFd":
847+
"""Create a :class:`~can.BitTimingFd` instance for a sample point.
848+
849+
This function tries to find bit timings, which are close to the requested
850+
sample points. It does not take physical bus properties into account, so the
851+
calculated bus timings might not work properly for you.
852+
853+
The :func:`oscillator_tolerance` function might be helpful to evaluate the
854+
bus timings.
855+
856+
:param int f_clock:
857+
The CAN system clock frequency in Hz.
858+
:param int nom_bitrate:
859+
Nominal bitrate in bit/s.
860+
:param int nom_sample_point:
861+
The sample point value of the arbitration phase in percent.
862+
:param int data_bitrate:
863+
Data bitrate in bit/s.
864+
:param int data_sample_point:
865+
The sample point value of the data phase in percent.
866+
:raises ValueError:
867+
if the arguments are invalid.
868+
"""
869+
if nom_sample_point < 50.0:
870+
raise ValueError(
871+
f"nom_sample_point (={nom_sample_point}) must not be below 50%."
872+
)
873+
874+
if data_sample_point < 50.0:
875+
raise ValueError(
876+
f"data_sample_point (={data_sample_point}) must not be below 50%."
877+
)
878+
879+
possible_solutions: List[BitTimingFd] = list(
880+
cls.iterate_from_sample_point(
881+
f_clock,
882+
nom_bitrate,
883+
nom_sample_point,
884+
data_bitrate,
885+
data_sample_point,
886+
)
887+
)
888+
825889
if not possible_solutions:
826890
raise ValueError("No suitable bit timings found.")
827891

test/test_bit_timing.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,32 @@ def test_from_sample_point():
286286
)
287287

288288

289+
def test_iterate_from_sample_point():
290+
for sp in range(50, 100):
291+
solutions = list(
292+
can.BitTiming.iterate_from_sample_point(
293+
f_clock=16_000_000,
294+
bitrate=500_000,
295+
sample_point=sp,
296+
)
297+
)
298+
assert len(solutions) >= 2
299+
300+
for nsp in range(50, 100):
301+
for dsp in range(50, 100):
302+
solutions = list(
303+
can.BitTimingFd.iterate_from_sample_point(
304+
f_clock=80_000_000,
305+
nom_bitrate=500_000,
306+
nom_sample_point=nsp,
307+
data_bitrate=2_000_000,
308+
data_sample_point=dsp,
309+
)
310+
)
311+
312+
assert len(solutions) >= 2
313+
314+
289315
def test_equality():
290316
t1 = can.BitTiming.from_registers(f_clock=8_000_000, btr0=0x00, btr1=0x14)
291317
t2 = can.BitTiming(f_clock=8_000_000, brp=1, tseg1=5, tseg2=2, sjw=1, nof_samples=1)

0 commit comments

Comments
 (0)