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 /// @file ClockSatStore.cpp
40 /// Store a tabular list of clock data (bias, drift, accel) for several satellites,
41 /// and compute values at any timetag from this table. Inherits TabularSatStore.
42 
43 #include "ClockSatStore.hpp"
44 #include "MiscMath.hpp"
45 
46 using namespace std;
47 
48 namespace gpstk
49 {
50    // Output stream operator is used by dump() in TabularSatStore
operator <<(ostream & os,const ClockRecord & rec)51    ostream& operator<<(ostream& os, const ClockRecord& rec) throw()
52    {
53       os << scientific << setprecision(12) << setw(19) << rec.bias
54          << " " << setw(19) << rec.sig_bias
55          << " " << setw(19) << rec.drift
56          << " " << setw(19) << rec.sig_drift
57          //<< " " << setw(19) << rec.accel
58          //<< " " << setw(19) << rec.sig_accel
59          ;
60       return os;
61    }
62 
63    // Return value for the given satellite at the given time (usually via
64    // interpolation of the data table). This interface from TabularSatStore.
65    // @param[in] sat the SatID of the satellite of interest
66    // @param[in] ttag the time (CommonTime) of interest
67    // @return object of type ClockRecord containing the data value(s).
68    // @throw InvalidRequest if data value cannot be computed, for example because
69    //  a) the time t does not lie within the time limits of the data table
70    //  b) checkDataGap is true and there is a data gap
71    //  c) checkInterval is true and the interval is larger than maxInterval
getValue(const SatID & sat,const CommonTime & ttag) const72    ClockRecord ClockSatStore::getValue(const SatID& sat, const CommonTime& ttag)
73       const
74    {
75       try {
76          checkTimeSystem(ttag.getTimeSystem());
77 
78          bool isExact;
79          ClockRecord rec;
80          DataTableIterator it1, it2, kt;        // cf. TabularSatStore.hpp
81 
82          isExact = getTableInterval(sat, ttag, Nhalf, it1, it2, haveClockDrift);
83          if(isExact && haveClockDrift) {
84             rec = it1->second;
85             return rec;
86          }
87 
88          // pull data out of the data table
89          size_t n,Nlow(Nhalf-1),Nhi(Nhalf),Nmatch(Nhalf);
90          CommonTime ttag0(it1->first);
91          vector<double> times,biases,drifts,accels,sig_biases,sig_drifts,sig_accels;
92 
93          kt=it1; n=0;
94          while(1) {
95             // find index of matching time tag
96             if(isExact && ABS(kt->first-ttag) < 1.e-8) Nmatch = n;
97             times.push_back(kt->first - ttag0);    // sec
98             biases.push_back(kt->second.bias);     // sec
99             drifts.push_back(kt->second.drift);    // sec/sec
100             accels.push_back(kt->second.accel);    // sec/sec^2
101             sig_biases.push_back(kt->second.sig_bias);     // sec
102             sig_drifts.push_back(kt->second.sig_drift);    // sec/sec
103             sig_accels.push_back(kt->second.sig_accel);    // sec/sec^2
104             if(kt == it2) break;
105             ++kt;
106             ++n;
107          };
108 
109          if(isExact && Nmatch == (int)(Nhalf-1)) { Nlow++; Nhi++; }
110 
111          // interpolate
112          rec.accel = rec.sig_accel = 0.0;              // defaults
113          double dt(ttag-ttag0), err, slope;
114          if(haveClockDrift) {
115             if(interpType == 2) {
116                // Lagrange interpolation
117                rec.bias = LagrangeInterpolation(times,biases,dt,err);      // sec
118                rec.drift = LagrangeInterpolation(times,drifts,dt,err);     // sec/sec
119             }
120             else {
121                // linear interpolation
122                slope = (biases[Nhi]-biases[Nlow]) /
123                                    (times[Nhi]-times[Nlow]);               // sec/sec
124                rec.bias = biases[Nlow] + slope*(dt-times[Nlow]);           // sec
125                slope = (drifts[Nhi]-drifts[Nlow])/(times[Nhi]-times[Nlow]);
126                rec.drift = drifts[Nlow] + slope*(dt-times[Nlow]);          // sec/sec
127             }
128 
129             // sigmas
130             if(isExact)
131                rec.sig_bias = sig_biases[Nmatch];
132             else
133                rec.sig_bias = RSS(sig_biases[Nhi],sig_biases[Nlow]);
134             rec.sig_drift = RSS(sig_drifts[Nhi],sig_drifts[Nlow]);
135          }
136          else {                              // must interpolate biases to get drift
137             if(interpType == 2) {
138                // Lagrange interpolation
139                LagrangeInterpolation(times,biases,dt,rec.bias,rec.drift);
140             }
141             else {
142                // linear interpolation
143                rec.drift = (biases[Nhi]-biases[Nlow]) /
144                                    (times[Nhi]-times[Nlow]);            // sec/sec^2
145                rec.bias = biases[Nlow] + (dt-times[Nlow])*rec.drift;    // sec/sec
146             }
147 
148             // sigmas
149             if(isExact)
150                rec.sig_bias = sig_biases[Nmatch];
151             else
152                rec.sig_bias = RSS(sig_biases[Nhi],sig_biases[Nlow]);
153             // TD ?
154             rec.sig_drift = rec.sig_bias/(times[Nhi]-times[Nlow]);
155          }
156 
157          if(haveClockAccel) {
158             if(interpType == 2) {
159                // Lagrange interpolation
160                rec.accel = LagrangeInterpolation(times,accels,dt,err);  // sec/sec^2
161             }
162             else {
163                // linear interpolation
164                slope = (drifts[Nhi]-drifts[Nlow]) /
165                                    (times[Nhi]-times[Nlow]);            // sec/sec^2
166                rec.accel = accels[Nlow] + slope*(dt-times[Nlow]);       // sec/sec^2
167             }
168 
169             // sigma
170             if(isExact)
171                rec.sig_accel = sig_accels[Nmatch];
172             else
173                rec.sig_accel = RSS(sig_accels[Nhi],sig_accels[Nlow]);
174          }
175          else if(haveClockDrift) {              // must interpolate drift to get accel
176             if(interpType == 2) {
177                // Lagrange interpolation  (err is a dummy here)
178                LagrangeInterpolation(times,drifts,dt,err,rec.accel);
179             }
180             else {
181                // linear interpolation                                  // sec/sec^2
182                rec.accel = (drifts[Nhi]-drifts[Nlow]) / (times[Nhi]-times[Nlow]);
183             }
184 
185             // sigmas  TD is there a better way?
186             rec.sig_accel = rec.sig_drift/(times[Nhi]-times[Nlow]);
187          }
188          // else zero
189 
190          return rec;
191       }
192       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
193    }
194 
195    // Return the clock bias for the given satellite at the given time
196    // @param[in] sat the SatID of the satellite of interest
197    // @param[in] ttag the time (CommonTime) of interest
198    // @return double the clock bias
199    // @throw InvalidRequest if bias cannot be computed, for example because
200    //  a) the time t does not lie within the time limits of the data table
201    //  b) checkDataGap is true and there is a data gap
202    //  c) checkInterval is true and the interval is larger than maxInterval
getClockBias(const SatID & sat,const CommonTime & ttag) const203    double ClockSatStore::getClockBias(const SatID& sat, const CommonTime& ttag)
204       const
205    {
206       try {
207          checkTimeSystem(ttag.getTimeSystem());
208 
209          DataTableIterator it1, it2, kt;
210          if(getTableInterval(sat, ttag, Nhalf, it1, it2, true)) {
211             // exact match
212             ClockRecord rec;
213             rec = it1->second;
214             return rec.bias;
215          }
216 
217          // pull data out of the data table
218          vector<double> times,biases;
219          CommonTime ttag0(it1->first);
220          kt = it1;
221          while(1) {
222             times.push_back(kt->first - ttag0);    // sec
223             biases.push_back(kt->second.bias);     // sec
224             if(kt == it2) break;
225             ++kt;
226          };
227 
228          // interpolate
229          double bias, dt(ttag-ttag0), err, slope;
230          if(interpType == 2) {                     // Lagrange interpolation
231             bias = LagrangeInterpolation(times,biases,dt,err);    // sec
232          }
233          else {                                    // linear interpolation
234             slope = (biases[Nhalf]-biases[Nhalf-1])/(times[Nhalf]-times[Nhalf-1]);
235             bias = biases[Nhalf-1] + slope*(dt-times[Nhalf-1]);   // sec
236          }
237 
238          return bias;
239       }
240       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
241    }
242 
243    // Return the clock drift for the given satellite at the given time
244    // @param[in] sat the SatID of the satellite of interest
245    // @param[in] ttag the time (CommonTime) of interest
246    // @return double the clock drift
247    // @throw InvalidRequest if drift cannot be computed, for example because
248    //  a) the time t does not lie within the time limits of the data table
249    //  b) checkDataGap is true and there is a data gap
250    //  c) checkInterval is true and the interval is larger than maxInterval
251    //  d) there is no drift data in the store
getClockDrift(const SatID & sat,const CommonTime & ttag) const252    double ClockSatStore::getClockDrift(const SatID& sat, const CommonTime& ttag)
253       const
254    {
255       try {
256          checkTimeSystem(ttag.getTimeSystem());
257 
258          DataTableIterator it1, it2, kt;
259          bool isExact(getTableInterval(sat, ttag, Nhalf, it1, it2, haveClockDrift));
260          if(isExact && haveClockDrift) {
261             ClockRecord rec;
262             rec = it1->second;
263             return rec.drift;
264          }
265 
266          // pull data out of the data table
267          size_t n,Nhi(Nhalf);
268          CommonTime ttag0(it1->first);
269          vector<double> times,biases,drifts;
270 
271          n = 0;
272          kt = it1;
273          while(1) {
274             if(isExact && ABS(kt->first-ttag) < 1.e-8) Nhi=n;
275             times.push_back(kt->first - ttag0);    // sec
276             if(haveClockDrift)
277                drifts.push_back(kt->second.bias);  // sec/sec
278             else
279                biases.push_back(kt->second.bias);  // sec
280             if(kt == it2) break;
281             ++kt;
282             ++n;
283          };
284 
285          if(isExact && Nhi == (int)(Nhalf-1)) Nhi++;
286 
287          // interpolate
288          double drift, dt(ttag-ttag0), err, slope;
289          if(haveClockDrift) {
290             if(interpType == 2) {
291                // Lagrange interpolation
292                drift = LagrangeInterpolation(times,drifts,dt,err);         // sec/sec
293             }
294             else {
295                // linear interpolation
296                slope = (drifts[Nhi]-drifts[Nhi-1]) / (times[Nhi]-times[Nhi-1]);
297                drift = drifts[Nhi-1] + slope*(dt-times[Nhi-1]);            // sec/sec
298             }
299          }
300          else {
301             if(interpType == 2) {
302                // Lagrange interpolation // slope is dummy
303                LagrangeInterpolation(times,biases,dt,slope,drift);
304             }
305             else {
306                // linear interpolation
307                drift = (biases[Nhi]-biases[Nhi-1])/(times[Nhi]-times[Nhi-1]);//sec/sec
308             }
309          }
310 
311          return drift;
312       }
313       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
314    }
315 
316    // Add a ClockRecord to the store.
addClockRecord(const SatID & sat,const CommonTime & ttag,const ClockRecord & rec)317    void ClockSatStore::addClockRecord(const SatID& sat, const CommonTime& ttag,
318                                       const ClockRecord& rec)
319    {
320       try {
321          checkTimeSystem(ttag.getTimeSystem());
322 
323          if(rec.drift != 0.0) haveClockDrift = true;
324          if(rec.accel != 0.0) haveClockAccel = true;
325 
326          if(tables.find(sat) != tables.end() &&
327             tables[sat].find(ttag) != tables[sat].end()) {
328                // record already exists in the table
329             ClockRecord& oldrec(tables[sat][ttag]);
330             oldrec.bias = rec.bias;
331             oldrec.sig_bias = rec.sig_bias;
332             if(haveClockDrift) {
333                oldrec.drift = rec.drift;
334                oldrec.sig_drift = rec.sig_drift;
335             }
336             if(haveClockAccel) {
337                oldrec.accel = rec.accel;
338                oldrec.sig_accel = rec.sig_accel;
339             }
340          }
341          else  // create a new entry in the table
342             tables[sat][ttag] = rec;
343       }
344       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
345    }
346 
347    // Add clock bias (only) data to the store
addClockBias(const SatID & sat,const CommonTime & ttag,const double & bias,const double & sig)348    void ClockSatStore::addClockBias(const SatID& sat, const CommonTime& ttag,
349                                     const double& bias, const double& sig)
350    {
351       try {
352          checkTimeSystem(ttag.getTimeSystem());
353 
354          if(tables.find(sat) != tables.end() &&
355             tables[sat].find(ttag) != tables[sat].end()) {
356                   // record already exists in the table
357             ClockRecord& oldrec(tables[sat][ttag]);
358             oldrec.bias = bias;
359             oldrec.sig_bias = sig;
360          }
361          else {   // create a new entry in the table
362             ClockRecord rec;
363             rec.bias = bias;
364             rec.sig_bias = sig;
365             rec.drift = rec.sig_drift = 0.0;
366             rec.accel = rec.sig_accel = 0.0;
367 
368             tables[sat][ttag] = rec;
369          }
370       }
371       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
372    }
373 
374    // Add clock drift (only) data to the store
addClockDrift(const SatID & sat,const CommonTime & ttag,const double & drift,const double & sig)375    void ClockSatStore::addClockDrift(const SatID& sat, const CommonTime& ttag,
376                                     const double& drift, const double& sig)
377    {
378       try {
379          checkTimeSystem(ttag.getTimeSystem());
380 
381          haveClockDrift = true;
382 
383          if(tables.find(sat) != tables.end() &&
384             tables[sat].find(ttag) != tables[sat].end()) {
385                   // record already exists in the table
386             ClockRecord& oldrec(tables[sat][ttag]);
387             oldrec.drift = drift;
388             oldrec.sig_drift = sig;
389          }
390          else {   // create a new entry in the table
391             ClockRecord rec;
392             rec.drift = drift;
393             rec.sig_drift = sig;
394             rec.bias = rec.sig_bias = 0.0;
395             rec.accel = rec.sig_accel = 0.0;
396 
397             tables[sat][ttag] = rec;
398          }
399       }
400       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
401    }
402 
403    // Add clock acceleration (only) data to the store
addClockAcceleration(const SatID & sat,const CommonTime & ttag,const double & accel,const double & sig)404    void ClockSatStore::addClockAcceleration(const SatID& sat, const CommonTime& ttag,
405                                     const double& accel, const double& sig)
406    {
407       try {
408          checkTimeSystem(ttag.getTimeSystem());
409 
410          haveClockAccel = true;
411 
412          if(tables.find(sat) != tables.end() &&
413             tables[sat].find(ttag) != tables[sat].end()) {
414                   // record already exists in the table
415             ClockRecord& oldrec(tables[sat][ttag]);
416             oldrec.accel = accel;
417             oldrec.sig_accel = sig;
418          }
419          else {   // create a new entry in the table
420             ClockRecord rec;
421             rec.accel = accel;
422             rec.sig_accel = sig;
423             rec.drift = rec.sig_drift = 0.0;
424             rec.bias = rec.sig_bias = 0.0;
425 
426             tables[sat][ttag] = rec;
427          }
428       }
429       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
430    }
431 
432 }  // End of namespace gpstk
433