NeQuickIonoNavData.cpp
Go to the documentation of this file.
1 //==============================================================================
2 //
3 // This file is part of GNSSTk, the ARL:UT GNSS Toolkit.
4 //
5 // The GNSSTk 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 GNSSTk 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 GNSSTk; 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-2022, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 
26 //==============================================================================
27 //
28 // This software was developed by Applied Research Laboratories at the
29 // University of Texas at Austin, under contract to an agency or agencies
30 // within the U.S. Department of Defense. The U.S. Government retains all
31 // rights to use, duplicate, distribute, disclose, or release this software.
32 //
33 // Pursuant to DoD Directive 523024
34 //
35 // DISTRIBUTION STATEMENT A: This software has been approved for public
36 // release, distribution is unlimited.
37 //
38 //==============================================================================
39 #include <cmath>
40 #include <string.h>
41 #include "NeQuickIonoNavData.hpp"
42 #include "TimeString.hpp"
43 #include "MODIP.hpp"
44 #include "FreqConv.hpp"
46 #include "DebugTrace.hpp"
47 
48 using namespace std;
49 
50 /*
51  * ALL EQUATION AND SECTION REFERENCES ARE TO THE DOCUMENT
52  * "Ionospheric Correction Algorithm for Galileo Single Frequency Users"
53  * aka "Galileo Ionospheric Model"
54  * UNLESS OTHERWISE STATED
55  *
56  * Apologies for all the magic numbers. They've been replaced with
57  * meaningful labels when possible, otherwise this is how it appears
58  * in the source document.
59  *
60  * See the NeQuickIonoNavData class documentation for additional references.
61  */
62 
64 constexpr double COEFF_THRESH = 1e-7;
66 constexpr double CRIT_FREQ_THRESH = 1e-6;
68 constexpr double CRIT_FREQ_THRESH_E = 1e-30;
70 constexpr double DEFAULT_IONO_LEVEL = 63.7;
72 constexpr double ABOVE_ELEV_EPSILON = 1e-5;
74 constexpr double INTEG_EPSILON = 1e-3;
76 constexpr double ELEC_DEN_SCALING = 1.0e11;
78 constexpr double TEC_SCALE_FACTOR = 1.0e-13;
80 constexpr double TECU_SCALE_FACTOR = 1.0e16;
82 constexpr double TOP_APPROX_EPSILON = 1.0e11;
84 constexpr double INTEGRATION_FIRST_POINT = 1000;
86 constexpr double INTEGRATION_SECOND_POINT = 2000;
88 constexpr double INTEG_EPSILON_S1_SA = 0.001;
90 constexpr double INTEG_EPSILON_SA_S2 = 0.01;
92 constexpr double NEEXP_MIN_VALUE = 1.8049e-35;
94 constexpr double NEEXP_MAX_VALUE = 5.5406e34;
96 constexpr double tan30 = 0.5773502691896;
98 constexpr double FREQ2NE_D = 0.124;
100 constexpr double hmE = 120.0; //eq.78
102 constexpr double BEbot = 5.0; //eq.89
103 
108 {
113  ExponentMin = -80,
114  ExponentMax = 80,
115 };
116 
117 
118 namespace gnsstk
119 {
120  NeQuickIonoNavData ::
121  NeQuickIonoNavData()
122  : ai{0,0,0},
123  idf{false,false,false,false,false}
124  {
125  }
126 
127 
129  dump(std::ostream& s, DumpDetail dl) const
130  {
131  if (dl == DumpDetail::OneLine)
132  {
133  NavData::dump(s,dl);
134  return;
135  }
136  // "header"
137  s << "****************************************************************"
138  << "************" << endl
139  << "Ionospheric correction data"
140  << endl
141  << endl
142  << getSignalString() << endl;
143 
144  // the rest is full details, so just return if Full is not asked for.
145  if (dl != DumpDetail::Full)
146  return;
147 
148  const ios::fmtflags oldFlags = s.flags();
149 
150  s.setf(ios::fixed, ios::floatfield);
151  s.setf(ios::right, ios::adjustfield);
152  s.setf(ios::uppercase);
153  s.precision(0);
154  s.fill(' ');
155 
156  s << " TIMES OF INTEREST"
157  << endl << endl
158  << " " << getDumpTimeHdr(dl) << endl
159  << "Transmit: " << getDumpTime(dl, timeStamp) << endl;
160 
161  s.setf(ios::scientific, ios::floatfield);
162  s.precision(8);
163  s.fill(' ');
164 
165  s << endl
166  << " NEQUICK IONO PARAMETERS" << endl
167  << "Parameter Value" << endl
168  << "a_i0 " << setw(16) << ai[0] << " sfu" << endl
169  << "a_i1 " << setw(16) << ai[1] << " sfu/degree" << endl
170  << "a_i2 " << setw(16) << ai[2] << " sfu/degree**2" << endl
171  << boolalpha
172  << "IDF region 1 " << setw(16) << idf[0] << endl
173  << "IDF region 2 " << setw(16) << idf[1] << endl
174  << "IDF region 3 " << setw(16) << idf[2] << endl
175  << "IDF region 4 " << setw(16) << idf[3] << endl
176  << "IDF region 5 " << setw(16) << idf[4] << endl;
177 
178  s.flags(oldFlags);
179  }
180 
181 
182  double NeQuickIonoNavData ::
184  const Position& rxgeo,
185  const Position& svgeo,
186  CarrierBand band) const
187  {
189  double tec = getTEC(when, rxgeo, svgeo);
190  // Obtain correction by converting STEC to code delay
191  double f = getFrequency(band);
192  double d1gr = tec * TECU_SCALE_FACTOR * 40.3/(f*f); // eq.1
193  return d1gr;
194  }
195 
196 
197  double NeQuickIonoNavData ::
198  getTEC(const CommonTime& when,
199  const Position& rxgeo,
200  const Position& svgeo)
201  const
202  {
204  // Get the time in civil units for later math
205  CivilTime civ(when);
206  // Obtain receiver modified dip latitude
207  MODIP modip;
208  CCIR ccir;
209  // pre-determine in a somewhat clumsy, but probably faster
210  // method than elevation() if the satellite is directly above
211  // the station.
212  bool vertical =
213  ((fabs(svgeo.geodeticLatitude()-rxgeo.geodeticLatitude()) <
215  (fabs(svgeo.longitude()-rxgeo.longitude()) < ABOVE_ELEV_EPSILON));
216  Position Pp(rxgeo.getRayPerigee(svgeo));
217  IntegrationParameters ip(rxgeo, svgeo, Pp, vertical);
218  DEBUGTRACE("computing azu");
219  double modip_u = modip.stModip(rxgeo);
220  double azu = getEffIonoLevel(modip_u);
221  DEBUGTRACE("azu = " << azu);
222  DEBUGTRACE("vertical=" << vertical);
223  DEBUGTRACE("rxgeo.geodeticLatitude()=" << rxgeo.geodeticLatitude());
224  DEBUGTRACE("rxgeo.longitude()=" << rxgeo.longitude());
225  DEBUGTRACE("svgeo.geodeticLatitude()=" << svgeo.geodeticLatitude());
226  DEBUGTRACE("svgeo.longitude()=" << svgeo.longitude());
227  DEBUGTRACE("svgeo.height()=" << svgeo.height());
228  Angle debugAngle(rxgeo.geodeticLatitude(),AngleType::Deg);
229  DEBUGTRACE("pPosition->latitude.rad=" << scientific << debugAngle.rad());
230  DEBUGTRACE("pPosition->latitude.degree=" << scientific << debugAngle.deg());
231  DEBUGTRACE("pPosition->latitude.sin=" << scientific << debugAngle.sin());
232  DEBUGTRACE("pPosition->latitude.cos=" << scientific << debugAngle.cos());
233  debugAngle = Angle(rxgeo.longitude(), AngleType::Deg);
234  DEBUGTRACE("pPosition->longitude.rad=" << scientific << debugAngle.rad());
235  DEBUGTRACE("pPosition->longitude.degree=" << scientific << debugAngle.deg());
236  DEBUGTRACE("pPosition->longitude.sin=" << scientific << debugAngle.sin());
237  DEBUGTRACE("pPosition->longitude.cos=" << scientific << debugAngle.cos());
238  DEBUGTRACE("pPosition->height()=" << scientific << (rxgeo.height() / 1000.0));
239  DEBUGTRACE("pPosition->radius_km()=" << scientific << (rxgeo.radius() / 1000.0));
240  DEBUGTRACE("# pContext->modip_degree=" << scientific << modip_u);
241  DEBUGTRACE("# a_sfu[0]=" << scientific << ai[0]);
242  DEBUGTRACE("# a_sfu[1]=" << scientific << ai[1]);
243  DEBUGTRACE("# a_sfu[2]=" << scientific << ai[2]);
244  double rv = 0;
245  // must have at least two slant heights to make an interval...
246  if (ip.integHeights.size() > 1)
247  {
248  for (unsigned i = 1; i < ip.integHeights.size(); i++)
249  {
250  rv += integrateGaussKronrod(ip.integHeights[i-1],
251  ip.integHeights[i],
252  rxgeo, svgeo, modip, modip_u, ccir, civ,
253  azu, ip.intThresh[i-1], vertical);
254  }
255  }
256  // scale as per eq.151 and eq.202
257  rv *= TEC_SCALE_FACTOR;
258  return rv;
259  }
260 
261 
262  double NeQuickIonoNavData ::
263  getEffIonoLevel(double modip_u)
264  const
265  {
267  // Obtain Effective Ionisation Level Azu (also eq.2)
268  double azu = ai[0] + ai[1]*modip_u + ai[2]*modip_u*modip_u; // eq.18
269  if ((fabs(ai[0]) < COEFF_THRESH) && // eq.17
270  (fabs(ai[1]) < COEFF_THRESH) &&
271  (fabs(ai[2]) < COEFF_THRESH))
272  {
273  azu = DEFAULT_IONO_LEVEL;
274  }
275  // Clamp Azu to [0,400] per section 3.3.
276  if (azu < 0)
277  azu = 0;
278  else if (azu > 400)
279  azu = 400;
280  return azu;
281  }
282 
283 
284  double NeQuickIonoNavData ::
285  getSED(double dist, const Position& rxgeo, const Position& svgeo,
286  const MODIP& modip, CCIR& ccirData, const CivilTime& when,
287  double azu)
288  const
289  {
291  DEBUGTRACE("height_km=" << setprecision(15) << dist);
292  Position current(rxgeo.getRayPosition(dist * 1000.0, svgeo));
293  double modip_u = modip.stModip(current);
294  DEBUGTRACE("constructing SED iono");
295  ModelParameters iono(modip_u, current, azu, ccirData, when);
296  double electronDensity = iono.electronDensity(current);
297  DEBUGTRACE("electron density=" << setprecision(15) << scientific
298  << electronDensity);
299  return electronDensity;
300  }
301 
302 
303  double NeQuickIonoNavData ::
304  getVED(double dist, const Position& rxgeo, const Position& svgeo,
305  double modip_u, CCIR& ccirData, const CivilTime& when,
306  double azu)
307  const
308  {
310  DEBUGTRACE("height_km=" << setprecision(15) << dist);
311  // remember that dist is a height for VED, and that it's in km
312  Position current(rxgeo.geocentricLatitude(), rxgeo.longitude(), dist*1000,
314  DEBUGTRACE("constructing VED iono");
315  ModelParameters iono(modip_u, current, azu, ccirData, when);
316  double electronDensity = iono.electronDensity(current);
317  return electronDensity;
318  }
319 
320 
321  double NeQuickIonoNavData ::
322  neExp(double x)
323  {
324  if (x > ExponentMax)
325  {
326  return NEEXP_MAX_VALUE;
327  }
328  else if (x < ExponentMin)
329  {
330  return NEEXP_MIN_VALUE;
331  }
332  return exp(x);
333  }
334 
335 
337  ModelParameters(double modip_u, const Position& pos, double az,
338  CCIR& ccirData, const CivilTime& when)
339  : fXeff(effSolarZenithAngle(pos,when)),
340  ffoF1(0.0), // default to 0, see eq.37
341  ccir(ccirData)
342  {
344  int seas;
345  DEBUGTRACE("pos = " << pos);
346  DEBUGTRACE("solar_12_month_running_mean_of_2800_MHZ_noise_flux=" << az);
347  // get the effective sunspot number
348  fAzr = sqrt(167273+(az-DEFAULT_IONO_LEVEL)*1123.6)-408.99; // eq.19
349  switch (when.month)
350  {
351  case 1:
352  case 2:
353  case 11:
354  case 12:
355  seas = -1; //eq.30
356  break;
357  case 3:
358  case 4:
359  case 9:
360  case 10:
361  seas = 0; //eq.31
362  break;
363  case 5:
364  case 6:
365  case 7:
366  case 8:
367  seas = 1; //eq.32
368  break;
369  default:
370  GNSSTK_THROW(Exception("Invalid month"));
371  break;
372  }
373  double phi = pos.geodeticLatitude();
374  double lambda = pos.longitude();
375  DEBUGTRACE("power=" << (0.3 * phi));
376  DEBUGTRACE("# pSolar_activity->effective_ionisation_level_sfu="
377  << scientific << az);
378  double ee = neExp(0.3 * phi); //eq.33
379  double seasp = (seas * (ee-1))/(ee+1); //eq.34
380  double term1 = (1.112-0.019*seasp);
381  double term2 = sqrt(az);
382  double term3 = pow(cos(fXeff), 0.6);
383  DEBUGTRACE("cos(fXeff)=" << cos(fXeff));
384  DEBUGTRACE("pow(cos(fXeff),0.6)=" << pow(cos(fXeff), 0.6));
385  DEBUGTRACE("pow(cos(fXeff),0.3)=" << pow(cos(fXeff), 0.3));
386  DEBUGTRACE("pow(cos(fXeff),0.3) (alt)=" << exp(log(cos(fXeff))*0.3));
387  ffoE = sqrt(term1*term1*term2*term3 + 0.49); //eq.35
388  DEBUGTRACE("ffoE=" << ffoE);
389  fNmE = FREQ2NE_D * ffoE * ffoE; //eq.36
390  if (ffoE >= 2.0) //eq.37
391  {
392  ffoF1 = 1.4 * ffoE;
393  }
395  if (ffoF1 < CRIT_FREQ_THRESH)
396  {
397  ffoF1 = 0.0;
398  }
399  if ((ffoF1 <= 0) && (ffoE > 2.0))
400  {
401  fNmF1 = FREQ2NE_D * (ffoE + 0.5) * (ffoE + 0.5); //eq.38
402  }
403  else
404  {
405  fNmF1 = FREQ2NE_D * ffoF1 * ffoF1; //eq.39
406  }
407  DEBUGTRACE("latitude=" << phi);
408  DEBUGTRACE("seas=" << seas);
409  DEBUGTRACE("ee=" << scientific << ee);
410  DEBUGTRACE("seasp=" << seasp);
411  // Compute the fourier time series for foF2 and M(3000)F2
412  ccir.fourier(when, fAzr);
413  legendre(modip_u, pos);
414  fNmF2 = FREQ2NE_D * ffoF2 * ffoF2; //eq.77
415  // Compute peak electron density height for each layer
416  height();
417  // Compute thickness parameters for each layer
418  thickness();
419  exosphereAdjust(when.month);
420  peakAmplitudes();
421  }
422 
423 
426  : ccir(ccirData),
427  fAzr(std::numeric_limits<double>::quiet_NaN()),
428  ffoE(std::numeric_limits<double>::quiet_NaN()),
429  fNmE(std::numeric_limits<double>::quiet_NaN()),
430  fBEtop(std::numeric_limits<double>::quiet_NaN()),
431  fA{std::numeric_limits<double>::quiet_NaN(),
432  std::numeric_limits<double>::quiet_NaN(),
433  std::numeric_limits<double>::quiet_NaN()},
434  ffoF1(std::numeric_limits<double>::quiet_NaN()),
435  fNmF1(std::numeric_limits<double>::quiet_NaN()),
436  fhmF1(std::numeric_limits<double>::quiet_NaN()),
437  fB1top(std::numeric_limits<double>::quiet_NaN()),
438  fB1bot(std::numeric_limits<double>::quiet_NaN()),
439  ffoF2(std::numeric_limits<double>::quiet_NaN()),
440  fNmF2(std::numeric_limits<double>::quiet_NaN()),
441  fhmF2(std::numeric_limits<double>::quiet_NaN()),
442  fB2bot(std::numeric_limits<double>::quiet_NaN()),
443  fM3000F2(std::numeric_limits<double>::quiet_NaN()),
444  fH0(std::numeric_limits<double>::quiet_NaN()),
445  fXeff()
446  {
448  }
449 
450 
453  {
456  CivilTime whenUTC(when);
457  if (whenUTC.getTimeSystem() != gnsstk::TimeSystem::UTC)
458  {
460  }
461  // compute day of year at the middle of the month
462  double dy = 30.5 * whenUTC.month - 15; //eq.20
463  // compute time in days
464  double t = dy + (18.0-whenUTC.getUTHour())/24.0; //eq.21
465  // compute the argument
466  double am = (0.9856 * t - 3.289) * DEG2RAD; //eq.22
467  double al = am + (1.916*sin(am) + 0.020*sin(2*am) + 282.634) * //eq.23
468  DEG2RAD;
469  return AngleReduced(0.39782*sin(al),AngleType::Sin); //eq.24
470  }
471 
472 
475  {
477  double phiRad = pos.geodeticLatitude() * DEG2RAD;
478  double lambda = pos.longitude();
479  AngleReduced deltaSun = solarDeclination(when);
480  // leave the UTC check up to solarDeclination
481  double lt = when.getUTHour() + (lambda / 15.0); //eq.4
482  // X is really chi.
483  double cosX=sin(phiRad) * sin(deltaSun) + //eq.26
484  cos(phiRad) * cos(deltaSun) * cos(PI/12*(12-lt));
485  return Angle(atan2(sqrt(1-cosX*cosX),cosX),AngleType::Rad); //eq.27
486  }
487 
488 
491  {
493  // x is really chi.
494  static const double x0 = 86.23292796211615; //eq.28
495  Angle x = solarZenithAngle(pos, when);
496  double exp2 = neExp(12*(x.deg()-x0)); //eq.29
497  return Angle((x.deg()+(90-0.24*neExp(20-0.2*x.deg()))*exp2) / (1+exp2),
499  }
500 
501 
503  legendre(double modip_u, const Position& pos)
504  {
506  // sine modified dip latitude coefficients
507  double M[F2LayerMODIPCoeffCount]; //eq.52
508  // cosine latitude coefficents
509  double P[F2LayerLongCoeffCount]; //eq.53
510  // sine and cosine longitude coefficients
511  double S[F2LayerLongCoeffCount]; //eq.54
512  double C[F2LayerLongCoeffCount]; //eq.55
513  // Legendre grades for F2 critical frequency
514  const unsigned Q[] {12,12,9,5,2,1,1,1,1}; //eq.63
515  const int K[] {-12, 12, 36, 54, 64, 68, 70, 72, 74}; //eq.66
516  M[0] = 1.0; //eq.56
517  // Legendre grades for F2 transmission factor M(3000)F2
518  const unsigned R[] {7,8,6,3,2,1,1}; //eq.71
519  const int H[] {-7,7,23,35,41,45,47}; //eq.74
520  double modip_uRad = modip_u * DEG2RAD;
521  double phiRad = pos.geodeticLatitude() * DEG2RAD;
522  double lambdaRad = pos.longitude() * DEG2RAD;
523  DEBUGTRACE("lambdaRad = " << scientific << lambdaRad);
524  DEBUGTRACE("lat.rad = " << scientific << phiRad);
525  DEBUGTRACE("lat.deg = " << scientific << pos.geodeticLatitude());
526  DEBUGTRACE("cos_lat = " << scientific << cos(phiRad));
527  // compute sine modififed dip latitude coefficients
528  for (unsigned k = 1; k<F2LayerMODIPCoeffCount; k++)
529  {
530  M[k] = M[k-1] * sin(modip_uRad); //eq.57
531  }
532  P[0] = 1.0; // not used except for initialization convenience
533  // compute cos lat, sin long, cos long coefficients
534  for (unsigned n = 1; n < F2LayerLongCoeffCount; n++)
535  {
536  P[n] = P[n-1] * cos(phiRad); //eq.58
537  S[n-1] = sin(n * lambdaRad); //eq.59
538  C[n-1] = cos(n * lambdaRad); //eq.60
539  DEBUGTRACE("lambda[" << n << "]=" << scientific << (n*lambdaRad));
540  DEBUGTRACE("S[" << n << "]=" << scientific << S[n]);
541  DEBUGTRACE("C[" << n << "]=" << scientific << C[n]);
542  }
543  // initialize higher order terms to zero before summation
544  ffoF2 = 0;
545  // compute foF2 order 0 term
546  for (unsigned k = 0; k<F2LayerMODIPCoeffCount; k++)
547  {
548  ffoF2 += ccir.getCF2(k) * M[k]; //eq.61
549  DEBUGTRACE("CF2[" << k << "]=" << scientific << ccir.getCF2(k)
550  << " M[" << k << "]=" << M[k] << " parameter=" << ffoF2);
551  }
552  // compute foF2 higher order terms 2-9
553  for (unsigned n = 1; n<F2LayerLongCoeffCount; n++)
554  {
555  for (unsigned k=0; k<Q[n]; k++)
556  {
557  ffoF2 += (ccir.getCF2(K[n]+2*k) * C[n-1] + //eq.67
558  ccir.getCF2(K[n]+2*k+1) * S[n-1]) * M[k] * P[n];
559  DEBUGTRACE("M[" << k << "]=" << scientific << M[k]
560  << " lat_coeff=" << P[n]
561  << " Fourier_coeff[" << (K[n]+2*k) << "]="
562  << ccir.getCF2(K[n]+2*k)
563  << " C[" << (n-1) << "]=" << C[n-1]
564  << " Fourier_coeff[" << (K[n]+2*k+1) << "]="
565  << ccir.getCF2(K[n]+2*k+1)
566  << " S[" << (n-1) << "]=" << S[n-1]
567  << " parameter=" << ffoF2);
568  }
569  }
570  // initialize higher order terms to zero before summation
571  fM3000F2 = 0;
572  // compute M(3000)F2 order 0 term
573  DEBUGTRACE("M(3000)F2");
574  for (unsigned k=0; k<F2TransFactorCoeffCount; k++)
575  {
576  fM3000F2 += ccir.getCM3(k) * M[k]; //eq.69
577  DEBUGTRACE("CM3[" << k << "]=" << scientific << ccir.getCM3(k)
578  << " M[" << k << "]=" << M[k] << " parameter="
579  << fM3000F2);
580  }
581  // compute M(3000)F2 higher order terms 2-7
582  for (unsigned n = 1; n<F2TransFactorCoeffCount; n++)
583  {
584  for (unsigned k=0; k<R[n]; k++)
585  {
586  fM3000F2 += (ccir.getCM3(H[n]+2*k) * C[n-1] + //eq.75
587  ccir.getCM3(H[n]+2*k+1) * S[n-1]) * M[k] * P[n];
588  DEBUGTRACE("M[" << k << "]=" << scientific << M[k]
589  << " lat_coeff=" << P[n]
590  << " Fourier_coeff[" << (H[n]+2*k) << "]="
591  << ccir.getCM3(H[n]+2*k)
592  << " C[" << (n-1) << "]=" << C[n-1]
593  << " Fourier_coeff[" << (H[n]+2*k+1) << "]="
594  << ccir.getCM3(H[n]+2*k+1)
595  << " S[" << (n-1) << "]=" << S[n-1]
596  << " parameter=" << fM3000F2);
597  }
598  }
599  }
600 
601 
604  {
606  // A lot of constants in these equations that I don't really
607  // know the context of. The EU code refers to them as
608  // DUDENEY.
609  double cfRatio = ffoF2 / ffoE; //eq.84
610  double eTerm = neExp(20*(cfRatio-1.75));
611  double rho = (cfRatio*eTerm + 1.75) / (eTerm + 1);
612  double M = fM3000F2; //eq.81
613  double dM = (ffoE<CRIT_FREQ_THRESH_E ? -0.012 : //eq.82
614  (0.253/(rho-1.215))-0.012); //eq.83
615  fhmF2 = ((1490 * M * sqrt((0.0196*M*M+1)/(1.2967*M*M-1))) / //eq.80
616  (M + dM)) - 176.0;
617  fhmF1 = (fhmF2+hmE) / 2.0; //eq.79
618  }
619 
620 
623  {
625  // note that the EU code uses the standard exp here too
626  fB2bot = ((0.385*fNmF2) / //eq.85
627  (0.01*exp(-3.467+0.857*log(ffoF2*ffoF2) +
628  2.02 * log(fM3000F2))));
629  fB1top = 0.3 * (fhmF2-fhmF1); //eq.86
630  fB1bot = 0.5 * (fhmF1 - hmE); //eq.87
631  fBEtop = std::max(fB1bot, 7.0); //eq.88
632  }
633 
634 
637  {
639  double k = 0;
640  double ka = 0;
641  DEBUGTRACE("pTime->month=" << month);
642  DEBUGTRACE("# pSolar_activity->effective_sun_spot_count=" << scientific << fAzr);
643  switch (month)
644  {
645  case 4:
646  case 5:
647  case 6:
648  case 7:
649  case 8:
650  case 9:
651  ka = 6.705 - 0.014*fAzr - 0.008*fhmF2; //eq.99
652  break;
653  case 1:
654  case 2:
655  case 3:
656  case 10:
657  case 11:
658  case 12:
659  ka = fhmF2/fB2bot;
660  ka = -7.77 + 0.097*ka*ka + 0.153*fNmF2; //eq.100
661  break;
662  default:
663  GNSSTK_THROW(Exception("Invalid month"));
664  break;
665  }
666  double kb = neExp(ka-2);
667  kb = (ka * kb + 2) / (1 + kb); //eq.101
668  k = neExp(kb-8);
669  k = (8 * k + kb) / (1 + k); //eq.102
670  double Ha = k * fB2bot; //eq.103
671  double x = (Ha - 150.0) / 100.0; //eq.104
672  double v = (0.041163*x - 0.183981)*x + 1.424472; //eq.105
673  DEBUGTRACE("shape_factor = " << k);
674  DEBUGTRACE("auxiliary_param_x = " << x);
675  DEBUGTRACE("auxiliary_param_v = " << v);
676  fH0 = Ha / v; //eq.106
677  DEBUGTRACE("top-side thickness parameter = " << fH0);
678  }
679 
680 
683  {
685  // F2 layer amplitude A1
686  fA[0] = 4 * fNmF2; //eq.90
687  double A3a = 0, A2a = 0;
688  if (ffoF1 < 0.5)
689  {
690  DEBUGTRACE("option 1");
691  // Eq.91 and Eq.92 appear to assign A2 and A3 directly,
692  // but the EU code makes it appear that they should
693  // actually be assigning to A2a and A3a instead, with
694  // Eq.97 and Eq.98 being applied regardless of the value
695  // of foF1.
696  A2a = 0; //eq.91
697  A3a = 4.0 * (fNmE - epstein(fA[0],fhmF2,fB2bot,hmE)); //eq.92
698  }
699  else
700  {
701  DEBUGTRACE("option 2");
702  A3a = 4.0 * fNmE; //eq.93
703  A2a = 0;
704  // section 2.5.5.9 simply says to repeat these equations 5 times.
705  for (unsigned i = 0; i < 5; i++)
706  {
707  A2a = 4.0 * (fNmF1 - epstein(fA[0],fhmF2,fB2bot,fhmF1) - //eq.94
708  epstein(A3a,hmE,fBEtop,fhmF1));
709  double tmp = neExp(A2a - 0.80 * fNmF1);
710  A2a = (A2a * tmp + 0.80 * fNmF1) / (1+tmp); //eq.95
711  A3a = 4.0 * (fNmE - epstein(A2a,fhmF1,fB1bot,hmE) - //eq.96
712  epstein(fA[0],fhmF2,fB2bot,hmE));
713  }
714  }
715  // F1 layer amplitude A2
716  fA[1] = A2a; //eq.97
717  double tmp = neExp(60.0*(A3a-0.005));
718  // E layer amplitude A3
719  fA[2] = (A3a * tmp + 0.05) / (1+tmp); //eq.98
720  }
721 
722 
725  {
727  double rv = 0;
728  // must convert height from m to km first
729  if ((pos.height() / 1000.0) <= fhmF2)
730  {
731  rv = electronDensityBottom(pos);
732  }
733  else
734  {
735  rv = electronDensityTop(pos);
736  }
737  DEBUGTRACE("electron density=" << scientific << rv);
738  return rv;
739  }
740 
741 
744  {
746  static constexpr double g = 0.125; //eq.122
747  static constexpr double r = 100; //eq.123
748  double h = pos.height() / 1000.0; // height in km
749  double deltah = h - fhmF2; //eq.124
750  double z = deltah / (fH0*(1+(r*g*deltah)/(r*fH0+g*deltah))); //eq.125
751  double ea = neExp(z); //eq.126
752  double rv;
753  if (ea > TOP_APPROX_EPSILON)
754  {
755  rv = 4 * fNmF2 / ea; //eq.127
756  }
757  else
758  {
759  rv = 4 * fNmF2 * ea / ((1+ea)*(1+ea)); //eq.127
760  }
761  return rv * ELEC_DEN_SCALING;
762  }
763 
764 
767  {
769  double h = pos.height() / 1000.0; // height in km
770  double BE = (h > hmE) ? fBEtop : BEbot; //eq.109
771  double BF1 = (h > fhmF1) ? fB1top : fB1bot; //eq.110
772  double mh = std::max(h, 100.0); // see note after eq.113
773  // note that the EU code uses the standard exp here too
774  double t2 = exp(10/(1+fabs(mh-fhmF2)));
775  double alpha[3];
776  alpha[0] = (mh - fhmF2) / fB2bot; //eq.111
777  alpha[1] = (mh - fhmF1) / BF1 * t2; //eq.112
778  alpha[2] = (mh - hmE) / BE * t2; //eq.113
779  // ea = exp(alpha)
780  double ea[3] { neExp(alpha[0]), neExp(alpha[1]), neExp(alpha[2]) };
781  double s[3];
782  for (unsigned i = 0; i < 3; i++)
783  {
784  if (fabs(alpha[i]) > 25)
785  {
786  s[i] = 0;
787  }
788  else
789  {
790  s[i] = fA[i] * ea[i]/((1+ea[i])*(1+ea[i])); //eq.114
791  }
792  }
793  double rv;
794  if (h >= 100.0)
795  {
796  rv = s[0] + s[1] + s[2]; //eq.115
797  }
798  else
799  {
800  double ds[3];
801  ds[0] = fabs(alpha[0]) > 25 ? 0 //eq.116
802  : (1/fB2bot)*((1-ea[0])/(1+ea[0]));
803  ds[1] = fabs(alpha[1]) > 25 ? 0 //eq.117
804  : (1/BF1)*((1-ea[1])/(1+ea[1]));
805  ds[2] = fabs(alpha[2]) > 25 ? 0 //eq.118
806  : (1/BE)*((1-ea[2])/(1+ea[2]));
807  double sumNum = s[0]*ds[0]+s[1]*ds[1]+s[2]*ds[2];
808  double sumDenom = s[0]+s[1]+s[2];
809  double BC = 1 - 10*sumNum/sumDenom; //eq.119
810  double z = (h-100)/10; //eq.120
811  rv = sumDenom * neExp(1-BC*z-neExp(-z)); //eq.121
812  }
813  return rv * ELEC_DEN_SCALING;
814  }
815 
816 
819  const Position& Pp, bool vertical)
820  {
823  if (vertical)
824  {
825  // minimum height of station is 0.
826  double h1 = std::max(0.0, rx.height() / 1000.0);
827  double ha = INTEGRATION_FIRST_POINT;
828  double hb = INTEGRATION_SECOND_POINT;
829  double h2 = sv.height() / 1000.0;
830  // populate the vector with all the heights that will be
831  // used as integration intervals.
832  integHeights.push_back(h1);
833  if ((ha > h1) && (ha < h2))
834  {
835  integHeights.push_back(ha);
836  intThresh.push_back(INTEG_EPSILON_S1_SA);
837  }
838  if ((hb > h1) && (hb < h2))
839  {
840  integHeights.push_back(hb);
841  intThresh.push_back(INTEG_EPSILON_SA_S2);
842  }
843  integHeights.push_back(h2);
844  // If intThresh is empty at this point, we know h1 and h2 are
845  // both below ha, so use the higher-precision epsilon.
846  // Otherwise, the h1 to h2 ray crosses one or both of ha,hb,
847  // so use the lower-precision epsilon.
848  if (intThresh.empty())
849  {
850  intThresh.push_back(INTEG_EPSILON_S1_SA);
851  }
852  else
853  {
854  intThresh.push_back(INTEG_EPSILON_SA_S2);
855  }
856  DEBUGTRACE("h1 = " << h1);
857  DEBUGTRACE("ha = " << ha);
858  DEBUGTRACE("hb = " << hb);
859  DEBUGTRACE("h2 = " << h2);
860  }
861  else
862  {
863  double rp = Pp.radius() / 1000.0; // ray perigee radius in km
864  double ip1 = elModel.a_km() + INTEGRATION_FIRST_POINT;
865  double ip2 = elModel.a_km() + INTEGRATION_SECOND_POINT;
866  // minimum radius of the station must be the surface of
867  // the "ellipsoid" radius is converted from m to km
868  double r1 = std::max(elModel.a_km(), rx.radius() / 1000.0);
869  // same as above, but for satellite, which we assume isn't
870  // underground
871  double r2 = sv.radius() / 1000.0;
872  double s1 = sqrt(fabs(r1*r1 - rp*rp));
873  double sa = sqrt(fabs(ip1*ip1 - rp*rp)); //eq.188
874  double sb = sqrt(fabs(ip2*ip2 - rp*rp)); //eq.189
875  double s2 = sqrt(fabs(r2*r2 - rp*rp));
876  // populate the vector with all the slant heights that will
877  // be used as integration intervals.
878  integHeights.push_back(s1);
879  if ((sa > s1) && (sa < s2))
880  {
881  integHeights.push_back(sa);
882  intThresh.push_back(INTEG_EPSILON_S1_SA);
883  }
884  if ((sb > s1) && (sb < s2))
885  {
886  integHeights.push_back(sb);
887  intThresh.push_back(INTEG_EPSILON_SA_S2);
888  }
889  integHeights.push_back(s2);
890  // If intThresh is empty at this point, we know s1 and s2 are
891  // both below sa, so use the higher-precision epsilon.
892  // Otherwise, the s1 to s2 ray crosses one or both of sa,sb,
893  // so use the lower-precision epsilon.
894  if (intThresh.empty())
895  {
896  intThresh.push_back(INTEG_EPSILON_S1_SA);
897  }
898  else
899  {
900  intThresh.push_back(INTEG_EPSILON_SA_S2);
901  }
902  DEBUGTRACE("s1 = " << s1);
903  DEBUGTRACE("sa = " << sa);
904  DEBUGTRACE("sb = " << sb);
905  DEBUGTRACE("s2 = " << s2);
906  }
907  DEBUGTRACE("integHeights.size() = " << integHeights.size());
908  DEBUGTRACE("intThresh.size() = " << intThresh.size());
909  }
910 
911 
912  double NeQuickIonoNavData ::
913  integrateGaussKronrod(double heightPt1, double heightPt2,
914  const Position& rxgeo, const Position& svgeo,
915  const MODIP& modip, double modipSta, CCIR& ccirData,
916  const CivilTime& when, double azu, double tolerance,
917  bool vertical, unsigned recursionLevel)
918  const
919  {
921  double rv = 0;
922  DEBUGTRACE("pResult(1) = " << scientific << rv);
923  // This code and the constants originate from section F.2.6.1
924  // \cite galileo:iono
927  // weights for K15 sample points
928  static const double wi[] = {
929  0.022935322010529224963732008058970,
930  0.063092092629978553290700663189204,
931  0.104790010322250183839876322541518,
932  0.140653259715525918745189590510238,
933  0.169004726639267902826583426598550,
934  0.190350578064785409913256402421014,
935  0.204432940075298892414161999234649,
936  0.209482141084727828012999174891714,
937  0.204432940075298892414161999234649,
938  0.190350578064785409913256402421014,
939  0.169004726639267902826583426598550,
940  0.140653259715525918745189590510238,
941  0.104790010322250183839876322541518,
942  0.063092092629978553290700663189204,
943  0.022935322010529224963732008058970
944  };
945  // weights for G7 sample points
946  static const double wig[] = {
947  0.129484966168869693270611432679082,
948  0.279705391489276667901467771423780,
949  0.381830050505118944950369775488975,
950  0.417959183673469387755102040816327,
951  0.381830050505118944950369775488975,
952  0.279705391489276667901467771423780,
953  0.129484966168869693270611432679082
954  };
955  // at what points the samples are used in integration process
956  // note that points 0-7 are the negative of points 9-15, in reverse.
957  static const double xi[] = {
958  -0.991455371120812639206854697526329,
959  -0.949107912342758524526189684047851,
960  -0.864864423359769072789712788640926,
961  -0.741531185599394439863864773280788,
962  -0.586087235467691130294144838258730,
963  -0.405845151377397166906606412076961,
964  -0.207784955007898467600689403773245,
965  0,
966  0.207784955007898467600689403773245,
967  0.405845151377397166906606412076961,
968  0.586087235467691130294144838258730,
969  0.741531185599394439863864773280788,
970  0.864864423359769072789712788640926,
971  0.949107912342758524526189684047851,
972  0.991455371120812639206854697526329
973  };
974  // half-difference
975  double h2 = (heightPt2 - heightPt1) / 2.0;
976  // mid-point
977  double hh = (heightPt2 + heightPt1) / 2.0;
978  // K15 integration results
979  double intk = 0.0;
980  // G7 integration results
981  double intg = 0.0;
982  // G7 counter/index
983  unsigned gind = 0;
984  DEBUGTRACE("mid_point=" << hh);
985  DEBUGTRACE("half_diff=" << h2);
986  // Iterating over K15, which is defined to have 15 values
987  for (unsigned i = 0; i < 15; i++)
988  {
989  double x = h2 * xi[i] + hh;
990  double y = 0;
991  DEBUGTRACE("i=" << i << " x=" << x);
992  if (vertical)
993  {
994  y = getVED(x, rxgeo, svgeo, modipSta, ccirData, when, azu);
995  }
996  else
997  {
998  y = getSED(x, rxgeo, svgeo, modip, ccirData, when, azu);
999  }
1000  DEBUGTRACE("GKI ED = " << scientific << y);
1001  // Accumulate on to the k15 total
1002  intk += y * wi[i];
1003  if (i % 2)
1004  {
1005  intg += y * wig[gind++];
1006  }
1007  }
1008  // Complete the calculation of the integration results
1009  intk = intk * h2;
1010  intg = intg * h2;
1011  // Check if the result is within tolerance
1012  if (((fabs(intk - intg) / intk) <= tolerance) ||
1013  (fabs(intk - intg) <= tolerance))
1014  {
1015  // Result is within tolerance, so return intk
1016  DEBUGTRACE("pResult(2) = " << scientific << intk);
1017  return intk;
1018  }
1019  else if (recursionLevel >= RecursionMax)
1020  {
1021  // No further integration allowed, return intk as best guess.
1022  DEBUGTRACE("pResult(3) = " << scientific << intk);
1023  return intk;
1024  }
1025  else
1026  {
1027  // Result is not within tolerance. Split portion into
1028  // equal halves and recurse.
1029  rv = integrateGaussKronrod(heightPt1, heightPt1 + h2, rxgeo, svgeo,
1030  modip, modipSta, ccirData, when, azu,
1031  tolerance, vertical, recursionLevel+1);
1032  DEBUGTRACE("pResult(4) = " << scientific << rv);
1033  rv += integrateGaussKronrod(heightPt1 + h2, heightPt2, rxgeo, svgeo,
1034  modip, modipSta, ccirData, when, azu,
1035  tolerance, vertical, recursionLevel+1);
1036  DEBUGTRACE("pResult(5) = " << scientific << rv);
1037  }
1038  return rv;
1039  }
1040 }
gnsstk::Position::Geodetic
@ Geodetic
geodetic latitude, longitude, and height above ellipsoid
Definition: Position.hpp:145
gnsstk::NavData::getSignalString
std::string getSignalString() const
Definition: NavData.cpp:86
NeQuickIonoNavDataConsts
NeQuickIonoNavDataConsts
Definition: NeQuickIonoNavData.cpp:107
gnsstk::AngleReduced
Definition: AngleReduced.hpp:60
TOP_APPROX_EPSILON
constexpr double TOP_APPROX_EPSILON
Topside electron density approximation epsilon per eq.127.
Definition: NeQuickIonoNavData.cpp:82
gnsstk::DEG2RAD
const double DEG2RAD
Multiply degrees by DEG2RAD to get radians.
Definition: GNSSconstants.hpp:64
gnsstk::Angle::rad
double rad() const
Get the angle in radians.
Definition: Angle.hpp:94
gnsstk::NeQuickIonoNavData::integrateGaussKronrod
double integrateGaussKronrod(double heightPt1, double heightPt2, const Position &rxgeo, const Position &svgeo, const MODIP &modip, double modipSta, CCIR &ccirData, const CivilTime &when, double azu, double tolerance, bool vertical, unsigned recursionLevel=0) const
Definition: NeQuickIonoNavData.cpp:913
TEC_SCALE_FACTOR
constexpr double TEC_SCALE_FACTOR
Scalar from integral to TEC per eq.151 and eq.202.
Definition: NeQuickIonoNavData.cpp:78
gnsstk::NavData::dump
virtual void dump(std::ostream &s, DumpDetail dl) const
Definition: NavData.cpp:79
F2TransFactorCoeffCount
@ F2TransFactorCoeffCount
Legendre coefficient count for transmission factor.
Definition: NeQuickIonoNavData.cpp:111
NeQuickIonoNavData.hpp
gnsstk::NeQuickIonoNavData::ModelParameters::ffoF1
double ffoF1
F1 layer critical frequency in MHz.
Definition: NeQuickIonoNavData.hpp:270
gnsstk::NeQuickIonoNavData::ModelParameters::ffoE
double ffoE
E layer critical frequency in MHz.
Definition: NeQuickIonoNavData.hpp:265
gnsstk::NeQuickIonoNavData::ModelParameters::effSolarZenithAngle
static Angle effSolarZenithAngle(const Position &pos, const CivilTime &when)
Definition: NeQuickIonoNavData.cpp:490
gnsstk::CarrierBand
CarrierBand
Definition: CarrierBand.hpp:54
RecursionMax
@ RecursionMax
Maximum integrateGaussKronrod recursion.
Definition: NeQuickIonoNavData.cpp:112
gnsstk::max
T max(const SparseMatrix< T > &SM)
Maximum element - return 0 if empty.
Definition: SparseMatrix.hpp:881
INTEG_EPSILON_S1_SA
constexpr double INTEG_EPSILON_S1_SA
Integration epsilon (tolerance) for the s1 to sa interval per 2.5.8.2.7.
Definition: NeQuickIonoNavData.cpp:88
gnsstk::AngleReduced::sin
double sin() const
Get the sine of this angle.
Definition: AngleReduced.hpp:94
DEBUGTRACE
#define DEBUGTRACE(EXPR)
Definition: DebugTrace.hpp:119
gnsstk::Position::geocentricLatitude
double geocentricLatitude() const noexcept
Definition: Position.cpp:398
gnsstk::NavData::getDumpTime
std::string getDumpTime(DumpDetail dl, const CommonTime &t) const
Definition: NavData.cpp:145
gnsstk::MODIP::stModip
double stModip(const Position &pos) const
Definition: MODIP.cpp:70
hmE
constexpr double hmE
E layer maximum density height in km.
Definition: NeQuickIonoNavData.cpp:100
gnsstk::NavData::getDumpTimeHdr
std::string getDumpTimeHdr(DumpDetail dl) const
Definition: NavData.cpp:127
gnsstk::PI
const double PI
GPS value of PI; also specified by GAL.
Definition: GNSSconstants.hpp:62
INTEGRATION_SECOND_POINT
constexpr double INTEGRATION_SECOND_POINT
TEC numerical integration second point in km point per 2.5.8.2.7.
Definition: NeQuickIonoNavData.cpp:86
FreqConv.hpp
gnsstk::Position::height
double height() const noexcept
return height above ellipsoid (meters) (Geodetic).
Definition: Position.cpp:455
gnsstk::NeQuickIonoNavData::idf
bool idf[5]
Ionospheric disturbance flag for regions 1-5 (0-4).
Definition: NeQuickIonoNavData.hpp:132
gnsstk
For Sinex::InputHistory.
Definition: BasicFramework.cpp:50
CRIT_FREQ_THRESH_E
constexpr double CRIT_FREQ_THRESH_E
E layer threshold for critical frequency to consider it zero per eq.82.
Definition: NeQuickIonoNavData.cpp:68
gnsstk::Angle
Definition: Angle.hpp:53
gnsstk::NeQuickIonoNavData::getSED
double getSED(double dist, const Position &rxgeo, const Position &svgeo, const MODIP &modip, CCIR &ccirData, const CivilTime &when, double azu) const
Definition: NeQuickIonoNavData.cpp:285
std::sin
double sin(gnsstk::Angle x)
Definition: Angle.hpp:144
NEEXP_MIN_VALUE
constexpr double NEEXP_MIN_VALUE
Minimum return value of neExp()
Definition: NeQuickIonoNavData.cpp:92
gnsstk::Position::geodeticLatitude
double geodeticLatitude() const noexcept
return geodetic latitude (degrees North).
Definition: Position.cpp:386
gnsstk::NeQuickIonoNavData::ModelParameters::ccir
CCIR & ccir
Reference to iono model data.
Definition: NeQuickIonoNavData.hpp:263
gnsstk::NavData::timeStamp
CommonTime timeStamp
Definition: NavData.hpp:173
gnsstk::NeQuickIonoNavData::ai
double ai[3]
Definition: NeQuickIonoNavData.hpp:125
gnsstk::Exception
Definition: Exception.hpp:151
gnsstk::AngleType::Rad
@ Rad
Value is in radians.
ELEC_DEN_SCALING
constexpr double ELEC_DEN_SCALING
Scalar for bottomside electron density per eq.115 and eq.121.
Definition: NeQuickIonoNavData.cpp:76
gnsstk::NeQuickIonoNavData::IntegrationParameters::IntegrationParameters
IntegrationParameters(const Position &rx, const Position &sv, const Position &Pp, bool vertical)
Definition: NeQuickIonoNavData.cpp:818
gnsstk::GalileoIonoEllipsoid::a_km
virtual double a_km() const noexcept
Definition: GalileoIonoEllipsoid.hpp:67
NEEXP_MAX_VALUE
constexpr double NEEXP_MAX_VALUE
Maximum return value of neExp()
Definition: NeQuickIonoNavData.cpp:94
gnsstk::AngleType::Sin
@ Sin
Value is the sine of the angle.
ExponentMax
@ ExponentMax
Maximum exponent for neExp().
Definition: NeQuickIonoNavData.cpp:114
y
page HOWTO subpage DoxygenGuide Documenting Your Code page DoxygenGuide Documenting Your Code todo Flesh out this document section doctips Tips for Documenting When defining make sure that the prototype is identical between the cpp and hpp including both the namespaces and the parameter names for you have std::string as the return type in the hpp file and string as the return type in the cpp Doxygen may get confused and autolink to the cpp version with no documentation If you don t use the same parameter names between the cpp and hpp that will also confuse Doxygen Don t put type information in return or param documentation It doesn t really add anything and will often cause Doxygen to complain and not produce the documentation< br > use note Do not put a comma after a param name unless you mean to document multiple parameters< br/> the output stream</code >< br/> y
Definition: DOCUMENTING.dox:15
gnsstk::NeQuickIonoNavData::IntegrationParameters::intThresh
std::vector< double > intThresh
Definition: NeQuickIonoNavData.hpp:317
BasicTimeSystemConverter.hpp
COEFF_THRESH
constexpr double COEFF_THRESH
Threshold for solar flux coefficients to consider them zero and unavailable.
Definition: NeQuickIonoNavData.cpp:64
gnsstk::Position::radius
double radius() const noexcept
Definition: Position.cpp:444
gnsstk::NeQuickIonoNavData::ModelParameters::fNmF1
double fNmF1
F1 layer maximum electron density in el m**-2.
Definition: NeQuickIonoNavData.hpp:271
ExponentMin
@ ExponentMin
Minimum exponent for neExp().
Definition: NeQuickIonoNavData.cpp:113
gnsstk::NeQuickIonoNavData::ModelParameters::fNmF2
double fNmF2
F2 layer maximum electron density in el m**-2.
Definition: NeQuickIonoNavData.hpp:276
gnsstk::AngleReduced::cos
double cos() const
Get the cosine of this angle.
Definition: AngleReduced.hpp:98
gnsstk::CommonTime
Definition: CommonTime.hpp:84
log
#define log
Definition: DiscCorr.cpp:625
gnsstk::NeQuickIonoNavData::ModelParameters
Aggregate the model parameters as defined in section 2.5.5.
Definition: NeQuickIonoNavData.hpp:154
INTEG_EPSILON
constexpr double INTEG_EPSILON
Relative difference between integration steps per 2.5.8.2.8.
Definition: NeQuickIonoNavData.cpp:74
gnsstk::AngleType::Deg
@ Deg
Value is in degrees.
gnsstk::NeQuickIonoNavData::ModelParameters::electronDensityTop
double electronDensityTop(const Position &pos)
Definition: NeQuickIonoNavData.cpp:743
gnsstk::NeQuickIonoNavData::getIonoCorr
double getIonoCorr(const CommonTime &when, const Position &rxgeo, const Position &svgeo, CarrierBand band) const override
Definition: NeQuickIonoNavData.cpp:183
gnsstk::GalileoIonoEllipsoid
Definition: GalileoIonoEllipsoid.hpp:57
gnsstk::Angle::deg
double deg() const
Get the angle in degrees.
Definition: Angle.hpp:98
std::cos
double cos(gnsstk::Angle x)
Definition: Angle.hpp:146
gnsstk::NeQuickIonoNavData::ModelParameters::thickness
void thickness()
Definition: NeQuickIonoNavData.cpp:622
example4.pos
pos
Definition: example4.py:125
gnsstk::MODIP
Definition: MODIP.hpp:54
gnsstk::DumpDetail::Full
@ Full
Include all detailed information.
gnsstk::NeQuickIonoNavData::ModelParameters::fNmE
double fNmE
E layer maximum electron density in el m**-2.
Definition: NeQuickIonoNavData.hpp:266
gnsstk::NeQuickIonoNavData::ModelParameters::solarZenithAngle
static Angle solarZenithAngle(const Position &pos, const CivilTime &when)
Definition: NeQuickIonoNavData.cpp:474
gnsstk::TrackingCode::P
@ P
Legacy GPS precise code.
gnsstk::NeQuickIonoNavData::ModelParameters::solarDeclination
static AngleReduced solarDeclination(const CivilTime &when)
Definition: NeQuickIonoNavData.cpp:452
gnsstk::TimeSystem::UTC
@ UTC
Coordinated Universal Time (e.g., from NTP)
gnsstk::NeQuickIonoNavData::ModelParameters::exosphereAdjust
void exosphereAdjust(unsigned month)
Definition: NeQuickIonoNavData.cpp:636
F2LayerLongCoeffCount
@ F2LayerLongCoeffCount
Legendre coefficient count for longitude.
Definition: NeQuickIonoNavData.cpp:110
gnsstk::CivilTime
Definition: CivilTime.hpp:55
gnsstk::NeQuickIonoNavData::ModelParameters::legendre
void legendre(double modip_u, const Position &pos)
Definition: NeQuickIonoNavData.cpp:503
TECU_SCALE_FACTOR
constexpr double TECU_SCALE_FACTOR
Scalar from TEC Units to electrons per square meter.
Definition: NeQuickIonoNavData.cpp:80
gnsstk::TimeTag::changeTimeSystem
bool changeTimeSystem(TimeSystem timeSys, TimeSystemConverter *conv)
Definition: TimeTag.cpp:181
gnsstk::NeQuickIonoNavData::ModelParameters::electronDensityBottom
double electronDensityBottom(const Position &pos)
Definition: NeQuickIonoNavData.cpp:766
tan30
constexpr double tan30
tangent of 30 degrees, used in the Gauss numerical integration
Definition: NeQuickIonoNavData.cpp:96
GNSSTK_ASSERT
#define GNSSTK_ASSERT(CONDITION)
Provide an "ASSERT" type macro.
Definition: Exception.hpp:373
CRIT_FREQ_THRESH
constexpr double CRIT_FREQ_THRESH
Threshold for critical frequency to consider it zero.
Definition: NeQuickIonoNavData.cpp:66
gnsstk::BasicTimeSystemConverter
Definition: BasicTimeSystemConverter.hpp:51
gnsstk::DumpDetail
DumpDetail
Specify level of detail for dump output.
Definition: DumpDetail.hpp:51
gnsstk::NeQuickIonoNavData::IntegrationParameters
Class to contain data used when integrating TEC.
Definition: NeQuickIonoNavData.hpp:291
BEbot
constexpr double BEbot
E layer bottom-side thickness in km.
Definition: NeQuickIonoNavData.cpp:102
INTEGRATION_FIRST_POINT
constexpr double INTEGRATION_FIRST_POINT
TEC numerical integration first point in km point per 2.5.8.2.7.
Definition: NeQuickIonoNavData.cpp:84
gnsstk::NeQuickIonoNavData::ModelParameters::height
void height()
Definition: NeQuickIonoNavData.cpp:603
gnsstk::NeQuickIonoNavData::ModelParameters::fAzr
double fAzr
Effective sunspot number.
Definition: NeQuickIonoNavData.hpp:264
gnsstk::CCIR
Definition: CCIR.hpp:59
gnsstk::Position::longitude
double longitude() const noexcept
Definition: Position.cpp:432
DebugTrace.hpp
std
Definition: Angle.hpp:142
gnsstk::CivilTime::month
int month
Definition: CivilTime.hpp:199
gnsstk::NeQuickIonoNavData::neExp
static double neExp(double x)
Definition: NeQuickIonoNavData.cpp:322
DEFAULT_IONO_LEVEL
constexpr double DEFAULT_IONO_LEVEL
Ref Eq.17 of Galileo Ionospheric Model.
Definition: NeQuickIonoNavData.cpp:70
gnsstk::NeQuickIonoNavData::dump
void dump(std::ostream &s, DumpDetail dl) const override
Definition: NeQuickIonoNavData.cpp:129
gnsstk::NeQuickIonoNavData::ModelParameters::ffoF2
double ffoF2
F2 layer critical frequency in MHz.
Definition: NeQuickIonoNavData.hpp:275
gnsstk::NeQuickIonoNavData::elModel
GalileoIonoEllipsoid elModel
Definition: NeQuickIonoNavData.hpp:394
gnsstk::Position
Definition: Position.hpp:136
gnsstk::NeQuickIonoNavData::getVED
double getVED(double dist, const Position &rxgeo, const Position &svgeo, double modip_u, CCIR &ccirData, const CivilTime &when, double azu) const
Definition: NeQuickIonoNavData.cpp:304
gnsstk::DumpDetail::OneLine
@ OneLine
Limit output to minimal information on a single line.
gnsstk::getFrequency
double getFrequency(CarrierBand band)
Definition: FreqConv.cpp:43
GNSSTK_THROW
#define GNSSTK_THROW(exc)
Definition: Exception.hpp:366
DEBUGTRACE_FUNCTION
#define DEBUGTRACE_FUNCTION()
Definition: DebugTrace.hpp:117
gnsstk::NeQuickIonoNavData::ModelParameters::electronDensity
double electronDensity(const Position &pos)
Definition: NeQuickIonoNavData.cpp:724
gnsstk::NeQuickIonoNavData::ModelParameters::peakAmplitudes
void peakAmplitudes()
Definition: NeQuickIonoNavData.cpp:682
example6.month
month
Definition: example6.py:65
INTEG_EPSILON_SA_S2
constexpr double INTEG_EPSILON_SA_S2
Integration epsilon (tolerance) for the sa to sb/s2 interval per 2.5.8.2.7.
Definition: NeQuickIonoNavData.cpp:90
gnsstk::NeQuickIonoNavData::ModelParameters::ModelParameters
ModelParameters(double modip_u, const Position &pos, double az, CCIR &ccirData, const CivilTime &when)
Definition: NeQuickIonoNavData.cpp:337
gnsstk::Position::getRayPosition
Position getRayPosition(double dist, const Position &target) const
Definition: Position.cpp:1604
gnsstk::Position::getRayPerigee
Position getRayPerigee(const Position &target) const
Definition: Position.cpp:1552
FREQ2NE_D
constexpr double FREQ2NE_D
Used to calculate associated electron density from critical frequency.
Definition: NeQuickIonoNavData.cpp:98
MODIP.hpp
gnsstk::NeQuickIonoNavData::IntegrationParameters::integHeights
std::vector< double > integHeights
Definition: NeQuickIonoNavData.hpp:314
ABOVE_ELEV_EPSILON
constexpr double ABOVE_ELEV_EPSILON
How close an elevation angle needs to be to +/- 90 to be considered polar.
Definition: NeQuickIonoNavData.cpp:72
F2LayerMODIPCoeffCount
@ F2LayerMODIPCoeffCount
Legendre coefficient count for mod dip lat.
Definition: NeQuickIonoNavData.cpp:109
gnsstk::TimeTag::getTimeSystem
TimeSystem getTimeSystem() const
Obtain time system info (enum).
Definition: TimeTag.hpp:169
gnsstk::NeQuickIonoNavData::getEffIonoLevel
double getEffIonoLevel(double modip_u) const
Definition: NeQuickIonoNavData.cpp:263
TimeString.hpp
gnsstk::NeQuickIonoNavData::ModelParameters::fXeff
Angle fXeff
Effective solar zenith angle.
Definition: NeQuickIonoNavData.hpp:281
gnsstk::CivilTime::getUTHour
double getUTHour() const
Get the "universal time"-esque fractional hour of day.
Definition: CivilTime.hpp:195
gnsstk::CCIR::fourier
void fourier(const CommonTime &when, double effSunSpots)
Definition: CCIR.cpp:81
gnsstk::NeQuickIonoNavData::getTEC
double getTEC(const CommonTime &when, const Position &rxgeo, const Position &svgeo) const
Definition: NeQuickIonoNavData.cpp:198


gnsstk
Author(s):
autogenerated on Wed Oct 25 2023 02:40:40