You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
9.3 KiB

2 years ago
  1. #include <stdint.h>
  2. #include <stdbool.h>
  3. #include <string.h>
  4. #include <math.h>
  5. #include "wmm.h"
  6. #define PI_CONST 3.14159265359f
  7. #define RADIANS_TO_DEGREES 0.017453292f
  8. #define DEGREES_TO_RADIANS (PI_CONST / 180.0f)
  9. #define A_CONST 6378.137f
  10. #define A2_CONST (A_CONST * A_CONST)
  11. #define B_CONST 6356.7523142f
  12. #define B2_CONST (B_CONST * B_CONST)
  13. #define RE_CONST 6371.2f
  14. #define A4_CONST (A2_CONST * A2_CONST)
  15. #define B4_CONST (B2_CONST * B2_CONST)
  16. #define C2_CONST (A2_CONST - B2_CONST)
  17. #define C4_CONST (A4_CONST - B4_CONST)
  18. #define COEFFICIENTS_COUNT 90U
  19. static float c[13][13];
  20. static float cd[13][13];
  21. static float k[13][13];
  22. static float snorm[169];
  23. static float fn[13];
  24. static float fm[13];
  25. const uint8_t wmm_cof_entries_encoded[] =
  26. {0xDD, 0xF2, 0x23, 0x00, 0x83, 0x01, 0x00, 0xEB, 0xE2, 0x01, 0x81, 0xD7, 0x05, 0x8D, 0x01, 0xFB, 0x03, 0xE8, 0x86, 0x03, 0x00, 0xF3, 0x01, 0x00, 0xBC, 0xD1, 0x03, 0xDC, 0xD3, 0x03, 0xC7, 0x01, 0xEE, 0x04, 0x80, 0x86, 0x02, 0xF4, 0x72, 0x56, 0xEF, 0x03, 0x87, 0xD5, 0x01, 0x00, 0x1C, 0x00, 0xC2, 0xF4, 0x02, 0xF6, 0x0C, 0x7E, 0x39, 0x8A, 0xC1, 0x01, 0xB2, 0x25, 0x22, 0x4A, 0x89, 0x52, 0xF5, 0x54, 0xFA, 0x01, 0x0B, 0x87, 0x8D, 0x01, 0x00, 0x4B, 0x00, 0x9E, 0x7E, 0x84, 0x2C, 0x50, 0x02, 0x9E, 0x0D, 0xF0, 0x18, 0x7C, 0x85, 0x01, 0xD6, 0x30, 0x8E, 0x1F, 0x36, 0x25, 0x9F, 0x07, 0xED, 0x36, 0x77, 0x78, 0xE8, 0x24, 0x00, 0x43, 0x00, 0xAF, 0x38, 0x9D, 0x07, 0x06, 0x01, 0x96, 0x1D, 0xA4, 0x20, 0x47, 0x19, 0xFF, 0x15, 0xFD, 0x12, 0x01, 0x49, 0xE8, 0x17, 0x82, 0x05, 0x0C, 0x1E, 0x89, 0x02, 0x9F, 0x0F, 0x0A, 0x05, 0x93, 0x0A, 0x00, 0x46, 0x00, 0x90, 0x0A, 0xFF, 0x02, 0x44, 0x01, 0x9A, 0x0B, 0xBA, 0x03, 0x05, 0x52, 0xFF, 0x12, 0x8F, 0x08, 0x0E, 0x4E, 0xEA, 0x05, 0xC4, 0x0A, 0x4E, 0x09, 0x87, 0x02, 0x9A, 0x01, 0x00, 0x01, 0xC7, 0x0A, 0xA9, 0x0A, 0x08, 0x0A, 0xA6, 0x0C, 0x00, 0x41, 0x00, 0xC0, 0x0C, 0xC2, 0x08, 0x43, 0x05, 0xD3, 0x01, 0xE8, 0x02, 0x41, 0x06, 0xB5, 0x08, 0x17, 0x07, 0x47, 0x9E, 0x02, 0xAB, 0x03, 0x02, 0x42, 0x80, 0x01, 0x56, 0x45, 0x4C, 0xC8, 0x01, 0xD0, 0x04, 0x48, 0x02, 0xA2, 0x01, 0x53, 0x0A, 0x03, 0xAC, 0x03, 0x00, 0x41, 0x00, 0xA2, 0x01, 0x94, 0x01, 0x01, 0x43, 0xEF, 0x02, 0xD9, 0x02, 0x41, 0x07, 0x44, 0x80, 0x02, 0x05, 0x42, 0xD3, 0x03, 0xF6, 0x01, 0x41, 0x05, 0x99, 0x02, 0x95, 0x02, 0x04, 0x43, 0x89, 0x02, 0x24, 0x05, 0x45, 0xE5, 0x02, 0xC5, 0x01, 0x00, 0x04, 0x43, 0x1C, 0x04, 0x01, 0x32, 0x00, 0x41, 0x00, 0x92, 0x01, 0xE9, 0x03, 0x42, 0x43, 0x1D, 0xAF, 0x01, 0x00, 0x02, 0x4E, 0xA2, 0x01, 0x04, 0x44, 0x4B, 0x73, 0x43, 0x04, 0xC5, 0x02, 0x7E, 0x00, 0x01, 0x0B, 0x8E, 0x01, 0x03, 0x00, 0x99, 0x01, 0x04, 0x00, 0x42, 0xDD, 0x01, 0x4F, 0x00, 0x05, 0xF7, 0x01, 0xA1, 0x01, 0x44, 0x02, 0x53, 0x00, 0x00, 0x00, 0x7E, 0x22, 0x00, 0x00, 0x41, 0x42, 0x00, 0x01, 0x11, 0x23, 0x02, 0x43, 0x49, 0x30, 0x41, 0x01, 0x06, 0xD6, 0x01, 0x42, 0x42, 0x49, 0x41, 0x00, 0x01, 0x13, 0x6A, 0x41, 0x00, 0x0E, 0x62, 0x42, 0x41, 0x58, 0x41, 0x41, 0x02, 0x67, 0xD8, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x41, 0x00, 0x59, 0x1A, 0x00, 0x01, 0x18, 0x45, 0x00, 0x00, 0x49, 0x44, 0x00, 0x02, 0x03, 0x06, 0x41, 0x00, 0x47, 0x42, 0x00, 0x00, 0x41, 0x51, 0x00, 0x01, 0x0E, 0x50, 0x41, 0x00, 0x46, 0x5E, 0x41, 0x41, 0x02, 0x54, 0x41, 0x00, 0x1F, 0x5A, 0x41, 0x00, 0x54, 0x00, 0x00, 0x00, 0x41, 0x4C, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x41, 0x4C, 0x52, 0x00, 0x01, 0x07, 0x01, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x05, 0x41, 0x00, 0x00, 0x42, 0x06, 0x00, 0x01, 0x45, 0x02, 0x00, 0x00, 0x01, 0x49, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x43, 0x05, 0x41, 0x41};
  27. static wmm_cof_record_t wmm_cof_entries[COEFFICIENTS_COUNT];
  28. static float convert_varint_to_float(char **bytes);
  29. float wmm_get_date(uint8_t year, uint8_t month, uint8_t date)
  30. {
  31. return (float)year + 2000.0f + (float)(month - 1U) / 12.0f + (float)(date - 1U) / (365.0f);
  32. }
  33. static float convert_varint_to_float(char **bytes)
  34. {
  35. float result;
  36. int32_t result_int;
  37. bool negative = false;
  38. bool first_byte = true;
  39. uint8_t shift;
  40. do
  41. {
  42. if (first_byte)
  43. {
  44. if (**bytes & 0x40)
  45. {
  46. negative = true;
  47. }
  48. result_int = **bytes & 0x3f;
  49. shift = 6U;
  50. first_byte = false;
  51. }
  52. else
  53. {
  54. result_int += (uint32_t)(**bytes & 0x7f) << shift;
  55. shift += 7U;
  56. }
  57. if ((**bytes & 0x80) == 0U)
  58. {
  59. (*bytes)++;
  60. break;
  61. }
  62. (*bytes)++;
  63. } while (true);
  64. result = ((float)result_int) / 10.0f;
  65. if (negative)
  66. {
  67. result = -result;
  68. }
  69. return result;
  70. }
  71. void wmm_init(void)
  72. {
  73. uint8_t j;
  74. uint8_t m;
  75. uint8_t n;
  76. uint8_t D2;
  77. float gnm;
  78. float hnm;
  79. float dgnm;
  80. float dhnm;
  81. float flnmj;
  82. uint8_t i;
  83. char *bytes = (char *)&wmm_cof_entries_encoded[0];
  84. // unpack coefficients
  85. for (i = 0U; i < COEFFICIENTS_COUNT; i++)
  86. {
  87. wmm_cof_entries[i].gnm = convert_varint_to_float(&bytes);
  88. wmm_cof_entries[i].hnm = convert_varint_to_float(&bytes);
  89. wmm_cof_entries[i].dgnm = convert_varint_to_float(&bytes);
  90. wmm_cof_entries[i].dhnm = convert_varint_to_float(&bytes);
  91. }
  92. c[0][0] = 0.0f;
  93. cd[0][0] = 0.0f;
  94. j = 0U;
  95. for (n = 1U; n <= 12U; n++)
  96. {
  97. for (m = 0U; m <= n; m++)
  98. {
  99. gnm = wmm_cof_entries[j].gnm;
  100. hnm = wmm_cof_entries[j].hnm;
  101. dgnm = wmm_cof_entries[j].dgnm;
  102. dhnm = wmm_cof_entries[j].dhnm;
  103. j++;
  104. if (m <= n)
  105. {
  106. c[m][n] = gnm;
  107. cd[m][n] = dgnm;
  108. if (m != 0U)
  109. {
  110. c[n][m - 1U] = hnm;
  111. cd[n][m - 1U] = dhnm;
  112. }
  113. }
  114. }
  115. }
  116. // CONVERT SCHMIDT NORMALIZED GAUSS COEFFICIENTS TO UNNORMALIZED
  117. *snorm = 1.0f;
  118. for (n = 1U; n <= 12U; n++)
  119. {
  120. *(snorm + n) = *(snorm + n - 1U) * (float)(2U * n - 1U) / (float)n;
  121. j = 2U;
  122. m = 0U;
  123. for (D2 = n - m + 1U; D2 > 0U; D2--)
  124. {
  125. k[m][n] = (float)(((n - 1U) * (n - 1U)) - (m * m)) / (float)((2U * n - 1U) * (2U * n - 3U));
  126. if (m > 0U)
  127. {
  128. flnmj = (float)((n - m + 1U) * j) / (float)(n + m);
  129. *(snorm + n + m * 13U) = *(snorm + n + (m - 1U) * 13U) * sqrt(flnmj);
  130. j = 1U;
  131. c[n][m - 1U] = *(snorm + n + m * 13U) * c[n][m - 1U];
  132. cd[n][m - 1U] = *(snorm + n + m * 13U) * cd[n][m - 1U];
  133. }
  134. c[m][n] = *(snorm + n + m * 13U) * c[m][n];
  135. cd[m][n] = *(snorm + n + m *13U) * cd[m][n];
  136. m += 1U;
  137. }
  138. fn[n] = (float)(n + 1U);
  139. fm[n] = (float)n;
  140. }
  141. k[1][1] = 0.0f;
  142. }
  143. void E0000(float glat, float glon, float time_years, float *dec)
  144. {
  145. static float tc[13][13];
  146. static float sp[13];
  147. static float cp[13];
  148. static float dp[13][13];
  149. static float pp[13];
  150. float dt = time_years - WMM_EPOCH;
  151. float rlon = glon * DEGREES_TO_RADIANS;
  152. float rlat = glat * DEGREES_TO_RADIANS;
  153. float srlon = sinf(rlon);
  154. float srlat = sinf(rlat);
  155. float crlon = cosf(rlon);
  156. float crlat = cosf(rlat);
  157. float srlat2 = srlat * srlat;
  158. float crlat2 = crlat * crlat;
  159. sp[0] = 0.0f;
  160. sp[1] = srlon;
  161. cp[0] = 1.0f;
  162. cp[1] = crlon;
  163. dp[0][0] = 0.0f;
  164. pp[0] = 1.0f;
  165. // CONVERT FROM GEODETIC COORDS. TO SPHERICAL COORDS
  166. float q = sqrtf(A2_CONST - C2_CONST * srlat2);
  167. float q2 = (A2_CONST / (B2_CONST)) * (A2_CONST / B2_CONST);
  168. float ct = srlat / sqrtf(q2 * crlat2 + srlat2);
  169. float st = sqrtf(1.0f - (ct * ct));
  170. float r2 = (A4_CONST - C4_CONST * srlat2) / (q * q);
  171. float r = sqrtf(r2);
  172. float d = sqrtf(A2_CONST * crlat2 + B2_CONST * srlat2);
  173. float ca = d / r;
  174. float sa = C2_CONST * crlat * srlat / (r * d);
  175. for (uint8_t m = 2U; m <= 12U; m++)
  176. {
  177. sp[m] = sp[1] * cp[m - 1U] + cp[1] * sp[m - 1U];
  178. cp[m] = cp[1] * cp[m - 1U] - sp[1] * sp[m - 1U];
  179. }
  180. float aor = RE_CONST / r;
  181. float ar = aor * aor;
  182. float br = 0.0f;
  183. float bt = 0.0f;
  184. float bp = 0.0f;
  185. float bpp = 0.0f;
  186. for (uint16_t n = 1U; n <= 12U; n++)
  187. {
  188. ar = ar * aor;
  189. uint8_t m = 0U;
  190. for (uint8_t D4 = n + 1U; D4 > 0U; D4--)
  191. {
  192. // COMPUTE UNNORMALIZED ASSOCIATED LEGENDRE POLYNOMIALS AND DERIVATIVES VIA RECURSION RELATIONS
  193. if (n == m)
  194. {
  195. *(snorm + n + m * 13U) = st * *(snorm + n - 1U + (m - 1U) * 13U);
  196. dp[m][n] = st * dp[m - 1U][n - 1U] + ct * *(snorm + n - 1U + (m - 1U) * 13U);
  197. goto S50;
  198. }
  199. if (n == 1U && m == 0U)
  200. {
  201. *(snorm + n + m * 13U) = ct * *(snorm + n - 1U + m * 13U);
  202. dp[m][n] = ct * dp[m][n - 1U] - st * *(snorm + n - 1U + m * 13U);
  203. goto S50;
  204. }
  205. if (n > 1U && n != m)
  206. {
  207. if (m > n - 2U)
  208. {
  209. *(snorm + n - 2U + m * 13U) = 0.0f;
  210. }
  211. if (m > n - 2U)
  212. {
  213. dp[m][n - 2U] = 0.0f;
  214. }
  215. *(snorm + n + m * 13U) = ct * *(snorm + n - 1U + m * 13U) - k[m][n] * *(snorm + n - 2U + m * 13U);
  216. dp[m][n] = ct * dp[m][n - 1U] - st * *(snorm + n - 1U + m * 13U) - k[m][n] * dp[m][n - 2U];
  217. }
  218. S50:
  219. // TIME ADJUST THE GAUSS COEFFICIENTS
  220. tc[m][n] = c[m][n] + dt * cd[m][n];
  221. if (m != 0U)
  222. {
  223. tc[n][m - 1U] = c[n][m - 1U] + dt * cd[n][m - 1U];
  224. }
  225. // ACCUMULATE TERMS OF THE SPHERICAL HARMONIC EXPANSIONS
  226. float par = ar * *(snorm + n + m * 13U);
  227. float temp1;
  228. float temp2;
  229. if (m == 0)
  230. {
  231. temp1 = tc[m][n] * cp[m];
  232. temp2 = tc[m][n] * sp[m];
  233. }
  234. else
  235. {
  236. temp1 = tc[m][n] * cp[m] + tc[n][m - 1U] * sp[m];
  237. temp2 = tc[m][n] * sp[m] - tc[n][m - 1U] * cp[m];
  238. }
  239. bt = bt - ar * temp1 * dp[m][n];
  240. bp += (fm[m] * temp2 * par);
  241. br += (fn[n] * temp1 * par);
  242. // SPECIAL CASE: NORTH/SOUTH GEOGRAPHIC POLES
  243. if (st == 0.0f && m == 1U)
  244. {
  245. if (n == 1U)
  246. {
  247. pp[n] = pp[n - 1U];
  248. }
  249. else
  250. {
  251. pp[n] = ct * pp[n - 1U] - k[m][n] * pp[n - 2U];
  252. }
  253. bpp += (fm[m] * temp2 * ar * pp[n]);
  254. }
  255. m += 1U;
  256. }
  257. }
  258. if (st == 0.0f)
  259. {
  260. bp = bpp;
  261. }
  262. else
  263. {
  264. bp /= st;
  265. }
  266. // ROTATE MAGNETIC VECTOR COMPONENTS FROM SPHERICAL TO GEODETIC COORDINATES
  267. float bx = -bt * ca - br * sa;
  268. float by = bp;
  269. // COMPUTE DECLINATION
  270. *dec = atan2f(by, bx) / DEGREES_TO_RADIANS;
  271. }