UAVX Multicopter Flight Controller.

Dependencies:   mbed

Revision:
2:90292f8bd179
Parent:
1:1e3318a30ddd
--- a/compass.c	Fri Feb 25 01:35:24 2011 +0000
+++ b/compass.c	Tue Apr 26 12:12:29 2011 +0000
@@ -2,7 +2,7 @@
 // =                              UAVXArm Quadrocopter Controller                                =
 // =                           Copyright (c) 2008 by Prof. Greg Egan                             =
 // =                 Original V3.15 Copyright (c) 2007 Ing. Wolfgang Mahringer                   =
-// =                     http://code.google.com/p/uavp-mods/ http://uavp.ch                      =
+// =                           http://code.google.com/p/uavp-mods/                               =
 // ===============================================================================================
 
 //    This is part of UAVXArm.
@@ -23,9 +23,9 @@
 // Local magnetic declination not included
 // http://www.ngdc.noaa.gov/geomagmodels/Declination.jsp
 
-
 void ReadCompass(void);
 void GetHeading(void);
+real32 MinimumTurn(real32);
 void CalibrateCompass(void);
 void ShowCompassType(void);
 void DoCompassTest(void);
@@ -33,10 +33,13 @@
 
 MagStruct Mag[3] = {{ 0,0 },{ 0,0 },{ 0,0 }};
 real32 MagDeviation, CompassOffset;
-real32 MagHeading, Heading, Headingp, FakeHeading;
+real32 MagHeading, Heading, HeadingP, FakeHeading;
 real32 HeadingSin, HeadingCos;
+real32 CompassMaxSlew;
 uint8 CompassType;
 
