88import math
99import os
1010import signal
11+ import time
1112
1213from pymavlink import quaternion
1314from pymavlink import mavutil
@@ -7334,6 +7335,80 @@ def MAV_CMD_NAV_LOITER_TURNS_zero_turn(self):
73347335
73357336 self .fly_home_land_and_disarm ()
73367337
7338+ class ValidateVFRHudClimbAgainstSimState (vehicle_test_suite .TestSuite .MessageHook ):
7339+ '''monitors VFR_HUD to make sure reported climbrate is in-line with SIM_STATE.vd'''
7340+ def __init__ (self , suite , max_allowed_divergence = 5 ):
7341+ super (AutoTestPlane .ValidateVFRHudClimbAgainstSimState , self ).__init__ (suite )
7342+ self .max_allowed_divergence = max_allowed_divergence # m/s
7343+ self .max_divergence = 0
7344+ self .vfr_hud = None
7345+ self .sim_state = None
7346+ self .last_print = 0
7347+ self .min_print_interval = 1 # seconds
7348+ self .instafail = True
7349+ self .failed = False
7350+
7351+ def progress_prefix (self ):
7352+ return "VVHCASS: "
7353+
7354+ def process (self , mav , m ):
7355+ if m .get_type () == 'VFR_HUD' :
7356+ self .vfr_hud = m
7357+ elif m .get_type () == 'SIM_STATE' :
7358+ self .sim_state = m
7359+ if self .vfr_hud is None :
7360+ return
7361+ if self .sim_state is None :
7362+ return
7363+
7364+ vfr_hud_climb = self .vfr_hud .climb
7365+ sim_state_climb = - self .sim_state .vd
7366+ divergence = abs (vfr_hud_climb - sim_state_climb )
7367+ if (time .time () - self .last_print > self .min_print_interval or
7368+ divergence > self .max_divergence ):
7369+ self .progress (f"climb delta is { divergence } " )
7370+ self .last_print = time .time ()
7371+ if divergence > self .max_divergence :
7372+ self .max_divergence = divergence
7373+ if divergence > self .max_allowed_divergence :
7374+ msg = f"VFR_HUD.climb diverged from SIM_STATE.vd by { divergence } m/s (max={ self .max_allowed_divergence } m/s"
7375+ if self .instafail :
7376+ raise NotAchievedException (msg )
7377+ else :
7378+ self .failed = True
7379+ self .progress (msg )
7380+
7381+ def hook_removed (self ):
7382+ if self .vfr_hud is None :
7383+ raise ValueError ("Did not receive VFR_HUD" )
7384+ if self .sim_state is None :
7385+ raise ValueError ("Did not receive SIM_STATE" )
7386+ msg = f"Maximum divergence was { self .max_divergence } m/s (max={ self .max_allowed_divergence } m/s)"
7387+ if self .failed :
7388+ raise NotAchievedException (msg )
7389+
7390+ self .progress (msg )
7391+
7392+ def SoaringClimbRate (self ):
7393+ '''test displayed climb rate when soaring'''
7394+ self .set_parameters ({
7395+ 'RC16_OPTION' : 88 , # soaring enable
7396+ 'SOAR_ENABLE' : 1 ,
7397+ })
7398+ self .set_rc (16 , 1000 ) # disable soaring
7399+ self .reboot_sitl ()
7400+ self .set_message_rate_hz ('SIM_STATE' , 10 )
7401+ self .install_message_hook_context (AutoTestPlane .ValidateVFRHudClimbAgainstSimState (self ))
7402+ self .takeoff (20 )
7403+ self .change_mode ('FBWB' )
7404+ self .set_rc (2 , 1000 ) # full climb
7405+ self .delay_sim_time (10 )
7406+ self .set_rc (16 , 2000 ) # enable soaring
7407+ self .delay_sim_time (10 )
7408+
7409+ self .set_rc (2 , 1500 )
7410+ self .fly_home_land_and_disarm ()
7411+
73377412 def tests (self ):
73387413 '''return list of all tests'''
73397414 ret = []
@@ -7351,6 +7426,7 @@ def tests1a(self):
73517426 self .ThrottleFailsafe ,
73527427 self .NeedEKFToArm ,
73537428 self .ThrottleFailsafeFence ,
7429+ self .SoaringClimbRate ,
73547430 self .TestFlaps ,
73557431 self .DO_CHANGE_SPEED ,
73567432 self .DO_REPOSITION ,
@@ -7509,6 +7585,7 @@ def disabled_tests(self):
75097585 "LandingDrift" : "Flapping test. See https://github.com/ArduPilot/ardupilot/issues/20054" ,
75107586 "InteractTest" : "requires user interaction" ,
75117587 "ClimbThrottleSaturation" : "requires https://github.com/ArduPilot/ardupilot/pull/27106 to pass" ,
7588+ "SoaringClimbRate" : "very bad sink rate" ,
75127589 }
75137590
75147591
0 commit comments