Subversion Repositories FlightCtrl

Rev

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
}