+enum MagCoords { MX, MY, MZ };
+
 void ReadCompass(void) {
     switch ( CompassType ) {
         case HMC5843:
@@ -94,16 +97,25 @@
 
 void GetHeading(void) {
 
-    const real32 CompassA = COMPASS_UPDATE_S / ( OneOnTwoPiCompassF + COMPASS_UPDATE_S );
+    static real32 HeadingChange, CompassA;
 
     ReadCompass();
 
-    Heading = Make2Pi( MagHeading - MagDeviation - CompassOffset );
-    if ( fabs(Heading - Headingp ) > PI )
-        Headingp = Heading;
+    Heading = Make2Pi( MagHeading + MagDeviation - CompassOffset );
+    HeadingChange = fabs( Heading - HeadingP );
+    if ( HeadingChange > ONEANDHALFPI ) // wrap 0 -> TwoPI
+        HeadingP = Heading;
+    else
+        if ( HeadingChange > CompassMaxSlew ) { // Sanity check - discard reading
+            Heading =SlewLimit(HeadingP, Heading, CompassMaxSlew ); 
+            Stats[CompassFailS]++;
+        }
 
-    Heading = Headingp + (Heading - Headingp) * CompassA;
-    Headingp = Heading;
+#ifndef SUPPRESS_COMPASS_FILTER
+    CompassA = dT / ( 1.0 / ( TWOPI * YawFilterLPFreq ) + dT );
+    Heading = Make2Pi( LPFilter(Heading, HeadingP, CompassA) );
+#endif // !SUPPRESS_COMPASS_FILTER
+    HeadingP = Heading;
 
 #ifdef SIMULATE
 #if ( defined AILERON | defined ELEVON )
@@ -121,6 +133,24 @@
 #endif // SIMULATE 
 } // GetHeading
 
+//boolean DirectionSelected = false;
+//real32 DirectionSense = 1.0;
+
+real32 MinimumTurn(real32 A ) {
+
+    static real32 AbsA;
+
+    AbsA = fabs(A);
+    if ( AbsA > PI )
+        A = ( AbsA - TWOPI ) * Sign(A);
+
+    //DirectionSelected = fabs(A) > THIRDPI; // avoid dithering around reciprocal heading
+    //DirectionSense = Sign(A);
+
+    return ( A );
+
+} // MinimumTurn
+
 void InitCompass(void) {
     if ( IsHMC5843Active() )
         CompassType = HMC5843;
@@ -145,7 +175,7 @@
 
     ReadCompass();
     mS[CompassUpdate] = mSClock();
-    Heading = Headingp = Make2Pi( MagHeading - MagDeviation - CompassOffset );
+    Heading = HeadingP = Make2Pi( MagHeading - MagDeviation - CompassOffset );
 
 } // InitCompass
 
@@ -163,16 +193,15 @@
 void ReadHMC5843(void) {
     static char b[6];
     static i16u X, Y, Z;
-    static uint8 r;
-    static real32 mx, my;
+    static real32 FX,FY;
     static real32 CRoll, SRoll, CPitch, SPitch;
 
     I2CCOMPASS.start();
-    r = I2CCOMPASS.write(HMC5843_WR);
-    r = I2CCOMPASS.write(0x03); // point to data
+    if ( I2CCOMPASS.write(HMC5843_WR) != I2C_ACK ) goto HMC5843Error;
+    if ( I2CCOMPASS.write(0x03) != I2C_ACK ) goto HMC5843Error; // point to data
     I2CCOMPASS.stop();
 
-    I2CCOMPASS.blockread(HMC5843_RD, b, 6);
+    if ( I2CCOMPASS.blockread(HMC5843_RD, b, 6) ) goto HMC5843Error;
 
     X.b1 = b[0];
     X.b0 = b[1];
@@ -192,55 +221,180 @@
     }
     DebugPin = true;
     CRoll = cos(Angle[Roll]);
-    SRoll = sin(Angle[Roll]);
+    SRoll = sin(Angle[Roll]);       // Acc[LR] - optimisation not worthwhile
     CPitch = cos(Angle[Pitch]);
-    SPitch = sin(Angle[Pitch]);
+    SPitch = sin(Angle[Pitch]);     // Acc[BF]
 
-    mx = (Mag[BF].V-Mag[BF].Offset) * CPitch + (Mag[LR].V-Mag[LR].Offset) * SRoll * SPitch + (Mag[UD].V-Mag[UD].Offset) * CRoll * SPitch;
-    my =  (Mag[LR].V-Mag[LR].Offset) * CRoll - (Mag[UD].V-Mag[UD].Offset) * SRoll;
+    FX = (Mag[BF].V-Mag[BF].Offset) * CPitch + (Mag[LR].V-Mag[LR].Offset) * SRoll * SPitch + (Mag[UD].V-Mag[UD].Offset) * CRoll * SPitch;
+    FY = (Mag[LR].V-Mag[LR].Offset) * CRoll - (Mag[UD].V-Mag[UD].Offset) * SRoll;
 
     // Magnetic Heading
-    MagHeading = MakePi(atan2( -my, mx ));
+    MagHeading = MakePi(atan2( -FY, FX ));
+
     DebugPin = false;
     F.CompassValid = true;
+
     return;
 
+HMC5843Error:
+    I2CCOMPASS.stop();
+
+    I2CError[HMC5843_ID]++;
+    if ( State == InFlight ) Stats[CompassFailS]++;
+
+    F.CompassMissRead = true;
+    F.CompassValid = false;
+
 } // ReadHMC5843
 
+real32  magFieldEarth[3],  magFieldBody[3], dcmMatrix[9];
+boolean firstPassMagOffset = true;
+
 void CalibrateHMC5843(void) {
 
-} // DoHMC5843Test
+    /*
+    void magOffsetCalc()
+    {
+      int16   i, j ;
+      static real32 tempMatrix[3] ;
+      static real32 offsetSum[3] ;
+
+      // Compute magnetic field of the earth
+
+      magFieldEarth[0] = vectorDotProduct(3, &dcmMatrix[0], &magFieldBody[0]);
+      magFieldEarth[1] = vectorDotProduct(3, &dcmMatrix[3], &magFieldBody[0]);
+      magFieldEarth[2] = vectorDotProduct(3, &dcmMatrix[6], &magFieldBody[0]);
+
+      // First pass thru?
+
+      if ( firstPassMagOffset )
+      {
+        setPastValues();                         // Yes, set initial values for previous values
+        firstPassMagOffset = false;              // Clear first pass flag
+      }
+
+      // Compute the offsets in the magnetometer:
+      vectorAdd(3, offsetSum , magFieldBody, magFieldBodyPrevious) ;
+
+      matrixMultiply(1, 3, 3, tempMatrix, magFieldEarthPrevious, dcmMatrix);
+      vectorSubtract(3, offsetSum, offsetSum, tempMatrix);
+      matrixMultiply(1, 3, 3, tempMatrix, magFieldEarth, dcmMatrixPrevious);
+      vectorSubtract(3, offsetSum, offsetSum, tempMatrix) ;
+
+      for ( i = 0 ; i < 3 ; i++ )
+        if ( abs(offsetSum[i] ) < 3 )
+          offsetSum[i] = 0 ;
+
+      vectorAdd (3, magOffset, magOffset, offsetSum);
+      setPastValues();
+    }
+
+    void setPastValues()
+    {
+      static uint8 i;
+
+      for ( i = 0; i < 3; i++)
+      {
+        magFieldEarthPrevious[i] = magFieldEarth[i];
+        magFieldBodyPrevious[i]  = magFieldBody[i];
+      }
+
+      for ( i = 0; i < 9; i++)
+        dcmMatrixPrevious[i] = dcmMatrix[i];
+
+    }
+    */
+} // CalibrateHMC5843
+
+void GetHMC5843Parameters(void) {
+
+    static char CP[16];
+    static uint8 i;
+
+    I2CCOMPASS.start();
+    if ( I2CCOMPASS.write(HMC5843_WR) != I2C_ACK) goto HMC5843Error;
+    if ( I2CCOMPASS.write(0x00) != I2C_ACK) goto HMC5843Error; // point to data
+    I2CCOMPASS.stop();
+
+    if ( I2CCOMPASS.blockread(HMC5843_RD, CP, 13) ) goto HMC5843Error;
+
+    for ( i = 0; i < (uint8)13; i++ ) {
+        TxVal32(i,0,0);
+        TxString(":\t0x");
+        TxValH(CP[i]);
+        TxChar(HT);
+        TxBin8(CP[i]);
+        TxNextLine();
+    }
+
+    return;
+
+HMC5843Error:
+    I2CCOMPASS.stop();
+
+    I2CError[HMC5843_ID]++;
+
+} // GetHMC5843Parameters
 
 void DoHMC5843Test(void) {
+
     TxString("\r\nCompass test (HMC5843)\r\n\r\n");
 
     ReadHMC5843();
 
-    TxString("Mag:\t");
+    GetHMC5843Parameters();
+
+    TxString("\r\nMag:\t");
+    TxVal32(Mag[BF].V, 0, HT);
     TxVal32(Mag[LR].V, 0, HT);
-    TxVal32(Mag[BF].V, 0, HT);
     TxVal32(Mag[UD].V, 0, HT);
     TxNextLine();
     TxNextLine();
 
-    TxVal32(MagHeading * RADDEG * 10.0, 1, 0);
-    TxString(" deg (Magnetic)\r\n");
+    TxVal32(MagHeading * RADDEG * 10.0, 1, ' ');
+    TxString("deg (Magnetic)\r\n");
+
+    Heading = HeadingP = Make2Pi( MagHeading + MagDeviation - CompassOffset );
+    TxVal32(Heading * RADDEG * 10.0, 1, ' ');
+    TxString("deg (True)\r\n");
+} // DoHMC5843Test
+
+boolean WriteByteHMC5843(uint8 a, uint8 d) {
 
-    Heading = Headingp = Make2Pi( MagHeading - MagDeviation - CompassOffset );
-    TxVal32(Heading * RADDEG * 10.0, 1, 0);
-    TxString(" deg (True)\r\n");
-} // DoHMC5843Test
+    I2CCOMPASS.start();
+    if ( I2CCOMPASS.write(HMC5843_WR) != I2C_ACK ) goto HMC5843Error;
+    if ( I2CCOMPASS.write(a) != I2C_ACK ) goto HMC5843Error;
+    if ( I2CCOMPASS.write(d) != I2C_ACK ) goto HMC5843Error;
+    I2CCOMPASS.stop();
+
+    return( false );
+
+HMC5843Error:
+    I2CCOMPASS.stop();
+
+    I2CError[HMC5843_ID]++;
+
+    return(true);
+
+} // WriteByteHMC5843
 
 void InitHMC5843(void) {
-    static uint8 r;
+
+    CompassMaxSlew = (COMPASS_SANITY_CHECK_RAD_S/HMC5843_UPDATE_S);
+
+    if ( WriteByteHMC5843(0x00, HMC5843_DR  << 2) ) goto HMC5843Error; // rate, normal measurement mode
+    if ( WriteByteHMC5843(0x02, 0x00) ) goto HMC5843Error; // mode continuous
 
-    I2CCOMPASS.start();
-    r = I2CCOMPASS.write(HMC5843_WR);
-    r = I2CCOMPASS.write(0x02);
-    r = I2CCOMPASS.write(0x00);   // Set continuous mode (default to 10Hz)
+    Delay1mS(50);
+
+    return;
+
+HMC5843Error:
     I2CCOMPASS.stop();
 
-    Delay1mS(50);
+    I2CError[HMC5843_ID]++;
+
+    F.CompassValid = false;
 
 } // InitHMC5843Magnetometer
 
