AlternatingSampler.java

  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.estimation.measurements.gnss;

  18. import org.hipparchus.util.FastMath;

  19. /** Sampler for generating long integers between two limits in an alternating pattern.
  20.  * <p>
  21.  * Given a center a and a radius r, this class will generate integers kᵢ such
  22.  * that a - r ≤ kᵢ ≤ a + r. The generation order will start from the middle
  23.  * (i.e. k₀ is the long integer closest to a) and go towards the boundaries,
  24.  * alternating between values lesser than a and values greater than a.
  25.  * For example, with a = 17.3 and r = 5.2, it will generate: k₀ = 17, k₁ = 18,
  26.  * k₂ = 16, k₃ = 19, k₄ = 15, k₅ = 20, k₆ = 14, k₇ = 21, k₈ = 13, k₉ = 22.
  27.  * </p>
  28.  * <p>
  29.  * There are no hard limits to the generation, i.e. in the example above, the
  30.  * generator will happily generate k₁₀ = 12, k₁₁ = 23, k₁₂ = 11... In fact, if
  31.  * there are no integers at all between {@code a - r} and {@code a + r}, even
  32.  * the initial k₀ that is implicitly generated at construction will be out of
  33.  * range. The {@link #inRange()} method can be used to check if the last generator
  34.  * is still producing numbers within the initial range or if it has already
  35.  * started generating out of range numbers.
  36.  * </p>
  37.  * <p>
  38.  * If there are integers between {@code a - r} and {@code a + r}, it is guaranteed
  39.  * that they will all be generated once before {@link #inRange()} starts returning
  40.  * {@code false}.
  41.  * </p>
  42.  * <p>
  43.  * This allows to explore the range for one integer ambiguity starting
  44.  * with the most probable values (closest to a) and continuing with
  45.  * values less probable.
  46.  * </p>
  47.  * @see <a href="https://www.researchgate.net/publication/2790708_The_LAMBDA_method_for_integer_ambiguity_estimation_implementation_aspects">
  48.  * The LAMBDA method for integer ambiguity estimation: implementation aspects</a>
  49.  * @see <a href="https://oeis.org/A001057">
  50.  * A001057: Canonical enumeration of integers: interleaved positive and negative integers with zero prepended.</a>
  51.  * @author Luc Maisonobe
  52.  * @since 10.0
  53.  */
  54. class AlternatingSampler {

  55.     /** Range midpoint. */
  56.     private final double a;

  57.     /** Offset with respect to A001057. */
  58.     private final long offset;

  59.     /** Sign with respect to A001057. */
  60.     private final long sign;

  61.     /** Minimum number to generate. */
  62.     private long min;

  63.     /** Maximum number to generate. */
  64.     private long max;

  65.     /** Previous generated number in A001057. */
  66.     private long k1;

  67.     /** Current generated number in A001057. */
  68.     private long k0;

  69.     /** Current generated number. */
  70.     private long current;

  71.     /** Simple constructor.
  72.      * <p>
  73.      * A first initial integer is already generated as a side effect of
  74.      * construction, so {@link #getCurrent()} can be called even before
  75.      * calling {@link #generateNext()}. If there are no integers at
  76.      * all between {@code a - r} and {@code a + r}, then this initial
  77.      * integer will already be out of range.
  78.      * </p>
  79.      * @param a range midpoint
  80.      * @param r range radius
  81.      */
  82.     AlternatingSampler(final double a, final double r) {

  83.         this.a      = a;
  84.         this.offset = (long) FastMath.rint(a);
  85.         this.sign   = offset <= a ? +1 : -1;
  86.         setRadius(r);

  87.         this.k1      = 0;
  88.         this.k0      = 0;
  89.         this.current = offset;
  90.     }

  91.     /** Reset the range radius.
  92.      * <p>
  93.      * Resetting radius is allowed during sampling, it simply changes
  94.      * the boundaries used when calling {@link #inRange()}. Resetting
  95.      * the radius does not change the sampling itself, neither the
  96.      * {@link #getCurrent() current} value nor the {@link #generateNext()
  97.      * next generated} ones.
  98.      * </p>
  99.      * <p>
  100.      * A typical use case for calling {@link #setRadius(double)} during
  101.      * sampling is to reduce sampling interval. It is used to shrink
  102.      * the search ellipsoid on the fly in LAMBDA-based methods in order
  103.      * to speed-up search.
  104.      * </p>
  105.      * @param r range radius
  106.      */
  107.     public void setRadius(final double r) {
  108.         this.min = (long) FastMath.ceil(a - r);
  109.         this.max = (long) FastMath.floor(a + r);
  110.     }

  111.     /** Get the range midpoint.
  112.      * @return range midpoint
  113.      */
  114.     public double getMidPoint() {
  115.         return a;
  116.     }

  117.     /** Get current value.
  118.      * @return current value
  119.      */
  120.     public long getCurrent() {
  121.         return current;
  122.     }

  123.     /** Check if the current value is within range.
  124.      * @return true if current value is within range
  125.      */
  126.     public boolean inRange() {
  127.         return min <= current && current <= max;
  128.     }

  129.     /** Generate next value.
  130.      */
  131.     public void generateNext() {

  132.         // apply A001057 recursion
  133.         final long k2 = k1;
  134.         k1 = k0;
  135.         k0 = 1 - (k1 << 1) - k2;

  136.         // take offset and sign into account
  137.         current = offset + sign * k0;

  138.     }

  139. }