1+ // SPDX-License-Identifier: MIT
2+ #include "py/mpconfig.h"
3+
4+ #if PYBRICKS_PY_EXPERIMENTAL
5+ #if defined(__ARM_ARCH_7M__ ) || defined(__ARM_ARCH_7EM__ )
6+
7+ #include "py/mphal.h"
8+ #include "py/runtime.h"
9+ #include <math.h>
10+ #include "pybricks/experimental/odometry.h"
11+
12+ // Force the math functions into RAM for zero-wait-state execution
13+ #define ACCEL_RAM __attribute__((section(".data"), noinline))
14+
15+ // High-speed Minimax Polynomial for Sine/Cosine
16+ // This replaces the heavy math.h calls with raw FPU instructions
17+ ACCEL_RAM static float fast_sin_internal (float theta ) {
18+ float x = theta * 0.159154943f ;
19+ x = theta - (float )((int )(x + (x > 0.0f ? 0.5f : -0.5f ))) * 6.2831853f ;
20+
21+ if (x > 1.5707963f ) x = 3.1415926f - x ;
22+ else if (x < -1.5707963f ) x = -3.1415926f - x ;
23+
24+ float x2 = x * x ;
25+ return x * (0.99999906f + x2 * (-0.16665554f + x2 * (0.00831190f + x2 * -0.00018488f )));
26+ }
27+
28+ 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 ) {
29+
30+ // Pre-calculate kinematic constants
31+ float deg_to_mm = wheel_circ * 0.0027777778f ; // (circ / 360)
32+ float inv_axle_track = 1.0f / axle_track ;
33+
34+ // Robot state
35+ float rx = 0.0f ;
36+ float ry = 0.0f ;
37+ float rh = 0.0f ;
38+
39+ // Initial sensor grab
40+ int32_t last_r = mp_obj_get_int (mp_call_function_0 (right_angle_func ));
41+ int32_t last_l = mp_obj_get_int (mp_call_function_0 (left_angle_func ));
42+
43+ uint32_t start_time = mp_hal_ticks_ms ();
44+
45+ for (int i = 0 ; i < num_iters ; i ++ ) {
46+ // 1. Fetch encoder data (The primary Spike bottleneck)
47+ int32_t cur_r = mp_obj_get_int (mp_call_function_0 (right_angle_func ));
48+ int32_t cur_l = mp_obj_get_int (mp_call_function_0 (left_angle_func ));
49+
50+ // 2. Delta math
51+ float dR = (float )(cur_r - last_r ) * deg_to_mm ;
52+ float dL = (float )(cur_l - last_l ) * deg_to_mm ;
53+ float dD = (dR + dL ) * 0.5f ;
54+ float dH = (dR - dL ) * inv_axle_track ;
55+
56+ // 3. RK2 Midpoint Integration
57+ if (dD != 0.0f || dH != 0.0f ) {
58+ float avg_h = rh + (dH * 0.5f );
59+ rx += dD * fast_sin_internal (avg_h + 1.5707963f ); // cos
60+ ry += dD * fast_sin_internal (avg_h ); // sin
61+ rh += dH ;
62+ }
63+
64+ last_r = cur_r ;
65+ last_l = cur_l ;
66+
67+ // 4. Housekeeping (Polled, not delayed)
68+ // Checking every 1024 iterations (bitwise & is faster than % on M4)
69+ if ((i & 0x3FF ) == 0 ) {
70+ mp_handle_pending (true);
71+ }
72+ }
73+
74+ uint32_t dur = mp_hal_ticks_ms () - start_time ;
75+
76+ // Packaging results for Python
77+ mp_obj_t tuple [5 ] = {
78+ mp_obj_new_float_from_f ((float )dur * 0.001f ),
79+ mp_obj_new_int (num_iters ),
80+ mp_obj_new_float_from_f ((float )num_iters / ((float )dur * 0.001f )),
81+ mp_obj_new_float_from_f (rx ),
82+ mp_obj_new_float_from_f (ry )
83+ };
84+ return mp_obj_new_tuple (5 , tuple );
85+ }
86+
87+ #endif
88+ #endif
0 commit comments