LCOV - code coverage report
Current view: top level - AP_Param - AP_Param.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1215 1518 80.0 %
Date: 2026-09-09 11:02:08 Functions: 87 94 92.6 %

          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             : //
      17             : //
      18             : 
      19             : // total up and check overflow
      20             : // check size of group var_info
      21             : 
      22             : /// @file   AP_Param.cpp
      23             : /// @brief  The AP variable store.
      24             : #include "AP_Param.h"
      25             : 
      26             : #include <cmath>
      27             : #include <string.h>
      28             : 
      29             : #include <AP_Common/AP_Common.h>
      30             : #include <AP_HAL/AP_HAL.h>
      31             : #include <AP_Math/AP_Math.h>
      32             : #include <GCS_MAVLink/GCS.h>
      33             : #include <StorageManager/StorageManager.h>
      34             : #include <AP_BoardConfig/AP_BoardConfig.h>
      35             : #include <AP_InternalError/AP_InternalError.h>
      36             : #include <AP_Filesystem/AP_Filesystem.h>
      37             : #include <stdio.h>
      38             : #include <AP_ROMFS/AP_ROMFS.h>
      39             : 
      40             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
      41             :     #include <SITL/SITL.h>
      42             : #endif
      43             : 
      44             : #include "AP_Param_config.h"
      45             : 
      46             : extern const AP_HAL::HAL &hal;
      47             : 
      48             : uint16_t AP_Param::sentinel_offset;
      49             : 
      50             : // singleton instance
      51             : AP_Param *AP_Param::_singleton;
      52             : 
      53             : #ifndef AP_PARAM_STORAGE_BAK_ENABLED
      54             : // we only have a storage region for backup storage if we have at
      55             : // least 32768 bytes or storage. We also don't enable when using flash
      56             : // storage as this can lead to loss of storage when updating to a
      57             : // larger storage size
      58             : #define AP_PARAM_STORAGE_BAK_ENABLED (HAL_STORAGE_SIZE>=32768) && !defined(STORAGE_FLASH_PAGE)
      59             : #endif
      60             : 
      61             : 
      62             : #define ENABLE_DEBUG 0
      63             : 
      64             : #if ENABLE_DEBUG
      65             : # define FATAL(fmt, args ...) AP_HAL::panic(fmt, ## args);
      66             :  # define Debug(fmt, args ...)  do {::printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)
      67             : #else
      68             :  # define FATAL(fmt, args ...) AP_HAL::panic("Bad parameter table");
      69             :  # define Debug(fmt, args ...)
      70             : #endif
      71             : 
      72             : #if HAL_GCS_ENABLED
      73             : #define GCS_SEND_PARAM(name, type, v) gcs().send_parameter_value(name, type, v)
      74             : #else
      75             : #define GCS_SEND_PARAM(name, type, v)
      76             : #endif
      77             : 
      78             : // Note about AP_Vector3f handling.
      79             : // The code has special cases for AP_Vector3f to allow it to be viewed
      80             : // as both a single 3 element vector and as a set of 3 AP_Float
      81             : // variables. This is done to make it possible for MAVLink to see
      82             : // vectors as parameters, which allows users to save their compass
      83             : // offsets in MAVLink parameter files. The code involves quite a few
      84             : // special cases which could be generalised to any vector/matrix type
      85             : // if we end up needing this behaviour for other than AP_Vector3f
      86             : 
      87             : 
      88             : // static member variables for AP_Param.
      89             : //
      90             : 
      91             : // number of rows in the _var_info[] table
      92             : uint16_t AP_Param::_num_vars;
      93             : 
      94             : #if AP_PARAM_DYNAMIC_ENABLED
      95             : uint16_t AP_Param::_num_vars_base;
      96             : AP_Param::Info *AP_Param::_var_info_dynamic;
      97             : static const char *_empty_string = "";
      98             : uint8_t AP_Param::_dynamic_table_sizes[AP_PARAM_MAX_DYNAMIC];
      99             : #endif
     100             : 
     101             : // cached parameter count
     102             : uint16_t AP_Param::_parameter_count;
     103             : uint16_t AP_Param::_count_marker;
     104             : uint16_t AP_Param::_count_marker_done;
     105             : HAL_Semaphore AP_Param::_count_sem;
     106             : 
     107             : // storage and naming information about all types that can be saved
     108             : const AP_Param::Info *AP_Param::_var_info;
     109             : 
     110             : struct AP_Param::param_override *AP_Param::param_overrides;
     111             : uint16_t AP_Param::param_overrides_len;
     112             : uint16_t AP_Param::num_param_overrides;
     113             : uint16_t AP_Param::num_read_only;
     114             : 
     115             : // goes true if we run out of param space
     116             : bool AP_Param::eeprom_full;
     117             : 
     118             : ObjectBuffer_TS<AP_Param::param_save> AP_Param::save_queue{30};
     119             : bool AP_Param::registered_save_handler;
     120             : 
     121             : bool AP_Param::done_all_default_params;
     122             : 
     123             : AP_Param::defaults_list *AP_Param::default_list;
     124             : 
     125             : // we need a dummy object for the parameter save callback
     126             : static AP_Param save_dummy;
     127             : 
     128             : #if AP_PARAM_MAX_EMBEDDED_PARAM > 0
     129             : /*
     130             :   this holds default parameters in the normal NAME=value form for a
     131             :   parameter file. It can be manipulated by apj_tool.py to change the
     132             :   defaults on a binary without recompiling
     133             :  */
     134             : const AP_Param::param_defaults_struct AP_Param::param_defaults_data = {
     135             :     "PARMDEF",
     136             :     { 0x55, 0x37, 0xf4, 0xa0, 0x38, 0x5d, 0x48, 0x5b },
     137             :     AP_PARAM_MAX_EMBEDDED_PARAM,
     138             :     0
     139             : };
     140             : #endif
     141             : 
     142             : // storage object
     143             : StorageAccess AP_Param::_storage(StorageManager::StorageParam);
     144             : 
     145             : #if AP_PARAM_STORAGE_BAK_ENABLED
     146             : // backup storage object
     147             : StorageAccess AP_Param::_storage_bak(StorageManager::StorageParamBak);
     148             : #endif
     149             : 
     150             : // flags indicating frame type
     151             : uint16_t AP_Param::_frame_type_flags;
     152             : 
     153             : // write to EEPROM
     154       99650 : void AP_Param::eeprom_write_check(const void *ptr, uint16_t ofs, uint8_t size)
     155             : {
     156       99650 :     _storage.write_block(ofs, ptr, size);
     157             : #if AP_PARAM_STORAGE_BAK_ENABLED
     158             :     _storage_bak.write_block(ofs, ptr, size);
     159             : #endif
     160       99650 : }
     161             : 
     162             : bool AP_Param::_hide_disabled_groups = true;
     163             : 
     164             : // write a sentinel value at the given offset
     165       14602 : void AP_Param::write_sentinel(uint16_t ofs)
     166             : {
     167             :     struct Param_header phdr;
     168       14602 :     phdr.type = _sentinel_type;
     169       14602 :     set_key(phdr, _sentinel_key);
     170       14602 :     phdr.group_element = _sentinel_group;
     171       14602 :     eeprom_write_check(&phdr, ofs, sizeof(phdr));
     172       14602 :     sentinel_offset = ofs;
     173       14602 : }
     174             : 
     175             : // erase all EEPROM variables by re-writing the header and adding
     176             : // a sentinel
     177         600 : void AP_Param::erase_all(void)
     178             : {
     179             :     struct EEPROM_header hdr;
     180             : 
     181             :     // write the header
     182         600 :     hdr.magic[0] = k_EEPROM_magic0;
     183         600 :     hdr.magic[1] = k_EEPROM_magic1;
     184         600 :     hdr.revision = k_EEPROM_revision;
     185         600 :     hdr.spare    = 0;
     186         600 :     eeprom_write_check(&hdr, 0, sizeof(hdr));
     187             : 
     188             :     // add a sentinel directly after the header
     189         600 :     write_sentinel(sizeof(struct EEPROM_header));
     190         600 : }
     191             : 
     192             : /* the 'group_id' of a element of a group is the 18 bit identifier
     193             :    used to distinguish between this element of the group and other
     194             :    elements of the same group. It is calculated using a bit shift per
     195             :    level of nesting, so the first level of nesting gets 6 bits the 2nd
     196             :    level gets the next 6 bits, and the 3rd level gets the last 6
     197             :    bits. This limits groups to having at most 64 elements.
     198             : */
     199  2768329221 : uint32_t AP_Param::group_id(const struct GroupInfo *grpinfo, uint32_t base, uint8_t i, uint8_t shift)
     200             : {
     201  2768329221 :     if (grpinfo[i].idx == 0 && shift != 0 && !(grpinfo[i].flags & AP_PARAM_FLAG_NO_SHIFT)) {
     202             :         /*
     203             :           this is a special case for a bug in the original design. An
     204             :           idx of 0 shifted by n bits is still zero, which makes it
     205             :           indistinguishable from a different parameter. This can lead
     206             :           to parameter loops. We use index 63 for that case.
     207             :         */
     208    20761005 :         return base + (63U<<shift);
     209             :     }
     210  2747568216 :     return base + (grpinfo[i].idx<<shift);
     211             : }
     212             : 
     213             : 
     214             : /*
     215             :   check if a frame type should be included. A frame is included if
     216             :   either there are no frame type flags on a parameter or if at least
     217             :   one of the flags has been set by set_frame_type_flags()
     218             :  */
     219  2627271543 : bool AP_Param::check_frame_type(uint16_t flags)
     220             : {
     221  2627271543 :     if (flags & AP_PARAM_FLAG_HIDDEN) {
     222             :         // hidden on all frames
     223     1503302 :         return false;
     224             :     }
     225  2625768241 :     uint16_t frame_flags = flags >> AP_PARAM_FRAME_TYPE_SHIFT;
     226  2625768241 :     if (frame_flags == 0) {
     227  2607195732 :         return true;
     228             :     }
     229    18572509 :     return (frame_flags & _frame_type_flags) != 0;
     230             : }
     231             : 
     232             : // validate a group info table
     233     1134045 : void AP_Param::check_group_info(const struct AP_Param::GroupInfo *  group_info,
     234             :                                 uint16_t *                          total_size,
     235             :                                 uint8_t                             group_shift,
     236             :                                 uint8_t                             prefix_length)
     237             : {
     238             :     uint8_t type;
     239     1134045 :     uint64_t used_mask = 0;
     240     9041991 :     for (uint8_t i=0;
     241     9041991 :          (type=group_info[i].type) != AP_PARAM_NONE;
     242             :          i++) {
     243     7907946 :         uint8_t idx = group_info[i].idx;
     244     7907946 :         if (idx >= (1<<_group_level_shift)) {
     245           0 :             FATAL("idx too large (%u) in %s", idx, group_info[i].name);
     246             :         }
     247     7907946 :         if (group_shift != 0 && idx == 0) {
     248             :             // treat idx 0 as 63 for duplicates. See group_id()
     249       57893 :             idx = 63;
     250             :         }
     251     7907946 :         if (used_mask & (1ULL<<idx)) {
     252           0 :             FATAL("Duplicate group idx %u for %s", idx, group_info[i].name);
     253             :         }
     254     7907946 :         used_mask |= (1ULL<<idx);
     255     7907946 :         if (type == AP_PARAM_GROUP) {
     256             :             // a nested group
     257     1158670 :             if (group_shift + _group_level_shift >= _group_bits) {
     258           0 :                 FATAL("double group nesting in %s", group_info[i].name);
     259             :             }
     260     1158670 :             const struct GroupInfo *ginfo = get_group_info(group_info[i]);
     261     1158670 :             if (ginfo == nullptr) {
     262      110665 :                 continue;
     263             :             }
     264     1048005 :             check_group_info(ginfo, total_size, group_shift + _group_level_shift, prefix_length + strlen(group_info[i].name));
     265     1048005 :             continue;
     266     1048005 :         }
     267     6749276 :         uint8_t size = type_size((enum ap_var_type)type);
     268     6749276 :         if (size == 0) {
     269           0 :             FATAL("invalid type in %s", group_info[i].name);
     270             :         }
     271     6749276 :         uint8_t param_name_length = prefix_length + strlen(group_info[i].name);
     272     6749276 :         if (type == AP_PARAM_VECTOR3F) {
     273             :             // need room for _X/_Y/_Z
     274      274157 :             param_name_length += 2;
     275             :         }
     276     6749276 :         if (param_name_length > 16) {
     277           0 :             FATAL("suffix is too long in %s (%u > 16)", group_info[i].name, param_name_length);
     278             :         }
     279     6749276 :         (*total_size) += size + sizeof(struct Param_header);
     280             :     }
     281     1134045 : }
     282             : 
     283             : // check for duplicate key values
     284      201816 : bool AP_Param::duplicate_key(uint16_t vindex, uint16_t key)
     285             : {
     286    10892011 :     for (uint16_t i=vindex+1; i<_num_vars; i++) {
     287    10690195 :         uint16_t key2 = var_info(i).key;
     288    10690195 :         if (key2 == key) {
     289             :             // no duplicate keys allowed
     290           0 :             return true;
     291             :         }
     292             :     }
     293      201816 :     return false;
     294             : }
     295             : 
     296             : /*
     297             :   get group_info pointer for a group
     298             :  */
     299   540116536 : const struct AP_Param::GroupInfo *AP_Param::get_group_info(const struct GroupInfo &ginfo)
     300             : {
     301   540116536 :     if (ginfo.flags & AP_PARAM_FLAG_INFO_POINTER) {
     302    41767198 :         return *ginfo.group_info_ptr;
     303             :     }
     304   498349338 :     return ginfo.group_info;
     305             : }
     306             : 
     307             : /*
     308             :   get group_info pointer for a group
     309             :  */
     310    44833089 : const struct AP_Param::GroupInfo *AP_Param::get_group_info(const struct Info &info)
     311             : {
     312    44833089 :     if (info.flags & AP_PARAM_FLAG_INFO_POINTER) {
     313     1023197 :         return *info.group_info_ptr;
     314             :     }
     315    43809892 :     return info.group_info;
     316             : }
     317             : 
     318             : // validate the _var_info[] table
     319        2094 : void AP_Param::check_var_info(void)
     320             : {
     321        2094 :     uint16_t total_size = sizeof(struct EEPROM_header);
     322             : 
     323      205912 :     for (uint16_t i=0; i<_num_vars; i++) {
     324      203818 :         const auto &info = var_info(i);
     325      203818 :         uint8_t type = info.type;
     326      203818 :         uint16_t key = info.key;
     327      203818 :         if (type == AP_PARAM_GROUP) {
     328       88042 :             if (i == 0) {
     329           0 :                 FATAL("first element can't be a group, for first() call");
     330             :             }
     331       88042 :             const struct GroupInfo *group_info = get_group_info(info);
     332       88042 :             if (group_info == nullptr) {
     333        2002 :                 continue;
     334             :             }
     335       86040 :             check_group_info(group_info, &total_size, 0, strlen(info.name));
     336             :         } else {
     337      115776 :             uint8_t size = type_size((enum ap_var_type)type);
     338      115776 :             if (size == 0) {
     339             :                 // not a valid type - the top level list can't contain
     340             :                 // AP_PARAM_NONE
     341           0 :                 FATAL("AP_PARAM_NONE at top level");
     342             :             }
     343      115776 :             total_size += size + sizeof(struct Param_header);
     344             :         }
     345      201816 :         if (duplicate_key(i, key)) {
     346           0 :             FATAL("duplicate key");
     347             :         }
     348      201816 :         if (type != AP_PARAM_GROUP && (info.flags & AP_PARAM_FLAG_POINTER)) {
     349           0 :             FATAL("only groups can be pointers");
     350             :         }
     351             :     }
     352             : 
     353             :     // we no longer check if total_size is larger than _eeprom_size,
     354             :     // as we allow for more variables than could fit, relying on not
     355             :     // saving default values
     356        2094 : }
     357             : 
     358             : 
     359             : // setup the _var_info[] table
     360        2094 : bool AP_Param::setup(void)
     361             : {
     362        2094 :     struct EEPROM_header hdr {};
     363             : 
     364             :     // check the header
     365        2094 :     _storage.read_block(&hdr, 0, sizeof(hdr));
     366             : 
     367             : #if AP_PARAM_STORAGE_BAK_ENABLED
     368             :     struct EEPROM_header hdr2 {};
     369             :     _storage_bak.read_block(&hdr2, 0, sizeof(hdr2));
     370             : #endif
     371             : 
     372        2094 :     if (hdr.magic[0] != k_EEPROM_magic0 ||
     373        2093 :         hdr.magic[1] != k_EEPROM_magic1 ||
     374        2093 :         hdr.revision != k_EEPROM_revision) {
     375             : #if AP_PARAM_STORAGE_BAK_ENABLED
     376             :         if (hdr2.magic[0] == k_EEPROM_magic0 &&
     377             :             hdr2.magic[1] == k_EEPROM_magic1 &&
     378             :             hdr2.revision == k_EEPROM_revision &&
     379             :             _storage.copy_area(_storage_bak)) {
     380             :             // restored from backup
     381             :             INTERNAL_ERROR(AP_InternalError::error_t::params_restored);
     382             :             return true;
     383             :         }
     384             : #endif // AP_PARAM_STORAGE_BAK_ENABLED
     385             :         // header doesn't match. We can't recover any variables. Wipe
     386             :         // the header and setup the sentinel directly after the header
     387             :         Debug("bad header in setup - erasing");
     388           1 :         erase_all();
     389             :     }
     390             : 
     391             : #if AP_PARAM_STORAGE_BAK_ENABLED
     392             :     // ensure that backup is in sync with primary
     393             :     _storage_bak.copy_area(_storage);
     394             : #endif
     395             : 
     396        2094 :     return true;
     397             : }
     398             : 
     399             : // check if AP_Param has been initialised
     400           0 : bool AP_Param::initialised(void)
     401             : {
     402           0 :     return _var_info != nullptr;
     403             : }
     404             : 
     405             : /*
     406             :   adjust offset of a group element for nested groups and group pointers
     407             : 
     408             :   The new_offset variable is relative to the vindex base. This makes
     409             :   dealing with pointer groups tricky
     410             :  */
     411   500121945 : bool AP_Param::adjust_group_offset(uint16_t vindex, const struct GroupInfo &group_info, ptrdiff_t &new_offset)
     412             : {
     413   500121945 :     if (group_info.flags & AP_PARAM_FLAG_NESTED_OFFSET) {
     414   462280680 :         new_offset += group_info.offset;
     415   462280680 :         return true;
     416             :     }
     417    37841265 :     if (group_info.flags & AP_PARAM_FLAG_POINTER) {
     418             :         // group_info.offset refers to a pointer
     419             :         ptrdiff_t base;
     420    36158632 :         if (!get_base(var_info(vindex), base)) {
     421             :             // the object is not allocated yet
     422    24266705 :             return false;
     423             :         }
     424    36158788 :         void **p = (void **)(base + new_offset + group_info.offset);
     425    36158788 :         if (*p == nullptr) {
     426             :             // the object is not allocated yet
     427    24266705 :             return false;
     428             :         }
     429             :         // calculate offset that is needed to take base object and adjust for this object
     430    11892083 :         new_offset = ((ptrdiff_t)*p) - base;
     431             :     }
     432    13574716 :     return true;
     433             : }
     434             : 
     435             : /*
     436             :   get the base pointer for a variable, accounting for AP_PARAM_FLAG_POINTER
     437             :  */
     438   355569801 : bool AP_Param::get_base(const struct Info &info, ptrdiff_t &base)
     439             : {
     440   355569801 :     if (info.flags & AP_PARAM_FLAG_POINTER) {
     441     7836287 :         base = *(ptrdiff_t *)info.ptr;
     442     7836287 :         return (base != (ptrdiff_t)0);
     443             :     }
     444   347733514 :     base = (ptrdiff_t)info.ptr;
     445   347733514 :     return true;
     446             : }
     447             : 
     448             : namespace AP {
     449             : 
     450           0 : AP_Param *param()
     451             : {
     452           0 :     return AP_Param::get_singleton();
     453             : }
     454             : 
     455             : }
     456             : 
     457             : // find the info structure given a header and a group_info table
     458             : // return the Info structure and a pointer to the variables storage
     459     3212541 : const struct AP_Param::Info *AP_Param::find_by_header_group(struct Param_header phdr, void **ptr,
     460             :                                                             uint16_t vindex,
     461             :                                                             const struct GroupInfo *group_info,
     462             :                                                             uint32_t group_base,
     463             :                                                             uint8_t group_shift,
     464             :                                                             ptrdiff_t group_offset)
     465             : {
     466             :     uint8_t type;
     467    27100503 :     for (uint8_t i=0;
     468    27100503 :          (type=group_info[i].type) != AP_PARAM_NONE;
     469             :          i++) {
     470    25047170 :         if (type == AP_PARAM_GROUP) {
     471             :             // a nested group
     472     4027755 :             if (group_shift + _group_level_shift >= _group_bits) {
     473             :                 // too deeply nested - this should have been caught by
     474             :                 // setup() !
     475      533248 :                 return nullptr;
     476             :             }
     477     4027755 :             const struct GroupInfo *ginfo = get_group_info(group_info[i]);
     478     4027755 :             if (ginfo == nullptr) {
     479     3494507 :                 continue;
     480             :             }
     481     2561323 :             ptrdiff_t new_offset = group_offset;
     482             : 
     483     2561323 :             if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
     484       71860 :                 continue;
     485             :             }
     486             : 
     487     2489463 :             const struct AP_Param::Info *ret = find_by_header_group(phdr, ptr, vindex, ginfo,
     488             :                                                                     group_id(group_info, group_base, i, group_shift),
     489     2489463 :                                                                     group_shift + _group_level_shift, new_offset);
     490     2489463 :             if (ret != nullptr) {
     491      533248 :                 return ret;
     492             :             }
     493     1956215 :             continue;
     494     1956215 :         }
     495    21019415 :         if (group_id(group_info, group_base, i, group_shift) == phdr.group_element && type == phdr.type) {
     496             :             // found a group element
     497             :             ptrdiff_t base;
     498      628070 :             if (!get_base(var_info(vindex), base)) {
     499        2110 :                 continue;
     500             :             }
     501      625960 :             *ptr = (void*)(base + group_info[i].offset + group_offset);
     502      625960 :             return &var_info(vindex);
     503             :         }
     504             :     }
     505     2053333 :     return nullptr;
     506             : }
     507             : 
     508             : // find the info structure given a header
     509             : // return the Info structure and a pointer to the variables storage
     510      731125 : const struct AP_Param::Info *AP_Param::find_by_header(struct Param_header phdr, void **ptr)
     511             : {
     512             :     // loop over all named variables
     513    70878016 :     for (uint16_t i=0; i<_num_vars; i++) {
     514    70874820 :         const auto &info = var_info(i);
     515    70874820 :         uint8_t type = info.type;
     516    70874820 :         uint16_t key = info.key;
     517    70874820 :         if (key != get_key(phdr)) {
     518             :             // not the right key
     519    70144328 :             continue;
     520             :         }
     521      730492 :         if (type == AP_PARAM_GROUP) {
     522      725641 :             const struct GroupInfo *group_info = get_group_info(info);
     523      725641 :             if (group_info == nullptr) {
     524        2563 :                 continue;
     525             :             }
     526      723078 :             return find_by_header_group(phdr, ptr, i, group_info, 0, 0, 0);
     527             :         }
     528        4851 :         if (type == phdr.type) {
     529             :             // found it
     530             :             ptrdiff_t base;
     531        4851 :             if (!get_base(info, base)) {
     532           0 :                 return nullptr;
     533             :             }
     534        4851 :             *ptr = (void*)base;
     535        4851 :             return &info;
     536             :         }
     537             :     }
     538        3196 :     return nullptr;
     539             : }
     540             : 
     541             : // find the info structure for a variable in a group
     542   202509629 : const struct AP_Param::Info *AP_Param::find_var_info_group(const struct GroupInfo * group_info,
     543             :                                                            uint16_t                 vindex,
     544             :                                                            uint32_t                 group_base,
     545             :                                                            uint8_t                  group_shift,
     546             :                                                            ptrdiff_t                group_offset,
     547             :                                                            uint32_t *               group_element,
     548             :                                                            const struct GroupInfo * &group_ret,
     549             :                                                            struct GroupNesting      &group_nesting,
     550             :                                                            uint8_t *                idx) const
     551             : {
     552             :     ptrdiff_t base;
     553   202509629 :     if (!get_base(var_info(vindex), base)) {
     554           0 :         return nullptr;
     555             :     }
     556             :     uint8_t type;
     557  1852218075 :     for (uint8_t i=0;
     558  1852218075 :          (type=group_info[i].type) != AP_PARAM_NONE;
     559             :          i++) {
     560  1659963169 :         ptrdiff_t ofs = group_info[i].offset + group_offset;
     561  1659963169 :         if (type == AP_PARAM_GROUP) {
     562   203125909 :             const struct GroupInfo *ginfo = get_group_info(group_info[i]);
     563   203125049 :             if (ginfo == nullptr) {
     564    24971761 :                 continue;
     565             :             }
     566             :             // a nested group
     567   185820503 :             if (group_shift + _group_level_shift >= _group_bits) {
     568             :                 // too deeply nested - this should have been caught by
     569             :                 // setup() !
     570     4847778 :                 return nullptr;
     571             :             }
     572             :             const struct AP_Param::Info *info;
     573   185820503 :             ptrdiff_t new_offset = group_offset;
     574   185820503 :             if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
     575     7667215 :                 continue;
     576             :             }
     577   178153311 :             if (group_nesting.level >= group_nesting.numlevels) {
     578           0 :                 return nullptr;
     579             :             }
     580   178153311 :             group_nesting.group_ret[group_nesting.level++] = &group_info[i];
     581   178154096 :             info = find_var_info_group(ginfo, vindex,
     582             :                                        group_id(group_info, group_base, i, group_shift),
     583   178153311 :                                        group_shift + _group_level_shift,
     584             :                                        new_offset,
     585             :                                        group_element,
     586             :                                        group_ret,
     587             :                                        group_nesting,
     588             :                                        idx);
     589   178156536 :             if (info != nullptr) {
     590     4847778 :                 return info;
     591             :             }
     592   173308758 :             group_nesting.level--;
     593  1456837260 :         } else if ((ptrdiff_t) this == base + ofs) {
     594     4947235 :             *group_element = group_id(group_info, group_base, i, group_shift);
     595     4947234 :             group_ret = &group_info[i];
     596     4947234 :             *idx = 0;
     597     4947234 :             return &var_info(vindex);
     598  1451890025 :         } else if (type == AP_PARAM_VECTOR3F &&
     599   109292839 :                    (base+ofs+(ptrdiff_t)sizeof(float) == (ptrdiff_t) this ||
     600   109078381 :                     base+ofs+2*(ptrdiff_t)sizeof(float) == (ptrdiff_t) this)) {
     601             :             // we are inside a Vector3f. We need to work out which
     602             :             // element of the vector the current object refers to.
     603      463561 :             *idx = (((ptrdiff_t) this) - (base+ofs))/sizeof(float);
     604      463561 :             *group_element = group_id(group_info, group_base, i, group_shift);
     605      428552 :             group_ret = &group_info[i];
     606      428552 :             return &var_info(vindex);
     607             :         }
     608             :     }
     609   192254906 :     return nullptr;
     610             : }
     611             : 
     612             : // find the info structure for a variable
     613     1383430 : const struct AP_Param::Info *AP_Param::find_var_info(uint32_t *                 group_element,
     614             :                                                      const struct GroupInfo *   &group_ret,
     615             :                                                      struct GroupNesting        &group_nesting,
     616             :                                                      uint8_t *                  idx) const
     617             : {
     618     1383430 :     group_ret = nullptr;
     619             :     
     620    87957028 :     for (uint16_t i=0; i<_num_vars; i++) {
     621    87874224 :         const auto &info = var_info(i);
     622    87874223 :         uint8_t type = info.type;
     623             :         ptrdiff_t base;
     624    87874223 :         if (!get_base(info, base)) {
     625      709138 :             continue;
     626             :         }
     627    87165120 :         if (type == AP_PARAM_GROUP) {
     628    20183826 :             const struct GroupInfo *group_info = get_group_info(info);
     629    20183820 :             if (group_info == nullptr) {
     630           0 :                 continue;
     631             :             }
     632             :             const struct AP_Param::Info *info2;
     633    20183820 :             info2 = find_var_info_group(group_info, i, 0, 0, 0, group_element, group_ret, group_nesting, idx);
     634    20183816 :             if (info2 != nullptr) {
     635     1300650 :                 return info2;
     636             :             }
     637    66981294 :         } else if (base == (ptrdiff_t) this) {
     638       98805 :             *group_element = 0;
     639       98805 :             *idx = 0;
     640       98805 :             return &info;
     641    66882489 :         } else if (type == AP_PARAM_VECTOR3F &&
     642          26 :                    (base+(ptrdiff_t)sizeof(float) == (ptrdiff_t) this ||
     643          26 :                     base+2*(ptrdiff_t)sizeof(float) == (ptrdiff_t) this)) {
     644             :             // we are inside a Vector3f. Work out which element we are
     645             :             // referring to.
     646           0 :             *idx = (((ptrdiff_t) this) - base)/sizeof(float);
     647           0 :             *group_element = 0;
     648           0 :             return &info;
     649             :         }
     650             :     }
     651       82804 :     return nullptr;
     652             : }
     653             : 
     654             : 
     655             : // find the info structure for a variable
     656     4341176 : const struct AP_Param::Info *AP_Param::find_var_info_token(const ParamToken &token,
     657             :                                                            uint32_t *                 group_element,
     658             :                                                            const struct GroupInfo *   &group_ret,
     659             :                                                            struct GroupNesting        &group_nesting,
     660             :                                                            uint8_t *                  idx) const
     661             : {
     662     4341176 :     uint16_t i = token.key;
     663     4341176 :     const auto &info = var_info(i);
     664     4341176 :     uint8_t type = info.type;
     665             :     ptrdiff_t base;
     666     4341176 :     if (!get_base(info, base)) {
     667           0 :         return nullptr;
     668             :     }
     669     4341176 :     group_ret = nullptr;
     670             :     
     671     4341176 :     if (type == AP_PARAM_GROUP) {
     672     4173943 :         const struct GroupInfo *group_info = get_group_info(info);
     673     4173943 :         if (group_info == nullptr) {
     674           0 :             return nullptr;
     675             :         }
     676             :         const struct AP_Param::Info *info2;
     677     4173943 :         info2 = find_var_info_group(group_info, i, 0, 0, 0, group_element, group_ret, group_nesting, idx);
     678     4173943 :         if (info2 != nullptr) {
     679     4173943 :             return info2;
     680             :         }
     681      167233 :     } else if (base == (ptrdiff_t) this) {
     682      167233 :         *group_element = 0;
     683      167233 :         *idx = 0;
     684      167233 :         return &info;
     685           0 :     } else if (type == AP_PARAM_VECTOR3F &&
     686           0 :                (base+(ptrdiff_t)sizeof(float) == (ptrdiff_t) this ||
     687           0 :                 base+2*(ptrdiff_t)sizeof(float) == (ptrdiff_t) this)) {
     688             :         // we are inside a Vector3f. Work out which element we are
     689             :         // referring to.
     690           0 :         *idx = (((ptrdiff_t) this) - base)/sizeof(float);
     691           0 :         *group_element = 0;
     692           0 :         return &info;
     693             :     }
     694           0 :     return nullptr;
     695             : }
     696             : 
     697             : // return the storage size for a AP_PARAM_* type.
     698             : // NOTE: if you add a type here, also add it to AP_Param_value_storage
     699             : // (below) so the value buffers used during conversion stay big enough.
     700    77835457 : uint8_t AP_Param::type_size(enum ap_var_type type)
     701             : {
     702    77835457 :     switch (type) {
     703           0 :     case AP_PARAM_NONE:
     704             :     case AP_PARAM_GROUP:
     705           0 :         return 0;
     706    23838705 :     case AP_PARAM_INT8:
     707    23838705 :         return 1;
     708    15193583 :     case AP_PARAM_INT16:
     709    15193583 :         return 2;
     710    18221516 :     case AP_PARAM_INT32:
     711    18221516 :         return 4;
     712    16318824 :     case AP_PARAM_FLOAT:
     713    16318824 :         return 4;
     714     4262829 :     case AP_PARAM_VECTOR3F:
     715     4262829 :         return 3*4;
     716             :     }
     717             :     Debug("unknown type %d\n", type);
     718           0 :     return 0;
     719             : }
     720             : 
     721             : // a union of every storable parameter type; an object of this type is
     722             : // large enough and suitably aligned to hold the value of any single
     723             : // parameter without assuming which type is largest.  Used as a value
     724             : // buffer during parameter conversion.  Keep the members in sync with
     725             : // type_size().
     726             : union AP_Param_value_storage {
     727             :     // Vector3f has a non-trivial constructor, so the union's default
     728             :     // constructor would otherwise be deleted; the value is always filled
     729             :     // in (read from EEPROM) before use.
     730      327384 :     AP_Param_value_storage() {}
     731             :     int8_t i8;
     732             :     int16_t i16;
     733             :     int32_t i32;
     734             :     float f;
     735             :     Vector3f v3f;
     736             : };
     737             : 
     738             : /*
     739             :   extract 9 bit key from Param_header
     740             :  */
     741   179718445 : uint16_t AP_Param::get_key(const Param_header &phdr)
     742             : {
     743   179718445 :     return ((uint16_t)phdr.key_high)<<8 | phdr.key_low;
     744             : }
     745             : 
     746             : /*
     747             :   set 9 bit key in Param_header
     748             :  */
     749     1500241 : void AP_Param::set_key(Param_header &phdr, uint16_t key)
     750             : {
     751     1500241 :     phdr.key_low  = key & 0xFF;
     752     1500241 :     phdr.key_high = key >> 8;
     753     1500241 : }
     754             : 
     755             : /*
     756             :   return true if a header is the end of eeprom sentinel
     757             :  */
     758    71624344 : bool AP_Param::is_sentinel(const Param_header &phdr)
     759             : {
     760             :     // note that this is an ||, not an && on the key and group, as
     761             :     // this makes us more robust to power off while adding a variable
     762             :     // to EEPROM
     763   141995932 :     if (phdr.type == _sentinel_type ||
     764    70371588 :         get_key(phdr) == _sentinel_key) {
     765     1252756 :         return true;
     766             :     }
     767             :     // also check for 0xFFFFFFFF and 0x00000000, which are the fill
     768             :     // values for storage. These can appear if power off occurs while
     769             :     // writing data
     770    70371588 :     uint32_t v = *(uint32_t *)&phdr;
     771    70371588 :     if (v == 0 || v == 0xFFFFFFFF) {
     772           0 :         return true;
     773             :     }
     774    70371588 :     return false;
     775             : }
     776             : 
     777             : // scan the EEPROM looking for a given variable by header content
     778             : // return true if found, along with the offset in the EEPROM where
     779             : // the variable is stored
     780             : // if not found return the offset of the sentinel
     781             : // if the sentinel isn't found either, the offset is set to 0xFFFF
     782     1485639 : bool AP_Param::scan(const AP_Param::Param_header *target, uint16_t *pofs)
     783             : {
     784             :     struct Param_header phdr;
     785     1485639 :     uint16_t ofs = sizeof(AP_Param::EEPROM_header);
     786    58022441 :     while (ofs < _storage.size()) {
     787    58017881 :         _storage.read_block(&phdr, ofs, sizeof(phdr));
     788    12371309 :         if (phdr.type == target->type &&
     789    70389190 :             get_key(phdr) == get_key(*target) &&
     790     1719798 :             phdr.group_element == target->group_element) {
     791             :             // found it
     792      491074 :             *pofs = ofs;
     793      491074 :             return true;
     794             :         }
     795    57526807 :         if (is_sentinel(phdr)) {
     796             :             // we've reached the sentinel
     797      990005 :             *pofs = ofs;
     798      990005 :             sentinel_offset = ofs;
     799      990005 :             return false;
     800             :         }
     801    56536802 :         ofs += type_size((enum ap_var_type)phdr.type) + sizeof(phdr);
     802             :     }
     803        4560 :     *pofs = 0xffff;
     804             :     Debug("scan past end of eeprom");
     805        4560 :     return false;
     806             : }
     807             : 
     808             : /**
     809             :  * add a _X, _Y, _Z suffix to the name of a Vector3f element
     810             :  * @param buffer
     811             :  * @param buffer_size
     812             :  * @param idx Suffix: 0 --> _X; 1 --> _Y; 2 --> _Z; (other --> undefined)
     813             :  */
     814      562635 : void AP_Param::add_vector3f_suffix(char *buffer, size_t buffer_size, uint8_t idx) const
     815             : {
     816      562635 :     const size_t len = strnlen(buffer, buffer_size);
     817      562635 :     if (len + 2 <= buffer_size) {
     818      562635 :         buffer[len] = '_';
     819      562635 :         buffer[len + 1] = static_cast<char>('X' + idx);
     820      562635 :         if (len + 3 <= buffer_size) {
     821      506616 :             buffer[len + 2] = 0;
     822             :         }
     823             :     }
     824      562635 : }
     825             : 
     826             : // Copy the variable's whole name to the supplied buffer.
     827             : //
     828             : // If the variable is a group member, prepend the group name.
     829             : //
     830     4341176 : void AP_Param::copy_name_token(const ParamToken &token, char *buffer, size_t buffer_size, bool force_scalar) const
     831             : {
     832             :     uint32_t group_element;
     833             :     const struct GroupInfo *ginfo;
     834     4341176 :     struct GroupNesting group_nesting {};
     835             :     uint8_t idx;
     836     4341176 :     const struct AP_Param::Info *info = find_var_info_token(token, &group_element, ginfo, group_nesting, &idx);
     837     4341176 :     if (info == nullptr) {
     838           0 :         *buffer = 0;
     839             :         Debug("no info found");
     840           0 :         return;
     841             :     }
     842     4341176 :     copy_name_info(info, ginfo, group_nesting, idx, buffer, buffer_size, force_scalar);
     843             : }
     844             : 
     845     4419553 : void AP_Param::copy_name_info(const struct AP_Param::Info *info,
     846             :                               const struct GroupInfo *ginfo,
     847             :                               const struct GroupNesting &group_nesting,
     848             :                               uint8_t idx, char *buffer, size_t buffer_size, bool force_scalar) const
     849             : {
     850     4419553 :     strncpy(buffer, info->name, buffer_size);
     851     8577016 :     for (uint8_t i=0; i<group_nesting.level; i++) {
     852     4157463 :         uint8_t len = strnlen(buffer, buffer_size);
     853     4157463 :         if (len < buffer_size) {
     854     4157463 :             strncpy(&buffer[len], group_nesting.group_ret[i]->name, buffer_size-len);
     855             :         }
     856             :     }
     857     4419553 :     if (ginfo != nullptr) {
     858     4250231 :         uint8_t len = strnlen(buffer, buffer_size);
     859     4250231 :         if (len < buffer_size) {
     860     4250231 :             strncpy(&buffer[len], ginfo->name, buffer_size-len);
     861             :         }
     862     4250231 :         if ((force_scalar || idx != 0) && AP_PARAM_VECTOR3F == ginfo->type) {
     863             :             // the caller wants a specific element in a Vector3f
     864      562633 :             add_vector3f_suffix(buffer, buffer_size, idx);
     865             :         }
     866      169322 :     } else if ((force_scalar || idx != 0) && AP_PARAM_VECTOR3F == info->type) {
     867           2 :         add_vector3f_suffix(buffer, buffer_size, idx);
     868             :     }
     869     4419553 : }
     870             : 
     871             : // Find a variable by name in a group
     872             : AP_Param *
     873     1352660 : AP_Param::find_group(const char *name, uint16_t vindex, ptrdiff_t group_offset,
     874             :                      const struct GroupInfo *group_info, enum ap_var_type *ptype)
     875             : {
     876             :     uint8_t type;
     877    14756423 :     for (uint8_t i=0;
     878    14756423 :          (type=group_info[i].type) != AP_PARAM_NONE;
     879             :          i++) {
     880    14477589 :         if (type == AP_PARAM_GROUP) {
     881     5368042 :             if (strncasecmp(name, group_info[i].name, strlen(group_info[i].name)) != 0) {
     882     4720379 :                 continue;
     883             :             }
     884      649651 :             const struct GroupInfo *ginfo = get_group_info(group_info[i]);
     885      649651 :             if (ginfo == nullptr) {
     886        1984 :                 continue;
     887             :             }
     888      647667 :             ptrdiff_t new_offset = group_offset;
     889             : 
     890      647667 :             if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
     891           4 :                 continue;
     892             :             }
     893             : 
     894      647663 :             AP_Param *ap = find_group(name+strlen(group_info[i].name), vindex, new_offset, ginfo, ptype);
     895      647663 :             if (ap != nullptr) {
     896      507927 :                 return ap;
     897             :             }
     898     9109547 :         } else if (strcasecmp(name, group_info[i].name) == 0) {
     899             :             ptrdiff_t base;
     900      398370 :             if (!get_base(var_info(vindex), base)) {
     901        5424 :                 continue;
     902             :             }
     903      392946 :             *ptype = (enum ap_var_type)type;
     904      392946 :             return (AP_Param *)(base + group_info[i].offset + group_offset);
     905     8711177 :         } else if (type == AP_PARAM_VECTOR3F) {
     906             :             // special case for finding Vector3f elements
     907     1395845 :             uint8_t suffix_len = strnlen(group_info[i].name, AP_MAX_NAME_SIZE);
     908     1395845 :             if (strncmp(name, group_info[i].name, suffix_len) == 0 &&
     909      206189 :                 name[suffix_len] == '_' &&
     910      172953 :                 (name[suffix_len+1] == 'X' ||
     911      115123 :                  name[suffix_len+1] == 'Y' ||
     912       57302 :                  name[suffix_len+1] == 'Z')) {
     913             :                 ptrdiff_t base;
     914      172953 :                 if (!get_base(var_info(vindex), base)) {
     915           0 :                     continue;
     916             :                 }
     917      172953 :                 AP_Float *v = (AP_Float *)(base + group_info[i].offset + group_offset);
     918      172953 :                 *ptype = AP_PARAM_FLOAT;
     919      172953 :                 switch (name[suffix_len+1]) {
     920       57830 :                 case 'X':
     921      172953 :                     return (AP_Float *)&v[0];
     922       57821 :                 case 'Y':
     923       57821 :                     return (AP_Float *)&v[1];
     924       57302 :                 case 'Z':
     925       57302 :                     return (AP_Float *)&v[2];
     926             :                 }
     927             :             }
     928             :         }
     929             :     }
     930      278834 :     return nullptr;
     931             : }
     932             : 
     933             : 
     934             : // Find a variable by name.
     935             : //
     936             : AP_Param *
     937      701320 : AP_Param::find(const char *name, enum ap_var_type *ptype, uint16_t *flags)
     938             : {
     939    53184530 :     for (uint16_t i=0; i<_num_vars; i++) {
     940    53144686 :         const auto &info = var_info(i);
     941    53144686 :         uint8_t type = info.type;
     942    53144686 :         if (type == AP_PARAM_GROUP) {
     943    15474937 :             uint8_t len = strnlen(info.name, AP_MAX_NAME_SIZE);
     944    15474937 :             if (strncmp(name, info.name, len) != 0) {
     945    14738032 :                 continue;
     946             :             }
     947      736905 :             const struct GroupInfo *group_info = get_group_info(info);
     948      736905 :             if (group_info == nullptr) {
     949       31908 :                 continue;
     950             :             }
     951      704997 :             AP_Param *ap = find_group(name + len, i, 0, group_info, ptype);
     952      704997 :             if (ap != nullptr) {
     953      565899 :                 if (flags != nullptr) {
     954        9541 :                     uint32_t group_element = 0;
     955             :                     const struct GroupInfo *ginfo;
     956        9541 :                     struct GroupNesting group_nesting {};
     957             :                     uint8_t idx;
     958        9541 :                     ap->find_var_info(&group_element, ginfo, group_nesting, &idx);
     959        9541 :                     if (ginfo != nullptr) {
     960        9541 :                         *flags = ginfo->flags;
     961             :                     } else {
     962           0 :                         *flags = 0;
     963             :                     }
     964             :                 }
     965      565899 :                 return ap;
     966             :             }
     967             :             // we continue looking as we want to allow top level
     968             :             // parameter to have the same prefix name as group
     969             :             // parameters, for example CAM_P_G
     970    37669749 :         } else if (strcasecmp(name, info.name) == 0) {
     971       95577 :             *ptype = (enum ap_var_type)type;
     972             :             ptrdiff_t base;
     973       95577 :             if (!get_base(info, base)) {
     974           0 :                 return nullptr;
     975             :             }
     976       95577 :             if (flags != nullptr) {
     977         586 :                 *flags = 0;
     978             :             }
     979       95577 :             return (AP_Param *)base;
     980             :         }
     981             :     }
     982       39844 :     return nullptr;
     983             : }
     984             : 
     985             : // Find a variable by index. Note that this is quite slow.
     986             : //
     987             : AP_Param *
     988           1 : AP_Param::find_by_index(uint16_t idx, enum ap_var_type *ptype, ParamToken *token)
     989             : {
     990             :     AP_Param *ap;
     991           1 :     uint16_t count=0;
     992           1 :     for (ap=AP_Param::first(token, ptype);
     993        1289 :          ap && count < idx;
     994        1288 :          ap=AP_Param::next_scalar(token, ptype)) {
     995        1288 :         count++;
     996             :     }
     997           1 :     return ap;    
     998             : }
     999             : 
    1000             : // by-name equivalent of find_by_index()
    1001          12 : AP_Param* AP_Param::find_by_name(const char* name, enum ap_var_type *ptype, ParamToken *token)
    1002             : {
    1003             :     AP_Param *ap;
    1004          12 :     for (ap = AP_Param::first(token, ptype);
    1005          42 :          ap && *ptype != AP_PARAM_GROUP && *ptype != AP_PARAM_NONE;
    1006          30 :          ap = AP_Param::next_scalar(token, ptype)) {
    1007          42 :         const auto nlen = strlen(var_info(token->key).name);
    1008             :         /*
    1009             :           the name must either match the token name (if a non-group top level param)
    1010             :           or match up to the length (if a group).
    1011             :           This check avoids us traversing down into most groups, saving a lot of calls to copy_name_token()
    1012             :          */
    1013          42 :         int32_t ret = strncasecmp(name, var_info(token->key).name, nlen);
    1014          42 :         if (ret == 0) {
    1015             :             char buf[AP_MAX_NAME_SIZE];
    1016          18 :             ap->copy_name_token(*token, buf, AP_MAX_NAME_SIZE);
    1017          18 :             if (strncasecmp(name, buf, AP_MAX_NAME_SIZE) == 0) {
    1018          12 :                 break;
    1019             :             }
    1020             :         }
    1021             :     }
    1022          12 :     return ap;
    1023             : }
    1024             : 
    1025             : /*
    1026             :   Find a variable by pointer, returning key. This is used for loading pointer variables
    1027             : */
    1028     5351021 : bool AP_Param::find_key_by_pointer_group(const void *ptr, uint16_t vindex,
    1029             :                                          const struct GroupInfo *group_info,
    1030             :                                          ptrdiff_t offset, uint16_t &key)
    1031             : {
    1032    45219820 :     for (uint8_t i=0; group_info[i].type != AP_PARAM_NONE; i++) {
    1033    39899106 :         if (group_info[i].type != AP_PARAM_GROUP) {
    1034    35176837 :             continue;
    1035             :         }
    1036             :         ptrdiff_t base;
    1037     5261207 :         if (!get_base(var_info(vindex), base)) {
    1038       39581 :             continue;
    1039             :         }
    1040     5221626 :         if (group_info[i].flags & AP_PARAM_FLAG_POINTER) {
    1041      575372 :             if (ptr == *(void **)(base+group_info[i].offset+offset)) {
    1042        9458 :                 key = var_info(vindex).key;
    1043       30307 :                 return true;
    1044             :             }
    1045     4646254 :         } else if (ptr == (void *)(base+group_info[i].offset+offset)) {
    1046       15622 :             key = var_info(vindex).key;
    1047       15622 :             return true;
    1048             :         }
    1049     5196546 :         ptrdiff_t new_offset = offset;
    1050     5196546 :         if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
    1051      498621 :             continue;
    1052             :         }
    1053     4697925 :         const struct GroupInfo *ginfo = get_group_info(group_info[i]);
    1054     4697925 :         if (ginfo == nullptr) {
    1055         736 :             continue;
    1056             :         }
    1057     4697189 :         if (find_key_by_pointer_group(ptr, vindex, ginfo, new_offset, key)) {
    1058        5227 :             return true;
    1059             :         }
    1060             :     }
    1061     5320714 :     return false;
    1062             : }
    1063             : 
    1064             : 
    1065             : /*
    1066             :   Find a variable by pointer, returning key. This is used for loading pointer variables
    1067             : */
    1068       33573 : bool AP_Param::find_key_by_pointer(const void *ptr, uint16_t &key)
    1069             : {
    1070     2289387 :     for (uint16_t i=0; i<_num_vars; i++) {
    1071     2289387 :         const auto &info = var_info(i);
    1072     2289387 :         if (info.type != AP_PARAM_GROUP) {
    1073     1618547 :             continue;
    1074             :         }
    1075      670840 :         if ((info.flags & AP_PARAM_FLAG_POINTER) &&
    1076      115888 :             ptr == *(void **)info.ptr) {
    1077        8493 :             key = info.key;
    1078        8493 :             return true;
    1079             :         }
    1080      662347 :         ptrdiff_t offset = 0;
    1081      662347 :         const struct GroupInfo *ginfo = get_group_info(info);
    1082      662347 :         if (ginfo == nullptr) {
    1083        8515 :             continue;
    1084             :         }
    1085      653832 :         if (find_key_by_pointer_group(ptr, i, ginfo, offset, key)) {
    1086       25080 :             return true;
    1087             :         }
    1088             :     }
    1089           0 :     return false;
    1090             : }
    1091             : 
    1092             : /*
    1093             :   Find key to top level group parameters by pointer
    1094             : */
    1095       18215 : bool AP_Param::find_top_level_key_by_pointer(const void *ptr, uint16_t &key)
    1096             : {
    1097     1179445 :     for (uint16_t i=0; i<_num_vars; i++) {
    1098     1179405 :         const auto &info = var_info(i);
    1099     1179405 :         if (info.type != AP_PARAM_GROUP) {
    1100      987179 :             continue;
    1101             :         }
    1102      192226 :         if (ptr == (void **)info.ptr) {
    1103       18175 :             key = info.key;
    1104       18175 :             return true;
    1105             :         }
    1106             :     }
    1107          40 :     return false;
    1108             : }
    1109             : 
    1110             : /*
    1111             :   fetch a parameter value based on the index within a group. This
    1112             :   is used to find the old value of a parameter that has been
    1113             :   removed from an object.
    1114             : */
    1115       13480 : bool AP_Param::get_param_by_index(void *obj_ptr, uint32_t idx, ap_var_type old_ptype, void *pvalue)
    1116             : {
    1117             :     uint16_t key;
    1118       13480 :     if (!find_top_level_key_by_pointer(obj_ptr, key)) {
    1119          40 :         return false;
    1120             :     }
    1121       13440 :     const ConversionInfo type_info = {key, idx, old_ptype, nullptr };
    1122       13440 :     return AP_Param::find_old_parameter(&type_info, (AP_Param *)pvalue);
    1123             : }
    1124             : 
    1125             : 
    1126             : // notify GCS of current value of parameter
    1127        7710 : void AP_Param::notify() const {
    1128        7710 :     uint32_t group_element = 0;
    1129             :     const struct GroupInfo *ginfo;
    1130        7710 :     struct GroupNesting group_nesting {};
    1131             :     uint8_t idx;
    1132             : 
    1133        7710 :     const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
    1134        7710 :     if (info == nullptr) {
    1135             :         // this is probably very bad
    1136           0 :         return;
    1137             :     }
    1138             : 
    1139             :     char name[AP_MAX_NAME_SIZE+1];
    1140        7710 :     copy_name_info(info, ginfo, group_nesting, idx, name, sizeof(name), true);
    1141             : 
    1142             :     uint32_t param_header_type;
    1143        7710 :     if (ginfo != nullptr) {
    1144        7710 :         param_header_type = ginfo->type;
    1145             :     } else {
    1146           0 :         param_header_type = info->type;
    1147             :     }
    1148             : 
    1149        7710 :     send_parameter(name, (enum ap_var_type)param_header_type, idx);
    1150             : }
    1151             : 
    1152             : 
    1153             : /*
    1154             :   Save the variable to HAL storage, synchronous version
    1155             : */
    1156       70770 : void AP_Param::save_sync(bool force_save, bool send_to_gcs)
    1157             : {
    1158       70770 :     uint32_t group_element = 0;
    1159             :     const struct GroupInfo *ginfo;
    1160       70770 :     struct GroupNesting group_nesting {};
    1161             :     uint8_t idx;
    1162       70770 :     const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
    1163             :     const AP_Param *ap;
    1164             : 
    1165       70770 :     if (info == nullptr) {
    1166             :         // we don't have any info on how to store it
    1167       56768 :         return;
    1168             :     }
    1169             : 
    1170             :     struct Param_header phdr;
    1171             : 
    1172             :     // create the header we will use to store the variable
    1173       70667 :     if (ginfo != nullptr) {
    1174       68578 :         phdr.type = ginfo->type;
    1175       68578 :         if (ginfo->flags & AP_PARAM_FLAG_HIDDEN) {
    1176        1009 :             send_to_gcs = false;
    1177             :         }
    1178             :     } else {
    1179        2089 :         phdr.type = info->type;
    1180        2089 :         if (info->flags & AP_PARAM_FLAG_HIDDEN) {
    1181           0 :             send_to_gcs = false;
    1182             :         }
    1183             :     }
    1184       70667 :     set_key(phdr, info->key);
    1185       70667 :     phdr.group_element = group_element;
    1186             : 
    1187       70667 :     ap = this;
    1188       70667 :     if (phdr.type != AP_PARAM_VECTOR3F && idx != 0) {
    1189             :         // only vector3f can have non-zero idx for now
    1190           0 :         return;
    1191             :     }
    1192       70667 :     if (idx != 0) {
    1193         832 :         ap = (const AP_Param *)((ptrdiff_t)ap) - (idx*sizeof(float));
    1194             :     }
    1195             : 
    1196       70667 :     if (phdr.type == AP_PARAM_INT8 && ginfo != nullptr && (ginfo->flags & AP_PARAM_FLAG_ENABLE)) {
    1197             :         // clear cached parameter count
    1198        2253 :         invalidate_count();
    1199             :     }
    1200             :     
    1201             :     char name[AP_MAX_NAME_SIZE+1];
    1202       70667 :     copy_name_info(info, ginfo, group_nesting, idx, name, sizeof(name), true);
    1203             : 
    1204             :     // scan EEPROM to find the right location
    1205             :     uint16_t ofs;
    1206       70667 :     if (scan(&phdr, &ofs)) {
    1207             :         // found an existing copy of the variable
    1208       56444 :         eeprom_write_check(ap, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
    1209       56444 :         if (send_to_gcs) {
    1210       56395 :             send_parameter(name, (enum ap_var_type)phdr.type, idx);
    1211             :         }
    1212       56444 :         return;
    1213             :     }
    1214       14223 :     if (ofs == (uint16_t) ~0) {
    1215           0 :         eeprom_full = true;
    1216           0 :         DEV_PRINTF("EEPROM full\n");
    1217           0 :         return;
    1218             :     }
    1219             : 
    1220             :     // if the value is the default value then don't save
    1221       14223 :     if (phdr.type <= AP_PARAM_FLOAT) {
    1222       13446 :         float v1 = cast_to_float((enum ap_var_type)phdr.type);
    1223             :         float v2;
    1224       13446 :         if (ginfo != nullptr) {
    1225       12835 :             v2 = get_default_value(this, *ginfo);
    1226             :         } else {
    1227         611 :             v2 = get_default_value(this, *info);
    1228             :         }
    1229       13446 :         if (is_equal(v1,v2) && !force_save) {
    1230         221 :             if (send_to_gcs) {
    1231         221 :                 GCS_SEND_PARAM(name, (enum ap_var_type)info->type, v2);
    1232             :             }
    1233         221 :             return;
    1234             :         }
    1235       13225 :         if (!force_save &&
    1236        6156 :             (phdr.type != AP_PARAM_INT32 &&
    1237        3336 :              (fabsf(v1-v2) < 0.0001f*fabsf(v1)))) {
    1238             :             // for other than 32 bit integers, we accept values within
    1239             :             // 0.01 percent of the current value as being the same
    1240           0 :             if (send_to_gcs) {
    1241           0 :                 GCS_SEND_PARAM(name, (enum ap_var_type)info->type, v2);
    1242             :             }
    1243           0 :             return;
    1244             :         }
    1245             :     }
    1246             : 
    1247       14002 :     if (ofs+type_size((enum ap_var_type)phdr.type)+2*sizeof(phdr) >= _storage.size()) {
    1248             :         // we are out of room for saving variables
    1249           0 :         eeprom_full = true;
    1250           0 :         DEV_PRINTF("EEPROM full\n");
    1251           0 :         return;
    1252             :     }
    1253             : 
    1254             :     // write a new sentinel, then the data, then the header
    1255       14002 :     write_sentinel(ofs + sizeof(phdr) + type_size((enum ap_var_type)phdr.type));
    1256       14002 :     eeprom_write_check(ap, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
    1257       14002 :     eeprom_write_check(&phdr, ofs, sizeof(phdr));
    1258             : 
    1259       14002 :     if (send_to_gcs) {
    1260       13033 :         send_parameter(name, (enum ap_var_type)phdr.type, idx);
    1261             :     }
    1262             : }
    1263             : 
    1264             : /*
    1265             :   put variable into queue to be saved
    1266             : */
    1267       73125 : void AP_Param::save(bool force_save)
    1268             : {
    1269             :     struct param_save p, p2;
    1270       73125 :     p.param = this;
    1271       73125 :     p.force_save = force_save;
    1272       73125 :     if (save_queue.peek(p2) &&
    1273       75483 :         p2.param == this &&
    1274        2358 :         p2.force_save == force_save) {
    1275             :         // this one is already at the head of the list to be
    1276             :         // saved. This check is cheap and catches the case where we
    1277             :         // are flooding the save queue with one parameter (eg. mission
    1278             :         // creation, changing MIS_TOTAL)
    1279        2354 :         return;
    1280             :     }
    1281       70774 :     while (!save_queue.push(p)) {
    1282             :         // if we can't save to the queue
    1283           3 :         if (hal.util->get_soft_armed() && hal.scheduler->in_main_thread()) {
    1284             :             // if we are armed in main thread then don't sleep, instead we lose the
    1285             :             // parameter save
    1286           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    1287           0 :             return;
    1288             :         }
    1289             :         // when we are disarmed then loop waiting for a slot to become
    1290             :         // available. This guarantees completion for large parameter
    1291             :         // set loads
    1292           3 :         hal.scheduler->expect_delay_ms(1);
    1293           3 :         hal.scheduler->delay_microseconds(500);
    1294           3 :         hal.scheduler->expect_delay_ms(0);
    1295             :     }
    1296             : }
    1297             : 
    1298             : /*
    1299             :   background function for saving parameters. This runs on the IO thread
    1300             :  */
    1301   243357862 : void AP_Param::save_io_handler(void)
    1302             : {
    1303             :     struct param_save p;
    1304   243428623 :     while (save_queue.pop(p)) {
    1305       70761 :         p.param->save_sync(p.force_save, true);
    1306             :     }
    1307   243357862 :     if (hal.scheduler->is_system_initialized()) {
    1308             :         // pay the cost of parameter counting in the IO thread
    1309   236970580 :         count_parameters();
    1310             :     }
    1311   243357862 : }
    1312             : 
    1313             : /*
    1314             :   wait for all parameters to save
    1315             : */
    1316       37315 : void AP_Param::flush(void)
    1317             : {
    1318       37315 :     uint16_t counter = 200; // 2 seconds max
    1319       39462 :     while (counter-- && save_queue.available()) {
    1320        2147 :         hal.scheduler->expect_delay_ms(10);
    1321        2147 :         hal.scheduler->delay(10);
    1322        2147 :         hal.scheduler->expect_delay_ms(0);
    1323             :     }
    1324       37315 : }
    1325             : 
    1326             : // Load the variable from EEPROM, if supported
    1327             : //
    1328      441155 : bool AP_Param::load(void)
    1329             : {
    1330      441155 :     uint32_t group_element = 0;
    1331             :     const struct GroupInfo *ginfo;
    1332      441155 :     struct GroupNesting group_nesting {};
    1333             :     uint8_t idx;
    1334      441155 :     const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
    1335      441155 :     if (info == nullptr) {
    1336             :         // we don't have any info on how to load it
    1337           0 :         return false;
    1338             :     }
    1339             : 
    1340             :     struct Param_header phdr;
    1341             : 
    1342             :     // create the header we will use to match the variable
    1343      441155 :     if (ginfo != nullptr) {
    1344      408472 :         phdr.type = ginfo->type;
    1345             :     } else {
    1346       32683 :         phdr.type = info->type;
    1347             :     }
    1348      441155 :     set_key(phdr, info->key);
    1349      441155 :     phdr.group_element = group_element;
    1350             : 
    1351             :     // scan EEPROM to find the right location
    1352             :     uint16_t ofs;
    1353      441155 :     if (!scan(&phdr, &ofs)) {
    1354             :         // if the value isn't stored in EEPROM then set the default value
    1355             :         ptrdiff_t base;
    1356       32301 :         if (!get_base(*info, base)) {
    1357           0 :             return false;
    1358             :         }
    1359             : 
    1360       32301 :         if (ginfo != nullptr) {
    1361             :             // add in nested group offset
    1362        1636 :             ptrdiff_t group_offset = 0;
    1363        1636 :             for (uint8_t i=0; i<group_nesting.level; i++) {
    1364           0 :                 group_offset += group_nesting.group_ret[i]->offset;
    1365             :             }
    1366        1636 :             set_value((enum ap_var_type)phdr.type, (void*)(base + ginfo->offset + group_offset),
    1367             :                       get_default_value(this, *ginfo));
    1368             :         } else {
    1369       30665 :             set_value((enum ap_var_type)phdr.type, (void*)base, 
    1370             :                       get_default_value(this, *info));
    1371             :         }
    1372       32301 :         return false;
    1373             :     }
    1374             : 
    1375      408854 :     if (phdr.type != AP_PARAM_VECTOR3F && idx != 0) {
    1376             :         // only vector3f can have non-zero idx for now
    1377           0 :         return false;
    1378             :     }
    1379             : 
    1380      408854 :     AP_Param *ap = this;
    1381      408854 :     if (idx != 0) {
    1382           0 :         ap = (AP_Param *)((ptrdiff_t)ap) - (idx*sizeof(float));
    1383             :     }
    1384             : 
    1385             :     // found it
    1386      408854 :     _storage.read_block(ap, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
    1387      408854 :     return true;
    1388             : }
    1389             : 
    1390      611521 : bool AP_Param::configured_in_storage(void) const
    1391             : {
    1392      611521 :     uint32_t group_element = 0;
    1393             :     const struct GroupInfo *ginfo;
    1394      611521 :     struct GroupNesting group_nesting {};
    1395             :     uint8_t idx;
    1396      611521 :     const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
    1397      611521 :     if (info == nullptr) {
    1398             :         // we don't have any info on how to load it
    1399       76360 :         return false;
    1400             :     }
    1401             : 
    1402             :     struct Param_header phdr;
    1403             : 
    1404             :     // create the header we will use to match the variable
    1405      535161 :     if (ginfo != nullptr) {
    1406      478784 :         phdr.type = ginfo->type;
    1407             :     } else {
    1408       56377 :         phdr.type = info->type;
    1409             :     }
    1410      535161 :     set_key(phdr, info->key);
    1411      535161 :     phdr.group_element = group_element;
    1412             : 
    1413             :     // scan EEPROM to find the right location
    1414             :     uint16_t ofs;
    1415             : 
    1416             :     // only vector3f can have non-zero idx for now
    1417      535161 :     return scan(&phdr, &ofs) && (phdr.type == AP_PARAM_VECTOR3F || idx == 0);
    1418             : }
    1419             : 
    1420      220053 : bool AP_Param::configured_in_defaults_file(bool &read_only) const
    1421             : {
    1422      220053 :     if (num_param_overrides == 0) {
    1423       79523 :         return false;
    1424             :     }
    1425      140530 :     uint32_t group_element = 0;
    1426             :     const struct GroupInfo *ginfo;
    1427      140530 :     struct GroupNesting group_nesting {};
    1428             :     uint8_t idx;
    1429      140530 :     const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
    1430      140530 :     if (info == nullptr) {
    1431             :         // we don't have any info on how to load it
    1432          34 :         return false;
    1433             :     }
    1434             : 
    1435    10488342 :     for (uint16_t i=0; i<num_param_overrides; i++) {
    1436    10353109 :         if (this == param_overrides[i].object_ptr) {
    1437        5263 :             read_only = param_overrides[i].read_only;
    1438        5263 :             return true;
    1439             :         }
    1440             :     }
    1441             : 
    1442      135233 :     return false;
    1443             : }
    1444             : 
    1445      220048 : bool AP_Param::configured(void) const
    1446             : {
    1447             :     bool read_only;
    1448      220048 :     return configured_in_defaults_file(read_only) || configured_in_storage();
    1449             : }
    1450             : 
    1451       10126 : bool AP_Param::is_read_only(void) const
    1452             : {
    1453       10126 :     if (num_read_only == 0) {
    1454       10121 :         return false;
    1455             :     }
    1456             :     bool read_only;
    1457           5 :     if (configured_in_defaults_file(read_only)) {
    1458           5 :         return read_only;
    1459             :     }
    1460           0 :     return false;
    1461             : }
    1462             : 
    1463             : // returns true if this parameter should be settable via the
    1464             : // MAVLink interface:
    1465       10125 : bool AP_Param::allow_set_via_mavlink(uint16_t flags) const
    1466             : {
    1467       10125 :     if (is_read_only()) {
    1468           2 :         return false;
    1469             :     }
    1470             : 
    1471       10123 :     if (flags & AP_PARAM_FLAG_INTERNAL_USE_ONLY) {
    1472             :         // the user can set BRD_OPTIONS to enable set of internal
    1473             :         // parameters, for developer testing or unusual use cases
    1474           0 :         if (!AP_BoardConfig::allow_set_internal_parameters()) {
    1475           0 :             return false;
    1476             :         }
    1477             :     }
    1478             : 
    1479             : #if HAL_GCS_ENABLED
    1480             :     // check the MAVLink library is OK with the concept:
    1481       10123 :     if (!gcs().get_allow_param_set()) {
    1482           6 :         return false;
    1483             :     }
    1484             : #endif  // HAL_GCS_ENABLED
    1485             : 
    1486       10117 :     return true;
    1487             : }
    1488             : 
    1489             : // set a AP_Param variable to a specified value
    1490     6749769 : void AP_Param::set_value(enum ap_var_type type, void *ptr, float value)
    1491             : {
    1492     6749769 :     switch (type) {
    1493     3389687 :     case AP_PARAM_INT8:
    1494     3389687 :         ((AP_Int8 *)ptr)->set(value);
    1495     3389687 :         break;
    1496     1165540 :     case AP_PARAM_INT16:
    1497     1165540 :         ((AP_Int16 *)ptr)->set(value);
    1498     1165540 :         break;
    1499      436623 :     case AP_PARAM_INT32:
    1500      436623 :         ((AP_Int32 *)ptr)->set(value);
    1501      436623 :         break;
    1502     1757919 :     case AP_PARAM_FLOAT:
    1503     1757919 :         ((AP_Float *)ptr)->set(value);
    1504     1757919 :         break;
    1505           0 :     default:
    1506           0 :         break;
    1507             :     }
    1508     6749769 : }
    1509             : 
    1510             : // load default values for scalars in a group. This does not recurse
    1511             : // into other objects. This is a static function that should be called
    1512             : // in the objects constructor
    1513     1126086 : void AP_Param::setup_object_defaults(const void *object_pointer, const struct GroupInfo *group_info)
    1514             : {
    1515     1126086 :     ptrdiff_t base = (ptrdiff_t)object_pointer;
    1516             :     uint8_t type;
    1517     9069327 :     for (uint8_t i=0;
    1518     9069327 :          (type=group_info[i].type) != AP_PARAM_NONE;
    1519             :          i++) {
    1520     7943241 :         if (type <= AP_PARAM_FLOAT) {
    1521     6601692 :             void *ptr = (void *)(base + group_info[i].offset);
    1522     6601692 :             set_value((enum ap_var_type)type, ptr,
    1523     6601692 :                       get_default_value((const AP_Param *)ptr, group_info[i]));
    1524     1341549 :         } else if (type == AP_PARAM_VECTOR3F) {
    1525             :             // Single default for all components
    1526      203509 :             void *ptr = (void *)(base + group_info[i].offset);
    1527      203509 :             const float default_val = get_default_value((const AP_Param *)ptr, group_info[i]);
    1528      203509 :             ((AP_Vector3f *)ptr)->set(Vector3f{default_val, default_val, default_val});
    1529             :         }
    1530             :     }
    1531     1126086 : }
    1532             : 
    1533             : // set a value directly in an object. This should only be used by
    1534             : // example code, not by mainline vehicle code
    1535           0 : bool AP_Param::set_object_value(const void *object_pointer,
    1536             :                                 const struct GroupInfo *group_info,
    1537             :                                 const char *name, float value)
    1538             : {
    1539           0 :     ptrdiff_t base = (ptrdiff_t)object_pointer;
    1540             :     uint8_t type;
    1541           0 :     bool found = false;
    1542           0 :     for (uint8_t i=0;
    1543           0 :          (type=group_info[i].type) != AP_PARAM_NONE;
    1544             :          i++) {
    1545           0 :         if (strcmp(name, group_info[i].name) == 0 && type <= AP_PARAM_FLOAT) {
    1546           0 :             void *ptr = (void *)(base + group_info[i].offset);
    1547           0 :             set_value((enum ap_var_type)type, ptr, value);
    1548             :             // return true here ?
    1549           0 :             found = true;
    1550             :         }
    1551             :     }
    1552           0 :     return found;
    1553             : }
    1554             : 
    1555             : 
    1556             : // load default values for all scalars in a sketch. This does not
    1557             : // recurse into sub-objects
    1558        2094 : void AP_Param::setup_sketch_defaults(void)
    1559             : {
    1560        2094 :     setup();
    1561      205912 :     for (uint16_t i=0; i<_num_vars; i++) {
    1562      203818 :         const auto &info = var_info(i);
    1563      203818 :         uint8_t type = info.type;
    1564      203818 :         if (type <= AP_PARAM_FLOAT) {
    1565             :             ptrdiff_t base;
    1566      115776 :             if (get_base(info, base)) {
    1567      115776 :                 set_value((enum ap_var_type)type, (void*)base,
    1568             :                           get_default_value((const AP_Param *)base, info));
    1569             :             }
    1570             :         }
    1571             :     }
    1572        2094 : }
    1573             : 
    1574             : 
    1575             : // Load all variables from EEPROM
    1576             : //
    1577        2104 : bool AP_Param::load_all()
    1578             : {
    1579             :     struct Param_header phdr;
    1580        2104 :     uint16_t ofs = sizeof(AP_Param::EEPROM_header);
    1581             : 
    1582        2104 :     reload_defaults_file(false);
    1583             : 
    1584        2104 :     if (!registered_save_handler) {
    1585        2096 :         registered_save_handler = true;
    1586        2096 :         hal.scheduler->register_io_process(FUNCTOR_BIND((&save_dummy), &AP_Param::save_io_handler, void));
    1587             :     }
    1588             :     
    1589      107471 :     while (ofs < _storage.size()) {
    1590      107471 :         _storage.read_block(&phdr, ofs, sizeof(phdr));
    1591      107471 :         if (is_sentinel(phdr)) {
    1592             :             // we've reached the sentinel
    1593        2104 :             sentinel_offset = ofs;
    1594        2104 :             return true;
    1595             :         }
    1596             : 
    1597             :         const struct AP_Param::Info *info;
    1598             :         void *ptr;
    1599             : 
    1600      105367 :         info = find_by_header(phdr, &ptr);
    1601      105367 :         if (info != nullptr) {
    1602       87946 :             _storage.read_block(ptr, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
    1603             :         }
    1604             : 
    1605      105367 :         ofs += type_size((enum ap_var_type)phdr.type) + sizeof(phdr);
    1606             :     }
    1607             : 
    1608             :     // we didn't find the sentinel
    1609             :     Debug("no sentinel in load_all");
    1610           0 :     return false;
    1611             : }
    1612             : 
    1613             : /*
    1614             :  * reload from hal.util defaults file or embedded param region
    1615             :  * @last_pass: if this is the last pass on defaults - unknown parameters are
    1616             :  *             ignored but if this is set a warning will be emitted
    1617             :  */
    1618        3868 : void AP_Param::reload_defaults_file(bool last_pass)
    1619             : {
    1620             : #if AP_PARAM_DEFAULTS_FILE_PARSING_ENABLED
    1621             : 
    1622             :     /*
    1623             :       if the HAL specifies a defaults parameter file then override
    1624             :       defaults using that file
    1625             :      */
    1626        3868 :     const char *default_file = hal.util->get_custom_defaults_file();
    1627        3868 :     if (default_file) {
    1628             : #if AP_FILESYSTEM_FILE_READING_ENABLED
    1629        3858 :         load_defaults_file_from_filesystem(default_file, last_pass);
    1630             : #elif defined(HAL_HAVE_AP_ROMFS_EMBEDDED_H)
    1631             :         load_defaults_file_from_romfs(default_file, last_pass);
    1632             : #endif
    1633             :     }
    1634             : 
    1635             : #endif  // AP_PARAM_DEFAULTS_FILE_PARSING_ENABLED
    1636             : 
    1637             : #if AP_PARAM_MAX_EMBEDDED_PARAM > 0
    1638             :     if (param_defaults_data.length != 0) {
    1639             :         load_embedded_param_defaults(last_pass);
    1640             :     }
    1641             : #endif
    1642             : 
    1643             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL && !defined(HAL_BUILD_AP_PERIPH)
    1644        3863 :     hal.util->set_cmdline_parameters();
    1645             : #endif
    1646        3868 : }
    1647             : 
    1648             : #if AP_FILESYSTEM_FILE_READING_ENABLED
    1649        3858 : void AP_Param::load_defaults_file_from_filesystem(const char *default_file, bool last_pass)
    1650             : {
    1651        3858 :     if (load_defaults_file(default_file, last_pass)) {
    1652             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1653        3858 :         printf("Loaded defaults from %s\n", default_file);
    1654             : #endif
    1655             :     } else {
    1656             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    1657           0 :         AP_HAL::panic("Failed to load defaults from %s", default_file);
    1658             : #else
    1659           0 :         printf("Failed to load defaults from %s\n", default_file);
    1660             : #endif
    1661             :     }
    1662        3858 : }
    1663             : #endif  // AP_FILESYSTEM_FILE_READING_ENABLED
    1664             : 
    1665             : #if defined(HAL_HAVE_AP_ROMFS_EMBEDDED_H)
    1666           0 : void AP_Param::load_defaults_file_from_romfs(const char *default_file, bool last_pass)
    1667             : {
    1668           0 :     const char *prefix = "@ROMFS/";
    1669           0 :     if (strncmp(default_file, prefix, strlen(prefix)) != 0) {
    1670             :         // does not start with ROMFS, do not attempt to retrieve from it
    1671           0 :         return;
    1672             :     }
    1673             : 
    1674             :     // filename without the prefix:
    1675           0 :     const char *trimmed_filename = &default_file[strlen(prefix)];
    1676             : 
    1677             :     uint32_t string_length;
    1678           0 :     const uint8_t *text = AP_ROMFS::find_decompress(trimmed_filename, string_length);
    1679           0 :     if (text == nullptr) {
    1680           0 :         return;
    1681             :     }
    1682             : 
    1683           0 :     load_param_defaults((const char*)text, string_length, last_pass);
    1684             : 
    1685           0 :     AP_ROMFS::free(text);
    1686             : 
    1687             : }
    1688             : #endif  // HAL_HAVE_AP_ROMFS_EMBEDDED_H
    1689             : 
    1690             : /* 
    1691             :    Load all variables from EEPROM for a particular object. This is
    1692             :    required for dynamically loaded objects
    1693             :  */
    1694       30311 : void AP_Param::load_object_from_eeprom(const void *object_pointer, const struct GroupInfo *group_info)
    1695             : {
    1696             :     struct Param_header phdr;
    1697             :     uint16_t key;
    1698             : 
    1699       30311 :     if (!find_key_by_pointer(object_pointer, key)) {
    1700           0 :         DEV_PRINTF("ERROR: Unable to find param pointer\n");
    1701           0 :         return;
    1702             :     }
    1703             :     
    1704      308645 :     for (uint8_t i=0; group_info[i].type != AP_PARAM_NONE; i++) {
    1705      278334 :         if (group_info[i].type == AP_PARAM_GROUP) {
    1706       14847 :             ptrdiff_t new_offset = 0;
    1707       14847 :             if (!adjust_group_offset(key, group_info[i], new_offset)) {
    1708           0 :                 continue;
    1709             :             }
    1710       14847 :             const struct GroupInfo *ginfo = get_group_info(group_info[i]);
    1711       14847 :             if (ginfo != nullptr) {
    1712       14847 :                 load_object_from_eeprom((void *)(((ptrdiff_t)object_pointer)+new_offset), ginfo);
    1713             :             }
    1714             :         }
    1715      278334 :         uint16_t ofs = sizeof(AP_Param::EEPROM_header);
    1716    13990066 :         while (ofs < _storage.size()) {
    1717    13990066 :             _storage.read_block(&phdr, ofs, sizeof(phdr));
    1718             :             // note that this is an || not an && for robustness
    1719             :             // against power off while adding a variable
    1720    13990066 :             if (is_sentinel(phdr)) {
    1721             :                 // we've reached the sentinel
    1722      260647 :                 sentinel_offset = ofs;
    1723      260647 :                 break;
    1724             :             }
    1725    13729419 :             if (get_key(phdr) == key) {
    1726             :                 const struct AP_Param::Info *info;
    1727             :                 void *ptr;
    1728             :                 
    1729      625758 :                 info = find_by_header(phdr, &ptr);
    1730      625758 :                 if (info != nullptr) {
    1731      542865 :                     if ((ptrdiff_t)ptr == ((ptrdiff_t)object_pointer)+group_info[i].offset) {
    1732       17687 :                         _storage.read_block(ptr, ofs+sizeof(phdr), type_size((enum ap_var_type)phdr.type));
    1733       17687 :                         break;
    1734             :                     }
    1735             :                 }
    1736             :             }
    1737    13711732 :             ofs += type_size((enum ap_var_type)phdr.type) + sizeof(phdr);
    1738             :         }
    1739             :     }
    1740             : 
    1741       30311 :     if (!done_all_default_params) {
    1742             :         /*
    1743             :           the new subtree may need defaults from defaults.parm
    1744             :          */
    1745          71 :         reload_defaults_file(false);
    1746             :     }
    1747             : 
    1748             :     // reset cached param counter as we may be loading a dynamic var_info
    1749       30311 :     invalidate_count();
    1750             : }
    1751             : 
    1752             : 
    1753             : // return the first variable in _var_info
    1754       12571 : AP_Param *AP_Param::first(ParamToken *token, enum ap_var_type *ptype, float *default_val)
    1755             : {
    1756       12571 :     token->key = 0;
    1757       12571 :     token->group_element = 0;
    1758       12571 :     token->idx = 0;
    1759       12571 :     if (_num_vars == 0) {
    1760           0 :         return nullptr;
    1761             :     }
    1762             :     ptrdiff_t base;
    1763       12571 :     if (!get_base(var_info(0), base)) {
    1764             :         // should be impossible, first var needs to be non-pointer
    1765           0 :         return nullptr;
    1766             :     }
    1767       12571 :     if (ptype != nullptr) {
    1768        3092 :         *ptype = (enum ap_var_type)var_info(0).type;
    1769             :     }
    1770             : #if AP_PARAM_DEFAULTS_ENABLED
    1771       12571 :     if (default_val != nullptr) {
    1772        2081 :         *default_val = get_default_value((AP_Param *)base, var_info(0));
    1773             :     }
    1774       12571 :     check_default((AP_Param *)base, default_val);
    1775             : #endif
    1776       12571 :     return (AP_Param *)base;
    1777             : }
    1778             : 
    1779             : /// Returns the next variable in a group, recursing into groups
    1780             : /// as needed
    1781   308215143 : AP_Param *AP_Param::next_group(const uint16_t vindex, const struct GroupInfo *group_info,
    1782             :                                bool *found_current,
    1783             :                                const uint32_t group_base,
    1784             :                                const uint8_t group_shift,
    1785             :                                const ptrdiff_t group_offset,
    1786             :                                ParamToken *token,
    1787             :                                enum ap_var_type *ptype,
    1788             :                                bool skip_disabled,
    1789             :                                float *default_val)
    1790             : {
    1791             :     enum ap_var_type type;
    1792  2880252681 :     for (uint8_t i=0;
    1793  2880252681 :          (type=(enum ap_var_type)group_info[i].type) != AP_PARAM_NONE;
    1794             :          i++) {
    1795  2608324328 :         if (!check_frame_type(group_info[i].flags)) {
    1796     9525781 :             continue;
    1797             :         }
    1798  2598798547 :         if (type == AP_PARAM_GROUP) {
    1799             :             // a nested group
    1800   326541767 :             const struct GroupInfo *ginfo = get_group_info(group_info[i]);
    1801   326541767 :             if (ginfo == nullptr) {
    1802    36587425 :                 continue;
    1803             :             }
    1804             :             AP_Param *ap;
    1805   305983456 :             ptrdiff_t new_offset = group_offset;
    1806             : 
    1807   305983456 :             if (!adjust_group_offset(vindex, group_info[i], new_offset)) {
    1808    16029114 :                 continue;
    1809             :             }
    1810             : 
    1811   289954342 :             ap = next_group(vindex, ginfo, found_current, group_id(group_info, group_base, i, group_shift),
    1812   289954342 :                             group_shift + _group_level_shift, new_offset, token, ptype, skip_disabled, default_val);
    1813   289954342 :             if (ap != nullptr) {
    1814    17298743 :                 return ap;
    1815             :             }
    1816             :         } else {
    1817  2272256780 :             if (*found_current) {
    1818             :                 // got a new one
    1819    15701763 :                 token->key = vindex;
    1820    15701763 :                 token->group_element = group_id(group_info, group_base, i, group_shift);
    1821    15701763 :                 token->idx = 0;
    1822    15701763 :                 if (ptype != nullptr) {
    1823    15701763 :                     *ptype = type;
    1824             :                 }
    1825             :                 ptrdiff_t base;
    1826    15701763 :                 if (!get_base(var_info(vindex), base)) {
    1827      259999 :                     continue;
    1828             :                 }
    1829             : 
    1830    15441764 :                 AP_Param *ret = (AP_Param*)(base + group_info[i].offset + group_offset);
    1831             : 
    1832    15441764 :                 if (skip_disabled &&
    1833    15319864 :                     _hide_disabled_groups &&
    1834    15319864 :                     group_info[i].type == AP_PARAM_INT8 &&
    1835    32228108 :                     (group_info[i].flags & AP_PARAM_FLAG_ENABLE) &&
    1836     1344580 :                     ((AP_Int8 *)ret)->get() == 0) {
    1837     1253469 :                     token->last_disabled = 1;
    1838             :                 }
    1839             : #if AP_PARAM_DEFAULTS_ENABLED
    1840    15441764 :                 if (default_val != nullptr) {
    1841     2543411 :                     *default_val = get_default_value(ret, group_info[i]);
    1842             :                 }
    1843             : #endif
    1844    15441764 :                 return ret;
    1845             :             }
    1846  2256555017 :             if (group_id(group_info, group_base, i, group_shift) == token->group_element) {
    1847    17734575 :                 *found_current = true;
    1848    17734575 :                 if (token->last_disabled) {
    1849     1253469 :                     token->last_disabled = 0;
    1850     1253469 :                     return nullptr;
    1851             :                 }
    1852    16481106 :                 if (type == AP_PARAM_VECTOR3F && token->idx < 3) {
    1853             :                     // return the next element of the vector as a
    1854             :                     // float
    1855     2292814 :                     token->idx++;
    1856     2292814 :                     if (ptype != nullptr) {
    1857     2292814 :                         *ptype = AP_PARAM_FLOAT;
    1858             :                     }
    1859             :                     ptrdiff_t base;
    1860     2292814 :                     if (!get_base(var_info(vindex), base)) {
    1861           0 :                         continue;
    1862             :                     }
    1863     2292814 :                     ptrdiff_t ofs = base + group_info[i].offset + group_offset;
    1864     2292814 :                     ofs += sizeof(float)*(token->idx - 1u);
    1865             : #if AP_PARAM_DEFAULTS_ENABLED
    1866     2292814 :                     if (default_val != nullptr) {
    1867      372165 :                         *default_val = get_default_value((AP_Param *)ofs, group_info[i]);
    1868             :                     }
    1869             : #endif
    1870     2292814 :                     return (AP_Param *)ofs;
    1871             :                 }
    1872             :             }
    1873             :         }
    1874             :     }
    1875   271928353 :     return nullptr;
    1876             : }
    1877             : 
    1878             : /// Returns the next variable in _var_info, recursing into groups
    1879             : /// as needed
    1880    18431031 : AP_Param *AP_Param::next(ParamToken *token, enum ap_var_type *ptype, bool skip_disabled, float *default_val)
    1881             : {
    1882    18431031 :     uint16_t i = token->key;
    1883    18431031 :     bool found_current = false;
    1884    18431031 :     if (i >= _num_vars) {
    1885             :         // illegal token
    1886           0 :         return nullptr;
    1887             :     }
    1888    18431031 :     enum ap_var_type type = (enum ap_var_type)var_info(i).type;
    1889             : 
    1890             :     // allow Vector3f to be seen as 3 variables. First as a vector,
    1891             :     // then as 3 separate floats
    1892    18431031 :     if (type == AP_PARAM_VECTOR3F && token->idx < 3) {
    1893           0 :         token->idx++;
    1894           0 :         if (ptype != nullptr) {
    1895           0 :             *ptype = AP_PARAM_FLOAT;
    1896             :         }
    1897           0 :         AP_Param *ret = (AP_Param *)(((token->idx - 1u)*sizeof(float))+(ptrdiff_t)var_info(i).ptr);
    1898             : #if AP_PARAM_DEFAULTS_ENABLED
    1899           0 :         if (default_val != nullptr) {
    1900           0 :             *default_val = get_default_value(ret, var_info(i));
    1901             :         }
    1902             : #endif
    1903           0 :         return ret;
    1904             :     }
    1905             : 
    1906    18431031 :     if (type != AP_PARAM_GROUP) {
    1907      696456 :         i++;
    1908      696456 :         found_current = true;
    1909             :     }
    1910    18959514 :     for (; i<_num_vars; i++) {
    1911    18947215 :         const auto &info = var_info(i);
    1912    18947215 :         if (!check_frame_type(info.flags)) {
    1913           0 :             continue;
    1914             :         }
    1915    18947215 :         type = (enum ap_var_type)info.type;
    1916    18947215 :         if (type == AP_PARAM_GROUP) {
    1917    18263061 :             const struct GroupInfo *group_info = get_group_info(info);
    1918    18263061 :             if (group_info == nullptr) {
    1919        2260 :                 continue;
    1920             :             }
    1921    18260801 :             AP_Param *ap = next_group(i, group_info, &found_current, 0, 0, 0, token, ptype, skip_disabled, default_val);
    1922    18260801 :             if (ap != nullptr) {
    1923    17734578 :                 return ap;
    1924             :             }
    1925             :         } else {
    1926             :             // found the next one
    1927      684154 :             token->key = i;
    1928      684154 :             token->group_element = 0;
    1929      684154 :             token->idx = 0;
    1930      684154 :             if (ptype != nullptr) {
    1931      684154 :                 *ptype = type;
    1932             :             }
    1933             : #if AP_PARAM_DEFAULTS_ENABLED
    1934      684154 :             if (default_val != nullptr) {
    1935      111199 :                 *default_val = get_default_value((AP_Param *)info.ptr, info);
    1936             :             }
    1937             : #endif
    1938      684154 :             return (AP_Param *)(info.ptr);
    1939             :         }
    1940             :     }
    1941       12299 :     return nullptr;
    1942             : }
    1943             : 
    1944             : /// Returns the next scalar in _var_info, recursing into groups
    1945             : /// as needed
    1946    17666759 : AP_Param *AP_Param::next_scalar(ParamToken *token, enum ap_var_type *ptype, float *default_val)
    1947             : {
    1948             :     AP_Param *ap;
    1949             :     enum ap_var_type type;
    1950    18431031 :     while ((ap = next(token, &type, true, default_val)) != nullptr && type > AP_PARAM_FLOAT) ;
    1951             : 
    1952    17666759 :     if (ap != nullptr) {
    1953    17654267 :         if (ptype != nullptr) {
    1954     4331898 :             *ptype = type;
    1955             :         }
    1956             :     }
    1957             : #if AP_PARAM_DEFAULTS_ENABLED
    1958    17666759 :     check_default(ap, default_val);
    1959             : #endif
    1960    17666759 :     return ap;
    1961             : }
    1962             : 
    1963             : 
    1964             : /// cast a variable to a float given its type
    1965     4470649 : float AP_Param::cast_to_float(enum ap_var_type type) const
    1966             : {
    1967     4470649 :     switch (type) {
    1968     1419625 :     case AP_PARAM_INT8:
    1969     1419625 :         return ((AP_Int8 *)this)->cast_to_float();
    1970      889783 :     case AP_PARAM_INT16:
    1971      889783 :         return ((AP_Int16 *)this)->cast_to_float();
    1972      347650 :     case AP_PARAM_INT32:
    1973      347650 :         return ((AP_Int32 *)this)->cast_to_float();
    1974     1813591 :     case AP_PARAM_FLOAT:
    1975     1813591 :         return ((AP_Float *)this)->cast_to_float();
    1976           0 :     default:
    1977           0 :         return NAN;
    1978             :     }
    1979             : }
    1980             : 
    1981             : /*
    1982             :   find an old parameter and return it.
    1983             :  */
    1984      342146 : bool AP_Param::find_old_parameter(const struct ConversionInfo *info, AP_Param *value)
    1985             : {
    1986             :     // find the old value in EEPROM.
    1987             :     uint16_t pofs;
    1988             :     AP_Param::Param_header header;
    1989      342146 :     header.type = info->type;
    1990      342146 :     set_key(header, info->old_key);
    1991      342146 :     header.group_element = info->old_group_element;
    1992      342146 :     if (!scan(&header, &pofs)) {
    1993             :         // the old parameter isn't saved in the EEPROM.
    1994      342140 :         return false;
    1995             :     }
    1996             : 
    1997             :     // load the old value from EEPROM
    1998           6 :     _storage.read_block(value, pofs+sizeof(header), type_size((enum ap_var_type)header.type));
    1999           6 :     return true;
    2000             : }
    2001             : 
    2002             : #pragma GCC diagnostic push
    2003             : #pragma GCC diagnostic ignored "-Wformat"
    2004             : // convert one old vehicle parameter to new object parameter
    2005      127076 : void AP_Param::convert_old_parameter(const struct ConversionInfo *info, float scaler, uint8_t flags)
    2006             : {
    2007             :     // a buffer large enough (and aligned) for any parameter value, which
    2008             :     // also avoids a VLA here
    2009      127076 :     AP_Param_value_storage old_value;
    2010      127076 :     AP_Param *ap = (AP_Param *)&old_value;
    2011             : 
    2012      127076 :     if (!find_old_parameter(info, ap)) {
    2013             :         // the old parameter isn't saved in the EEPROM. It was
    2014             :         // probably still set to the default value, which isn't stored
    2015             :         // no need to convert
    2016      127072 :         return;
    2017             :     }
    2018             : 
    2019             :     // find the new variable in the variable structures
    2020             :     enum ap_var_type ptype;
    2021           4 :     AP_Param *ap2 = find(&info->new_name[0], &ptype);
    2022           4 :     if (ap2 == nullptr) {
    2023           0 :         DEV_PRINTF("Unknown conversion '%s'\n", info->new_name);
    2024           0 :         return;
    2025             :     }
    2026             : 
    2027             :     // see if we can load it from EEPROM
    2028           4 :     if (!(flags & CONVERT_FLAG_FORCE) && ap2->configured_in_storage()) {
    2029             :         // the new parameter already has a value set by the user, or
    2030             :         // has already been converted
    2031           0 :         return;
    2032             :     }
    2033             : 
    2034             :     // see if they are the same type and no scaling applied
    2035           4 :     if (ptype == info->type && is_equal(scaler, 1.0f) && flags == 0) {
    2036             :         // copy the value over only if the new parameter does not already
    2037             :         // have the old value (via a default).  Size the copy by the
    2038             :         // parameter type; old_value is large enough for any type, so
    2039             :         // sizeof() it would overrun the destination parameter.
    2040           4 :         const uint8_t value_size = type_size(info->type);
    2041           4 :         if (memcmp(ap2, ap, value_size) != 0) {
    2042           4 :             memcpy(ap2, ap, value_size);
    2043             :             // and save
    2044           4 :             ap2->save();
    2045             :         }
    2046           0 :     } else if (ptype <= AP_PARAM_FLOAT && info->type <= AP_PARAM_FLOAT) {
    2047             :         // perform scalar->scalar conversion
    2048           0 :         float v = ap->cast_to_float(info->type);
    2049           0 :         if (flags & CONVERT_FLAG_REVERSE) {
    2050             :             // convert a _REV parameter to a _REVERSED parameter
    2051           0 :             v = is_equal(v, -1.0f)?1:0;
    2052             :         }
    2053           0 :         if (!is_equal(v,ap2->cast_to_float(ptype))) {
    2054             :             // the value needs to change
    2055           0 :             set_value(ptype, ap2, v * scaler);
    2056           0 :             ap2->save();
    2057             :         }
    2058           0 :     } else {
    2059             :         // can't do vector<->scalar conversion, or different vector types
    2060           0 :         DEV_PRINTF("Bad conversion type '%s'\n", info->new_name);
    2061             :     }
    2062             : }
    2063             : #pragma GCC diagnostic pop
    2064             : 
    2065             : 
    2066             : // convert old vehicle parameters to new object parameters
    2067        4721 : void AP_Param::convert_old_parameters(const struct ConversionInfo *conversion_table, uint8_t table_size, uint8_t flags)
    2068             : {
    2069        4721 :     convert_old_parameters_scaled(conversion_table, table_size, 1.0f, flags);
    2070        4721 : }
    2071             : 
    2072             : // convert old vehicle parameters to new object parameters with scaling - assumes all parameters will have the same scaling factor
    2073       13584 : void AP_Param::convert_old_parameters_scaled(const struct ConversionInfo *conversion_table, uint8_t table_size, float scaler, uint8_t flags)
    2074             : {
    2075       93223 :     for (uint8_t i=0; i<table_size; i++) {
    2076       79639 :         convert_old_parameter(&conversion_table[i], scaler, flags);
    2077             :     }
    2078             :     // we need to flush here to prevent a later set_default_by_name()
    2079             :     // causing a save to be done on a converted parameter
    2080       13584 :     flush();
    2081       13584 : }
    2082             : 
    2083             : // move all parameters from a class to a new location
    2084             : // is_top_level: Is true if the class had its own top level key, param_key. It is false if the class was a subgroup
    2085       22092 : void AP_Param::convert_class(uint16_t param_key, void *object_pointer,
    2086             :                                     const struct AP_Param::GroupInfo *group_info,
    2087             :                                     uint16_t old_index, bool is_top_level, bool recurse_sub_groups)
    2088             : {
    2089       22092 :     const uint8_t group_shift = is_top_level ? 0 : 6;
    2090             : 
    2091      226522 :     for (uint8_t i=0; group_info[i].type != AP_PARAM_NONE; i++) {
    2092             :         struct ConversionInfo info;
    2093      204430 :         info.old_key = param_key;
    2094      204430 :         info.type = (ap_var_type)group_info[i].type;
    2095      204430 :         info.new_name = nullptr;
    2096             : 
    2097      204430 :         uint16_t idx = group_info[i].idx;
    2098      204430 :         if (group_shift != 0 && idx == 0) {
    2099             :             // Note: Index 0 is treated as 63 for group bit shifting purposes. See group_id()
    2100        5408 :             idx = 63;
    2101             :         }
    2102             : 
    2103      204430 :         if (info.type == AP_PARAM_GROUP) {
    2104             :             // Convert subgroups if enabled
    2105        4122 :             if (recurse_sub_groups) {
    2106             :                 // Only recurse once
    2107        4122 :                 convert_class(param_key, (uint8_t *)object_pointer + group_info[i].offset, get_group_info(group_info[i]), idx, false, false);
    2108             :             }
    2109      204428 :             continue;
    2110             :         }
    2111             : 
    2112      200308 :         info.old_group_element = (idx << group_shift) + old_index;
    2113             : 
    2114             :         // a buffer large enough (and aligned) for any parameter value,
    2115             :         // which also avoids a VLA here
    2116      200308 :         AP_Param_value_storage old_value;
    2117      200308 :         AP_Param *ap = (AP_Param *)&old_value;
    2118             : 
    2119      200308 :         if (!AP_Param::find_old_parameter(&info, ap)) {
    2120             :             // the parameter wasn't set in the old eeprom
    2121      200306 :             continue;
    2122             :         }
    2123             : 
    2124           2 :         AP_Param *ap2 = (AP_Param *)(group_info[i].offset + (uint8_t *)object_pointer);
    2125           2 :         if (ap2->configured_in_storage()) {
    2126             :             // user has already set a value, or previous conversion was done
    2127           0 :             continue;
    2128             :         }
    2129             :         // size the copy by the parameter type; old_value is large enough
    2130             :         // for any type, so sizeof() it would overrun the destination
    2131           2 :         memcpy(ap2, ap, type_size(info.type));
    2132             :         // and save
    2133           2 :         ap2->save();
    2134             :     }
    2135             : 
    2136             :     // we need to flush here to prevent a later set_default_by_name()
    2137             :     // causing a save to be done on a converted parameter
    2138       22092 :     flush();
    2139       22092 : }
    2140             : 
    2141             : // convert an object which was stored in a vehicle's G2 into a new
    2142             : // object in AP_Vehicle.cpp:
    2143        2061 : void AP_Param::convert_g2_objects(const void *g2, const G2ObjectConversion g2_conversions[], uint8_t num_conversions)
    2144             : {
    2145             :     // Find G2's Top Level Key
    2146             :     ConversionInfo info;
    2147        2061 :     if (!find_top_level_key_by_pointer(g2, info.old_key)) {
    2148           0 :         return;
    2149             :     }
    2150        9530 :     for (uint8_t i=0; i<num_conversions; i++) {
    2151        7469 :         const auto &c { g2_conversions[i] };
    2152        7469 :         convert_class(info.old_key, c.object_pointer, c.var_info, c.old_index, false);
    2153             :     }
    2154             : }
    2155             : 
    2156        2094 : void AP_Param::convert_toplevel_objects(const TopLevelObjectConversion conversions[], uint8_t num_conversions)
    2157             : {
    2158        4188 :     for (uint8_t i=0; i<num_conversions; i++) {
    2159        2094 :         const auto &c { conversions[i] };
    2160        2094 :         convert_class(c.old_index, c.object_pointer, c.var_info, 0, true);
    2161             :     }
    2162        2094 : }
    2163             : 
    2164             : /*
    2165             :  convert width of a parameter, allowing update to wider scalar values
    2166             :  without changing the parameter indexes
    2167             : */
    2168      102911 : bool AP_Param::_convert_parameter_width(ap_var_type old_ptype, float scale_factor, bool bitmask)
    2169             : {
    2170      102911 :     if (configured_in_storage()) {
    2171             :         // already converted or set by the user
    2172         708 :         return false;
    2173             :     }
    2174             : 
    2175      102203 :     uint32_t group_element = 0;
    2176             :     const struct GroupInfo *ginfo;
    2177      102203 :     struct GroupNesting group_nesting {};
    2178             :     uint8_t idx;
    2179      102203 :     const struct AP_Param::Info *info = find_var_info(&group_element, ginfo, group_nesting, &idx);
    2180             : 
    2181      102203 :     if (info == nullptr) {
    2182        6282 :         return false;
    2183             :     }
    2184             : 
    2185             :     // remember the type
    2186             :     ap_var_type new_ptype;
    2187       95921 :     if (ginfo != nullptr) {
    2188       88354 :         new_ptype = (ap_var_type)ginfo->type;
    2189             :     } else {
    2190        7567 :         new_ptype = (ap_var_type)info->type;
    2191             :     }
    2192             :     
    2193             :     // create the header we will use to scan for the variable
    2194             :     struct Param_header phdr;
    2195       95921 :     phdr.type = old_ptype;
    2196       95921 :     set_key(phdr, info->key);
    2197       95921 :     phdr.group_element = group_element;
    2198             : 
    2199             :     // scan EEPROM to find the right location
    2200             :     uint16_t pofs;
    2201       95921 :     if (!scan(&phdr, &pofs)) {
    2202             :         // it isn't in storage
    2203       95921 :         return false;
    2204             :     }
    2205             : 
    2206             :     // load the old value from EEPROM.  a buffer large enough (and aligned)
    2207             :     // for any parameter value also avoids a VLA here
    2208           0 :     AP_Param_value_storage old_value;
    2209           0 :     _storage.read_block(&old_value, pofs+sizeof(phdr), type_size(old_ptype));
    2210             : 
    2211           0 :     AP_Param *old_ap = (AP_Param *)&old_value;
    2212             : 
    2213           0 :     if (!bitmask) {
    2214             :         // Numeric conversion
    2215             :         // going via float is safe as the only time we would be converting
    2216             :         // from AP_Int32 is when converting to float
    2217           0 :         float old_float_value = old_ap->cast_to_float(old_ptype);
    2218           0 :         set_value(new_ptype, this, old_float_value*scale_factor);
    2219             : 
    2220             :     } else {
    2221             :         // Bitmask conversion, go via uint32
    2222             :         // int8 -1 should convert to int16 255
    2223             :         uint32_t mask;
    2224           0 :         switch (old_ptype) {
    2225           0 :         case AP_PARAM_INT8:
    2226           0 :             mask = (uint8_t)(*(AP_Int8*)old_ap);
    2227           0 :             break;
    2228           0 :         case AP_PARAM_INT16:
    2229           0 :             mask = (uint16_t)(*(AP_Int16*)old_ap);
    2230           0 :             break;
    2231           0 :         case AP_PARAM_INT32:
    2232           0 :             mask = (uint32_t)(*(AP_Int32*)old_ap);
    2233           0 :             break;
    2234           0 :         default:
    2235           0 :             return false;
    2236             :         }
    2237             : 
    2238           0 :         switch (new_ptype) {
    2239           0 :         case AP_PARAM_INT8:
    2240           0 :             ((AP_Int8 *)this)->set(mask);
    2241           0 :             break;
    2242           0 :         case AP_PARAM_INT16:
    2243           0 :             ((AP_Int16 *)this)->set(mask);
    2244           0 :             break;
    2245           0 :         case AP_PARAM_INT32:
    2246           0 :             ((AP_Int32 *)this)->set(mask);
    2247           0 :             break;
    2248           0 :         default:
    2249           0 :             return false;
    2250             :         }
    2251             :     }
    2252             : 
    2253             : 
    2254             :     // force save as the new type
    2255           0 :     save(true);
    2256             : 
    2257           0 :     return true;
    2258             : }
    2259             : 
    2260             : /*
    2261             :   set a parameter to a float value
    2262             :  */
    2263      295683 : void AP_Param::set_float(float value, enum ap_var_type var_type)
    2264             : {
    2265      295683 :     if (isnan(value) || isinf(value)) {
    2266           0 :         return;
    2267             :     }
    2268             : 
    2269             :     // add a small amount before casting parameter values
    2270             :     // from float to integer to avoid truncating to the
    2271             :     // next lower integer value.
    2272      295683 :     float rounding_addition = 0.01f;
    2273             :         
    2274             :     // handle variables with standard type IDs
    2275      295683 :     if (var_type == AP_PARAM_FLOAT) {
    2276      136092 :         ((AP_Float *)this)->set(value);
    2277      159591 :     } else if (var_type == AP_PARAM_INT32) {
    2278        2249 :         if (value < 0) rounding_addition = -rounding_addition;
    2279        2249 :         float v = value+rounding_addition;
    2280        2249 :         v = constrain_float(v, INT32_MIN, INT32_MAX);
    2281        2249 :         ((AP_Int32 *)this)->set(v);
    2282      157342 :     } else if (var_type == AP_PARAM_INT16) {
    2283       97772 :         if (value < 0) rounding_addition = -rounding_addition;
    2284       97772 :         float v = value+rounding_addition;
    2285       97772 :         v = constrain_float(v, INT16_MIN, INT16_MAX);
    2286       97772 :         ((AP_Int16 *)this)->set(v);
    2287       59570 :     } else if (var_type == AP_PARAM_INT8) {
    2288       59570 :         if (value < 0) rounding_addition = -rounding_addition;
    2289       59570 :         float v = value+rounding_addition;
    2290       59570 :         v = constrain_float(v, INT8_MIN, INT8_MAX);
    2291       59570 :         ((AP_Int8 *)this)->set(v);
    2292             :     }
    2293             : }
    2294             : 
    2295             : 
    2296             : /*
    2297             :   parse a parameter file line
    2298             :  */
    2299      704062 : bool AP_Param::parse_param_line(char *line, char **vname, float &value, bool &read_only)
    2300             : {
    2301      704062 :     if (line[0] == '#') {
    2302       68966 :         return false;
    2303             :     }
    2304      635096 :     char *saveptr = nullptr;
    2305             :     /*
    2306             :       note that we need the \r\n as delimiters to prevent us getting
    2307             :       strings with line termination in the results
    2308             :      */
    2309      635096 :     char *pname = strtok_r(line, ", =\t\r\n", &saveptr);
    2310      635096 :     if (pname == nullptr) {
    2311        8144 :         return false;
    2312             :     }
    2313      626952 :     if (strlen(pname) > AP_MAX_NAME_SIZE) {
    2314           0 :         return false;
    2315             :     }
    2316             : 
    2317             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
    2318             :     // Workaround to prevent FORMAT_VERSION in param file resulting in invalid
    2319             :     // EEPROM. For details, see: https://github.com/ArduPilot/ardupilot/issues/15579
    2320      626952 :     if (strcmp(pname, "FORMAT_VERSION") == 0) {
    2321           0 :         ::printf("Warning: Ignoring FORMAT_VERSION in param file\n");
    2322           0 :         return false;
    2323             :     }
    2324             : #endif
    2325             : 
    2326      626952 :     const char *value_s = strtok_r(nullptr, ", =\t\r\n", &saveptr);
    2327      626952 :     if (value_s == nullptr) {
    2328           8 :         return false;
    2329             :     }
    2330      626944 :     value = strtof(value_s, NULL);
    2331      626944 :     *vname = pname;
    2332             : 
    2333      626944 :     const char *flags_s = strtok_r(nullptr, ", =\t\r\n", &saveptr);
    2334      626944 :     if (flags_s && strcmp(flags_s, "@READONLY") == 0) {
    2335           4 :         read_only = true;
    2336             :     } else {
    2337      626940 :         read_only = false;
    2338             :     }
    2339             : 
    2340      626944 :     return true;
    2341             : }
    2342             : 
    2343             : 
    2344             : #if AP_PARAM_DEFAULTS_FILE_PARSING_ENABLED || AP_PARAM_DYNAMIC_ENABLED
    2345             : 
    2346             : // increments num_defaults for each default found in filename
    2347       10104 : bool AP_Param::count_defaults_in_file(const char *filename, uint16_t &num_defaults)
    2348             : {
    2349             :     // try opening the file both in the posix filesystem and using AP::FS
    2350       10104 :     int file_apfs = AP::FS().open(filename, O_RDONLY, true);
    2351       10104 :     if (file_apfs == -1) {
    2352           0 :         return false;
    2353             :     }
    2354             :     char line[100];
    2355             : 
    2356             :     /*
    2357             :       work out how many parameter default structures to allocate
    2358             :      */
    2359      362135 :     while (AP::FS().fgets(line, sizeof(line), file_apfs)) {
    2360             :         char *pname;
    2361             :         float value;
    2362             :         bool read_only;
    2363      352031 :         if (!parse_param_line(line, &pname, value, read_only)) {
    2364       58217 :             continue;
    2365             :         }
    2366             :         enum ap_var_type var_type;
    2367      313472 :         if (!find(pname, &var_type)) {
    2368       19658 :             continue;
    2369             :         }
    2370      293814 :         num_defaults++;
    2371             :     }
    2372       10104 :     AP::FS().close(file_apfs);
    2373             : 
    2374       10104 :     return true;
    2375             : }
    2376             : 
    2377       10104 : bool AP_Param::read_param_defaults_file(const char *filename, bool last_pass, uint16_t &idx)
    2378             : {
    2379             :     // try opening the file both in the posix filesystem and using AP::FS
    2380       10104 :     int file_apfs = AP::FS().open(filename, O_RDONLY, true);
    2381       10104 :     if (file_apfs == -1) {
    2382           0 :         AP_HAL::panic("AP_Param: Failed to re-open defaults file");
    2383             :         return false;
    2384             :     }
    2385             : 
    2386       10104 :     bool done_all = true;
    2387             :     char line[100];
    2388      362135 :     while (AP::FS().fgets(line, sizeof(line), file_apfs)) {
    2389             :         char *pname;
    2390             :         float value;
    2391             :         bool read_only;
    2392      352031 :         if (!parse_param_line(line, &pname, value, read_only)) {
    2393       58217 :             continue;
    2394             :         }
    2395             :         enum ap_var_type var_type;
    2396      313472 :         AP_Param *vp = find(pname, &var_type);
    2397      313472 :         if (!vp) {
    2398             :             if (last_pass) {
    2399             : #if ENABLE_DEBUG
    2400             :                 ::printf("Ignored unknown param %s in defaults file %s\n",
    2401             :                          pname, filename);
    2402             :                 hal.console->printf(
    2403             :                          "Ignored unknown param %s in defaults file %s\n",
    2404             :                          pname, filename);
    2405             : #endif
    2406             :             }
    2407       19658 :             done_all = false;
    2408       19658 :             continue;
    2409             :         }
    2410      293814 :         if (idx >= param_overrides_len) {
    2411           0 :             INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
    2412           0 :             break;
    2413             :         }
    2414      293814 :         param_overrides[idx].object_ptr = vp;
    2415      293814 :         param_overrides[idx].value = value;
    2416      293814 :         param_overrides[idx].read_only = read_only;
    2417      293814 :         if (read_only) {
    2418           2 :             num_read_only++;
    2419             :         }
    2420      293814 :         idx++;
    2421      293814 :         if (!vp->configured_in_storage()) {
    2422      285566 :             vp->set_float(value, var_type);
    2423             :         }
    2424             :     }
    2425       10104 :     AP::FS().close(file_apfs);
    2426             : 
    2427       10104 :     done_all_default_params = done_all;
    2428             : 
    2429       10104 :     return true;
    2430             : }
    2431             : 
    2432             : /*
    2433             :   load a default set of parameters from a file
    2434             :  */
    2435        3858 : bool AP_Param::load_defaults_file(const char *filename, bool last_pass)
    2436             : {
    2437        3858 :     if (filename == nullptr) {
    2438           0 :         return false;
    2439             :     }
    2440             : 
    2441        3858 :     char *mutable_filename = strdup(filename);
    2442        3858 :     if (mutable_filename == nullptr) {
    2443           0 :         AP_HAL::panic("AP_Param: Failed to allocate mutable string");
    2444             :     }
    2445             : 
    2446        3858 :     uint16_t num_defaults = 0;
    2447        3858 :     char *saveptr = nullptr;
    2448        3858 :     for (char *pname = strtok_r(mutable_filename, ",", &saveptr);
    2449       13962 :          pname != nullptr;
    2450       10104 :          pname = strtok_r(nullptr, ",", &saveptr)) {
    2451       10104 :         if (!count_defaults_in_file(pname, num_defaults)) {
    2452           0 :             free(mutable_filename);
    2453           0 :             return false;
    2454             :         }
    2455             :     }
    2456        3858 :     free(mutable_filename);
    2457             : 
    2458        3858 :     delete[] param_overrides;
    2459        3858 :     param_overrides_len = 0;
    2460        3858 :     num_param_overrides = 0;
    2461        3858 :     num_read_only = 0;
    2462             : 
    2463        3858 :     param_overrides = NEW_NOTHROW param_override[num_defaults];
    2464        3858 :     if (param_overrides == nullptr) {
    2465           0 :         AP_HAL::panic("AP_Param: Failed to allocate overrides");
    2466             :         return false;
    2467             :     }
    2468        3858 :     param_overrides_len = num_defaults;
    2469             : 
    2470        3858 :     if (num_defaults == 0) {
    2471           0 :         return true;
    2472             :     }
    2473             : 
    2474        3858 :     saveptr = nullptr;
    2475        3858 :     mutable_filename = strdup(filename);
    2476        3858 :     if (mutable_filename == nullptr) {
    2477           0 :         AP_HAL::panic("AP_Param: Failed to allocate mutable string");
    2478             :     }
    2479        3858 :     uint16_t idx = 0;
    2480        3858 :     for (char *pname = strtok_r(mutable_filename, ",", &saveptr);
    2481       13962 :          pname != nullptr;
    2482       10104 :          pname = strtok_r(nullptr, ",", &saveptr)) {
    2483       10104 :         if (!read_param_defaults_file(pname, last_pass, idx)) {
    2484           0 :             free(mutable_filename);
    2485           0 :             return false;
    2486             :         }
    2487             :     }
    2488        3858 :     free(mutable_filename);
    2489             : 
    2490        3858 :     num_param_overrides = num_defaults;
    2491             : 
    2492             : #if AP_PARAM_DEFAULTS_ENABLED
    2493        3858 :     purge_defaults_list_overrides();
    2494             : #endif
    2495             : 
    2496        3858 :     return true;
    2497             : }
    2498             : #endif // AP_PARAM_DEFAULTS_FILE_PARSING_ENABLED
    2499             : 
    2500             : #if AP_PARAM_MAX_EMBEDDED_PARAM > 0 || defined(HAL_HAVE_AP_ROMFS_EMBEDDED_H)
    2501             : /*
    2502             :   count the number of parameter defaults present in supplied string
    2503             :  */
    2504           0 : bool AP_Param::count_param_defaults(const volatile char *ptr, int32_t length, uint16_t &count)
    2505             : {
    2506           0 :     count = 0;
    2507             :     
    2508           0 :     while (length>0) {
    2509             :         char line[100];
    2510             :         char *pname;
    2511             :         float value;
    2512             :         bool read_only;
    2513             :         uint16_t i;
    2514           0 :         uint16_t n = length;
    2515           0 :         for (i=0;i<n;i++) {
    2516           0 :             if (ptr[i] == '\n') {
    2517           0 :                 break;
    2518             :             }
    2519             :         }
    2520             : 
    2521           0 :         uint16_t linelen = MIN(i,sizeof(line)-1);
    2522           0 :         memcpy(line, (void *)ptr, linelen);
    2523           0 :         line[linelen] = 0;
    2524             : 
    2525           0 :         length -= i+1;
    2526           0 :         ptr += i+1;
    2527             :         
    2528           0 :         if (line[0] == '#' || line[0] == 0) {
    2529           0 :             continue;
    2530             :         }
    2531             : 
    2532           0 :         if (!parse_param_line(line, &pname, value, read_only)) {
    2533           0 :             continue;
    2534             :         }
    2535             : 
    2536             :         enum ap_var_type var_type;
    2537           0 :         if (!find(pname, &var_type)) {
    2538           0 :             continue;
    2539             :         }
    2540             : 
    2541           0 :         count++;
    2542             :     }
    2543           0 :     return true;
    2544             : }
    2545             : 
    2546             : /*
    2547             :  *  load parameter defaults from supplied string
    2548             :  */
    2549           0 : void AP_Param::load_param_defaults(const volatile char *ptr, int32_t length, bool last_pass)
    2550             : {
    2551           0 :     delete[] param_overrides;
    2552           0 :     param_overrides = nullptr;
    2553           0 :     param_overrides_len = 0;
    2554           0 :     num_param_overrides = 0;
    2555           0 :     num_read_only = 0;
    2556             : 
    2557           0 :     uint16_t num_defaults = 0;
    2558           0 :     if (!count_param_defaults(ptr, length, num_defaults)) {
    2559           0 :         return;
    2560             :     }
    2561             : 
    2562           0 :     param_overrides = NEW_NOTHROW param_override[num_defaults];
    2563           0 :     if (param_overrides == nullptr) {
    2564           0 :         AP_HAL::panic("AP_Param: Failed to allocate overrides");
    2565             :         return;
    2566             :     }
    2567             : 
    2568           0 :     param_overrides_len = num_defaults;
    2569             : 
    2570           0 :     uint16_t idx = 0;
    2571             :     
    2572           0 :     while (idx < num_defaults && length > 0) {
    2573             :         char line[100];
    2574             :         char *pname;
    2575             :         float value;
    2576             :         bool read_only;
    2577             :         uint16_t i;
    2578           0 :         uint16_t n = length;
    2579           0 :         for (i=0;i<n;i++) {
    2580           0 :             if (ptr[i] == '\n') {
    2581           0 :                 break;
    2582             :             }
    2583             :         }
    2584             : 
    2585           0 :         uint16_t linelen = MIN(i,sizeof(line)-1);
    2586           0 :         memcpy(line, (void *)ptr, linelen);
    2587           0 :         line[linelen] = 0;
    2588             : 
    2589           0 :         length -= i+1;
    2590           0 :         ptr += i+1;
    2591             : 
    2592           0 :         if (line[0] == '#' || line[0] == 0) {
    2593           0 :             continue;
    2594             :         }
    2595             :         
    2596           0 :         if (!parse_param_line(line, &pname, value, read_only)) {
    2597           0 :             continue;
    2598             :         }
    2599             :         enum ap_var_type var_type;
    2600           0 :         AP_Param *vp = find(pname, &var_type);
    2601           0 :         if (!vp) {
    2602             :             if (last_pass) {
    2603             : #if ENABLE_DEBUG && (AP_PARAM_MAX_EMBEDDED_PARAM > 0)
    2604             :                 ::printf("Ignored unknown param %s from embedded region (offset=%u)\n",
    2605             :                          pname, unsigned(ptr - param_defaults_data.data));
    2606             :                 hal.console->printf(
    2607             :                          "Ignored unknown param %s from embedded region (offset=%u)\n",
    2608             :                          pname, unsigned(ptr - param_defaults_data.data));
    2609             : #endif
    2610             :             }
    2611           0 :             continue;
    2612             :         }
    2613           0 :         param_overrides[idx].object_ptr = vp;
    2614           0 :         param_overrides[idx].value = value;
    2615           0 :         param_overrides[idx].read_only = read_only;
    2616           0 :         if (read_only) {
    2617           0 :             num_read_only++;
    2618             :         }
    2619           0 :         idx++;
    2620           0 :         if (!vp->configured_in_storage()) {
    2621           0 :             vp->set_float(value, var_type);
    2622             :         }
    2623             :     }
    2624           0 :     num_param_overrides = num_defaults;
    2625             : 
    2626             : #if AP_PARAM_DEFAULTS_ENABLED
    2627           0 :     purge_defaults_list_overrides();
    2628             : #endif
    2629             : }
    2630             : #endif // AP_PARAM_MAX_EMBEDDED_PARAM > 0 || defined(HAL_HAVE_AP_ROMFS_EMBEDDED_H)
    2631             : 
    2632             : 
    2633             : #if AP_PARAM_MAX_EMBEDDED_PARAM > 0
    2634             : /*
    2635             :  * load a default set of parameters from a embedded parameter region
    2636             :  * @last_pass: if this is the last pass on defaults - unknown parameters are
    2637             :  *             ignored but if this is set a warning will be emitted
    2638             :  */
    2639             : void AP_Param::load_embedded_param_defaults(bool last_pass)
    2640             : {
    2641             :     load_param_defaults(param_defaults_data.data, param_defaults_data.length, last_pass);
    2642             : }
    2643             : #endif  // AP_PARAM_MAX_EMBEDDED_PARAM > 0
    2644             : 
    2645             : 
    2646             : /* 
    2647             :    find a default value given a pointer to a default value in flash
    2648             :  */
    2649     9735248 : float AP_Param::get_default_value(const AP_Param *vp, const struct GroupInfo &info)
    2650             : {
    2651   245060004 :     for (uint16_t i=0; i<num_param_overrides; i++) {
    2652   235463042 :         if (vp == param_overrides[i].object_ptr) {
    2653      138286 :             return param_overrides[i].value;
    2654             :         }
    2655             :     }
    2656     9596962 :     if ((info.flags & AP_PARAM_FLAG_DEFAULT_POINTER) != 0) {
    2657     1965651 :         return *((float*)((ptrdiff_t)vp - info.def_value_offset));
    2658             :     }
    2659     7631311 :     return info.def_value;
    2660             : }
    2661             : 
    2662      260332 : float AP_Param::get_default_value(const AP_Param *vp, const struct Info &info)
    2663             : {
    2664     9218979 :     for (uint16_t i=0; i<num_param_overrides; i++) {
    2665     9001522 :         if (vp == param_overrides[i].object_ptr) {
    2666       42875 :             return param_overrides[i].value;
    2667             :         }
    2668             :     }
    2669      217457 :     if ((info.flags & AP_PARAM_FLAG_DEFAULT_POINTER) != 0) {
    2670           0 :         return *((float*)((ptrdiff_t)vp - info.def_value_offset));
    2671             :     }
    2672      217457 :     return info.def_value;
    2673             : }
    2674             : 
    2675             : 
    2676       77138 : void AP_Param::send_parameter(const char *name, enum ap_var_type var_type, uint8_t idx) const
    2677             : {
    2678       77138 :     if (idx != 0 && var_type == AP_PARAM_VECTOR3F) {
    2679         832 :         var_type = AP_PARAM_FLOAT;
    2680             :     }
    2681       77138 :     if (var_type > AP_PARAM_VECTOR3F) {
    2682             :         // invalid
    2683       71972 :         return;
    2684             :     }
    2685       77138 :     if (var_type != AP_PARAM_VECTOR3F) {
    2686             :         // nice and simple for scalar types
    2687       71972 :         GCS_SEND_PARAM(name, var_type, cast_to_float(var_type));
    2688       71972 :         return;
    2689             :     }
    2690             : 
    2691             :     // for vectors we need to send 3 messages. Note that we also come here for the case
    2692             :     // of a set of the first element of a AP_Vector3f. This happens as the ap->save() call can't
    2693             :     // distinguish between a vector and scalar save. It means that setting first element of a vector
    2694             :     // via MAVLink results in sending all 3 elements to the GCS
    2695             : #if HAL_GCS_ENABLED
    2696        5166 :     const Vector3f &v = ((AP_Vector3f *)this)->get();
    2697             :     char name2[AP_MAX_NAME_SIZE+1];
    2698        5166 :     strncpy(name2, name, AP_MAX_NAME_SIZE);
    2699        5166 :     name2[AP_MAX_NAME_SIZE] = 0;
    2700        5166 :     char &name_axis = name2[strlen(name)-1];
    2701             :     
    2702        5166 :     name_axis = 'X';
    2703        5166 :     GCS_SEND_PARAM(name2, AP_PARAM_FLOAT, v.x);
    2704        5166 :     name_axis = 'Y';
    2705        5166 :     GCS_SEND_PARAM(name2, AP_PARAM_FLOAT, v.y);
    2706        5166 :     name_axis = 'Z';
    2707        5166 :     GCS_SEND_PARAM(name2, AP_PARAM_FLOAT, v.z);
    2708             : #endif // HAL_GCS_ENABLED
    2709             : }
    2710             : 
    2711             : /*
    2712             :   return count of all scalar parameters.
    2713             :   Note that this function may be called from the IO thread, so needs
    2714             :   to be thread safe
    2715             :  */
    2716   470852707 : uint16_t AP_Param::count_parameters(void)
    2717             : {
    2718             :     // if we haven't cached the parameter count yet...
    2719   470852707 :     WITH_SEMAPHORE(_count_sem);
    2720   470852707 :     if (_parameter_count != 0 &&
    2721   470850613 :         _count_marker == _count_marker_done) {
    2722   470843289 :         return _parameter_count;
    2723             :     }
    2724             :     /*
    2725             :       cope with another thread invalidating the count while we are
    2726             :       counting
    2727             :      */
    2728        9418 :     uint8_t limit = 4;
    2729        9418 :     while ((_parameter_count == 0 ||
    2730       28376 :             _count_marker != _count_marker_done) &&
    2731        9479 :            limit--) {
    2732             :         AP_Param  *vp;
    2733        9479 :         AP_Param::ParamToken token {};
    2734        9479 :         uint16_t count = 0;
    2735        9479 :         uint16_t marker = _count_marker;
    2736             : 
    2737        9479 :         for (vp = AP_Param::first(&token, nullptr);
    2738    13341327 :              vp != nullptr;
    2739    13331848 :              vp = AP_Param::next_scalar(&token, nullptr)) {
    2740    13331848 :             count++;
    2741             :         }
    2742        9479 :         _parameter_count = count;
    2743        9479 :         _count_marker_done = marker;
    2744             :     }
    2745        9418 :     return _parameter_count;
    2746   470852707 : }
    2747             : 
    2748             : /*
    2749             :   invalidate parameter count cache
    2750             :  */
    2751       41882 : void AP_Param::invalidate_count(void)
    2752             : {
    2753             :     // we don't take the semaphore here as we don't want to block. The
    2754             :     // not-equal test is strong enough to ensure we get the right
    2755             :     // answer
    2756       41882 :     _count_marker++;
    2757       41882 : }
    2758             : 
    2759             : /*
    2760             :   set a default value by name
    2761             :  */
    2762       29581 : bool AP_Param::set_default_by_name(const char *name, float value)
    2763             : {
    2764             :     enum ap_var_type vtype;
    2765       29581 :     AP_Param *vp = find(name, &vtype);
    2766       29581 :     if (vp == nullptr) {
    2767           0 :         return false;
    2768             :     }
    2769       29581 :     switch (vtype) {
    2770        1079 :     case AP_PARAM_INT8:
    2771        1079 :         ((AP_Int8 *)vp)->set_default(value);
    2772        1079 :         return true;
    2773        1835 :     case AP_PARAM_INT16:
    2774        1835 :         ((AP_Int16 *)vp)->set_default(value);
    2775        1835 :         return true;
    2776         274 :     case AP_PARAM_INT32:
    2777         274 :         ((AP_Int32 *)vp)->set_default(value);
    2778         274 :         return true;
    2779       26393 :     case AP_PARAM_FLOAT:
    2780       26393 :         ((AP_Float *)vp)->set_default(value);
    2781       26393 :         return true;
    2782           0 :     default:
    2783           0 :         break;
    2784             :     }
    2785             :     // not a supported type
    2786           0 :     return false;
    2787             : }
    2788             : 
    2789             : /*
    2790             :   set parameter defaults from a defaults_struct table
    2791             :   sends GCS message and panics (in SITL only) if parameter is not found
    2792             :  */
    2793         368 : void AP_Param::set_defaults_from_table(const struct defaults_table_struct *table, uint8_t count)
    2794             : {
    2795        7616 :     for (uint8_t i=0; i<count; i++) {
    2796        7248 :         if (!AP_Param::set_default_by_name(table[i].name, table[i].value)) {
    2797           0 :             AP_BoardConfig::config_error("param deflt fail:%s", table[i].name);
    2798             :         }
    2799             :     }
    2800         368 : }
    2801             : 
    2802             : /*
    2803             :   set a value by name
    2804             :  */
    2805          83 : bool AP_Param::set_by_name(const char *name, float value)
    2806             : {
    2807             :     enum ap_var_type vtype;
    2808          83 :     AP_Param *vp = find(name, &vtype);
    2809          83 :     if (vp == nullptr) {
    2810           0 :         return false;
    2811             :     }
    2812          83 :     switch (vtype) {
    2813           0 :     case AP_PARAM_INT8:
    2814           0 :         ((AP_Int8 *)vp)->set(value);
    2815           0 :         return true;
    2816           0 :     case AP_PARAM_INT16:
    2817           0 :         ((AP_Int16 *)vp)->set(value);
    2818           0 :         return true;
    2819           0 :     case AP_PARAM_INT32:
    2820           0 :         ((AP_Int32 *)vp)->set(value);
    2821           0 :         return true;
    2822          83 :     case AP_PARAM_FLOAT:
    2823          83 :         ((AP_Float *)vp)->set(value);
    2824          83 :         return true;
    2825           0 :     default:
    2826           0 :         break;
    2827             :     }
    2828             :     // not a supported type
    2829           0 :     return false;
    2830             : }
    2831             : 
    2832             : /*
    2833             :   get a value by name
    2834             :  */
    2835        4700 : bool AP_Param::get(const char *name, float &value)
    2836             : {
    2837             :     enum ap_var_type vtype;
    2838        4700 :     AP_Param *vp = find(name, &vtype);
    2839        4700 :     if (vp == nullptr) {
    2840           0 :         return false;
    2841             :     }
    2842        4700 :     switch (vtype) {
    2843        2140 :     case AP_PARAM_INT8:
    2844        2140 :         value = ((AP_Int8 *)vp)->get();
    2845        2140 :         break;
    2846        1407 :     case AP_PARAM_INT16:
    2847        1407 :         value = ((AP_Int16 *)vp)->get();
    2848        1407 :         break;
    2849             : 
    2850         226 :     case AP_PARAM_INT32:
    2851         226 :         value = ((AP_Int32 *)vp)->get();
    2852         226 :         break;
    2853             : 
    2854         927 :     case AP_PARAM_FLOAT:
    2855         927 :         value = ((AP_Float *)vp)->get();
    2856         927 :         break;
    2857             : 
    2858           0 :     default:
    2859             :         // not a supported type
    2860           0 :         return false;
    2861             :     }
    2862        4700 :     return true;
    2863             : }
    2864             : 
    2865             : /*
    2866             :   set and save a value by name
    2867             :  */
    2868          58 : bool AP_Param::set_and_save_by_name(const char *name, float value)
    2869             : {
    2870             :     enum ap_var_type vtype;
    2871          58 :     AP_Param *vp = find(name, &vtype);
    2872          58 :     if (vp == nullptr) {
    2873           0 :         return false;
    2874             :     }
    2875          58 :     switch (vtype) {
    2876          16 :     case AP_PARAM_INT8:
    2877          16 :         ((AP_Int8 *)vp)->set_and_save(value);
    2878          16 :         return true;
    2879          28 :     case AP_PARAM_INT16:
    2880          28 :         ((AP_Int16 *)vp)->set_and_save(value);
    2881          28 :         return true;
    2882           2 :     case AP_PARAM_INT32:
    2883           2 :         ((AP_Int32 *)vp)->set_and_save(value);
    2884           2 :         return true;
    2885          12 :     case AP_PARAM_FLOAT:
    2886          12 :         ((AP_Float *)vp)->set_and_save(value);
    2887          12 :         return true;
    2888           0 :     default:
    2889           0 :         break;
    2890             :     }
    2891             :     // not a supported type
    2892           0 :     return false;
    2893             : }
    2894             : 
    2895             : /*
    2896             :   set and save a value by name
    2897             :  */
    2898           0 : bool AP_Param::set_and_save_by_name_ifchanged(const char *name, float value)
    2899             : {
    2900             :     enum ap_var_type vtype;
    2901           0 :     AP_Param *vp = find(name, &vtype);
    2902           0 :     if (vp == nullptr) {
    2903           0 :         return false;
    2904             :     }
    2905           0 :     switch (vtype) {
    2906           0 :     case AP_PARAM_INT8:
    2907           0 :         ((AP_Int8 *)vp)->set_and_save_ifchanged(value);
    2908           0 :         return true;
    2909           0 :     case AP_PARAM_INT16:
    2910           0 :         ((AP_Int16 *)vp)->set_and_save_ifchanged(value);
    2911           0 :         return true;
    2912           0 :     case AP_PARAM_INT32:
    2913           0 :         ((AP_Int32 *)vp)->set_and_save_ifchanged(value);
    2914           0 :         return true;
    2915           0 :     case AP_PARAM_FLOAT:
    2916           0 :         ((AP_Float *)vp)->set_and_save_ifchanged(value);
    2917           0 :         return true;
    2918           0 :     default:
    2919           0 :         break;
    2920             :     }
    2921             :     // not a supported type
    2922           0 :     return false;
    2923             : }
    2924             : 
    2925             : #if AP_PARAM_DEFAULTS_ENABLED
    2926    17679317 : void AP_Param::check_default(AP_Param *ap, float *default_value)
    2927             : {
    2928    17679317 :     if (default_value == nullptr || ap == nullptr) {
    2929    14774547 :         return;
    2930             :     }
    2931     2904770 :     if (default_list != nullptr) {
    2932   191414873 :         for (defaults_list *item = default_list; item; item = item->next) {
    2933   188552992 :             if (item->ap == ap) {
    2934       42889 :                 *default_value = item->val;
    2935       42889 :                 return;
    2936             :             }
    2937             :         }
    2938             :     }
    2939             : }
    2940             : 
    2941             : /*
    2942             :   Remove default_list entries that are in param_overrides.  Constructors may
    2943             :   call add_default() before param_overrides is populated, leaving stale nodes.
    2944             :  */
    2945             : #if AP_PARAM_DEFAULTS_ENABLED
    2946        3858 : void AP_Param::purge_defaults_list_overrides(void)
    2947             : {
    2948        3858 :     defaults_list **prev = &default_list;
    2949        3858 :     defaults_list *item = default_list;
    2950      179975 :     while (item != nullptr) {
    2951      176117 :         bool found = false;
    2952    13826622 :         for (uint16_t i = 0; i < num_param_overrides; i++) {
    2953    13653929 :             if (item->ap == param_overrides[i].object_ptr) {
    2954        3424 :                 found = true;
    2955        3424 :                 break;
    2956             :             }
    2957             :         }
    2958      176117 :         if (found) {
    2959        3424 :             *prev = item->next;
    2960        3424 :             defaults_list *to_delete = item;
    2961        3424 :             item = item->next;
    2962        3424 :             delete to_delete;
    2963             :         } else {
    2964      172693 :             prev = &item->next;
    2965      172693 :             item = item->next;
    2966             :         }
    2967             :     }
    2968        3858 : }
    2969             : #endif // AP_PARAM_DEFAULTS_ENABLED
    2970             : 
    2971      158573 : void AP_Param::add_default(AP_Param *ap, float v)
    2972             : {
    2973             :     // Embedded defaults trump runtime, don't allow override
    2974     5813470 :     for (uint16_t i=0; i<num_param_overrides; i++) {
    2975     5659090 :         if (ap == param_overrides[i].object_ptr) {
    2976        4193 :             return;
    2977             :         }
    2978             :     }
    2979             : 
    2980      154380 :     if (default_list != nullptr) {
    2981             :         // check is param is already in list
    2982     4934528 :         for (defaults_list *item = default_list; item; item = item->next) {
    2983             :             // update existing entry
    2984     4795229 :             if (item->ap == ap) {
    2985       12981 :                 item->val = v;
    2986       12981 :                 return;
    2987             :             }
    2988             :         }
    2989             :     }
    2990             : 
    2991             :     // add to list
    2992      141399 :     defaults_list *new_item = NEW_NOTHROW defaults_list;
    2993      141399 :     if (new_item == nullptr) {
    2994           0 :         return;
    2995             :     }
    2996      141399 :     new_item->ap = ap;
    2997      141399 :     new_item->val = v;
    2998      141399 :     new_item->next = default_list;
    2999      141399 :     default_list = new_item;
    3000             : }
    3001             : #endif // AP_PARAM_DEFAULTS_ENABLED
    3002             : 
    3003             : 
    3004             : #if AP_PARAM_KEY_DUMP
    3005             : /*
    3006             :   do not remove this show_all() code, it is essential for debugging
    3007             :   and creating conversion tables
    3008             :  */
    3009             : 
    3010             : // print the value of all variables
    3011             : void AP_Param::show(const AP_Param *ap, const char *s,
    3012             :                     enum ap_var_type type, AP_HAL::BetterStream *port)
    3013             : {
    3014             :     switch (type) {
    3015             :     case AP_PARAM_INT8:
    3016             :         ::printf("%s: %d\n", s, (int)((AP_Int8 *)ap)->get());
    3017             :         break;
    3018             :     case AP_PARAM_INT16:
    3019             :         ::printf("%s: %d\n", s, (int)((AP_Int16 *)ap)->get());
    3020             :         break;
    3021             :     case AP_PARAM_INT32:
    3022             :         ::printf("%s: %ld\n", s, (long)((AP_Int32 *)ap)->get());
    3023             :         break;
    3024             :     case AP_PARAM_FLOAT:
    3025             :         ::printf("%s: %f\n", s, (double)((AP_Float *)ap)->get());
    3026             :         break;
    3027             :     default:
    3028             :         break;
    3029             :     }
    3030             : }
    3031             : 
    3032             : // print the value of all variables
    3033             : void AP_Param::show(const AP_Param *ap, const ParamToken &token,
    3034             :                     enum ap_var_type type, AP_HAL::BetterStream *port)
    3035             : {
    3036             :     char s[AP_MAX_NAME_SIZE+1];
    3037             :     ap->copy_name_token(token, s, sizeof(s), true);
    3038             :     s[AP_MAX_NAME_SIZE] = 0;
    3039             :     show(ap, s, type, port);
    3040             : }
    3041             : 
    3042             : // print the value of all variables
    3043             : void AP_Param::show_all(AP_HAL::BetterStream *port, bool showKeyValues)
    3044             : {
    3045             :     ParamToken token;
    3046             :     AP_Param *ap;
    3047             :     enum ap_var_type type;
    3048             :     float default_value = NaNf;  // from logger quiet_nanf
    3049             : 
    3050             :     for (ap=AP_Param::first(&token, &type, &default_value);
    3051             :          ap;
    3052             :          ap=AP_Param::next_scalar(&token, &type, &default_value)) {
    3053             :         if (showKeyValues) {
    3054             :             ::printf("Key %u: Index %u: GroupElement %u : Default %f  :", (unsigned)var_info(token.key).key, (unsigned)token.idx, (unsigned)token.group_element, default_value);
    3055             :             default_value = NaNf;
    3056             :         }
    3057             :         show(ap, token, type, port);
    3058             :         hal.scheduler->delay(1);
    3059             :     }
    3060             : 
    3061             : #if AP_PARAM_DEFAULTS_ENABLED
    3062             :     uint16_t list_len = 0;
    3063             :     if (default_list != nullptr) {
    3064             :         for (defaults_list *item = default_list; item; item = item->next) {
    3065             :             list_len++;
    3066             :         }
    3067             :     }
    3068             :     ::printf("Defaults list containts %i params (%li bytes)\n", list_len, list_len*sizeof(defaults_list));
    3069             : #endif
    3070             : }
    3071             : #endif // AP_PARAM_KEY_DUMP
    3072             : 
    3073             : 
    3074             : #if AP_PARAM_DYNAMIC_ENABLED
    3075             : /*
    3076             :   allow for dynamically added parameter tables from scripts
    3077             : 
    3078             :   The layout we create is as follows:
    3079             :     - a top level Info with the given prefix, using one of the 10 possible slots in _var_info_dynamic
    3080             :     - a dynamically allocated GroupInfo table, never freed, of size (num_params+2)
    3081             :     - the GroupInfo table has an initial AP_Int32 hidden entry with a 32 bit CRC of the prefix
    3082             :     - the last GroupInfo is a footer
    3083             : */
    3084          75 : bool AP_Param::add_table(uint8_t _key, const char *prefix, uint8_t num_params)
    3085             : {
    3086             :     // check if the key already exists. We only check base params to allow
    3087             :     // for scripting reload without a conflict
    3088          75 :     uint16_t key = uint16_t(_key) + AP_PARAM_DYNAMIC_KEY_BASE;
    3089        9049 :     for (uint16_t i=0; i<_num_vars_base; i++) {
    3090        8974 :         if (var_info(i).key == key) {
    3091           0 :             return false;
    3092             :         }
    3093             :     }
    3094          75 :     if (num_params > 63) {
    3095           0 :         return false;
    3096             :     }
    3097             : 
    3098             :     // we use a crc of the prefix to ensure the table key isn't re-used
    3099          75 :     const int32_t crc = int32_t(crc32_small(0, (const uint8_t *)prefix, strlen(prefix)));
    3100             :     int32_t current_crc;
    3101          75 :     if (load_int32(key, 0, current_crc) && current_crc != crc) {
    3102             :         // crc mismatch, we have a conflict with an existing use of this key
    3103           0 :         return false;
    3104             :     }
    3105             : 
    3106             :     // create the dynamic table if needed. This is never freed
    3107          75 :     if (_var_info_dynamic == nullptr) {
    3108          43 :         _var_info_dynamic = (Info *)calloc(AP_PARAM_MAX_DYNAMIC, sizeof(struct Info));
    3109          43 :         if (_var_info_dynamic == nullptr) {
    3110           0 :             return false;
    3111             :         }
    3112         473 :         for (uint8_t i=0; i<AP_PARAM_MAX_DYNAMIC; i++) {
    3113         430 :             auto &info = _var_info_dynamic[i];
    3114         430 :             info.type = AP_PARAM_NONE;
    3115         430 :             info.name = _empty_string;
    3116         430 :             info.key = 0xFFFF;
    3117         430 :             info.ptr = nullptr;
    3118         430 :             info.group_info = nullptr;
    3119         430 :             info.flags = 0;
    3120             :         }
    3121             :         // make tables available
    3122          43 :         _num_vars += AP_PARAM_MAX_DYNAMIC;
    3123             :     }
    3124             : 
    3125             :     // find existing key (allows for script reload)
    3126             :     uint8_t i;
    3127         726 :     for (i=0; i<AP_PARAM_MAX_DYNAMIC; i++) {
    3128         661 :         auto &info = _var_info_dynamic[i];
    3129         661 :         if (info.type != AP_PARAM_NONE && info.key == key) {
    3130          10 :             if (_dynamic_table_sizes[i] != 0 &&
    3131          10 :                 num_params > _dynamic_table_sizes[i]) {
    3132             :                 // can't expand the table at runtime
    3133           0 :                 return false;
    3134             :             }
    3135          10 :             if (strcmp(prefix, info.name) != 0) {
    3136             :                 // prefix has changed, reject as two scripts running
    3137             :                 // with the same key
    3138           0 :                 return false;
    3139             :             }
    3140          10 :             break;
    3141             :         }
    3142             :     }
    3143             : 
    3144          75 :     if (i == AP_PARAM_MAX_DYNAMIC) {
    3145             :         // find an unused slot
    3146         108 :         for (i=0; i<AP_PARAM_MAX_DYNAMIC; i++) {
    3147         108 :             auto &info = _var_info_dynamic[i];
    3148         108 :             if (info.type == AP_PARAM_NONE ) {
    3149          65 :                 break;
    3150             :             }
    3151             :         }
    3152             :     }
    3153             : 
    3154          75 :     if (i == AP_PARAM_MAX_DYNAMIC) {
    3155             :         // no empty slots
    3156           0 :         return false;
    3157             :     }
    3158             : 
    3159          75 :     auto &info = _var_info_dynamic[i];
    3160             : 
    3161             :     // create memory for the array of floats if needed
    3162             :     // first float is used for the crc
    3163          75 :     if (info.ptr == nullptr) {
    3164          65 :         info.ptr = calloc(num_params+1, sizeof(float));
    3165          65 :         if (info.ptr == nullptr) {
    3166           0 :             return false;
    3167             :         }
    3168             :     }
    3169             : 
    3170             :     // allocate the name
    3171          75 :     if (info.name == _empty_string) {
    3172          65 :         info.name = strdup(prefix);
    3173          65 :         if (info.name == nullptr) {
    3174           0 :             free(const_cast<void*>(info.ptr));
    3175           0 :             info.ptr = nullptr;
    3176           0 :             info.name = _empty_string;
    3177           0 :             return false;
    3178             :         }
    3179             :     }
    3180             : 
    3181             :     // if it doesn't exist then create the table
    3182          75 :     if (info.group_info == nullptr) {
    3183          65 :         info.group_info = (GroupInfo *)calloc(num_params+2, sizeof(GroupInfo));
    3184          65 :         if (info.group_info == nullptr) {
    3185           0 :             free(const_cast<void*>(info.ptr));
    3186           0 :             free(const_cast<char*>(info.name));
    3187           0 :             info.ptr = nullptr;
    3188           0 :             info.name = _empty_string;
    3189           0 :             return false;
    3190             :         }
    3191             :     }
    3192             :     // fill in footer for all entries
    3193        1305 :     for (uint8_t gi=1; gi<num_params+2; gi++) {
    3194        1230 :         auto &ginfo = const_cast<GroupInfo*>(info.group_info)[gi];
    3195        1230 :         ginfo.name = _empty_string;
    3196        1230 :         ginfo.idx = 0xff;
    3197        1230 :         ginfo.flags = AP_PARAM_FLAG_HIDDEN;
    3198             :     }
    3199             :     // hidden first parameter containing AP_Int32 crc
    3200          75 :     auto &hinfo = const_cast<GroupInfo*>(info.group_info)[0];
    3201          75 :     hinfo.flags = AP_PARAM_FLAG_HIDDEN;
    3202          75 :     hinfo.name = _empty_string;
    3203          75 :     hinfo.idx = 0;
    3204          75 :     hinfo.offset = 0;
    3205          75 :     hinfo.type = AP_PARAM_INT32;
    3206             :     // fill in default value with the CRC. Relies on sizeof crc == sizeof float
    3207          75 :     memcpy((uint8_t *)&hinfo.def_value, (const uint8_t *)&crc, sizeof(crc));
    3208             : 
    3209             :     // remember the table size
    3210          75 :     if (_dynamic_table_sizes[i] == 0) {
    3211          65 :         _dynamic_table_sizes[i] = num_params;
    3212             :     }
    3213             :     
    3214             :     // make the group active
    3215          75 :     info.key = key;
    3216          75 :     info.type = AP_PARAM_GROUP;
    3217             : 
    3218          75 :     invalidate_count();
    3219             : 
    3220             :     // save the CRC
    3221          75 :     AP_Int32 *crc_param = const_cast<AP_Int32 *>((AP_Int32 *)info.ptr);
    3222          75 :     crc_param->set(crc);
    3223          75 :     crc_param->save(true);
    3224             : 
    3225          75 :     return true;
    3226             : }
    3227             : 
    3228             : /*
    3229             :  Load an AP_Int32 variable from EEPROM using top level key and group element. Used to confirm
    3230             :  a key in add_table()
    3231             : */
    3232         589 : bool AP_Param::load_int32(uint16_t key, uint32_t group_element, int32_t &value)
    3233             : {
    3234             :     struct Param_header phdr;
    3235             : 
    3236         589 :     phdr.type = AP_PARAM_INT32;
    3237         589 :     set_key(phdr, key);
    3238         589 :     phdr.group_element = group_element;
    3239             : 
    3240             :     // scan EEPROM to find the right location
    3241             :     uint16_t ofs;
    3242         589 :     if (!scan(&phdr, &ofs)) {
    3243         176 :         return false;
    3244             :     }
    3245             : 
    3246             :     // found it
    3247         413 :     _storage.read_block(&value, ofs+sizeof(phdr), type_size(AP_PARAM_INT32));
    3248         413 :     return true;
    3249             : }
    3250             : 
    3251             : /*
    3252             :   add a parameter to a dynamic table
    3253             :  */
    3254         514 : bool AP_Param::add_param(uint8_t _key, uint8_t param_num, const char *pname, float default_value)
    3255             : {
    3256         514 :     if (_var_info_dynamic == nullptr) {
    3257             :         // No dynamic tables available
    3258           0 :         return false;
    3259             :     }
    3260             : 
    3261             :     // check for valid values
    3262         514 :     if (param_num == 0 || param_num > 63 || strlen(pname) > AP_MAX_NAME_SIZE) {
    3263           0 :         return false;
    3264             :     }
    3265             : 
    3266         514 :     uint16_t key = uint16_t(_key) + AP_PARAM_DYNAMIC_KEY_BASE;
    3267             :     // find the info
    3268             :     uint8_t i;
    3269         653 :     for (i=0; i<AP_PARAM_MAX_DYNAMIC; i++) {
    3270         653 :         auto &info = _var_info_dynamic[i];
    3271         653 :         if (info.key == key) {
    3272         514 :             break;
    3273             :         }
    3274             :     }
    3275         514 :     if (i == AP_PARAM_MAX_DYNAMIC) {
    3276             :         // not found
    3277           0 :         return false;
    3278             :     }
    3279             : 
    3280         514 :     if (param_num > _dynamic_table_sizes[i]) {
    3281           0 :         return false;
    3282             :     }
    3283             : 
    3284         514 :     auto &info = _var_info_dynamic[i];
    3285         514 :     if (info.ptr == nullptr) {
    3286           0 :         return false;
    3287             :     }
    3288             : 
    3289             :     // check CRC
    3290         514 :     const auto &hinfo = const_cast<GroupInfo*>(info.group_info)[0];
    3291         514 :     const int32_t crc = float_to_int32_le(hinfo.def_value);
    3292             : 
    3293             :     int32_t current_crc;
    3294         514 :     if (load_int32(key, 0, current_crc) && current_crc != crc) {
    3295             :         // crc mismatch, we have a conflict with an existing use of this key
    3296           0 :         return false;
    3297             :     }
    3298             : 
    3299             :     // Check length
    3300         514 :     char fullName[AP_MAX_NAME_SIZE+1] = {};
    3301         514 :     const int fullNameLength = hal.util->snprintf(fullName, sizeof(fullName), "%s%s", info.name, pname);
    3302         514 :     if ((fullNameLength < 0) || (fullNameLength > AP_MAX_NAME_SIZE)) {
    3303             :         // Param is too long with table prefix (or snprintf failed)
    3304           0 :         return false;
    3305             :     }
    3306             : 
    3307             :     // Get param object
    3308         514 :     AP_Float *pvalues = const_cast<AP_Float *>((const AP_Float *)info.ptr);
    3309         514 :     AP_Float &p = pvalues[param_num];
    3310             : 
    3311             :     // Check for conflicting name
    3312             :     enum ap_var_type existingType;
    3313         514 :     AP_Param* existingParam = find(fullName, &existingType);
    3314         514 :     if ((existingParam != nullptr) && ((existingType != AP_PARAM_FLOAT) || ((AP_Float*)existingParam != &p))) {
    3315             :         // There is a existing parameter with this name (which is not this parameter from a previous script run)
    3316           0 :         return false;
    3317             :     }
    3318             : 
    3319             :     // fill in idx of any gaps, leaving them hidden, this allows
    3320             :     // scripts to remove parameters
    3321        4180 :     for (uint8_t j=1; j<param_num; j++) {
    3322        3666 :         auto &g = const_cast<GroupInfo*>(info.group_info)[j];
    3323        3666 :         if (g.idx == 0xff) {
    3324         110 :             g.idx = j;
    3325         110 :             g.flags = AP_PARAM_FLAG_HIDDEN;
    3326         110 :             g.offset = j*sizeof(float);
    3327         110 :             g.type = AP_PARAM_FLOAT;
    3328             :         }
    3329             :     }
    3330             : 
    3331         514 :     auto &ginfo = const_cast<GroupInfo*>(info.group_info)[param_num];
    3332             : 
    3333         514 :     if (ginfo.name == _empty_string) {
    3334             :         // we don't allow name change while running
    3335         514 :         ginfo.name = strdup(pname);
    3336         514 :         if (ginfo.name == nullptr) {
    3337           0 :             ginfo.name = _empty_string;
    3338           0 :             return false;
    3339             :         }
    3340             :     }
    3341         514 :     ginfo.offset = param_num*sizeof(float);
    3342         514 :     ginfo.idx = param_num;
    3343         514 :     float *def_value = const_cast<float *>(&ginfo.def_value);
    3344         514 :     *def_value = default_value;
    3345         514 :     ginfo.type = AP_PARAM_FLOAT;
    3346             : 
    3347             :     // load from storage if available, the param is hidden during this
    3348             :     // load so the param is not visible to MAVLink until after it is
    3349             :     // loaded
    3350         514 :     p.set_default(default_value);
    3351         514 :     p.load();
    3352             : 
    3353             :     // clear the hidden flag if set and invalidate the count
    3354             :     // so we recount the parameters
    3355         514 :     ginfo.flags = 0;
    3356         514 :     invalidate_count();
    3357             :     
    3358         514 :     return true;
    3359             : }
    3360             : #endif

Generated by: LCOV version 1.14