-
Notifications
You must be signed in to change notification settings - Fork 1
/
06_compass_RTC.ino
296 lines (224 loc) · 35.4 KB
/
06_compass_RTC.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// SETUP MPU9250 ACCELLEROMETER AND MAGNETOMETER
bool MPU9250_setup(){
if (!i2c_init()){ // i2c init failed
i2c_write_byte(MPU9250_ADDRESS,29,0x06); // need to do something for some reason
return 0; // exit function with error code
}
// Set accelerometers low pass filter at 5Hz
if (!i2c_write_byte(MPU9250_ADDRESS,29,0x06)) return 0; // set, exit with error code upon failure
// Set gyroscope low pass filter at 5Hz
if (!i2c_write_byte(MPU9250_ADDRESS,26,0x06)) return 0;
// Configure accelerometers range 0x00 2G, 0x08 4G, 0x10 8G, 0x18 16G
if (!i2c_write_byte(MPU9250_ADDRESS,28,0x00)) return 0;
// Set bypass mode for the magnetometers
if (!i2c_write_byte(MPU9250_ADDRESS,0x37,0x02)) return 0; // there are two chips in the IC. This setting affects, how they interact.
// Request continuous magnetometer measurements in 16 bits
if (!i2c_write_byte(MAG_ADDRESS,0x0A,0x16)) return 0;
return 1; // initialization successfull
// }
}
// READ ACCELEROMETER AND CALCULATE ELEVATION
void acceleration()
{
i2c_start_read_burst(MPU9250_ADDRESS,0x3B); //start reading in burst mode
// Accelerometer
/*old orientation of the compass chip
int16_t ax=i2c_read(0) << 8 | i2c_read(0);
i2c_read(0);i2c_read(0); //discard ay
int16_t az=i2c_read(0) << 8 | i2c_read(0);
*/
i2c_read(0);i2c_read(0); //discard ay (actually now ax in the present orientation of the chip)
/* for controlbox in the housing
int16_t ax=-(i2c_read(0) << 8 | i2c_read(0)); U // acceleration in x-direction. Create 2 byte integer from stored bytes
int16_t az=-(i2c_read(0) << 8 | i2c_read(0)); // acceleration in z-direction
*/
// for test setup
int16_t ax=(i2c_read(0) << 8 | i2c_read(0)); // acceleration in x-direction. Create 2 byte integer from stored bytes
int16_t az=(i2c_read(0) << 8 | i2c_read(0)); // acceleration in z-direction
temperature = i2c_read(0) << 8 | i2c_read(1); // temperature reading from temperature correction, presently not used
i2c_stop(); // burst finished
elevation_compass=(atan((float)az/ax)-PI/2)+PI/2; // calculate tilt (elevation)
if (elevation_compass<-0.3) elevation_compass += PI; // make sure that the elevation is in the 0..Pi range
elevation_compass *= 1800 / PI; // calculates in 0.1 degree steps to be compatible with integer
elevation_compass -= ((ERI(EA_el_up_limit)-elevation_compass)*ERI(EA_el_down_corr) + (elevation_compass-ERI(EA_el_down_limit))*ERI(EA_el_up_corr))/(ERI(EA_el_up_limit)-ERI(EA_el_down_limit));
// apply corrections which are set in the calibration routine
// Serial.print(F(" El=")); Serial.print(elevation_compass);
}
// READ MAGNETOMETER AND CALCULATE COMPASS READING ( AZIMUTH )
void magnetometer()
{
int mx_old,my_old,mz_old,mx_new,my_new,mz_new;
mx_raw=0; my_raw=0; mz_raw=0;
for (int i=0; i<20; i++) // 20x oversampling
{
while (!(i2c_read_byte(MAG_ADDRESS,0x02) &0x01 )){}; // &0x01 masks last bit in ST1. Last bit is data ready.
i2c_start_read_burst(MAG_ADDRESS,0x03); //read seven bytes from 0x03 register
// Magnetometer
/* old arrangement, change to y=-x, x=-y, z=-z
my_raw+=-(i2c_read(0) | i2c_read(0) << 8);
mx_raw+=-(i2c_read(0) | i2c_read(0) << 8);
mz_raw+=(i2c_read(0) | i2c_read(0) << 8);
*/
/* for controlbox in the housing.
mx_raw+=(i2c_read(0) | i2c_read(0) << 8); // read 3 axis magnetometer values
my_raw+=(i2c_read(0) | i2c_read(0) << 8); // create 16 bits integers from 8 bits data
mz_raw-=(i2c_read(0) | i2c_read(0) << 8); // change sign for z-axis
*/
// for test setup, y-arrow on MPU9250 points up in morning position. xyz readable from top. Negate x and z for mounting on tracker.
mx_new=-(i2c_read(0) | i2c_read(0) << 8); // read 3 axis magnetometer values
my_new=(i2c_read(0) | i2c_read(0) << 8); // create 16 bits integers from 8 bits data
mz_new=(i2c_read(0) | i2c_read(0) << 8); // change sign for z-axis
i2c_read(1); // read last byte
i2c_stop();
if ( (i % 2 == 1) & ((abs(mx_new-mx_old)>100) | (abs(my_new-my_old)>100) | (abs(mz_new-mz_old)>100))){ // check for unrealistic differences and discard last two values if necessary
i-=2; // Serial.println("bad");
}
else {
mx_raw += mx_old + mx_new;
my_raw += my_old + my_new;
mz_raw += mz_old + mz_new;
}
mx_old = mx_new;
my_old = my_new;
mz_old = mz_new;
}
mx_raw /= 10; // divide by 10 to make sure that a drift of the zero positions will not result in an integer overflow at +/-32k
my_raw /= 10;
mz_raw /= 10;
if ((calibration_step>8)&(calibration_step<21)){
mx_raw += temporary_mx_neutral;
mz_raw += temporary_mz_neutral; // corrections used in the calibration routine. Will be saved to EEPROM at the end of the calibration routine.
}
else {
mx_raw += ERI(EA_mx_neutral); // user defined shift of neutral positions to correct for high offset values
mz_raw += ERI(EA_mz_neutral);
}
compass_calculations(); // calculate the compass reading (AZIMUTH)
}
// MAIN ROUTINE FOR COMPASS CALCULATIONS AND CORRECTIONS
void compass_calculations(){
elevation1_position_float = (elevation_compass - float(ERI(EA_el_down_limit)))/(float(ERI(EA_el_up_limit)-ERI(EA_el_down_limit)))*(2*ERI(EA_el_steps)); //calculate an elevation position
if (elevation1_position_float < 0) {elevation1_position_float = 0.01; } // slightly above (2*ERI(EA_el_steps)) or below 0 results in reading undesired positions in EEPROM --> correct
if (elevation1_position_float > (2*ERI(EA_el_steps))) {elevation1_position_float = (2*ERI(EA_el_steps))-0.01; }
elevation1_position = int(elevation1_position_float); // int cuts off anything after the decimal point
elevation1_position_float -= float(elevation1_position); // what has been cut off. This is important for the interpolation between defined elevation positions
//
/*
* DISABLED but may be helpful in some special cases: load west readings for present elevation from EEPROM, interpolate
* HOW TO CORRECT THE WEST POSITION FOR KNOWN ANGULAR DEVIATIONS FROM CALIBRATION
* we have stored the west readings of the magnetic components my and mx_horizontal in EEPROM during the multiple passes in calibration_step == 9.
* In calibration_step == 20 angular corrections in west directions were calculated and stored to EEPROM-positions 752ff
* These are restored now in order to calculate an angular correction of the Azimuth for the present elevation position.
* West should be at 270° = 2700*0.1°. Therefore we calculate the difference of the calculated compass position to 2700.
* We want to correct, so we take the negative of this value and store it into the the variable azimuth compass. The actual reading will be added to this value.
azimuth_compass = 0 -(ERI(752+2*elevation1_position)*elevation1_position_float + ERI(750+2*elevation1_position)*(1-elevation1_position_float));
*/
azimuth_compass = 0; // preset
// present readings for first pass
mx = mx_raw;
my = my_raw;
mz = mz_raw;
calc_mx_horizontal(); // calculate horizontal component in x-direction with all errors
azimuth_compass += compass_calculation_2(); // get compass angle (azimuth) with present magnetometer readings
azimuth_compass -= ERI(EA_north_corr); // user defined compass correction angle
correct_azimuth_compass_range_to_0_3600(); // make the compass reading unambiguous
while (azimuth_compass - prev_azimuth_compass < -1799){azimuth_compass += 3600;} // supress jumps, count up or down continuously
while (azimuth_compass - prev_azimuth_compass > 1799){azimuth_compass -= 3600;}
if ((azimuth_compass < 700)&(ERI(EA_azimuth_left_of_east) < 1)) {EWI(EA_azimuth_left_of_east,1);Serial.println(F("to1"));} // remember left of east azimuth values in case of power failure
if ((azimuth_compass > 1100)&(ERI(EA_azimuth_left_of_east) > 0)) {EWI(EA_azimuth_left_of_east,0);Serial.println(F("to0"));}
if(print_compass) {Serial.print(F("AzC=")); Serial.print(azimuth_compass); Serial.print(F(" "));} // user enabled printing of compass values
if (averaged_azimuth_compass == 10000){averaged_azimuth_compass = azimuth_compass;} // handle first pass through this routine by a preset of 10000 in the header
// Smooth azimuth compass readings. Add or subtract fullspeed Azimuth movements. Use the Azimuth speed 1/(ms per degree) measured during the calibration
else if (fabs(azimuth_compass-averaged_azimuth_compass)<100) // smooth if the deviations are smaller than 10 degree, use moving average
{averaged_azimuth_compass = 0.1*azimuth_compass +0.9*(averaged_azimuth_compass + float((millis()-prevmil_[0]))/(float(ERI(EA_azimuth_rotation_msdeg))/10)*(!(move_pwm_state == 0))*(move_step == 0)*((move_direction==3)-(move_direction==2)));}
else {averaged_azimuth_compass = azimuth_compass;} // use azimuth compass for deviations >10 degree
azimuth_compass = averaged_azimuth_compass;
prevmil_[0] = millis(); // remember system time to calculate expected angular step size in next pass for averaging
if (first_pass == 0) prev_azimuth_compass = azimuth_compass; // workaround for wrong Azimuth readings after upload of code
if(print_compass) {Serial.print(F("\tAzC2=")); Serial.print(azimuth_compass);}
first_pass = 0;
}
// CALCULATE THE COMPASS AZIMUTH ANGLE
float compass_calculation_2(){
// correct my and mx_horizontal values with calibration results
my -= ERI(2*elevation1_position + 350 + 2)*elevation1_position_float + ERI(2*elevation1_position + 350 )*(1.0-elevation1_position_float); // subtract interpolated neutral position
my /= ERI(2*elevation1_position + 400 + 2)*elevation1_position_float + ERI(2*elevation1_position + 400 )*(1.0-elevation1_position_float); // divide by interpolated range
mx_horizontal -= ERI(2*elevation1_position + 250 + 2)*elevation1_position_float + ERI(2*elevation1_position + 250 )*(1.0-elevation1_position_float); // subtract interpolated neutral position
mx_horizontal /= ERI(2*elevation1_position + 300 + 2)*elevation1_position_float + ERI(2*elevation1_position + 300 )*(1.0-elevation1_position_float); // divide by interpolated range
float azimuth_compass_2 = atan(my/mx_horizontal); // calculate the Azimuth compass reading
if (mx_horizontal<0) {azimuth_compass_2 +=PI;} // make the reading unambiguous, otherwise chaning signs on both components would yield the same result. -0.5Pi..´1.5Pi range
azimuth_compass_2 *= 1800 / PI; // calculates in 0.1 degree steps to be compatible with integer
return azimuth_compass_2;
}
// CORRECT THE COMPASS RANGE TO 0..3600 degree
void correct_azimuth_compass_range_to_0_3600() {
while ((azimuth_compass > 3600)|(azimuth_compass < 0)){
if (azimuth_compass > 3600){azimuth_compass -= 3600;}
if (azimuth_compass < 0){azimuth_compass += 3600;}
}
}
// PROJECT THE MAGNETIC VECTOR ONTO A HORIZONTAL PLANE (my is allready horizontal)
void calc_mx_horizontal(){
elevation_compass *= PI/1800; // we need elevation_compass in the correct format
mx_horizontal=(mx*sin(elevation_compass) - mz*cos(elevation_compass)); // This is a simple projection making use of the elevation angle
elevation_compass *= 1800/PI;
}
// RECEIVE TIME FROM RTC AND CALCULATE FRACTIONAL DAY AND FRACTIONAL YEAR
// The first 3 bytes seconds, minutes, hours are used to calculate a fractional day in the range [0..1]
// The third byte is the day of the week and discarded
// The following 3 bytes are used to estimate the number that have passed in the present year and calculate a fractional year [0..1]
void RTC_receive(){