@@ -271,23 +425,38 @@
     static i16u v;
 
     I2CCOMPASS.start();
-    F.CompassMissRead = I2CCOMPASS.write(HMC6352_RD) != I2C_ACK;
+    if ( I2CCOMPASS.write(HMC6352_RD) != I2C_ACK ) goto HMC6352Error;
     v.b1 = I2CCOMPASS.read(I2C_ACK);
     v.b0 = I2CCOMPASS.read(I2C_NACK);
     I2CCOMPASS.stop();
 
     MagHeading = Make2Pi( ((real32)v.i16 * PI) / 1800.0 - CompassOffset ); // Radians
+
+    return;
+
+HMC6352Error:
+    I2CCOMPASS.stop();
+
+    if ( State == InFlight ) Stats[CompassFailS]++;
+
+    F.CompassMissRead = true;
+
 } // ReadHMC6352
 
 uint8 WriteByteHMC6352(uint8 d) {
+
     I2CCOMPASS.start();
-    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto WError;
-    if ( I2CCOMPASS.write(d) != I2C_ACK ) goto WError;
+    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(d) != I2C_ACK ) goto HMC6352Error;
     I2CCOMPASS.stop();
 
     return( I2C_ACK );
-WError:
+
+HMC6352Error:
     I2CCOMPASS.stop();
