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