Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2189 | - | 1 | /// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- |
2 | #include "Compass.h" |
||
3 | |||
4 | // Default constructor. |
||
5 | // Note that the Vector/Matrix constructors already implicitly zero |
||
6 | // their values. |
||
7 | // |
||
8 | Compass::Compass(void) : |
||
9 | product_id(AP_COMPASS_TYPE_UNKNOWN), |
||
10 | _declination (0.0), |
||
11 | _learn(1), |
||
12 | _use_for_yaw(1), |
||
13 | _null_enable(false), |
||
14 | _null_init_done(false), |
||
15 | _auto_declination(1), |
||
16 | _orientation(ROTATION_NONE) |
||
17 | { |
||
18 | } |
||
19 | |||
20 | // Default init method, just returns success. |
||
21 | // |
||
22 | bool |
||
23 | Compass::init() |
||
24 | { |
||
25 | return true; |
||
26 | } |
||
27 | |||
28 | /* set_orientation |
||
29 | void |
||
30 | Compass::set_orientation(enum Rotation rotation) |
||
31 | { |
||
32 | _orientation = rotation; |
||
33 | } |
||
34 | */ |
||
35 | |||
36 | /* set_offsets |
||
37 | void |
||
38 | Compass::set_offsets(const Vector3f &offsets) |
||
39 | { |
||
40 | _offset.set(offsets); |
||
41 | } |
||
42 | */ |
||
43 | |||
44 | /* save_offsets |
||
45 | void |
||
46 | Compass::save_offsets() |
||
47 | { |
||
48 | _offset.save(); |
||
49 | } |
||
50 | */ |
||
51 | |||
52 | /* get_offsets |
||
53 | Vector3f & |
||
54 | Compass::get_offsets() |
||
55 | { |
||
56 | return _offset; |
||
57 | } |
||
58 | */ |
||
59 | |||
60 | /* set_initial_location |
||
61 | void |
||
62 | Compass::set_initial_location(long latitude, long longitude) |
||
63 | { |
||
64 | // if automatic declination is configured, then compute |
||
65 | // the declination based on the initial GPS fix |
||
66 | if (_auto_declination) { |
||
67 | // Set the declination based on the lat/lng from GPS |
||
68 | null_offsets_disable(); |
||
69 | _declination.set(radians(AP_Declination::get_declination((float)latitude / 10000000, (float)longitude / 10000000))); |
||
70 | null_offsets_enable(); |
||
71 | } |
||
72 | } |
||
73 | */ |
||
74 | |||
75 | /* set_declination |
||
76 | void |
||
77 | Compass::set_declination(float radians) |
||
78 | { |
||
79 | _declination.set_and_save(radians); |
||
80 | } |
||
81 | */ |
||
82 | |||
83 | /* get_declination |
||
84 | float |
||
85 | Compass::get_declination() |
||
86 | { |
||
87 | return _declination.get(); |
||
88 | } |
||
89 | */ |
||
90 | |||
91 | /* calculate |
||
92 | void |
||
93 | Compass::calculate(float roll, float pitch) |
||
94 | { |
||
95 | // Note - This function implementation is deprecated |
||
96 | // The alternate implementation of this function using the dcm matrix is preferred |
||
97 | float headX; |
||
98 | float headY; |
||
99 | float cos_roll; |
||
100 | float sin_roll; |
||
101 | float cos_pitch; |
||
102 | float sin_pitch; |
||
103 | cos_roll = cos(roll); |
||
104 | sin_roll = sin(roll); |
||
105 | cos_pitch = cos(pitch); |
||
106 | sin_pitch = sin(pitch); |
||
107 | |||
108 | // Tilt compensated magnetic field X component: |
||
109 | headX = mag_x*cos_pitch + mag_y*sin_roll*sin_pitch + mag_z*cos_roll*sin_pitch; |
||
110 | // Tilt compensated magnetic field Y component: |
||
111 | headY = mag_y*cos_roll - mag_z*sin_roll; |
||
112 | // magnetic heading |
||
113 | heading = atan2(-headY,headX); |
||
114 | |||
115 | // Declination correction (if supplied) |
||
116 | if( fabs(_declination) > 0.0 ) |
||
117 | { |
||
118 | heading = heading + _declination; |
||
119 | if (heading > M_PI) // Angle normalization (-180 deg, 180 deg) |
||
120 | heading -= (2.0 * M_PI); |
||
121 | else if (heading < -M_PI) |
||
122 | heading += (2.0 * M_PI); |
||
123 | } |
||
124 | |||
125 | // Optimization for external DCM use. Calculate normalized components |
||
126 | heading_x = cos(heading); |
||
127 | heading_y = sin(heading); |
||
128 | } |
||
129 | */ |
||
130 | |||
131 | void |
||
132 | Compass::calculate(const Matrix3f &dcm_matrix) |
||
133 | { |
||
134 | float headX; |
||
135 | float headY; |
||
136 | float cos_pitch = safe_sqrt(1-(dcm_matrix.c.x*dcm_matrix.c.x)); |
||
137 | // sin(pitch) = - dcm_matrix(3,1) |
||
138 | // cos(pitch)*sin(roll) = - dcm_matrix(3,2) |
||
139 | // cos(pitch)*cos(roll) = - dcm_matrix(3,3) |
||
140 | |||
141 | if (cos_pitch == 0.0) { |
||
142 | // we are pointing straight up or down so don't update our |
||
143 | // heading using the compass. Wait for the next iteration when |
||
144 | // we hopefully will have valid values again. |
||
145 | return; |
||
146 | } |
||
147 | |||
148 | // Tilt compensated magnetic field X component: |
||
149 | headX = mag_x*cos_pitch - mag_y*dcm_matrix.c.y*dcm_matrix.c.x/cos_pitch - mag_z*dcm_matrix.c.z*dcm_matrix.c.x/cos_pitch; |
||
150 | // Tilt compensated magnetic field Y component: |
||
151 | headY = mag_y*dcm_matrix.c.z/cos_pitch - mag_z*dcm_matrix.c.y/cos_pitch; |
||
152 | // magnetic heading |
||
153 | // 6/4/11 - added constrain to keep bad values from ruining DCM Yaw - Jason S. |
||
154 | heading = constrain(atan2(-headY,headX), -3.15, 3.15); |
||
155 | |||
156 | // Declination correction (if supplied) |
||
157 | if( fabs(_declination) > 0.0 ) |
||
158 | { |
||
159 | heading = heading + _declination; |
||
160 | if (heading > M_PI) // Angle normalization (-180 deg, 180 deg) |
||
161 | heading -= (2.0 * M_PI); |
||
162 | else if (heading < -M_PI) |
||
163 | heading += (2.0 * M_PI); |
||
164 | } |
||
165 | |||
166 | // Optimization for external DCM use. Calculate normalized components |
||
167 | heading_x = cos(heading); |
||
168 | heading_y = sin(heading); |
||
169 | |||
170 | #if 0 |
||
171 | if (isnan(heading_x) || isnan(heading_y)) { |
||
172 | Serial.printf("COMPASS: c.x %f c.y %f c.z %f cos_pitch %f mag_x %d mag_y %d mag_z %d headX %f headY %f heading %f heading_x %f heading_y %f\n", |
||
173 | dcm_matrix.c.x, |
||
174 | dcm_matrix.c.y, |
||
175 | dcm_matrix.c.x, |
||
176 | cos_pitch, |
||
177 | (int)mag_x, (int)mag_y, (int)mag_z, |
||
178 | headX, headY, |
||
179 | heading, |
||
180 | heading_x, heading_y); |
||
181 | } |
||
182 | #endif |
||
183 | } |
||
184 | |||
185 | |||
186 | /* |
||
187 | this offset nulling algorithm is inspired by this paper from Bill Premerlani |
||
188 | |||
189 | http://gentlenav.googlecode.com/files/MagnetometerOffsetNullingRevisited.pdf |
||
190 | |||
191 | The base algorithm works well, but is quite sensitive to |
||
192 | noise. After long discussions with Bill, the following changes were |
||
193 | made: |
||
194 | |||
195 | 1) we keep a history buffer that effectively divides the mag |
||
196 | vectors into a set of N streams. The algorithm is run on the |
||
197 | streams separately |
||
198 | |||
199 | 2) within each stream we only calculate a change when the mag |
||
200 | vector has changed by a significant amount. |
||
201 | |||
202 | This gives us the property that we learn quickly if there is no |
||
203 | noise, but still learn correctly (and slowly) in the face of lots of |
||
204 | noise. |
||
205 | */ |
||
206 | /* null_offsets |
||
207 | void |
||
208 | Compass::null_offsets(void) |
||
209 | { |
||
210 | if (_null_enable == false || _learn == 0) { |
||
211 | // auto-calibration is disabled |
||
212 | return; |
||
213 | } |
||
214 | |||
215 | // this gain is set so we converge on the offsets in about 5 |
||
216 | // minutes with a 10Hz compass |
||
217 | const float gain = 0.01; |
||
218 | const float max_change = 10.0; |
||
219 | const float min_diff = 50.0; |
||
220 | Vector3f ofs; |
||
221 | |||
222 | ofs = _offset.get(); |
||
223 | |||
224 | if (!_null_init_done) { |
||
225 | // first time through |
||
226 | _null_init_done = true; |
||
227 | for (uint8_t i=0; i<_mag_history_size; i++) { |
||
228 | // fill the history buffer with the current mag vector, |
||
229 | // with the offset removed |
||
230 | _mag_history[i] = Vector3i((mag_x+0.5) - ofs.x, (mag_y+0.5) - ofs.y, (mag_z+0.5) - ofs.z); |
||
231 | } |
||
232 | _mag_history_index = 0; |
||
233 | return; |
||
234 | } |
||
235 | |||
236 | Vector3f b1, b2, diff; |
||
237 | float length; |
||
238 | |||
239 | // get a past element |
||
240 | b1 = Vector3f(_mag_history[_mag_history_index].x, |
||
241 | _mag_history[_mag_history_index].y, |
||
242 | _mag_history[_mag_history_index].z); |
||
243 | // the history buffer doesn't have the offsets |
||
244 | b1 += ofs; |
||
245 | |||
246 | // get the current vector |
||
247 | b2 = Vector3f(mag_x, mag_y, mag_z); |
||
248 | |||
249 | // calculate the delta for this sample |
||
250 | diff = b2 - b1; |
||
251 | length = diff.length(); |
||
252 | if (length < min_diff) { |
||
253 | // the mag vector hasn't changed enough - we don't get |
||
254 | // enough information from this vector to use it. |
||
255 | // Note that we don't put the current vector into the mag |
||
256 | // history here. We want to wait for a larger rotation to |
||
257 | // build up before calculating an offset change, as accuracy |
||
258 | // of the offset change is highly dependent on the size of the |
||
259 | // rotation. |
||
260 | _mag_history_index = (_mag_history_index + 1) % _mag_history_size; |
||
261 | return; |
||
262 | } |
||
263 | |||
264 | // put the vector in the history |
||
265 | _mag_history[_mag_history_index] = Vector3i((mag_x+0.5) - ofs.x, (mag_y+0.5) - ofs.y, (mag_z+0.5) - ofs.z); |
||
266 | _mag_history_index = (_mag_history_index + 1) % _mag_history_size; |
||
267 | |||
268 | // equation 6 of Bills paper |
||
269 | diff = diff * (gain * (b2.length() - b1.length()) / length); |
||
270 | |||
271 | // limit the change from any one reading. This is to prevent |
||
272 | // single crazy readings from throwing off the offsets for a long |
||
273 | // time |
||
274 | length = diff.length(); |
||
275 | if (length > max_change) { |
||
276 | diff *= max_change / length; |
||
277 | } |
||
278 | |||
279 | // set the new offsets |
||
280 | _offset.set(_offset.get() - diff); |
||
281 | } |
||
282 | */ |
||
283 | |||
284 | // Have no idea why this is necessary: |
||
285 | bool Compass::read(void) { |
||
286 | return false; |
||
287 | } |
||
288 | |||
289 | void |
||
290 | Compass::null_offsets_enable(void) |
||
291 | { |
||
292 | _null_init_done = false; |
||
293 | _null_enable = true; |
||
294 | } |
||
295 | |||
296 | void |
||
297 | Compass::null_offsets_disable(void) |
||
298 | { |
||
299 | _null_init_done = false; |
||
300 | _null_enable = false; |
||
301 | } |