ardupilot/libraries/AP_Common/AP_Var.h
tridge60@gmail.com a2589c3573 AP_Var: added EEPROM wear levelling
This adds a variable length dummy variable at the front of the EEPROM
when we erase it, which has the effect of moving the location of any
hot variables within the EEPROM. This should improve EEPROM
life. Thanks to Mike for the implementation.

Pair-Programmed-With: Mike Smith

git-svn-id: https://arducopter.googlecode.com/svn/trunk@3234 f9c3cf11-9bcb-44bc-f272-b75c42450872
2011-09-04 06:39:57 +00:00

968 lines
34 KiB
C++

// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
//
// This is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
//
/// @file AP_Var.h
/// @brief A system for managing and storing variables that are of
/// general interest to the system.
#ifndef AP_VAR_H
#define AP_VAR_H
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "AP_MetaClass.h"
class AP_Var_group;
/// Base class for variables.
///
/// Provides naming and lookup services for variables.
///
class AP_Var : public AP_Meta_class
{
public:
/// EEPROM header
///
/// This structure is placed at the head of the EEPROM to indicate
/// that the ROM is formatted for AP_Var.
///
/// The EEPROM may thus be cheaply erased by overwriting just one
/// byte of the header magic.
///
struct EEPROM_header {
uint16_t magic;
uint8_t revision;
uint8_t spare;
};
static const uint16_t k_EEPROM_magic = 0x5041; ///< "AP"
static const uint16_t k_EEPROM_revision = 2; ///< current format revision
/// Storage key for variables that are saved in EEPROM.
///
/// The key is used to associate a known variable in memory
/// with storage for the variable in the EEPROM.
///
/// At creation time, the _key value has the k_key_not_located bit
/// set, and its value is an ordinal uniquely identifying the
/// variable. Once the address of the variable in EEPROM is known,
/// either as a result of scanning or due to allocation of new
/// space in the EEPROM, the k_key_not_located bit will be cleared
/// and the _key value gives the offset into the EEPROM where
/// the variable's data can be found.
///
typedef uint16_t Key;
/// This header is prepended to a variable stored in EEPROM.
///
/// Note that the size value is the first element in the header.
/// The sentinel entry marking the end of the EEPROM is always written
/// before updating the header of a new variable at the end of EEPROM.
/// This provides protection against corruption that might be caused
/// if the update process is interrupted.
///
struct Var_header {
/// The size of the variable, minus one.
/// This allows a variable or group to be anything from one to 64 bytes long.
///
uint8_t size:6;
/// Spare bits, currently unused
///
/// @todo One could be a parity bit?
///
uint8_t spare:2;
/// The key assigned to the variable.
///
uint8_t key;
};
/// A key value that indicates that a variable is not to be saved to EEPROM.
///
/// As the value has all bits set to 1, it's not a legal EEPROM address for any
/// EEPROM smaller than 64K (and it's too big to fit the Var_header::key field).
///
/// This value is normally the default.
///
static const Key k_key_none = 0xffff;
/// A key that has this bit set is still a key; if it has been cleared then
/// the key's value is actually an address in EEPROM.
///
static const Key k_key_not_located = (Key)1 << 15;
/// A key that has this bit set was not found during a scan of the EEPROM.
/// If this bit is set in the key, it's not useful to scan the EEPROM again
/// in an attempt to find it.
///
static const Key k_key_not_allocated = (Key)1 << 14;
/// Key assigned to the terminal entry in EEPROM.
///
static const Key k_key_sentinel = 0xff;
/// Key assigned to the wear-balancing pad entry in EEPROM.
///
static const Key k_key_pad = 0xfe;
/// A bitmask that removes any control bits from a key giving just the
/// value.
///
static const Key k_key_mask = ~(k_key_not_located | k_key_not_allocated);
/// The largest variable that will be saved to EEPROM.
/// This affects the amount of stack space that is required by the ::save, ::load,
/// ::save_all and ::load_all functions. It should match the maximum size that can
/// be encoded in the Var_header::size field.
///
static const size_t k_size_max = 64;
/// Optional flags affecting the behavior and usage of the variable.
///
typedef uint8_t Flags;
static const Flags k_flags_none = 0;
/// The variable will not be loaded by ::load_all or saved by ::save_all, but it
/// has a key and can be loaded/saved manually.
static const Flags k_flag_no_auto_load = (1 << 0);
/// This flag is advisory; it indicates that the variable's value should not be
/// imported from outside, e.g. from a GCS.
static const Flags k_flag_no_import = (1 << 1);
/// This flag indicates that the variable is really a group; it is normally
/// set automatically by the AP_Var_group constructor.
///
static const Flags k_flag_is_group = (1 << 2);
/// This flag indicates that the variable wants to opt out of being listed.
///
static const Flags k_flag_unlisted = (1 << 3);
/// @todo Flag that indicates variable is non-volatile (auto-saved when set).
static AP_Meta_class::Type_id k_typeid_float; ///< meta_type_id() value for AP_Float
static AP_Meta_class::Type_id k_typeid_float16; ///< meta_type_id() value for AP_Float16
static AP_Meta_class::Type_id k_typeid_int32; ///< meta_type_id() value for AP_Int32
static AP_Meta_class::Type_id k_typeid_int16; ///< meta_type_id() value for AP_Int16
static AP_Meta_class::Type_id k_typeid_int8; ///< meta_type_id() value for AP_Int8
static AP_Meta_class::Type_id k_typeid_group; ///< meta_type_id() value for AP_Var_group
/// Constructor for a freestanding variable
///
/// @param key The storage key to be associated with this variable.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags which control how the variable behaves.
///
AP_Var(Key key = k_key_none, const prog_char_t *name = NULL, Flags flags = k_flags_none);
/// Constructor for variable belonging to a group
///
/// @param group The group the variable belongs to.
/// @param index The position of the variable in the group.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags which control how the variable behaves.
///
AP_Var(AP_Var_group *group, Key index, const prog_char_t *name, Flags flags = k_flags_none);
/// Destructor
///
/// For freestanding variables, this will remove the variable from the global list
/// of variables. The list is organised FIFO, so locally-constructed freestanding
/// variables are typically cheap to destroy as they tend to be at or very close to
/// the head of the list.
///
/// Destroying a variable that is a group member may be less efficient as the list
/// of variables that are group members is sorted by key, requiring a traversal
/// of the list up to the index before it can be removed.
///
/// Destroying a group removes all variables that are members of the group from
/// the list, requiring a complete traversal of the list of group-member variables.
/// If the group is destroyed before its members, they will also traverse the entire
/// list as they attempt to remove themselves.
///
/// The moral of the story: be careful when creating groups with local scope.
///
~AP_Var(void);
/// Copy the variable's name, prefixed by any containing group name, to a buffer.
///
/// If the variable has no name, the buffer will contain an empty string.
///
/// Note that if the combination of names is larger than the buffer, the
/// result in the buffer will be truncated.
///
/// @param buffer The destination buffer
/// @param bufferSize Total size of the destination buffer.
///
void copy_name(char *buffer, size_t bufferSize) const;
/// Find a variable by name.
///
/// If the variable has no name, it cannot be found by this interface.
///
/// @param name The full name of the variable to be found.
/// @return A pointer to the variable, or NULL if
/// it does not exist.
///
static AP_Var *find(const char *name);
/// Find a variable by key.
///
/// @param key The key being looked up.
/// @return A pointer to the variable, or NULL if
/// it does not exist.
///
static AP_Var *find(Key key);
/// Save the current value of the variable to EEPROM.
///
/// This interface works for any subclass that implements
/// AP_Meta_class::serialize.
///
/// Note that invoking this method on a variable that is a group
/// member will cause the entire group to be saved.
///
/// @return True if the variable was saved successfully.
///
bool save(void);
/// Load the variable from EEPROM.
///
/// This interface works for any subclass that implements
/// AP_Meta_class::unserialize.
///
/// If the variable has not previously been saved to EEPROM, this
/// routine will return failure.
///
/// Note that invoking this method on a variable that is a group
/// member will cause the entire group to be loaded.
///
/// @return True if the variable was loaded successfully.
///
bool load(void);
/// Save all variables to EEPROM
///
/// This routine performs a best-efforts attempt to save all
/// of the variables to EEPROM. If some fail to save, the others
/// that succeed will still all be saved.
///
/// @return False if any variable failed to save.
///
static bool save_all(void);
/// Load all variables from EEPROM
///
/// This function performs a best-efforts attempt to load all
/// of the variables from EEPROM. If some fail to load, their
/// values will remain as they are.
///
/// @return False if any variable failed to load. Note
/// That this may be caused by a variable not having
/// previously been saved.
///
static bool load_all(void);
/// Erase all variables in EEPROM.
///
/// This can be used prior to save_all to ensure that only known variables
/// will be present in the EEPROM.
///
/// It can also be used immediately prior to reset, followed by a save_all,
/// to restore all saved variables to their initial value.
///
static void erase_all(void);
/// Test for flags that may be set.
///
/// @param flagval Flag or flags to be tested
/// @return True if all of the bits in flagval are set in the flags.
///
bool has_flags(Flags flagval) const {
return (_flags & flagval) == flagval;
}
/// Returns the group that a variable belongs to
///
/// @return The parent group, or NULL if the variable is not grouped.
///
AP_Var_group *group(void) { return _group; }
/// Returns the first variable in the global list.
///
/// @return The first variable in the global list, or NULL if
/// there are none.
///
static AP_Var *first(void) { return _variables; }
/// Returns the next variable in the global list.
///
/// All standalone variables are returned first, then all grouped variables.
/// Note that groups themselves are considered standalone variables.
///
/// A caller not wishing to iterate grouped variables should test the return
/// value from this function with ::group, and if non-null, ignore it.
///
/// XXX how to ignore groups?
///
/// @return The next variable, either the next group member in order or
/// the next variable in an arbitrary order, or NULL if this is
/// the last variable in the list.
///
AP_Var *next(void);
/// Returns the first variable that is a member of a specific group.
///
/// @param group The group whose member(s) are sought.
/// @return The first variable in the group, or NULL if there are none.
///
static AP_Var *first_member(AP_Var_group *group);
/// Returns the next variable that is a member of the same group.
///
/// This will not behave correctly if called on a variable that is not a group member.
///
/// @param group The group whose member(s) are sought.
/// @return The next variable in the group, or NULL if there are none.
///
AP_Var *next_member();
/// Returns the storage key for a variable.
///
/// Note that group members do not have storage keys - the key is held by the group
/// and instead the key indicates the variable's ordering within the group.
///
/// @return The variable's key, or k_key_none if it does not have a key.
///
Key key(void);
/// Casts the value to float, if possible.
///
/// This is a convenience method for code that would rather not try to
/// meta-cast to the various subtypes. It cannot guarantee that a useful value
/// will be returned; the caller must check the returned value against NAN
/// before using it.
///
/// @return The value cast to float, or NAN if it cannot be cast.
///
virtual float cast_to_float() const;
/// Report the amount of memory being used by AP_Var subclasses.
///
/// @return The sum of sizeof(*this) for all constructed AP_Var subclass instances.
///
static uint16_t get_memory_use() { return _bytes_in_use; }
protected:
// Memory statistics
static uint16_t _bytes_in_use;
private:
AP_Var_group *_group; ///< Group that the variable may be a member of
AP_Var *_link; ///< linked list pointer to next variable
Key _key; ///< Storage key; see the discussion of Key above.
const prog_char_t *_name; ///< name known to external agents (GCS, etc.)
uint8_t _flags; ///< flag bits
// static state used by ::lookup
static AP_Var *_variables; ///< linked list of all freestanding variables
static AP_Var *_grouped_variables; ///< linked list of all grouped variables
// EEPROM space allocation and scanning
static uint16_t _tail_sentinel; ///< EEPROM address of the tail sentinel
static const uint16_t k_EEPROM_size = 4096; ///< XXX avr-libc doesn't consistently export this
/// Scan the EEPROM and assign addresses to any known variables
/// that have entries there.
///
/// @return True if the EEPROM was scanned successfully.
///
static bool _EEPROM_scan(void);
/// Locate this variable in the EEPROM.
///
/// @param allocate If true, and the variable does not already have
/// space allocated in EEPROM, allocate it.
/// @return True if the _key field is a valid EEPROM address with
/// space reserved for the variable to be saved. False
/// if the variable does not have a key, or space could not
/// be allocated and the variable does not already exist in
/// EEPROM.
///
bool _EEPROM_locate(bool allocate);
};
/// Variable groups.
///
/// Grouped variables are treated as a single variable when loaded from
/// or saved to EEPROM. The size limits for variables apply to the entire
/// group; thus a group cannot be larger than the largest legal variable.
///
/// When AP_Var is asked for the name of a variable that is a member
/// of a group, it will prepend the name of the group; this helps save
/// memory.
///
/// Variables belonging to a group are always sorted into the global
/// variable list after the group.
///
class AP_Var_group : public AP_Var
{
public:
/// Constructor
///
/// @param key Storage key for the group.
/// @param name An optional name prefix for members of the group.
///
AP_Var_group(Key with_key = k_key_none, const prog_char_t *name = NULL, Flags flags = k_flags_none) :
AP_Var(with_key, name, flags | k_flag_is_group)
{
_bytes_in_use += sizeof(*this);
}
/// Serialize the group.
///
/// Iteratively serializes the entire group into the supplied buffer.
///
/// @param buf Buffer into which serialized data should be placed.
/// @param buf_size The size of the buffer provided.
/// @return The size of the serialized data, even if that data would
/// have overflowed the buffer. If the value is less than or
/// equal to buf_size, serialization was successful.
///
virtual size_t serialize(void *buf, size_t buf_size) const;
/// Unserialize the group.
///
/// Iteratively unserializes the group from the supplied buffer.
///
/// @param buf Buffer containing serialized data.
/// @param buf_size The size of the buffer.
/// @return The number of bytes from the buffer that would be consumed
/// unserializing the data. If the value is less than or equal
/// to buf_size, unserialization was successful.
///
virtual size_t unserialize(void *buf, size_t buf_size);
private:
/// Common implementation of the group member traversal and accounting used
/// by serialize/unserialize.
///
/// @param buf Buffer containing serialized data.
/// @param buf_size The size of the buffer.
/// @param do_serialize True if the operation should serialize, false if it should
/// unserialize.
/// @return The number of bytes from the buffer that would be consumed
/// operating on the data. If the value is less than or equal
/// to buf_size, the operation was successful.
///
size_t _serialize_unserialize(void *buf, size_t buf_size, bool do_serialize);
};
/// Template class for scalar variables.
///
/// Objects of this type have a value, and can be treated in many ways as though they
/// were the value.
///
/// @tparam T The scalar type of the variable
///
template<typename T>
class AP_VarT : public AP_Var
{
public:
/// Constructor for non-grouped variable.
///
/// Initialises a stand-alone variable with optional initial value, storage key, name and flags.
///
/// @param default_value Value the variable should have at startup.
/// @param key Storage key for the variable. If not set, or set to AP_Var::k_key_none
/// the variable cannot be loaded from or saved to EEPROM.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags that may affect the behaviour of the variable.
///
AP_VarT<T> (const T initial_value = 0,
Key with_key = k_key_none,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(with_key, name, flags),
_value(initial_value)
{
_bytes_in_use += sizeof(*this);
}
/// Constructor for grouped variable.
///
/// Initialises a variable that is a member of a group with optional initial value, name and flags.
///
/// @param group The group that this variable belongs to.
/// @param index The variable's index within the group. Index values must be unique
/// within the group, as they ensure that the group's layout in EEPROM
/// is consistent.
/// @param initial_value The value the variable takes at startup.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags that may affect the behaviour of the variable.
///
AP_VarT<T> (AP_Var_group *with_group, // XXX maybe make this a ref?
Key vindex,
T initial_value,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(with_group, vindex, name, flags),
_value(initial_value)
{
_bytes_in_use += sizeof(*this);
}
// serialize _value into the buffer, but only if it is big enough.
//
virtual size_t serialize(void *buf, size_t size) const {
if (size >= sizeof(_value)) {
*(T *)buf = _value;
}
return sizeof(_value);
}
// Unserialize from the buffer, but only if it is big enough.
//
virtual size_t unserialize(void *buf, size_t size) {
if (size >= sizeof(_value)) {
_value = *(T *)buf;
}
return sizeof(_value);
}
/// Value getter
///
T get(void) const {
return _value;
}
/// Value setter
///
void set(T v) {
_value = v;
}
/// Combined set and save
///
bool set_and_save(T v) {
set(v);
return save();
}
/// Conversion to T returns a reference to the value.
///
/// This allows the class to be used in many situations where the value would be legal.
///
operator T &() {
return _value;
}
/// Copy assignment from self does nothing.
///
AP_VarT<T>& operator=(AP_VarT<T>& v) {
return v;
}
/// Copy assignment from T is equivalent to ::set.
///
AP_VarT<T>& operator=(T v) {
_value = v;
return *this;
}
/// AP_VarT types can implement AP_Var::cast_to_float
///
virtual float cast_to_float() const;
protected:
T _value;
};
// Implement AP_Var::cast_to_float.
//
template<typename T>
float
AP_VarT<T>::cast_to_float() const
{
return (float)_value;
}
/// Template class for structure variables.
///
/// Objects created using this variable contain an instance of the type T
///
/// Note that the size of the structure cannot be greater than AP_Var::k_size_max
/// if it is ever to be loaded or saved to EEPROM.
/// Unfortunately there is no way to assert this at compile time; a larger
/// array will compile correctly and work as expected, except that ::save and
/// ::load will always fail.
///
/// @note The initial value of the fields of the structure are set by
/// the type's constructor at the time that the template is constructed.
///
template<typename T>
class AP_VarS : public AP_Var
{
public:
/// Constructor for a non-grouped structure variable.
///
/// Initializes a stand-alone structure variable with optional storage key, name and flags.
///
/// @param key Storage key for the variable. If not set, or set to AP_Var::k_key_none
/// the variable cannot be loaded from or saved to EEPROM.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags that may affect the behavior of the variable.
///
AP_VarS<T> (Key with_key = k_key_none,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(with_key, name, flags)
{
_bytes_in_use += sizeof(*this);
}
/// Constructor for a grouped structure variable.
///
/// Initializes a variable that is a member of a group with optional initial value, name and flags.
///
/// @param group The group that this variable belongs to.
/// @param index The variable's index within the group. Index values must be unique
/// within the group, as they ensure that the group's layout in EEPROM
/// is consistent.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags that may affect the behavior of the variable.
///
AP_VarS<T> (AP_Var_group *with_group, // XXX maybe make this a ref?
Key index,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(with_group, index, name, flags)
{
_bytes_in_use += sizeof(*this);
}
// serialize _value into the buffer, but only if it is big enough.
//
virtual size_t serialize(void *buf, size_t size) const {
if (size >= sizeof(_value)) {
memcpy(buf, &_value, sizeof(_value));
}
return sizeof(_value);
}
// Unserialize from the buffer, but only if it is big enough.
//
virtual size_t unserialize(void *buf, size_t size) {
if (size >= sizeof(_value)) {
memcpy(&_value, buf, sizeof(_value));
}
return sizeof(_value);
}
/// Value getter
///
T& get() {
return _value;
}
/// Value getter
///
const T& get() const {
return _value;
}
/// Value setter
///
void set(T v) {
_value = v;
}
/// Combined set and save
///
void set_and_save(T v) {
set(v);
save();
}
// Note no attempt to do anything fancy with the assignment or cast operators.
// Implementing something that works reliably and produces readable code is not
// straightforward, so it's been deferred for now.
protected:
T _value;
};
/// Template class for array variables.
///
/// Objects created using this template behave like arrays of the type T,
/// but are stored like single variables.
///
/// Note that the size of the array cannot be greater than AP_Var::k_size_max
/// if it is ever to be loaded or saved to EEPROM.
/// Unfortunately there is no way to assert this at compile time; a larger
/// array will compile correctly and work as expected, except that ::save and
/// ::load will always fail.
///
/// For example:
///
/// AP_VarA<float,4> float_array(some_key, PSTR("some_name"));
///
/// will create float_array as an array of four floats, stored using the
/// key some_key and visible with the name 'some_name'.
///
/// @note Like regular arrays, the initial value of the members of the
/// array is zero if the object is declared with global scope, and
/// undefined if declared in a block (function, etc.).
///
/// @tparam T The scalar type of the variable
///
template<typename T, uint8_t N>
class AP_VarA : public AP_Var
{
public:
/// Constructor for non-grouped array.
///
/// Initializes a stand-alone array with optional storage key, name and flags.
///
/// @param key Storage key for the variable. If not set, or set to AP_Var::k_key_none
/// the variable cannot be loaded from or saved to EEPROM.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags that may affect the behavior of the variable.
///
AP_VarA<T,N> (Key with_key = k_key_none,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(with_key, name, flags)
{
_bytes_in_use += sizeof(*this);
}
/// Constructor for a grouped array.
///
/// Initializes a variable that is a member of a group with optional initial value, name and flags.
///
/// @param group The group that this variable belongs to.
/// @param index The variable's index within the group. Index values must be unique
/// within the group, as they ensure that the group's layout in EEPROM
/// is consistent.
/// @param name An optional name by which the variable may be known.
/// @param flags Optional flags that may affect the behavior of the variable.
///
AP_VarA<T,N> (AP_Var_group *with_group, // XXX maybe make this a ref?
Key index,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Var(with_group, index, name, flags)
{
_bytes_in_use += sizeof(*this);
}
// serialize _value into the buffer, but only if it is big enough.
//
virtual size_t serialize(void *buf, size_t size) const {
if (size >= sizeof(_value)) {
memcpy(buf, &_value[0], sizeof(_value));
}
return sizeof(_value);
}
// Unserialize from the buffer, but only if it is big enough.
//
virtual size_t unserialize(void *buf, size_t size) {
if (size >= sizeof(_value)) {
memcpy(&_value[0], buf, sizeof(_value));
}
return sizeof(_value);
}
/// Array operator accesses members.
///
/// @note It would be nice to range-check i here, but then what would we return?
///
T &operator [](uint8_t i) {
return _value[i];
}
/// Value getter
///
/// @note Returns zero for index values out of range.
///
T get(uint8_t i) const {
if (i < N) {
return _value[i];
} else {
return (T)0;
}
}
/// Value setter
///
/// @note Attempts to set an index out of range are discarded.
///
void set(uint8_t i, T v) {
if (i < N) {
_value[i] = v;
}
}
/// Copy assignment from self does nothing.
///
AP_VarA<T,N>& operator=(AP_VarA<T,N>& v) {
return v;
}
protected:
T _value[N];
};
/// Convenience macro for defining instances of the AP_VarT template.
///
#define AP_VARDEF(_t, _n) typedef AP_VarT<_t> AP_##_n;
AP_VARDEF(float, Float); // defines AP_Float
AP_VARDEF(int8_t, Int8); // defines AP_Int8
AP_VARDEF(int16_t, Int16); // defines AP_Int16
AP_VARDEF(int32_t, Int32); // defines AP_Int32
/// Rely on built in casting for other variable types
/// to minimize template creation and save memory
#define AP_Uint8 AP_Int8
#define AP_Uint16 AP_Int16
#define AP_Uint32 AP_Int32
#define AP_Bool AP_Int8
/// Many float values can be saved as 16-bit fixed-point values, reducing EEPROM
/// consumption. AP_Float16 subclasses AP_Float and overloads serialize/unserialize
/// to achieve this.
///
/// Note that any protocol transporting serialized data should be aware that the
/// encoding used is effectively Q5.10 (one sign bit, 5 integer bits, 10 fractional bits),
/// giving an effective range of approximately +/-31.999
///
class AP_Float16 : public AP_Float
{
public:
/// Constructors mimic AP_Float::AP_Float()
///
AP_Float16(float initial_value = 0,
Key with_key = k_key_none,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Float(initial_value, with_key, name, flags)
{
_bytes_in_use += sizeof(*this);
}
AP_Float16(AP_Var_group *with_group,
Key index,
float initial_value = 0,
const prog_char_t *name = NULL,
Flags flags = k_flags_none) :
AP_Float(with_group, index, initial_value, name, flags)
{
_bytes_in_use += sizeof(*this);
}
// Serialize _value as Q5.10.
//
virtual size_t serialize(void *buf, size_t size) const {
uint16_t *sval = (uint16_t *)buf;
if (size >= sizeof(*sval)) {
*sval = _value * 1024.0; // scale by power of 2, may be more efficient
}
return sizeof(*sval);
}
// Unserialize _value from Q5.10.
//
virtual size_t unserialize(void *buf, size_t size) {
uint16_t *sval = (uint16_t *)buf;
if (size >= sizeof(*sval)) {
_value = (float)*sval / 1024.0; // scale by power of 2, may be more efficient
return sizeof(*sval);
}
return 0;
}
// copy operators must be redefined in subclasses to get correct behavior
AP_Float16 &operator=(AP_Float16 &v) {
return v;
}
AP_Float16 &operator=(float v) {
_value = v;
return *this;
}
};
/// Some convenient constant AP_Vars.
///
/// @todo Work out why these can't be const and fix if possible.
///
/// @todo Work out how to get these into a namespace and name them properly.
///
extern AP_Float AP_Float_unity;
extern AP_Float AP_Float_negative_unity;
extern AP_Float AP_Float_zero;
/// Print the value of an AP_Var
///
/// This function knows about the types listed in the AP_Var::k_typeid_* constants,
/// and it will print their value using Serial, which is assumed to be a BetterStream
/// serial port (e.g. FastSerial).
///
/// @param vp The variable to print.
///
extern void AP_Var_print(AP_Var *vp);
#ifndef __AP_COMMON_MENU_H
# error Must include menu.h
#endif
/// Menu function for setting an AP_Var.
///
/// This function can be directly called from a Menu. It expects two args, the
/// first is the full name of a variable, the second is the value to set the variable
/// to.
///
/// This function knows about the types listed in the AP_Var::k_typeid_* constants.
///
/// @param argc The number of arguments; must be 3.
/// @param argv Arguments. argv[1] must contain a variable name, argv[2].f and .i
/// are consulted when setting the value.
/// @return Zero if the variable was set successfully, -1 if it could not be set.
///
extern int8_t AP_Var_menu_set(uint8_t argc, const Menu::arg *argv);
/// Menu function for displaying AP_Vars.
///
/// This function can be directly called from a Menu. It expects zero or one argument.
/// If no arguments are supplied, all known variables are printed. If an argument is
/// supplied, it is expected to be the name of a variable and that variable will be
/// printed.
///
/// @param argc The number of arguments, must be 1 or 2.
/// @param argv Argument array; argv[1] may be set to the name of a variable to show.
///
extern int8_t AP_Var_menu_show(uint8_t argc, const Menu::arg *argv);
#endif // AP_VAR_H