@@ -32,6 +32,7 @@ typedef struct {
3232 FusionAhrs ahrs ; // Fusion AHRS algorithm
3333 double t_last ;
3434 float cursor_x , cursor_y ; // Virtual cursor position (accumulated)
35+ float smoothed_vel_x , smoothed_vel_y ; // Smoothed cursor velocity
3536 int initialized ;
3637} FusionFilterState ;
3738
@@ -138,14 +139,14 @@ void process_sensor_data(UInputDevice* device, const SensorPacket* packet) {
138139 // Initialize Fusion AHRS
139140 FusionAhrsInitialise (& fusion_state .ahrs );
140141
141- // Set AHRS settings optimized for fast, accurate mouse control
142+ // Set AHRS settings optimized for tilt-based mouse control
142143 FusionAhrsSettings settings = {
143144 .convention = FusionConventionNwu , // North-West-Up coordinate system
144- .gain = 1.0f , // Higher gain for faster convergence
145+ .gain = 0.5f , // Moderate gain (balance speed vs stability)
145146 .gyroscopeRange = 2000.0f , // ±2000 degrees/s range
146- .accelerationRejection = 10 .0f , // Lower rejection for mouse movements
147+ .accelerationRejection = 20 .0f , // Moderate rejection (filter fast movements)
147148 .magneticRejection = 0.0f , // No magnetometer
148- .recoveryTriggerPeriod = 2 * 200 // 2 seconds at 200Hz (faster recovery )
149+ .recoveryTriggerPeriod = 5 * 50 // 5 seconds at 50Hz (don't recover too fast )
149150 };
150151 FusionAhrsSetSettings (& fusion_state .ahrs , & settings );
151152
@@ -158,48 +159,43 @@ void process_sensor_data(UInputDevice* device, const SensorPacket* packet) {
158159 // Update AHRS with sensor data (no magnetometer)
159160 FusionAhrsUpdateNoMagnetometer (& fusion_state .ahrs , gyroscope , accelerometer , dt );
160161
161- // Get current quaternion
162+ // Get current orientation as Euler angles (roll, pitch, yaw)
162163 FusionQuaternion quaternion = FusionAhrsGetQuaternion (& fusion_state .ahrs );
163-
164- // Get linear acceleration (with gravity removed by Fusion)
165- FusionVector linear_acceleration = FusionAhrsGetLinearAcceleration (& fusion_state .ahrs );
166-
167- // Transform linear acceleration from device frame to world frame using current orientation
168- // This makes movement independent of device rotation - move device left = cursor left
169- FusionMatrix rotation_matrix = FusionQuaternionToMatrix (quaternion );
170- FusionVector world_acceleration = FusionMatrixMultiplyVector (rotation_matrix , linear_acceleration );
171-
172- // Debug: log sensor fusion values
164+ FusionEuler euler = FusionQuaternionToEuler (quaternion );
165+
166+ // Use orientation angles directly for cursor control
167+ // Roll (tilting left/right) → X movement
168+ // Pitch (tilting forward/back) → Y movement
169+ // Yaw ignored (no magnetometer, drifts anyway)
170+ float roll = euler .angle .roll ; // degrees
171+ float pitch = euler .angle .pitch ; // degrees
172+
173+ // Apply dead zone to angles (filter small unintentional tilts)
174+ float dead_zone_deg = config .dead_zone ; // e.g., 3.0 degrees
175+ if (fabsf (roll ) < dead_zone_deg ) roll = 0.0f ;
176+ if (fabsf (pitch ) < dead_zone_deg ) pitch = 0.0f ;
177+
178+ // Map tilt angles to cursor velocity (degrees → pixels/second)
179+ // Larger tilt = faster cursor movement
180+ float raw_vel_x = roll * config .movement_sensitivity ; // Roll → horizontal velocity
181+ float raw_vel_y = pitch * config .movement_sensitivity ; // Pitch → vertical velocity
182+
183+ // Apply exponential smoothing to reduce jitter (alpha = 0.3 = 30% new, 70% old)
184+ // Lower alpha = smoother but more lag, higher alpha = more responsive but jittery
185+ float alpha = 0.3f ;
186+ fusion_state .smoothed_vel_x = alpha * raw_vel_x + (1.0f - alpha ) * fusion_state .smoothed_vel_x ;
187+ fusion_state .smoothed_vel_y = alpha * raw_vel_y + (1.0f - alpha ) * fusion_state .smoothed_vel_y ;
188+
189+ // Integrate smoothed velocity to cursor position
190+ fusion_state .cursor_x += fusion_state .smoothed_vel_x * dt ;
191+ fusion_state .cursor_y += fusion_state .smoothed_vel_y * dt ;
192+
193+ // Debug: log orientation and velocity
173194 static int debug_count = 0 ;
174- if (++ debug_count % 10 == 0 ) { // Every 10 frames (~200ms)
175- FusionEuler euler = FusionQuaternionToEuler (quaternion );
176- syslog (LOG_INFO , "FUSION: Roll:%.1f° Pitch:%.1f° Yaw:%.1f° | WorldAccel(%.3f, %.3f, %.3f) dt:%.4f" ,
195+ if (++ debug_count % 10 == 0 ) { // Every 10 frames (~200ms at 50Hz)
196+ syslog (LOG_INFO , "Angles: Roll:%.1f° Pitch:%.1f° Yaw:%.1f° | Vel(%.1f, %.1f) px/s | dt:%.4f" ,
177197 euler .angle .roll , euler .angle .pitch , euler .angle .yaw ,
178- world_acceleration .axis .x , world_acceleration .axis .y , world_acceleration .axis .z , dt );
179- }
180-
181- // Apply dead zone to filter small movements (in g units)
182- float dead_zone_g = config .dead_zone ; // e.g., 0.03 g
183- if (fabsf (world_acceleration .axis .x ) < dead_zone_g ) world_acceleration .axis .x = 0.0f ;
184- if (fabsf (world_acceleration .axis .y ) < dead_zone_g ) world_acceleration .axis .y = 0.0f ;
185-
186- // Map world-space acceleration to cursor velocity
187- // World X acceleration → horizontal cursor movement
188- // World Y acceleration → vertical cursor movement
189- // Z acceleration ignored (vertical in world frame)
190- float cursor_vel_x = world_acceleration .axis .x * config .movement_sensitivity ; // World X → Screen X
191- float cursor_vel_y = - world_acceleration .axis .y * config .movement_sensitivity ; // World Y → Screen Y (inverted)
192-
193- // Integrate velocity to position
194- fusion_state .cursor_x += cursor_vel_x * dt ;
195- fusion_state .cursor_y += cursor_vel_y * dt ;
196-
197- // Debug velocity and accumulation
198- if (debug_count % 10 == 0 ) {
199- syslog (LOG_INFO , "VEL: (%.2f, %.2f) px/s | cursor_accum: (%.2f, %.2f) | sens:%.1f deadzone:%.3f g" ,
200- cursor_vel_x , cursor_vel_y ,
201- fusion_state .cursor_x , fusion_state .cursor_y ,
202- config .movement_sensitivity , dead_zone_g );
198+ fusion_state .smoothed_vel_x , fusion_state .smoothed_vel_y , dt );
203199 }
204200
205201 // Extract integer deltas for mouse movement
@@ -223,8 +219,8 @@ void process_sensor_data(UInputDevice* device, const SensorPacket* packet) {
223219 // Log periodically (every 50 packets ~1 second)
224220 static int log_count = 0 ;
225221 if (++ log_count % 50 == 0 ) {
226- syslog (LOG_INFO , "FUSION Angular Vel: (%.2f , %.2f) | Cursor: (%.2f, %.2f) -> dx:%d dy:%d" ,
227- cursor_vel_x , cursor_vel_y ,
222+ syslog (LOG_INFO , "Movement: Vel (%.1f , %.1f) px/s | Accum (%.2f, %.2f) | Delta dx:%d dy:%d" ,
223+ fusion_state . smoothed_vel_x , fusion_state . smoothed_vel_y ,
228224 fusion_state .cursor_x , fusion_state .cursor_y ,
229225 dx , dy );
230226 }
0 commit comments