@@ -30,29 +30,22 @@ static const float INV_TWO_PI_F = 0.1591549431f;
3030// Internal Math Engines
3131// -----------------------------------------------------------------------------
3232
33- // High-Precision Sine (5th-degree Minimax Polynomial)
34- // This is the core engine for both sin and cos.
3533static float fast_sin_internal (float theta ) {
36- // Range reduction to [-PI, PI]
3734 float quot = theta * INV_TWO_PI_F ;
3835 float x = theta - (float )((int )(quot + (quot > 0 ? 0.5f : -0.5f ))) * TWO_PI_F ;
3936
40- // Symmetry reduction to [-PI/2, PI/2]
4137 if (x > HALF_PI_F ) x = PI_F - x ;
4238 else if (x < - HALF_PI_F ) x = - PI_F - x ;
4339
44- // 5th-degree Minimax Polynomial
4540 float x2 = x * x ;
4641 return x * (1.0f + x2 * (-0.1666665f + x2 * 0.0083322f ));
4742}
4843
49- // Atan2 using the 0.273 parabolic approximation (valid for |z| <= 1)
5044static float fast_atan2_internal (float y , float x ) {
5145 float ay = fabsf (y ) + 1e-10f ;
5246 float ax = fabsf (x );
5347 float z , angle ;
5448
55- // Range reduction to ensure we stay on the stable part of the approximation
5649 if (ax >= ay ) {
5750 z = y / ax ;
5851 angle = (0.7853982f + 0.273f * (1.0f - fabsf (z ))) * z ;
@@ -71,21 +64,18 @@ static float fast_atan2_internal(float y, float x) {
7164// MicroPython Functions
7265// -----------------------------------------------------------------------------
7366
74- // pybricks.experimental.sin(radians)
7567static mp_obj_t experimental_sin (mp_obj_t theta_in ) {
7668 float theta = mp_obj_get_float (theta_in );
7769 return mp_obj_new_float_from_f (fast_sin_internal (theta ));
7870}
7971static MP_DEFINE_CONST_FUN_OBJ_1 (experimental_sin_obj , experimental_sin ) ;
8072
81- // pybricks.experimental.cos(radians)
8273static mp_obj_t experimental_cos (mp_obj_t theta_in ) {
8374 float theta = mp_obj_get_float (theta_in );
8475 return mp_obj_new_float_from_f (fast_sin_internal (theta + HALF_PI_F ));
8576}
8677static MP_DEFINE_CONST_FUN_OBJ_1 (experimental_cos_obj , experimental_cos ) ;
8778
88- // pybricks.experimental.atan2(y, x)
8979static mp_obj_t experimental_atan2 (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
9080 PB_PARSE_ARGS_FUNCTION (n_args , pos_args , kw_args ,
9181 PB_ARG_REQUIRED (y ),
@@ -98,15 +88,50 @@ static mp_obj_t experimental_atan2(size_t n_args, const mp_obj_t *pos_args, mp_m
9888}
9989static MP_DEFINE_CONST_FUN_OBJ_KW (experimental_atan2_obj , 2 , experimental_atan2 ) ;
10090
91+ // -----------------------------------------------------------------------------
92+ // New: Detailed Internal Benchmark
93+ // -----------------------------------------------------------------------------
94+
95+ // Runs sin, cos, and atan2 inside a C loop to measure pure CPU performance
96+ // Returns a tuple: (total_time_ms, avg_ns_per_triple_op)
97+ static mp_obj_t experimental_benchmark_internal (mp_obj_t n_in ) {
98+ int32_t n = mp_obj_get_int (n_in );
99+ volatile float result = 0.0f ;
100+
101+ uint32_t start = mp_hal_ticks_ms ();
102+
103+ for (int32_t i = 0 ; i < n ; i ++ ) {
104+ // We use the internal engines directly to bypass ANY MicroPython overhead
105+ result += fast_sin_internal (1.23f );
106+ result += fast_sin_internal (1.23f + HALF_PI_F ); // Cos
107+ result += fast_atan2_internal (1.23f , 1.23f );
108+ }
109+
110+ uint32_t end = mp_hal_ticks_ms ();
111+ uint32_t total_ms = end - start ;
112+
113+ // Calculate nanoseconds per loop (1ms = 1,000,000ns)
114+ // Avoid division by zero
115+ float ns_per_op = (n > 0 ) ? ((float )total_ms * 1000000.0f ) / n : 0 ;
116+
117+ mp_obj_t tuple [2 ];
118+ tuple [0 ] = mp_obj_new_int (total_ms );
119+ tuple [1 ] = mp_obj_new_float (ns_per_op );
120+
121+ return mp_obj_new_tuple (2 , tuple );
122+ }
123+ static MP_DEFINE_CONST_FUN_OBJ_1 (experimental_benchmark_internal_obj , experimental_benchmark_internal ) ;
124+
101125// -----------------------------------------------------------------------------
102126// Module Registry
103127// -----------------------------------------------------------------------------
104128
105129static const mp_rom_map_elem_t experimental_globals_table [] = {
106- { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_experimental ) },
107- { MP_ROM_QSTR (MP_QSTR_sin ), MP_ROM_PTR (& experimental_sin_obj ) },
108- { MP_ROM_QSTR (MP_QSTR_cos ), MP_ROM_PTR (& experimental_cos_obj ) },
109- { MP_ROM_QSTR (MP_QSTR_atan2 ), MP_ROM_PTR (& experimental_atan2_obj ) },
130+ { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_experimental ) },
131+ { MP_ROM_QSTR (MP_QSTR_sin ), MP_ROM_PTR (& experimental_sin_obj ) },
132+ { MP_ROM_QSTR (MP_QSTR_cos ), MP_ROM_PTR (& experimental_cos_obj ) },
133+ { MP_ROM_QSTR (MP_QSTR_atan2 ), MP_ROM_PTR (& experimental_atan2_obj ) },
134+ { MP_ROM_QSTR (MP_QSTR_benchmark_internal ), MP_ROM_PTR (& experimental_benchmark_internal_obj ) },
110135};
111136static MP_DEFINE_CONST_DICT (pb_module_experimental_globals , experimental_globals_table ) ;
112137
0 commit comments