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.propagation.semianalytical.dsst.utilities;
18  
19  import org.hipparchus.complex.Complex;
20  import org.hipparchus.random.MersenneTwister;
21  import org.hipparchus.util.FastMath;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.Test;
24  
25  public class GHmsjTest {
26  
27      private static final double eps = 1e-10;
28  
29      /** Gmsj and Hmsj computation test based on 2 independent methods.
30       *  If they give same results, we assume them to be consistent.
31       */
32      @Test
33      public void testGHmsj() {
34          final int sMax = 30;
35          final int mMax = 20;
36          final MersenneTwister random = new MersenneTwister(123456);
37          for (int i = 0; i < 10; i++) {
38              final double k = random.nextDouble();
39              final double h = random.nextDouble();
40              final double a = random.nextDouble();
41              final double b = random.nextDouble();
42              final GHmsjPolynomials gMSJ = new GHmsjPolynomials(k, h, a, b, 1);
43              for (int s = -sMax; s <= sMax; s++) {
44                  for (int m = 2; m <= mMax; m+=2) {
45                      final int j = m / 2;
46                      final double[] GHmsj = getGHmsj(k, h, a, b, m, s, j);
47                      Assertions.assertEquals(GHmsj[0], gMSJ.getGmsj(m, s, j), FastMath.abs(eps * GHmsj[0]));
48                      Assertions.assertEquals(GHmsj[1], gMSJ.getHmsj(m, s, j), FastMath.abs(eps * GHmsj[1]));
49                  }
50              }
51          }
52      }
53  
54      /** dG/dk and dH/dk computations test based on 2 independent methods.
55       *  If they give same results, we assume them to be consistent.
56       */
57      @Test
58      public void testdGHdk() {
59          final int sMax = 30;
60          final int mMax = 20;
61          final MersenneTwister random = new MersenneTwister(456789);
62          for (int i = 0; i < 10; i++) {
63              final double k = random.nextDouble();
64              final double h = random.nextDouble();
65              final double a = random.nextDouble();
66              final double b = random.nextDouble();
67              final GHmsjPolynomials gMSJ = new GHmsjPolynomials(k, h, a, b, 1);
68              for (int s = -sMax; s <= sMax; s++) {
69                  for (int m = 2; m <= mMax; m+=2) {
70                      final int j = m / 2;
71                      final double[] dGHdk = getdGHdk(k, h, a, b, m, s, j);
72                      Assertions.assertEquals(dGHdk[0], gMSJ.getdGmsdk(m, s, j), FastMath.abs(eps * dGHdk[0]));
73                      Assertions.assertEquals(dGHdk[1], gMSJ.getdHmsdk(m, s, j), FastMath.abs(eps * dGHdk[1]));
74                  }
75              }
76          }
77      }
78  
79      /** dG/dh computation test based on 2 independent methods.
80       *  If they give same results, we assume them to be consistent.
81       */
82      @Test
83      public void testdGHdh() {
84          final int sMax = 30;
85          final int mMax = 20;
86          final MersenneTwister random = new MersenneTwister(123789);
87          for (int i = 0; i < 10; i++) {
88              final double k = random.nextDouble();
89              final double h = random.nextDouble();
90              final double a = random.nextDouble();
91              final double b = random.nextDouble();
92              final GHmsjPolynomials gMSJ = new GHmsjPolynomials(k, h, a, b, 1);
93              for (int s = -sMax; s <= sMax; s++) {
94                  for (int m = 2; m <= mMax; m+=2) {
95                      final int j = m / 2;
96                      final double[] dGHdh = getdGHdh(k, h, a, b, m, s, j);
97                      Assertions.assertEquals(dGHdh[0], gMSJ.getdGmsdh(m, s, j), FastMath.abs(eps * dGHdh[0]));
98                      Assertions.assertEquals(dGHdh[1], gMSJ.getdHmsdh(m, s, j), FastMath.abs(eps * dGHdh[1]));
99                  }
100             }
101         }
102     }
103 
104     /** dG/dα and dH/dα computations test based on 2 independent methods.
105      *  If they give same results, we assume them to be consistent.
106      */
107     @Test
108     public void testdGHdAlpha() {
109         final int sMax = 30;
110         final int mMax = 20;
111         final MersenneTwister random = new MersenneTwister(123456789);
112         for (int i = 0; i < 10; i++) {
113             final double k = random.nextDouble();
114             final double h = random.nextDouble();
115             final double a = random.nextDouble();
116             final double b = random.nextDouble();
117             final GHmsjPolynomials gMSJ = new GHmsjPolynomials(k, h, a, b, 1);
118             for (int s = -sMax; s <= sMax; s++) {
119                 for (int m = 2; m <= mMax; m+=2) {
120                     final int j = m / 2;
121                     final double[] dGHda = getdGHda(k, h, a, b, m, s, j);
122                     Assertions.assertEquals(dGHda[0], gMSJ.getdGmsdAlpha(m, s, j), FastMath.abs(eps * dGHda[0]));
123                     Assertions.assertEquals(dGHda[1], gMSJ.getdHmsdAlpha(m, s, j), FastMath.abs(eps * dGHda[1]));
124                 }
125             }
126         }
127     }
128 
129     /** dG/dβ and dH/dβ computations test based on 2 independent methods.
130      *  If they give same results, we assume them to be consistent.
131      */
132     @Test
133     public void testdGHdBeta() {
134         final int sMax = 30;
135         final int mMax = 20;
136         final MersenneTwister random = new MersenneTwister(987654321);
137         for (int i = 0; i < 10; i++) {
138             final double k = random.nextDouble();
139             final double h = random.nextDouble();
140             final double a = random.nextDouble();
141             final double b = random.nextDouble();
142             final GHmsjPolynomials gMSJ = new GHmsjPolynomials(k, h, a, b, 1);
143             for (int s = -sMax; s <= sMax; s++) {
144                 for (int m = 2; m <= mMax; m+=2) {
145                     final int j = m / 2;
146                     final double[] dGHdb = getdGHdb(k, h, a, b, m, s, j);
147                     Assertions.assertEquals(dGHdb[0], gMSJ.getdGmsdBeta(m, s, j), FastMath.abs(eps * dGHdb[0]));
148                     Assertions.assertEquals(dGHdb[1], gMSJ.getdHmsdBeta(m, s, j), FastMath.abs(eps * dGHdb[1]));
149                 }
150             }
151         }
152     }
153 
154     /** Compute directly G<sup>j</sup><sub>ms</sub> and H<sup>j</sup><sub>ms</sub> from equation 2.7.1-(14).
155      * @param k x-component of the eccentricity vector
156      * @param h y-component of the eccentricity vector
157      * @param a 1st direction cosine
158      * @param b 2nd direction cosine
159      * @param m order
160      * @param s d'Alembert characteristic
161      * @param j index
162      * @return G<sub>ms</sub><sup>j</sup> and H<sup>j</sup><sub>ms</sub> values
163      */
164     private static double[] getGHmsj(final double k, final double h,
165                                      final double a, final double b,
166                                      final int m, final int s, final int j) {
167         final Complex kh = new Complex(k, h * sgn(s - j)).pow(FastMath.abs(s - j));
168         Complex ab;
169         if (FastMath.abs(s) < m) {
170             ab = new Complex(a, b).pow(m - s);
171         } else {
172             ab = new Complex(a, -b * sgn(s - m)).pow(FastMath.abs(s - m));
173         }
174         final Complex khab = kh.multiply(ab);
175 
176         return new double[] {khab.getReal(), khab.getImaginary()};
177     }
178 
179     /** Compute directly dG/dk and dH/dk from equation 2.7.1-(14).
180      * @param k x-component of the eccentricity vector
181      * @param h y-component of the eccentricity vector
182      * @param a 1st direction cosine
183      * @param b 2nd direction cosine
184      * @param m order
185      * @param s d'Alembert characteristic
186      * @param j index
187      * @return dG/dk and dH/dk values
188      */
189     private static double[] getdGHdk(final double k, final double h,
190                                      final double a, final double b,
191                                      final int m, final int s, final int j) {
192         final Complex kh = new Complex(k, h * sgn(s - j)).pow(FastMath.abs(s - j)-1).multiply(FastMath.abs(s - j));
193         Complex ab;
194         if (FastMath.abs(s) < m) {
195             ab = new Complex(a, b).pow(m - s);
196         } else {
197             ab = new Complex(a, -b * sgn(s - m)).pow(FastMath.abs(s - m));
198         }
199         final Complex khab = kh.multiply(ab);
200 
201         return new double[] {khab.getReal(), khab.getImaginary()};
202     }
203 
204     /** Compute directly dG/dh and dH/dh from equation 2.7.1-(14).
205      * @param k x-component of the eccentricity vector
206      * @param h y-component of the eccentricity vector
207      * @param a 1st direction cosine
208      * @param b 2nd direction cosine
209      * @param m order
210      * @param s d'Alembert characteristic
211      * @param j index
212      * @return dG/dh and dH/dh values
213      */
214     private static double[] getdGHdh(final double k, final double h,
215                                      final double a, final double b,
216                                      final int m, final int s, final int j) {
217         final Complex kh1 = new Complex(k, h * sgn(s - j)).pow(FastMath.abs(s - j)-1);
218         final Complex kh2 = new Complex(0., FastMath.abs(s - j) * sgn(s - j));
219         final Complex kh = kh1.multiply(kh2);
220         Complex ab;
221         if (FastMath.abs(s) < m) {
222             ab = new Complex(a, b).pow(m - s);
223         } else {
224             ab = new Complex(a, -b * sgn(s - m)).pow(FastMath.abs(s - m));
225         }
226         final Complex khab = kh.multiply(ab);
227 
228         return new double[] {khab.getReal(), khab.getImaginary()};
229     }
230 
231     /** Compute directly dG/dα and dH/dα from equation 2.7.1-(14).
232      * @param k x-component of the eccentricity vector
233      * @param h y-component of the eccentricity vector
234      * @param a 1st direction cosine
235      * @param b 2nd direction cosine
236      * @param m order
237      * @param s d'Alembert characteristic
238      * @param j index
239      * @return dG/dα and dH/dα values
240      */
241     private static double[] getdGHda(final double k, final double h,
242                                      final double a, final double b,
243                                      final int m, final int s, final int j) {
244         final Complex kh = new Complex(k, h * sgn(s - j)).pow(FastMath.abs(s - j));
245         Complex ab;
246         if (FastMath.abs(s) < m) {
247             ab = new Complex(a, b).pow(m - s - 1).multiply(m - s);
248         } else {
249             ab = new Complex(a, -b * sgn(s - m)).pow(FastMath.abs(s - m) - 1).multiply(FastMath.abs(s - m));
250         }
251         final Complex khab = kh.multiply(ab);
252 
253         return new double[] {khab.getReal(), khab.getImaginary()};
254     }
255 
256     /** Compute directly dG/dβ and dH/dβ from equation 2.7.1-(14).
257      * @param k x-component of the eccentricity vector
258      * @param h y-component of the eccentricity vector
259      * @param a 1st direction cosine
260      * @param b 2nd direction cosine
261      * @param m order
262      * @param s d'Alembert characteristic
263      * @param j index
264      * @return dG/dβ and dH/dβ values
265      */
266     private static double[] getdGHdb(final double k, final double h,
267                                      final double a, final double b,
268                                      final int m, final int s, final int j) {
269         final Complex kh = new Complex(k, h * sgn(s - j)).pow(FastMath.abs(s - j));
270         Complex ab;
271         if (FastMath.abs(s) < m) {
272             Complex ab1 = new Complex(a, b).pow(m - s - 1);
273             Complex ab2 = new Complex(0., m - s);
274             ab = ab1.multiply(ab2);
275         } else {
276             Complex ab1 = new Complex(a, -b * sgn(s - m)).pow(FastMath.abs(s - m) - 1);
277             Complex ab2 = new Complex(0., FastMath.abs(s - m) * sgn(m - s));
278             ab = ab1.multiply(ab2);
279         }
280         final Complex khab = kh.multiply(ab);
281 
282         return new double[] {khab.getReal(), khab.getImaginary()};
283     }
284 
285     /** Get the sign of an integer.
286      *  @param i number on which evaluation is done
287      *  @return -1 or +1 depending on sign of i
288      */
289     private static int sgn(final int i) {
290         return (i < 0) ? -1 : 1;
291     }
292 }