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