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 /**
40  * @file Synchronization.cpp
41  * Interpolate the phase data to correct for the clock offset, synchronizing the
42  * data at different stations; part of program DDBase.
43  */
44 
45 //------------------------------------------------------------------------------------
46 // TD Synchronization.cpp make number of phase points in fit an input parameter
47 // TD Synchronization.cpp make MaxGap=10; an input parameter and
48 // TD Synchronization.cpp use this in EditRawDataBuffers() to remove single points
49 // TD Synchronization.cpp   that have gaps larger than this on each side of them.
50 
51 //------------------------------------------------------------------------------------
52 // includes
53 // system
54 #include <deque>
55 #include "TimeString.hpp"
56 // GPSTk
57 #include "GNSSconstants.hpp"             // DEG_TO_RAD
58 #include "PolyFit.hpp"
59 #include "EphemerisRange.hpp"
60 // geomatics
61 #include "SunEarthSatGeometry.hpp"
62 #include "PhaseWindup.hpp"
63 #include "index.hpp"
64 // DDBase
65 #include "DDBase.hpp"
66 
67 //------------------------------------------------------------------------------------
68 using namespace std;
69 using namespace gpstk;
70 
71 //------------------------------------------------------------------------------------
72 // prototypes -- this module only -- called by Synchronization()
73 void FitPhaseAndMoveData(GSatID& sat, string site, Station& st, RawData& rd, int freq);
74 
75 //------------------------------------------------------------------------------------
Synchronization(void)76 int Synchronization(void)
77 {
78 try {
79    if(CI.Verbose) oflog << "BEGIN Synchronization()"
80       << " at total time " << fixed << setprecision(3)
81       << double(clock()-totaltime)/double(CLOCKS_PER_SEC) << " seconds."
82       << endl;
83 
84    GSatID sat;
85    map<string,Station>::iterator it;
86    map<GSatID,RawData>::iterator jt;
87 
88       // loop over stations
89    for(it=Stations.begin(); it != Stations.end(); it++) {
90       //string label = it->first;
91       Station& st=it->second;
92          // loop over satellites
93       for(jt=st.RawDataBuffers.begin(); jt != st.RawDataBuffers.end(); jt++) {
94          sat = jt->first;
95          RawData& rawdat=jt->second;
96 
97          if(rawdat.count.size() == 0) continue;
98 
99             // Loop over all points in the buffers, using a sliding window.
100             // For each window, fit a polynomial to the phase data.
101             // At each point, evaluate the polynomial at the true receive time.
102          if(CI.Frequency != 2)
103             FitPhaseAndMoveData(sat,it->first,st,rawdat,1);
104          if(CI.Frequency != 1)
105             FitPhaseAndMoveData(sat,it->first,st,rawdat,2);
106 
107       }  // loop over sats
108 
109    }  // loop over stations
110 
111    return 0;
112 }
113 catch(Exception& e) { GPSTK_RETHROW(e); }
114 catch(std::exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
115 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
116 }   // end Synchronization()
117 
118 //------------------------------------------------------------------------------------
119 // Process using a sliding window:
120 // loop over all points in the buffers of RawData, using a sliding window of fixed
121 // length which is centered (as much as possible) about the buffer point of interest.
122 // Process each buffer point using the data in the sliding window.
FitPhaseAndMoveData(GSatID & sat,string site,Station & statn,RawData & rawdat,int freq)123 void FitPhaseAndMoveData(GSatID& sat, string site, Station& statn, RawData& rawdat,
124       int freq)
125 {
126 try {
127    const int N=11;   // size of the window // best odd  // TD make input
128    const int D=3;    // degree of polynomial to be fit   // TD make input
129    bool change;      // mark a change in the deques --> fit a new polynomial
130    int nc;           // index into the buffer at the current point
131    int nbeg;         // index into the buffer at the start of the window
132    int nend;         // index into the buffer at the end of the window
133    int nhalf=N/2;    // half the window size
134    int len;          // length of the buffers
135    int ngap;         // number of counts between the end pt (nend) and the next
136    int nsize;        // size of the sliding window (deques)
137    int i,j;
138    double x,x0,d0,dx,dph;
139    PolyFit<double> PF;// fit polynomials to phase
140    deque<int> dc;     // the sliding window : time -- keep the deques
141    deque<double> dp;  // the sliding window : data -- parallel
142 
143    //if(CI.Verbose) oflog << "BEGIN FitPhasesAndMoveData() for site " << site
144    //   << " and sat " << sat << " at total time " << fixed << setprecision(3)
145    //   << double(clock()-totaltime)/double(CLOCKS_PER_SEC) << " seconds."
146    //   << endl;
147 
148       // starting: nend is before the current point (0)
149    nbeg = 0;
150    nend = -1;
151    change = true;
152    len = int(rawdat.count.size());  // length of the buffers
153 
154       // Loop over count (epochs). At each count, fill a 'sliding window' deque
155       // (one for each of count, L1 and L2) with up to N points, including
156       // containing the current count. The points run from index nbeg to nend.
157    for(nc=0; nc<len; nc++) {
158          // -------------------------------------------------------------
159          // the only way this could be true is if the current point is the
160          // first point past a big (>=MaxGap) gap
161       if(nc > nend) {
162             // clear window and start again
163          dc.clear();
164          dp.clear();
165          nbeg = nend = nc;
166          ngap = rawdat.count[nend+1]-rawdat.count[nend];
167          if(ngap >= CI.MaxGap) continue;        // skip this point if there's a gap
168          dc.push_back(rawdat.count[nend]);      // time / DataInterval
169          dp.push_back(freq == 1 ? rawdat.L1[nend] :
170                                   rawdat.L2[nend]);         // cycles
171          change = true;
172       }
173 
174          // -------------------------------------------------------------
175          // advance the end of the window (nend) when all these are true:
176       while(   (nend < len-1)       // point is not beyond the end of the buffer
177             && (nend-nbeg+1 < N)    // & the window is not full
178                                     // & there is not a big gap
179             && ((ngap = rawdat.count[nend+1]-rawdat.count[nend]) < CI.MaxGap)
180             && (nc >= nbeg)         // & the current point will stay in window
181             ) {
182             // expand the window one point into the future
183          nend++;
184          dc.push_back(rawdat.count[nend]);      // keep the deques parallel
185          dp.push_back(freq == 1 ? rawdat.L1[nend] :
186                                   rawdat.L2[nend]);
187          change = true;
188       };
189 
190          // -------------------------------------------------------------
191          // is this an isolated point?
192       //if(ngap >= CI.MaxGap) {
193       //   nc = nend+1;
194       //   continue;
195       //}
196 
197          // -------------------------------------------------------------
198          // Process the deques when a change has been made
199       if(change) {
200             // size of the sliding window (deques)
201          nsize = dc.size();
202 
203             // must not have isolated points
204             // EditRawBuffers should have removed these
205          if(nsize < 2) {
206             Exception e( (nsize == 0 ?
207                string("ERROR - empty window") : string("ERROR - isolated point"))
208                + string(" for station ") + site + string(" and satellite ")
209                + sat.toString()
210                + string(" at count ") + StringUtils::asString(rawdat.count[nc])
211                + string(" = time ")
212                + printTime((FirstEpoch + rawdat.count[nc]*CI.DataInterval),
213                   "%Y/%m/%d %H:%02M:%6.3f = %F/%10.3g"));
214             GPSTK_THROW(e);
215          }
216 
217          // fit a polynomial of degree D to the points in deques
218          PF.Reset(D<nsize ? D : nsize);
219 
220             // debias using the first point
221          x0 = double(dc[0]);
222          d0 = dp[0];
223 
224             // use all the data in the sliding window
225          for(i=0; i<nsize; i++) {
226                // x is nominal receive time in units of count (DataInterval)
227             x = double(dc[i]);
228                // find the same count in the station buffers
229             j = index(statn.CountBuffer,dc[i]);
230             //if(j == -1) ?? TD
231             x -= statn.RxTimeOffset[j]/CI.DataInterval;
232             PF.Add(dp[i]-d0,x-x0);
233          }
234 
235          change = false;
236 
237          //if(CI.Debug)
238          //   for(i=0; i<nsize; i++) {
239          //      x = double(dc[i]);         // count
240          //      j = index(statn.CountBuffer,dc[i]);
241          //      x -= statn.RxTimeOffset[j]/CI.DataInterval;
242          //      //PF.Add(dp[i]-d0,x-x0);
243          //      oflog << "FIT " << site << " " << sat
244          //         << " " << nc << " " << rawdat.count[nc]
245          //         << " " << (D<nsize?D:nsize) << " " << nsize
246          //         << fixed << setprecision(6)
247          //         << " " << nbeg+i << " " << dc[i] << " " << rawdat.count[nbeg+i]
248          //         << " " << x-x0 << " " << dp[i]-d0
249          //         << " " << PF.Evaluate(x-x0)
250          //         << " " << dp[i]-d0 - PF.Evaluate(x-x0)
251          //         << endl;
252          //}
253 
254       }  // end if change
255 
256          // -------------------------------------------------------------
257          // Process each point in the window/buffer
258          // correct L1,L2,P1,P2 using the polynomials and dt=RxTTOffset+clk/c
259          // statn.ClockBuffer contains raw PRS clock solution
260          // statn.RxTimeOffset contains SolutionEpoch - Rx timetag
261          //
262          // nominal time for point nc
263       x = double(rawdat.count[nc]);
264          // find the index of the same count in the station buffers
265       j = index(statn.CountBuffer,rawdat.count[nc]);
266          // time difference due to receiver clock, in units of count
267       dx =  statn.RxTimeOffset[j]/CI.DataInterval
268          + (statn.ClockBuffer[j]/C_MPS)/CI.DataInterval;
269          // change in phase between nominal and true time
270       dph = PF.Evaluate(x-x0) - PF.Evaluate(x-dx-x0);
271       if(freq == 1) {
272          rawdat.L1[nc] += dph;
273          rawdat.P1[nc] += dph * wl1;
274       }
275       else {
276          rawdat.L2[nc] += dph;
277          rawdat.P2[nc] += dph * wl2;
278       }
279 
280       //if(CI.Debug) oflog << "FIT " << site << " " << sat
281       //   << " " << nc << " " << rawdat.count[nc]
282       //   << fixed << setprecision(6)
283       //   << " " << x-x0 << " " << dx
284       //   << " " << statn.RxTimeOffset[nc]
285       //   << " " << statn.ClockBuffer[nc]/C_MPS
286       //   << " " << dph << " eval" << endl;
287 
288          // -------------------------------------------------------------
289          // remove old point(s) from the deques
290       while(   (nend < len-1)      // a new end point would not go beyond buffer
291             && (ngap < CI.MaxGap)  // & there would not be a big gap
292             && (nend-nbeg+1 > N-1) // & window is full
293             && (nc >= nbeg+nhalf)  // & current point is at mid-window or later
294             ) {
295          dc.pop_front();      // keep the deques parallel
296          dp.pop_front();
297          nbeg++;
298          change = true;
299       };
300 
301    }  // loop over counts
302 
303 }
304 catch(Exception& e) { GPSTK_RETHROW(e); }
305 catch(std::exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
306 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
307 } // end FitPhaseAndMoveData
308 
309 //------------------------------------------------------------------------------------
RecomputeFromEphemeris(void)310 int RecomputeFromEphemeris(void)
311 {
312 try {
313    if(CI.Verbose) oflog << "BEGIN RecomputeFromEphemeris()"
314       << " at total time " << fixed << setprecision(3)
315       << double(clock()-totaltime)/double(CLOCKS_PER_SEC) << " seconds."
316       << endl;
317 
318    size_t nc;
319    double angle,pwu,prevpwu,shadow;
320    CommonTime tt;
321    GSatID sat;
322    Position SV;
323    Position West,North,Rx2Tx;
324    CorrectedEphemerisRange CER;  // TD PreciseRange?
325    map<string,Station>::iterator it;
326    map<GSatID,RawData>::iterator jt;
327 
328       // loop over stations
329    for(it=Stations.begin(); it != Stations.end(); it++) {
330       //string label = it->first;
331       Station& statn=it->second;
332 
333       //if(CI.Verbose) oflog << " Station " << it->first
334       //   << " with " << statn.RawDataBuffers.size() << " raw buffers." << endl;
335 
336          // compute W and N unit vectors at this station,
337          // rotated by antenna azimuth angle
338       angle = statn.ant_azimuth * DEG_TO_RAD;
339       if(fabs(angle) > 0.0001) {    // also below..
340          Matrix<double> Rot;
341          Rot = SingleAxisRotation(angle,1) * UpEastNorth(statn.pos);
342          West = Position(-Rot(1,0),-Rot(1,1),-Rot(1,2));
343          North = Position(Rot(2,0),Rot(2,1),Rot(2,2));
344       }
345 
346          // loop over satellites
347       for(jt=statn.RawDataBuffers.begin(); jt!=statn.RawDataBuffers.end(); jt++) {
348          sat = jt->first;
349          RawData& rawdat=jt->second;
350 
351          //if(CI.Verbose) oflog << "   Satellite " << sat
352          //   << " with buffer size " << rawdat.count.size() << endl;
353 
354          if(rawdat.count.size() == 0) continue;
355 
356             // Loop over count (epochs). At each count, recompute the ephemeris
357             // range and correct the phase for phase windup.
358          prevpwu = 0.0;
359          for(nc=0; nc<rawdat.count.size(); nc++) {
360 
361                // nominal time is now the actual receive time of the data
362             tt = FirstEpoch + rawdat.count[nc] * CI.DataInterval;
363 
364                // try to get the ephemeris info
365             try {
366                   // update ephemeris range and elevation
367                rawdat.ER[nc] =
368                   CER.ComputeAtReceiveTime(tt, statn.pos, sat, *pEph);
369                rawdat.elev[nc] = CER.elevation;
370                rawdat.az[nc] = CER.azimuth;
371 
372                   // correct for phase windup
373                if(fabs(angle) > 0.0001) {    // also above..
374                      // get the receiver-to-transmitter unit vector
375                      // and the satellite position
376                   Rx2Tx = Position(CER.cosines);
377                   SV = Position(CER.svPosVel.x[0],
378                                 CER.svPosVel.x[1],
379                                 CER.svPosVel.x[2]);
380 
381                      // compute phase windup
382                   pwu = PhaseWindup(prevpwu,tt,SV,Rx2Tx,West,North,shadow);
383                   prevpwu = pwu;
384 
385                   // TD eclipse alert
386                   //if(shadow > 0.0) { ... }
387 
388                      // correct the phase
389                   rawdat.L1[nc] += pwu * wl1;
390                   rawdat.L2[nc] += pwu * wl2;
391                }
392             }
393             catch(InvalidRequest& e) {
394                // these should have been caught and removed before...
395                oflog << "Warning - No ephemeris found for sat " << sat
396                      << " at time "
397                      << printTime(tt,"%Y/%02m/%02d %2H:%02M:%6.3f=%F/%10.3g")
398                      << " in RecomputeFromEphemeris()" << endl;
399                rawdat.ER[nc] = 0.0;
400                rawdat.elev[nc] = -90.0;
401                rawdat.az[nc] = 0.0;
402             }
403          }  // end loop over counts
404       }  // loop over sats
405 
406    }  // loop over stations
407 
408    return 0;
409 }
410 catch(Exception& e) { GPSTK_RETHROW(e); }
411 catch(std::exception& e) { Exception E("std except: "+string(e.what())); GPSTK_THROW(E); }
412 catch(...) { Exception e("Unknown exception"); GPSTK_THROW(e); }
413 }
414 
415 //------------------------------------------------------------------------------------
416 //------------------------------------------------------------------------------------
417