+
+    I2CError[HMC6352_ID]++;
+
     return ( I2C_NACK );
 } // WriteByteHMC6352
 
@@ -296,13 +465,13 @@
 #define TEST_COMP_OPMODE 0x70    // standby mode to reliably read EEPROM
 
 void GetHMC6352Parameters(void) {
-    uint8 r;
+    int16 Temp;
 
     I2CCOMPASS.start();
-    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write('G')  != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write(TEST_COMP_OPMODE) != I2C_ACK ) goto CTerror;
+    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write('G')  != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(TEST_COMP_OPMODE) != I2C_ACK ) goto HMC6352Error;
     I2CCOMPASS.stop();
 
     Delay1mS(20);
@@ -310,50 +479,21 @@
     for (r = 0; r <= (uint8)8; r++) { // must have this timing - not block read!
 
         I2CCOMPASS.start();
-        if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto CTerror;
-        if ( I2CCOMPASS.write('r')  != I2C_ACK ) goto CTerror;
-        if ( I2CCOMPASS.write(r)  != I2C_ACK ) goto CTerror;
+        if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto HMC6352Error;
+        if ( I2CCOMPASS.write('r')  != I2C_ACK ) goto HMC6352Error;
+        if ( I2CCOMPASS.write(r)  != I2C_ACK ) goto HMC6352Error;
         I2CCOMPASS.stop();
 
         Delay1mS(10);
 
         I2CCOMPASS.start();
-        if ( I2CCOMPASS.write(HMC6352_RD) != I2C_ACK ) goto CTerror;
+        if ( I2CCOMPASS.write(HMC6352_RD) != I2C_ACK ) goto HMC6352Error;
         CP[r] = I2CCOMPASS.read(I2C_NACK);
         I2CCOMPASS.stop();
 
         Delay1mS(10);
     }
 
-    return;
-
-CTerror:
-    I2CCOMPASS.stop();
-    TxString("FAIL\r\n");
-
-} // GetHMC6352Parameters
-
-void DoHMC6352Test(void) {
-    static real32 Temp;
-
-    TxString("\r\nCompass test (HMC6352)\r\n");
-
-    I2CCOMPASS.start();
-    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write('G')  != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write(TEST_COMP_OPMODE) != I2C_ACK ) goto CTerror;
-    I2CCOMPASS.stop();
-
-    Delay1mS(1);
-
-    //  I2CCOMPASS.start(); // Do Set/Reset now
-    if ( WriteByteHMC6352('O')  != I2C_ACK ) goto CTerror;
-
-    Delay1mS(7);
-
-    GetHMC6352Parameters();
-
     TxString("\r\nRegisters\r\n");
     TxString("\t0:\tI2C");
     TxString("\t 0x");
@@ -419,24 +559,59 @@
     }
     TxNextLine();
 
+    return;
+
+HMC6352Error:
+    I2CCOMPASS.stop();
+
+    I2CError[HMC6352_ID]++;
+
+    TxString("FAIL\r\n");
+
+} // GetHMC6352Parameters
+
+void DoHMC6352Test(void) {
+
+    TxString("\r\nCompass test (HMC6352)\r\n");
+
+    I2CCOMPASS.start();
+    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write('G')  != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(TEST_COMP_OPMODE) != I2C_ACK ) goto HMC6352Error;
+    I2CCOMPASS.stop();
+
+    Delay1mS(1);
+
+    //  I2CCOMPASS.start(); // Do Set/Reset now
+    if ( WriteByteHMC6352('O')  != I2C_ACK ) goto HMC6352Error;
+
+    Delay1mS(7);
+
+    GetHMC6352Parameters();
+
     InitCompass();
