satpredict.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /* This program is derived from the publicly licensed
  2. * PREDICT: A satellite tracking/orbital prediction program
  3. * Copyright John A. Magliacane, KD2BD 1991-2000
  4. *
  5. * [John's original licensing terms]
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation; either version 2 of the License or any later
  9. * version.
  10. *
  11. * This program is distributed in the hope that it will useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * for more details.
  15. */
  16. /* SATCLOCK: A satellite tracking/orbital prediction program
  17. * intended for Amateur Radio Satellites. Originally predict1
  18. * that was hacked together in 2000, and again in 2018 by
  19. * Chris Baird VK2FJDA/VK2CJB <cjb@brushtail.apana.org.au>.
  20. * ...and again in 2020 for use on Vailsa RS41 hardware.
  21. */
  22. /* Oh, STM32F1 doesn't support double...
  23. * Thankfully Andrew Thall's "float2" work saved the day!
  24. * http://andrewthall.org/papers/df64_qf128.pdf
  25. */
  26. #include <math.h>
  27. #include "config.h"
  28. #include "ublox.h" /* struct GPSEntry */
  29. #include "satpredict.h"
  30. #include "satdata.h"
  31. extern GPSEntry gpsData;
  32. extern volatile char flaga;
  33. struct SatStatus satstatus[MAXSATS];
  34. /* ---------------------------------------------------------------------- */
  35. void split (float2* r, float a)
  36. {
  37. const float SPLIT = ((1<<12)+1);
  38. float t = a * SPLIT;
  39. float a_hi = t - (t-a);
  40. float a_lo = a - a_hi;
  41. r->x = a_hi;
  42. r->y = a_lo;
  43. }
  44. void quickTwoSum (float2 *r, float a, float b)
  45. {
  46. /* iff |a| >= |b| */
  47. float s = a + b;
  48. float e = b - (s-a);
  49. r->x = s;
  50. r->y = e;
  51. }
  52. void twoSum (float2 *r, float a, float b)
  53. {
  54. float s = a + b;
  55. float v = s - a ;
  56. float e = (a-(s-v)) + (b-v);
  57. r->x = s;
  58. r->y = e;
  59. }
  60. void df64add (float2 *s, float2 *a, float2 *b)
  61. {
  62. float2 t;
  63. twoSum(s, a->x, b->x);
  64. twoSum(&t, a->y, b->y);
  65. s->y += t.x;
  66. quickTwoSum(s, s->x, s->y);
  67. s->y += t.y;
  68. quickTwoSum(s, s->x, s->y);
  69. }
  70. int df64lt (float2 *a, float2 *b)
  71. {
  72. return ((b->x - a->x) + (b->y - a->y) > 0.0) ? 1 : 0;
  73. /* return (a->x < b->x || ((a->x == b->x) && (a->y < b->y))); */
  74. }
  75. /* ---------------------------------------------------------------------- */
  76. struct {
  77. float stnlat;
  78. float stnlon; /* in opposite orientation? */
  79. int stnalt; /* in metres */
  80. } qth;
  81. #define FF (3.35289186924e-03)
  82. #define KM (1.609344) /* lol america */
  83. #define R0 (6378.135) /* radius of earth */
  84. #define TP (6.283185307179586)
  85. #define deg2rad (1.74532925199e-02)
  86. float2 daynum;
  87. float age, ak, apogee, azimuth, c0, c1, c2, c3, c7, c8, c9, c[3][2],
  88. df, e1, e2, e, elevation, g7, k2, l8, m1, m5, m, n0, o,
  89. perigee, q0, q, r3, r9, r, range, rk, s0, s1, s2, s3, s7, seight, s9,
  90. se, sma, t1, w, x0, x1, x5, x8, x9, x, y5, y8,
  91. y9, y, yone, yr, yzero, z1, z5, z8, z9, z;
  92. /* ---------------------------------------------------------------------- */
  93. /* reference: http://nghiaho.com/?p=997 .. claims 0.09 degree accuracy */
  94. float myatanf (float x)
  95. {
  96. #if 1 /* slow_and_uses_up_flash_but_accurate */
  97. return atanf(x) / deg2rad;
  98. #endif
  99. #if fast_but_slightly_dodgy
  100. #define M_PI_6 (0.5235987755982988)
  101. #define M_PI_12 (0.2617993877991494)
  102. float y;
  103. int complement = 0;
  104. int region = 0;
  105. int sign = 0;
  106. if (x < 0) { x = -x; sign = 1; }
  107. if (x > 1.0) { x = 1.0/x; complement = 1; }
  108. if (x > M_PI_12) { x = (x - M_PI_6) / (1 + M_PI_6 * x); region = 1; }
  109. #if 1 /*this_approximation_is_about_?2deg_off_in_the_final_results*/
  110. float ax = fabs(x);
  111. y = M_PI_4*x - x*(ax-1) * (0.2447 + 0.0663*ax);
  112. #endif
  113. #if this_is_about_2deg_off_as_well
  114. float x2 = x*x;
  115. y = x*(1.6867629106 + x2*0.4378497304)/(1.6867633134 + x2);
  116. #endif
  117. if (region) y += M_PI_6;
  118. if (complement) y = M_PI_2 - y;
  119. if (sign) y = -y;
  120. return y / deg2rad;
  121. #endif
  122. }
  123. /* ---------------------------------------------------------------------- */
  124. long DayNum (int m, int d, int y)
  125. {
  126. /* calculate the day number from m/d/y */
  127. long dn;
  128. float mm, yy;
  129. if (m < 3)
  130. {
  131. y--;
  132. m += 12;
  133. }
  134. if (y <= 50) y += 100; /* Correct for Y2K... */
  135. yy = (float)y;
  136. mm = (float)m;
  137. dn = (long)(floorf(365.25 * (yy - 80.0)) - floorf(19.0 + yy / 100.0) +
  138. floorf(4.75 + yy / 400.0) - 16.0);
  139. dn += d + 30*m + (long)floorf(0.6 * mm - 0.3);
  140. return dn;
  141. }
  142. void CurrentDaynum (void)
  143. {
  144. float day, d;
  145. float2 temp1, temp2;
  146. day = DayNum (gpsData.month, gpsData.day, gpsData.year);
  147. d = (float)gpsData.seconds/(float)86400.0;
  148. d += (float)gpsData.minutes/(float)1440.0;
  149. d += (float)gpsData.hours/(float)24.0;
  150. split (&temp1, day);
  151. split (&temp2, d);
  152. df64add (&daynum, &temp1, &temp2);
  153. }
  154. int AosHappens (int satnum)
  155. {
  156. /* This function returns a 1 if the satellite can ever
  157. * rise above the horizon of the ground station */
  158. float lin, sma, apogee;
  159. lin = satdata[satnum].incl;
  160. if (lin >= 90.0) lin = 180.0 - lin;
  161. sma = 331.25 * powf(1440.0/satdata[satnum].meanmo, 2.0/3.0);
  162. apogee = sma * (1.0+satdata[satnum].eccn) - R0;
  163. if ((acosf(R0 / (apogee + R0)) + (lin*deg2rad))
  164. > fabs((qth.stnlat+HORIZON) * deg2rad))
  165. return 1;
  166. else
  167. return 0;
  168. }
  169. int Decayed (int satnum)
  170. {
  171. /* This function returns a 1 if it appears that the current satellite has
  172. * decayed at the time of 'daynum' */
  173. float2 nsatepoch;
  174. float2 r;
  175. float t;
  176. t = (16.666666 - satdata[satnum].meanmo) / (10.0 * fabs(satdata[satnum].drag));
  177. t += satdata[satnum].refepoch;
  178. twoSum(&nsatepoch, -DayNum(1, 0, satdata[satnum].year), -t);
  179. df64add(&r, &daynum, &nsatepoch);
  180. if (r.x > 0)
  181. return 1;
  182. else
  183. return 0;
  184. }
  185. int Geostationary (int satnum)
  186. {
  187. /* This function returns a 1 if the current satellite appears to be in a
  188. * geostationary orbit */
  189. if (fabs(satdata[satnum].meanmo - 1.0027) < 0.0002)
  190. return 1;
  191. else
  192. return 0;
  193. }
  194. void CalcSat (int satnum)
  195. {
  196. float xx, xxt;
  197. age = ((daynum.x - DayNum(1, 0, satdata[satnum].year)) - satdata[satnum].refepoch) + daynum.y;
  198. yr = (float)satdata[satnum].year;
  199. if (yr <= 50.0) yr += 100.0; /* Y2K... */
  200. t1 = yr - 1.0;
  201. df = 366.0 + floorf(365.25 * (t1 - 80.0)) -
  202. floorf(t1 / 100.0) + floorf(t1 / 400.0 + 0.75);
  203. t1 = (df + 29218.5) / 36525.0;
  204. t1 = 6.6460656 + t1*(2400.051262 + t1*2.581e-5);
  205. se = t1 / 24.0 - yr;
  206. n0 = satdata[satnum].meanmo + (age * satdata[satnum].drag);
  207. sma = 331.25 * powf((1440.0 / n0), (2.0 / 3.0));
  208. e2 = 1.0 - (satdata[satnum].eccn * satdata[satnum].eccn);
  209. e1 = sqrtf(e2);
  210. k2 = 9.95 * powf(R0 / sma, 3.5) / e2 * e2;
  211. s1 = sinf(satdata[satnum].incl * deg2rad);
  212. c1 = cosf(satdata[satnum].incl * deg2rad);
  213. l8 = qth.stnlat * deg2rad;
  214. s9 = sinf(l8);
  215. c9 = cosf(l8);
  216. seight = sinf(-qth.stnlon * deg2rad);
  217. c8 = cosf(qth.stnlon * deg2rad);
  218. r9 = R0 * (1.0 - (FF/2.0) * (cosf(2.0*l8) - 1.0)) - (qth.stnalt/1000.0);
  219. xx = (1.0-FF)*(1.0-FF) * s9 / c9;
  220. xxt = r9 / sqrtf(1 + xx*xx); /* sin(atan(x)) = x/((1+x^2)^-2) */
  221. z9 = xxt * xx;
  222. x9 = xxt * c8;
  223. y9 = xxt * seight;
  224. apogee = sma * (1.0 + satdata[satnum].eccn) - R0;
  225. perigee = sma * (1.0 - satdata[satnum].eccn) - R0;
  226. age = ((daynum.x - DayNum(1, 0, satdata[satnum].year)) - satdata[satnum].refepoch) + daynum.y;
  227. o = deg2rad * (satdata[satnum].raan - age * k2 * c1);
  228. s0 = sinf(o);
  229. c0 = cosf(o);
  230. w = deg2rad * (satdata[satnum].argper + age * k2 * (2.5 * c1 * c1 - 0.5));
  231. s2 = sinf(w);
  232. c2 = cosf(w);
  233. c[0][0] = (c2*c0) - (s2*s0*c1);
  234. c[1][0] = (c2*s0) + (s2*c0*c1);
  235. c[0][1] = (-s2*c0) - (c2*s0*c1);
  236. c[1][1] = (-s2*s0) + (c2*c0*c1);
  237. c[2][0] = s2*s1;
  238. c[2][1] = c2*s1;
  239. q0 = (satdata[satnum].meanan / 360.0) + satdata[satnum].orbitnum;
  240. q = n0 * age + q0;
  241. q = q - floorf(q);
  242. m = q * TP;
  243. e = m + satdata[satnum].eccn * (sinf(m) + 0.5*satdata[satnum].eccn * sinf(m*2.0));
  244. do /* Kepler's Equation */
  245. {
  246. s3 = sinf(e);
  247. c3 = cosf(e);
  248. r3 = 1.0 - (satdata[satnum].eccn * c3);
  249. m1 = e - (satdata[satnum].eccn * s3);
  250. m5 = m1 - m;
  251. e = e - m5 / r3;
  252. }
  253. while (fabs(m5) >= 1.0e-6);
  254. x0 = sma * (c3 - satdata[satnum].eccn);
  255. yzero = sma * e1 * s3;
  256. r = sma * r3;
  257. x1 = (x0 * c[0][0]) + (yzero * c[0][1]);
  258. yone = (x0 * c[1][0]) + (yzero * c[1][1]);
  259. z1 = (x0 * c[2][0]) + (yzero * c[2][1]);
  260. g7 = ((daynum.x - df) + daynum.y) * 1.0027379093 + se;
  261. g7 = TP * (g7 - floorf(g7));
  262. s7 = -sinf(g7);
  263. c7 = cosf(g7);
  264. x = (x1 * c7) - (yone * s7);
  265. y = (x1 * s7) + (yone * c7);
  266. z = z1;
  267. x5 = x - x9;
  268. y5 = y - y9;
  269. z5 = z - z9;
  270. range = (x5*x5) + (y5*y5) + (z5*z5);
  271. z8 = (x5*c8*c9) + (y5*seight*c9) + (z5*s9);
  272. x8 = (-x5*c8*s9) - (y5*seight*s9) + (z5*c9);
  273. y8 = (y5*c8) - (x5*seight);
  274. ak = r - R0;
  275. rk = sqrtf(range);
  276. elevation = myatanf(z8 / sqrtf(range - z8*z8));
  277. azimuth = myatanf(y8 / x8);
  278. if (x8 < 0.0) azimuth += 180.0;
  279. if (azimuth < 0.0) azimuth += 360.0;
  280. struct SatStatus *s = &satstatus[satnum];
  281. s->lastrange = s->range;
  282. s->lastdaynum.x = s->daynum.x;
  283. s->lastdaynum.y = s->daynum.y;
  284. s->daynum.x = daynum.x;
  285. s->daynum.y = daynum.y;
  286. s->range = rk;
  287. s->elevation = elevation;
  288. s->azimuth = azimuth;
  289. // Thall did provide a diff and division procedure, but I'm cutting corners..
  290. float dt = 86400.0 * (s->daynum.x - s->lastdaynum.x);
  291. dt += (86400.0 * s->daynum.y) - (86400.0 * s->lastdaynum.y);
  292. float dr = (rk - s->lastrange) * 1000.0;
  293. if (s->lastrange && (dt > 0.0))
  294. s->dopplerhz = dr * satdata[satnum].uplink * 1000000.0 / (-299792458.0 * dt);
  295. }
  296. /* ---------------------------------------------------------------------- */
  297. void sat_update (void)
  298. {
  299. if (!(flaga & 0x80))
  300. {
  301. for (int i = 0; i < numsats; i++)
  302. satstatus[i].state = -8;
  303. return;
  304. }
  305. qth.stnlat = gpsData.lat_raw / 10000000.0;
  306. qth.stnlon = -gpsData.lon_raw / 10000000.0;
  307. qth.stnalt = gpsData.alt_raw / 1000.0;
  308. CurrentDaynum();
  309. /* something to do when time & location details are known */
  310. for (int i = 0; i < numsats; i++)
  311. {
  312. satstatus[i].state = 0;
  313. if (Geostationary(i)) satstatus[i].state -= 1;
  314. if (Decayed(i)) satstatus[i].state -= 2;
  315. if (!AosHappens(i)) satstatus[i].state -= 4;
  316. }
  317. /* scan sats for visibility */
  318. for (int i = 0; i < numsats; i++)
  319. {
  320. if (satstatus[i].state < 0) /* eliminated sats */
  321. continue;
  322. satstatus[i].state = 1;
  323. CalcSat(i);
  324. }
  325. }
  326. /* ---------------------------------------------------------------------- */