1010#include "py/runtime.h"
1111#include <math.h>
1212
13- // Direct Hardware Access Headers
14- #include <pbio/tacho.h>
15- #include <pbio/port.h>
16-
17- // Architecture Detection & Optimization Macros
1813#if defined(__ARM_ARCH_7M__ ) || defined(__ARM_ARCH_7EM__ )
1914 #define IS_CORTEX_M 1
2015 #define ACCEL_RAM __attribute__((section(".data"), noinline))
@@ -30,9 +25,8 @@ static const float HALF_PI_F = 1.570796326794896f;
3025static const float INV_TWO_PI_F = 0.159154943091895f ;
3126
3227// -----------------------------------------------------------------------------
33- // Core Math Engines
28+ // Core Math Engine (Lasse Schlör Absolute Error Optimized)
3429// -----------------------------------------------------------------------------
35-
3630ACCEL_RAM static float fast_sin_internal (float theta ) {
3731 float x = theta * INV_TWO_PI_F ;
3832 x = theta - (float )((int )(x + (x > 0 ? 0.5f : -0.5f ))) * TWO_PI_F ;
@@ -41,68 +35,79 @@ ACCEL_RAM static float fast_sin_internal(float theta) {
4135 else if (x < - HALF_PI_F ) { x = - PI_F - x ; }
4236
4337 float x2 = x * x ;
44- float res ;
45-
4638 #if IS_CORTEX_M
47- res = -0.0001848814f ;
39+ float res = -0.0001848814f ;
4840 res = 0.0083119000f + (x2 * res );
4941 res = -0.1666555409f + (x2 * res );
50- res = 1.0f + (x2 * res ); // Note: 1.0f is faster for absolute error Minimax
42+ return x * ( 0.9999990609f + (x2 * res ));
5143 #else
52- res = 0.9999990609f + x2 * (-0.1666555409f + x2 * (0.0083119000f + x2 * -0.0001848814f ));
44+ return x * ( 0.9999990609f + x2 * (-0.1666555409f + x2 * (0.0083119000f + x2 * -0.0001848814f ) ));
5345 #endif
54-
55- return x * res ;
46+ }
47+
48+ // Helper to safely unpack hardware values regardless of Pybricks version
49+ static float get_float_from_obj (mp_obj_t obj ) {
50+ if (MP_OBJ_IS_SMALL_INT (obj )) {
51+ return (float )MP_OBJ_SMALL_INT_VALUE (obj );
52+ } else if (mp_obj_is_type (obj , & mp_type_float )) {
53+ return mp_obj_get_float (obj );
54+ } else {
55+ return (float )mp_obj_get_int (obj );
56+ }
5657}
5758
5859// -----------------------------------------------------------------------------
59- // Hardware Benchmarks
60+ // The "No-Tax" Odometry Benchmark
6061// -----------------------------------------------------------------------------
61-
62- static mp_obj_t experimental_odometry_benchmark ( mp_obj_t num_iters_in , mp_obj_t wheel_circ_in ) {
63- int num_iters = mp_obj_get_int (num_iters_in );
64- float wheel_circ = mp_obj_get_float ( wheel_circ_in );
65- float deg_to_mm = wheel_circ / 360.0f ;
66-
67- float robot_x = 0.0f , robot_y = 0.0f ;
68- int32_t last_left = 0 , last_right = 0 ;
69- float heading = 0 .0f ;
62+ static mp_obj_t experimental_odometry_benchmark ( size_t n_args , const mp_obj_t * args ) {
63+ // 1. Unpack Python Arguments
64+ int num_iters = mp_obj_get_int (args [ 0 ] );
65+ float wheel_circ = get_float_from_obj ( args [ 1 ] );
66+ mp_obj_t right_angle_func = args [ 2 ] ;
67+ mp_obj_t left_angle_func = args [ 3 ];
68+ mp_obj_t db_angle_func = args [ 4 ] ;
69+
70+ float deg_to_mm = wheel_circ / 720 .0f ;
7071
71- // FIX 1: Updated to pbio_tacho_get_angle
72- pbio_tacho_get_angle (pbio_tacho_get_tacho (PBIO_PORT_ID_A ), & last_left );
73- pbio_tacho_get_angle (pbio_tacho_get_tacho (PBIO_PORT_ID_D ), & last_right );
72+ // State variables
73+ float robot_x = 0.0f , robot_y = 0.0f ;
74+ float last_linear = 0.0f ;
75+ float last_heading = 0.0f ;
7476
7577 uint32_t start_time = mp_hal_ticks_ms ();
7678
79+ // 2. The Core High-Speed C Loop
7780 for (int i = 0 ; i < num_iters ; i ++ ) {
78- int32_t cur_left , cur_right ;
79- pbio_tacho_get_angle (pbio_tacho_get_tacho (PBIO_PORT_ID_A ), & cur_left );
80- pbio_tacho_get_angle (pbio_tacho_get_tacho (PBIO_PORT_ID_D ), & cur_right );
81-
82- float d_left = (float )(cur_left - last_left ) * deg_to_mm ;
83- float d_right = (float )(cur_right - last_right ) * deg_to_mm ;
84- float linear_delta = (d_left + d_right ) / 2.0f ;
85- float heading_delta = (d_right - d_left ) / 96.0f ; // Axle track 96mm
86-
87- if (fabsf (linear_delta ) > 0.0001f ) {
88- float avg_h = heading + (heading_delta / 2.0f );
89- robot_x += linear_delta * fast_sin_internal (avg_h + HALF_PI_F );
90- robot_y += linear_delta * fast_sin_internal (avg_h );
81+
82+ // Execute the exact same hardware getters as your Python script
83+ float right_angle = get_float_from_obj (mp_call_function_0 (right_angle_func ));
84+ float left_angle = get_float_from_obj (mp_call_function_0 (left_angle_func ));
85+ float robot_heading = get_float_from_obj (mp_call_function_0 (db_angle_func ));
86+
87+ // Exactly mirroring your Python math logic
88+ float current_linear = (right_angle + left_angle ) * deg_to_mm ;
89+ float linear = current_linear - last_linear ;
90+ float heading_difference = robot_heading - last_heading ;
91+
92+ last_linear = current_linear ;
93+ last_heading = robot_heading ;
94+
95+ if (fabsf (linear ) > 0.0f ) {
96+ float avg_heading_deg = last_heading - (heading_difference / 2.0f );
97+ float avg_heading_rad = avg_heading_deg * 0.0174532925f ; // DEG_TO_RAD
98+
99+ robot_x += linear * fast_sin_internal (avg_heading_rad + HALF_PI_F ); // cos
100+ robot_y += linear * fast_sin_internal (avg_heading_rad ); // sin
91101 }
92102
93- heading += heading_delta ;
94- last_left = cur_left ;
95- last_right = cur_right ;
96-
97- // FIX 2 & 3: Semicolon and proper event polling for Pybricks bricks
98- if ((i % 1000 ) == 0 ) {
99- mp_handle_pending (true);
100- }
103+ // Safety: Prevent watchdog timeouts during massive loops
104+ if ((i % 1000 ) == 0 ) { mp_handle_pending (true); }
101105 }
102106
103107 uint32_t duration_ms = mp_hal_ticks_ms () - start_time ;
104108 float duration = (float )duration_ms / 1000.0f ;
105109
110+ // 3. Return Results Tuple
106111 mp_obj_t tuple [5 ] = {
107112 mp_obj_new_float_from_f (duration ),
108113 mp_obj_new_int (num_iters ),
@@ -112,7 +117,8 @@ static mp_obj_t experimental_odometry_benchmark(mp_obj_t num_iters_in, mp_obj_t
112117 };
113118 return mp_obj_new_tuple (5 , tuple );
114119}
115- static MP_DEFINE_CONST_FUN_OBJ_2 (experimental_odometry_benchmark_obj , experimental_odometry_benchmark ) ;
120+ // Define function accepting 5 arguments
121+ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (experimental_odometry_benchmark_obj , 5 , 5 , experimental_odometry_benchmark ) ;
116122
117123// Module Globals
118124static const mp_rom_map_elem_t experimental_globals_table [] = {
0 commit comments