AP_Declination.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. * Adam M Rivera
  15. * With direction from: Andrew Tridgell, Jason Short, Justin Beech
  16. *
  17. * Adapted from: http://www.societyofrobots.com/robotforum/index.php?topic=11855.0
  18. * Scott Ferguson
  19. * scottfromscott@gmail.com
  20. *
  21. */
  22. #include "AP_Declination.h"
  23. #include <cmath>
  24. #include <AP_Common/AP_Common.h>
  25. #include <AP_Math/AP_Math.h>
  26. /*
  27. calculate magnetic field intensity and orientation
  28. */
  29. bool AP_Declination::get_mag_field_ef(float latitude_deg, float longitude_deg, float &intensity_gauss, float &declination_deg, float &inclination_deg)
  30. {
  31. bool valid_input_data = true;
  32. /* round down to nearest sampling resolution */
  33. int32_t min_lat = static_cast<int32_t>(static_cast<int32_t>(latitude_deg / SAMPLING_RES) * SAMPLING_RES);
  34. int32_t min_lon = static_cast<int32_t>(static_cast<int32_t>(longitude_deg / SAMPLING_RES) * SAMPLING_RES);
  35. /* for the rare case of hitting the bounds exactly
  36. * the rounding logic wouldn't fit, so enforce it.
  37. */
  38. /* limit to table bounds - required for maxima even when table spans full globe range */
  39. if (latitude_deg <= SAMPLING_MIN_LAT) {
  40. min_lat = static_cast<int32_t>(SAMPLING_MIN_LAT);
  41. valid_input_data = false;
  42. }
  43. if (latitude_deg >= SAMPLING_MAX_LAT) {
  44. min_lat = static_cast<int32_t>(static_cast<int32_t>(latitude_deg / SAMPLING_RES) * SAMPLING_RES - SAMPLING_RES);
  45. valid_input_data = false;
  46. }
  47. if (longitude_deg <= SAMPLING_MIN_LON) {
  48. min_lon = static_cast<int32_t>(SAMPLING_MIN_LON);
  49. valid_input_data = false;
  50. }
  51. if (longitude_deg >= SAMPLING_MAX_LON) {
  52. min_lon = static_cast<int32_t>(static_cast<int32_t>(longitude_deg / SAMPLING_RES) * SAMPLING_RES - SAMPLING_RES);
  53. valid_input_data = false;
  54. }
  55. /* find index of nearest low sampling point */
  56. uint32_t min_lat_index = static_cast<uint32_t>((-(SAMPLING_MIN_LAT) + min_lat) / SAMPLING_RES);
  57. uint32_t min_lon_index = static_cast<uint32_t>((-(SAMPLING_MIN_LON) + min_lon) / SAMPLING_RES);
  58. /* calculate intensity */
  59. float data_sw = intensity_table[min_lat_index][min_lon_index];
  60. float data_se = intensity_table[min_lat_index][min_lon_index + 1];;
  61. float data_ne = intensity_table[min_lat_index + 1][min_lon_index + 1];
  62. float data_nw = intensity_table[min_lat_index + 1][min_lon_index];
  63. /* perform bilinear interpolation on the four grid corners */
  64. float data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
  65. float data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
  66. intensity_gauss = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
  67. /* calculate declination */
  68. data_sw = declination_table[min_lat_index][min_lon_index];
  69. data_se = declination_table[min_lat_index][min_lon_index + 1];;
  70. data_ne = declination_table[min_lat_index + 1][min_lon_index + 1];
  71. data_nw = declination_table[min_lat_index + 1][min_lon_index];
  72. /* perform bilinear interpolation on the four grid corners */
  73. data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
  74. data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
  75. declination_deg = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
  76. /* calculate inclination */
  77. data_sw = inclination_table[min_lat_index][min_lon_index];
  78. data_se = inclination_table[min_lat_index][min_lon_index + 1];;
  79. data_ne = inclination_table[min_lat_index + 1][min_lon_index + 1];
  80. data_nw = inclination_table[min_lat_index + 1][min_lon_index];
  81. /* perform bilinear interpolation on the four grid corners */
  82. data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
  83. data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
  84. inclination_deg = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
  85. return valid_input_data;
  86. }
  87. /*
  88. calculate magnetic field intensity and orientation
  89. */
  90. float AP_Declination::get_declination(float latitude_deg, float longitude_deg)
  91. {
  92. float declination_deg=0, inclination_deg=0, intensity_gauss=0;
  93. get_mag_field_ef(latitude_deg, longitude_deg, intensity_gauss, declination_deg, inclination_deg);
  94. return declination_deg;
  95. }
  96. /*
  97. get earth field as a Vector3f in Gauss given a Location
  98. */
  99. Vector3f AP_Declination::get_earth_field_ga(const Location &loc)
  100. {
  101. float declination_deg=0, inclination_deg=0, intensity_gauss=0;
  102. get_mag_field_ef(loc.lat*1.0e-7f, loc.lng*1.0e-7f, intensity_gauss, declination_deg, inclination_deg);
  103. // create earth field
  104. Vector3f mag_ef = Vector3f(intensity_gauss, 0.0, 0.0);
  105. Matrix3f R;
  106. R.from_euler(0.0f, -ToRad(inclination_deg), ToRad(declination_deg));
  107. mag_ef = R * mag_ef;
  108. return mag_ef;
  109. }