Mercurial > public > hwos_code
comparison src/compass.c @ 604:ca4556fb60b9
bump to 2.99beta, work on 3.00 stable
author | heinrichsweikamp |
---|---|
date | Thu, 22 Nov 2018 19:47:26 +0100 |
parents | 7d9edd3b8c86 |
children | cd986267a5ca |
comparison
equal
deleted
inserted
replaced
603:00b24fb4324d | 604:ca4556fb60b9 |
---|---|
1 ////////////////////////////////////////////////////////////////////////////// | 1 ////////////////////////////////////////////////////////////////////////////// |
2 /// compass.c | 2 /// compass.c |
3 /// Compute north direction from magnetic/acceleration measures. | 3 /// Compute north direction from magnetic/acceleration measures |
4 /// | |
4 /// Copyright (c) 2012-2015, JD Gascuel, HeinrichsWeikamp, all right reserved. | 5 /// Copyright (c) 2012-2015, JD Gascuel, HeinrichsWeikamp, all right reserved. |
5 ////////////////////////////////////////////////////////////////////////////// | 6 ////////////////////////////////////////////////////////////////////////////// |
6 // HISTORY | 7 // HISTORY |
7 // 2012-12-01 [jDG] Creation | 8 // 2012-12-01 [jDG] Creation |
8 // 2012-12-23 [jDG] Added filtering. | 9 // 2012-12-23 [jDG] Added filtering. |
10 // 2015-05-22 [jDG] Minor cleanups. Smaller calibration code. | 11 // 2015-05-22 [jDG] Minor cleanups. Smaller calibration code. |
11 | 12 |
12 #include "compass.h" | 13 #include "compass.h" |
13 | 14 |
14 ////////////////////////////////////////////////////////////////////////////// | 15 ////////////////////////////////////////////////////////////////////////////// |
15 // mH: Crude work-around, needs to be made right | 16 // mH: crude work-around, needs to be fixed up |
16 #ifndef UNIX | 17 #ifndef UNIX |
17 # pragma udata overlay bank8=0x800 | 18 # pragma udata overlay bank8=0x800 |
18 static char C_STACK[256]; // Overlay C-code data stack here. | 19 static char C_STACK[256]; // overlay C-code data stack here |
19 # define RESET_C_STACK \ | 20 # define RESET_C_STACK \ |
20 _asm \ | 21 _asm \ |
21 LFSR 1, 0x800 \ | 22 LFSR 1, 0x800 \ |
22 LFSR 2, 0x800 \ | 23 LFSR 2, 0x800 \ |
23 _endasm | 24 _endasm |
24 # pragma udata overlay bank9_compass | 25 # pragma udata overlay bank9_compass |
25 # pragma code compass_run | 26 # pragma code compass_run |
26 #else | 27 #else |
27 # define RESET_C_STACK | 28 # define RESET_C_STACK |
28 #endif | 29 #endif |
29 | 30 |
30 ////////////////////////////////////////////////////////////////////////////// | 31 ////////////////////////////////////////////////////////////////////////////// |
31 // fifth order of polynomial approximation of atan(), giving 0.05 deg max error | 32 // fifth order of polynomial approximation of atan(), giving 0.05 deg max error |
32 // | 33 // |
33 #define K1 (5701) // Needs K1/2**16 | 34 #define K1 (5701) // needs K1/2**16 |
34 #define K2 (1645) // Needs K2/2**48 WAS NEGATIV | 35 #define K2 (1645) // needs K2/2**48 WAS NEGATIV |
35 #define K3 ( 446) // Needs K3/2**80 | 36 #define K3 ( 446) // needs K3/2**80 |
36 | 37 |
37 ////////////////////////////////////////////////////////////////////////////// | 38 |
38 // Interface to assembleur multiplies | 39 ////////////////////////////////////////////////////////////////////////////// |
40 // Interface to assembler multiplies | |
41 | |
39 Int16 umul(PARAMETER Int16 a, PARAMETER Int16 b) | 42 Int16 umul(PARAMETER Int16 a, PARAMETER Int16 b) |
40 { | 43 { |
41 extern Int16 compass_umul(void); | 44 extern Int16 compass_umul(void); |
42 extern Int16 compass_a, compass_b; | 45 extern Int16 compass_a, compass_b; |
43 compass_a = a; | 46 compass_a = a; |
44 compass_b = b; | 47 compass_b = b; |
45 return compass_umul(); | 48 return compass_umul(); |
46 } | 49 } |
47 | 50 |
48 Int16 imul(PARAMETER Int16 a, PARAMETER Int16 b) | 51 Int16 imul(PARAMETER Int16 a, PARAMETER Int16 b) |
49 { | 52 { |
50 extern Int16 compass_imul(void); | 53 extern Int16 compass_imul(void); |
51 extern Int16 compass_a, compass_b; | 54 extern Int16 compass_a, compass_b; |
52 compass_a = a; | 55 compass_a = a; |
53 compass_b = b; | 56 compass_b = b; |
54 return compass_imul(); | 57 return compass_imul(); |
55 } | 58 } |
56 | 59 |
57 ////////////////////////////////////////////////////////////////////////////// | 60 ////////////////////////////////////////////////////////////////////////////// |
58 /// Returns a / b * 2**16 | 61 /// Returns a / b * 2**16 |
59 /// | 62 /// |
60 /// A 16/16 -> 16 bits divide, returning a scalled result. | 63 /// A 16/16 -> 16 bits divide, returning a scaled result. |
61 /// Used to multiply fractional numbers in the range 0..1, | 64 /// Used to multiply fractional numbers in the range 0..1, |
62 /// represented as 0..32767. | 65 /// represented as 0..32767. |
66 | |
63 Int16 udiv(PARAMETER Int16 a, PARAMETER Int16 b) | 67 Int16 udiv(PARAMETER Int16 a, PARAMETER Int16 b) |
64 { | 68 { |
65 OVERLAY Int16 d, r; | 69 OVERLAY Int16 d, r; |
66 | 70 |
67 //---- Pre-scale both numerator and denominator -------------------------- | 71 //---- Pre-scale both numerator and denominator -------------------------- |
68 while( (((a>>8) | (b>>8)) & 0xC0) == 0 ) | 72 while( (((a>>8) | (b>>8)) & 0xC0) == 0 ) |
69 { | 73 { |
70 a <<= 1; | 74 a <<= 1; |
71 b <<= 1; | 75 b <<= 1; |
72 } | 76 } |
73 | 77 |
74 //---- Make division trials ---------------------------------------------- | 78 //---- Make division trials ---------------------------------------------- |
75 d = 0x4000; // Starts with 0.5, because 1.0 is sign bit. | 79 d = 0x4000; // start with 0.5, because 1.0 is sign bit |
76 b >>= 1; // Hence pre-shift b. | 80 b >>= 1; // hence pre-shift b |
77 r = 0; | 81 r = 0; |
78 do { | 82 do { |
79 if( a >= b ) { // a is big enough ? | 83 if( a >= b ) // a is big enough ? |
80 a -= b; // then count d times b out of it. | 84 { |
81 r |= d; // and accumulate that bit. | 85 a -= b; // then count d times b out of it |
82 } | 86 r |= d; // and accumulate that bit |
83 b >>= 1; // then loop trying twice smaller. | 87 } |
84 d >>= 1; | 88 b >>= 1; // then loop trying twice smaller |
85 } while( b ); | 89 d >>= 1; |
86 return r; | 90 } while( b ); |
91 return r; | |
87 } | 92 } |
88 | 93 |
89 ////////////////////////////////////////////////////////////////////////////// | 94 ////////////////////////////////////////////////////////////////////////////// |
90 /// Computes atan(y/x) in Angle, for x, y in range 0..32767 | 95 /// Computes atan(y/x) in Angle, for x, y in range 0..32767 |
91 /// | 96 /// |
92 /// Results a single quadrant Angle, in the range 0 .. Q_PI/2 | 97 /// Results a single quadrant Angle, in the range 0 .. Q_PI/2 |
98 | |
93 Angle utan(PARAMETER Int16 y, PARAMETER Int16 x) | 99 Angle utan(PARAMETER Int16 y, PARAMETER Int16 x) |
94 { | 100 { |
95 OVERLAY Int16 ratio, angle, x2, x3; | 101 OVERLAY Int16 ratio, angle, x2, x3; |
96 | 102 |
97 //---- Handle zero divisor ----------------------------------------------- | 103 //---- Handle zero divisor ----------------------------------------------- |
98 if( x == 0 ) | 104 if( x == 0 ) |
99 return (y == 0) ? 0 : Q_PIO2; | 105 return (y == 0) ? 0 : Q_PIO2; |
100 | 106 |
101 //---- Make it half-quadrant : 0 .. 45 deg ------------------------------- | 107 //---- Make it half-quadrant : 0 .. 45 deg ------------------------------- |
102 ratio = (x > y) ? udiv(y, x) : udiv(x, y); | 108 ratio = (x > y) ? udiv(y, x) : udiv(x, y); |
103 | 109 |
104 //---- Then apply the polynomial approximation --------------------------- | 110 //---- Then apply the polynomial approximation --------------------------- |
105 angle = umul(K1, ratio); // r*K1 / 2**16 | 111 angle = umul(K1, ratio); // r*K1 / 2**16 |
106 x2 = umul(ratio, ratio); // r**2 / 2**16 | 112 x2 = umul(ratio, ratio); // r**2 / 2**16 |
107 x3 = umul(x2, ratio); // r**3 / 2**32 | 113 x3 = umul(x2, ratio); // r**3 / 2**32 |
108 angle -= umul(x3, K2); // K2*r**3 / 2**48: NEGATIV. | 114 angle -= umul(x3, K2); // K2*r**3 / 2**48: NEGATIV. |
109 | 115 |
110 x3 = umul(x3, x2); // r**5 / 2**64 | 116 x3 = umul(x3, x2); // r**5 / 2**64 |
111 angle += umul(x3, K3); // K3*r**5 / 2**80 | 117 angle += umul(x3, K3); // K3*r**5 / 2**80 |
112 | 118 |
113 //---- Recover the full quadrant ----------------------------------------- | 119 //---- Recover the full quadrant ----------------------------------------- |
114 return (x < y) ? (Angle)(Q_PIO2 - angle) | 120 return (x < y) ? (Angle)(Q_PIO2 - angle) |
115 : (Angle)(angle); | 121 : (Angle)(angle); |
116 } | 122 } |
117 | 123 |
118 ////////////////////////////////////////////////////////////////////////////// | 124 ////////////////////////////////////////////////////////////////////////////// |
119 /// Computes atan2(y/x) in Angle, for x, y in range -32768 to 32767 | 125 /// Computes atan2(y/x) in Angle, for x, y in range -32768 to 32767 |
120 /// | 126 /// |
121 /// Results a four quadrant Angle, in the range -Q_PI .. +Q_PI | 127 /// Results a four quadrant Angle, in the range -Q_PI .. +Q_PI |
128 | |
122 Angle itan(PARAMETER Int16 y, PARAMETER Int16 x) | 129 Angle itan(PARAMETER Int16 y, PARAMETER Int16 x) |
123 { | 130 { |
124 // Beware: -32768 is not properly handled (sgn error). | 131 // Beware: -32768 is not properly handled (sign error) |
125 if( x == -32768 ) x = -32767; | 132 if( x == -32768 ) x = -32767; |
126 if( y == -32768 ) y = -32767; | 133 if( y == -32768 ) y = -32767; |
127 | 134 |
128 if( x >= 0 ) | 135 if( x >= 0 ) |
129 if( y >= 0 ) // First quadrant: 0..90 deg. | 136 if( y >= 0 ) // first quadrant: 0..90 deg. |
130 return utan(y,x); | 137 return utan(y,x); |
131 else // Fourth quadrant: 0..-90 deg | 138 else // fourth quadrant: 0..-90 deg |
132 return -utan(-y,x); | 139 return -utan(-y,x); |
133 else | 140 else |
134 if( y >= 0 ) // Second quadrant: 90..180 deg | 141 if( y >= 0 ) // second quadrant: 90..180 deg |
135 return Q_PI - utan(y, -x); | 142 return Q_PI - utan(y, -x); |
136 else // Third quadrant: -90..-180 deg; | 143 else // third quadrant: -90..-180 deg; |
137 return -Q_PI + utan(-y, -x); | 144 return -Q_PI + utan(-y, -x); |
138 } | 145 } |
139 | 146 |
140 ////////////////////////////////////////////////////////////////////////////// | 147 ////////////////////////////////////////////////////////////////////////////// |
141 /// Computes cos(theta) = sqrtf(x2/h2), | 148 /// Computes cos(theta) = sqrtf(x2/h2), |
142 /// when theta = atan(y/x) and h2=x*x+y*y | 149 /// when theta = atan(y/x) and h2=x*x+y*y |
143 /// | 150 /// |
151 | |
144 Int16 cosxh(PARAMETER Int16 x2, PARAMETER Int16 h2) | 152 Int16 cosxh(PARAMETER Int16 x2, PARAMETER Int16 h2) |
145 { | 153 { |
146 OVERLAY Int16 r = 0; | 154 OVERLAY Int16 r = 0; |
147 OVERLAY Int16 d = 0x4000; | 155 OVERLAY Int16 d = 0x4000; |
148 | 156 |
149 do { | 157 do { |
150 OVERLAY Int16 a = r + d; | 158 OVERLAY Int16 a = r + d; |
151 a = umul(a, a); | 159 a = umul(a, a); |
152 a = umul(a, h2); | 160 a = umul(a, h2); |
153 if( a <= x2 ) r += d; | 161 if( a <= x2 ) r += d; |
154 d >>= 1; | 162 d >>= 1; |
155 } while( d ); | 163 } while( d ); |
156 | 164 |
157 return r; | 165 return r; |
158 } | 166 } |
159 | 167 |
160 ////////////////////////////////////////////////////////////////////////////// | 168 ////////////////////////////////////////////////////////////////////////////// |
161 /// Computes both sin and cos of angle y/x, | 169 /// Computes both sin and cos of angle y/x, |
162 /// with h = sqrt(x**2+y**2). | 170 /// with h = sqrt(x**2+y**2). |
163 /// | 171 /// |
172 | |
164 void sincos(PARAMETER Int16 x, PARAMETER Int16 y, Int16* sin, Int16* cos) | 173 void sincos(PARAMETER Int16 x, PARAMETER Int16 y, Int16* sin, Int16* cos) |
165 { | 174 { |
166 OVERLAY Int16 x2, y2, h2; | 175 OVERLAY Int16 x2, y2, h2; |
167 | 176 |
168 //---- Fold into one quadant --------------------------------------------- | 177 //---- Fold into one quadant --------------------------------------------- |
169 OVERLAY char neg = 0; | 178 OVERLAY char neg = 0; |
170 if( x < 0 ) | 179 if( x < 0 ) |
171 { | 180 { |
172 neg |= 1; | 181 neg |= 1; |
173 x = -x; | 182 x = -x; |
174 } | 183 } |
175 if( y < 0 ) | 184 if( y < 0 ) |
176 { | 185 { |
177 neg |= 2; | 186 neg |= 2; |
178 y = -y; | 187 y = -y; |
179 } | 188 } |
180 | 189 |
181 //---- Pre-scale both numerator and denominator ---------------------- | 190 //---- Pre-scale both numerator and denominator ---------------------- |
182 while( (((x>>8) | (y>>8)) & 0xE0) == 0 ) | 191 while( (((x>>8) | (y>>8)) & 0xE0) == 0 ) |
183 { | 192 { |
184 x <<= 1; | 193 x <<= 1; |
185 y <<= 1; | 194 y <<= 1; |
186 } | 195 } |
187 | 196 |
188 //---- Uses trig() to do the stuff one on quadrant ------------------- | 197 //---- Uses trig() to do the stuff one on quadrant ------------------- |
189 x2 = umul(x,x); | 198 x2 = umul(x,x); |
190 y2 = umul(y,y); | 199 y2 = umul(y,y); |
191 h2 = x2 + y2; | 200 h2 = x2 + y2; |
192 x2 = cosxh(x2, h2); | 201 x2 = cosxh(x2, h2); |
193 | 202 |
194 //---- Results back in four quadrants -------------------------------- | 203 //---- Results back in four quadrants -------------------------------- |
195 *cos = (neg & 1) ? -x2 : x2; | 204 *cos = (neg & 1) ? -x2 : x2; |
196 y2 = cosxh(y2, h2); | 205 y2 = cosxh(y2, h2); |
197 *sin = (neg & 2) ? -y2 : y2; | 206 *sin = (neg & 2) ? -y2 : y2; |
198 } | 207 } |
199 | 208 |
200 ////////////////////////////////////////////////////////////////////////////// | 209 ////////////////////////////////////////////////////////////////////////////// |
201 // | 210 // |
202 | 211 |
203 void compass(void) | 212 void compass(void) |
204 { | 213 { |
205 OVERLAY Int16 sin, cos; | 214 OVERLAY Int16 sin, cos; |
206 OVERLAY Int16 iBfx, iBfy, Gz; | 215 OVERLAY Int16 iBfx, iBfy, Gz; |
207 OVERLAY Int16 iBpx, iBpy, iBpz; | 216 OVERLAY Int16 iBpx, iBpy, iBpz; |
208 RESET_C_STACK; | 217 |
209 | 218 RESET_C_STACK; |
210 //---- Make hard iron correction ----------------------------------------- | 219 |
211 // Measured magnetometer orientation, measured ok. | 220 //---- Make hard iron correction ----------------------------------------- |
212 // From matthias drawing: (X,Y,Z) --> (X,Y,Z) : no rotation. | 221 // Measured magnetometer orientation, measured ok |
213 iBpx = compass_DX_f - compass_CX_f; // X | 222 // From matthias drawing: (X,Y,Z) --> (X,Y,Z) : no rotation |
214 iBpy = compass_DY_f - compass_CY_f; // Y | 223 iBpx = compass_DX_f - compass_CX_f; // X |
215 iBpz = compass_DZ_f - compass_CZ_f; // Z | 224 iBpy = compass_DY_f - compass_CY_f; // Y |
216 | 225 iBpz = compass_DZ_f - compass_CZ_f; // Z |
217 //---- Calculate sine and cosine of roll angle Phi ----------------------- | 226 |
218 sincos(accel_DZ_f, accel_DY_f, &sin, &cos); | 227 //---- Calculate sine and cosine of roll angle Phi ----------------------- |
219 | 228 sincos(accel_DZ_f, accel_DY_f, &sin, &cos); |
220 //---- rotate by roll angle (-Phi) --------------------------------------- | 229 |
221 iBfy = imul(iBpy, cos) - imul(iBpz, sin); | 230 //---- rotate by roll angle (-Phi) --------------------------------------- |
222 iBpz = imul(iBpy, sin) + imul(iBpz, cos); | 231 iBfy = imul(iBpy, cos) - imul(iBpz, sin); |
223 Gz = imul(accel_DY_f, sin) + imul(accel_DZ_f, cos); | 232 iBpz = imul(iBpy, sin) + imul(iBpz, cos); |
224 | 233 Gz = imul(accel_DY_f, sin) + imul(accel_DZ_f, cos); |
225 //---- calculate sin and cosine of pitch angle Theta --------------------- | 234 |
226 sincos(Gz, -accel_DX_f, &sin, &cos); // NOTE: changed sin sign. | 235 //---- calculate sin and cosine of pitch angle Theta --------------------- |
227 | 236 sincos(Gz, -accel_DX_f, &sin, &cos); // NOTE: changed sin sign |
228 /* correct cosine if pitch not in range -90 to 90 degrees */ | 237 |
229 if( cos < 0 ) cos = -cos; | 238 /* correct cosine if pitch not in range -90 to 90 degrees */ |
230 | 239 if( cos < 0 ) cos = -cos; |
231 ///---- de-rotate by pitch angle Theta ----------------------------------- | 240 |
232 iBfx = imul(iBpx, cos) + imul(iBpz, sin); | 241 ///---- de-rotate by pitch angle Theta ----------------------------------- |
233 | 242 iBfx = imul(iBpx, cos) + imul(iBpz, sin); |
234 //---- Detect uncalibrated compass --------------------------------------- | 243 |
235 if( !compass_CX_f && !compass_CY_f && !compass_CZ_f ) | 244 //---- Detect uncalibrated compass --------------------------------------- |
236 { | 245 if( !compass_CX_f && !compass_CY_f && !compass_CZ_f ) |
237 compass_heading = -1; | 246 { |
238 return; | 247 compass_heading = -1; |
239 } | 248 return; |
240 | 249 } |
241 //---- calculate current yaw = e-compass angle Psi ----------------------- | 250 |
242 // Result in degree (no need of 0.01 deg precision... | 251 //---- calculate current yaw = e-compass angle Psi ----------------------- |
243 compass_heading = itan(-iBfy, iBfx) / 100; | 252 // Result in degree (no need of 0.01 deg precision... |
244 | 253 compass_heading = itan(-iBfy, iBfx) / 100; |
245 // Result in 0..360 range: | 254 |
246 if( compass_heading < 0 ) | 255 // Result in 0..360 range: |
247 compass_heading += 360; | 256 if( compass_heading < 0 ) |
248 } | 257 compass_heading += 360; |
258 } |