1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 /*! \file debug.c
10     \brief ApplyPerturbation function used to analyze dynamic performance
11 
12     The ApplyPerturbation function applies a user-specified step function to
13     prior fusion results which is then "released" in the next fusion cycle.
14     When used in conjustion with the NXP Sensor Fusion Toolbox, this provides
15     a visual indication of the dynamic behavior of the library.
16 
17     Also included is some code for white-box testing within the IAR debug
18     environment.  It can be used to evaluate propagation delays for tilt and
19     eCompass algorithms.  It makes no sense with regard to "Rotation", because
20     that algorithm is simple gyro integration, and will never return to the
21     starting point.  It will also overestimate delays for the kalman filters,
22     as there is no actual gyro data corresponding to the simulated step function.
23     So those filters are not operating as they would in the normal world.
24 */
25 
26 #include "sensor_fusion.h"
27 #include "control.h"
28 #include "stdlib.h"
29 #include "build.h"
30 
31 /// The ApplyPerturbation function applies a user-specified step function to
32 /// prior fusion results which is then "released" in the next fusion cycle.
33 /// When used in conjustion with the NXP Sensor Fusion Toolbox, this provides
34 /// a visual indication of the dynamic behavior of the library.
35 /// This function is normally involved via the "sfg." global pointer.
ApplyPerturbation(SensorFusionGlobals * sfg)36 void ApplyPerturbation(SensorFusionGlobals *sfg)
37 {
38 #ifdef INCLUDE_DEBUG_FUNCTIONS
39     // volatile keyword used to force compiler not to optimize out these
40     // variables.  this does unfortunately result in a couple of warnings (which
41     // can be ignored) farther down in this code.
42     volatile static uint16_t iTestProgress;        ///< Perturbation test status
43     volatile static uint16_t iTestDelay = 0;       ///< Measured delay
44     //volatile static uint16_t iTestAngle = 0;       ///< Integer Residual angle associated with measured delay
45     volatile float angle=0.0f;                     ///< Float Residual angle associated with measured delay
46     volatile static float threshold=0;
47     static Quaternion StartingQ = {
48         .q0 = 1.0,
49         .q1 = 0.0,
50         .q2 = 0.0,
51         .q3 = 0.0
52     };
53     Quaternion CurrentQ =  {
54         .q0 = 1.0,
55         .q1 = 0.0,
56         .q2 = 0.0,
57         .q3 = 0.0
58     };
59     // cache local copies of control flags so we don't have to keep dereferencing pointers below
60     quaternion_type quaternionPacketType;
61     quaternionPacketType = sfg->pControlSubsystem->QuaternionPacketType;
62 
63     Quaternion  ftmpq;  // scratch quaternion
64 
65     // calculate the test perturbation
66     switch (sfg->iPerturbation)
67     {
68         case 1:  // 180 degrees about X
69             ftmpq.q0 = 0.0F;
70             ftmpq.q1 = 1.0F;
71             ftmpq.q2 = 0.0F;
72             ftmpq.q3 = 0.0F;
73             threshold = 90.0;
74             break;
75 
76         case 2:  // 180 degrees about Y
77             ftmpq.q0 = 0.0F;
78             ftmpq.q1 = 0.0F;
79             ftmpq.q2 = 1.0F;
80             ftmpq.q3 = 0.0F;
81             threshold = 90.0;
82             break;
83 
84         case 3:  // 180 degrees about Z
85             ftmpq.q0 = 0.0F;
86             ftmpq.q1 = 0.0F;
87             ftmpq.q2 = 0.0F;
88             ftmpq.q3 = 1.0F;
89             threshold = 90.0;
90             break;
91 
92         case 4:  // -90 degrees about X
93             ftmpq.q0 = ONEOVERSQRT2;
94             ftmpq.q1 = -ONEOVERSQRT2;
95             ftmpq.q2 = 0.0F;
96             ftmpq.q3 = 0.0F;
97             threshold = 45.0;
98             break;
99 
100         case 5:  // +90 degrees about X
101             ftmpq.q0 = ONEOVERSQRT2;
102             ftmpq.q1 = ONEOVERSQRT2;
103             ftmpq.q2 = 0.0F;
104             ftmpq.q3 = 0.0F;
105             threshold = 45.0;
106             break;
107 
108         case 6:  // -90 degrees about Y
109             ftmpq.q0 = ONEOVERSQRT2;
110             ftmpq.q1 = 0.0F;
111             ftmpq.q2 = -ONEOVERSQRT2;
112             ftmpq.q3 = 0.0F;
113             threshold = 45.0;
114             break;
115 
116         case 7:  // +90 degrees about Y
117             ftmpq.q0 = ONEOVERSQRT2;
118             ftmpq.q1 = 0.0F;
119             ftmpq.q2 = ONEOVERSQRT2;
120             ftmpq.q3 = 0.0F;
121             threshold = 45.0;
122             break;
123 
124         case 8:  // -90 degrees about Z
125             ftmpq.q0 = ONEOVERSQRT2;
126             ftmpq.q1 = 0.0F;
127             ftmpq.q2 = 0.0F;
128             ftmpq.q3 = -ONEOVERSQRT2;
129             threshold = 45.0;
130             break;
131 
132         case 9:  // +90 degrees about Z
133             ftmpq.q0 = ONEOVERSQRT2;
134             ftmpq.q1 = 0.0F;
135             ftmpq.q2 = 0.0F;
136             ftmpq.q3 = ONEOVERSQRT2;
137             threshold = 45.0;
138             break;
139 
140         default: // No rotation
141             ftmpq.q0 = 1.0F;
142             ftmpq.q1 = 0.0F;
143             ftmpq.q2 = 0.0F;
144             ftmpq.q3 = 0.0F;
145             break;
146     }
147 
148     switch (quaternionPacketType) {
149 #if F_3DOF_G_BASIC
150     case (Q3):
151         CurrentQ = sfg->SV_3DOF_G_BASIC.fLPq;
152         qAeqAxB(&(sfg->SV_3DOF_G_BASIC.fLPq), &ftmpq);
153         break;
154 #endif
155 #if F_3DOF_B_BASIC
156     case (Q3M):
157         CurrentQ = sfg->SV_3DOF_B_BASIC.fLPq;
158         qAeqAxB(&(sfg->SV_3DOF_B_BASIC.fLPq), &ftmpq);
159         break;
160 #endif
161 #if F_3DOF_Y_BASIC
162     case (Q3G):
163         CurrentQ = sfg->SV_3DOF_Y_BASIC.fq;
164         qAeqAxB(&(sfg->SV_3DOF_Y_BASIC.fq), &ftmpq);
165         break;
166 #endif
167 #if F_6DOF_GB_BASIC
168     case (Q6MA):
169         CurrentQ = sfg->SV_6DOF_GB_BASIC.fLPq;
170         qAeqAxB(&(sfg->SV_6DOF_GB_BASIC.fLPq), &ftmpq);
171         break;
172 #endif
173 #if F_6DOF_GY_KALMAN
174     case (Q6AG):
175         CurrentQ = sfg->SV_6DOF_GY_KALMAN.fqPl;
176         qAeqAxB(&(sfg->SV_6DOF_GY_KALMAN.fqPl), &ftmpq);
177         break;
178 #endif
179 #if F_9DOF_GBY_KALMAN
180     case (Q9):
181         CurrentQ = sfg->SV_9DOF_GBY_KALMAN.fqPl;
182         qAeqAxB(&(sfg->SV_9DOF_GBY_KALMAN.fqPl), &ftmpq);
183         break;
184 #endif
185     default:
186         CurrentQ.q0 = 1.0;
187         CurrentQ.q1 = 0.0;
188         CurrentQ.q2 = 0.0;
189         CurrentQ.q3 = 0.0;
190         break;
191     }
192 
193     // Begin of code for white-box testing - requires IAR debugger
194     switch (iTestProgress) {
195     case 0:  // no test in progress, check to see if we should start one
196         if (sfg->iPerturbation>0) {
197             // Start Test
198             iTestProgress = 1;
199             sfg->iPerturbation = 0;
200             iTestDelay = 0;
201             //iTestAngle = 0;
202             // We'll need the complex conjugate of the starting quaternion
203             StartingQ.q0 = CurrentQ.q0;
204             StartingQ.q1 = -1 * CurrentQ.q1;
205             StartingQ.q2 = -1 * CurrentQ.q2;
206             StartingQ.q3 = -1 * CurrentQ.q3;
207         }
208         break;
209     default:  // Test in progress, check to see if trigger reached
210         iTestDelay += 1;
211         qAeqAxB(&CurrentQ, &StartingQ);
212         angle = 2 * F180OVERPI * acos(CurrentQ.q0);
213         angle = fmod(fabs(angle), 180.0);
214         //iTestAngle = (uint16_t) (10 * angle);
215         // In IAR, you can use a Log breakpoint to monitor "return to stationary pose".
216         // Use the following expression in the Message field and check the
217         // checkbox for C-Spy macro.  Then Click any of the "Test" buttons
218         // in the Sensor Fusion Toolbox and monitor the results in the Messages window.
219         //"Delay=", iTestDelay:%d, " Angle=",iTestAngle:%d
220         if (angle<threshold)           iTestProgress=2;  // triggered
221         if (angle < (0.2 * threshold)) iTestProgress=0;  // test is done
222         if (iTestDelay>100) iTestProgress=0;  // abort test
223         break;
224     }
225     // End of code for white-box testing
226 #endif
227 }
228