1   /* Copyright 2020 Exotrail
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    * Exotrail 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.maneuvers.propulsion;
18  
19  import org.hipparchus.geometry.euclidean.threed.Rotation;
20  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.Decimal64;
23  import org.hipparchus.util.FastMath;
24  import org.junit.Assert;
25  import org.junit.Before;
26  import org.junit.Test;
27  import org.orekit.Utils;
28  import org.orekit.attitudes.Attitude;
29  import org.orekit.attitudes.AttitudeProvider;
30  import org.orekit.attitudes.LofOffset;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.frames.FactoryManagedFrame;
33  import org.orekit.frames.FramesFactory;
34  import org.orekit.frames.LOFType;
35  import org.orekit.orbits.KeplerianOrbit;
36  import org.orekit.orbits.PositionAngle;
37  import org.orekit.propagation.analytical.KeplerianPropagator;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.FieldAbsoluteDate;
40  import org.orekit.time.TimeScalesFactory;
41  import org.orekit.utils.Constants;
42  import org.orekit.utils.IERSConventions;
43  import org.orekit.utils.PVCoordinatesProvider;
44  
45  public class ThrustDirectionAndAttitudeProviderTest {
46      /** */
47      private static final double EPSILON_ROTATION = 1E-15;
48      /** */
49      private FactoryManagedFrame frame;
50      /** */
51      private AbsoluteDate date;
52      /** */
53      private PVCoordinatesProvider pvProv;
54      /** */
55      private final Vector3D thrusterAxisInSatelliteFrame = Vector3D.PLUS_J;
56  
57      @Before
58      public void setUp() {
59          Utils.setDataRoot("regular-data");
60          frame = FramesFactory.getCIRF(IERSConventions.IERS_2010, true);
61          date = new AbsoluteDate(2020, 01, 01, TimeScalesFactory.getUTC());
62          final double sma = Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 700e3;
63          final double ecc = 0.01;
64          final double inc = FastMath.toRadians(60);
65          final double pa = FastMath.toRadians(0);
66          final double raan = 0.;
67          final double anomaly = FastMath.toRadians(0);
68  
69          final KeplerianOrbit initialOrbit = new KeplerianOrbit(sma, ecc, inc, pa, raan, anomaly, PositionAngle.TRUE,
70                  FramesFactory.getCIRF(IERSConventions.IERS_2010, true), date, Constants.EGM96_EARTH_MU);
71          pvProv = new KeplerianPropagator(initialOrbit);
72      }
73  
74      private AttitudeProvider buildVelocityAttitudeProvider() {
75          return new LofOffset(frame, LOFType.TNW);
76      }
77  
78      @Test(expected = OrekitException.class)
79      public void fixedDirectionCanNotProvideTheAttitude() {
80  
81          final ThrustDirectionAndAttitudeProvider provider = ThrustDirectionAndAttitudeProvider
82                  .buildFromFixedDirectionInSatelliteFrame(Vector3D.PLUS_I);
83          Assert.assertNull(provider.getManeuverAttitudeProvider());
84          provider.getAttitude(pvProv, date, frame); // raise an error
85      }
86  
87      @Test(expected = OrekitException.class)
88      public void missingParameterTest() {
89          ThrustDirectionAndAttitudeProvider.buildFromDirectionInFrame(frame, null, thrusterAxisInSatelliteFrame);
90      }
91  
92      @Test
93      public void attitudeFromDirectionInFrame() {
94  
95          final Vector3D fixedThrustDirection = new Vector3D(1, 2, 3).normalize();
96          final ConstantThrustDirectionProvider thrustDirectionInertial = new ConstantThrustDirectionProvider(
97                  fixedThrustDirection);
98  
99          final ThrustDirectionAndAttitudeProvider provider = ThrustDirectionAndAttitudeProvider
100                 .buildFromDirectionInFrame(frame, thrustDirectionInertial, thrusterAxisInSatelliteFrame);
101 
102         Assert.assertNotNull(provider.getManeuverAttitudeProvider());
103 
104         // attitude from Frame: inertial => sat
105         final Attitude inertialToSat = provider.getAttitude(pvProv, date, frame);
106 
107         final Vector3D thrustDirectionRecomputed = inertialToSat.getRotation().revert()
108                 .applyTo(thrusterAxisInSatelliteFrame);
109         Assert.assertEquals(0, fixedThrustDirection.subtract(thrustDirectionRecomputed).getNorm(), EPSILON_ROTATION);
110 
111     }
112 
113     @Test
114     public void attitudeFromDirectionInLOF() {
115         final Vector3D fixedThrustDirection = Vector3D.PLUS_I;
116         final ConstantThrustDirectionProvider thrustDirectionTNW = new ConstantThrustDirectionProvider(Vector3D.PLUS_I);
117 
118         final ThrustDirectionAndAttitudeProvider provider = ThrustDirectionAndAttitudeProvider
119                 .buildFromDirectionInLOF(LOFType.TNW, thrustDirectionTNW, thrusterAxisInSatelliteFrame);
120 
121         Assert.assertNotNull(provider.getManeuverAttitudeProvider());
122 
123         // attitude from Frame: inertial => sat
124         final Attitude inertialToSat = provider.getAttitude(pvProv, date, frame);
125 
126         // recompute rotation from satellite frame to TNW
127         final Rotation inertialToLof = LOFType.TNW.transformFromInertial(date, pvProv.getPVCoordinates(date, frame))
128                 .getRotation();
129         final Rotation satToLof = inertialToLof.compose(inertialToSat.getRotation().revert(),
130                 RotationConvention.VECTOR_OPERATOR);
131 
132         final Vector3D thrustDirectionRecomputed = satToLof.applyTo(thrusterAxisInSatelliteFrame);
133         Assert.assertEquals(0., fixedThrustDirection.subtract(thrustDirectionRecomputed).getNorm(), EPSILON_ROTATION);
134     }
135 
136     @Test
137     public void attitudeFromCustomProvider() {
138         final ThrustDirectionAndAttitudeProvider provider = ThrustDirectionAndAttitudeProvider
139                 .buildFromCustomAttitude(buildVelocityAttitudeProvider(), thrusterAxisInSatelliteFrame);
140         Assert.assertNotNull(provider.getManeuverAttitudeProvider());
141         Assert.assertEquals(
142                 buildVelocityAttitudeProvider().getAttitude(pvProv, date, frame).getRotation()
143                         .applyTo(thrusterAxisInSatelliteFrame),
144                 provider.getAttitude(pvProv, date, frame).getRotation().applyTo(thrusterAxisInSatelliteFrame));
145     }
146 
147     @Test(expected = OrekitException.class)
148     public void getAttitudeFieldError() {
149 
150         final ThrustDirectionAndAttitudeProvider provider = ThrustDirectionAndAttitudeProvider
151                 .buildFromCustomAttitude(buildVelocityAttitudeProvider(), Vector3D.PLUS_I);
152         Assert.assertNotNull(provider.getManeuverAttitudeProvider());
153         provider.getAttitude(null, new FieldAbsoluteDate<>(date, new Decimal64(0)), frame); // raise an error
154     }
155 }