1 //==============================================================================
2 //
3 // This file is part of GPSTk, the GPS Toolkit.
4 //
5 // The GPSTk is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published
7 // by the Free Software Foundation; either version 3.0 of the License, or
8 // any later version.
9 //
10 // The GPSTk is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with GPSTk; if not, write to the Free Software Foundation,
17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 // This software was developed by Applied Research Laboratories at the
20 // University of Texas at Austin.
21 // Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24
25 //==============================================================================
26 //
27 // This software was developed by Applied Research Laboratories at the
28 // University of Texas at Austin, under contract to an agency or agencies
29 // within the U.S. Department of Defense. The U.S. Government retains all
30 // rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 // Pursuant to DoD Directive 523024
33 //
34 // DISTRIBUTION STATEMENT A: This software has been approved for public
35 // release, distribution is unlimited.
36 //
37 //==============================================================================
38
39 #include "EngEphemeris.hpp"
40 #include "TestUtil.hpp"
41 #include "GPSWeekZcount.hpp"
42 #include <iostream>
43 #include <sstream>
44 using namespace gpstk;
45
46 #ifdef _MSC_VER
47 #define LDEXP(x,y) ldexp(x,y)
48 #else
49 #define LDEXP(x,y) std::ldexp(x,y)
50 #endif
51
52
53 /** Ephemeris subframe words at the end of a week. Useful for a
54 * week-rollover test of toe and toc as well as other things.
55 * Sorry about the decimal, it came that way out of HDF5.
56 * @note this data has been modified so that toe != toc, to facilitate
57 * verifying that the appropriate quantity is used where
58 * appropriate. */
59 const uint32_t ephEOW[] =
60 { 583228942, 824945128, 904134685, 184026330, 459310087,
61 16899638, 845363969, 0x0f647980, 4193148, 1073290676,
62 583228942, 824953464, 260012308, 225364840, 787693093,
63 1065730353, 298759921, 46377054, 57870868, 8172,
64 583228942, 824962032, 1072401983, 485782594, 84477,
65 301605863, 145566781, 506082625, 1072230894, 259901040 };
66 /* original data as broadcast
67 { 583228942, 824945128, 904134685, 184026330, 459310087,
68 16899638, 845363969, 255852580, 4193148, 1073290676,
69 583228942, 824953464, 260012308, 225364840, 787693093,
70 1065730353, 298759921, 46377054, 57870868, 8172,
71 583228942, 824962032, 1072401983, 485782594, 84477,
72 301605863, 145566781, 506082625, 1072230894, 259901040 };
73 */
74 const unsigned ephEOWwk = 1886;
75 const unsigned ephEOWToeWk = 1887;
76 const unsigned ephEOWprn = 14;
77 // the rest of these values were broken out by hand
78 const CommonTime ephEOWhowTime1 = GPSWeekZcount(ephEOWwk, 402804);
79 const CommonTime ephEOWhowTime2 = GPSWeekZcount(ephEOWwk, 402808);
80 const CommonTime ephEOWhowTime3 = GPSWeekZcount(ephEOWwk, 402812);
81 const long ephEOWhowSec1 = 604206;
82 const long ephEOWhowSec2 = 604212;
83 const long ephEOWhowSec3 = 604218;
84 const CommonTime ephEOWxmitTime1 = ephEOWhowTime1 - 6;
85 const CommonTime ephEOWxmitTime2 = ephEOWhowTime2 - 6;
86 const CommonTime ephEOWxmitTime3 = ephEOWhowTime3 - 6;
87 const double ephEOWxmitTimeSec1 = GPSWeekSecond(ephEOWxmitTime1).sow;
88 const double ephEOWtocSec = 597600;
89 const long ephEOWtocZ = 398400;
90 const CommonTime ephEOWtoc = GPSWeekZcount(ephEOWwk, ephEOWtocZ);
91 // as-broadcast
92 //const CommonTime ephEOWtoc = GPSWeekZcount(ephEOWwk+1, 0);
93 const double ephEOWaf0 = LDEXP(double( int32_t(0xfffff91d)), -31);
94 const double ephEOWaf1 = LDEXP(double( int16_t(0xffed)), -43);
95 const double ephEOWaf2 = 0.;
96 const double ephEOWiode = 61.;
97 const double ephEOWCrs = LDEXP(double( int16_t(0xfde4)), -5);
98 const double ephEOWdn = LDEXP(double( int16_t(0x35bb)), -43) * PI;
99 const double ephEOWM0 = LDEXP(double( int32_t(0x2dbbccf8)), -31) * PI;
100 const double ephEOWCuc = LDEXP(double( int16_t(0xfe17)), -29);
101 const double ephEOWecc = LDEXP(double(uint32_t(0x04473adb)), -33);
102 const double ephEOWCus = LDEXP(double( int16_t(0x0b0e)), -29);
103 const double ephEOWAhalf = LDEXP(double(uint32_t(0xa10dcc28)), -19);
104 const double ephEOWToe = 0.; //LDEXP(double(uint16_t()),4);
105 const CommonTime ephEOWtoe = GPSWeekSecond(ephEOWToeWk, ephEOWToe);
106 const double ephEOWCic = LDEXP(double( int16_t(0xffae)), -29);
107 const double ephEOWOMEGA0 = LDEXP(double( int32_t(0x3873d1d1)), -31) * PI;
108 const double ephEOWCis = LDEXP(double( int16_t(0x0005)), -29);
109 const double ephEOWi0 = LDEXP(double( int32_t(0x2747e88f)), -31) * PI;
110 const double ephEOWCrc = LDEXP(double( int16_t(0x22b4)), -5);
111 const double ephEOWw = LDEXP(double( int32_t(0xb078a8d5)), -31) * PI;
112 const double ephEOWOMEGAdot = LDEXP(double( int32_t(0xffffa3c7)), -43) * PI;
113 const double ephEOWidot = LDEXP(double( int16_t(0xfdc6)), -43) * PI;
114 const double ephEOWTgd = LDEXP(double( int8_t(0xec)), -31);
115 const short ephEOWcodeflgs = 1;
116 const short ephEOWl2pData = 0;
117 const short ephEOWhealth = 0;
118 const double ephEOWiodc = 0x03d;
119 // URA index = 0, worst case 2.4m 20.3.3.3.1.3
120 const double ephEOWacc = 2.4;
121 // fit interval *flag*
122 const double ephEOWfitint = 0;
123
124
125 class EngEphemeris_T
126 {
127 public:
128 /// Default Constructor, set the precision value
EngEphemeris_T()129 EngEphemeris_T()
130 {
131 eps = 1E-12;
132 b10 = 10;
133 }
134
~EngEphemeris_T()135 ~EngEphemeris_T() {}
136
137 /** Used to initalize a object before rewriting with the valid
138 * subframes. Necessary for addSubframe and
139 * addSubframeNoParity. Makes it seem like it has 3 valid
140 * subframes. */
fakeEphemerisInit(void)141 gpstk::EngEphemeris fakeEphemerisInit(void)
142 {
143 gpstk::EngEphemeris fakeEphemeris;
144
145 // array 30 bit words all set to one, an invalid
146 // subframe. Word 2 is different, contains SF id
147 const uint32_t data1[10] =
148 { 0x22FFFFFF, 0x3FFFF930, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF,
149 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF };
150 const uint32_t data2[10] =
151 { 0x22FFFFFF, 0x3FFFFA88, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF,
152 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF };
153 const uint32_t data3[10] =
154 { 0x22FFFFFF, 0x3FFFFBD0, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF,
155 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF };
156
157 fakeEphemeris.haveSubframe[0] = true;
158 fakeEphemeris.haveSubframe[1] = true;
159 fakeEphemeris.haveSubframe[2] = true;
160 for (int i=0; i<10; ++i) fakeEphemeris.subframeStore[0][i] = data1[i];
161 for (int i=0; i<10; ++i) fakeEphemeris.subframeStore[1][i] = data2[i];
162 for (int i=0; i<10; ++i) fakeEphemeris.subframeStore[2][i] = data3[i];
163
164 return fakeEphemeris;
165 }
166
167 //=======================================================================
168 // The following 3 methods are used to see if the data specificly set for
169 // each subframe was set correctly
170 //
171 // Data can be stored in EngEphemeris in multiple ways. This ensures
172 // the method of storing data was successful
173 //
174 // Doesn't test any of the data stored in the orbit or clock objects
175 //=======================================================================
176
subframe1Check(gpstk::EngEphemeris dataStore,gpstk::TestUtil & testFramework,bool skipASAlert=false)177 void subframe1Check(gpstk::EngEphemeris dataStore,
178 gpstk::TestUtil& testFramework,
179 bool skipASAlert = false)
180 {
181 TUASSERT(dataStore.haveSubframe[0]);
182 TUASSERTE(unsigned short, 0, dataStore.tlm_message[0]);
183 TUASSERTE(short, 6, dataStore.PRNID);
184 TUASSERTE(short, 1, dataStore.tracker);
185
186 // ASAlert is set to 1 by that addIncompleteSF1Thru3, so
187 // bypassing the ASAlerts test only for that function HOWTime
188 // is not set by this function either
189 if (!skipASAlert)
190 {
191 TUASSERTE(short, 0, dataStore.ASalert[0]);
192 TUASSERTE(long, 409902, dataStore.HOWtime[0]);
193 }
194
195 TUASSERTE(short, 1025, dataStore.weeknum);
196 TUASSERTE(short, 2, dataStore.codeflags);
197 TUASSERTE(short, 0, dataStore.health);
198 TUASSERTE(short, 0, dataStore.L2Pdata);
199 TUASSERTE(short, 91, dataStore.IODC);
200 }
201
202
subframe2Check(gpstk::EngEphemeris dataStore,gpstk::TestUtil & testFramework,bool skipASAlert=false)203 void subframe2Check(gpstk::EngEphemeris dataStore,
204 gpstk::TestUtil& testFramework,
205 bool skipASAlert = false)
206 {
207 TUASSERT(dataStore.haveSubframe[1]);
208 TUASSERTE(unsigned short, 0, dataStore.tlm_message[1]);
209
210 // ASAlert is set to 1 by that addIncompleteSF1Thru3, so
211 // bypassing the ASAlerts test only for that function HOWTime
212 // is not set by this function either
213 if (!skipASAlert)
214 {
215 TUASSERTE(short, 0, dataStore.ASalert[1]);
216 TUASSERTE(long, 409908, dataStore.HOWtime[1]);
217 }
218
219 TUASSERTE(short, 91, dataStore.IODE);
220 TUASSERTE(short, 0, dataStore.fitint);
221 }
222
223
subframe3Check(gpstk::EngEphemeris dataStore,gpstk::TestUtil & testFramework,bool skipASAlert=false)224 void subframe3Check(gpstk::EngEphemeris dataStore,
225 gpstk::TestUtil& testFramework,
226 bool skipASAlert = false)
227 {
228 TUASSERT(dataStore.haveSubframe[2]);
229 TUASSERTE(unsigned short, 0, dataStore.tlm_message[2]);
230
231 // ASAlert is set to 1 by that addIncompleteSF1Thru3, so
232 // bypassing the ASAlerts test only for that function HOWTime
233 // is not set by this function either
234 if (!skipASAlert)
235 {
236 TUASSERTE(short, 0, dataStore.ASalert[2]);
237 TUASSERTE(long, 409914, dataStore.HOWtime[2]);
238 }
239 }
240
241
initializationTest(void)242 unsigned initializationTest(void)
243 {
244 TUDEF("EngEphemeris", "Default Constructor");
245 unsigned badCount = 0;
246
247 gpstk::EngEphemeris empty;
248
249 TUASSERTE(short, 0, empty.PRNID);
250 TUASSERTE(short, 0, empty.tracker);
251 TUASSERTE(short, 0, empty.IODC);
252 TUASSERTE(short, 0, empty.IODE);
253 TUASSERTE(short, 0, empty.weeknum);
254 TUASSERTE(short, 0, empty.codeflags);
255 TUASSERTE(short, 0, empty.health);
256 TUASSERTE(short, 0, empty.L2Pdata);
257 TUASSERTE(std::string, "", empty.satSys);
258 TUASSERTFE(0, empty.Tgd);
259 TUASSERT(empty.isFIC);
260 TUASSERTE(short, 0, empty.ASalert[0]);
261 TUASSERTE(short, 0, empty.ASalert[1]);
262 TUASSERTE(short, 0, empty.ASalert[2]);
263 TUASSERTE(long, 0, empty.HOWtime[0]);
264 TUASSERTE(long, 0, empty.HOWtime[1]);
265 TUASSERTE(long, 0, empty.HOWtime[2]);
266 TUASSERT(!empty.haveSubframe[0]);
267 TUASSERT(!empty.haveSubframe[1]);
268 TUASSERT(!empty.haveSubframe[2]);
269
270 for (unsigned i=0; i<3; i++)
271 {
272 for (unsigned j=0; j<10; j++)
273 {
274 if (empty.subframeStore[i][j] > eps)
275 badCount++;
276 }
277 }
278 TUASSERTE(unsigned, 0, badCount);
279 badCount = 0;
280
281 for (unsigned i=0; i<3; i++)
282 {
283 if (empty.haveSubframe[i] != false)
284 badCount++;
285 }
286 TUASSERTE(unsigned, 0, badCount);
287 badCount = 0;
288
289 TUASSERT(!empty.isValid());
290 TUASSERT(!empty.isDataSet());
291
292 TURETURN();
293 }
294
295
addSubframeTest(void)296 unsigned addSubframeTest(void)
297 {
298 TUDEF("EngEphemeris", "addSubframe");
299
300 gpstk::EngEphemeris dataStore;
301
302 // Same values as for addSubframeNoParityTest below, just
303 // added correct parity. Parity was calculated using the
304 // verified functions from EngNav, bad use of time to do at
305 // least 30 binary calculations with take >5 minutes each
306
307 const uint32_t subframe1P[10] =
308 { 0x22c000e4, 0x215ba160, 0x00180012, 0x1fffffc0, 0x3fffffc3,
309 0x3ffffffc, 0x3fffc009, 0x16d904f0, 0x003fdbac, 0x1b83ed54 };
310 const uint32_t subframe2P[10] =
311 { 0x22c000e4, 0x215bc2f0, 0x16c2eb4d, 0x09f7c524, 0x2fdc3384,
312 0x0289c0dd, 0x0d5ecc38, 0x036b6842, 0x034f4df0, 0x1904c0b4 };
313 const uint32_t subframe3P[10] =
314 { 0x22c000e4, 0x215be378, 0x3ffcc344, 0x1a8441d8, 0x3ff80b74,
315 0x1c8deb5e, 0x0a34d525, 0x14a5012e, 0x3fee8c06, 0x16c35c80 };
316 dataStore = fakeEphemerisInit();
317
318 // Week: 1025, PRN: 6, tracker:1
319 TUASSERT(dataStore.addSubframe(subframe1P, 1025, 6, 1));
320 subframe1Check(dataStore, testFramework);
321
322 TUASSERT(dataStore.addSubframe(subframe2P, 1025, 6, 1));
323 subframe2Check(dataStore, testFramework);
324
325 TUASSERT(dataStore.addSubframe(subframe3P, 1025, 6, 1));
326 subframe3Check(dataStore, testFramework);
327
328 TURETURN();
329 }
330
331
addSubframeNoParityTest(void)332 unsigned addSubframeNoParityTest(void) //calls add subframe'
333 {
334 TUDEF("EngEphemeris", "addSubframeNoParity");
335
336 gpstk::EngEphemeris dataStore;
337 dataStore = fakeEphemerisInit();
338
339 /*
340 The following huge comments describe how the broadcast subframe data
341 is reconstructed from data/test_input_rinex_nav_FilterTest2.99n
342
343
344 Feeding in raw binary values output by satellite (without the parity bits)
345 Without parity each word in 24 bits instead of 30
346 All reserved bits taken to be 1
347
348 Raw SV data is reconstructed from data/test_input_rinex_nav_FilterTest2.99n
349 using the data format in fig 20-1 of IS-GPS-200D
350
351 Word 1 formed by TLM preamble followed by TLM mesg (Msg defined by
352 Control Segment and Space segment (?), so left blank)
353 ref. IS-GPS-200D p 82 fig 20-2
354 preamble msg RESERVED
355 10001011 00000000000000 11 = 0x8B0003
356
357 Word 2 formed by 17b truncated TOW, 1b Alert Flag and 1b Spoof Flag
358 followed by 3b subframe ID (p 81) and 2 parity computation bits
359 ref. IS-GPS-200D p 82 fig 20-2
360 TOW was modified to be evenly divisible by 6
361 17b truncated TOW (409902/6) Alert AntiSpoof SubframeID Parity
362 10000101011011101 0 0 001 00 = 0x856E84
363
364 Word 3 formed by 10b Week Number (mod 1024), 2b codeflag, 4b URA
365 index, 6b SV health, and first 2 bits of IODC
366 ref. IS-GPS-200D 20.3.3.3 (p 82)
367 Week Num (1025) CodeFlag (C/A code must exist, and P code flag set to 0) URA svhealth IODC 2 MSB
368 0000000001 10 0000 000000 00 = 0x006000
369
370 Word 4 is L2 P code flag (0) followed by 23 reserved bits, setting them to 1
371 0x7FFFFF
372
373 Words 5 and 6 are composed on 24 reserved bits, setting them to 1
374 0xFFFFFF
375
376 Word 7 formed by 16 reserved bits, setting them to 1, followed by 8b TGD
377 TGD is represented by integer scaled by a factor of 2^-31 (IS-GPS-200D
378 table 20-I)
379 Reserved TGD * 2^-31 (0)
380 1111111111111111 00000000 = 0xFFFF00
381
382 Word 8 formed by 8 LSBs of IODC and 16b toc scaled by 2^4
383 Toc is obtained by converting UTC time from Rinex Nav, scaled to GPS
384 week second (=25619)
385 (IS-GPS-200D table 20-I)
386 8 LSBs of IODC toc * 2^-4
387 0101 1011 0110 0100 0001 0011 = 0x5B6413
388
389 Word 9 formed by 8b of af2 scaled by 2^-55, and 16b of af1 scaled by
390 2^-43 (IS-GPS-200D table 20-I)
391 af2 * 2^52 af1 * 2^43
392 0000 0000 1111 1111 0110 1110 = 0x00FF6E
393
394 Word 10 formed by 22b of af0 scaled by 2^-31 and 2 reserved parity
395 bits (marked 0)
396 af * 2^31. # is signed, so found by 2^22 + af0*2^31
397 -.839701388031E-03 * 2^31 =
398 1001000111110000010011 + 00 = 1001 0001 1111 0000 0100 1100 = 0x91F04C
399 */
400
401 // Is this function designed to just update already stored subframes?
402 // Must be, because loop to see if all 3 subframes stored
403
404 const uint32_t subframe1[10] =
405 { 0x8B0003, 0x856E84, 0x006000, 0x7FFFFF, 0xFFFFFF,
406 0xFFFFFF, 0xFFFF00, 0x5B6413, 0x00FF6E, 0x91F04C };
407
408 TUASSERT(dataStore.addSubframeNoParity(subframe1, 1025, 6, 1));
409 subframe1Check(dataStore, testFramework);
410
411 /*
412
413 Feeding in raw binary values output by satellite (without the parity bits)
414 Without parity each word in 24 bits instead of 30
415 All reserved bits taken to be 1
416
417 Raw SV data is reconstructed from data/test_input_rinex_nav_FilterTest2.99n
418 using the data format in fig 20-1 of IS-GPS-200D
419
420 Word 1 formed by TLM preamble followed by TLM mesg (Msg defined by
421 Control Segment and Space segment (?), so left blank)
422 ref. IS-GPS-200D p 82 fig 20-2
423 preamble msg RESERVED
424 10001011 00000000000000 11 = 0x8B0003
425
426 Word 2 formed by 17b truncated TOW, 1b Alert Flag and 1b Spoof Flag
427 followed by 3b subframe ID (p 81) and 2 parity computation bits
428 ref. IS-GPS-200D p 82 fig 20-2
429 Previous TOW + 6 for transmit time
430 17b truncated TOW (409908/6) Alert AntiSpoof SubframeID Parity
431 10000101011011110 0 0 010 00 = 0x856F08
432
433 Word 3 formed by 8b IODE and signed 16b Crs, scaled by 2^-5
434 IODE (91) Crs * 2^5 (93.40625 * 2^5)
435 01011011 0000101110101101 = 0x5B0BAD
436
437 Word 4 is signed 16b delta N scaled by 2^-43 with the 8 msbs of M0
438 scaled by 2^-31
439 Delta N * 2^43 (.11604054784E-8 * 2^43 / pi = 3249)
440 0000 1100 1011 0001
441 M0 * 2^31 (0.162092304801 * 2^31 / pi = 110800671)
442 0000 0110 (1001 1010 1010 1111 0001 1111) = 0x0CB106
443
444 Word 5 is the other 24 bits of M0 listed above
445 M0
446 1001 1010 1010 1111 0001 1111 = 0x9AAF1F
447
448 Word 6 is signed 16b Cuc scaled by 2^-29 with the signed 8 msbs of e
449 scaled by 2^-33
450 Cuc*2^29 (.484101474285E-5*2^29) e*2^33 (.626740418375E-2*2^33)
451 0000 1010 0010 0111 0000 0011 (0011 0101 0111 1011 0011 0000) = 0x0A2703
452
453 Word 7 is the other 24 bits of e listed above
454 e
455 0011 0101 0111 1011 0011 0000 = 0x357B30
456
457 Word 8 is signed 16b of Cus scaled by 2^-29 and the unsigned 8mbs of
458 sqrtA scaled by 2^-19
459 Cus*2^29 (.652112066746E-5*2^29) sqrtA*2^19(.515365489006E4*2^19)
460 0000 1101 1010 1101 1010 0001 (0000 1101 0011 1101 0011 0111) = 0x0DADA1
461
462 Word 9 is the other 24 bits of sqrtA listed above
463 A
464 0000 1101 0011 1101 0011 0111 = 0x0D3D37
465
466 Word 10 is 16b toe scaled by 2^4 with 1b fit interval flag and 5b AODO
467 Toe (409902*2^-4) fitInt AODO (age of almanac observations, doesn't matter)
468 0110 0100 0001 0011 0 000 00 00 = 0x641300
469 */
470
471 const uint32_t subframe2[10] =
472 { 0x8B0003, 0x856F08, 0x5B0BAD, 0x0CB106, 0x9AAF1F,
473 0x0A2703, 0x357B30, 0x0DADA1, 0x0D3D37, 0x641300 };
474
475 TUASSERT(dataStore.addSubframeNoParity(subframe2, 1025, 6, 1));
476 subframe2Check(dataStore, testFramework);
477
478 /*
479 Feeding in raw binary values output by satellite (without the parity bits)
480 Without parity each word in 24 bits instead of 30
481 All reserved bits taken to be 1
482
483 Raw SV data is reconstructed from data/test_input_rinex_nav_FilterTest2.99n
484 using the data format in fig 20-1 of IS-GPS-200D
485
486 Word 1 formed by TLM preamble followed by TLM mesg (Msg defined by
487 Control Segment and Space segment (?), so left blank)
488 ref. IS-GPS-200D p 82 fig 20-2
489 preamble msg RESERVED
490 10001011 00000000000000 11 = 0x8B0003
491
492 Word 2 formed by 17b truncated TOW, 1b Alert Flag and 1b Spoof Flag
493 followed by 3b subframe ID (p 81) and 2 parity computation bits
494 ref. IS-GPS-200D p 82 fig 20-2
495 Previous TOW + 6 for transmit time
496 17b truncated TOW (409914/6) Alert AntiSpoof SubframeID Parity
497 10000101011011111 0 0 011 00 = 0x856F0C
498
499 Word 3 is signed 16b Cic scaled by 2^-29 with the signed 8 msbs of
500 omega0 scaled by 2^-31
501 Cic*2^29 (-.242143869400E-7*2^29) omega0*2^31 (.329237003460*2^31/pi)
502 1111 1111 1111 0010 0000 1101 (0110 1010 0001 0001 0000 0111) = 0xFFF30D
503
504 Word 4 is the other 24 bits of omega0 listed above
505 omega0
506 0110 1010 0001 0001 0000 0111 = 0x6A1107
507
508 Word 5 is signed 16b Cis scaled by 2^-29 with the signed 8 msbs of I0
509 scaled by 2^-31
510 Cis*2^29 (-.596046447754E-7*2^29) I0*2^31 (1.11541663136*2^31/pi)
511 1111 1111 1110 0000 0010 1101 (0111 0010 0011 0111 1010 1101) = 0xFFE02D
512
513 Word 6 is the other 24 bits of I0 listed above
514 I0
515 0111 0010 0011 0111 1010 1101 = 0x7237AD
516
517 Word 7 is signed 16b of Crc scaled by 2^-5 and the unsigned 8mbs of
518 omega scaled by 2^-31
519 Crc*2^5 (326.59375*2^5) omega*2^31(2.06958726335*2^31/pi)
520 0010 1000 1101 0011 0101 0100 (0101 0010 1001 0100 0000 0100) = 0x28D354
521
522 Word 8 is the other 24 bits of omega listed above
523 omega
524 0101 0010 1001 0100 0000 0100 = 0x529404
525
526 Word 9 is signed 24b of OMEGADOT scaled by 2^-43
527 OMEGADOT (-.638312302555E-8*2^43/pi)
528 1111 1111 1011 1010 0011 0000 = 0xFFBA30
529
530 Word 10 is 8b IODE with signed 14b IDOT scaled by 2^-43
531 IODE (91) IDOT*2^43 (.307155651409E-9*2^43/pi) parity comp
532 0101 1011 0000 1101 0111 00 00 = 0x5B0D70
533 */
534
535 const uint32_t subframe3[10] =
536 { 0x8B0003, 0x856F8C, 0xFFF30D, 0x6A1107, 0xFFE02D,
537 0x7237AD, 0x28D354, 0x529404, 0xFFBA30, 0x5B0D70 };
538
539 TUASSERT(dataStore.addSubframeNoParity(subframe3, 1025, 6, 1));
540 subframe3Check(dataStore, testFramework);
541
542 TURETURN();
543 }
544
545
setSF1Test(gpstk::EngEphemeris & dataStore)546 unsigned setSF1Test(gpstk::EngEphemeris& dataStore)
547 {
548 TUDEF("EngEphemeris", "setSF1");
549
550 // The following values were taken from
551 // data/test_input_rinex_nav_FilterTest2.99n
552
553 // Rinex documentation found at
554 // http://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt
555 // helpful animation at
556 // http://emedia.rmit.edu.au/satellite/node/21
557
558 // TLM value just taken to be the TLM msg, which is defined
559 // by the CS and SS, so left blank. HOW value taken to be
560 // Time of Week in seconds.
561
562 // More detailed info on each data type can be found in IS-GPS-200D
563
564 dataStore.setSF1(0, // tlm
565 409902, // how
566 0, // ASalert
567 1025, // week
568 2, // cflags
569 0, // acc
570 0, // svhealth
571 91, // IODC
572 0, // l2pdate
573 0.0, // tgd
574 25619, // Toc
575 0.0, // af2
576 -.165982783074*pow(b10,-10), // af1
577 -.839701388031*pow(b10,-3), // af0
578 1, // tracker
579 6); // prn
580
581 subframe1Check(dataStore, testFramework);
582
583 TUASSERT(!dataStore.isValid());
584 TUASSERT(!dataStore.isDataSet());
585
586 TURETURN();
587 }
588
589
setSF2Test(gpstk::EngEphemeris & dataStore)590 unsigned setSF2Test(gpstk::EngEphemeris& dataStore)
591 {
592 TUDEF("EngEphemeris", "setSF2");
593
594 // The following values were taken from
595 // data/test_input_rinex_nav_FilterTest2.99n
596
597 // Rinex documentation found at
598 // http://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt
599 // helpful animation at
600 // http://emedia.rmit.edu.au/satellite/node/21
601
602 // TLM value just taken to be the TLM msg, which is defined
603 // by the CS and SS, so left blank. HOW value taken to be
604 // Time of Week in seconds.
605
606 // More detailed info on each data type can be found in IS-GPS-200D
607
608 dataStore.setSF2(0, // tlm
609 409908.0, // how
610 0, // ASalert
611 91, // IODE
612 93.40625, // crs
613 .11604054784 * pow(b10,-8), // Dn
614 0.162092304801, // m0
615 .484101474285*pow(b10,-5), // cuc
616 .626740418375*pow(b10,-2), // ecc
617 .652112066746*pow(b10,-5), // cus
618 .515365489006*pow(b10,4), // ahalf
619 409902, // toe
620 0); // fitint
621
622 subframe2Check(dataStore, testFramework);
623
624 TUASSERT(!dataStore.isDataSet());
625
626 TURETURN();
627 }
628
629
setSF3Test(gpstk::EngEphemeris & dataStore)630 unsigned setSF3Test(gpstk::EngEphemeris& dataStore)
631 {
632 TUDEF("EngEphemeris", "setSF3");
633
634 // The following values were taken from
635 // data/test_input_rinex_nav_FilterTest2.99n
636
637 // Rinex documentation found at
638 // http://igscb.jpl.nasa.gov/igscb/data/format/rinex211.txt
639 // helpful animation at
640 // http://emedia.rmit.edu.au/satellite/node/21
641
642 // TLM value just taken to be the TLM msg, which is defined
643 // by the CS and SS, so left blank. HOW value taken to be
644 // Time of Week in seconds.
645
646 // More detailed info on each data type can be found in IS-GPS-200D
647
648 dataStore.setSF3(0, // tlm
649 409914.0, // how
650 0, // ASalert
651 -.242143869400*pow(b10,-7), // cic
652 .10479939309884491, // Omega0 aka OMEGA
653 -.596046447754*pow(b10,-7), // cis
654 0.3550481409757088, // I0
655 326.59375, // crc
656 0.6587700862443613, // W (aka omega)
657 -2.0318111637599545*pow(b10,-9), // OmegaDot
658 .307155651409*pow(b10,-9)); // idot
659
660 subframe3Check(dataStore, testFramework);
661
662 TUASSERT(dataStore.isDataSet());
663
664 TURETURN();
665 }
666
667
getTest(gpstk::EngEphemeris & dataStore)668 unsigned getTest(gpstk::EngEphemeris& dataStore)
669 {
670 TUDEF("EngEphemeris", "Get Methods");
671
672 TUASSERTE(short, 6, dataStore.getPRNID());
673 TUASSERTE(short, 1, dataStore.getTracker());
674 TUASSERTFE(409902, dataStore.getHOWTime(1));
675 TUASSERTE(short, 0, dataStore.getASAlert(1));
676 TUASSERTE(short, 1025, dataStore.getFullWeek());
677 TUASSERTE(short, 2, dataStore.getCodeFlags());
678 TUASSERTFE(25619, dataStore.getToc());
679 TUASSERTFE(-.839701388031*pow(b10,-3), dataStore.getAf0());
680 TUASSERTFE(-.165982783074*pow(b10,-10), dataStore.getAf1());
681 TUASSERTFE(0, dataStore.getAf2());
682 TUASSERTE(short, 0, dataStore.getHealth());
683 TUASSERTE(short, 0, dataStore.getL2Pdata());
684 TUASSERTE(short, 91, dataStore.getIODC());
685 TUASSERTE(short, 91, dataStore.getIODE());
686
687 //setSF# doesn't set AODO, is only set by loadData which is not tested by getMethods. Skipping
688 // testMesg = "The getAODO method didn't function correctly";
689 // TUASSERTE(dataStore.getAODO() == 0/*VALUE NOT SET BY SETSF3*/);
690
691 TUASSERTFE(.652112066746*pow(b10,-5), dataStore.getCus());
692 TUASSERTFE(93.40625, dataStore.getCrs());
693 TUASSERTFE(.484101474285*pow(b10,-5), dataStore.getCuc());
694 TUASSERTFE(409902, dataStore.getToe());
695 TUASSERTFE(0.162092304801, dataStore.getM0());
696 TUASSERTFE(.11604054784*pow(b10,-8), dataStore.getDn());
697 TUASSERTFE(.626740418375*pow(b10,-2), dataStore.getEcc());
698 TUASSERTFE(.515365489006*pow(b10,4), dataStore.getAhalf());
699 TUASSERTFE(pow(.515365489006*pow(b10,4),2), dataStore.getA());
700 TUASSERTFE(-.596046447754*pow(b10,-7), dataStore.getCis());
701 TUASSERTFE(326.59375, dataStore.getCrc());
702 TUASSERTFE(-.242143869400*pow(b10,-7), dataStore.getCic());
703 TUASSERTFE(0.10479939309884491, dataStore.getOmega0());
704 TUASSERTFE(0.3550481409757088, dataStore.getI0());
705 TUASSERTFE(0.6587700862443613, dataStore.getW());
706 TUASSERTFE(-2.0318111637599545*pow(b10,-9), dataStore.getOmegaDot());
707 TUASSERTFE(.307155651409*pow(b10,-9), dataStore.getIDot());
708 TUASSERTE(long, 409890, dataStore.getTot());
709 // earliest of the HOW's (409902) rounded down to nearest
710 // multiple of 30
711
712 TURETURN();
713 }
714
loadDataTest(void)715 unsigned loadDataTest(void)
716 {
717 TUDEF("EngEphemeris", "loadData");
718
719 gpstk::EngEphemeris dataStore;
720
721 unsigned short tlm[3] = {0,0,0};
722 const long how[3] = {409902, 409908, 409914};
723 const short ASalert[3] = {0,0,0};
724
725 dataStore.loadData((std::string) "No Idea",
726 tlm,
727 how,
728 ASalert,
729 1, // tracker
730 6, // prn
731 1025, // week
732 2, // cflags
733 0, // acc
734 0, // svhealth
735 91, // iodc
736 0, // l2p
737 0.0, // aodo
738 0.0, // tgd
739 25619, // toc
740 0.0, // af2
741 -.165982783074*pow(b10,-10), // af1
742 -.839701388031*pow(b10,-3), // af0
743 91, // iode
744 93.40625, // crs
745 .11604054784 * pow(b10,-8), // dn
746 0.162092304801, // M0
747 .484101474285*pow(b10,-5), // cuc
748 .626740418375*pow(b10,-2), // ecc
749 .652112066746*pow(b10,-5), // cus
750 .515365489006*pow(b10,4), // Ahalf
751 409902, // toe
752 0, // fit int
753 -.242143869400*pow(b10,-7), // cic
754 .10479939309884491, // Omega0 aka OMEGA
755 -.596046447754*pow(b10,-7), // cis
756 0.3550481409757088, // i0
757 326.59375, // crc
758 0.6587700862443613, // W aka omega
759 -2.0318111637599545*pow(b10,-9), // OmegaDot
760 .307155651409*pow(b10,-9)); // IDot
761
762 subframe1Check(dataStore, testFramework);
763 subframe2Check(dataStore, testFramework);
764 subframe3Check(dataStore, testFramework);
765
766 TURETURN();
767 }
768
769
addIncompleteTest(void)770 unsigned addIncompleteTest(void)
771 {
772 TUDEF("EngEphemeris", "addIncomplete");
773
774 gpstk::EngEphemeris dataStore;
775
776 const uint32_t subframe1P[8] =
777 { 0x00180012, 0x1fffffc0, 0x3fffffc3, 0x3ffffffc,
778 0x3fffc009, 0x16d904f0, 0x003fdbac, 0x1b83ed54 };
779 const uint32_t subframe2P[8] =
780 { 0x16c2eb4d, 0x09f7c524, 0x2fdc3384, 0x0289c0dd,
781 0x0d5ecc38, 0x036b6842, 0x034f4df0, 0x1904c0b4 };
782 const uint32_t subframe3P[8] =
783 { 0x3ffcc344, 0x1a8441d8, 0x3ff80b74, 0x1c8deb5e,
784 0x0a34d525, 0x14a5012e, 0x3fee8c06, 0x16c35c80 };
785
786 TUASSERT(dataStore.addIncompleteSF1Thru3(subframe1P, subframe2P,
787 subframe3P, 444, 1025, 6, 1));
788
789 //ASAlert tests fail, data is not included in the incomplete subframe.
790 subframe1Check(dataStore, testFramework, true);
791 subframe2Check(dataStore, testFramework, true);
792 subframe3Check(dataStore, testFramework, true);
793
794 TURETURN();
795 }
796
797
endOfWeekTest()798 unsigned endOfWeekTest()
799 {
800 TUDEF("EngEphemeris", "addSubframe");
801 EngEphemeris eeph;
802 TUASSERT(eeph.addSubframe(&ephEOW[ 0], ephEOWwk, ephEOWprn, 1));
803 TUASSERT(eeph.addSubframe(&ephEOW[10], ephEOWwk, ephEOWprn, 1));
804 TUASSERT(eeph.addSubframe(&ephEOW[20], ephEOWwk, ephEOWprn, 1));
805
806 TUASSERTE(short, ephEOWprn, eeph.PRNID);
807 TUASSERTE(CommonTime, ephEOWtoc, eeph.getEpochTime());
808 TUASSERTFE(ephEOWaf0, eeph.bcClock.getAf0());
809 TUASSERTFE(ephEOWaf1, eeph.bcClock.getAf1());
810 TUASSERTFE(ephEOWaf2, eeph.bcClock.getAf2());
811 TUASSERTFE(ephEOWiode, eeph.IODE);
812 TUASSERTFE(ephEOWCrs, eeph.orbit.getCrs());
813 TUASSERTFE(ephEOWdn, eeph.orbit.getDn());
814 TUASSERTFE(ephEOWM0, eeph.orbit.getM0());
815 TUASSERTFE(ephEOWCuc, eeph.orbit.getCuc());
816 TUASSERTFE(ephEOWecc, eeph.orbit.getEcc());
817 TUASSERTFE(ephEOWCus, eeph.orbit.getCus());
818 TUASSERTFE(ephEOWAhalf, eeph.orbit.getAhalf());
819 TUASSERTFE(ephEOWtoe, eeph.getEphemerisEpoch());
820 TUASSERTFE(ephEOWCic, eeph.orbit.getCic());
821 TUASSERTFE(ephEOWOMEGA0, eeph.orbit.getOmega0());
822 TUASSERTFE(ephEOWCis, eeph.orbit.getCis());
823 TUASSERTFE(ephEOWi0, eeph.orbit.getI0());
824 TUASSERTFE(ephEOWCrc, eeph.orbit.getCrc());
825 TUASSERTFE(ephEOWw, eeph.orbit.getW());
826 TUASSERTFE(ephEOWOMEGAdot, eeph.orbit.getOmegaDot());
827 TUASSERTFE(ephEOWidot, eeph.orbit.getIDot());
828 TUASSERTE(short, ephEOWcodeflgs, eeph.codeflags);
829 TUASSERTE(short, ephEOWl2pData, eeph.L2Pdata);
830 TUASSERTFE(ephEOWacc, eeph.getAccuracy());
831 TUASSERTE(short, ephEOWhealth, eeph.health);
832 TUASSERTFE(ephEOWTgd, eeph.Tgd);
833 TUASSERTFE(ephEOWiodc, eeph.IODC);
834 TUASSERTFE(ephEOWfitint, eeph.fitint);
835
836 TURETURN();
837 }
838
839
equalityTest()840 unsigned equalityTest()
841 {
842 TUDEF("EngEphemeris", "operator== / !=");
843 EngEphemeris eeph;
844 TUASSERT(eeph.addSubframe(&ephEOW[ 0], ephEOWwk, ephEOWprn, 1));
845 TUASSERT(eeph.addSubframe(&ephEOW[10], ephEOWwk, ephEOWprn, 1));
846 TUASSERT(eeph.addSubframe(&ephEOW[20], ephEOWwk, ephEOWprn, 1));
847 EngEphemeris eephCopy(eeph);
848 // make sure our copy reports as being the same
849 TUASSERTE(EngEphemeris, eeph, eephCopy);
850 // Tweak each data member one by one and compare.
851 // Yes the comments are fairly redundant but it helps to
852 // visually separate each batch of statements per data
853 // member.
854 eephCopy.tlm_message[0] = 0xbeef;
855 TUASSERT(eephCopy != eeph);
856 TUASSERT(!(eephCopy == eeph));
857 // tlm[1]
858 TUCATCH(eephCopy = eeph);
859 TUASSERTE(EngEphemeris, eeph, eephCopy);
860 eephCopy.tlm_message[1] = 0xbeef;
861 TUASSERT(eephCopy != eeph);
862 TUASSERT(!(eephCopy == eeph));
863 // tlm[2]
864 TUCATCH(eephCopy = eeph);
865 TUASSERTE(EngEphemeris, eeph, eephCopy);
866 eephCopy.tlm_message[2] = 0xbeef;
867 TUASSERT(eephCopy != eeph);
868 TUASSERT(!(eephCopy == eeph));
869 // satSys
870 TUCATCH(eephCopy = eeph);
871 TUASSERTE(EngEphemeris, eeph, eephCopy);
872 eephCopy.satSys = "twaffle";
873 TUASSERT(eephCopy != eeph);
874 TUASSERT(!(eephCopy == eeph));
875 // PRNID
876 TUCATCH(eephCopy = eeph);
877 TUASSERTE(EngEphemeris, eeph, eephCopy);
878 eephCopy.PRNID = 93;
879 TUASSERT(eephCopy != eeph);
880 TUASSERT(!(eephCopy == eeph));
881 // tracker
882 TUCATCH(eephCopy = eeph);
883 TUASSERTE(EngEphemeris, eeph, eephCopy);
884 eephCopy.tracker = 39;
885 TUASSERT(eephCopy != eeph);
886 TUASSERT(!(eephCopy == eeph));
887 // HOWtime[0]
888 TUCATCH(eephCopy = eeph);
889 TUASSERTE(EngEphemeris, eeph, eephCopy);
890 eephCopy.HOWtime[0] = 0xbeef;
891 TUASSERT(eephCopy != eeph);
892 TUASSERT(!(eephCopy == eeph));
893 // HOWtime[1]
894 TUCATCH(eephCopy = eeph);
895 TUASSERTE(EngEphemeris, eeph, eephCopy);
896 eephCopy.HOWtime[1] = 0xbeef;
897 TUASSERT(eephCopy != eeph);
898 TUASSERT(!(eephCopy == eeph));
899 // HOWtime[2]
900 TUCATCH(eephCopy = eeph);
901 TUASSERTE(EngEphemeris, eeph, eephCopy);
902 eephCopy.HOWtime[2] = 0xbeef;
903 TUASSERT(eephCopy != eeph);
904 TUASSERT(!(eephCopy == eeph));
905 // ASalert[0]
906 TUCATCH(eephCopy = eeph);
907 TUASSERTE(EngEphemeris, eeph, eephCopy);
908 eephCopy.ASalert[0] = 0xbe;
909 TUASSERT(eephCopy != eeph);
910 TUASSERT(!(eephCopy == eeph));
911 // ASalert[1]
912 TUCATCH(eephCopy = eeph);
913 TUASSERTE(EngEphemeris, eeph, eephCopy);
914 eephCopy.ASalert[1] = 0xbe;
915 TUASSERT(eephCopy != eeph);
916 TUASSERT(!(eephCopy == eeph));
917 // ASalert[2]
918 TUCATCH(eephCopy = eeph);
919 TUASSERTE(EngEphemeris, eeph, eephCopy);
920 eephCopy.ASalert[2] = 0xbe;
921 TUASSERT(eephCopy != eeph);
922 TUASSERT(!(eephCopy == eeph));
923 // weeknum
924 TUCATCH(eephCopy = eeph);
925 TUASSERTE(EngEphemeris, eeph, eephCopy);
926 eephCopy.weeknum = 1027;
927 TUASSERT(eephCopy != eeph);
928 TUASSERT(!(eephCopy == eeph));
929 // codeflags
930 TUCATCH(eephCopy = eeph);
931 TUASSERTE(EngEphemeris, eeph, eephCopy);
932 eephCopy.codeflags = 7;
933 TUASSERT(eephCopy != eeph);
934 TUASSERT(!(eephCopy == eeph));
935 // health
936 TUCATCH(eephCopy = eeph);
937 TUASSERTE(EngEphemeris, eeph, eephCopy);
938 eephCopy.health = 9;
939 TUASSERT(eephCopy != eeph);
940 TUASSERT(!(eephCopy == eeph));
941 // L2Pdata
942 TUCATCH(eephCopy = eeph);
943 TUASSERTE(EngEphemeris, eeph, eephCopy);
944 eephCopy.L2Pdata = 11;
945 TUASSERT(eephCopy != eeph);
946 TUASSERT(!(eephCopy == eeph));
947 // IODC
948 TUCATCH(eephCopy = eeph);
949 TUASSERTE(EngEphemeris, eeph, eephCopy);
950 eephCopy.IODC = 13;
951 TUASSERT(eephCopy != eeph);
952 TUASSERT(!(eephCopy == eeph));
953 // IODE
954 TUCATCH(eephCopy = eeph);
955 TUASSERTE(EngEphemeris, eeph, eephCopy);
956 eephCopy.IODE = 17;
957 TUASSERT(eephCopy != eeph);
958 TUASSERT(!(eephCopy == eeph));
959 // AODO
960 TUCATCH(eephCopy = eeph);
961 TUASSERTE(EngEphemeris, eeph, eephCopy);
962 eephCopy.AODO = 19;
963 TUASSERT(eephCopy != eeph);
964 TUASSERT(!(eephCopy == eeph));
965 // fitint
966 TUCATCH(eephCopy = eeph);
967 TUASSERTE(EngEphemeris, eeph, eephCopy);
968 eephCopy.fitint = 23;
969 TUASSERT(eephCopy != eeph);
970 TUASSERT(!(eephCopy == eeph));
971 // Tgd
972 TUCATCH(eephCopy = eeph);
973 TUASSERTE(EngEphemeris, eeph, eephCopy);
974 eephCopy.Tgd = 31;
975 TUASSERT(eephCopy != eeph);
976 TUASSERT(!(eephCopy == eeph));
977 TURETURN();
978 }
979
980 std::string testMesg;
981
982 private:
983 double eps;
984 double b10;
985 };
986
main()987 int main() //Main function to initialize and run all tests above
988 {
989 using namespace std;
990 EngEphemeris_T testClass;
991 unsigned errorTotal = 0;
992
993 // Used to have a running ephemeris for functions that only set
994 // part of it
995 gpstk::EngEphemeris dataStore;
996
997 errorTotal += testClass.initializationTest();
998
999 // setSF# require previous subframes to be set, and get needs a
1000 // valid ephemeris object
1001 // Passing one between the tests
1002 // If one of the setSF# tests fail, all other setSF# and get
1003 // tests will fail too
1004 errorTotal += testClass.setSF1Test(dataStore);
1005 errorTotal += testClass.setSF2Test(dataStore);
1006 errorTotal += testClass.setSF3Test(dataStore);
1007 errorTotal += testClass.getTest(dataStore);
1008 errorTotal += testClass.addSubframeNoParityTest();
1009 errorTotal += testClass.addSubframeTest();
1010 errorTotal += testClass.loadDataTest();
1011 errorTotal += testClass.addIncompleteTest();
1012 errorTotal += testClass.endOfWeekTest();
1013 errorTotal += testClass.equalityTest();
1014
1015 cout << "Total Failures for " << __FILE__ << ": " << errorTotal << endl;
1016
1017 return errorTotal; // Return the total number of errors
1018 }
1019