1+ // SPDX-License-Identifier: MIT
2+ #include "py/mpconfig.h"
3+
4+ #if PYBRICKS_PY_EXPERIMENTAL
5+
6+ #include "py/mphal.h"
7+ #include "py/runtime.h"
8+ #include "pybricks/experimental/odometry.h"
9+
10+ // The exact Minimax Polynomial used on Spike Prime
11+ // Optimized for the [-PI/2, PI/2] range with 0.000001 precision
12+ static float arm9_fast_sin_internal (float theta ) {
13+ float x = theta * 0.159154943f ;
14+ x = theta - (float )((int )(x + (x > 0.0f ? 0.5f : -0.5f ))) * 6.2831853f ;
15+
16+ if (x > 1.5707963f ) x = 3.1415926f - x ;
17+ else if (x < -1.5707963f ) x = -3.1415926f - x ;
18+
19+ float x2 = x * x ;
20+ // The exact coefficients from the Spike Prime engine
21+ return x * (0.99999906f + x2 * (-0.16665554f + x2 * (0.00831190f + x2 * -0.00018488f )));
22+ }
23+
24+ mp_obj_t calculate_odometry (int num_iters , float wheel_circ , float axle_track , mp_obj_t right_angle_func , mp_obj_t left_angle_func ) {
25+
26+ float deg_to_mm = wheel_circ * 0.0027777778f ; // circ / 360
27+ float inv_axle_track = 1.0f / axle_track ;
28+
29+ float rx = 0.0f ;
30+ float ry = 0.0f ;
31+ float rh = 0.0f ;
32+
33+ int32_t last_r = mp_obj_get_int (mp_call_function_0 (right_angle_func ));
34+ int32_t last_l = mp_obj_get_int (mp_call_function_0 (left_angle_func ));
35+
36+ uint32_t start_time = mp_hal_ticks_ms ();
37+
38+ for (int i = 0 ; i < num_iters ; i ++ ) {
39+ int32_t cur_r = mp_obj_get_int (mp_call_function_0 (right_angle_func ));
40+ int32_t cur_l = mp_obj_get_int (mp_call_function_0 (left_angle_func ));
41+
42+ float dR = (float )(cur_r - last_r ) * deg_to_mm ;
43+ float dL = (float )(cur_l - last_l ) * deg_to_mm ;
44+
45+ float dD = (dR + dL ) * 0.5f ;
46+ float dH = (dR - dL ) * inv_axle_track ;
47+
48+ if (dD != 0.0f || dH != 0.0f ) {
49+ float avg_h = rh + (dH * 0.5f );
50+
51+ // Project X and Y using the Spike Polynomial
52+ rx += dD * arm9_fast_sin_internal (avg_h + 1.5707963f ); // Cosine approximation
53+ ry += dD * arm9_fast_sin_internal (avg_h ); // Sine approximation
54+
55+ rh += dH ;
56+ }
57+
58+ last_r = cur_r ;
59+ last_l = cur_l ;
60+
61+ // Yield to MicroPython core using bitwise scheduling
62+ // Faster than modulo on ARM9 as it avoids software division calls
63+ if ((i & 0x3FF ) == 0 ) {
64+ mp_handle_pending (true);
65+ }
66+ }
67+
68+ uint32_t dur = mp_hal_ticks_ms () - start_time ;
69+
70+ mp_obj_t tuple [5 ] = {
71+ mp_obj_new_float_from_f ((float )dur * 0.001f ),
72+ mp_obj_new_int (num_iters ),
73+ mp_obj_new_float_from_f ((float )num_iters / ((float )dur * 0.001f )),
74+ mp_obj_new_float_from_f (rx ),
75+ mp_obj_new_float_from_f (ry )
76+ };
77+ return mp_obj_new_tuple (5 , tuple );
78+ }
79+
80+ #endif // PYBRICKS_PY_EXPERIMENTAL
0 commit comments