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.data;
18  
19  import org.orekit.errors.OrekitInternalError;
20  
21  /** Encoder/decoder for Delaunay and planetary multipliers keys.
22   * <p>
23   * As Delaunay and planetary multipliers often have a lot of zeroes
24   * and the non-zero multipliers are in a small range, it makes sense
25   * to encode them in a compact representation that can be used as
26   * a key in hash tables. This class does the encoding/decoding of
27   * such keys.
28   * </p>
29   * <p>
30   * The encoding scheme is as follows, numbering bits from
31   * 0 for least significant bit to 63 for most significant bit:
32   * </p>
33   * <ul>
34   *   <li>bits  0 to 14: mask for the 15 coefficients</li>
35   *   <li>bits 15 to 63: split into 7 slots each 7 bits long and
36   *   each encoding a coefficient ci + 64, where ci is the i-th
37   *   non-zero coefficient</li>
38   * </ul>
39   * <p>
40   * This scheme allows to encode 7 non-zero integers between -64 to +63 among 15.
41   * As the current Poisson series used in Orekit have at most 6 non-zero coefficients
42   * and all the coefficients are between -21 and +20, we have some extension margin.
43   * </p>
44   */
45  class NutationCodec {
46  
47      /** Current multiplier flag bit. */
48      private long flag;
49  
50      /** Current coefficient shift. */
51      private int shift;
52  
53      /** Current key value. */
54      private long key;
55  
56      /** Simple constructor.
57       * @param key key
58       */
59      private NutationCodec(final long key) {
60          flag  = 0x1l;
61          shift = 15;
62          this.key = key;
63      }
64  
65      /** Get the key value.
66       * @return key value
67       */
68      public long getKey() {
69          return key;
70      }
71  
72      /** Encode one more multiplier in the key.
73       * @param multiplier multiplier to encode
74       */
75      private void addMultiplier(final int multiplier) {
76  
77          if (multiplier != 0) {
78              // this is a coefficient we want to store
79              key = key | flag;
80              if (shift > 57 || multiplier < -64 || multiplier > 63) {
81                  // this should never happen, we exceed the encoding capacity
82                  throw new OrekitInternalError(null);
83              }
84              key    = key | (((multiplier + 64l) & 0x7Fl) << shift);
85              shift += 7;
86          }
87  
88          // update bit mask
89          flag = flag << 1;
90  
91      }
92  
93      /** Decode one multiplier from the key.
94       * @return decoded multiplier
95       */
96      private int nextMultiplier() {
97          final int multiplier;
98          if ((key & flag) == 0x0l) {
99              // no values are stored for this coefficient, it is 0
100             multiplier = 0;
101         } else {
102             // there is a stored value for this coefficient
103             multiplier = ((int) ((key >>> shift) & 0x7Fl)) - 64;
104             shift += 7;
105         }
106 
107         // update bit mask
108         flag = flag << 1;
109 
110         return multiplier;
111 
112     }
113 
114     /** Encode all tide, Delaunay and planetary multipliers into one key.
115      * @param multipliers multipliers to encode
116      * @return a key merging all multipliers as one long integer
117      */
118     public static long encode(final int... multipliers) {
119         final NutationCodec encoder = new NutationCodec(0x0l);
120         for (final int multiplier : multipliers) {
121             encoder.addMultiplier(multiplier);
122         }
123         return encoder.getKey();
124     }
125 
126     /** Decode a key into all tide, Delaunay and planetary multipliers.
127      * @param key key merging all multipliers as one long integer
128      * @return all tide, Delaunay and planetary multiplers, in the order
129      * cGamma, cL, cLPrime, cF, cD, cOmega, cMe, cVe, cE, cMa, cJu, cSa, cUr, cNe, cPa
130      */
131     public static int[] decode(final long key) {
132         final int[] multipliers = new int[15];
133         final NutationCodec decoder = new NutationCodec(key);
134         for (int i = 0; i < multipliers.length; ++i) {
135             multipliers[i] = decoder.nextMultiplier();
136         }
137         return multipliers;
138     }
139 
140 }