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 PositionSatStore.cpp
40 // Store a tabular list of ephemeris data (position, option velocity & acceleration)
41 // for several satellites, and compute values at any timetag from this table.
42 // Inherits TabularSatStore.
43 
44 #include "PositionSatStore.hpp"
45 #include "MiscMath.hpp"
46 #include <vector>
47 
48 using namespace std;
49 
50 namespace gpstk
51 {
52    /** @addtogroup ephemstore */
53    //@{
54 
55    // Output stream operator is used by dump() in TabularSatStore
operator <<(ostream & os,const PositionRecord & rec)56    ostream& operator<<(ostream& os, const PositionRecord& rec) throw()
57    {
58       os << "Pos" << fixed << setprecision(6)
59          << " " << setw(13) << rec.Pos[0]
60          << " " << setw(13) << rec.Pos[1]
61          << " " << setw(13) << rec.Pos[2]
62          << " sigP" << scientific << setprecision(2)
63          << " " << setw(9) << rec.sigPos[0]
64          << " " << setw(9) << rec.sigPos[1]
65          << " " << setw(9) << rec.sigPos[2]
66          << " Vel" << fixed << setprecision(6)
67          << " " << setw(13) << rec.Vel[0]
68          << " " << setw(13) << rec.Vel[1]
69          << " " << setw(13) << rec.Vel[2]
70          << " sigV" << scientific << setprecision(2)
71          << " " << setw(9) << rec.sigVel[0]
72          << " " << setw(9) << rec.sigVel[1]
73          << " " << setw(9) << rec.sigVel[2]
74          //<< " Acc" << fixed << setprecision(6)
75          //<< " " << setw(13) << rec.Acc[0]
76          //<< " " << setw(13) << rec.Acc[1]
77          //<< " " << setw(13) << rec.Acc[2]
78          //<< " sigA" << scientific << setprecision(2)
79          //<< " " << setw(9) << rec.sigAcc[0]
80          //<< " " << setw(9) << rec.sigAcc[1]
81          //<< " " << setw(9) << rec.sigAcc[2]
82          ;
83       return os;
84    }
85 
86    // Return value for the given satellite at the given time (usually via
87    // interpolation of the data table). This interface from TabularSatStore.
88    // @param[in] sat the SatID of the satellite of interest
89    // @param[in] ttag the time (CommonTime) of interest
90    // @return object of type PositionRecord containing the data value(s).
91    // @throw InvalidRequest if data value cannot be computed, for example because
92    //  a) the time t does not lie within the time limits of the data table
93    //  b) checkDataGap is true and there is a data gap
94    //  c) checkInterval is true and the interval is larger than maxInterval
getValue(const SatID & sat,const CommonTime & ttag) const95    PositionRecord PositionSatStore::getValue(const SatID& sat, const CommonTime& ttag)
96       const
97    {
98       try {
99          bool isExact;
100          int i;
101          PositionRecord rec;
102          DataTableIterator it1, it2, kt;        // cf. TabularSatStore.hpp
103 
104          isExact = getTableInterval(sat, ttag, Nhalf, it1, it2, haveVelocity);
105          if(isExact && haveVelocity) {
106             rec = it1->second;
107             return rec;
108          }
109 
110          // pull data out of the data table
111          size_t n,Nlow(Nhalf-1),Nhi(Nhalf),Nmatch(Nhalf);
112          CommonTime ttag0(it1->first);
113          vector<double> times,P[3],V[3],A[3],sigP[3],sigV[3],sigA[3];
114 
115          kt = it1; n=0;
116          while(1) {
117             // find index matching ttag
118             if(isExact && ABS(kt->first - ttag) < 1.e-8)
119                Nmatch = n;
120             times.push_back(kt->first - ttag0);          // sec
121             for(i=0; i<3; i++) {
122                P[i].push_back(kt->second.Pos[i]);
123                V[i].push_back(kt->second.Vel[i]);
124                A[i].push_back(kt->second.Acc[i]);
125                sigP[i].push_back(kt->second.sigPos[i]);
126                sigV[i].push_back(kt->second.sigVel[i]);
127                sigA[i].push_back(kt->second.sigAcc[i]);
128             }
129             if(kt == it2) break;
130             ++kt;
131             ++n;
132          };
133 
134          if(isExact && Nmatch == (int)(Nhalf-1)) { Nlow++; Nhi++; }
135 
136          // Lagrange interpolation
137          rec.sigAcc = rec.Acc = Triple(0,0,0);        // default
138          double dt(ttag-ttag0), err;           // dt in seconds
139          if(haveVelocity) {
140             for(i=0; i<3; i++) {
141                // interpolate the positions
142                rec.Pos[i] = LagrangeInterpolation(times,P[i],dt,err);
143                if(haveAcceleration) {
144                   // interpolate velocities and acclerations
145                   rec.Vel[i] = LagrangeInterpolation(times,V[i],dt,err);
146                   rec.Acc[i] = LagrangeInterpolation(times,A[i],dt,err);
147                }
148                else {
149                   // interpolate velocities(dm/s) to get V and A
150                   LagrangeInterpolation(times,V[i],dt,rec.Vel[i],rec.Acc[i]);
151                   rec.Acc[i] *= 0.1;      // dm/s/s -> m/s/s
152                }
153 
154                if(isExact) {
155                   rec.sigPos[i] = sigP[i][Nmatch];
156                   rec.sigVel[i] = sigV[i][Nmatch];
157                   if(haveAcceleration) rec.sigAcc[i] = sigA[i][Nmatch];
158                }
159                else {
160                   // TD is this sigma related to 'err' in the Lagrange call?
161                   rec.sigPos[i] = RSS(sigP[i][Nhi],sigP[i][Nlow]);
162                   rec.sigVel[i] = RSS(sigV[i][Nhi],sigV[i][Nlow]);
163                   if(haveAcceleration)
164                      rec.sigAcc[i] = RSS(sigA[i][Nhi],sigA[i][Nlow]);
165                }
166                // else Acc=sig_Acc=0   // TD can we do better?
167             }
168          }
169          else {               // no V data - must interpolate position to get velocity
170             for(i=0; i<3; i++) {
171                // interpolate positions(km) to get P and V
172                LagrangeInterpolation(times,P[i],dt,rec.Pos[i],rec.Vel[i]);
173                rec.Vel[i] *= 10000.;         // km/sec -> dm/sec
174 
175                if(isExact) {
176                   rec.sigPos[i] = sigP[i][Nmatch];
177                }
178                else {
179                   rec.sigPos[i] = RSS(sigP[i][Nhi],sigP[i][Nlow]);
180                }
181                // TD
182                rec.sigVel[i] = 0.0;
183             }
184          }
185          return rec;
186       }
187       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
188    }
189 
190    // Return the position for the given satellite at the given time
191    // @param[in] sat the SatID of the satellite of interest
192    // @param[in] ttag the time (CommonTime) of interest
193    // @return Triple containing the position ECEF XYZ meters
194    // @throw InvalidRequest if result cannot be computed, for example because
195    //  a) the time t does not lie within the time limits of the data table
196    //  b) checkDataGap is true and there is a data gap
197    //  c) checkInterval is true and the interval is larger than maxInterval
getPosition(const SatID & sat,const CommonTime & ttag) const198    Triple PositionSatStore::getPosition(const SatID& sat, const CommonTime& ttag)
199       const
200    {
201       try {
202          int i;
203          DataTableIterator it1, it2, kt;
204 
205          if(getTableInterval(sat, ttag, Nhalf, it1, it2, true)) {
206             // exact match
207             for(unsigned int i=0; i<Nhalf; i++) ++it1;
208             PositionRecord rec(it1->second);
209             return rec.Pos;
210          }
211 
212          // pull data out of the data table
213          vector<double> times,P[3];
214          CommonTime ttag0(it1->first);
215          kt = it1;
216          while(1) {
217             times.push_back(kt->first - ttag0);    // sec
218             for(i=0; i<3; i++)
219                P[i].push_back(kt->second.Pos[i]);
220             if(kt == it2) break;
221             ++kt;
222          };
223 
224          // interpolate
225          Triple pos;
226          double dt(ttag-ttag0), err;
227          for(i=0; i<3; i++)
228             pos[i] = LagrangeInterpolation(times,P[i],dt,err);
229 
230          return pos;
231       }
232       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
233    }
234 
235    // Return the velocity for the given satellite at the given time
236    // @param[in] sat the SatID of the satellite of interest
237    // @param[in] ttag the time (CommonTime) of interest
238    // @return Triple containing the velocity ECEF XYZ meters/second
239    // @throw InvalidRequest if result cannot be computed, for example because
240    //  a) the time t does not lie within the time limits of the data table
241    //  b) checkDataGap is true and there is a data gap
242    //  c) checkInterval is true and the interval is larger than maxInterval
getVelocity(const SatID & sat,const CommonTime & ttag) const243    Triple PositionSatStore::getVelocity(const SatID& sat, const CommonTime& ttag)
244       const
245    {
246       try {
247          int i;
248          DataTableIterator it1, it2, kt;
249 
250          bool isExact(getTableInterval(sat, ttag, Nhalf, it1, it2, haveVelocity));
251          if(isExact && haveVelocity) {
252             for(unsigned int i=0; i<Nhalf; i++) ++it1;
253             PositionRecord rec(it1->second);
254             return rec.Vel;
255          }
256 
257          // pull data out of the data table
258          CommonTime ttag0(it1->first);
259          vector<double> times,D[3];       // D will be either Pos or Vel
260 
261          kt = it1;
262          while(1) {
263             times.push_back(kt->first - ttag0);    // sec
264             for(i=0; i<3; i++)
265                D[i].push_back(haveVelocity ? kt->second.Vel[i] : kt->second.Pos[i]);
266             if(kt == it2) break;
267             ++kt;
268          };
269 
270          // interpolate
271          Triple Vel;
272          double dt(ttag-ttag0), err;
273          for(i=0; i<3; i++) {
274             if(haveVelocity)
275                Vel[i] = LagrangeInterpolation(times,D[i],dt,err);
276             else {
277                // interpolate positions(km) to get velocity // err is dummy
278                LagrangeInterpolation(times,D[i],dt,err,Vel[i]);
279                Vel[i] *= 10000.;                                  // km/s -> dm/s
280             }
281          }
282 
283          return Vel;
284       }
285       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
286    }
287 
288    // Return the acceleration for the given satellite at the given time
289    // @param[in] sat the SatID of the satellite of interest
290    // @param[in] ttag the time (CommonTime) of interest
291    // @return Triple containing the acceleration ECEF XYZ meters/second/second
292    // @throw InvalidRequest if result cannot be computed, for example because
293    //  a) the time t does not lie within the time limits of the data table
294    //  b) checkDataGap is true and there is a data gap
295    //  c) checkInterval is true and the interval is larger than maxInterval
296    //  d) neither velocity nor acceleration data are present
getAcceleration(const SatID & sat,const CommonTime & ttag) const297    Triple PositionSatStore::getAcceleration(const SatID& sat, const CommonTime& ttag)
298       const
299    {
300       if(!haveVelocity && !haveAcceleration) {
301          InvalidRequest e("Neither velocity nor acceleration data are present");
302          GPSTK_THROW(e);
303       }
304 
305       try {
306          int i;
307          DataTableIterator it1, it2, kt;
308 
309          bool isExact(getTableInterval(sat,ttag,Nhalf,it1,it2,haveAcceleration));
310          if(isExact && haveAcceleration) {
311             // exact match, and have acceleration data
312             for(unsigned int i=0; i<Nhalf; i++) ++it1;
313             PositionRecord rec(it1->second);
314             return rec.Acc;
315          }
316 
317          // pull data out of the data table
318          vector<double> times,D[3];                // D will be either Vel or Acc
319          CommonTime ttag0(it1->first);
320          kt = it1;
321          while(1) {
322             times.push_back(kt->first - ttag0);    // sec
323             for(i=0; i<3; i++)
324                D[i].push_back(haveAcceleration ? kt->second.Acc[i]:kt->second.Vel[i]);
325             if(kt == it2) break;
326             ++kt;
327          };
328 
329          // interpolate
330          Triple Acc;
331          double dt(ttag-ttag0), err;
332          for(i=0; i<3; i++) {
333             if(haveAcceleration) {
334                Acc[i] = LagrangeInterpolation(times,D[i],dt,err);
335             }
336             else {
337                LagrangeInterpolation(times,D[i],dt,err,Acc[i]);   // err is dummy
338                Acc[i] *= 0.1;                                     // dm/s/s -> m/s/s
339             }
340          }
341 
342          return Acc;
343       }
344       catch(InvalidRequest& e) { GPSTK_RETHROW(e); }
345    }
346 
347    // Add a PositionRecord to the store.
addPositionRecord(const SatID & sat,const CommonTime & ttag,const PositionRecord & rec)348    void PositionSatStore::addPositionRecord(const SatID& sat, const CommonTime& ttag,
349                                             const PositionRecord& rec)
350    {
351       try {
352          checkTimeSystem(ttag.getTimeSystem());
353 
354          int i;
355          if(!haveVelocity)
356             for(i=0; i<3; i++)
357                if(rec.Vel[i] != 0.0) { haveVelocity = true; break; }
358          if(!haveAcceleration)
359             for(i=0; i<3; i++)
360                if(rec.Acc[i] != 0.0) { haveAcceleration = true; break; }
361 
362          if(tables.find(sat) != tables.end() &&
363             tables[sat].find(ttag) != tables[sat].end()) {
364                   // record already exists in table
365             PositionRecord& oldrec(tables[sat][ttag]);
366             oldrec.Pos = rec.Pos;
367             oldrec.sigPos = rec.sigPos;
368             if(haveVelocity) { oldrec.Vel = rec.Vel; oldrec.sigVel = rec.sigVel; }
369             if(haveAcceleration) { oldrec.Acc = rec.Acc; oldrec.sigAcc = rec.sigAcc; }
370          }
371          else {   // create a new entry in the table
372             tables[sat][ttag] = rec;
373          }
374       }
375       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
376    }
377 
378    // Add position data (only) to the store
addPositionData(const SatID & sat,const CommonTime & ttag,const Triple & Pos,const Triple & Sig)379    void PositionSatStore::addPositionData(const SatID& sat, const CommonTime& ttag,
380                      const Triple& Pos, const Triple& Sig)
381    {
382       try {
383          checkTimeSystem(ttag.getTimeSystem());
384 
385          if(tables.find(sat) != tables.end() &&
386             tables[sat].find(ttag) != tables[sat].end()) {
387                   // record already exists in table
388             PositionRecord& oldrec(tables[sat][ttag]);
389             oldrec.Pos = Pos;
390             oldrec.sigPos = Sig;
391          }
392          else {   // create a new entry in the table
393             PositionRecord rec;
394             rec.Pos = Pos;
395             rec.sigPos = Sig;
396             rec.Vel = rec.sigVel = rec.Acc = rec.sigAcc = Triple(0,0,0);
397 
398             tables[sat][ttag] = rec;
399          }
400       }
401       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
402    }
403 
404    // Add velocity data (only) to the store
addVelocityData(const SatID & sat,const CommonTime & ttag,const Triple & Vel,const Triple & Sig)405    void PositionSatStore::addVelocityData(const SatID& sat, const CommonTime& ttag,
406                         const Triple& Vel, const Triple& Sig)
407    {
408       try {
409          checkTimeSystem(ttag.getTimeSystem());
410 
411          haveVelocity = true;
412 
413          if(tables.find(sat) != tables.end() &&
414             tables[sat].find(ttag) != tables[sat].end()) {
415                   // record already exists in table
416             PositionRecord& oldrec(tables[sat][ttag]);
417             oldrec.Vel = Vel;
418             oldrec.sigVel = Sig;
419          }
420          else {   // create a new entry in the table
421             PositionRecord rec;
422             rec.Vel = Vel;
423             rec.sigVel = Sig;
424             rec.Pos = rec.sigPos = rec.Acc = rec.sigAcc = Triple(0,0,0);
425 
426             tables[sat][ttag] = rec;
427          }
428       }
429       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
430    }
431 
432    // Add acceleration data (only) to the store
addAccelerationData(const SatID & sat,const CommonTime & ttag,const Triple & Acc,const Triple & Sig)433    void PositionSatStore::addAccelerationData(const SatID& sat,
434                         const CommonTime& ttag, const Triple& Acc, const Triple& Sig)
435    {
436       try {
437          checkTimeSystem(ttag.getTimeSystem());
438 
439          haveAcceleration = true;
440 
441          if(tables.find(sat) != tables.end() &&
442             tables[sat].find(ttag) != tables[sat].end()) {
443                   // record already exists in table
444             PositionRecord& oldrec(tables[sat][ttag]);
445             oldrec.Acc = Acc;
446             oldrec.sigAcc = Sig;
447          }
448          else {   // create a new entry in the table
449             PositionRecord rec;
450             rec.Acc = Acc;
451             rec.sigAcc = Sig;
452             rec.Vel = rec.sigVel = rec.Pos = rec.sigPos = Triple(0,0,0);
453 
454             tables[sat][ttag] = rec;
455          }
456       }
457       catch(InvalidRequest& ir) { GPSTK_RETHROW(ir); }
458    }
459 
460    //@}
461 
462 }  // End of namespace gpstk
463