LCOV - code coverage report
Current view: top level - SITL - ServoModel.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 48 49 98.0 %
Date: 2026-09-09 11:02:08 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : /*
      16             :   simple model of a servo. Model is:
      17             : 
      18             :     - time delay for transport protocol delay
      19             :     - slew limit
      20             :     - 2-pole butterworth
      21             : */
      22             : 
      23             : #include "ServoModel.h"
      24             : #include "SITL.h"
      25             : 
      26             : // SITL servo model parameters
      27             : const AP_Param::GroupInfo SITL::SIM::ServoParams::var_info[] = {
      28             :     // @Param: SPEED
      29             :     // @DisplayName: servo speed
      30             :     // @Description: servo speed (time for 60 degree deflection). If DELAY and FILTER are not set then this is converted to a 1p lowpass filter. If DELAY or FILTER are set then this is treated as a rate of change limit
      31             :     // @Units: s
      32             :     AP_GROUPINFO("SPEED",     1, ServoParams,  servo_speed, 0.14),
      33             : 
      34             :     // @Param: DELAY
      35             :     // @DisplayName: servo delay
      36             :     // @Description: servo delay
      37             :     // @Units: s
      38             :     AP_GROUPINFO("DELAY",     2, ServoParams,  servo_delay, 0.0),
      39             : 
      40             :     // @Param: FILTER
      41             :     // @DisplayName: servo filter
      42             :     // @Description: servo filter
      43             :     // @Units: Hz
      44             :     AP_GROUPINFO("FILTER",    3, ServoParams,  servo_filter, 0),
      45             :     
      46             :     AP_GROUPEND
      47             : };
      48             : 
      49             : /*
      50             :   simpler filter used when SIM_SERVO_FILTER and SIM_SERVO_DELAY are not set
      51             :   this filter is a 1p low pass based on SIM_SERVO_SPEED
      52             :  */
      53   333928295 : float ServoModel::apply_simple_filter(float v, float dt)
      54             : {
      55   333928295 :     const auto *sitl = AP::sitl();
      56   333928295 :     if (!is_positive(sitl->servo.servo_speed)) {
      57           0 :         return v;
      58             :     }
      59   333928295 :     const float cutoff = 1.0f / (2 * M_PI * sitl->servo.servo_speed);
      60   333928295 :     filter1p.set_cutoff_frequency(cutoff);
      61   333928295 :     return filter1p.apply(v, dt);
      62             : }
      63             : 
      64             : /*
      65             :   apply a filter for a servo model consistting of a delay, speed and 2p filter
      66             :  */
      67   344538527 : float ServoModel::apply_filter(float v, float dt)
      68             : {
      69   344538527 :     const auto *sitl = AP::sitl();
      70   344538527 :     if (!sitl) {
      71           6 :         return v;
      72             :     }
      73             : 
      74   678466816 :     if (!is_positive(sitl->servo.servo_delay) &&
      75   333928295 :         !is_positive(sitl->servo.servo_filter)) {
      76             :         // fallback to a simpler 1p filter model
      77   333928295 :         return apply_simple_filter(v, dt);
      78             :     }
      79             : 
      80             :     // apply delay
      81    10610226 :     if (sitl->servo.servo_delay > 0) {
      82    10610226 :         uint32_t delay_len = MAX(1,sitl->servo.servo_delay * sitl->loop_rate_hz);
      83    10610226 :         if (!delay) {
      84           6 :             delay = NEW_NOTHROW ObjectBuffer<float>();
      85             :         }
      86    10610226 :         if (delay->get_size() != delay_len) {
      87           6 :             delay->set_size(delay_len);
      88             :         }
      89    21220518 :         while (delay->space() > 0) {
      90    10610292 :             delay->push(v);
      91             :         }
      92    10610226 :         IGNORE_RETURN(delay->pop(v));
      93             :     }
      94             : 
      95             :     // apply slew limit
      96    10610226 :     if (sitl->servo.servo_speed > 0) {
      97             :         // assume SIM_SERVO_SPEED is time for 60 degrees
      98    10610226 :         float time_per_degree = sitl->servo.servo_speed / 60.0;
      99    10610226 :         float proportion_per_second = 1.0 / (angle_deg * time_per_degree);
     100    10610226 :         delta_max = proportion_per_second * dt;
     101    10610226 :         v = constrain_float(v, last_v-delta_max, last_v+delta_max);
     102    10610226 :         v = constrain_float(v, -1, 1);
     103    10610226 :         last_v = v;
     104             :     }
     105             : 
     106             :     // apply filter
     107    10610226 :     if (sitl->servo.servo_filter > 0) {
     108    10610226 :         filter.set_cutoff_frequency(sitl->loop_rate_hz, sitl->servo.servo_filter);
     109    10610226 :         v = filter.apply(v);
     110             :     }
     111             : 
     112    10610226 :     return v;
     113             : }
     114             : 
     115   135454372 : float ServoModel::filter_range(uint16_t pwm, float dt)
     116             : {
     117   135454372 :     const float v = (pwm - pwm_min)/float(pwm_max - pwm_min);
     118   135454372 :     return apply_filter(v, dt);
     119             : }
     120             : 
     121   209084155 : float ServoModel::filter_angle(uint16_t pwm, float dt)
     122             : {
     123   209084155 :     const float v = (pwm - 0.5*(pwm_max+pwm_min))/(0.5*float(pwm_max - pwm_min));
     124   209084155 :     return apply_filter(v, dt);
     125             : }
     126             : 
     127             : // return pwm required for current angle
     128     5295439 : float ServoModel::angle_pwm() const
     129             : {
     130     5295439 :     return angle() * (0.5*float(pwm_max - pwm_min)) + 0.5*(pwm_max+pwm_min);
     131             : }
     132             : 
     133     7073484 : void ServoModel::set_pwm_range(uint16_t _pwm_min, uint16_t _pwm_max)
     134             : {
     135     7073484 :     pwm_min = _pwm_min;
     136     7073484 :     pwm_max = _pwm_max;
     137     7073484 : }
     138             : 
     139     7073484 : void ServoModel::set_deflection(float _angle_deg)
     140             : {
     141     7073484 :     angle_deg = fabsf(_angle_deg);
     142     7073484 : }

Generated by: LCOV version 1.14