@@ -331,15 +331,86 @@ pub async fn pkt_modify_hook(
331331 SENSOR_MESSAGE_BATCH => {
332332 if let Ok ( mut msg) = SensorBatch :: parse_from_bytes ( data) {
333333 if cfg. video_in_motion {
334+ // === DRIVING STATUS: must be UNRESTRICTED (0) ===
335+ // This is the primary flag AA checks. Value is a bitmask:
336+ // 0 = unrestricted, 1 = no video, 2 = no keyboard, etc.
337+ if !msg. driving_status_data . is_empty ( ) {
338+ msg. driving_status_data [ 0 ] . set_status ( 0 ) ;
339+ }
340+
341+ // === GEAR: force PARK ===
334342 if !msg. gear_data . is_empty ( ) {
335- // forcing gear
336343 msg. gear_data [ 0 ] . set_gear ( GEAR_PARK ) ;
337344 }
338- if !msg. driving_status_data . is_empty ( ) {
339- // forcing status to 0 value
340- msg. driving_status_data [ 0 ] . set_status ( 0 ) ;
345+
346+ // === PARKING BRAKE: engaged ===
347+ // Modern AA cross-checks parking brake with gear/speed.
348+ if !msg. parking_brake_data . is_empty ( ) {
349+ msg. parking_brake_data [ 0 ] . set_parking_brake ( true ) ;
350+ }
351+
352+ // === VEHICLE SPEED: zero ===
353+ // SpeedData.speed_e3 is speed in m/s * 1000. Zero = stopped.
354+ if !msg. speed_data . is_empty ( ) {
355+ msg. speed_data [ 0 ] . set_speed_e3 ( 0 ) ;
356+ // Also ensure cruise control is disengaged
357+ msg. speed_data [ 0 ] . set_cruise_engaged ( false ) ;
358+ }
359+
360+ // === GPS/LOCATION: zero speed, keep position ===
361+ // LocationData.speed_e3 is GPS-derived speed.
362+ // Modern AA compares this against SpeedData for consistency.
363+ if !msg. location_data . is_empty ( ) {
364+ msg. location_data [ 0 ] . set_speed_e3 ( 0 ) ;
365+ // Zero bearing = not turning
366+ msg. location_data [ 0 ] . set_bearing_e6 ( 0 ) ;
341367 }
342- // regenerating payload data
368+
369+ // === ACCELEROMETER: gravity only (stationary) ===
370+ // A parked car only feels gravity on Z axis (~9810 mm/s²).
371+ // Any X/Y acceleration implies movement/turning.
372+ if !msg. accelerometer_data . is_empty ( ) {
373+ msg. accelerometer_data [ 0 ] . set_acceleration_x_e3 ( 0 ) ;
374+ msg. accelerometer_data [ 0 ] . set_acceleration_y_e3 ( 0 ) ;
375+ msg. accelerometer_data [ 0 ] . set_acceleration_z_e3 ( 9810 ) ;
376+ }
377+
378+ // === GYROSCOPE: zero rotation ===
379+ // Any rotation speed implies the vehicle is turning.
380+ if !msg. gyroscope_data . is_empty ( ) {
381+ msg. gyroscope_data [ 0 ] . set_rotation_speed_x_e3 ( 0 ) ;
382+ msg. gyroscope_data [ 0 ] . set_rotation_speed_y_e3 ( 0 ) ;
383+ msg. gyroscope_data [ 0 ] . set_rotation_speed_z_e3 ( 0 ) ;
384+ }
385+
386+ // === DEAD RECKONING: zero wheel speed + steering ===
387+ // Wheel speed ticks and steering angle are used by Toyota
388+ // and other modern HUs as independent motion verification.
389+ if !msg. dead_reckoning_data . is_empty ( ) {
390+ msg. dead_reckoning_data [ 0 ] . set_steering_angle_e1 ( 0 ) ;
391+ msg. dead_reckoning_data [ 0 ] . wheel_speed_e3 . clear ( ) ;
392+ // Push four zero values for the four wheels
393+ msg. dead_reckoning_data [ 0 ] . wheel_speed_e3 . push ( 0 ) ;
394+ msg. dead_reckoning_data [ 0 ] . wheel_speed_e3 . push ( 0 ) ;
395+ msg. dead_reckoning_data [ 0 ] . wheel_speed_e3 . push ( 0 ) ;
396+ msg. dead_reckoning_data [ 0 ] . wheel_speed_e3 . push ( 0 ) ;
397+ }
398+
399+ // === COMPASS: freeze bearing ===
400+ // Changing compass bearing implies turning/moving.
401+ if !msg. compass_data . is_empty ( ) {
402+ msg. compass_data [ 0 ] . set_pitch_e6 ( 0 ) ;
403+ msg. compass_data [ 0 ] . set_roll_e6 ( 0 ) ;
404+ }
405+
406+ // === RPM: idle engine ===
407+ // High RPM with zero speed is suspicious on some HUs.
408+ // ~700 RPM idle is realistic for a parked car.
409+ if !msg. rpm_data . is_empty ( ) {
410+ msg. rpm_data [ 0 ] . set_rpm_e3 ( 700_000 ) ;
411+ }
412+
413+ // Regenerate payload with ALL spoofed fields
343414 pkt. payload = msg. write_to_bytes ( ) ?;
344415 pkt. payload . insert ( 0 , ( message_id >> 8 ) as u8 ) ;
345416 pkt. payload . insert ( 1 , ( message_id & 0xff ) as u8 ) ;
@@ -587,6 +658,46 @@ pub async fn pkt_modify_hook(
587658 }
588659 }
589660
661+ // video_in_motion: strip motion-related sensors from SDR capabilities
662+ // and downgrade location_characterization so AA cannot cross-validate
663+ if cfg. video_in_motion {
664+ if let Some ( svc) = msg
665+ . services
666+ . iter_mut ( )
667+ . find ( |svc| !svc. sensor_source_service . sensors . is_empty ( ) )
668+ {
669+ // Remove sensor types that reveal vehicle motion.
670+ // Keep DRIVING_STATUS, GEAR, PARKING_BRAKE, LOCATION (we spoof those)
671+ // but remove the ones that are harder to spoof consistently per-HU.
672+ let sensors_to_strip = [
673+ SENSOR_ACCELEROMETER_DATA ,
674+ SENSOR_GYROSCOPE_DATA ,
675+ SENSOR_DEAD_RECKONING_DATA ,
676+ SENSOR_SPEED ,
677+ ] ;
678+ svc. sensor_source_service
679+ . as_mut ( )
680+ . unwrap ( )
681+ . sensors
682+ . retain ( |s| !sensors_to_strip. contains ( & s. sensor_type ( ) ) ) ;
683+
684+ // Reset location_characterization to RAW_GPS_ONLY (256).
685+ // This tells AA the HU does NOT fuse wheel speed, gyroscope,
686+ // accelerometer, or dead reckoning into position fixes, so AA
687+ // will not expect those signals for cross-validation.
688+ svc. sensor_source_service
689+ . as_mut ( )
690+ . unwrap ( )
691+ . set_location_characterization ( 256 ) ; // RAW_GPS_ONLY
692+
693+ info ! (
694+ "{} <yellow>{:?}</> video_in_motion: stripped motion sensors from SDR, location_characterization=RAW_GPS_ONLY" ,
695+ get_name( proxy_type) ,
696+ control. unwrap( ) ,
697+ ) ;
698+ }
699+ }
700+
590701 // enabling developer mode
591702 if cfg. developer_mode {
592703 msg. set_make ( DHU_MAKE . into ( ) ) ;
0 commit comments