1   /* Copyright 2002-2022 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.models.earth.weather;
18  
19  import org.hipparchus.util.FastMath;
20  import org.hipparchus.util.SinCos;
21  import org.orekit.annotation.DefaultDataContext;
22  import org.orekit.data.DataContext;
23  import org.orekit.frames.Frame;
24  import org.orekit.models.earth.Geoid;
25  import org.orekit.models.earth.ReferenceEllipsoid;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.time.DateTimeComponents;
28  import org.orekit.utils.LegendrePolynomials;
29  
30  /** The Global Pressure and Temperature model.
31   * This model is an empirical model that provides the temperature and the pressure depending
32   * the latitude and the longitude of the station.
33   * <p>
34   * The Global Pressure and Temperature model is based on spherical harmonics up
35   * to degree and order of 9. The residual values ​​of this model can reach 20 hPa
36   * for pressure and 10 ° C for temperature. They are significant for higher latitudes and
37   * small near the equator (Böhm, 2007)
38   * </p>
39   *
40   * @see "J. Böhm, R. Heinkelmann, and H. Schuh (2007),
41   * Short Note: A global model of pressure and temperature for geodetic applications. J Geod,
42   * doi:10.1007/s00190-007-0135-3."
43   *
44   * @author Bryan Cazabonne
45   *
46   */
47  public class GlobalPressureTemperatureModel implements WeatherModel {
48  
49      /** Temperature gradient (°C/m). */
50      private static final double TEMPERATURE_GRADIENT = -6.5e-3;
51  
52      /** Geodetic latitude, in radians. */
53      private final double latitude;
54  
55      /** Geodetic longitude, in radians. */
56      private final double longitude;
57  
58      /** Temperature site, in kelvins. */
59      private double temperature;
60  
61      /** Pressure site, in hPa. */
62      private double pressure;
63  
64      /** Body frame related to body shape. */
65      private final Frame bodyFrame;
66  
67      /** Data context for time and gravity. */
68      private final DataContext dataContext;
69  
70      /** Build a new instance.
71       * <p>
72       * At the initialization the values of the pressure and the temperature are set to NaN.
73       * The user has to call {@link #weatherParameters(double, AbsoluteDate)} method before using
74       * the values of the pressure and the temperature.
75       * </p>
76       *
77       * <p>This method uses the {@link DataContext#getDefault() default data context}.
78       *
79       * @param latitude geodetic latitude, in radians
80       * @param longitude geodetic longitude, in radians
81       * @param bodyFrame the frame to attach to the ellipsoid. The origin is at
82       *                  the center of mass, the z axis is the minor axis.
83       * @see #GlobalPressureTemperatureModel(double, double, Frame, DataContext)
84       */
85      @DefaultDataContext
86      public GlobalPressureTemperatureModel(final double latitude, final double longitude, final Frame bodyFrame) {
87          this(latitude, longitude, bodyFrame, DataContext.getDefault());
88      }
89  
90      /** Build a new instance.
91       * <p>
92       * At the initialization the values of the pressure and the temperature are set to NaN.
93       * The user has to call {@link #weatherParameters(double, AbsoluteDate)} method before using
94       * the values of the pressure and the temperature.
95       * </p>
96       * @param latitude geodetic latitude, in radians
97       * @param longitude geodetic longitude, in radians
98       * @param bodyFrame the frame to attach to the ellipsoid. The origin is at
99       * @param dataContext to use for time and gravity.
100      * @since 10.1
101      */
102     public GlobalPressureTemperatureModel(final double latitude,
103                                           final double longitude,
104                                           final Frame bodyFrame,
105                                           final DataContext dataContext) {
106         this.bodyFrame   = bodyFrame;
107         this.latitude    = latitude;
108         this.longitude   = longitude;
109         this.temperature = Double.NaN;
110         this.pressure    = Double.NaN;
111         this.dataContext = dataContext;
112     }
113 
114     /** Get the atmospheric temperature of the station depending its position.
115      * @return the temperature in kelvins
116      */
117     public double getTemperature() {
118         return temperature;
119     }
120 
121     /** Get the atmospheric pressure of the station depending its position.
122      * @return the pressure in hPa
123      */
124     public double getPressure() {
125         return pressure;
126     }
127 
128     @Override
129     @DefaultDataContext
130     public void weatherParameters(final double height, final AbsoluteDate date) {
131 
132         // Day of year computation
133         final DateTimeComponents dtc =
134                 date.getComponents(dataContext.getTimeScales().getUTC());
135         final int dofyear = dtc.getDate().getDayOfYear();
136 
137         // Reference day: 28 January 1980 (Niell, 1996)
138         final int t0 = 28;
139         final double coef = ((dofyear + 1 - t0) / 365.25) * 2 * FastMath.PI;
140         final double cosCoef = FastMath.cos(coef);
141 
142         // Compute Legendre Polynomials Pnm(sin(phi))
143         final int degree = 9;
144         final int order  = 9;
145         final LegendrePolynomials p = new LegendrePolynomials(degree, order, FastMath.sin(latitude));
146 
147         // Geoid for height computation
148         final Geoid geoid = new Geoid(
149                 dataContext.getGravityFields().getNormalizedProvider(degree, order),
150                 ReferenceEllipsoid.getWgs84(bodyFrame));
151 
152         // Corrected height
153         final double correctedheight = FastMath.max(0.0, height - geoid.getUndulation(latitude, longitude, date));
154 
155         // Eq. 4 (Ref)
156         double meanT0      = 0.0;
157         double amplitudeT0 = 0.0;
158         double meanP0      = 0.0;
159         double amplitudeP0 = 0.0;
160         final ABCoefficients abCoef = new ABCoefficients();
161         int j = 0;
162         for (int n = 0; n <= 9; n++) {
163             for (int m = 0; m <= n; m++) {
164                 final SinCos sc = FastMath.sinCos(m * longitude);
165                 final double pCosmLambda = p.getPnm(n, m) * sc.cos();
166                 final double pSinmLambda = p.getPnm(n, m) * sc.sin();
167 
168                 meanT0      = meanT0 +
169                                 (abCoef.getAnmTemperatureMean(j) * pCosmLambda + abCoef.getBnmTemperatureMean(j) * pSinmLambda);
170                 amplitudeT0 = amplitudeT0 +
171                                 (abCoef.getAnmTemperatureAmpl(j) * pCosmLambda + abCoef.getBnmTemperatureAmpl(j) * pSinmLambda);
172                 meanP0      = meanP0 +
173                                 (abCoef.getAnmPressureMean(j) * pCosmLambda + abCoef.getBnmPressureMean(j) * pSinmLambda);
174                 amplitudeP0 = amplitudeP0 +
175                                 (abCoef.getAnmPressureAmpl(j) * pCosmLambda + abCoef.getBnmPressureAmpl(j) * pSinmLambda);
176 
177                 j = j + 1;
178             }
179         }
180 
181         // Eq. 3 (Ref)
182         final double temp0 = meanT0 + amplitudeT0 * cosCoef;
183         final double pres0 = meanP0 + amplitudeP0 * cosCoef;
184 
185         // Compute pressure and temperature Eq. 1 and 2 (Ref)
186         final double degrees = temp0 + TEMPERATURE_GRADIENT * correctedheight;
187         this.temperature = degrees + 273.15;
188         this.pressure    = pres0 * FastMath.pow(1.0 - correctedheight * 0.0000226, 5.225);
189     }
190 
191     private static class ABCoefficients {
192 
193         /** Mean A<sub>nm</sub> coefficients for the pressure. */
194         private static final double[] A_PRESSURE_MEAN = {
195             1.0108e+03,
196             8.4886e+00,
197             1.4799e+00,
198             -1.3897e+01,
199             3.7516e-03,
200             -1.4936e-01,
201             1.2232e+01,
202             -7.6615e-01,
203             -6.7699e-02,
204             8.1002e-03,
205             -1.5874e+01,
206             3.6614e-01,
207             -6.7807e-02,
208             -3.6309e-03,
209             5.9966e-04,
210             4.8163e+00,
211             -3.7363e-01,
212             -7.2071e-02,
213             1.9998e-03,
214             -6.2385e-04,
215             -3.7916e-04,
216             4.7609e+00,
217             -3.9534e-01,
218             8.6667e-03,
219             1.1569e-02,
220             1.1441e-03,
221             -1.4193e-04,
222             -8.5723e-05,
223             6.5008e-01,
224             -5.0889e-01,
225             -1.5754e-02,
226             -2.8305e-03,
227             5.7458e-04,
228             3.2577e-05,
229             -9.6052e-06,
230             -2.7974e-06,
231             1.3530e+00,
232             -2.7271e-01,
233             -3.0276e-04,
234             3.6286e-03,
235             -2.0398e-04,
236             1.5846e-05,
237             -7.7787e-06,
238             1.1210e-06,
239             9.9020e-08,
240             5.5046e-01,
241             -2.7312e-01,
242             3.2532e-03,
243             -2.4277e-03,
244             1.1596e-04,
245             2.6421e-07,
246             -1.3263e-06,
247             2.7322e-07,
248             1.4058e-07,
249             4.9414e-09
250         };
251 
252         /** Mean B<sub>nm</sub> coefficients for the pressure. */
253         private static final double[] B_PRESSURE_MEAN = {
254             0.0000e+00,
255             0.0000e+00,
256             -1.2878e+00,
257             0.0000e+00,
258             7.0444e-01,
259             3.3222e-01,
260             0.0000e+00,
261             -2.9636e-01,
262             7.2248e-03,
263             7.9655e-03,
264             0.0000e+00,
265             1.0854e+00,
266             1.1145e-02,
267             -3.6513e-02,
268             3.1527e-03,
269             0.0000e+00,
270             -4.8434e-01,
271             5.2023e-02,
272             -1.3091e-02,
273             1.8515e-03,
274             1.5422e-04,
275             0.0000e+00,
276             6.8298e-01,
277             2.5261e-03,
278             -9.9703e-04,
279             -1.0829e-03,
280             +1.7688e-04,
281             -3.1418e-05,
282             +0.0000e+00,
283             -3.7018e-01,
284             4.3234e-02,
285             7.2559e-03,
286             3.1516e-04,
287             2.0024e-05,
288             -8.0581e-06,
289             -2.3653e-06,
290             0.0000e+00,
291             1.0298e-01,
292             -1.5086e-02,
293             5.6186e-03,
294             3.2613e-05,
295             4.0567e-05,
296             -1.3925e-06,
297             -3.6219e-07,
298             -2.0176e-08,
299             0.0000e+00,
300             -1.8364e-01,
301             1.8508e-02,
302             7.5016e-04,
303             -9.6139e-05,
304             -3.1995e-06,
305             1.3868e-07,
306             -1.9486e-07,
307             3.0165e-10,
308             -6.4376e-10
309         };
310 
311         /** Amplitude A<sub>nm</sub> coefficients for the pressure. */
312         private static final double[] A_PRESSURE_AMPLITUDE = {
313             -1.0444e-01,
314             1.6618e-01,
315             -6.3974e-02,
316             1.0922e+00,
317             5.7472e-01,
318             -3.0277e-01,
319             -3.5087e+00,
320             7.1264e-03,
321             -1.4030e-01,
322             3.7050e-02,
323             4.0208e-01,
324             -3.0431e-01,
325             -1.3292e-01,
326             4.6746e-03,
327             -1.5902e-04,
328             2.8624e+00,
329             -3.9315e-01,
330             -6.4371e-02,
331             1.6444e-02,
332             -2.3403e-03,
333             4.2127e-05,
334             1.9945e+00,
335             -6.0907e-01,
336             -3.5386e-02,
337             -1.0910e-03,
338             -1.2799e-04,
339             4.0970e-05,
340             2.2131e-05,
341             -5.3292e-01,
342             -2.9765e-01,
343             -3.2877e-02,
344             1.7691e-03,
345             5.9692e-05,
346             3.1725e-05,
347             2.0741e-05,
348             -3.7622e-07,
349             2.6372e+00,
350             -3.1165e-01,
351             1.6439e-02,
352             2.1633e-04,
353             1.7485e-04,
354             2.1587e-05,
355             6.1064e-06,
356             -1.3755e-08,
357             -7.8748e-08,
358             -5.9152e-01,
359             -1.7676e-01,
360             8.1807e-03,
361             1.0445e-03,
362             2.3432e-04,
363             9.3421e-06,
364             2.8104e-06,
365             -1.5788e-07,
366             -3.0648e-08,
367             2.6421e-10
368         };
369 
370         /** Amplitude B<sub>nm</sub> coefficients for the pressure. */
371         private static final double[] B_PRESSURE_AMPLITUDE = {
372             0.0000e+00,
373             0.0000e+00,
374             9.3340e-01,
375             0.0000e+00,
376             8.2346e-01,
377             2.2082e-01,
378             0.0000e+00,
379             9.6177e-01,
380             -1.5650e-02,
381             1.2708e-03,
382             0.0000e+00,
383             -3.9913e-01,
384             2.8020e-02,
385             2.8334e-02,
386             8.5980e-04,
387             0.0000e+00,
388             3.0545e-01,
389             -2.1691e-02,
390             6.4067e-04,
391             -3.6528e-05,
392             -1.1166e-04,
393             0.0000e+00,
394             -7.6974e-02,
395             -1.8986e-02,
396             +5.6896e-03,
397             -2.4159e-04,
398             -2.3033e-04,
399             -9.6783e-06,
400             0.0000e+00,
401             -1.0218e-01,
402             -1.3916e-02,
403             -4.1025e-03,
404             -5.1340e-05,
405             -7.0114e-05,
406             -3.3152e-07,
407             1.6901e-06,
408             0.0000e+00,
409             -1.2422e-02,
410             +2.5072e-03,
411             +1.1205e-03,
412             -1.3034e-04,
413             -2.3971e-05,
414             -2.6622e-06,
415             5.7852e-07,
416             4.5847e-08,
417             0.0000e+00,
418             4.4777e-02,
419             -3.0421e-03,
420             2.6062e-05,
421             -7.2421e-05,
422             1.9119e-06,
423             3.9236e-07,
424             2.2390e-07,
425             2.9765e-09,
426             -4.6452e-09
427         };
428 
429         /** Mean A<sub>nm</sub> coefficients for the temperature. */
430         private static final double[] A_TEMPERATURE_MEAN = {
431             1.6257e+01,
432             2.1224e+00,
433             9.2569e-01,
434             -2.5974e+01,
435             1.4510e+00,
436             9.2468e-02,
437             -5.3192e-01,
438             2.1094e-01,
439             -6.9210e-02,
440             -3.4060e-02,
441             -4.6569e+00,
442             2.6385e-01,
443             -3.6093e-02,
444             1.0198e-02,
445             -1.8783e-03,
446             7.4983e-01,
447             1.1741e-01,
448             3.9940e-02,
449             5.1348e-03,
450             5.9111e-03,
451             8.6133e-06,
452             6.3057e-01,
453             1.5203e-01,
454             3.9702e-02,
455             4.6334e-03,
456             2.4406e-04,
457             1.5189e-04,
458             1.9581e-07,
459             5.4414e-01,
460             3.5722e-01,
461             5.2763e-02,
462             4.1147e-03,
463             -2.7239e-04,
464             -5.9957e-05,
465             1.6394e-06,
466             -7.3045e-07,
467             -2.9394e+00,
468             5.5579e-02,
469             1.8852e-02,
470             3.4272e-03,
471             -2.3193e-05,
472             -2.9349e-05,
473             3.6397e-07,
474             2.0490e-06,
475             -6.4719e-08,
476             -5.2225e-01,
477             2.0799e-01,
478             1.3477e-03,
479             3.1613e-04,
480             -2.2285e-04,
481             -1.8137e-05,
482             -1.5177e-07,
483             6.1343e-07,
484             7.8566e-08,
485             1.0749e-09
486         };
487 
488         /** Mean B<sub>nm</sub> coefficients for the temperature. */
489         private static final double[] B_TEMPERATURE_MEAN = {
490             0.0000e+00,
491             0.0000e+00,
492             1.0210e+00,
493             0.0000e+00,
494             6.0194e-01,
495             1.2292e-01,
496             0.0000e+00,
497             -4.2184e-01,
498             1.8230e-01,
499             4.2329e-02,
500             0.0000e+00,
501             9.3312e-02,
502             9.5346e-02,
503             -1.9724e-03,
504             5.8776e-03,
505             0.0000e+00,
506             -2.0940e-01,
507             3.4199e-02,
508             -5.7672e-03,
509             -2.1590e-03,
510             5.6815e-04,
511             0.0000e+00,
512             2.2858e-01,
513             1.2283e-02,
514             -9.3679e-03,
515             -1.4233e-03,
516             -1.5962e-04,
517             4.0160e-05,
518             0.0000e+00,
519             3.6353e-02,
520             -9.4263e-04,
521             -3.6762e-03,
522             5.8608e-05,
523             -2.6391e-05,
524             3.2095e-06,
525             -1.1605e-06,
526             0.0000e+00,
527             1.6306e-01,
528             1.3293e-02,
529             -1.1395e-03,
530             5.1097e-05,
531             3.3977e-05,
532             7.6449e-06,
533             -1.7602e-07,
534             -7.6558e-08,
535             0.0000e+00,
536             -4.5415e-02,
537             -1.8027e-02,
538             3.6561e-04,
539             -1.1274e-04,
540             1.3047e-05,
541             2.0001e-06,
542             -1.5152e-07,
543             -2.7807e-08,
544             7.7491e-09
545         };
546 
547         /** Amplitude A<sub>nm</sub> coefficients for the temperature. */
548         private static final double[] A_TEMPERATURE_AMPLITUDE = {
549             -1.8654e+00,
550             -9.0041e+00,
551             -1.2974e-01,
552             -3.6053e+00,
553             2.0284e-02,
554             2.1872e-01,
555             -1.3015e+00,
556             4.0355e-01,
557             2.2216e-01,
558             -4.0605e-03,
559             1.9623e+00,
560             4.2887e-01,
561             2.1437e-01,
562             -1.0061e-02,
563             -1.1368e-03,
564             -6.9235e-02,
565             5.6758e-01,
566             1.1917e-01,
567             -7.0765e-03,
568             3.0017e-04,
569             3.0601e-04,
570             1.6559e+00,
571             2.0722e-01,
572             6.0013e-02,
573             1.7023e-04,
574             -9.2424e-04,
575             1.1269e-05,
576             -6.9911e-06,
577             -2.0886e+00,
578             -6.7879e-02,
579             -8.5922e-04,
580             -1.6087e-03,
581             -4.5549e-05,
582             3.3178e-05,
583             -6.1715e-06,
584             -1.4446e-06,
585             -3.7210e-01,
586             1.5775e-01,
587             -1.7827e-03,
588             -4.4396e-04,
589             2.2844e-04,
590             -1.1215e-05,
591             -2.1120e-06,
592             -9.6421e-07,
593             -1.4170e-08,
594             7.8720e-01,
595             -4.4238e-02,
596             -1.5120e-03,
597             -9.4119e-04,
598             4.0645e-06,
599             -4.9253e-06,
600             -1.8656e-06,
601             -4.0736e-07,
602             -4.9594e-08,
603             1.6134e-09
604         };
605 
606         /** Amplitude B<sub>nm</sub> coefficients for the temperature. */
607         private static final double[] B_TEMPERATURE_AMPLITUDE = {
608             0.0000e+00,
609             0.0000e+00,
610             -8.9895e-01,
611             0.0000e+00,
612             -1.0790e+00,
613             -1.2699e-01,
614             0.0000e+00,
615             -5.9033e-01,
616             3.4865e-02,
617             -3.2614e-02,
618             0.0000e+00,
619             -2.4310e-02,
620             1.5607e-02,
621             -2.9833e-02,
622             -5.9048e-03,
623             0.0000e+00,
624             2.8383e-01,
625             4.0509e-02,
626             -1.8834e-02,
627             -1.2654e-03,
628             -1.3794e-04,
629             0.0000e+00,
630             1.3306e-01,
631             3.4960e-02,
632             -3.6799e-03,
633             -3.5626e-04,
634             1.4814e-04,
635             3.7932e-06,
636             0.0000e+00,
637             2.0801e-01,
638             6.5640e-03,
639             -3.4893e-03,
640             -2.7395e-04,
641             7.4296e-05,
642             -7.9927e-06,
643             -1.0277e-06,
644             0.0000e+00,
645             3.6515e-02,
646             -7.4319e-03,
647             -6.2873e-04,
648             8.2461e-05,
649             3.1095e-05,
650             -5.3860e-07,
651             -1.2055e-07,
652             -1.1517e-07,
653             0.0000e+00,
654             3.1404e-02,
655             1.5580e-02,
656             -1.1428e-03,
657             3.3529e-05,
658             1.0387e-05,
659             -1.9378e-06,
660             -2.7327e-07,
661             7.5833e-09,
662             -9.2323e-09
663         };
664 
665         /** Build a new instance. */
666         ABCoefficients() {
667 
668         }
669 
670         /** Get the value of the mean A<sub>nm</sub> pressure coefficient for the given index.
671          * @param index index
672          * @return the mean A<sub>nm</sub> pressure coefficient for the given index
673          */
674         public double getAnmPressureMean(final int index) {
675             return A_PRESSURE_MEAN[index];
676         }
677 
678         /** Get the value of the mean B<sub>nm</sub> pressure coefficient for the given index.
679          * @param index index
680          * @return the mean B<sub>nm</sub> pressure coefficient for the given index
681          */
682         public double getBnmPressureMean(final int index) {
683             return B_PRESSURE_MEAN[index];
684         }
685 
686         /** Get the value of the amplitude A<sub>nm</sub> pressure coefficient for the given index.
687          * @param index index
688          * @return the amplitude A<sub>nm</sub> pressure coefficient for the given index.
689          */
690         public double getAnmPressureAmpl(final int index) {
691             return A_PRESSURE_AMPLITUDE[index];
692         }
693 
694         /** Get the value of the amplitude B<sub>nm</sub> pressure coefficient for the given index.
695          * @param index index
696          * @return the amplitude B<sub>nm</sub> pressure coefficient for the given index
697          */
698         public double getBnmPressureAmpl(final int index) {
699             return B_PRESSURE_AMPLITUDE[index];
700         }
701 
702         /** Get the value of the mean A<sub>nm</sub> temperature coefficient for the given index.
703          * @param index index
704          * @return the mean A<sub>nm</sub> temperature coefficient for the given index
705          */
706         public double getAnmTemperatureMean(final int index) {
707             return A_TEMPERATURE_MEAN[index];
708         }
709 
710         /** Get the value of the mean B<sub>nm</sub> temperature coefficient for the given index.
711          * @param index index
712          * @return the mean B<sub>nm</sub> temperature coefficient for the given index
713          */
714         public double getBnmTemperatureMean(final int index) {
715             return B_TEMPERATURE_MEAN[index];
716         }
717 
718         /** Get the value of the amplitude A<sub>nm</sub> temperature coefficient for the given index.
719          * @param index index
720          * @return the amplitude A<sub>nm</sub> temperature coefficient for the given index.
721          */
722         public double getAnmTemperatureAmpl(final int index) {
723             return A_TEMPERATURE_AMPLITUDE[index];
724         }
725 
726         /** Get the value of the amplitude B<sub>nm</sub> temperature coefficient for the given index.
727          * @param index index
728          * @return the amplitude B<sub>nm</sub> temperature coefficient for the given index
729          */
730         public double getBnmTemperatureAmpl(final int index) {
731             return B_TEMPERATURE_AMPLITUDE[index];
732         }
733     }
734 
735 }