Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add the possibility to return all the possible solutions using the from_sample_point static methods #1671

Merged
114 changes: 89 additions & 25 deletions can/bit_timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,10 @@ def from_registers(
)

@classmethod
def from_sample_point(
def iterate_from_sample_point(
cls, f_clock: int, bitrate: int, sample_point: float = 69.0
) -> "BitTiming":
"""Create a :class:`~can.BitTiming` instance for a sample point.

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

The :func:`oscillator_tolerance` function might be helpful to evaluate the
bus timings.
) -> Iterator["BitTiming"]:
"""Create a :class:`~can.BitTiming` iterator with all the solutions for a sample point.

:param int f_clock:
The CAN system clock frequency in Hz.
Expand All @@ -238,7 +231,6 @@ def from_sample_point(
if sample_point < 50.0:
raise ValueError(f"sample_point (={sample_point}) must not be below 50%.")

possible_solutions: List[BitTiming] = []
for brp in range(1, 65):
nbt = round(int(f_clock / (bitrate * brp)))
if nbt < 8:
Expand All @@ -264,10 +256,40 @@ def from_sample_point(
sjw=sjw,
strict=True,
)
possible_solutions.append(bt)
yield bt
except ValueError:
continue

@classmethod
def from_sample_point(
cls, f_clock: int, bitrate: int, sample_point: float = 69.0
) -> "BitTiming":
"""Create a :class:`~can.BitTiming` instance for a sample point.

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

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

:param int f_clock:
The CAN system clock frequency in Hz.
:param int bitrate:
Bitrate in bit/s.
:param int sample_point:
The sample point value in percent.
:raises ValueError:
if the arguments are invalid.
"""

if sample_point < 50.0:
raise ValueError(f"sample_point (={sample_point}) must not be below 50%.")

possible_solutions: List[BitTiming] = list(
cls.iterate_from_sample_point(f_clock, bitrate, sample_point)
)

if not possible_solutions:
raise ValueError("No suitable bit timings found.")

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

@classmethod
def from_sample_point(
def iterate_from_sample_point(
cls,
f_clock: int,
nom_bitrate: int,
nom_sample_point: float,
data_bitrate: int,
data_sample_point: float,
) -> "BitTimingFd":
"""Create a :class:`~can.BitTimingFd` instance for a given nominal/data sample point pair.

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

The :func:`oscillator_tolerance` function might be helpful to evaluate the
bus timings.
) -> Iterator["BitTimingFd"]:
"""Create an :class:`~can.BitTimingFd` iterator with all the solutions for a sample point.

:param int f_clock:
The CAN system clock frequency in Hz.
Expand All @@ -769,8 +784,6 @@ def from_sample_point(
f"data_sample_point (={data_sample_point}) must not be below 50%."
)

possible_solutions: List[BitTimingFd] = []

sync_seg = 1

for nom_brp in range(1, 257):
Expand Down Expand Up @@ -818,10 +831,61 @@ def from_sample_point(
data_sjw=data_sjw,
strict=True,
)
possible_solutions.append(bt)
yield bt
except ValueError:
continue

@classmethod
def from_sample_point(
cls,
f_clock: int,
nom_bitrate: int,
nom_sample_point: float,
data_bitrate: int,
data_sample_point: float,
) -> "BitTimingFd":
"""Create a :class:`~can.BitTimingFd` instance for a sample point.

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

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

:param int f_clock:
The CAN system clock frequency in Hz.
:param int nom_bitrate:
Nominal bitrate in bit/s.
:param int nom_sample_point:
The sample point value of the arbitration phase in percent.
:param int data_bitrate:
Data bitrate in bit/s.
:param int data_sample_point:
The sample point value of the data phase in percent.
:raises ValueError:
if the arguments are invalid.
"""
if nom_sample_point < 50.0:
raise ValueError(
f"nom_sample_point (={nom_sample_point}) must not be below 50%."
)

if data_sample_point < 50.0:
raise ValueError(
f"data_sample_point (={data_sample_point}) must not be below 50%."
)

possible_solutions: List[BitTimingFd] = list(
cls.iterate_from_sample_point(
f_clock,
nom_bitrate,
nom_sample_point,
data_bitrate,
data_sample_point,
)
)

if not possible_solutions:
raise ValueError("No suitable bit timings found.")

Expand Down
26 changes: 26 additions & 0 deletions test/test_bit_timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,32 @@ def test_from_sample_point():
)


def test_iterate_from_sample_point():
for sp in range(50, 100):
solutions = list(
can.BitTiming.iterate_from_sample_point(
f_clock=16_000_000,
bitrate=500_000,
sample_point=sp,
)
)
assert len(solutions) >= 2

for nsp in range(50, 100):
for dsp in range(50, 100):
solutions = list(
can.BitTimingFd.iterate_from_sample_point(
f_clock=80_000_000,
nom_bitrate=500_000,
nom_sample_point=nsp,
data_bitrate=2_000_000,
data_sample_point=dsp,
)
)

assert len(solutions) >= 2


def test_equality():
t1 = can.BitTiming.from_registers(f_clock=8_000_000, btr0=0x00, btr1=0x14)
t2 = can.BitTiming(f_clock=8_000_000, brp=1, tseg1=5, tseg2=2, sjw=1, nof_samples=1)
Expand Down