CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_Declination/AP_Declination.cpp
Views: 1798
1
/*
2
This program is free software: you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation, either version 3 of the License, or
5
(at your option) any later version.
6
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
11
12
You should have received a copy of the GNU General Public License
13
along with this program. If not, see <http://www.gnu.org/licenses/>.
14
*/
15
16
/*
17
* Adam M Rivera
18
* With direction from: Andrew Tridgell, Jason Short, Justin Beech
19
*
20
* Adapted from: http://www.societyofrobots.com/robotforum/index.php?topic=11855.0
21
* Scott Ferguson
22
* [email protected]
23
*
24
*/
25
#include "AP_Declination.h"
26
27
#include <cmath>
28
29
#include <AP_Common/AP_Common.h>
30
#include <AP_Math/AP_Math.h>
31
32
/*
33
calculate magnetic field intensity and orientation
34
*/
35
bool AP_Declination::get_mag_field_ef(float latitude_deg, float longitude_deg, float &intensity_gauss, float &declination_deg, float &inclination_deg)
36
{
37
bool valid_input_data = true;
38
39
/* round down to nearest sampling resolution. On some platforms (e.g. clang on macOS),
40
the behaviour of implicit casts from int32 to float can be undefined thus making it explicit here. */
41
float min_lat = float(static_cast<int32_t>(static_cast<int32_t>(floorf(latitude_deg / SAMPLING_RES)) * SAMPLING_RES));
42
float min_lon = float(static_cast<int32_t>(static_cast<int32_t>(floorf(longitude_deg / SAMPLING_RES)) * SAMPLING_RES));
43
44
/* for the rare case of hitting the bounds exactly
45
* the rounding logic wouldn't fit, so enforce it.
46
*/
47
48
/* limit to table bounds - required for maxima even when table spans full globe range */
49
if (latitude_deg <= SAMPLING_MIN_LAT) {
50
min_lat = float(static_cast<int32_t>(SAMPLING_MIN_LAT));
51
valid_input_data = false;
52
}
53
54
if (latitude_deg >= SAMPLING_MAX_LAT) {
55
min_lat = float(static_cast<int32_t>(static_cast<int32_t>(latitude_deg / SAMPLING_RES) * SAMPLING_RES - SAMPLING_RES));
56
valid_input_data = false;
57
}
58
59
if (longitude_deg <= SAMPLING_MIN_LON) {
60
min_lon = float(static_cast<int32_t>(SAMPLING_MIN_LON));
61
valid_input_data = false;
62
}
63
64
if (longitude_deg >= SAMPLING_MAX_LON) {
65
min_lon = float(static_cast<int32_t>(static_cast<int32_t>(longitude_deg / SAMPLING_RES) * SAMPLING_RES - SAMPLING_RES));
66
valid_input_data = false;
67
}
68
69
/* find index of nearest low sampling point */
70
uint32_t min_lat_index = constrain_int32(static_cast<uint32_t>((-(SAMPLING_MIN_LAT) + min_lat) / SAMPLING_RES), 0, LAT_TABLE_SIZE - 2);
71
uint32_t min_lon_index = constrain_int32(static_cast<uint32_t>((-(SAMPLING_MIN_LON) + min_lon) / SAMPLING_RES), 0, LON_TABLE_SIZE -2);
72
73
/* calculate intensity */
74
75
float data_sw = intensity_table[min_lat_index][min_lon_index];
76
float data_se = intensity_table[min_lat_index][min_lon_index + 1];
77
float data_ne = intensity_table[min_lat_index + 1][min_lon_index + 1];
78
float data_nw = intensity_table[min_lat_index + 1][min_lon_index];
79
80
/* perform bilinear interpolation on the four grid corners */
81
82
float data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
83
float data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
84
85
intensity_gauss = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
86
87
/* calculate declination */
88
89
data_sw = declination_table[min_lat_index][min_lon_index];
90
data_se = declination_table[min_lat_index][min_lon_index + 1];
91
data_ne = declination_table[min_lat_index + 1][min_lon_index + 1];
92
data_nw = declination_table[min_lat_index + 1][min_lon_index];
93
94
/* perform bilinear interpolation on the four grid corners */
95
96
data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
97
data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
98
99
declination_deg = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
100
101
/* calculate inclination */
102
103
data_sw = inclination_table[min_lat_index][min_lon_index];
104
data_se = inclination_table[min_lat_index][min_lon_index + 1];
105
data_ne = inclination_table[min_lat_index + 1][min_lon_index + 1];
106
data_nw = inclination_table[min_lat_index + 1][min_lon_index];
107
108
/* perform bilinear interpolation on the four grid corners */
109
110
data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
111
data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
112
113
inclination_deg = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
114
115
return valid_input_data;
116
}
117
118
119
/*
120
calculate magnetic field intensity and orientation
121
*/
122
float AP_Declination::get_declination(float latitude_deg, float longitude_deg)
123
{
124
float declination_deg=0, inclination_deg=0, intensity_gauss=0;
125
126
get_mag_field_ef(latitude_deg, longitude_deg, intensity_gauss, declination_deg, inclination_deg);
127
128
return declination_deg;
129
}
130
131
/*
132
get earth field as a Vector3f in Gauss given a Location
133
*/
134
Vector3f AP_Declination::get_earth_field_ga(const Location &loc)
135
{
136
float declination_deg=0, inclination_deg=0, intensity_gauss=0;
137
get_mag_field_ef(loc.lat*1.0e-7f, loc.lng*1.0e-7f, intensity_gauss, declination_deg, inclination_deg);
138
139
// create earth field
140
Vector3f mag_ef = Vector3f(intensity_gauss, 0.0, 0.0);
141
Matrix3f R;
142
143
R.from_euler(0.0f, -ToRad(inclination_deg), ToRad(declination_deg));
144
mag_ef = R * mag_ef;
145
return mag_ef;
146
}
147
148