2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
26 #include "build/build_config.h"
27 #include "build/debug.h"
29 #include "common/axis.h"
30 #include "common/filter.h"
31 #include "common/maths.h"
33 #include "sensors/sensors.h"
34 #include "sensors/acceleration.h"
35 #include "sensors/boardalignment.h"
37 #include "flight/pid.h"
38 #include "flight/imu.h"
40 #include "fc/config.h"
41 #include "fc/runtime_config.h"
43 #include "navigation/navigation.h"
44 #include "navigation/navigation_private.h"
46 #include "navigation/navigation_declination_gen.c"
48 static float get_lookup_table_val(unsigned lat_index
, unsigned lon_index
)
50 return declination_table
[lat_index
][lon_index
];
53 float geoCalculateMagDeclination(const gpsLocation_t
* llh
) // degrees units
56 * If the values exceed valid ranges, return zero as default
57 * as we have no way of knowing what the closest real value
60 const float lat
= llh
->lat
/ 10000000.0f
;
61 const float lon
= llh
->lon
/ 10000000.0f
;
63 if (lat
< -90.0f
|| lat
> 90.0f
||
64 lon
< -180.0f
|| lon
> 180.0f
) {
68 /* round down to nearest sampling resolution */
69 int min_lat
= (int)(lat
/ SAMPLING_RES
) * SAMPLING_RES
;
70 int min_lon
= (int)(lon
/ SAMPLING_RES
) * SAMPLING_RES
;
72 /* for the rare case of hitting the bounds exactly
73 * the rounding logic wouldn't fit, so enforce it.
76 /* limit to table bounds - required for maxima even when table spans full globe range */
77 if (lat
<= SAMPLING_MIN_LAT
) {
78 min_lat
= SAMPLING_MIN_LAT
;
81 if (lat
>= SAMPLING_MAX_LAT
) {
82 min_lat
= (int)(lat
/ SAMPLING_RES
) * SAMPLING_RES
- SAMPLING_RES
;
85 if (lon
<= SAMPLING_MIN_LON
) {
86 min_lon
= SAMPLING_MIN_LON
;
89 if (lon
>= SAMPLING_MAX_LON
) {
90 min_lon
= (int)(lon
/ SAMPLING_RES
) * SAMPLING_RES
- SAMPLING_RES
;
93 /* find index of nearest low sampling point */
94 const unsigned min_lat_index
= (-(SAMPLING_MIN_LAT
) + min_lat
) / SAMPLING_RES
;
95 const unsigned min_lon_index
= (-(SAMPLING_MIN_LON
) + min_lon
) / SAMPLING_RES
;
97 const float declination_sw
= get_lookup_table_val(min_lat_index
, min_lon_index
);
98 const float declination_se
= get_lookup_table_val(min_lat_index
, min_lon_index
+ 1);
99 const float declination_ne
= get_lookup_table_val(min_lat_index
+ 1, min_lon_index
+ 1);
100 const float declination_nw
= get_lookup_table_val(min_lat_index
+ 1, min_lon_index
);
102 /* perform bilinear interpolation on the four grid corners */
104 const float declination_min
= ((lon
- min_lon
) / SAMPLING_RES
) * (declination_se
- declination_sw
) + declination_sw
;
105 const float declination_max
= ((lon
- min_lon
) / SAMPLING_RES
) * (declination_ne
- declination_nw
) + declination_nw
;
107 return ((lat
- min_lat
) / SAMPLING_RES
) * (declination_max
- declination_min
) + declination_min
;
110 void geoSetOrigin(gpsOrigin_t
*origin
, const gpsLocation_t
*llh
, geoOriginResetMode_e resetMode
)
112 if (resetMode
== GEO_ORIGIN_SET
) {
113 origin
->valid
= true;
114 origin
->lat
= llh
->lat
;
115 origin
->lon
= llh
->lon
;
116 origin
->alt
= llh
->alt
;
117 origin
->scale
= constrainf(cos_approx((ABS(origin
->lat
) / 10000000.0f
) * 0.0174532925f
), 0.01f
, 1.0f
);
119 else if (origin
->valid
&& (resetMode
== GEO_ORIGIN_RESET_ALTITUDE
)) {
120 origin
->alt
= llh
->alt
;
124 bool geoConvertGeodeticToLocal(fpVector3_t
*pos
, const gpsOrigin_t
*origin
, const gpsLocation_t
*llh
, geoAltitudeConversionMode_e altConv
)
127 pos
->x
= (llh
->lat
- origin
->lat
) * DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR
;
128 pos
->y
= (llh
->lon
- origin
->lon
) * (DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR
* origin
->scale
);
130 // If flag GEO_ALT_RELATIVE, than llh altitude is already relative to origin
131 if (altConv
== GEO_ALT_RELATIVE
) {
134 pos
->z
= llh
->alt
- origin
->alt
;
145 bool geoConvertGeodeticToLocalOrigin(fpVector3_t
* pos
, const gpsLocation_t
*llh
, geoAltitudeConversionMode_e altConv
)
147 return geoConvertGeodeticToLocal(pos
, &posControl
.gpsOrigin
, llh
, altConv
);
150 bool geoConvertLocalToGeodetic(gpsLocation_t
*llh
, const gpsOrigin_t
* origin
, const fpVector3_t
*pos
)
155 llh
->lat
= origin
->lat
;
156 llh
->lon
= origin
->lon
;
157 llh
->alt
= origin
->alt
;
158 scaleLonDown
= origin
->scale
;
167 llh
->lat
+= lrintf(pos
->x
/ DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR
);
168 llh
->lon
+= lrintf(pos
->y
/ (DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR
* scaleLonDown
));
169 llh
->alt
+= lrintf(pos
->z
);
170 return origin
->valid
;