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