NutationCodec.java

/* Copyright 2002-2022 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.data;

import org.orekit.errors.OrekitInternalError;

/** Encoder/decoder for Delaunay and planetary multipliers keys.
 * <p>
 * As Delaunay and planetary multipliers often have a lot of zeroes
 * and the non-zero multipliers are in a small range, it makes sense
 * to encode them in a compact representation that can be used as
 * a key in hash tables. This class does the encoding/decoding of
 * such keys.
 * </p>
 * <p>
 * The encoding scheme is as follows, numbering bits from
 * 0 for least significant bit to 63 for most significant bit:
 * </p>
 * <ul>
 *   <li>bits  0 to 14: mask for the 15 coefficients</li>
 *   <li>bits 15 to 63: split into 7 slots each 7 bits long and
 *   each encoding a coefficient ci + 64, where ci is the i-th
 *   non-zero coefficient</li>
 * </ul>
 * <p>
 * This scheme allows to encode 7 non-zero integers between -64 to +63 among 15.
 * As the current Poisson series used in Orekit have at most 6 non-zero coefficients
 * and all the coefficients are between -21 and +20, we have some extension margin.
 * </p>
 */
class NutationCodec {

    /** Current multiplier flag bit. */
    private long flag;

    /** Current coefficient shift. */
    private int shift;

    /** Current key value. */
    private long key;

    /** Simple constructor.
     * @param key key
     */
    private NutationCodec(final long key) {
        flag  = 0x1l;
        shift = 15;
        this.key = key;
    }

    /** Get the key value.
     * @return key value
     */
    public long getKey() {
        return key;
    }

    /** Encode one more multiplier in the key.
     * @param multiplier multiplier to encode
     */
    private void addMultiplier(final int multiplier) {

        if (multiplier != 0) {
            // this is a coefficient we want to store
            key = key | flag;
            if (shift > 57 || multiplier < -64 || multiplier > 63) {
                // this should never happen, we exceed the encoding capacity
                throw new OrekitInternalError(null);
            }
            key    = key | (((multiplier + 64l) & 0x7Fl) << shift);
            shift += 7;
        }

        // update bit mask
        flag = flag << 1;

    }

    /** Decode one multiplier from the key.
     * @return decoded multiplier
     */
    private int nextMultiplier() {
        final int multiplier;
        if ((key & flag) == 0x0l) {
            // no values are stored for this coefficient, it is 0
            multiplier = 0;
        } else {
            // there is a stored value for this coefficient
            multiplier = ((int) ((key >>> shift) & 0x7Fl)) - 64;
            shift += 7;
        }

        // update bit mask
        flag = flag << 1;

        return multiplier;

    }

    /** Encode all tide, Delaunay and planetary multipliers into one key.
     * @param multipliers multipliers to encode
     * @return a key merging all multipliers as one long integer
     */
    public static long encode(final int... multipliers) {
        final NutationCodec encoder = new NutationCodec(0x0l);
        for (final int multiplier : multipliers) {
            encoder.addMultiplier(multiplier);
        }
        return encoder.getKey();
    }

    /** Decode a key into all tide, Delaunay and planetary multipliers.
     * @param key key merging all multipliers as one long integer
     * @return all tide, Delaunay and planetary multiplers, in the order
     * cGamma, cL, cLPrime, cF, cD, cOmega, cMe, cVe, cE, cMa, cJu, cSa, cUr, cNe, cPa
     */
    public static int[] decode(final long key) {
        final int[] multipliers = new int[15];
        final NutationCodec decoder = new NutationCodec(key);
        for (int i = 0; i < multipliers.length; ++i) {
            multipliers[i] = decoder.nextMultiplier();
        }
        return multipliers;
    }

}