[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: