//#DOC Main, program entry and main loop
#include "main.h"
#include <otMath.h>
#include <otSerial.h>
#include <otTimer.h>
#include <otMalloc.h>
#include <otPrintf.h>
#include <otI2cBus.h>
#include <otMPU9250.h>

// Number of milliseconds that have elapsed since the system was started, up to 49.7 days.
U32         _ticker = 0;

// IMU class
otMPU9250   imu;

// Timer 1 callback
FAST_CODE_2 void _timer1CallBack()
{
    _ticker++;
}

// IMU calibrarion and selftest
void imuCalibrate()
{
    F32   SelfTest[6];  // Holds results of gyro and accelerometer self test
    imu.reset();
    printf("IMU Self test started ...\n");
    imu.selfTest(SelfTest);
    printf("Acceleration  trim within = X:%6.1f, Y:%6.1f, Z:%6.1f %% of factory value\n", SelfTest[0], SelfTest[1], SelfTest[2]);
    printf("Gyroscope     trim within = X:%6.1f, Y:%6.1f, Z:%6.1f %% of factory value\n", SelfTest[3], SelfTest[4], SelfTest[5]);
    printf("Calibrating gyroscope and accelerometer ...\n");
    imu.accelGyroCalibration(); // Calibrate gyro and accelerometers, load biases in bias registers
    printf("Accelerometer biases (mg) = X:%6.1f, Y:%6.1f, Z:%6.1f\n", 1000. * imu.d->accelBias[0], 1000. * imu.d->accelBias[1], 1000. * imu.d->accelBias[2]);
    printf("Gyroscope     biases (dps)= X:%6.1f, Y:%6.1f, Z:%6.1f\n", imu.d->gyroBias[0], imu.d->gyroBias[1], imu.d->gyroBias[2]);
    imu.setRange(otMPU9250::AFS_2G, otMPU9250::GFS_250DPS, otMPU9250::MFS_16BITS);
    imu.imuInit();
    if(imu.magnetometerCheck())
    {
        // Get magnetometer calibration from AK8963 ROM
        imu.magnetometerInit();
        printf("Magnetometer initialized for active data mode ...\n"); // Initialize device for active mode read of magnetometer
        imu.magnetometerCalibration();
        printf("Magnetometer biases (mG)  = X:%6.1f, Y:%6.1f, Z:%6.1f\n", imu.d->magBias[0], imu.d->magBias[1], imu.d->magBias[2]);
        printf("Magnetometer scale  (mG)  = X:%6.1f, Y:%6.1f, Z:%6.1f\n", imu.d->magScale[0], imu.d->magScale[1], imu.d->magScale[2]);
    }
}

void setup()
{
    UartEnableSignals(UART0);
    UartSetBaud(UART0, 115200);
    
    // Set system tick for every 1ms
    TimerSetInterruptService(TIMER1, _timer1CallBack, TimerRateMs(1));
    TimerEnable(TIMER1, true);
   
    _initMalloc();
   
    setStdout(4096, UART0); // printf output is now directed on UART0
    
    I2cBusReset();
    I2cBusFrequency(100, 40);
    
    DelayMs(1000);          // Wait terminal start
    
    imu.init();
    if(imu.check())
        imuCalibrate();
    else
    {
        printf("Can't find MPU9250\n");
        IDLE;
    }
}

void loop()
{
    bool    dump = false;
    U32     st, et, loop = 0;
    F32     deltat;                             // Integration interval for both filter schemes
    F32     ax, ay, az, gx, gy, gz, mx, my, mz; // Variables to hold latest sensor data values
    F32     yaw, pitch, roll;                   // Tait?Bryan angles are also called Cardan angles
    
    st = _ticker;    // Grab start time
    while(1)
    {
        if(imu.ready())
        {
            imu.accelerometerGyroscope(ax, ay, az, gx, gy, gz);
            imu.magnetometer(mx, my, mz);
            et = _ticker;   // Grab end time
            deltat = ((float) (et - st)) / 1000.f;
            st = _ticker;   // Grab start time
            if(dump) printf("[%6u] Integration time  (s) = t:%6.3f\n", _ticker, deltat);
            // Display results
            // Sensors x (y)-axis of the accelerometer/gyro is aligned with the y (x)-axis of the magnetometer;
            // the magnetometer z-axis (+ down) is misaligned with z-axis (+ up) of accelerometer and gyro!
            // We have to make some allowance for this orientation mismatch in feeding the output to the quaternion filter.
            // For the MPU9250+MS5637 Mini breakout the +x accel/gyro is North, then -y accel/gyro is East. So if we want te quaternions properly aligned
            // we need to feed into the Madgwick function Ax, -Ay, -Az, Gx, -Gy, -Gz, My, -Mx, and Mz. But because gravity is by convention
            // positive down, we need to invert the accel data, so we pass -Ax, Ay, Az, Gx, -Gy, -Gz, My, -Mx, and Mz into the Madgwick
            // function to get North along the accel +x-axis, East along the accel -y-axis, and Down along the accel -z-axis.
            // This orientation choice can be modified to allow any convenient (non-NED) orientation convention.
            // Pass gyro rate as rad/s
            imu.madgwickQuaternionUpdate(-ax, ay, az, gx * M_PI / 180.0f, -gy * M_PI / 180.0f, -gz * M_PI / 180.0f,  my, -mx, mz, deltat);
            if(dump) printf("[%6u] Accelerometer (mg)    = X:%7.2f, Y:%7.2f, Z:%7.2f\n", _ticker, 1000.f * ax, 1000.f * ay, 1000.f * az);
            if(dump) printf("[%6u] Gyroscope     (deg/s) = X:%7.2f, Y:%7.2f, Z:%7.2f\n", _ticker, gx, gy, gz);
            if(dump) printf("[%6u] Magnetometer  (mG)    = X:%7.2f, Y:%7.2f, Z:%7.2f\n", _ticker, mx, my, mz);
            
            if(dump) printf("[%6u] Quaternion  = 0: %5.3f, X: %5.3f,  Y: %5.3f, Z: %5.3f\n", _ticker, imu.d->q[0],  imu.d->q[1],  imu.d->q[2],  imu.d->q[3]);
            if(dump) printf("[%6u] Temperature = C: %6.1f\n", _ticker, (float) imu.temperature());
            imu.taitBryanAngles(yaw, pitch, roll);
            if(dump)
            {
                printf("[%6u] Euler angle = Y: %5d, P: %5d, R: %5d\n", _ticker, (int) yaw, (int) pitch, (int) roll);
                dump = false;
            }
            loop++;
        }
        // Reduce the number of views, otherwise you don't understand anything!
        if((loop % 100) == 0) dump = true;
    }
}