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.attitudes;
18  
19  
20  import org.hipparchus.Field;
21  import org.hipparchus.CalculusFieldElement;
22  import org.hipparchus.geometry.euclidean.threed.Rotation;
23  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
24  import org.hipparchus.geometry.euclidean.threed.RotationOrder;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.hipparchus.util.Decimal64Field;
27  import org.hipparchus.util.FastMath;
28  import org.junit.After;
29  import org.junit.Assert;
30  import org.junit.Before;
31  import org.junit.Test;
32  import org.orekit.Utils;
33  import org.orekit.bodies.OneAxisEllipsoid;
34  import org.orekit.errors.OrekitException;
35  import org.orekit.errors.OrekitMessages;
36  import org.orekit.frames.Frame;
37  import org.orekit.frames.FramesFactory;
38  import org.orekit.frames.LOFType;
39  import org.orekit.orbits.CircularOrbit;
40  import org.orekit.orbits.FieldOrbit;
41  import org.orekit.orbits.KeplerianOrbit;
42  import org.orekit.orbits.Orbit;
43  import org.orekit.orbits.PositionAngle;
44  import org.orekit.propagation.FieldSpacecraftState;
45  import org.orekit.propagation.Propagator;
46  import org.orekit.propagation.SpacecraftState;
47  import org.orekit.propagation.analytical.KeplerianPropagator;
48  import org.orekit.time.AbsoluteDate;
49  import org.orekit.time.DateComponents;
50  import org.orekit.time.FieldAbsoluteDate;
51  import org.orekit.time.TimeComponents;
52  import org.orekit.time.TimeScalesFactory;
53  import org.orekit.utils.AngularCoordinates;
54  import org.orekit.utils.IERSConventions;
55  import org.orekit.utils.TimeStampedFieldPVCoordinates;
56  import org.orekit.utils.TimeStampedPVCoordinates;
57  
58  
59  public class LofOffsetPointingTest {
60  
61      // Computation date
62      private AbsoluteDate date;
63  
64      // Body mu
65      private double mu;
66  
67      // Reference frame = ITRF
68      private Frame frameItrf;
69  
70      // Earth shape
71      OneAxisEllipsoid earthSpheric;
72  
73      /** Test if both constructors are equivalent
74       */
75      @Test
76      public void testLof() {
77  
78          //  Satellite position
79          final CircularOrbit circ =
80              new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(0.), FastMath.toRadians(270.),
81                                     FastMath.toRadians(5.300), PositionAngle.MEAN,
82                                     FramesFactory.getEME2000(), date, mu);
83  
84          // Create lof aligned law
85          //************************
86          final LofOffset lofLaw = new LofOffset(circ.getFrame(), LOFType.LVLH_CCSDS);
87          final LofOffsetPointing lofPointing = new LofOffsetPointing(circ.getFrame(), earthSpheric, lofLaw, Vector3D.PLUS_K);
88          final Rotation lofRot = lofPointing.getAttitude(circ, date, circ.getFrame()).getRotation();
89  
90          // Compare to body center pointing law
91          //*************************************
92          final BodyCenterPointing centerLaw = new BodyCenterPointing(circ.getFrame(), earthSpheric);
93          final Rotation centerRot = centerLaw.getAttitude(circ, date, circ.getFrame()).getRotation();
94          final double angleBodyCenter = centerRot.composeInverse(lofRot, RotationConvention.VECTOR_OPERATOR).getAngle();
95          Assert.assertEquals(0., angleBodyCenter, Utils.epsilonAngle);
96  
97          // Compare to nadir pointing law
98          //*******************************
99          final NadirPointing nadirLaw = new NadirPointing(circ.getFrame(), earthSpheric);
100         final Rotation nadirRot = nadirLaw.getAttitude(circ, date, circ.getFrame()).getRotation();
101         final double angleNadir = nadirRot.composeInverse(lofRot, RotationConvention.VECTOR_OPERATOR).getAngle();
102         Assert.assertEquals(0., angleNadir, Utils.epsilonAngle);
103 
104     }
105 
106     @Test
107     public void testMiss() {
108         final CircularOrbit circ =
109             new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(0.), FastMath.toRadians(270.),
110                                    FastMath.toRadians(5.300), PositionAngle.MEAN,
111                                    FramesFactory.getEME2000(), date, mu);
112         final LofOffset upsideDown = new LofOffset(circ.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYX, FastMath.PI, 0, 0);
113         final LofOffsetPointing pointing = new LofOffsetPointing(circ.getFrame(), earthSpheric, upsideDown, Vector3D.PLUS_K);
114         try {
115             pointing.getTargetPV(circ, date, circ.getFrame());
116             Assert.fail("an exception should have been thrown");
117         } catch (OrekitException oe) {
118             Assert.assertEquals(OrekitMessages.ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND, oe.getSpecifier());
119         }
120     }
121 
122     @Test
123     public void testSpin() {
124 
125         AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01),
126                                              new TimeComponents(3, 25, 45.6789),
127                                              TimeScalesFactory.getUTC());
128         KeplerianOrbit orbit =
129             new KeplerianOrbit(7178000.0, 1.e-4, FastMath.toRadians(50.),
130                               FastMath.toRadians(10.), FastMath.toRadians(20.),
131                               FastMath.toRadians(30.), PositionAngle.MEAN,
132                               FramesFactory.getEME2000(), date, 3.986004415e14);
133 
134         final AttitudeProvider law =
135             new LofOffsetPointing(orbit.getFrame(), earthSpheric,
136                                   new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYX, 0.1, 0.2, 0.3),
137                                   Vector3D.PLUS_K);
138 
139         Propagator propagator = new KeplerianPropagator(orbit, law);
140 
141         double h = 0.01;
142         SpacecraftState sMinus = propagator.propagate(date.shiftedBy(-h));
143         SpacecraftState s0     = propagator.propagate(date);
144         SpacecraftState sPlus  = propagator.propagate(date.shiftedBy(h));
145 
146         // check spin is consistent with attitude evolution
147         double errorAngleMinus     = Rotation.distance(sMinus.shiftedBy(h).getAttitude().getRotation(),
148                                                        s0.getAttitude().getRotation());
149         double evolutionAngleMinus = Rotation.distance(sMinus.getAttitude().getRotation(),
150                                                        s0.getAttitude().getRotation());
151         Assert.assertEquals(0.0, errorAngleMinus, 1.0e-6 * evolutionAngleMinus);
152         double errorAnglePlus      = Rotation.distance(s0.getAttitude().getRotation(),
153                                                        sPlus.shiftedBy(-h).getAttitude().getRotation());
154         double evolutionAnglePlus  = Rotation.distance(s0.getAttitude().getRotation(),
155                                                        sPlus.getAttitude().getRotation());
156         Assert.assertEquals(0.0, errorAnglePlus, 1.0e-6 * evolutionAnglePlus);
157 
158         Vector3D spin0 = s0.getAttitude().getSpin();
159         Vector3D reference = AngularCoordinates.estimateRate(sMinus.getAttitude().getRotation(),
160                                                              sPlus.getAttitude().getRotation(),
161                                                              2 * h);
162         Assert.assertTrue(spin0.getNorm() > 1.0e-3);
163         Assert.assertEquals(0.0, spin0.subtract(reference).getNorm(), 1.0e-10);
164 
165     }
166 
167     @Test
168     public void testTypesField() {
169         AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01),
170                                              new TimeComponents(3, 25, 45.6789),
171                                              TimeScalesFactory.getUTC());
172         KeplerianOrbit orbit =
173             new KeplerianOrbit(7178000.0, 1.e-4, FastMath.toRadians(50.),
174                               FastMath.toRadians(10.), FastMath.toRadians(20.),
175                               FastMath.toRadians(30.), PositionAngle.MEAN,
176                               FramesFactory.getEME2000(), date, 3.986004415e14);
177 
178         for (final LOFType type : LOFType.values()) {
179             RotationOrder order = RotationOrder.ZXY;
180             double alpha1 = 0.123;
181             double alpha2 = 0.456;
182             double alpha3 = 0.789;
183             LofOffset law = new LofOffset(orbit.getFrame(), type, order, alpha1, alpha2, alpha3);
184             final Vector3D dir;
185             switch (type) {
186                 case TNW:
187                     dir = Vector3D.PLUS_J;
188                     break;
189                 case QSW:
190                     dir = Vector3D.MINUS_I;
191                     break;
192                 case LVLH:
193                     dir = Vector3D.MINUS_I;
194                     break;
195                 case LVLH_CCSDS:
196                     dir = Vector3D.PLUS_K;
197                     break;
198                 case VNC:
199                     dir = Vector3D.MINUS_K;
200                     break;
201                 case NTW:
202                     dir = Vector3D.MINUS_I;
203                     break;
204                 default :
205                     // EQW and deprecated VVLH, not used in this test
206                     continue;
207             }
208             LofOffsetPointing lop = new LofOffsetPointing(orbit.getFrame(), earthSpheric, law, dir);
209             checkField(Decimal64Field.getInstance(), lop, orbit, date, orbit.getFrame());
210         }
211     }
212 
213     private <T extends CalculusFieldElement<T>> void checkField(final Field<T> field, final GroundPointing provider,
214                                                             final Orbit orbit, final AbsoluteDate date,
215                                                             final Frame frame)
216         {
217 
218         final Attitude attitudeD = provider.getAttitude(orbit, date, frame);
219         final FieldOrbit<T> orbitF = new FieldSpacecraftState<>(field, new SpacecraftState(orbit)).getOrbit();
220         final FieldAbsoluteDate<T> dateF = new FieldAbsoluteDate<>(field, date);
221         final FieldAttitude<T> attitudeF = provider.getAttitude(orbitF, dateF, frame);
222         Assert.assertEquals(0.0, Rotation.distance(attitudeD.getRotation(), attitudeF.getRotation().toRotation()), 1.0e-15);
223         Assert.assertEquals(0.0, Vector3D.distance(attitudeD.getSpin(), attitudeF.getSpin().toVector3D()), 1.0e-15);
224         Assert.assertEquals(0.0, Vector3D.distance(attitudeD.getRotationAcceleration(), attitudeF.getRotationAcceleration().toVector3D()), 1.0e-15);
225 
226         final TimeStampedPVCoordinates         pvD = provider.getTargetPV(orbit, date, frame);
227         final TimeStampedFieldPVCoordinates<T> pvF = provider.getTargetPV(orbitF, dateF, frame);
228         
229         Assert.assertEquals(0.0, Vector3D.distance(pvD.getPosition(),     pvF.getPosition().toVector3D()),     6.0e-9);
230         Assert.assertEquals(0.0, Vector3D.distance(pvD.getVelocity(),     pvF.getVelocity().toVector3D()),     5.0e-13);
231         Assert.assertEquals(0.0, Vector3D.distance(pvD.getAcceleration(), pvF.getAcceleration().toVector3D()), 2.0e-6);
232     }
233 
234     @Before
235     public void setUp() {
236         try {
237 
238             Utils.setDataRoot("regular-data");
239 
240             // Computation date
241             date = new AbsoluteDate(new DateComponents(2008, 04, 07),
242                                     TimeComponents.H00,
243                                     TimeScalesFactory.getUTC());
244 
245             // Body mu
246             mu = 3.9860047e14;
247 
248             // Reference frame = ITRF
249             frameItrf = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
250 
251             // Elliptic earth shape
252             earthSpheric =
253                 new OneAxisEllipsoid(6378136.460, 0., frameItrf);
254 
255         } catch (OrekitException oe) {
256             Assert.fail(oe.getMessage());
257         }
258 
259     }
260 
261     @After
262     public void tearDown() {
263         date = null;
264         frameItrf = null;
265         earthSpheric = null;
266     }
267 }
268