Rose
Plan13.h
Go to the documentation of this file.
1 
8 // Plan13.cpp
9 //
10 // An implementation of Plan13 in C++ by Mark VandeWettering
11 //
12 // Plan13 is an algorithm for satellite orbit prediction first formulated
13 // by James Miller G3RUH. I learned about it when I saw it was the basis
14 // of the PIC based antenna rotator project designed by G6LVB.
15 //
16 // http://www.g6lvb.com/Articles/LVBTracker2/index.htm
17 //
18 // I ported the algorithm to Python, and it was my primary means of orbit
19 // prediction for a couple of years while I operated the "Easy Sats" with
20 // a dual band hand held and an Arrow antenna.
21 //
22 // I've long wanted to redo the work in C++ so that I could port the code
23 // to smaller processors including the Atmel AVR chips. Bruce Robertson,
24 // VE9QRP started the qrpTracker project to fulfill many of the same goals,
25 // but I thought that the code could be made more compact and more modular,
26 // and could serve not just the embedded targets but could be of more
27 // use for more general applications. And, I like the BSD License a bit
28 // better too.
29 //
30 // So, here it is!
31 //
32 
33 #pragma once
34 
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstdint>
38 #include <cmath>
39 #include <ctime>
40 #include <tuple>
41 #include <array>
42 #include <iostream>
43 #include <iomanip>
44 #include <map>
45 #include "constexpertrig.h"
46 
51 template<typename T>
52 constexpr T RADIANS(T deg) {
53  return deg * M_PI / 180.;
54 }
55 
56 template<typename T>
57 constexpr T DEGREES(T rad) {
58  return rad * 180. / M_PI;
59 }
60 
65 struct P13 {
66  constexpr static double RE = 6378.137;
67  constexpr static double FL = 1. / 298.257224;
68  constexpr static double GM = 3.986E5;
69  constexpr static double J2 = 1.08263E-3;
70  constexpr static double YM = 365.25;
71  constexpr static double YT = 365.2421874;
72  constexpr static double WW = 2. * M_PI / YT;
73  constexpr static double WE = 2. * M_PI + WW;
74  constexpr static double W0 = WE / 86400.;
75  constexpr static double YG = 2014.;
76  constexpr static double G0 = 99.5828;
77  constexpr static double MAS0 = 356.4105;
78  constexpr static double MASD = 0.98560028;
79  constexpr static double EQC1 = 0.03340;
80  constexpr static double EQC2 = 0.00035;
81  constexpr static double INS = RADIANS(23.4375);
82  constexpr static double CNS = cx_math::cos(INS);
83  constexpr static double SNS = cx_math::sin(INS);
84 
85  /*
86  constexpr static SatelliteEphemeris ISS =
87  {"ISS",
88  "1 25544U 98067A 20239.80208397 .00000993 00000-0 26004-4 0 9990",
89  "2 25544 51.6471 359.0049 0001779 59.1240 72.0472 15.49189559242962"};
90  constexpr static SatelliteEphemeris Moon =
91  { "Moon",
92  "1 1U 1A 20241.93195602 .00000000 00000-0 0000000 0 0019",
93  "2 1 335.6972 191.4324 0362000 0.1506 91.1318 0.03660000 14"};
94  */
95 
96  constexpr static double MAX_TLE_AGE = 7.0;
97  constexpr static double MAX_TLE_AGE_MOON = 1.5;
98 
99  template<typename T>
100  [[nodiscard]] static constexpr std::tuple<T, T> mod(const T x) {
101  T i, f;
102  f = modf(x, &i);
103  return std::make_tuple(f, i);
104  }
105 
106  template<typename T>
107  [[maybe_unused]] [[nodiscard]] static constexpr T to_integer(const T x) {
108  auto[f, i] = mod(x);
109  return i;
110  }
111 
112  template<typename T>
113  [[maybe_unused]] [[nodiscard]] static constexpr T to_fraction(const T x) {
114  auto[f, i] = mod(x);
115  return f;
116  }
117 };
118 
119 //------------------------------------------------------------------------
120 
121 // the original BASIC code used three variables (e.g. Ox, Oy, Oz) to
122 // represent a vector quantity. I think that makes for slightly more
123 // obtuse code, so I going to collapse them into a single variable
124 // which is an array of three elements
125 
126 //typedef double Vec3[3];
127 
128 template<typename T>
129 class Vec3Base : public std::array<T, 3> {
130 };
131 
132 typedef Vec3Base<double> Vec3;
133 
134 
135 //----------------------------------------------------------------------
136 
141 class DateTime {
142 public:
143  long DN;
144  double TN;
145 
153  [[nodiscard]] static long fnday(long year, uint8_t month, uint8_t day);
154 
160  [[nodiscard]] static std::tuple<int, uint8_t, uint8_t> fndate(long dt);
161 
171  DateTime(int year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t seconds)
172  : DateTime() { settime(year, month, day, hour, minute, seconds); }
173 
178  explicit DateTime(bool setToNow = false) : DN{0}, TN{0} {
179  if (setToNow)
180  userNow();
181  }
182 
183  DateTime(const DateTime &) = default;
184 
185  ~DateTime() = default;
186 
187  DateTime &operator=(const DateTime &source) = default;
188 
198  void settime(int year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t seconds);
199 
204  [[nodiscard]] std::tuple<int, uint, uint, uint, uint, uint>
205  gettime() const;
206 
207  [[nodiscard]] time_t mktime() const;
208 
209  bool operator<(const DateTime &rhs) const;
210 
211  bool operator>(const DateTime &rhs) const { return (rhs < *this); }
212 
213  DateTime operator+(long seconds);
214 
215  DateTime &operator+=(long seconds);
216 
217  DateTime operator+(double days);
218 
219  DateTime &operator+=(double days);
220 
221  double operator-(const DateTime &rhs) const;
222 
223  /* return a DateTime for the current time
224  */
225  void userNow() {
226  struct tm *tm;
227  auto t = time(nullptr);
228  tm = gmtime(&t); // ToDo: overlap in memcpy
229  int yr = tm->tm_year + 1900;
230  uint8_t mo = tm->tm_mon + 1;
231  uint8_t dy = tm->tm_mday;
232  uint8_t hr = tm->tm_hour;
233  uint8_t mn = tm->tm_min;
234  uint8_t sc = tm->tm_sec;
235 
236  settime(yr, mo, dy, hr, mn, sc);
237  }
238 
239  [[maybe_unused]] std::ostream &print_on(std::ostream &os) const {
240  auto[yr, mo, da, h, m, s] = gettime();
241  os << yr << '-' << (long) mo << '-' << (long) da << ' ' << (long) h << ':' << (long) m << ':' << (long) s;
242  return os;
243  }
244 };
245 
246 inline std::ostream &operator<<(std::ostream &os, const DateTime &dateTime) { return dateTime.print_on(os); }
247 
248 //----------------------------------------------------------------------
249 
254 class Observer {
255 public:
256  double LA;
257  double LO;
258  double HT;
259  Vec3 U, E, N, O, V;
260 
261  Observer() = default;
262 
269  Observer(double const &latitude, double const &longitude, double const &elevation);
270 
271  Observer(const Observer &observer) = default;
272 
273  Observer &operator=(const Observer &observer) = default;
274 };
275 
276 //----------------------------------------------------------------------
277 
282 struct Sun {
283  Vec3 SUN, H;
284 
285  void predict(const DateTime &dt);
286 };
287 
288 //----------------------------------------------------------------------
289 
294 class Satellite {
295  bool isMoon{};
296  bool isValid{};
297  std::string name;
298  long N{};
299  long YE{};
300  double IN{};
301  double RA{};
302  double EC{};
303  double WP{};
304  double MA{};
305  double MM{};
306  double M2{};
307  double RV{};
308 
309 
310  // these values are stored, but could be calculated on the fly
311  // during calls to predict()
312  // classic space/time tradeoff
313 
314  double N0{}, A_0{}, B_0{};
315  double PC{};
316  double QD{}, WD{}, DC{};
317  double RS{};
318 
324  void tle(const std::string_view &l1, const std::string_view &l2);
325 
326 public:
327  long DE{};
328  double TE{};
329 
330  Vec3 SAT{}, VEL{}; // celestial coordinates
331  Vec3 S{}, V{}; // geocentric coordinates
332 
333  DateTime mPrediction{};
334 
335  Satellite() = default;
336 
341  explicit Satellite(const std::array<std::string_view, 3> &ephemeris);
342 
343  void setEphemeris(const std::array<std::string_view,3> &ephemeris);
344 
345  constexpr explicit operator bool() const noexcept { return isValid; }
346 
351  void predict(const DateTime &dateTime);
352 
357  [[nodiscard]] std::string_view getName() const {
358  return name;
359  }
360 
365  [[nodiscard]] bool checkSatEpoch() const {
366  DateTime t_now(true);
367  DateTime t_epo = epoch();
368  if (isMoon)
369  return (t_epo + P13::MAX_TLE_AGE_MOON > t_now && t_now + P13::MAX_TLE_AGE_MOON > t_epo);
370  else
371  return (t_epo + P13::MAX_TLE_AGE > t_now && t_now + P13::MAX_TLE_AGE > t_epo);
372  }
373 
379  bool eclipsed(const Sun &sp);
380 
387  [[nodiscard]] std::tuple<double, double, double, double> topo(const Observer &obs);
388 
394  [[nodiscard]] std::tuple<double, double> geo();
395 
401  [[nodiscard]] std::tuple<double, double> celest();
402 
407  [[nodiscard]] double period() const;
408 
414  double viewingRadius(double alt);
415 
420  [[nodiscard]] DateTime epoch() const;
421 };
422 
Encapsulate the day-in-space for orbital mechanics computations.
Definition: Plan13.h:141
Sun position in the sky.
Definition: Plan13.h:282
DateTime(int year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t seconds)
Constructor to initialize to a calendar date and clock time.
Definition: Plan13.h:171
static constexpr double MAX_TLE_AGE
max age to use a TLE, days (except moon)
Definition: Plan13.h:96
bool checkSatEpoch() const
Determine if the sat epoch is known to be good.
Definition: Plan13.h:365
Data specifying an observer for computing relative visibility data.
Definition: Plan13.h:254
Plan13 general functions and constants.
Definition: Plan13.h:65
DateTime(bool setToNow=false)
Default constructor with the option to initialzie to current time.
Definition: Plan13.h:178
std::string_view getName() const
Access the satellite name.
Definition: Plan13.h:357
static constexpr double MAX_TLE_AGE_MOON
max age to use a TLE, days for the moon
Definition: Plan13.h:97
GaugeIndex operator+(const GaugeIndex &gaugeIndex, unsigned long increment)
Add an unsigned integer to a GaugeIndex.
Definition: Gauge.h:60
Satellite orbital mechanics.
Definition: Plan13.h:294
Definition: Plan13.h:129