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