LCOV - code coverage report
Current view: top level - AP_OSD - AP_OSD.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 76 214 35.5 %
Date: 2026-09-09 11:02:08 Functions: 10 12 83.3 %

          Line data    Source code
       1             : /*
       2             :  * This file is free software: you can redistribute it and/or modify it
       3             :  * under the terms of the GNU General Public License as published by the
       4             :  * Free Software Foundation, either version 3 of the License, or
       5             :  * (at your option) any later version.
       6             :  *
       7             :  * This file is distributed in the hope that it will be useful, but
       8             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      10             :  * See the GNU General Public License for more details.
      11             :  *
      12             :  * You should have received a copy of the GNU General Public License along
      13             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  *
      15             :  * AP_OSD partially based on betaflight and inav osd.c implemention.
      16             :  * clarity.mcm font is taken from inav configurator.
      17             :  * Many thanks to their authors.
      18             :  */
      19             : 
      20             : #include "AP_OSD.h"
      21             : 
      22             : #if OSD_ENABLED || OSD_PARAM_ENABLED
      23             : 
      24             : #include "AP_OSD_MAX7456.h"
      25             : #ifdef WITH_SITL_OSD
      26             : #include "AP_OSD_SITL.h"
      27             : #endif
      28             : #include "AP_OSD_MSP.h"
      29             : #include "AP_OSD_MSP_DisplayPort.h"
      30             : #include <AP_HAL/AP_HAL.h>
      31             : #include <AP_HAL/Util.h>
      32             : #include <RC_Channel/RC_Channel.h>
      33             : #include <AP_AHRS/AP_AHRS.h>
      34             : #include <AP_BattMonitor/AP_BattMonitor.h>
      35             : #include <utility>
      36             : #include <AP_Notify/AP_Notify.h>
      37             : #include <AP_Terrain/AP_Terrain.h>
      38             : #include <AP_RSSI/AP_RSSI.h>
      39             : #include <GCS_MAVLink/GCS.h>
      40             : 
      41             : // macro for easy use of var_info2
      42             : #define AP_SUBGROUPINFO2(element, name, idx, thisclazz, elclazz) { name, AP_VAROFFSET(thisclazz, element), { group_info : elclazz::var_info2 }, AP_PARAM_FLAG_NESTED_OFFSET, idx, AP_PARAM_GROUP }
      43             : 
      44             : const AP_Param::GroupInfo AP_OSD::var_info[] = {
      45             : 
      46             :     // @Param: _TYPE
      47             :     // @DisplayName: OSD type
      48             :     // @Description: OSD type. TXONLY makes the OSD parameter selection available to other modules even if there is no native OSD support on the board, for instance CRSF.
      49             :     // @Values: 0:None,1:MAX7456,2:SITL,3:MSP,4:TXONLY,5:MSP_DISPLAYPORT
      50             :     // @User: Standard
      51             :     // @RebootRequired: True
      52             :     AP_GROUPINFO_FLAGS("_TYPE", 1, AP_OSD, osd_type, 0, AP_PARAM_FLAG_ENABLE),
      53             : 
      54             : #if OSD_ENABLED
      55             :     // @Param: _CHAN
      56             :     // @DisplayName: Screen switch transmitter channel
      57             :     // @Description: This sets the channel used to switch different OSD screens.
      58             :     // @Values: 0:Disable,5:Chan5,6:Chan6,7:Chan7,8:Chan8,9:Chan9,10:Chan10,11:Chan11,12:Chan12,13:Chan13,14:Chan14,15:Chan15,16:Chan16
      59             :     // @User: Standard
      60             :     AP_GROUPINFO("_CHAN", 2, AP_OSD, rc_channel, 0),
      61             : 
      62             :     // @Group: 1_
      63             :     // @Path: AP_OSD_Screen.cpp
      64             :     AP_SUBGROUPINFO(screen[0], "1_", 3, AP_OSD, AP_OSD_Screen),
      65             : 
      66             :     // @Group: 2_
      67             :     // @Path: AP_OSD_Screen.cpp
      68             :     AP_SUBGROUPINFO(screen[1], "2_", 4, AP_OSD, AP_OSD_Screen),
      69             : 
      70             :     // @Group: 3_
      71             :     // @Path: AP_OSD_Screen.cpp
      72             :     AP_SUBGROUPINFO(screen[2], "3_", 5, AP_OSD, AP_OSD_Screen),
      73             : 
      74             :     // @Group: 4_
      75             :     // @Path: AP_OSD_Screen.cpp
      76             :     AP_SUBGROUPINFO(screen[3], "4_", 6, AP_OSD, AP_OSD_Screen),
      77             : 
      78             :     // @Param: _SW_METHOD
      79             :     // @DisplayName: Screen switch method
      80             :     // @Description: This sets the method used to switch different OSD screens.
      81             :     // @Values: 0: switch to next screen if channel value was changed, 1: select screen based on pwm ranges specified for each screen, 2: switch to next screen after low to high transition and every 1s while channel value is high
      82             :     // @User: Standard
      83             :     AP_GROUPINFO("_SW_METHOD", 7, AP_OSD, sw_method, AP_OSD::TOGGLE),
      84             : 
      85             :     // @Param: _OPTIONS
      86             :     // @DisplayName: OSD Options
      87             :     // @Description: This sets options that change the display
      88             :     // @Bitmask: 0:UseDecimalPack, 1:InvertedWindArrow, 2:InvertedAHRoll, 3:Convert feet to miles at 5280ft instead of 10000ft, 4:DisableCrosshair, 5:TranslateArrows, 6:AviationStyleAH, 7:Prefix LQ with RF Mode
      89             :     // @User: Standard
      90             :     AP_GROUPINFO("_OPTIONS", 8, AP_OSD, options, OPTION_DECIMAL_PACK),
      91             : 
      92             :     // @Param: _FONT
      93             :     // @DisplayName: OSD Font
      94             :     // @Description: This sets which OSD font to use. It is an integer from 0 to the number of fonts available
      95             :     // @User: Standard
      96             :     // @RebootRequired: True
      97             :     AP_GROUPINFO("_FONT", 9, AP_OSD, font_num, 0),
      98             : 
      99             :     // @Param: _V_OFFSET
     100             :     // @DisplayName: OSD vertical offset
     101             :     // @Description: Sets vertical offset of the osd inside image
     102             :     // @Range: 0 31
     103             :     // @User: Standard
     104             :     // @RebootRequired: True
     105             :     AP_GROUPINFO("_V_OFFSET", 10, AP_OSD, v_offset, 16),
     106             : 
     107             :     // @Param: _H_OFFSET
     108             :     // @DisplayName: OSD horizontal offset
     109             :     // @Description: Sets horizontal offset of the osd inside image
     110             :     // @Range: 0 63
     111             :     // @User: Standard
     112             :     // @RebootRequired: True
     113             :     AP_GROUPINFO("_H_OFFSET", 11, AP_OSD, h_offset, 32),
     114             : 
     115             :     // @Param: _W_RSSI
     116             :     // @DisplayName: RSSI warn level (in %)
     117             :     // @Description: Set level at which RSSI item will flash (in positive % or negative dBm values as applicable). 30% or -100dBm are defaults.
     118             :     // @Range: -128 100
     119             :     // @User: Standard
     120             :     AP_GROUPINFO("_W_RSSI", 12, AP_OSD, warn_rssi, AP_OSD_WARN_RSSI_DEFAULT),
     121             : 
     122             :     // @Param: _W_NSAT
     123             :     // @DisplayName: NSAT warn level
     124             :     // @Description: Set level at which NSAT item will flash
     125             :     // @Range: 1 30
     126             :     // @User: Standard
     127             :     AP_GROUPINFO("_W_NSAT", 13, AP_OSD, warn_nsat, 9),
     128             : 
     129             :     // @Param: _W_BATVOLT
     130             :     // @DisplayName: BAT_VOLT warn level
     131             :     // @Description: Set level at which BAT_VOLT item will flash
     132             :     // @Range: 0 100
     133             :     // @User: Standard
     134             :     AP_GROUPINFO("_W_BATVOLT", 14, AP_OSD, warn_batvolt, 10.0f),
     135             : 
     136             :     // @Param: _UNITS
     137             :     // @DisplayName: Display Units
     138             :     // @Description: Sets the units to use in displaying items
     139             :     // @Values: 0:Metric,1:Imperial,2:SI,3:Aviation
     140             :     // @User: Standard
     141             :     AP_GROUPINFO("_UNITS", 15, AP_OSD, units, 0),
     142             : 
     143             :     // @Param: _MSG_TIME
     144             :     // @DisplayName: Message display duration in seconds
     145             :     // @Description: Sets message duration seconds
     146             :     // @Range: 1 20
     147             :     // @User: Standard
     148             :     AP_GROUPINFO("_MSG_TIME", 16, AP_OSD, msgtime_s, 10),
     149             : 
     150             :     // @Param: _ARM_SCR
     151             :     // @DisplayName: Arm screen
     152             :     // @Description: Screen to be shown on Arm event. Zero to disable the feature.
     153             :     // @Range: 0 4
     154             :     // @User: Standard
     155             :     AP_GROUPINFO("_ARM_SCR", 17, AP_OSD, arm_scr, 0),
     156             : 
     157             :     // @Param: _DSARM_SCR
     158             :     // @DisplayName: Disarm screen
     159             :     // @Description: Screen to be shown on disarm event. Zero to disable the feature.
     160             :     // @Range: 0 4
     161             :     // @User: Standard
     162             :     AP_GROUPINFO("_DSARM_SCR", 18, AP_OSD, disarm_scr, 0),
     163             : 
     164             :     // @Param: _FS_SCR
     165             :     // @DisplayName: Failsafe screen
     166             :     // @Description: Screen to be shown on failsafe event. Zero to disable the feature.
     167             :     // @Range: 0 4
     168             :     // @User: Standard
     169             :     AP_GROUPINFO("_FS_SCR", 19, AP_OSD, failsafe_scr, 0),
     170             : 
     171             : #if OSD_PARAM_ENABLED
     172             :     // @Param: _BTN_DELAY
     173             :     // @DisplayName: Button delay
     174             :     // @Description: Debounce time in ms for stick commanded parameter navigation.
     175             :     // @Range: 0 3000
     176             :     // @User: Advanced
     177             :     AP_GROUPINFO("_BTN_DELAY", 20, AP_OSD, button_delay_ms, 300),
     178             : #endif
     179             : #if AP_TERRAIN_AVAILABLE
     180             :     // @Param: _W_TERR
     181             :     // @DisplayName: Terrain warn level
     182             :     // @Description: Set level below which TER_HGT item will flash. -1 disables.
     183             :     // @Range: -1 3000
     184             :     // @Units: m
     185             :     // @User: Standard
     186             :     AP_GROUPINFO("_W_TERR", 23, AP_OSD, warn_terr, -1),
     187             : #endif
     188             : 
     189             :     // @Param: _W_AVGCELLV
     190             :     // @DisplayName: AVGCELLV warn level
     191             :     // @Description: Set level at which AVGCELLV item will flash
     192             :     // @Range: 0 100
     193             :     // @User: Standard
     194             :     AP_GROUPINFO("_W_AVGCELLV", 24, AP_OSD, warn_avgcellvolt, 3.6f),
     195             : 
     196             :    // @Param: _CELL_COUNT
     197             :     // @DisplayName: Battery cell count
     198             :     // @Description: Used for average cell voltage display. -1 disables, 0 uses cell count autodetection for well charged LIPO/LIION batteries at connection, other values manually select cell count used.
     199             :     // @Increment: 1
     200             :     // @User: Advanced
     201             :     AP_GROUPINFO("_CELL_COUNT", 25, AP_OSD, cell_count, -1),
     202             : 
     203             :     // @Param: _W_RESTVOLT
     204             :     // @DisplayName: RESTVOLT warn level
     205             :     // @Description: Set level at which RESTVOLT item will flash
     206             :     // @Range: 0 100
     207             :     // @User: Standard
     208             :     AP_GROUPINFO("_W_RESTVOLT", 26, AP_OSD, warn_restvolt, 10.0f),
     209             :        
     210             :     // @Param: _W_ACRVOLT
     211             :     // @DisplayName: Avg Cell Resting Volt warn level
     212             :     // @Description: Set level at which ACRVOLT item will flash
     213             :     // @Range: 0 100
     214             :     // @User: Standard
     215             :     AP_GROUPINFO("_W_ACRVOLT", 31, AP_OSD, warn_avgcellrestvolt, 3.6f),
     216             : 
     217             : #if AP_OSD_EXTENDED_LNK_STATS
     218             :     // @Param: _W_LQ
     219             :     // @DisplayName: RC link quality warn level (in %)
     220             :     // @Description: Set level at which RC_LQ item will flash (%)
     221             :     // @Range: 0 100
     222             :     // @User: Standard
     223             :     AP_GROUPINFO("_W_LQ", 33, AP_OSD, warn_lq, 50),
     224             : 
     225             :     // @Param: _W_SNR
     226             :     // @DisplayName: RC link SNR warn level (in %)
     227             :     // @Description: Set level at which RC_SNR item will flash (in db)
     228             :     // @Range: -20 10
     229             :     // @User: Standard
     230             :     AP_GROUPINFO("_W_SNR", 34, AP_OSD, warn_snr, 0),
     231             : #endif
     232             : 
     233             : #if HAL_OSD_SIDEBAR_ENABLE
     234             :     // @Param: _SB_H_OFS
     235             :     // @DisplayName: Sidebar horizontal offset
     236             :     // @Description: Extends the spacing between the sidebar elements by this amount of columns. Positive values increases the width to the right of the screen.
     237             :     // @Range: 0 20
     238             :     // @User: Standard
     239             :     AP_GROUPINFO("_SB_H_OFS", 35, AP_OSD, sidebar_h_offset, 0),
     240             : 
     241             :     // @Param: _SB_V_EXT
     242             :     // @DisplayName: Sidebar vertical extension
     243             :     // @Description: Increase of vertical length of the sidebar itens by this amount of lines. Applied equally both above and below the default setting.
     244             :     // @Range: 0 10
     245             :     // @User: Standard
     246             :     AP_GROUPINFO("_SB_V_EXT", 36, AP_OSD, sidebar_v_ext, 0),
     247             : #endif // HAL_OSD_SIDEBAR_ENABLE
     248             : 
     249             : #endif //osd enabled
     250             : #if OSD_PARAM_ENABLED
     251             :     // @Group: 5_
     252             :     // @Path: AP_OSD_ParamScreen.cpp
     253             :     AP_SUBGROUPINFO(param_screen[0], "5_", 21, AP_OSD, AP_OSD_ParamScreen),
     254             : 
     255             :     // @Group: 6_
     256             :     // @Path: AP_OSD_ParamScreen.cpp
     257             :     AP_SUBGROUPINFO(param_screen[1], "6_", 22, AP_OSD, AP_OSD_ParamScreen),
     258             : #endif
     259             : 
     260             : #if OSD_ENABLED
     261             :     // additional tables to go beyond 63 limit
     262             :     AP_SUBGROUPINFO2(screen[0], "1_", 27, AP_OSD, AP_OSD_Screen),
     263             :     AP_SUBGROUPINFO2(screen[1], "2_", 28, AP_OSD, AP_OSD_Screen),
     264             :     AP_SUBGROUPINFO2(screen[2], "3_", 29, AP_OSD, AP_OSD_Screen),
     265             :     AP_SUBGROUPINFO2(screen[3], "4_", 30, AP_OSD, AP_OSD_Screen),
     266             : #endif
     267             : 
     268             :     // @Param: _TYPE2
     269             :     // @DisplayName: OSD type 2
     270             :     // @Description: OSD type 2. TXONLY makes the OSD parameter selection available to other modules even if there is no native OSD support on the board, for instance CRSF.
     271             :     // @Values: 0:None,1:MAX7456,2:SITL,3:MSP,4:TXONLY,5:MSP_DISPLAYPORT
     272             :     // @User: Standard
     273             :     // @RebootRequired: True
     274             :     AP_GROUPINFO("_TYPE2", 32, AP_OSD, osd_type2, 0),
     275             : 
     276             :     AP_GROUPEND
     277             : };
     278             : 
     279             : extern const AP_HAL::HAL& hal;
     280             : 
     281             : // singleton instance
     282             : AP_OSD *AP_OSD::_singleton;
     283             : 
     284       10305 : AP_OSD::AP_OSD()
     285             : {
     286        2061 :     if (_singleton != nullptr) {
     287           0 :         AP_HAL::panic("AP_OSD must be singleton");
     288             :     }
     289        2061 :     AP_Param::setup_object_defaults(this, var_info);
     290             : #if OSD_ENABLED
     291             :     // force first screen enabled
     292        2061 :     screen[0].enabled.set_and_default(1);
     293        2061 :     previous_pwm_screen = -1;
     294             : #endif
     295             : #ifdef WITH_SITL_OSD
     296             :     osd_type.set_default(OSD_SITL);
     297             : #endif
     298             : 
     299             : #ifdef HAL_OSD_TYPE_DEFAULT
     300             :     osd_type.set_default(HAL_OSD_TYPE_DEFAULT);
     301             : #endif
     302        2061 :     _singleton = this;
     303        2061 : }
     304             : 
     305        2061 : void AP_OSD::init()
     306             : {
     307             :     const AP_OSD::osd_types types[OSD_MAX_INSTANCES] = {
     308        2061 :         osd_types(osd_type.get()),
     309        4122 :         osd_types(osd_type2.get())
     310        2061 :     };
     311        6183 :     for (uint8_t instance = 0; instance < OSD_MAX_INSTANCES; instance++) {
     312        4122 :         if (init_backend(types[instance], instance)) {
     313           2 :             _backend_count++;
     314             :         }
     315             :     }
     316        2061 :     if (_backend_count > 0) {
     317           2 :         hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_OSD::osd_thread, void), "OSD", 1280, AP_HAL::Scheduler::PRIORITY_IO, 1);
     318             :     }
     319        2061 : }
     320             : 
     321        4122 : bool AP_OSD::init_backend(const AP_OSD::osd_types type, const uint8_t instance)
     322             : {
     323             :     // don't re-initialize if backend already exists
     324        4122 :     if (_backends[instance] != nullptr) {
     325           0 :         return false;
     326             :     }
     327             : 
     328             :     // check if we can run this backend instance in parallel with backend instance 0
     329        4122 :     if (instance > 0) {
     330        2061 :         if (_backends[0] && !_backends[0]->is_compatible_with_backend_type(type)) {
     331           0 :             return false;
     332             :         }
     333             :     }
     334        4122 :     switch (type) {
     335        4120 :     case OSD_NONE:
     336             :     case OSD_TXONLY:
     337             :     default:
     338        4120 :         break;
     339             : 
     340           0 :     case OSD_MAX7456: {
     341             : #ifdef HAL_WITH_SPI_OSD
     342             :         AP_HAL::OwnPtr<AP_HAL::Device> spi_dev = std::move(hal.spi->get_device("osd"));
     343             :         if (!spi_dev) {
     344             :             break;
     345             :         }
     346             : #if HAL_WITH_OSD_BITMAP
     347             :         _backends[instance] = AP_OSD_MAX7456::probe(*this, std::move(spi_dev));
     348             : #endif
     349             :         if (_backends[instance] == nullptr) {
     350             :             break;
     351             :         }
     352             :         DEV_PRINTF("Started MAX7456 OSD\n");
     353             : #endif
     354           0 :         break;
     355             :     }
     356             : 
     357             : #ifdef WITH_SITL_OSD
     358             :     case OSD_SITL: {
     359             :         _backends[instance] = AP_OSD_SITL::probe(*this);
     360             :         if (_backends[instance] == nullptr) {
     361             :             break;
     362             :         }
     363             :         DEV_PRINTF("Started SITL OSD\n");
     364             :         break;
     365             :     }
     366             : #endif
     367           0 :     case OSD_MSP: {
     368           0 :         _backends[instance] = AP_OSD_MSP::probe(*this);
     369           0 :         if (_backends[instance] == nullptr) {
     370           0 :             break;
     371             :         }
     372           0 :         DEV_PRINTF("Started MSP OSD\n");
     373           0 :         break;
     374             :     }
     375             : #if HAL_WITH_MSP_DISPLAYPORT
     376           2 :     case OSD_MSP_DISPLAYPORT: {
     377           2 :         _backends[instance] = AP_OSD_MSP_DisplayPort::probe(*this);
     378           2 :         if (_backends[instance] == nullptr) {
     379           0 :             break;
     380             :         }
     381           2 :         DEV_PRINTF("Started MSP DisplayPort OSD\n");
     382           2 :         break;
     383             :     }
     384             : #endif
     385             :     }
     386             : #if OSD_ENABLED
     387        4122 :     if (_backends[instance] != nullptr) {
     388             :         // populate the fonts lookup table
     389           2 :         _backends[instance]->init_symbol_set(AP_OSD_AbstractScreen::symbols_lookup_table, AP_OSD_NUM_SYMBOLS);
     390           2 :         return true;
     391             :     }
     392             : #endif
     393        4120 :     return false;
     394             : }
     395             : 
     396             : #if OSD_ENABLED
     397           2 : void AP_OSD::osd_thread()
     398             : {
     399             :     // initialize thread specific code once
     400           4 :     for (uint8_t instance = 0; instance < _backend_count; instance++) {
     401           2 :         _backends[instance]->osd_thread_run_once();
     402             :     }
     403             : 
     404             : 
     405             :     while (true) {
     406         550 :         hal.scheduler->delay(100);
     407         548 :         if (!_disable) {
     408         548 :             update_stats();
     409         548 :             update_current_screen();
     410             :         }
     411             : #if AP_SCRIPTING_ENABLED
     412             :         // hold the semaphore so scripting cannot draw to the OSD while the
     413             :         // OSD thread is updating it, and vice versa
     414         548 :         WITH_SEMAPHORE(_sem);
     415         548 :         if (!scripting_override) {
     416         548 :             update_osd();
     417             :         } else {
     418           0 :             override_count++;
     419           0 :             if (override_count > 20) {
     420             :                 // timeout after 2 seconds and return to normal OSD
     421           0 :                 scripting_override = false;
     422           0 :                 override_count = 0;
     423             :             }
     424             :         }
     425             : #else
     426             :         update_osd();
     427             : #endif // AP_SCRIPTING_ENABLED
     428         548 :     }
     429             : }
     430             : 
     431         548 : void AP_OSD::update_osd()
     432             : {
     433        1096 :     for (uint8_t instance = 0; instance < _backend_count; instance++) {
     434         548 :         _backends[instance]->clear();
     435             : 
     436         548 :         if (!_disable) {
     437         548 :             get_screen(current_screen).set_backend(_backends[instance]);
     438             :             // skip drawing for MSP OSD backends to save some resources
     439         548 :             if (_backends[instance]->get_backend_type() != OSD_MSP) {
     440         548 :                 get_screen(current_screen).draw();
     441             :             }
     442             :         }
     443             : 
     444         548 :         _backends[instance]->flush();
     445             :     }
     446         548 : }
     447             : 
     448             : //update maximums and totals
     449         548 : void AP_OSD::update_stats()
     450             : {
     451             :     // allow other threads to consume stats info
     452         548 :     WITH_SEMAPHORE(_sem);
     453             : 
     454         548 :     uint32_t now = AP_HAL::millis();
     455         548 :     if (!AP_Notify::flags.armed) {
     456         548 :         _stats.last_update_ms = now;
     457         548 :         return;
     458             :     }
     459             : 
     460             :     // flight distance
     461           0 :     uint32_t delta_ms = now - _stats.last_update_ms;
     462           0 :     _stats.last_update_ms = now;
     463             : 
     464           0 :     Vector2f v;
     465           0 :     Location loc {};
     466           0 :     Location home_loc;
     467             :     bool home_is_set;
     468             :     bool have_airspeed_estimate;
     469             :     float alt;
     470           0 :     float aspd_mps = 0.0f;
     471             :     {
     472             :         // minimize semaphore scope
     473           0 :         AP_AHRS &ahrs = AP::ahrs();
     474           0 :         WITH_SEMAPHORE(ahrs.get_semaphore());
     475           0 :         v = ahrs.groundspeed_vector();
     476           0 :         home_is_set = ahrs.get_location(loc) && ahrs.home_is_set();
     477           0 :         if (home_is_set) {
     478           0 :             home_loc = ahrs.get_home();
     479             :         }
     480           0 :         ahrs.get_relative_position_D_home(alt);
     481           0 :         have_airspeed_estimate = ahrs.airspeed_EAS(aspd_mps);
     482           0 :     }
     483           0 :     float speed = v.length();
     484           0 :     if (speed < 0.178) {
     485           0 :         speed = 0.0;
     486             :     }
     487           0 :     float dist_m = (speed * delta_ms)*0.001;
     488           0 :     _stats.last_distance_m += dist_m;
     489             : 
     490             :     // maximum ground speed
     491           0 :     _stats.max_speed_mps = fmaxf(_stats.max_speed_mps,speed);
     492             : 
     493             :     // maximum distance
     494           0 :     if (home_is_set) {
     495           0 :         float distance = home_loc.get_distance(loc);
     496           0 :         _stats.max_dist_m = fmaxf(_stats.max_dist_m, distance);
     497             :     }
     498             : 
     499             :     // maximum altitude
     500           0 :     alt = -alt;
     501           0 :     _stats.max_alt_m = fmaxf(_stats.max_alt_m, alt);
     502             : #if AP_BATTERY_ENABLED
     503             :     // maximum current
     504           0 :     AP_BattMonitor &battery = AP::battery();
     505             :     float amps;
     506           0 :     if (battery.current_amps(amps)) {
     507           0 :         _stats.max_current_a = fmaxf(_stats.max_current_a, amps);
     508             :     }
     509             :     // minimum voltage
     510           0 :     float voltage = battery.voltage();
     511           0 :     if (voltage > 0) {
     512           0 :         _stats.min_voltage_v = fminf(_stats.min_voltage_v, voltage);
     513             :     }
     514             : #endif
     515             : #if AP_RSSI_ENABLED
     516             :     // minimum rssi
     517           0 :     AP_RSSI *ap_rssi = AP_RSSI::get_singleton();
     518           0 :     if (ap_rssi) {
     519           0 :         _stats.min_rssi = fminf(_stats.min_rssi, ap_rssi->read_receiver_rssi());
     520             :     }
     521             : #endif
     522             :     // max airspeed either true or synthetic
     523           0 :     if (have_airspeed_estimate) {
     524           0 :         _stats.max_airspeed_mps = fmaxf(_stats.max_airspeed_mps, aspd_mps);
     525             :     }
     526             : #if HAL_WITH_ESC_TELEM
     527             :     // max esc temp
     528           0 :     AP_ESC_Telem& telem = AP::esc_telem();
     529           0 :     int16_t highest_temperature = 0;
     530           0 :     telem.get_highest_temperature(highest_temperature);
     531           0 :     _stats.max_esc_temp = MAX(_stats.max_esc_temp, highest_temperature);
     532             : #endif
     533         548 : }
     534             : 
     535             : //Thanks to minimosd authors for the multiple osd screen idea
     536         548 : void AP_OSD::update_current_screen()
     537             : {
     538             :     // Switch on ARM/DISARM event
     539         548 :     if (AP_Notify::flags.armed) {
     540           0 :         if (!was_armed && arm_scr > 0 && arm_scr <= AP_OSD_NUM_DISPLAY_SCREENS && get_screen(arm_scr-1).enabled) {
     541           0 :             current_screen = arm_scr-1;
     542             :         }
     543           0 :         was_armed = true;
     544         548 :     } else if (was_armed) {
     545           0 :         if (disarm_scr > 0 && disarm_scr <= AP_OSD_NUM_DISPLAY_SCREENS && get_screen(disarm_scr-1).enabled) {
     546           0 :             current_screen = disarm_scr-1;
     547             :         }
     548           0 :         was_armed = false;
     549             :     }
     550             : 
     551             :     // Switch on failsafe event
     552         548 :     if (AP_Notify::flags.failsafe_radio || AP_Notify::flags.failsafe_battery) {
     553           0 :         if (!was_failsafe && failsafe_scr > 0 && failsafe_scr <= AP_OSD_NUM_DISPLAY_SCREENS && get_screen(failsafe_scr-1).enabled) {
     554           0 :             pre_fs_screen = current_screen;
     555           0 :             current_screen = failsafe_scr-1;
     556             :         }
     557           0 :         was_failsafe = true;
     558         548 :     } else if (was_failsafe) {
     559           0 :         if (get_screen(pre_fs_screen).enabled) {
     560           0 :             current_screen = pre_fs_screen;
     561             :         }
     562           0 :         was_failsafe = false;
     563             :     }
     564             : 
     565         548 :     if (rc_channel == 0) {
     566         548 :         return;
     567             :     }
     568             : 
     569             : #if AP_RC_CHANNEL_ENABLED
     570           0 :     RC_Channel *channel = rc().channel(rc_channel-1);
     571           0 :     if (channel == nullptr) {
     572           0 :         return;
     573             :     }
     574             : 
     575           0 :     int16_t channel_value = channel->get_radio_in();
     576           0 :     switch (sw_method) {
     577             :     //switch to next screen if channel value was changed
     578           0 :     default:
     579             :     case TOGGLE:
     580           0 :         if (previous_channel_value == 0) {
     581             :             //do not switch to the next screen just after initialization
     582           0 :             previous_channel_value = channel_value;
     583             :         }
     584           0 :         if (abs(channel_value-previous_channel_value) > 200) {
     585           0 :             if (switch_debouncer) {
     586           0 :                 next_screen();
     587           0 :                 previous_channel_value = channel_value;
     588             :             } else {
     589           0 :                 switch_debouncer = true;
     590           0 :                 return;
     591             :             }
     592             :         }
     593           0 :         break;
     594             :     //select screen based on pwm ranges specified
     595           0 :     case PWM_RANGE:
     596           0 :         for (int i=0; i<AP_OSD_NUM_SCREENS; i++) {
     597           0 :             if (get_screen(i).enabled && get_screen(i).channel_min <= channel_value && get_screen(i).channel_max > channel_value) {
     598           0 :                 if (previous_pwm_screen == i) {
     599           0 :                     break;
     600             :                 } else {
     601           0 :                 current_screen = previous_pwm_screen = i;
     602             :                 }
     603             :             }
     604             :         }
     605           0 :         break;
     606             :     //switch to next screen after low to high transition and every 1s while channel value is high
     607           0 :     case AUTO_SWITCH:
     608           0 :         if (channel_value > channel->get_radio_trim()) {
     609           0 :             if (switch_debouncer) {
     610           0 :                 uint32_t now = AP_HAL::millis();
     611           0 :                 if (now - last_switch_ms > 1000) {
     612           0 :                     next_screen();
     613           0 :                     last_switch_ms = now;
     614             :                 }
     615             :             } else {
     616           0 :                 switch_debouncer = true;
     617           0 :                 return;
     618             :             }
     619             :         } else {
     620           0 :             last_switch_ms = 0;
     621             :         }
     622           0 :         break;
     623             :     }
     624           0 :     switch_debouncer = false;
     625             : #endif  // AP_RC_CHANNEL_ENABLED
     626             : }
     627             : 
     628             : //select next avaliable screen, do nothing if all screens disabled
     629           0 : void AP_OSD::next_screen()
     630             : {
     631           0 :     uint8_t t = current_screen;
     632             :     do {
     633           0 :         t = (t + 1)%AP_OSD_NUM_SCREENS;
     634           0 :     } while (t != current_screen && !get_screen(t).enabled);
     635           0 :     current_screen = t;
     636           0 : }
     637             : 
     638             : // set navigation information for display
     639      132557 : void AP_OSD::set_nav_info(NavInfo &navinfo)
     640             : {
     641             :     // do this without a lock for now
     642      132557 :     nav_info = navinfo;
     643      132557 : }
     644             : 
     645             : // pre_arm_check - returns true if all pre-takeoff checks have completed successfully
     646      131378 : bool AP_OSD::pre_arm_check(char *failure_msg, const uint8_t failure_msg_len) const
     647             : {
     648             : #if OSD_PARAM_ENABLED
     649             :     // currently in the OSD menu, do not allow arming
     650      131378 :     if (!is_readonly_screen()) {
     651           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "In OSD menu");
     652           0 :         return false;
     653             :     }
     654             : #endif  
     655             : 
     656             :     //check if second backend was requested by user but not instantiated
     657      131378 :     if (osd_type.get() != OSD_NONE && _backend_count == 1 && osd_type2.get() != OSD_NONE) {
     658           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "OSD_TYPE2 not compatible with first OSD");
     659           0 :         return false; 
     660             :     }
     661             : 
     662             :     // if we got this far everything must be ok
     663      131378 :     return true;
     664             : }
     665             : 
     666             : #endif // OSD_ENABLED
     667             : 
     668             : // handle OSD parameter configuration
     669             : #if HAL_GCS_ENABLED
     670           0 : void AP_OSD::handle_msg(const mavlink_message_t &msg, const GCS_MAVLINK& link)
     671             : {
     672           0 :     bool found = false;
     673             : 
     674           0 :     switch (msg.msgid) {
     675           0 :     case MAVLINK_MSG_ID_OSD_PARAM_CONFIG: {
     676             :         mavlink_osd_param_config_t packet;
     677           0 :         mavlink_msg_osd_param_config_decode(&msg, &packet);
     678             : #if OSD_PARAM_ENABLED
     679           0 :         for (uint8_t i = 0; i < AP_OSD_NUM_PARAM_SCREENS; i++) {
     680           0 :             if (packet.osd_screen == i + AP_OSD_NUM_DISPLAY_SCREENS + 1) {
     681           0 :                 param_screen[i].handle_write_msg(packet, link);
     682           0 :                 found = true;
     683             :             }
     684             :         }
     685             : #endif
     686             :         // send back an error
     687           0 :         if (!found) {
     688           0 :             mavlink_msg_osd_param_config_reply_send(link.get_chan(), packet.request_id, OSD_PARAM_INVALID_SCREEN);
     689             :         }
     690             :     }
     691           0 :         break;
     692           0 :     case MAVLINK_MSG_ID_OSD_PARAM_SHOW_CONFIG: {
     693             :         mavlink_osd_param_show_config_t packet;
     694           0 :         mavlink_msg_osd_param_show_config_decode(&msg, &packet);
     695             : #if OSD_PARAM_ENABLED
     696           0 :         for (uint8_t i = 0; i < AP_OSD_NUM_PARAM_SCREENS; i++) {
     697           0 :             if (packet.osd_screen == i + AP_OSD_NUM_DISPLAY_SCREENS + 1) {
     698           0 :                 param_screen[i].handle_read_msg(packet, link);
     699           0 :                 found = true;
     700             :             }
     701             :         }
     702             : #endif
     703             :         // send back an error
     704           0 :         if (!found) {
     705           0 :             mavlink_msg_osd_param_show_config_reply_send(link.get_chan(), packet.request_id, OSD_PARAM_INVALID_SCREEN,
     706             :                 nullptr, OSD_PARAM_NONE, 0, 0, 0);
     707             :         }
     708             :     }
     709           0 :         break;
     710           0 :     default:
     711           0 :         break;
     712             :     }
     713           0 : }
     714             : #endif
     715             : 
     716      310843 : AP_OSD *AP::osd() {
     717      310843 :     return AP_OSD::get_singleton();
     718             : }
     719             : 
     720             : #endif // OSD_ENABLED || OSD_PARAM_ENABLED

Generated by: LCOV version 1.14