source: ntrip/trunk/BNC/combination/bnccomb.cpp@ 3446

Last change on this file since 3446 was 3446, checked in by mervart, 13 years ago
File size: 19.5 KB
Line 
1/* -------------------------------------------------------------------------
2 * BKG NTRIP Client
3 * -------------------------------------------------------------------------
4 *
5 * Class: bncComb
6 *
7 * Purpose: Combinations of Orbit/Clock Corrections
8 *
9 * Author: L. Mervart
10 *
11 * Created: 22-Jan-2011
12 *
13 * Changes:
14 *
15 * -----------------------------------------------------------------------*/
16
17#include <newmatio.h>
18#include <iomanip>
19#include <sstream>
20
21#include "bnccomb.h"
22#include "bncapp.h"
23#include "upload/bncrtnetdecoder.h"
24#include "bncsettings.h"
25#include "bncmodel.h"
26#include "bncutils.h"
27#include "bncpppclient.h"
28#include "bncsp3.h"
29#include "bncantex.h"
30#include "bnctides.h"
31
32const int moduloTime = 10;
33
34const double sig0_offAC = 1000.0;
35const double sig0_offACSat = 100.0;
36const double sigP_offACSat = 0.0;
37const double sig0_clkSat = 10.0;
38const double sigP_clkSat = 10.0;
39
40const double sigObs = 0.05;
41
42const int MAXPRN_GPS = 32;
43
44using namespace std;
45
46// Constructor
47////////////////////////////////////////////////////////////////////////////
48cmbParam::cmbParam(parType type_, int index_,
49 const QString& ac_, const QString& prn_) {
50
51 type = type_;
52 index = index_;
53 AC = ac_;
54 prn = prn_;
55 xx = 0.0;
56 eph = 0;
57
58 if (type == offAC) {
59 epoSpec = true;
60 sig0 = sig0_offAC;
61 sigP = sig0;
62 }
63 else if (type == offACSat) {
64 epoSpec = false;
65 sig0 = sig0_offACSat;
66 sigP = sigP_offACSat;
67 }
68 else if (type == clkSat) {
69 epoSpec = false;
70 sig0 = sig0_clkSat;
71 sigP = sigP_clkSat;
72 }
73}
74
75// Destructor
76////////////////////////////////////////////////////////////////////////////
77cmbParam::~cmbParam() {
78}
79
80// Partial
81////////////////////////////////////////////////////////////////////////////
82double cmbParam::partial(const QString& AC_, const QString& prn_) {
83
84 if (type == offAC) {
85 if (AC == AC_) {
86 return 1.0;
87 }
88 }
89 else if (type == offACSat) {
90 if (AC == AC_ && prn == prn_) {
91 return 1.0;
92 }
93 }
94 else if (type == clkSat) {
95 if (prn == prn_) {
96 return 1.0;
97 }
98 }
99
100 return 0.0;
101}
102
103//
104////////////////////////////////////////////////////////////////////////////
105QString cmbParam::toString() const {
106
107 QString outStr;
108
109 if (type == offAC) {
110 outStr = "AC offset " + AC;
111 }
112 else if (type == offACSat) {
113 outStr = "Sat Offset " + AC + " " + prn;
114 }
115 else if (type == clkSat) {
116 outStr = "Clk Corr " + prn;
117 }
118
119 return outStr;
120}
121
122// Constructor
123////////////////////////////////////////////////////////////////////////////
124bncComb::bncComb() {
125
126 bncSettings settings;
127
128 QStringList combineStreams = settings.value("combineStreams").toStringList();
129
130 if (combineStreams.size() >= 1 && !combineStreams[0].isEmpty()) {
131 QListIterator<QString> it(combineStreams);
132 while (it.hasNext()) {
133 QStringList hlp = it.next().split(" ");
134 cmbAC* newAC = new cmbAC();
135 newAC->mountPoint = hlp[0];
136 newAC->name = hlp[1];
137 newAC->weight = hlp[2].toDouble();
138 _ACs.append(newAC);
139 }
140 }
141
142 _rtnetDecoder = 0;
143
144 connect(this, SIGNAL(newMessage(QByteArray,bool)),
145 ((bncApp*)qApp), SLOT(slotMessage(const QByteArray,bool)));
146
147
148 // Initialize Parameters (model: Clk_Corr = AC_Offset + Sat_Offset + Clk)
149 // ----------------------------------------------------------------------
150 int nextPar = 0;
151 QListIterator<cmbAC*> it(_ACs);
152 while (it.hasNext()) {
153 cmbAC* AC = it.next();
154 _params.push_back(new cmbParam(cmbParam::offAC, ++nextPar, AC->name, ""));
155 for (int iGps = 1; iGps <= MAXPRN_GPS; iGps++) {
156 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
157 _params.push_back(new cmbParam(cmbParam::offACSat, ++nextPar,
158 AC->name, prn));
159 }
160 }
161 for (int iGps = 1; iGps <= MAXPRN_GPS; iGps++) {
162 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
163 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
164 }
165
166 // Initialize Variance-Covariance Matrix
167 // -------------------------------------
168 _QQ.ReSize(_params.size());
169 _QQ = 0.0;
170 for (int iPar = 1; iPar <= _params.size(); iPar++) {
171 cmbParam* pp = _params[iPar-1];
172 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
173 }
174
175 // ANTEX File
176 // ----------
177 _antex = 0;
178 QString antexFileName = settings.value("pppAntex").toString();
179 if (!antexFileName.isEmpty()) {
180 _antex = new bncAntex();
181 if (_antex->readFile(antexFileName) != success) {
182 emit newMessage("wrong ANTEX file", true);
183 delete _antex;
184 _antex = 0;
185 }
186 }
187
188 // Maximal Residuum
189 // ----------------
190 _MAXRES = settings.value("cmbMaxres").toDouble();
191 if (_MAXRES <= 0.0) {
192 _MAXRES = 999.0;
193 }
194}
195
196// Destructor
197////////////////////////////////////////////////////////////////////////////
198bncComb::~bncComb() {
199 QListIterator<cmbAC*> icAC(_ACs);
200 while (icAC.hasNext()) {
201 delete icAC.next();
202 }
203 delete _rtnetDecoder;
204 delete _antex;
205 for (int iPar = 1; iPar <= _params.size(); iPar++) {
206 delete _params[iPar-1];
207 }
208 QVectorIterator<cmbCorr*> itCorr(corrs());
209 while (itCorr.hasNext()) {
210 delete itCorr.next();
211 }
212}
213
214// Read and store one correction line
215////////////////////////////////////////////////////////////////////////////
216void bncComb::processCorrLine(const QString& staID, const QString& line) {
217 QMutexLocker locker(&_mutex);
218
219 // Find the AC Name
220 // ----------------
221 QString acName;
222 QListIterator<cmbAC*> icAC(_ACs);
223 while (icAC.hasNext()) {
224 cmbAC* AC = icAC.next();
225 if (AC->mountPoint == staID) {
226 acName = AC->name;
227 break;
228 }
229 }
230 if (acName.isEmpty()) {
231 return;
232 }
233
234 // Read the Correction
235 // -------------------
236 cmbCorr* newCorr = new cmbCorr();
237 newCorr->acName = acName;
238 if (!newCorr->readLine(line) == success) {
239 delete newCorr;
240 return;
241 }
242
243 // Check Modulo Time
244 // -----------------
245 if (int(newCorr->tt.gpssec()) % moduloTime != 0.0) {
246 delete newCorr;
247 return;
248 }
249
250 // Delete old corrections
251 // ----------------------
252 if (_resTime.valid() && newCorr->tt <= _resTime) {
253 delete newCorr;
254 return;
255 }
256
257 // Check the Ephemeris
258 //--------------------
259 if (_eph.find(newCorr->prn) == _eph.end()) {
260 delete newCorr;
261 return;
262 }
263 else {
264 t_eph* lastEph = _eph[newCorr->prn]->last;
265 t_eph* prevEph = _eph[newCorr->prn]->prev;
266 if (lastEph && lastEph->IOD() == newCorr->iod) {
267 newCorr->eph = lastEph;
268 }
269 else if (prevEph && prevEph->IOD() == newCorr->iod) {
270 newCorr->eph = prevEph;
271 switchToLastEph(lastEph, newCorr);
272 }
273 else {
274 delete newCorr;
275 return;
276 }
277 }
278
279 // Process previous Epoch(s)
280 // -------------------------
281 QListIterator<bncTime> itTime(_buffer.keys());
282 while (itTime.hasNext()) {
283 bncTime epoTime = itTime.next();
284 if (epoTime < newCorr->tt - moduloTime) {
285 _resTime = epoTime;
286 processEpoch();
287 }
288 }
289
290 // Merge or add the correction
291 // ---------------------------
292 QVector<cmbCorr*>& corrs = _buffer[newCorr->tt].corrs;
293 cmbCorr* existingCorr = 0;
294 QVectorIterator<cmbCorr*> itCorr(corrs);
295 while (itCorr.hasNext()) {
296 cmbCorr* hlp = itCorr.next();
297 if (hlp->prn == newCorr->prn && hlp->acName == newCorr->prn) {
298 existingCorr = hlp;
299 break;
300 }
301 }
302 if (existingCorr) {
303 delete newCorr;
304 existingCorr->readLine(line); // merge (multiple messages)
305 }
306 else {
307 corrs.append(newCorr);
308 }
309}
310
311// Change the correction so that it refers to last received ephemeris
312////////////////////////////////////////////////////////////////////////////
313void bncComb::switchToLastEph(const t_eph* lastEph, t_corr* corr) {
314
315 if (corr->eph == lastEph) {
316 return;
317 }
318
319 ColumnVector oldXC(4);
320 ColumnVector oldVV(3);
321 corr->eph->position(corr->tt.gpsw(), corr->tt.gpssec(),
322 oldXC.data(), oldVV.data());
323
324 ColumnVector newXC(4);
325 ColumnVector newVV(3);
326 lastEph->position(corr->tt.gpsw(), corr->tt.gpssec(),
327 newXC.data(), newVV.data());
328
329 ColumnVector dX = newXC.Rows(1,3) - oldXC.Rows(1,3);
330 ColumnVector dV = newVV - oldVV;
331 double dC = newXC(4) - oldXC(4);
332
333 ColumnVector dRAO(3);
334 XYZ_to_RSW(newXC.Rows(1,3), newVV, dX, dRAO);
335
336 ColumnVector dDotRAO(3);
337 XYZ_to_RSW(newXC.Rows(1,3), newVV, dV, dDotRAO);
338
339 QString msg = "switch corr " + corr->prn
340 + QString(" %1 -> %2 %3").arg(corr->iod,3)
341 .arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
342
343 emit newMessage(msg.toAscii(), false);
344
345 corr->iod = lastEph->IOD();
346 corr->eph = lastEph;
347 corr->rao += dRAO;
348 corr->dotRao += dDotRAO;
349 corr->dClk -= dC;
350}
351
352// Process Epoch
353////////////////////////////////////////////////////////////////////////////
354void bncComb::processEpoch() {
355
356 int nPar = _params.size();
357
358 _log.clear();
359
360 QTextStream out(&_log, QIODevice::WriteOnly);
361
362 out << endl << "Combination:" << endl
363 << "------------------------------" << endl;
364
365 // Observation Statistics
366 // ----------------------
367 QListIterator<cmbAC*> icAC(_ACs);
368 while (icAC.hasNext()) {
369 cmbAC* AC = icAC.next();
370 AC->numObs = 0;
371 QVectorIterator<cmbCorr*> itCorr(corrs());
372 while (itCorr.hasNext()) {
373 cmbCorr* corr = itCorr.next();
374 if (corr->acName == AC->name) {
375 AC->numObs += 1;
376 }
377 }
378 out << AC->name.toAscii().data() << ": " << AC->numObs << endl;
379 }
380
381 // Prediction Step
382 // ---------------
383 ColumnVector x0(nPar);
384 for (int iPar = 1; iPar <= _params.size(); iPar++) {
385 cmbParam* pp = _params[iPar-1];
386 QString prn = pp->prn;
387 if (!prn.isEmpty() && _eph.find(prn) != _eph.end()) {
388 switchToLastEph(_eph[prn]->last, pp);
389 }
390 if (pp->epoSpec) {
391 pp->xx = 0.0;
392 _QQ.Row(iPar) = 0.0;
393 _QQ.Column(iPar) = 0.0;
394 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
395 }
396 else {
397 _QQ(iPar,iPar) += pp->sigP * pp->sigP;
398 }
399 x0(iPar) = pp->xx;
400 }
401
402 SymmetricMatrix QQ_sav = _QQ;
403
404 ColumnVector dx;
405 QMap<QString, t_corr*> resCorr;
406
407 // Update and outlier detection loop
408 // ---------------------------------
409 while (true) {
410
411 Matrix AA;
412 ColumnVector ll;
413 DiagonalMatrix PP;
414
415 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
416 return;
417 }
418
419 bncModel::kalman(AA, ll, PP, _QQ, dx);
420 ColumnVector vv = ll - AA * dx;
421
422 int maxResIndex;
423 double maxRes = vv.maximum_absolute_value1(maxResIndex);
424 out.setRealNumberNotation(QTextStream::FixedNotation);
425 out.setRealNumberPrecision(3);
426 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
427 << " Maximum Residuum " << maxRes << ' '
428 << corrs()[maxResIndex-1]->acName << ' ' << corrs()[maxResIndex-1]->prn;
429
430 if (maxRes > _MAXRES) {
431 for (int iPar = 1; iPar <= _params.size(); iPar++) {
432 cmbParam* pp = _params[iPar-1];
433 if (pp->type == cmbParam::offACSat &&
434 pp->AC == corrs()[maxResIndex-1]->acName &&
435 pp->prn == corrs()[maxResIndex-1]->prn) {
436 QQ_sav.Row(iPar) = 0.0;
437 QQ_sav.Column(iPar) = 0.0;
438 QQ_sav(iPar,iPar) = pp->sig0 * pp->sig0;
439 }
440 }
441
442 out << " Outlier" << endl;
443 _QQ = QQ_sav;
444 corrs().remove(maxResIndex-1);
445 }
446 else {
447 out << " OK" << endl;
448 break;
449 }
450 }
451
452 // Update Parameter Values
453 // -----------------------
454 for (int iPar = 1; iPar <= _params.size(); iPar++) {
455 cmbParam* pp = _params[iPar-1];
456 pp->xx += dx(iPar);
457 if (pp->type == cmbParam::clkSat) {
458 if (resCorr.find(pp->prn) != resCorr.end()) {
459 resCorr[pp->prn]->dClk = pp->xx / t_CST::c;
460 }
461 }
462 out << _resTime.datestr().c_str() << " "
463 << _resTime.timestr().c_str() << " ";
464 out.setRealNumberNotation(QTextStream::FixedNotation);
465 out.setFieldWidth(8);
466 out.setRealNumberPrecision(4);
467 out << pp->toString() << " "
468 << pp->xx << " +- " << sqrt(_QQ(pp->index,pp->index)) << endl;
469 out.setFieldWidth(0);
470 }
471
472 // Print Results
473 // -------------
474 printResults(out, resCorr);
475 dumpResults(resCorr);
476
477 emit newMessage(_log, false);
478
479 // Delete Data
480 // -----------
481 _buffer.remove(_resTime);
482}
483
484// Print results
485////////////////////////////////////////////////////////////////////////////
486void bncComb::printResults(QTextStream& out,
487 const QMap<QString, t_corr*>& resCorr) {
488
489 QMapIterator<QString, t_corr*> it(resCorr);
490 while (it.hasNext()) {
491 it.next();
492 t_corr* corr = it.value();
493 const t_eph* eph = corr->eph;
494 if (eph) {
495 double xx, yy, zz, cc;
496 eph->position(_resTime.gpsw(), _resTime.gpssec(), xx, yy, zz, cc);
497
498 out << _resTime.datestr().c_str() << " "
499 << _resTime.timestr().c_str() << " ";
500 out.setFieldWidth(3);
501 out << "Full Clock " << corr->prn << " " << corr->iod << " ";
502 out.setFieldWidth(14);
503 out << (cc + corr->dClk) * t_CST::c << endl;
504 out.setFieldWidth(0);
505 }
506 else {
507 out << "bncComb::printResuls bug" << endl;
508 }
509 }
510}
511
512// Send results to RTNet Decoder and directly to PPP Client
513////////////////////////////////////////////////////////////////////////////
514void bncComb::dumpResults(const QMap<QString, t_corr*>& resCorr) {
515
516 ostringstream out; out.setf(std::ios::fixed);
517 QStringList corrLines;
518
519 unsigned year, month, day, hour, minute;
520 double sec;
521 _resTime.civil_date(year, month, day);
522 _resTime.civil_time(hour, minute, sec);
523
524 out << "* "
525 << setw(4) << year << " "
526 << setw(2) << month << " "
527 << setw(2) << day << " "
528 << setw(2) << hour << " "
529 << setw(2) << minute << " "
530 << setw(12) << setprecision(8) << sec << " "
531 << endl;
532
533 QMapIterator<QString, t_corr*> it(resCorr);
534 while (it.hasNext()) {
535 it.next();
536 t_corr* corr = it.value();
537
538 double dT = 60.0;
539
540 for (int iTime = 1; iTime <= 2; iTime++) {
541
542 bncTime time12 = (iTime == 1) ? _resTime : _resTime + dT;
543
544 ColumnVector xc(4);
545 ColumnVector vv(3);
546 corr->eph->position(time12.gpsw(), time12.gpssec(),
547 xc.data(), vv.data());
548 bncPPPclient::applyCorr(time12, corr, xc, vv);
549
550 // Relativistic Correction
551 // -----------------------
552 double relCorr = - 2.0 * DotProduct(xc.Rows(1,3),vv) / t_CST::c / t_CST::c;
553 xc(4) -= relCorr;
554
555 // Code Biases
556 // -----------
557 double dcbP1C1 = 0.0;
558 double dcbP1P2 = 0.0;
559
560 // Correction Phase Center --> CoM
561 // -------------------------------
562 ColumnVector dx(3); dx = 0.0;
563 if (_antex) {
564 double Mjd = time12.mjd() + time12.daysec()/86400.0;
565 if (_antex->satCoMcorrection(corr->prn, Mjd, xc.Rows(1,3), dx) == success) {
566 xc(1) -= dx(1);
567 xc(2) -= dx(2);
568 xc(3) -= dx(3);
569 }
570 else {
571 cout << "antenna not found" << endl;
572 }
573 }
574
575 if (iTime == 1) {
576 out << 'P' << corr->prn.toAscii().data()
577 << setw(14) << setprecision(6) << xc(1) / 1000.0
578 << setw(14) << setprecision(6) << xc(2) / 1000.0
579 << setw(14) << setprecision(6) << xc(3) / 1000.0
580 << setw(14) << setprecision(6) << xc(4) * 1e6
581 << setw(14) << setprecision(6) << relCorr * 1e6
582 << setw(8) << setprecision(3) << dx(1)
583 << setw(8) << setprecision(3) << dx(2)
584 << setw(8) << setprecision(3) << dx(3)
585 << setw(8) << setprecision(3) << dcbP1C1
586 << setw(8) << setprecision(3) << dcbP1P2
587 << setw(6) << setprecision(1) << dT;
588
589 QString line;
590 int messageType = COTYPE_GPSCOMBINED;
591 int updateInt = 0;
592 line.sprintf("%d %d %d %.1f %s"
593 " %3d"
594 " %8.3f %8.3f %8.3f %8.3f"
595 " %10.5f %10.5f %10.5f %10.5f"
596 " %10.5f %10.5f %10.5f %10.5f INTERNAL",
597 messageType, updateInt, time12.gpsw(), time12.gpssec(),
598 corr->prn.toAscii().data(),
599 corr->iod,
600 corr->dClk * t_CST::c,
601 corr->rao[0],
602 corr->rao[1],
603 corr->rao[2],
604 corr->dotDClk * t_CST::c,
605 corr->dotRao[0],
606 corr->dotRao[1],
607 corr->dotRao[2],
608 corr->dotDotDClk * t_CST::c,
609 corr->dotDotRao[0],
610 corr->dotDotRao[1],
611 corr->dotDotRao[2]);
612 corrLines << line;
613 }
614 else {
615 out << setw(14) << setprecision(6) << xc(1) / 1000.0
616 << setw(14) << setprecision(6) << xc(2) / 1000.0
617 << setw(14) << setprecision(6) << xc(3) / 1000.0 << endl;
618 }
619 }
620
621 delete corr;
622 }
623 out << "EOE" << endl; // End Of Epoch flag
624
625 if (!_rtnetDecoder) {
626 _rtnetDecoder = new bncRtnetDecoder();
627 }
628
629 vector<string> errmsg;
630 _rtnetDecoder->Decode((char*) out.str().data(), out.str().size(), errmsg);
631
632 // Optionally send new Corrections to PPP
633 // --------------------------------------
634 bncApp* app = (bncApp*) qApp;
635 if (app->_bncPPPclient) {
636 app->_bncPPPclient->slotNewCorrections(corrLines);
637 }
638}
639
640// Create First Design Matrix and Vector of Measurements
641////////////////////////////////////////////////////////////////////////////
642t_irc bncComb::createAmat(Matrix& AA, ColumnVector& ll, DiagonalMatrix& PP,
643 const ColumnVector& x0,
644 QMap<QString, t_corr*>& resCorr) {
645
646 unsigned nPar = _params.size();
647 unsigned nObs = corrs().size();
648
649 if (nObs == 0) {
650 return failure;
651 }
652
653 const int nCon = 2;
654
655 AA.ReSize(nObs+nCon, nPar); AA = 0.0;
656 ll.ReSize(nObs+nCon); ll = 0.0;
657 PP.ReSize(nObs+nCon); PP = 1.0 / (sigObs * sigObs);
658
659 int iObs = 0;
660
661 QVectorIterator<cmbCorr*> itCorr(corrs());
662 while (itCorr.hasNext()) {
663 cmbCorr* corr = itCorr.next();
664 QString prn = corr->prn;
665 switchToLastEph(_eph[prn]->last, corr);
666 ++iObs;
667
668 if (resCorr.find(prn) == resCorr.end()) {
669 resCorr[prn] = new t_corr(*corr);
670 }
671
672 for (int iPar = 1; iPar <= _params.size(); iPar++) {
673 cmbParam* pp = _params[iPar-1];
674 AA(iObs, iPar) = pp->partial(corr->acName, prn);
675 }
676
677 ll(iObs) = corr->dClk * t_CST::c - DotProduct(AA.Row(iObs), x0);
678 }
679
680 // Regularization
681 // --------------
682 const double Ph = 1.e6;
683 PP(nObs+1) = Ph;
684 PP(nObs+2) = Ph;
685 for (int iPar = 1; iPar <= _params.size(); iPar++) {
686 cmbParam* pp = _params[iPar-1];
687 if (pp->type == cmbParam::clkSat) {
688 AA(nObs+1, iPar) = 1.0;
689 }
690 else if (pp->type == cmbParam::offAC) {
691 AA(nObs+2, iPar) = 1.0;
692 }
693 }
694
695 return success;
696}
697
698// Change the parameter so that it refers to last received ephemeris
699////////////////////////////////////////////////////////////////////////////
700void bncComb::switchToLastEph(const t_eph* lastEph, cmbParam* pp) {
701
702 if (pp->type != cmbParam::offACSat) {
703 return;
704 }
705
706 if (pp->eph == 0) {
707 pp->eph = lastEph;
708 return;
709 }
710
711 if (pp->eph == lastEph) {
712 return;
713 }
714
715 ColumnVector oldXC(4);
716 ColumnVector oldVV(3);
717 pp->eph->position(_resTime.gpsw(), _resTime.gpssec(),
718 oldXC.data(), oldVV.data());
719
720 ColumnVector newXC(4);
721 ColumnVector newVV(3);
722 lastEph->position(_resTime.gpsw(), _resTime.gpssec(),
723 newXC.data(), newVV.data());
724
725 double dC = newXC(4) - oldXC(4);
726
727 QString msg = "switch param " + pp->prn
728 + QString(" %1 -> %2 %3").arg(pp->eph->IOD(),3)
729 .arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
730
731 emit newMessage(msg.toAscii(), false);
732
733 pp->eph = lastEph;
734 pp->xx -= dC * t_CST::c;
735}
736
Note: See TracBrowser for help on using the repository browser.