1   /* Copyright 2002-2021 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.forces.radiation;
18  
19  import java.util.List;
20  import java.util.stream.Stream;
21  
22  import org.hipparchus.Field;
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.analysis.polynomials.PolynomialFunction;
25  import org.hipparchus.analysis.polynomials.PolynomialsUtils;
26  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
27  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28  import org.hipparchus.geometry.euclidean.threed.Rotation;
29  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
30  import org.hipparchus.geometry.euclidean.threed.Vector3D;
31  import org.hipparchus.util.FastMath;
32  import org.hipparchus.util.FieldSinCos;
33  import org.hipparchus.util.MathUtils;
34  import org.hipparchus.util.SinCos;
35  import org.orekit.annotation.DefaultDataContext;
36  import org.orekit.bodies.OneAxisEllipsoid;
37  import org.orekit.data.DataContext;
38  import org.orekit.forces.AbstractForceModel;
39  import org.orekit.frames.FieldTransform;
40  import org.orekit.frames.Frame;
41  import org.orekit.frames.Transform;
42  import org.orekit.propagation.FieldSpacecraftState;
43  import org.orekit.propagation.SpacecraftState;
44  import org.orekit.propagation.events.EventDetector;
45  import org.orekit.propagation.events.FieldEventDetector;
46  import org.orekit.time.AbsoluteDate;
47  import org.orekit.time.FieldAbsoluteDate;
48  import org.orekit.time.TimeScale;
49  import org.orekit.utils.Constants;
50  import org.orekit.utils.ExtendedPVCoordinatesProvider;
51  import org.orekit.utils.ParameterDriver;
52  
53  /** The Knocke Earth Albedo and IR emission force model.
54   * <p>
55   * This model is based on "EARTH RADIATION PRESSURE EFFECTS ON SATELLITES", 1988, by P. C. Knocke, J. C. Ries, and B. D. Tapley.
56   * </p> <p>
57   * This model represents the effects of radiation pressure coming from the Earth.
58   * It considers Solar radiation which has been reflected by Earth (albedo) and Earth infrared emissions.
59   * The planet is considered as a sphere and is divided into elementary areas.
60   * Each elementary area is considered as a plane and emits radiation according to Lambert's law.
61   * The flux the satellite receives is then equal to the sum of the elementary fluxes coming from Earth.
62   * </p> <p>
63   * The radiative model of the satellite, and its ability to diffuse, reflect  or absorb radiation is handled
64   * by a {@link RadiationSensitive radiation sensitive model}.
65   * </p> <p>
66   * <b>Caution:</b> The spacecraft state must be defined in an Earth centered frame.
67   * </p>
68   *
69   * @author Thomas Paulet
70   * @since 10.3
71   */
72  public class KnockeRediffusedForceModel extends AbstractForceModel {
73  
74      /** Earth rotation around Sun pulsation in rad/sec. */
75      private static final double EARTH_AROUND_SUN_PULSATION = MathUtils.TWO_PI / Constants.JULIAN_YEAR;
76  
77      /** Coefficient for solar irradiance computation. */
78      private static final double ES_COEFF = 4.5606E-6;
79  
80      /** First coefficient for albedo computation. */
81      private static final double A0 = 0.34;
82  
83      /** Second coefficient for albedo computation. */
84      private static final double C0 = 0.;
85  
86      /** Third coefficient for albedo computation. */
87      private static final double C1 = 0.10;
88  
89      /** Fourth coefficient for albedo computation. */
90      private static final double C2 = 0.;
91  
92      /** Fifth coefficient for albedo computation. */
93      private static final double A2 = 0.29;
94  
95      /** First coefficient for Earth emissivity computation. */
96      private static final double E0 = 0.68;
97  
98      /** Second coefficient for Earth emissivity computation. */
99      private static final double K0 = 0.;
100 
101     /** Third coefficient for Earth emissivity computation. */
102     private static final double K1 = -0.07;
103 
104     /** Fourth coefficient for Earth emissivity computation. */
105     private static final double K2 = 0.;
106 
107     /** Fifth coefficient for Earth emissivity computation. */
108     private static final double E2 = -0.18;
109 
110     /** Sun model. */
111     private final ExtendedPVCoordinatesProvider sun;
112 
113     /** Spacecraft. */
114     private final RadiationSensitive spacecraft;
115 
116     /** Angular resolution for emissivity and albedo computation in rad. */
117     private final double angularResolution;
118 
119     /** Earth equatorial radius in m. */
120     private double equatorialRadius;
121 
122     /** Reference date for periodic terms: December 22nd 1981.
123      * Without more precision, the choice is to set it at midnight, UTC. */
124     private final AbsoluteDate referenceEpoch;
125 
126     /** Default Constructor.
127      * <p>This constructor uses the {@link DataContext#getDefault() default data context}</p>.
128      * @param sun Sun model
129      * @param spacecraft the object physical and geometrical information
130      * @param equatorialRadius the Earth equatorial radius in m
131      * @param angularResolution angular resolution in rad
132      */
133     @DefaultDataContext
134     public KnockeRediffusedForceModel (final ExtendedPVCoordinatesProvider sun,
135                                        final RadiationSensitive spacecraft,
136                                        final double equatorialRadius,
137                                        final double angularResolution) {
138 
139         this(sun, spacecraft, equatorialRadius, angularResolution, DataContext.getDefault().getTimeScales().getUTC());
140     }
141 
142     /** General constructor.
143      * @param sun Sun model
144      * @param spacecraft the object physical and geometrical information
145      * @param equatorialRadius the Earth equatorial radius in m
146      * @param angularResolution angular resolution in rad
147      * @param utc the UTC time scale to define reference epoch
148      */
149     public KnockeRediffusedForceModel (final ExtendedPVCoordinatesProvider sun,
150                                        final RadiationSensitive spacecraft,
151                                        final double equatorialRadius,
152                                        final double angularResolution,
153                                        final TimeScale utc) {
154         this.sun               = sun;
155         this.spacecraft        = spacecraft;
156         this.equatorialRadius  = equatorialRadius;
157         this.angularResolution = angularResolution;
158         this.referenceEpoch    = new AbsoluteDate(1981, 12, 22, 0, 0, 0.0, utc);
159     }
160 
161 
162     /** {@inheritDoc} */
163     @Override
164     public boolean dependsOnPositionOnly() {
165         return false;
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     public Stream<EventDetector> getEventsDetectors() {
171         return Stream.of();
172     }
173 
174     /** {@inheritDoc} */
175     @Override
176     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
177         return Stream.of();
178     }
179 
180     /** {@inheritDoc} */
181     @Override
182     public Vector3D acceleration(final SpacecraftState s,
183                                  final double[] parameters) {
184 
185         // Get date
186         final AbsoluteDate date = s.getDate();
187 
188         // Get frame
189         final Frame frame = s.getFrame();
190 
191         // Get satellite position
192         final Vector3D satellitePosition = s.getPVCoordinates().getPosition();
193 
194         // Get Sun position
195         final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition();
196 
197         // Get spherical Earth model
198         final OneAxisEllipsoid earth = new OneAxisEllipsoid(equatorialRadius, 0.0, frame);
199 
200         // Project satellite on Earth as vector
201         final Vector3D projectedToGround = satellitePosition.normalize().scalarMultiply(equatorialRadius);
202 
203         // Get elementary vector east for Earth browsing using rotations
204         final Vector3D east = earth.transform(satellitePosition, frame, date).getEast();
205 
206         // Initialize rediffused flux with elementary flux coming from the circular area around the projected satellite
207         final double centerArea = MathUtils.TWO_PI * equatorialRadius * equatorialRadius *
208                                  (1.0 - FastMath.cos(angularResolution));
209         Vector3D rediffusedFlux = computeElementaryFlux(s, projectedToGround, sunPosition, earth, centerArea);
210 
211         // Sectorize the part of Earth which is seen by the satellite into crown sectors with constant angular resolution
212         for (double eastAxisOffset = 1.5 * angularResolution;
213              eastAxisOffset < FastMath.asin(equatorialRadius / satellitePosition.getNorm());
214              eastAxisOffset = eastAxisOffset + angularResolution) {
215 
216             // Build rotation transformations to get first crown elementary sector center
217             final Transform eastRotation = new Transform(date,
218                                                           new Rotation(east, eastAxisOffset, RotationConvention.VECTOR_OPERATOR));
219 
220             // Get first elementary crown sector center
221             final Vector3D firstCrownSectorCenter = eastRotation.transformPosition(projectedToGround);
222 
223             // Browse the entire crown
224             for (double radialAxisOffset = 0.5 * angularResolution;
225                  radialAxisOffset < MathUtils.TWO_PI;
226                  radialAxisOffset = radialAxisOffset + angularResolution) {
227 
228                 // Build rotation transformations to get elementary area center
229                 final Transform radialRotation  = new Transform(date,
230                                                                 new Rotation(projectedToGround, radialAxisOffset, RotationConvention.VECTOR_OPERATOR));
231 
232                 // Get current elementary crown sector center
233                 final Vector3D currentCenter = radialRotation.transformPosition(firstCrownSectorCenter);
234 
235                 // Compute current elementary crown sector area, it results of the integration of an elementary crown sector
236                 // over the angular resolution
237                 final double sectorArea = equatorialRadius * equatorialRadius *
238                                           2.0 * angularResolution * FastMath.sin(0.5 * angularResolution) * FastMath.sin(eastAxisOffset);
239 
240                 // Add current sector contribution to total rediffused flux
241                 rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, earth, sectorArea));
242             }
243         }
244         return spacecraft.radiationPressureAcceleration(date, frame, satellitePosition, s.getAttitude().getRotation(),
245                                                         s.getMass(), rediffusedFlux, parameters);
246     }
247 
248 
249     /** {@inheritDoc} */
250     @Override
251     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
252                                                                          final T[] parameters) {
253         // Get date
254         final FieldAbsoluteDate<T> date = s.getDate();
255 
256         // Get frame
257         final Frame frame = s.getFrame();
258 
259         // Get zero
260         final T zero = date.getField().getZero();
261 
262         // Get satellite position
263         final FieldVector3D<T> satellitePosition = s.getPVCoordinates().getPosition();
264 
265         // Get Sun position
266         final FieldVector3D<T> sunPosition = sun.getPVCoordinates(date, frame).getPosition();
267 
268         // Get spherical Earth model
269         final OneAxisEllipsoid earth = new OneAxisEllipsoid(equatorialRadius, 0.0, frame);
270 
271         // Project satellite on Earth as vector
272         final FieldVector3D<T> projectedToGround = satellitePosition.normalize().scalarMultiply(equatorialRadius);
273 
274         // Get elementary vector east for Earth browsing using rotations
275         final FieldVector3D<T> east = earth.transform(satellitePosition, frame, date).getEast();
276 
277         // Initialize rediffused flux with elementary flux coming from the circular area around the projected satellite
278         final T centerArea = zero.getPi().multiply(2.0).multiply(equatorialRadius).multiply(equatorialRadius).
279                         multiply(1.0 - FastMath.cos(angularResolution));
280         FieldVector3D<T> rediffusedFlux = computeElementaryFlux(s, projectedToGround, sunPosition, earth, centerArea);
281 
282         // Sectorize the part of Earth which is seen by the satellite into crown sectors with constant angular resolution
283         for (double eastAxisOffset = 1.5 * angularResolution;
284              eastAxisOffset < FastMath.asin(equatorialRadius / satellitePosition.getNorm().getReal());
285              eastAxisOffset = eastAxisOffset + angularResolution) {
286 
287             // Build rotation transformations to get first crown elementary sector center
288             final FieldTransform<T> eastRotation = new FieldTransform<>(date,
289                                                                         new FieldRotation<>(east,
290                                                                                             zero.add(eastAxisOffset),
291                                                                                             RotationConvention.VECTOR_OPERATOR));
292 
293             // Get first elementary crown sector center
294             final FieldVector3D<T> firstCrownSectorCenter = eastRotation.transformPosition(projectedToGround);
295 
296             // Browse the entire crown
297             for (double radialAxisOffset = 0.5 * angularResolution;
298                  radialAxisOffset < MathUtils.TWO_PI;
299                  radialAxisOffset = radialAxisOffset + angularResolution) {
300 
301                 // Build rotation transformations to get elementary area center
302                 final FieldTransform<T> radialRotation  = new FieldTransform<>(date,
303                                                                                new FieldRotation<>(projectedToGround,
304                                                                                                    zero.add(radialAxisOffset),
305                                                                                                    RotationConvention.VECTOR_OPERATOR));
306 
307                 // Get current elementary crown sector center
308                 final FieldVector3D<T> currentCenter = radialRotation.transformPosition(firstCrownSectorCenter);
309 
310                 // Compute current elementary crown sector area, it results of the integration of an elementary crown sector
311                 // over the angular resolution
312                 final T sectorArea = zero.add(equatorialRadius * equatorialRadius *
313                                               2.0 * angularResolution * FastMath.sin(0.5 * angularResolution) * FastMath.sin(eastAxisOffset));
314 
315                 // Add current sector contribution to total rediffused flux
316                 rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, earth, sectorArea));
317             }
318         }
319         return spacecraft.radiationPressureAcceleration(date, frame, satellitePosition, s.getAttitude().getRotation(),
320                                                         s.getMass(), rediffusedFlux, parameters);
321     }
322 
323 
324     /** {@inheritDoc} */
325     @Override
326     public List<ParameterDriver> getParametersDrivers() {
327         return spacecraft.getRadiationParametersDrivers();
328     }
329 
330     /** Compute Earth albedo.
331      * Albedo value represents the fraction of solar radiative flux that is reflected by Earth.
332      * Its value is in [0;1].
333      * @param date the date
334      * @param phi the equatorial latitude in rad
335      * @return the albedo in [0;1]
336      */
337     private double computeAlbedo(final AbsoluteDate date, final double phi) {
338 
339         // Get duration since coefficient reference epoch
340         final double deltaT = date.durationFrom(referenceEpoch);
341 
342         // Compute 1rst Legendre polynomial coeficient
343         final SinCos sc = FastMath.sinCos(EARTH_AROUND_SUN_PULSATION * deltaT);
344         final double A1 = C0 +
345                           C1 * sc.cos() +
346                           C2 * sc.sin();
347 
348         // Get 1rst and 2nd order Legendre polynomials
349         final PolynomialFunction firstLegendrePolynomial  = PolynomialsUtils.createLegendrePolynomial(1);
350         final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
351 
352         // Get latitude sinus
353         final double sinPhi = FastMath.sin(phi);
354 
355         // Compute albedo
356         return A0 +
357                A1 * firstLegendrePolynomial.value(sinPhi) +
358                A2 * secondLegendrePolynomial.value(sinPhi);
359 
360     }
361 
362 
363     /** Compute Earth albedo.
364      * Albedo value represents the fraction of solar radiative flux that is reflected by Earth.
365      * Its value is in [0;1].
366      * @param date the date
367      * @param phi the equatorial latitude in rad
368      * @param <T> extends CalculusFieldElement
369      * @return the albedo in [0;1]
370      */
371     private <T extends CalculusFieldElement<T>> T computeAlbedo(final FieldAbsoluteDate<T> date, final T phi) {
372 
373         // Get duration since coefficient reference epoch
374         final T deltaT = date.durationFrom(referenceEpoch);
375 
376         // Compute 1rst Legendre polynomial coeficient
377         final FieldSinCos<T> sc = FastMath.sinCos(deltaT.multiply(EARTH_AROUND_SUN_PULSATION));
378         final T A1 = sc.cos().multiply(C1).add(
379                      sc.sin().multiply(C2)).add(C0);
380 
381         // Get 1rst and 2nd order Legendre polynomials
382         final PolynomialFunction firstLegendrePolynomial  = PolynomialsUtils.createLegendrePolynomial(1);
383         final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
384 
385         // Get latitude sinus
386         final T sinPhi = FastMath.sin(phi);
387 
388         // Compute albedo
389         return firstLegendrePolynomial.value(sinPhi).multiply(A1).add(
390                secondLegendrePolynomial.value(sinPhi).multiply(A2)).add(A0);
391 
392     }
393 
394     /** Compute Earth emisivity.
395      * Emissivity is used to compute the infrared flux that is emitted by Earth.
396      * Its value is in [0;1].
397      * @param date the date
398      * @param phi the equatorial latitude in rad
399      * @return the emissivity in [0;1]
400      */
401     private double computeEmissivity(final AbsoluteDate date, final double phi) {
402 
403         // Get duration since coefficient reference epoch
404         final double deltaT = date.durationFrom(referenceEpoch);
405 
406         // Compute 1rst Legendre polynomial coefficient
407         final SinCos sc = FastMath.sinCos(EARTH_AROUND_SUN_PULSATION * deltaT);
408         final double E1 = K0 +
409                           K1 * sc.cos() +
410                           K2 * sc.sin();
411 
412         // Get 1rst and 2nd order Legendre polynomials
413         final PolynomialFunction firstLegendrePolynomial  = PolynomialsUtils.createLegendrePolynomial(1);
414         final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
415 
416         // Get latitude sinus
417         final double sinPhi = FastMath.sin(phi);
418 
419         // Compute albedo
420         return E0 +
421                E1 * firstLegendrePolynomial.value(sinPhi) +
422                E2 * secondLegendrePolynomial.value(sinPhi);
423 
424     }
425 
426 
427     /** Compute Earth emisivity.
428      * Emissivity is used to compute the infrared flux that is emitted by Earth.
429      * Its value is in [0;1].
430      * @param date the date
431      * @param phi the equatorial latitude in rad
432      * @param <T> extends CalculusFieldElement
433      * @return the emissivity in [0;1]
434      */
435     private <T extends CalculusFieldElement<T>> T computeEmissivity(final FieldAbsoluteDate<T> date, final T phi) {
436 
437         // Get duration since coefficient reference epoch
438         final T deltaT = date.durationFrom(referenceEpoch);
439 
440         // Compute 1rst Legendre polynomial coeficient
441         final FieldSinCos<T> sc = FastMath.sinCos(deltaT.multiply(EARTH_AROUND_SUN_PULSATION));
442         final T E1 = sc.cos().multiply(K1).add(
443                      sc.sin().multiply(K2)).add(K0);
444 
445         // Get 1rst and 2nd order Legendre polynomials
446         final PolynomialFunction firstLegendrePolynomial  = PolynomialsUtils.createLegendrePolynomial(1);
447         final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
448 
449         // Get latitude sinus
450         final T sinPhi = FastMath.sin(phi);
451 
452         // Compute albedo
453         return firstLegendrePolynomial.value(sinPhi).multiply(E1).add(
454                secondLegendrePolynomial.value(sinPhi).multiply(E2)).add(E0);
455 
456     }
457 
458     /** Compute total solar flux impacting Earth.
459      * @param sunPosition the Sun position in an Earth centered frame
460      * @return the total solar flux impacting Earth in J/m^3
461      */
462     private double computeSolarFlux(final Vector3D sunPosition) {
463 
464         // Compute Earth - Sun distance in UA
465         final double earthSunDistance = sunPosition.getNorm() / Constants.JPL_SSD_ASTRONOMICAL_UNIT;
466 
467         // Compute Solar flux
468         return ES_COEFF * Constants.SPEED_OF_LIGHT / (earthSunDistance * earthSunDistance);
469     }
470 
471 
472     /** Compute total solar flux impacting Earth.
473      * @param sunPosition the Sun position in an Earth centered frame
474      * @param <T> extends CalculusFieldElement
475      * @return the total solar flux impacting Earth in J/m^3
476      */
477     private <T extends CalculusFieldElement<T>> T computeSolarFlux(final FieldVector3D<T> sunPosition) {
478 
479         // Compute Earth - Sun distance in UA
480         final T earthSunDistance = sunPosition.getNorm().divide(Constants.JPL_SSD_ASTRONOMICAL_UNIT);
481 
482         // Compute Solar flux
483         return earthSunDistance.multiply(earthSunDistance).reciprocal().multiply(ES_COEFF * Constants.SPEED_OF_LIGHT);
484     }
485 
486 
487     /** Compute elementary rediffused flux on satellite.
488      * @param state the current spacecraft state
489      * @param elementCenter the position of the considered area center
490      * @param sunPosition the position of the Sun in the spacecraft frame
491      * @param earth the Earth model
492      * @param elementArea the area of the current element
493      * @return the rediffused flux from considered element on the spacecraft
494      */
495     private Vector3D computeElementaryFlux(final SpacecraftState state,
496                                            final Vector3D elementCenter,
497                                            final Vector3D sunPosition,
498                                            final OneAxisEllipsoid earth,
499                                            final double elementArea) {
500 
501         // Get satellite position
502         final Vector3D satellitePosition = state.getPVCoordinates().getPosition();
503 
504         // Get current date
505         final AbsoluteDate date = state.getDate();
506 
507         // Get frame
508         final Frame frame = state.getFrame();
509 
510         // Get solar flux impacting Earth
511         final double solarFlux = computeSolarFlux(sunPosition);
512 
513         // Get satellite viewing angle as seen from current elementary area
514         final double alpha = Vector3D.angle(elementCenter, satellitePosition);
515 
516         // Check that satellite sees the current area
517         if (FastMath.abs(alpha) < MathUtils.SEMI_PI) {
518 
519             // Get current elementary area center latitude
520             final double currentLatitude = earth.transform(elementCenter, frame, date).getLatitude();
521 
522             // Compute Earth emissivity value
523             final double e = computeEmissivity(date, currentLatitude);
524 
525             // Initialize albedo
526             double a = 0.0;
527 
528             // Check if elementary area is in day light
529             final double sunAngle = Vector3D.angle(elementCenter, sunPosition);
530 
531             if (FastMath.abs(sunAngle) < MathUtils.SEMI_PI) {
532                 // Elementary area is in day light, compute albedo value
533                 a = computeAlbedo(date, currentLatitude);
534             }
535 
536             // Compute elementary area contribution to rediffused flux
537             final double albedoAndIR = a * solarFlux * FastMath.cos(sunAngle) +
538                                        e * solarFlux * 0.25;
539 
540             // Compute elementary area - satellite vector and distance
541             final Vector3D r = satellitePosition.subtract(elementCenter);
542             final double rNorm = r.getNorm();
543 
544             // Compute attenuated projected elemetary area vector
545             final Vector3D projectedAreaVector = r.scalarMultiply(elementArea * FastMath.cos(alpha) /
546                                                                  (FastMath.PI * rNorm * rNorm * rNorm));
547 
548             // Compute elementary radiation flux from current elementary area
549             return projectedAreaVector.scalarMultiply(albedoAndIR / Constants.SPEED_OF_LIGHT);
550 
551         } else {
552 
553             // Spacecraft does not see the elementary area
554             return new Vector3D(0.0, 0.0, 0.0);
555         }
556 
557     }
558 
559 
560     /** Compute elementary rediffused flux on satellite.
561      * @param state the current spacecraft state
562      * @param elementCenter the position of the considered area center
563      * @param sunPosition the position of the Sun in the spacecraft frame
564      * @param earth the Earth model
565      * @param elementArea the area of the current element
566      * @param <T> extends CalculusFieldElement
567      * @return the rediffused flux from considered element on the spacecraft
568      */
569     private <T extends CalculusFieldElement<T>> FieldVector3D<T> computeElementaryFlux(final FieldSpacecraftState<T> state,
570                                                                                    final FieldVector3D<T> elementCenter,
571                                                                                    final FieldVector3D<T> sunPosition,
572                                                                                    final OneAxisEllipsoid earth,
573                                                                                    final T elementArea) {
574 
575         // Get satellite position
576         final FieldVector3D<T> satellitePosition = state.getPVCoordinates().getPosition();
577 
578         // Get current date
579         final FieldAbsoluteDate<T> date = state.getDate();
580 
581         // Get frame
582         final Frame frame = state.getFrame();
583 
584         // Get zero
585         final T zero = date.getField().getZero();
586 
587         // Get solar flux impacting Earth
588         final T solarFlux = computeSolarFlux(sunPosition);
589 
590         // Get satellite viewing angle as seen from current elementary area
591         final T alpha = FieldVector3D.angle(elementCenter, satellitePosition);
592 
593         // Check that satellite sees the current area
594         if (FastMath.abs(alpha).getReal() < MathUtils.SEMI_PI) {
595 
596             // Get current elementary area center latitude
597             final T currentLatitude = earth.transform(elementCenter, frame, date).getLatitude();
598 
599             // Compute Earth emissivity value
600             final T e = computeEmissivity(date, currentLatitude);
601 
602             // Initialize albedo
603             T a = zero;
604 
605             // Check if elementary area is in day light
606             final T sunAngle = FieldVector3D.angle(elementCenter, sunPosition);
607 
608             if (FastMath.abs(sunAngle).getReal() < MathUtils.SEMI_PI) {
609                 // Elementary area is in day light, compute albedo value
610                 a = computeAlbedo(date, currentLatitude);
611             }
612 
613             // Compute elementary area contribution to rediffused flux
614             final T albedoAndIR = a.multiply(solarFlux).multiply(FastMath.cos(sunAngle)).add(
615                                   e.multiply(solarFlux).multiply(0.25));
616 
617             // Compute elementary area - satellite vector and distance
618             final FieldVector3D<T> r = satellitePosition.subtract(elementCenter);
619             final T rNorm = r.getNorm();
620 
621             // Compute attenuated projected elemetary area vector
622             final FieldVector3D<T> projectedAreaVector = r.scalarMultiply(elementArea.multiply(FastMath.cos(alpha)).divide(
623                                                                           rNorm.multiply(rNorm).multiply(rNorm).multiply(zero.getPi())));
624 
625             // Compute elementary radiation flux from current elementary area
626             return projectedAreaVector.scalarMultiply(albedoAndIR.divide(Constants.SPEED_OF_LIGHT));
627 
628         } else {
629 
630             // Spacecraft does not see the elementary area
631             return new FieldVector3D<T>(zero, zero, zero);
632         }
633 
634     }
635 
636 }