LCOV - code coverage report
Current view: top level - AP_HAL - SIMState.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 37 40 92.5 %
Date: 2026-09-09 11:02:08 Functions: 1 1 100.0 %

          Line data    Source code
       1             : #include "SIMState.h"
       2             : 
       3             : #if AP_SIM_ENABLED
       4             : 
       5             : /*
       6             :  *  This is a very-much-cut-down AP_HAL_SITL object.  We should make
       7             :  *  PA_HAL_SITL use this object - by moving a lot more code from over
       8             :  *  there into here.
       9             :  */
      10             : 
      11             : #include <SITL/SIM_Multicopter.h>
      12             : #include <SITL/SIM_Helicopter.h>
      13             : #include <SITL/SIM_SingleCopter.h>
      14             : #include <SITL/SIM_Plane.h>
      15             : #include <SITL/SIM_Glider.h>
      16             : #include <SITL/SIM_QuadPlane.h>
      17             : #include <SITL/SIM_Rover.h>
      18             : #include <SITL/SIM_BalanceBot.h>
      19             : #include <SITL/SIM_Sailboat.h>
      20             : #include <SITL/SIM_MotorBoat.h>
      21             : #include <SITL/SIM_Tracker.h>
      22             : #include <SITL/SIM_Submarine.h>
      23             : #include <SITL/SIM_Blimp.h>
      24             : #include <SITL/SIM_NoVehicle.h>
      25             : #include <SITL/SIM_JSON.h>
      26             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      27             : 
      28             : #include <AP_Baro/AP_Baro.h>
      29             : 
      30             : #include <AP_BoardConfig/AP_BoardConfig.h>
      31             : 
      32             : extern const AP_HAL::HAL& hal;
      33             : 
      34             : using namespace AP_HAL;
      35             : 
      36             : #include <AP_Terrain/AP_Terrain.h>
      37             : 
      38             : #ifndef AP_SIM_FRAME_CLASS
      39             : #if APM_BUILD_TYPE(APM_BUILD_ArduCopter)
      40             : #define AP_SIM_FRAME_CLASS MultiCopter
      41             : #elif APM_BUILD_TYPE(APM_BUILD_Heli)
      42             : #define AP_SIM_FRAME_CLASS Helicopter
      43             : #elif APM_BUILD_TYPE(APM_BUILD_AntennaTracker)
      44             : #define AP_SIM_FRAME_CLASS Tracker
      45             : #elif APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      46             : #define AP_SIM_FRAME_CLASS Plane
      47             : #elif APM_BUILD_TYPE(APM_BUILD_Rover)
      48             : #define AP_SIM_FRAME_CLASS SimRover
      49             : #elif APM_BUILD_TYPE(APM_BUILD_Blimp)
      50             : #define AP_SIM_FRAME_CLASS Blimp
      51             : #elif APM_BUILD_TYPE(APM_BUILD_ArduSub)
      52             : #define AP_SIM_FRAME_CLASS Submarine
      53             : #else
      54             : #define AP_SIM_FRAME_CLASS NoVehicle
      55             : #endif
      56             : #endif
      57             : 
      58             : #ifndef AP_SIM_FRAME_STRING
      59             : #if APM_BUILD_TYPE(APM_BUILD_ArduCopter)
      60             : #define AP_SIM_FRAME_STRING "+"
      61             : #elif APM_BUILD_TYPE(APM_BUILD_Heli)
      62             : #define AP_SIM_FRAME_STRING "heli"
      63             : #elif APM_BUILD_TYPE(APM_BUILD_AntennaTracker)
      64             : #define AP_SIM_FRAME_STRING "tracker"
      65             : #elif APM_BUILD_TYPE(APM_BUILD_ArduPlane)
      66             : #define AP_SIM_FRAME_STRING "plane"
      67             : #elif APM_BUILD_TYPE(APM_BUILD_Rover)
      68             : #define AP_SIM_FRAME_STRING "rover"
      69             : #elif APM_BUILD_TYPE(APM_BUILD_Blimp)
      70             : #define AP_SIM_FRAME_STRING "blimp"
      71             : #elif APM_BUILD_TYPE(APM_BUILD_ArduSub)
      72             : #define AP_SIM_FRAME_STRING "sub"
      73             : #else
      74             : #define AP_SIM_FRAME_STRING ""
      75             : #endif
      76             : #endif
      77             : 
      78             : 
      79             : #if CONFIG_HAL_BOARD != HAL_BOARD_SITL
      80             : void SIMState::update()
      81             : {
      82             :     static bool init_done;
      83             :     if (!init_done) {
      84             :         AP::sitl()->init();
      85             :         init_done = true;
      86             :         sitl_model = SITL::AP_SIM_FRAME_CLASS::create(AP_SIM_FRAME_STRING);
      87             :     }
      88             : 
      89             :     _fdm_input_step();
      90             : }
      91             : 
      92             : /*
      93             :   setup for SITL handling
      94             :  */
      95             : void SIMState::_sitl_setup(const char *home_str)
      96             : {
      97             :     _home_str = home_str;
      98             : 
      99             :     printf("Starting SITL input\n");
     100             : }
     101             : 
     102             : 
     103             : /*
     104             :   step the FDM by one time step
     105             :  */
     106             : void SIMState::_fdm_input_step(void)
     107             : {
     108             :     fdm_input_local();
     109             : }
     110             : 
     111             : /*
     112             :   get FDM input from a local model
     113             :  */
     114             : void SIMState::fdm_input_local(void)
     115             : {
     116             :     struct sitl_input input;
     117             : 
     118             :     // construct servos structure for FDM
     119             :     _simulator_servos(input);
     120             : 
     121             :     // read servo inputs from ride along flight controllers
     122             :     // ride_along.receive(input);
     123             : 
     124             :     // update the model
     125             :     sitl_model->update_home();
     126             :     sitl_model->update_model(input);
     127             : 
     128             :     // get FDM output from the model
     129             :     if (_sitl == nullptr) {
     130             :         _sitl = AP::sitl();
     131             :     }
     132             :     if (_sitl) {
     133             :         sitl_model->fill_fdm(_sitl->state);
     134             :     }
     135             : 
     136             :     // output JSON state to ride along flight controllers
     137             :     // ride_along.send(_sitl->state,sitl_model->get_position_relhome());
     138             : 
     139             : #if AP_SIM_SOLOGIMBAL_ENABLED
     140             :     if (gimbal != nullptr) {
     141             :         gimbal->update();
     142             :     }
     143             : #endif  // AP_SIM_SOLOGIMBAL_ENABLED
     144             : #if AP_SIM_ADSB_ENABLED
     145             :     if (adsb != nullptr) {
     146             :         adsb->update();
     147             :     }
     148             : #endif  // AP_SIM_ADSB_ENABLED
     149             : #if AP_SIM_VICON_ENABLED
     150             :     if (vicon != nullptr) {
     151             :         Quaternion attitude;
     152             :         sitl_model->get_attitude(attitude);
     153             :         vicon->update(sitl_model->get_location(),
     154             :                       sitl_model->get_position_relhome(),
     155             :                       sitl_model->get_velocity_ef(),
     156             :                       attitude);
     157             :     }
     158             : #endif  // AP_SIM_VICON_ENABLED
     159             :     if (benewake_tf02 != nullptr) {
     160             :         benewake_tf02->update(sitl_model->rangefinder_range());
     161             :     }
     162             :     if (benewake_tf03 != nullptr) {
     163             :         benewake_tf03->update(sitl_model->rangefinder_range());
     164             :     }
     165             :     if (benewake_tfmini != nullptr) {
     166             :         benewake_tfmini->update(sitl_model->rangefinder_range());
     167             :     }
     168             :     if (nooploop != nullptr) {
     169             :         nooploop->update(sitl_model->rangefinder_range());
     170             :     }
     171             :     if (teraranger_serial != nullptr) {
     172             :         teraranger_serial->update(sitl_model->rangefinder_range());
     173             :     }
     174             :     if (lightwareserial != nullptr) {
     175             :         lightwareserial->update(sitl_model->rangefinder_range());
     176             :     }
     177             :     if (lightwareserial_binary != nullptr) {
     178             :         lightwareserial_binary->update(sitl_model->rangefinder_range());
     179             :     }
     180             :     if (lanbao != nullptr) {
     181             :         lanbao->update(sitl_model->rangefinder_range());
     182             :     }
     183             :     if (blping != nullptr) {
     184             :         blping->update(sitl_model->rangefinder_range());
     185             :     }
     186             :     if (leddarone != nullptr) {
     187             :         leddarone->update(sitl_model->rangefinder_range());
     188             :     }
     189             :     if (rds02uf != nullptr) {
     190             :         rds02uf->update(sitl_model->rangefinder_range());
     191             :     }
     192             :     if (USD1_v0 != nullptr) {
     193             :         USD1_v0->update(sitl_model->rangefinder_range());
     194             :     }
     195             :     if (USD1_v1 != nullptr) {
     196             :         USD1_v1->update(sitl_model->rangefinder_range());
     197             :     }
     198             :     if (maxsonarseriallv != nullptr) {
     199             :         maxsonarseriallv->update(sitl_model->rangefinder_range());
     200             :     }
     201             :     if (wasp != nullptr) {
     202             :         wasp->update(sitl_model->rangefinder_range());
     203             :     }
     204             :     if (nmea != nullptr) {
     205             :         nmea->update(sitl_model->rangefinder_range());
     206             :     }
     207             :     if (rf_mavlink != nullptr) {
     208             :         rf_mavlink->update(sitl_model->rangefinder_range());
     209             :     }
     210             :     if (gyus42v2 != nullptr) {
     211             :         gyus42v2->update(sitl_model->rangefinder_range());
     212             :     }
     213             :     if (efi_ms != nullptr) {
     214             :         efi_ms->update(*sitl_model);
     215             :     }
     216             : 
     217             :     if (frsky_d != nullptr) {
     218             :         frsky_d->update();
     219             :     }
     220             : 
     221             : #if AP_SIM_CRSF_ENABLED
     222             :     if (crsf != nullptr) {
     223             :         crsf->update();
     224             :     }
     225             : #endif
     226             : 
     227             : #if AP_SIM_PS_RPLIDARA2_ENABLED
     228             :     if (rplidara2 != nullptr) {
     229             :         rplidara2->update(sitl_model->get_location());
     230             :     }
     231             : #endif
     232             : 
     233             : #if AP_SIM_PS_TERARANGERTOWER_ENABLED
     234             :     if (terarangertower != nullptr) {
     235             :         terarangertower->update(sitl_model->get_location());
     236             :     }
     237             : #endif
     238             : 
     239             : #if AP_SIM_PS_LIGHTWARE_SF45B_ENABLED
     240             :     if (sf45b != nullptr) {
     241             :         sf45b->update(sitl_model->get_location());
     242             :     }
     243             : #endif
     244             : 
     245             :     if (vectornav != nullptr) {
     246             :         vectornav->update();
     247             :     }
     248             : 
     249             :     if (microstrain5 != nullptr) {
     250             :         microstrain5->update();
     251             :     }
     252             :     if (inertiallabs != nullptr) {
     253             :         inertiallabs->update();
     254             :     }
     255             : #if AP_SIM_AERON_ENABLED
     256             :     if (aeron != nullptr) {
     257             :         aeron->update();
     258             :     }
     259             : #endif  // AP_SIM_AERON_ENABLED
     260             : 
     261             : #if AP_SIM_AIS_ENABLED
     262             :     if (ais != nullptr) {
     263             :         ais->update();
     264             :     }
     265             : #endif
     266             :     for (uint8_t i=0; i<ARRAY_SIZE(gps); i++) {
     267             :         if (gps[i] != nullptr) {
     268             :             gps[i]->update();
     269             :         }
     270             :     }
     271             : 
     272             :     // update simulation time
     273             :     if (_sitl) {
     274             :         hal.scheduler->stop_clock(_sitl->state.timestamp_us);
     275             :     } else {
     276             :         hal.scheduler->stop_clock(AP_HAL::micros64()+100);
     277             :     }
     278             : 
     279             :     set_height_agl();
     280             : 
     281             :     _update_count++;
     282             : }
     283             : #endif  // CONFIG_HAL_BOARD != HAL_BOARD_SITL
     284             : 
     285             : #if AP_SIM_WIND_SIMULATION_ENABLED
     286   187275482 : void SIMState::update_simulated_wind(struct sitl_input &input)
     287             : {
     288   187275482 :     float wind_speed = 0;
     289   187275482 :     float wind_direction = 0;
     290   187275482 :     float wind_dir_z = 0;
     291             : 
     292   187275482 :     uint32_t now = AP_HAL::micros();
     293             : 
     294   187275482 :     if (_sitl == nullptr)  {
     295        2094 :         _sitl = AP::sitl();
     296             :     }
     297             :     // give 5 seconds to calibrate airspeed sensor at 0 wind speed
     298   187275482 :     if (wind_start_delay_micros == 0) {
     299        2094 :         wind_start_delay_micros = now;
     300   187273388 :     } else if ((now - wind_start_delay_micros) > 5000000) {
     301             :         // The EKF does not like step inputs so this LPF keeps it happy.
     302   175244979 :         uint32_t dt_us = now - last_wind_update_us;
     303   175244979 :         if (dt_us > 1000) {
     304    87622904 :             last_wind_update_us = now;
     305             :             // slew wind based on the configured time constant
     306    87622904 :             const float dt = dt_us * 1.0e-6;
     307    87622904 :             const float tc = MAX(_sitl->wind_change_tc, 0.1);
     308    87622904 :             const float alpha = calc_lowpass_alpha_dt(dt, 1.0/tc);
     309    87622904 :             _sitl->wind_speed_active     += (_sitl->wind_speed - _sitl->wind_speed_active) * alpha;
     310    87622904 :             _sitl->wind_direction_active += (wrap_180(_sitl->wind_direction - _sitl->wind_direction_active)) * alpha;
     311    87622904 :             _sitl->wind_dir_z_active     += (_sitl->wind_dir_z - _sitl->wind_dir_z_active) * alpha;
     312    87622904 :             _sitl->wind_direction_active = wrap_180(_sitl->wind_direction_active);
     313             :         }
     314   175244979 :         wind_speed =     _sitl->wind_speed_active;
     315   175244979 :         wind_direction = _sitl->wind_direction_active;
     316   175244979 :         wind_dir_z =     _sitl->wind_dir_z_active;
     317             : 
     318             :         // pass wind into simulators using different wind types via param SIM_WIND_T*.
     319   175244979 :         const float altitude = _sitl->state.height_agl;
     320   175244979 :         switch (_sitl->wind_type) {
     321   173436382 :         case SITL::SIM::WIND_TYPE_SQRT:
     322   173436382 :             if (altitude < _sitl->wind_type_alt) {
     323   142001243 :                 wind_speed *= sqrtf(MAX(altitude / _sitl->wind_type_alt, 0));
     324             :             }
     325   173436382 :             break;
     326             : 
     327           0 :         case SITL::SIM::WIND_TYPE_COEF:
     328           0 :             wind_speed += (altitude - _sitl->wind_type_alt) * _sitl->wind_type_coef;
     329           0 :             break;
     330             : 
     331     1808597 :         case SITL::SIM::WIND_TYPE_NO_LIMIT:
     332             :         default:
     333     1808597 :             break;
     334             :         }
     335             : 
     336             :         // never allow negative wind velocity
     337   175244979 :         wind_speed = MAX(wind_speed, 0);
     338             :     }
     339             : 
     340   187275482 :     input.wind.speed = wind_speed;
     341   187275482 :     input.wind.direction = wind_direction;
     342   187275482 :     input.wind.turbulence = _sitl->wind_turbulance;
     343   187275482 :     input.wind.dir_z = wind_dir_z;
     344   187275482 : }
     345             : #endif  // AP_SIM_WIND_SIMULATION_ENABLED
     346             : 
     347             : #if CONFIG_HAL_BOARD != HAL_BOARD_SITL
     348             : /*
     349             :   create sitl_input structure for sending to FDM
     350             :  */
     351             : void SIMState::_simulator_servos(struct sitl_input &input)
     352             : {
     353             :     if (_sitl == nullptr) {
     354             :         return;
     355             :     }
     356             : 
     357             :     update_simulated_wind(input);
     358             : 
     359             :     for (uint8_t i=0; i<ARRAY_SIZE(pwm_output); i++) {
     360             :         if (pwm_output[i] == 0xFFFF) {
     361             :             input.servos[i] = 0;
     362             :         } else {
     363             :             input.servos[i] = pwm_output[i];
     364             :         }
     365             :     }
     366             : 
     367             :     {
     368             :         // FETtec ESC simulation support.  Input signals of 1000-2000
     369             :         // are positive thrust, 0 to 1000 are negative thrust.  Deeper
     370             :         // changes required to support negative thrust - potentially
     371             :         // adding a field to input.
     372             :             if (_sitl->fetteconewireesc_sim.enabled()) {
     373             :                 _sitl->fetteconewireesc_sim.update_sitl_input_pwm(input);
     374             :                 for (uint8_t i=0; i<ARRAY_SIZE(input.servos); i++) {
     375             :                     if (input.servos[i] != 0 && input.servos[i] < 1000) {
     376             :                         AP_HAL::panic("Bad input servo value (%u)", input.servos[i]);
     377             :                     }
     378             :                 }
     379             :             }
     380             :     }
     381             : 
     382             : #if AP_SIM_VOLZ_ENABLED
     383             :     // update simulation input based on data received via "serial" to
     384             :     // Volz servos:
     385             :     if (_sitl->volz_sim.enabled()) {
     386             :         _sitl->volz_sim.update_sitl_input_pwm(input);
     387             :         for (uint8_t i=0; i<ARRAY_SIZE(input.servos); i++) {
     388             :             if (input.servos[i] != 0 && input.servos[i] < 1000) {
     389             :                 AP_HAL::panic("Bad input servo value (%u)", input.servos[i]);
     390             :             }
     391             :         }
     392             :     }
     393             : #endif
     394             : 
     395             :     float voltage = 0;
     396             :     _current = 0;
     397             : 
     398             :     {
     399             :         if (_sitl->state.battery_voltage <= 0) {
     400             :         } else {
     401             :             // FDM provides voltage and current
     402             :             voltage = _sitl->state.battery_voltage;
     403             :             _current = _sitl->state.battery_current;
     404             :         }
     405             :     }
     406             : 
     407             :     // assume 3DR power brick
     408             :     voltage_pin_value = ((voltage / 10.1f) / 5.0f) * 1024;
     409             :     current_pin_value = ((_current / 17.0f) / 5.0f) * 1024;
     410             :     // fake battery2 as just a 25% gain on the first one
     411             :     voltage2_pin_value = ((voltage * 0.25f / 10.1f) / 5.0f) * 1024;
     412             :     current2_pin_value = ((_current * 0.25f / 17.0f) / 5.0f) * 1024;
     413             : }
     414             : 
     415             : /*
     416             :   set height above the ground in meters
     417             :  */
     418             : void SIMState::set_height_agl(void)
     419             : {
     420             :     static float home_alt = -1;
     421             : 
     422             :     if (!_sitl) {
     423             :         // in example program
     424             :         return;
     425             :     }
     426             : 
     427             :     if (is_equal(home_alt, -1.0f) && _sitl->state.altitude > 0) {
     428             :         // remember home altitude as first non-zero altitude
     429             :         home_alt = _sitl->state.altitude;
     430             :     }
     431             : 
     432             : #if AP_TERRAIN_AVAILABLE
     433             :     if (_sitl != nullptr &&
     434             :         _sitl->terrain_enable) {
     435             :         // get height above terrain from AP_Terrain. This assumes
     436             :         // AP_Terrain is working
     437             :         float terrain_height_amsl;
     438             :         Location location;
     439             :         location.lat = _sitl->state.latitude*1.0e7;
     440             :         location.lng = _sitl->state.longitude*1.0e7;
     441             : 
     442             :         AP_Terrain *_terrain = AP_Terrain::get_singleton();
     443             :         if (_terrain != nullptr &&
     444             :             _terrain->height_amsl(location, terrain_height_amsl)) {
     445             :             _sitl->state.height_agl = _sitl->state.altitude - terrain_height_amsl;
     446             :             return;
     447             :         }
     448             :     }
     449             : #endif
     450             : 
     451             :     if (_sitl != nullptr) {
     452             :         // fall back to flat earth model
     453             :         _sitl->state.height_agl = _sitl->state.altitude - home_alt;
     454             :     }
     455             : }
     456             : 
     457             : #endif  // CONFIG_HAL_BOARD != HAL_BOARD_SITL
     458             : 
     459             : #endif  // AP_SIM_ENABLED

Generated by: LCOV version 1.14