@@ -20,7 +20,8 @@ use thiserror::Error;
2020
2121const CAN_SYNC_SEG : u32 = 1 ;
2222const CAN_CALC_MAX_ERROR : u32 = 50 ; // 0.50% in one-hundredth percent units
23- const SAMPLE_POINT_SCALE : f64 = 1000.0 ;
23+ const SAMPLE_POINT_SCALE_INT : u32 = 1000 ;
24+ const SAMPLE_POINT_SCALE : f64 = SAMPLE_POINT_SCALE_INT as f64 ;
2425const DEFAULT_SAMPLE_POINT_HIGH_BITRATE_THRESHOLD : u32 = 800_000 ;
2526const DEFAULT_SAMPLE_POINT_MEDIUM_BITRATE_THRESHOLD : u32 = 500_000 ;
2627const DEFAULT_SAMPLE_POINT_HIGH_BITRATE : u32 = 750 ;
@@ -85,13 +86,13 @@ pub struct BitrateConfig {
8586 pub timing : AdapterBitTiming ,
8687 /// Actual bitrate in bits per second.
8788 pub bitrate : u32 ,
88- /// Actual sample point in normalized form (`0.0.. 1.0`).
89+ /// Actual sample point in normalized form (`0.0 < sample_point < 1.0`).
8990 pub sample_point : f64 ,
9091 /// Optional CAN-FD data phase adapter-facing timing values.
9192 pub data_timing : Option < AdapterBitTiming > ,
9293 /// Optional CAN-FD data phase bitrate in bits per second.
9394 pub data_bitrate : Option < u32 > ,
94- /// Optional CAN-FD data phase sample point in normalized form (`0.0.. 1.0`).
95+ /// Optional CAN-FD data phase sample point in normalized form (`0.0 < sample_point < 1.0`).
9596 pub data_sample_point : Option < f64 > ,
9697}
9798
@@ -140,7 +141,7 @@ pub enum BitrateError {
140141 InvalidSjwMax ,
141142 #[ error( "bitrate must be greater than 0" ) ]
142143 InvalidBitrate ,
143- #[ error( "sample_point must be in range [ 0.0, 1.0)" ) ]
144+ #[ error( "sample_point must be in range ( 0.0, 1.0)" ) ]
144145 InvalidSamplePoint ,
145146 #[ error( "cannot mix bitrate-based and direct timing configuration" ) ]
146147 MixedConfiguration ,
@@ -395,7 +396,7 @@ impl BitrateBuilder {
395396 self
396397 }
397398
398- /// Target sample point in normalized form (`0.0.. 1.0`).
399+ /// Target sample point in normalized form (`0.0 < sample_point < 1.0`).
399400 ///
400401 /// If omitted, the default depends on bitrate:
401402 /// - `bitrate > 800_000`: `0.750`
@@ -442,7 +443,7 @@ impl BitrateBuilder {
442443 self
443444 }
444445
445- /// Optional CAN-FD data phase sample point in normalized form (`0.0.. 1.0`).
446+ /// Optional CAN-FD data phase sample point in normalized form (`0.0 < sample_point < 1.0`).
446447 ///
447448 /// If omitted, the default depends on `data_bitrate`:
448449 /// - `data_bitrate > 800_000`: `0.750`
@@ -805,11 +806,19 @@ fn calc_default_sample_point_nrz(bitrate: u32) -> u32 {
805806}
806807
807808fn sample_point_to_int ( sample_point : f64 ) -> Result < u32 , BitrateError > {
808- if !sample_point. is_finite ( ) || !( 0.0 ..1.0 ) . contains ( & sample_point) {
809+ if !sample_point. is_finite ( ) || !( 0.0 ..1.0 ) . contains ( & sample_point) || sample_point == 0.0 {
809810 return Err ( BitrateError :: InvalidSamplePoint ) ;
810811 }
811812
812- Ok ( ( sample_point * SAMPLE_POINT_SCALE ) as u32 )
813+ let mut sample_point_quantized = ( sample_point * SAMPLE_POINT_SCALE ) . round ( ) as u32 ;
814+ if sample_point_quantized == 0 {
815+ return Err ( BitrateError :: InvalidSamplePoint ) ;
816+ }
817+ if sample_point_quantized >= SAMPLE_POINT_SCALE_INT {
818+ sample_point_quantized = SAMPLE_POINT_SCALE_INT - 1 ;
819+ }
820+
821+ Ok ( sample_point_quantized)
813822}
814823
815824fn sample_point_to_float ( sample_point : u32 ) -> f64 {
@@ -827,17 +836,22 @@ fn update_sample_point(
827836 let mut best_tseg2 = 0 ;
828837
829838 for i in 0 ..=1 {
839+ let tsegall = tseg + CAN_SYNC_SEG ;
830840 let mut tseg2 =
831- tseg + CAN_SYNC_SEG - ( sample_point_reference * ( tseg + CAN_SYNC_SEG ) ) / 1000 - i ;
841+ tsegall . saturating_sub ( ( sample_point_reference * tsegall ) / SAMPLE_POINT_SCALE_INT + i ) ;
832842 tseg2 = tseg2. clamp ( btc. tseg2_min , btc. tseg2_max ) ;
833843
844+ if tseg2 > tseg {
845+ continue ;
846+ }
847+
834848 let mut tseg1 = tseg - tseg2;
835849 if tseg1 > btc. tseg1_max {
836850 tseg1 = btc. tseg1_max ;
837851 tseg2 = tseg - tseg1;
838852 }
839853
840- let sample_point = 1000 * ( tseg + CAN_SYNC_SEG - tseg2) / ( tseg + CAN_SYNC_SEG ) ;
854+ let sample_point = SAMPLE_POINT_SCALE_INT * ( tsegall - tseg2) / tsegall ;
841855 let sample_point_error = sample_point_reference. abs_diff ( sample_point) ;
842856
843857 if sample_point <= sample_point_reference && sample_point_error < best_sample_point_error {
@@ -1042,6 +1056,35 @@ mod tests {
10421056 assert_eq ! ( err, BitrateError :: InvalidSamplePoint ) ;
10431057 }
10441058
1059+ #[ test]
1060+ fn zero_sample_point_rejected ( ) {
1061+ let err = BitrateBuilder :: new :: < DummyTimingAdapter > ( )
1062+ . bitrate ( 500_000 )
1063+ . sample_point ( 0.0 )
1064+ . build ( )
1065+ . unwrap_err ( ) ;
1066+
1067+ assert_eq ! ( err, BitrateError :: InvalidSamplePoint ) ;
1068+ }
1069+
1070+ #[ test]
1071+ fn too_small_sample_point_rejected ( ) {
1072+ let err = BitrateBuilder :: new :: < DummyTimingAdapter > ( )
1073+ . bitrate ( 500_000 )
1074+ . sample_point ( 0.0001 )
1075+ . build ( )
1076+ . unwrap_err ( ) ;
1077+
1078+ assert_eq ! ( err, BitrateError :: InvalidSamplePoint ) ;
1079+ }
1080+
1081+ #[ test]
1082+ fn sample_point_conversion_rounds_and_clamps ( ) {
1083+ assert_eq ! ( sample_point_to_int( 0.8004 ) . unwrap( ) , 800 ) ;
1084+ assert_eq ! ( sample_point_to_int( 0.8005 ) . unwrap( ) , 801 ) ;
1085+ assert_eq ! ( sample_point_to_int( 0.9999 ) . unwrap( ) , 999 ) ;
1086+ }
1087+
10451088 #[ test]
10461089 fn invalid_timing_const_brp_inc_zero_rejected ( ) {
10471090 let mut btc = PEAK_NOMINAL_BTC ;
0 commit comments