// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-

/// @file	IMU.h
/// @brief	Abstract class defining the interface to a real or virtual
///         Inertial Measurement Unit.

#ifndef IMU_h
#define IMU_h

#include "../AP_Math/AP_Math.h"
#include "../AP_PeriodicProcess/AP_PeriodicProcess.h"
#include <inttypes.h>

class IMU
{

public:
    /// Constructor
    IMU();

    enum Start_style {
        COLD_START = 0,
        WARM_START
    };

    /// Perform startup initialisation.
    ///
    /// Called to initialise the state of the IMU.
    ///
    /// For COLD_START, implementations using real sensors can assume
    /// that the airframe is stationary and nominally oriented.
    ///
    /// For WARM_START, no assumptions should be made about the
    /// orientation or motion of the airframe.  Calibration should be
    /// as for the previous COLD_START call.
    ///
    /// @param style	The initialisation startup style.
    ///
    virtual void        init( Start_style           style,
                              void                  (*delay_cb)(unsigned long t),
                              void                  (*flash_leds_cb)(bool on),
                              AP_PeriodicProcess *  scheduler );

    /// Perform cold startup initialisation for just the accelerometers.
    ///
    /// @note This should not be called unless ::init has previously
    ///       been called, as ::init may perform other work.
    ///
    virtual void        init_accel(void (*callback)(unsigned long t), void (*flash_leds_cb)(bool on));

    /// Perform cold-start initialisation for just the gyros.
    ///
    /// @note This should not be called unless ::init has previously
    ///       been called, as ::init may perform other work
    ///
    virtual void        init_gyro(void (*callback)(unsigned long t), void (*flash_leds_cb)(bool on));

    /// Give the IMU some cycles to perform/fetch an update from its
    /// sensors.
    ///
    /// @returns	True if some state was updated.
    ///
    virtual bool        update(void);

    // true if new data is available from the sensors
    virtual bool        new_data_available(void);

    /// Fetch the current gyro values
    ///
    /// @returns	vector of rotational rates in radians/sec
    ///
    Vector3f        get_gyro(void) {
        return _gyro;
    }

    /// Fetch the current accelerometer values
    ///
    /// @returns	vector of current accelerations in m/s/s
    ///
    Vector3f        get_accel(void) {
        return _accel;
    }


    /// return the number of seconds that the last update represents
    ///
    /// @returns	number of seconds
    ///
    float        get_delta_time(void) {
        return _sample_time * 1.0e-6;
    }

    /// return the maximum gyro drift rate in radians/s/s. This
    /// depends on what gyro chips are being used
    virtual float        get_gyro_drift_rate(void);

    /// A count of bad sensor readings
    ///
    /// @todo This should be renamed, as there's no guarantee that sensors
    ///       are using ADCs, etc.
    ///
    uint8_t                 adc_constraints;

    virtual float           gx(void);
    virtual float           gy(void);
    virtual float           gz(void);
    virtual float           ax(void);
    virtual float           ay(void);
    virtual float           az(void);
    virtual void            ax(const float v);
    virtual void            ay(const float v);
    virtual void            az(const float v);

    static const struct AP_Param::GroupInfo        var_info[];

protected:

    AP_Vector6f         _sensor_cal;    ///< Calibrated sensor offsets

    /// Most recent accelerometer reading obtained by ::update
    Vector3f            _accel;

    /// Most recent gyro reading obtained by ::update
    Vector3f            _gyro;

    /// number of microseconds that the accel and gyro values
    /// were sampled over
    uint32_t        _sample_time;

    AP_Int16        _product_id;        // this is the product id returned from the INS init
};

#endif