[education/kstars] kstars/ekos/guide/internalguide/MPI_IS_gaussian_process/src: Guide: Fix GPG predictive-guiding circular buffer losing insertion order after 8192 samples

Jasem Mutlaq <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 588caf1ea8318ce2691b44fe638d90a348919652 by Jasem Mutlaq, on behalf of Andreas R..
Committed on 24/07/2026 at 07:08.
Pushed by mutlaqja into branch 'master'.

Guide: Fix GPG predictive-guiding circular buffer losing insertion order after 8192 samples

The internal circular_buffer used by the Gaussian Process Guiding (GPG /
Predictive PEC) algorithm never advanced its read offset (start) when the
buffer filled up. It advanced the write cursor (end) and pinned num_items
at the capacity, but left start fixed at 0.

As a result, once CIRCULAR_BUFFER_SIZE (8192) samples had been collected,
operator[] no longer returned the stored points in insertion order:

  - The returned sequence became rotated relative to true chronological
    order, introducing a discontinuity where the oldest slot was
    overwritten. Timestamps read out of order break the regularization and
    de-trending steps in UpdateGP().
  - get_last_point() (== operator[](size()-1)) stopped tracking the most
    recently inserted sample. Because size() is pinned at the capacity and
    start stays 0, it always resolved to a fixed physical slot instead of
    the newest point, so HandleGuiding()/HandleControls() read and wrote
    the wrong entry every cycle.
  - UpdateGP() reconstructs the accumulated gear error as an order-dependent
    cumulative sum of the control signals (sum_controls + measurements), so
    a rotated/misaligned view corrupts the data the GP is trained on.

Because one point is pushed per guide cycle, the buffer fills after
8192 exposures. At short guide-exposure cadences common on harmonic-drive
mounts (0.5-0.7 s) this happens after only ~68-82 minutes of continuous
guiding, after which predictive guiding quality silently degrades. The
guard assert() in operator[] is compiled out in release builds, so the
misbehavior is silent.

Fix: advance start (the index of the oldest entry) whenever a push
overwrites the oldest item, i.e. once the buffer is full. This makes the
class behave as a proper ring buffer: index 0 is always the oldest
surviving sample and size()-1 is always the newest, for an unbounded number
of pushes. Pre-saturation behavior is unchanged.

Verified with a standalone test that pushes well past the capacity and
asserts operator[] returns a strictly monotonic insertion-ordered sequence,
that the oldest/newest endpoints are correct, and that get_last_point()
returns the most recently inserted item.

M  +13   -2    kstars/ekos/guide/internalguide/MPI_IS_gaussian_process/src/gaussian_process_guider.h

https://invent.kde.org/education/kstars/-/commit/588caf1ea8318ce2691b44fe638d90a348919652

diff --git a/kstars/ekos/guide/internalguide/MPI_IS_gaussian_process/src/gaussian_process_guider.h b/kstars/ekos/guide/internalguide/MPI_IS_gaussian_process/src/gaussian_process_guider.h
index ce78956aac..19e875acf2 100644
--- a/kstars/ekos/guide/internalguide/MPI_IS_gaussian_process/src/gaussian_process_guider.h
+++ b/kstars/ekos/guide/internalguide/MPI_IS_gaussian_process/src/gaussian_process_guider.h
@@ -83,10 +83,21 @@ class GaussianProcessGuider
                 void push_front(T item)
                 {
                     buff[end] = item;
-                    num_items++;
-                    if (num_items >= size_) num_items = size_;
                     end++;
                     if (end >= size_) end = 0;
+                    if (num_items >= size_)
+                    {
+                        // Buffer is full: this push overwrote the oldest item, so advance
+                        // start to keep operator[] returning items in insertion order
+                        // (index 0 = oldest, size()-1 = newest). Without this, once the
+                        // buffer wraps, the read order becomes rotated relative to the true
+                        // chronological order and get_last_point() no longer tracks the most
+                        // recently inserted sample.
+                        start++;
+                        if (start >= size_) start = 0;
+                    }
+                    else
+                        num_items++;
                 }
 
             private:
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.