-    if ( !F.CompassValid ) goto CTerror;
+    if ( !F.CompassValid ) goto HMC6352Error;
 
     Delay1mS(50);
 
     ReadHMC6352();
-    if ( F.CompassMissRead ) goto CTerror;
+    if ( F.CompassMissRead ) goto HMC6352Error;
 
     TxNextLine();
     TxVal32(MagHeading * RADDEG * 10.0, 1, 0);
     TxString(" deg (Magnetic)\r\n");
-    Heading = Headingp = Make2Pi( MagHeading - MagDeviation - CompassOffset );
+    Heading = HeadingP = Make2Pi( MagHeading - MagDeviation - CompassOffset );
     TxVal32(Heading * RADDEG * 10.0, 1, 0);
     TxString(" deg (True)\r\n");
 
     return;
-CTerror:
+
+HMC6352Error:
     I2CCOMPASS.stop();
+
+    I2CError[HMC6352_ID]++;
+
     TxString("FAIL\r\n");
 } // DoHMC6352Test
 
@@ -445,18 +620,18 @@
     while ( PollRxChar() != 'x' ); // UAVPSet uses 'x' for CONTINUE button
 
     // Do Set/Reset now
-    if ( WriteByteHMC6352('O') != I2C_ACK ) goto CCerror;
+    if ( WriteByteHMC6352('O') != I2C_ACK ) goto HMC6352Error;
 
     Delay1mS(7);
 
     // set Compass device to Calibration mode
-    if ( WriteByteHMC6352('C') != I2C_ACK ) goto CCerror;
+    if ( WriteByteHMC6352('C') != I2C_ACK ) goto HMC6352Error;
 
     TxString("\r\nRotate horizontally 720 deg in ~30 sec. - Press CONTINUE button (x) to Finish\r\n");
     while ( PollRxChar() != 'x' );
 
     // set Compass device to End-Calibration mode
-    if ( WriteByteHMC6352('E') != I2C_ACK ) goto CCerror;
+    if ( WriteByteHMC6352('E') != I2C_ACK ) goto HMC6352Error;
 
     TxString("\r\nCalibration complete\r\n");
 
@@ -466,7 +641,11 @@
 
     return;
 
-CCerror:
+HMC6352Error:
+    I2CCOMPASS.stop();
+
+    I2CError[HMC6352_ID]++;
+
     TxString("Calibration FAILED\r\n");
 } // CalibrateHMC6352
 
@@ -479,35 +658,41 @@
 #define COMP_OPMODE 0x72
 #endif // SUPPRESS_COMPASS_SR
 
+    CompassMaxSlew =  (COMPASS_SANITY_CHECK_RAD_S/HMC6352_UPDATE_S);
+
     // Set device to Compass mode
     I2CCOMPASS.start();
-    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write('G')  != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto CTerror;
-    if ( I2CCOMPASS.write(COMP_OPMODE) != I2C_ACK ) goto CTerror;
+    if ( I2CCOMPASS.write(HMC6352_WR) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write('G')  != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto HMC6352Error;
+    if ( I2CCOMPASS.write(COMP_OPMODE) != I2C_ACK ) goto HMC6352Error;
     I2CCOMPASS.stop();
 
-    Delay1mS(1);
+    Delay1mS(10);
 
     // save operation mode in Flash
-    if ( WriteByteHMC6352('L') != I2C_ACK ) goto CTerror;
+    if ( WriteByteHMC6352('L') != I2C_ACK ) goto HMC6352Error;
 
-    Delay1mS(1);
+    Delay1mS(10);
 
     // Do Bridge Offset Set/Reset now
-    if ( WriteByteHMC6352('O') != I2C_ACK ) goto CTerror;
+    if ( WriteByteHMC6352('O') != I2C_ACK ) goto HMC6352Error;
 
     Delay1mS(50);
 
     F.CompassValid = true;
 
     return;
-CTerror:
+
+HMC6352Error:
+    I2CCOMPASS.stop();
+
+    I2CError[HMC6352_ID]++;
+
     F.CompassValid = false;
-    Stats[CompassFailS]++;
+
     F.CompassFailure = true;
 
-    I2CCOMPASS.stop();
 } // InitHMC6352
 
 boolean HMC6352Active(void) {