17#include "gaussian_process_guider.h"
26#include "ekos_guide_debug.h"
28#define DLOG if (false) qCDebug
30#define SAVE_FFT_DATA_ 0
31#define PRINT_TIMINGS_ 0
33#define CIRCULAR_BUFFER_SIZE 8192
34#define REGULAR_BUFFER_SIZE 2048
36#define GRID_INTERVAL 5.0
37#define MAX_DITHER_STEPS 10
39#define DEFAULT_LEARNING_RATE 0.01
43GaussianProcessGuider::GaussianProcessGuider(guide_parameters parameters) :
44 start_time_(std::chrono::system_clock::now()),
45 last_time_(std::chrono::system_clock::now()),
48 last_prediction_end_(0),
50 dithering_active_(false),
52 circular_buffer_data_(CIRCULAR_BUFFER_SIZE),
53 covariance_function_(),
54 output_covariance_function_(),
55 gp_(covariance_function_),
56 learning_rate_(DEFAULT_LEARNING_RATE),
57 parameters(parameters)
59 circular_buffer_data_.push_front(data_point());
60 circular_buffer_data_[0].control = 0;
61 gp_.enableExplicitTrend();
62 gp_.enableOutputProjection(output_covariance_function_);
64 std::vector<double> hyperparameters(NumParameters);
65 hyperparameters[SE0KLengthScale] = parameters.SE0KLengthScale_;
66 hyperparameters[SE0KSignalVariance] = parameters.SE0KSignalVariance_;
67 hyperparameters[PKLengthScale] = parameters.PKLengthScale_;
68 hyperparameters[PKSignalVariance] = parameters.PKSignalVariance_;
69 hyperparameters[SE1KLengthScale] = parameters.SE1KLengthScale_;
70 hyperparameters[SE1KSignalVariance] = parameters.SE1KSignalVariance_;
71 hyperparameters[PKPeriodLength] = parameters.PKPeriodLength_;
72 SetGPHyperparameters(hyperparameters);
74 qCDebug(KSTARS_EKOS_GUIDE) <<
75 QString(
"GPG Parameters: control_gain %1 min_move %2 pred_gain %3 min_for_inf %4 min_for_period %5 pts %6 cpd %7 -- se0L %8 se0V %9 PL %10 PV %11 Se1L %12 se1V %13 ppd %14")
76 .
arg(parameters.control_gain_, 6,
'f', 3)
77 .
arg(parameters.min_move_, 6,
'f', 3)
78 .
arg(parameters.prediction_gain_, 6,
'f', 3)
79 .
arg(parameters.min_periods_for_inference_, 6,
'f', 3)
80 .
arg(parameters.min_periods_for_period_estimation_, 6,
'f', 3)
81 .
arg(parameters.points_for_approximation_)
82 .
arg(parameters.compute_period_)
83 .
arg(parameters.SE0KLengthScale_, 6,
'f', 3)
84 .
arg(parameters.SE0KSignalVariance_, 6,
'f', 3)
85 .
arg(parameters.PKLengthScale_, 6,
'f', 3)
86 .
arg(parameters.PKSignalVariance_, 6,
'f', 3)
87 .
arg(parameters.SE1KLengthScale_, 6,
'f', 3)
88 .
arg(parameters.SE1KSignalVariance_, 6,
'f', 3)
89 .
arg(parameters.PKPeriodLength_, 6,
'f', 3);
92GaussianProcessGuider::~GaussianProcessGuider()
96void GaussianProcessGuider::SetTimestamp()
98 auto current_time = std::chrono::system_clock::now();
99 double delta_measurement_time = std::chrono::duration<double>(current_time - last_time_).count();
100 last_time_ = current_time;
101 get_last_point().timestamp = std::chrono::duration<double>(current_time - start_time_).count()
102 - (delta_measurement_time / 2.0)
107void GaussianProcessGuider::HandleGuiding(
double input,
double SNR)
110 get_last_point().measurement = input;
111 get_last_point().variance = CalculateVariance(SNR);
115 last_prediction_end_ = get_last_point().timestamp;
118void GaussianProcessGuider::HandleDarkGuiding()
121 get_last_point().measurement = 0;
122 get_last_point().variance = 1e4;
125void GaussianProcessGuider::HandleControls(
double control_input)
127 get_last_point().control = control_input;
130double GaussianProcessGuider::CalculateVariance(
double SNR)
132 SNR = std::max(SNR, 3.4);
135 double standard_deviation = 2.1752 * 1 / (SNR - 3.3) + 0.5;
137 return standard_deviation * standard_deviation;
143 clock_t begin = std::clock();
146 size_t N = get_number_of_measurements();
149 Eigen::VectorXd timestamps(N - 1);
150 Eigen::VectorXd measurements(N - 1);
151 Eigen::VectorXd variances(N - 1);
152 Eigen::VectorXd sum_controls(N - 1);
154 double sum_control = 0;
157 for (
size_t i = 0; i < N - 1; i++)
159 sum_control += circular_buffer_data_[i].control;
160 timestamps(i) = circular_buffer_data_[i].timestamp;
161 measurements(i) = circular_buffer_data_[i].measurement;
162 variances(i) = circular_buffer_data_[i].variance;
163 sum_controls(i) = sum_control;
166 Eigen::VectorXd gear_error(N - 1);
167 Eigen::VectorXd linear_fit(N - 1);
170 gear_error = sum_controls + measurements;
173 clock_t end = std::clock();
174 double time_init = double(end - begin) / CLOCKS_PER_SEC;
175 begin = std::clock();
182 timestamps =
result.row(0);
183 gear_error =
result.row(1);
184 variances =
result.row(2);
188 double time_regularize = double(end - begin) / CLOCKS_PER_SEC;
189 begin = std::clock();
193 Eigen::MatrixXd feature_matrix(2, timestamps.rows());
194 feature_matrix.row(0) = Eigen::MatrixXd::Ones(1, timestamps.rows());
195 feature_matrix.row(1) = timestamps.array();
198 Eigen::VectorXd weights = (feature_matrix * feature_matrix.transpose()
199 + 1e-3 * Eigen::Matrix<double, 2, 2>::Identity()).ldlt().solve(feature_matrix * gear_error);
202 linear_fit = weights.transpose() * feature_matrix;
205 Eigen::VectorXd gear_error_detrend = gear_error - linear_fit;
209 double time_detrend = double(end - begin) / CLOCKS_PER_SEC;
210 begin = std::clock();
215 double period_length = GetGPHyperparameters()[PKPeriodLength];
216 if (GetBoolComputePeriod() && get_last_point().timestamp > parameters.min_periods_for_period_estimation_ * period_length)
219 period_length = EstimatePeriodLength(timestamps, gear_error_detrend);
224 time_fft = double(end - begin) / CLOCKS_PER_SEC;
229 begin = std::clock();
233 gp_.inferSD(timestamps, gear_error, parameters.points_for_approximation_, variances, prediction_point);
237 double time_gp = double(end - begin) / CLOCKS_PER_SEC;
239 printf(
"timings: init: %f, regularize: %f, detrend: %f, fft: %f, gp: %f, total: %f\n",
240 time_init, time_regularize, time_detrend, time_fft, time_gp,
241 time_init + time_regularize + time_detrend + time_fft + time_gp);
245double GaussianProcessGuider::PredictGearError(
double prediction_location)
248 if (last_prediction_end_ < 0.0)
250 last_prediction_end_ = std::chrono::duration<double>(std::chrono::system_clock::now() - start_time_).count();
254 Eigen::VectorXd next_location(2);
255 next_location << last_prediction_end_, prediction_location + dither_offset_;
256 Eigen::VectorXd prediction = gp_.predictProjected(next_location);
258 double p1 = prediction(1);
259 double p0 = prediction(0);
261 assert(!math_tools::isNaN(p1 - p0));
263 last_prediction_end_ = next_location(1);
277 double hyst_percentage = 0.0;
278 double period_length;
280 if (dithering_active_)
282 DLOG(KSTARS_EKOS_GUIDE) <<
QString(
"GPG::result(in=%1,snr=%2,ts=%3,ppt=%4) dithering active")
283 .
arg(input, 6,
'f', 2).
arg(SNR, 5,
'f', 1).
arg(time_step, 6,
'f', 2).
arg(prediction_point, 5,
'f', 1);
284 if (--dither_steps_ <= 0)
286 dithering_active_ =
false;
290 GPDebug->Log(
"PPEC rslt(dithering): input = %.2f, final = %.2f",
291 input, parameters.control_gain_ * input);
293 DLOG(KSTARS_EKOS_GUIDE)
294 <<
QString(
"PPEC rslt(dithering): input = %1, final = %2")
295 .
arg(input, 5,
'f', 2)
296 .
arg(parameters.control_gain_ * input, 5,
'f', 2);
298 return parameters.control_gain_ * input;
301 DLOG(KSTARS_EKOS_GUIDE) <<
QString(
"GPG::result(in=%1,snr=%2,ts=%3,ppt=%4)")
302 .
arg(input, 6,
'f', 2).
arg(SNR, 5,
'f', 1).
arg(time_step, 6,
'f', 2).
arg(prediction_point, 5,
'f', 1);
304 if (get_number_of_measurements() == 1)
306 start_time_ = std::chrono::system_clock::now();
307 last_time_ = start_time_;
311 HandleGuiding(input, SNR);
314 double last_control = 0.0;
315 if (get_number_of_measurements() > 1)
317 last_control = get_second_last_point().control;
319 double hysteresis_control = (1.0 - HYSTERESIS) * input + HYSTERESIS * last_control;
320 hysteresis_control *= parameters.control_gain_;
322 control_signal_ = parameters.control_gain_ * input;
323 if (std::abs(input) < parameters.min_move_)
325 control_signal_ = 0.0;
326 hysteresis_control = 0.0;
328 assert(std::abs(control_signal_) == 0.0 || std::abs(input) >= parameters.min_move_);
331 if (get_number_of_measurements() > 10)
333 if (prediction_point < 0.0)
335 prediction_point = std::chrono::duration<double>(std::chrono::system_clock::now() - start_time_).count();
338 UpdateGP(prediction_point + 0.5 * time_step);
341 prediction_ = PredictGearError(prediction_point + time_step);
342 control_signal_ += parameters.prediction_gain_ * prediction_;
345 period_length = GetGPHyperparameters()[PKPeriodLength];
346 if (get_last_point().timestamp < parameters.min_periods_for_inference_ * period_length)
348 double percentage = get_last_point().timestamp / (parameters.min_periods_for_inference_ * period_length);
349 percentage = std::min(percentage, 1.0);
350 hyst_percentage = 1.0 - percentage;
351 control_signal_ = percentage * control_signal_ + (1.0 - percentage) * hysteresis_control;
356 period_length = GetGPHyperparameters()[PKPeriodLength];
360 assert(!math_tools::isNaN(control_signal_));
363 if (math_tools::isNaN(control_signal_))
365 control_signal_ = hysteresis_control;
369 HandleControls(control_signal_);
371 GPDebug->Log(
"PPEC rslt: input = %.2f, final = %.2f, react = %.2f, pred = %.2f, hyst = %.2f, hyst_pct = %.2f, period_length = %.2f",
372 input, control_signal_, parameters.control_gain_ * input, parameters.prediction_gain_ * prediction_, hysteresis_control,
373 hyst_percentage, period_length);
375 DLOG(KSTARS_EKOS_GUIDE) <<
376 QString(
"PPEC rslt: input = %1, final = %2, react = %3, pred = %4, hyst = %5, hyst_pct = %6, period_length = %7")
377 .
arg(input, 5,
'f', 2)
378 .
arg(control_signal_, 5,
'f', 2)
379 .
arg(parameters.control_gain_ * input, 5,
'f', 2)
380 .
arg(parameters.prediction_gain_ * prediction_, 5,
'f', 2)
381 .
arg(hysteresis_control, 5,
'f', 2)
382 .
arg(hyst_percentage, 5,
'f', 2)
383 .
arg(period_length, 5,
'f', 2);
384 return control_signal_;
393 if (get_number_of_measurements() > 10
394 && get_last_point().timestamp > parameters.min_periods_for_inference_ * GetGPHyperparameters()[PKPeriodLength])
396 if (prediction_point < 0.0)
398 prediction_point = std::chrono::duration<double>(std::chrono::system_clock::now() - start_time_).count();
401 UpdateGP(prediction_point + 0.5 * time_step);
404 prediction_ = PredictGearError(prediction_point + time_step);
405 control_signal_ += prediction_;
409 HandleControls(control_signal_);
412 assert(!math_tools::isNaN(control_signal_));
415 if (math_tools::isNaN(control_signal_))
417 control_signal_ = 0.0;
420 return control_signal_;
425 qCDebug(KSTARS_EKOS_GUIDE) <<
QString(
"GPG::reset()");
426 circular_buffer_data_.clear();
431 circular_buffer_data_.push_front(data_point());
432 circular_buffer_data_[0].control = 0;
434 last_prediction_end_ = -1.0;
435 start_time_ = std::chrono::system_clock::now();
436 last_time_ = std::chrono::system_clock::now();
438 dither_offset_ = 0.0;
440 dithering_active_ =
false;
445 DLOG(KSTARS_EKOS_GUIDE) <<
QString(
"GPG::GuidingDithered(amt=%1,rate=%2)")
446 .
arg(amt, 6,
'f', 2).
arg(rate, 5,
'f', 1);
449 dither_offset_ += amt / rate;
451 dithering_active_ =
true;
452 dither_steps_ = MAX_DITHER_STEPS;
457 DLOG(KSTARS_EKOS_GUIDE) <<
QString(
"GPG::GuidingDitherSettleDone(%1)")
458 .
arg(success ?
"true" :
"false");
475double GaussianProcessGuider::GetControlGain(
void)
const
477 return parameters.control_gain_;
480bool GaussianProcessGuider::SetControlGain(
double control_gain)
482 parameters.control_gain_ = control_gain;
486bool GaussianProcessGuider::GetBoolComputePeriod()
const
488 return parameters.compute_period_;
491bool GaussianProcessGuider::SetBoolComputePeriod(
bool active)
493 parameters.compute_period_ = active;
497std::vector<double> GaussianProcessGuider::GetGPHyperparameters()
const
500 Eigen::VectorXd hyperparameters_full = gp_.getHyperParameters().array().exp();
502 Eigen::VectorXd hyperparameters = hyperparameters_full.tail(NumParameters);
505 hyperparameters(PKLengthScale) = std::asin(hyperparameters(PKLengthScale) / 4.0) * hyperparameters(PKPeriodLength) / M_PI;
508 return std::vector<double>(hyperparameters.data(),
509 hyperparameters.data() + NumParameters);
512bool GaussianProcessGuider::SetGPHyperparameters(std::vector<double>
const &hyperparameters)
514 Eigen::VectorXd hyperparameters_eig = Eigen::VectorXd::Map(&hyperparameters[0], hyperparameters.size());
517 hyperparameters_eig(SE0KLengthScale) = std::max(hyperparameters_eig(SE0KLengthScale), 1.0);
518 hyperparameters_eig(PKLengthScale) = std::max(hyperparameters_eig(PKLengthScale), 1.0);
519 hyperparameters_eig(SE1KLengthScale) = std::max(hyperparameters_eig(SE1KLengthScale), 1.0);
522 hyperparameters_eig(PKLengthScale) = 4 * std::sin(hyperparameters_eig(PKLengthScale)
523 * M_PI / hyperparameters_eig(PKPeriodLength));
526 hyperparameters_eig = hyperparameters_eig.array().max(1e-10);
529 Eigen::VectorXd hyperparameters_full(NumParameters + 1);
530 hyperparameters_full << 1.0, hyperparameters_eig;
533 gp_.setHyperParameters(hyperparameters_full.array().log());
537double GaussianProcessGuider::GetMinMove()
const
539 return parameters.min_move_;
542bool GaussianProcessGuider::SetMinMove(
double min_move)
544 parameters.min_move_ = min_move;
548int GaussianProcessGuider::GetNumPointsForApproximation()
const
550 return parameters.points_for_approximation_;
553bool GaussianProcessGuider::SetNumPointsForApproximation(
int num_points)
555 parameters.points_for_approximation_ = num_points;
559double GaussianProcessGuider::GetPeriodLengthsInference()
const
561 return parameters.min_periods_for_inference_;
564bool GaussianProcessGuider::SetPeriodLengthsInference(
double num_periods)
566 parameters.min_periods_for_inference_ = num_periods;
570double GaussianProcessGuider::GetPeriodLengthsPeriodEstimation()
const
572 return parameters.min_periods_for_period_estimation_;
575bool GaussianProcessGuider::SetPeriodLengthsPeriodEstimation(
double num_periods)
577 parameters.min_periods_for_period_estimation_ = num_periods;
581double GaussianProcessGuider::GetPredictionGain()
const
583 return parameters.prediction_gain_;
586bool GaussianProcessGuider::SetPredictionGain(
double prediction_gain)
588 parameters.prediction_gain_ = prediction_gain;
595 HandleGuiding(input, SNR);
596 last_prediction_end_ = timestamp;
597 get_last_point().timestamp = timestamp;
599 start_time_ = std::chrono::system_clock::now() - std::chrono::seconds((
int) timestamp);
602 HandleControls(control);
605double GaussianProcessGuider::EstimatePeriodLength(
const Eigen::VectorXd &time,
const Eigen::VectorXd &data)
608 Eigen::VectorXd windowed_data = data.array() * math_tools::hamming_window(data.rows()).array();
611 std::pair<Eigen::VectorXd, Eigen::VectorXd>
result = math_tools::compute_spectrum(windowed_data, FFT_SIZE);
613 Eigen::ArrayXd amplitudes =
result.first;
614 Eigen::ArrayXd frequencies =
result.second;
616 double dt = (time(time.rows() - 1) - time(0)) / (time.rows() - 1);
620 Eigen::ArrayXd periods = 1 / frequencies.array();
621 amplitudes = (periods > 1500.0).select(0, amplitudes);
623 assert(amplitudes.size() == frequencies.size());
625 Eigen::VectorXd::Index maxIndex;
626 amplitudes.maxCoeff(&maxIndex);
628 double max_frequency = frequencies(maxIndex);
632 if (maxIndex < frequencies.size() - 1 && maxIndex > 0)
634 double spread = std::abs(frequencies(maxIndex - 1) - frequencies(maxIndex + 1));
636 Eigen::VectorXd interp_loc(3);
637 interp_loc << frequencies(maxIndex - 1), frequencies(maxIndex), frequencies(maxIndex + 1);
638 interp_loc = interp_loc.array() - max_frequency;
639 interp_loc = interp_loc.array() / spread;
641 Eigen::VectorXd interp_dat(3);
642 interp_dat << amplitudes(maxIndex - 1), amplitudes(maxIndex), amplitudes(maxIndex + 1);
643 interp_dat = interp_dat.array() / amplitudes(maxIndex);
647 if (interp_dat.maxCoeff() - interp_dat.minCoeff() < 1e-10)
649 return 1 / max_frequency;
654 Eigen::MatrixXd phi(3, 3);
655 phi.row(0) = interp_loc.array().pow(2);
656 phi.row(1) = interp_loc.array().pow(1);
657 phi.row(2) = interp_loc.array().pow(0);
660 Eigen::VectorXd w = (phi * phi.transpose()).ldlt().solve(phi * interp_dat);
663 max_frequency = max_frequency - w(1) / (2 * w(0)) * spread;
668 std::ofstream outfile;
669 outfile.open(
"spectrum_data.csv", std::ios_base::out);
672 outfile <<
"period, amplitude\n";
673 for (
int i = 0; i < amplitudes.size(); ++i)
675 outfile << std::setw(8) << periods[i] <<
"," << std::setw(8) << amplitudes[i] <<
"\n";
680 std::cout <<
"unable to write to file" << std::endl;
686 double period_length = 1 / max_frequency;
687 return period_length;
692 std::vector<double> hypers = GetGPHyperparameters();
695 assert(!math_tools::isNaN(period_length));
698 if (math_tools::isNaN(period_length))
700 period_length = hypers[PKPeriodLength];
704 hypers[PKPeriodLength] = (1 - learning_rate_) * hypers[PKPeriodLength] + learning_rate_ * period_length;
706 SetGPHyperparameters(hypers);
710 const Eigen::VectorXd &gear_error,
const Eigen::VectorXd &variances)
712 size_t N = get_number_of_measurements();
713 double grid_interval = GRID_INTERVAL;
714 double last_cell_end = -grid_interval;
715 double last_timestamp = -grid_interval;
716 double last_gear_error = 0.0;
717 double last_variance = 0.0;
718 double gear_error_sum = 0.0;
719 double variance_sum = 0.0;
720 int grid_size =
static_cast<int>(std::ceil(timestamps(timestamps.size() - 1) / grid_interval)) + 1;
721 assert(grid_size > 0);
722 Eigen::VectorXd reg_timestamps(grid_size);
723 Eigen::VectorXd reg_gear_error(grid_size);
724 Eigen::VectorXd reg_variances(grid_size);
726 for (
size_t i = 0; i < N - 1; ++i)
728 if (timestamps(i) < last_cell_end + grid_interval)
730 gear_error_sum += (timestamps(i) - last_timestamp) * 0.5 * (last_gear_error + gear_error(i));
731 variance_sum += (timestamps(i) - last_timestamp) * 0.5 * (last_variance + variances(i));
732 last_timestamp = timestamps(i);
736 while (timestamps(i) >= last_cell_end + grid_interval)
738 double inter_timestamp = last_cell_end + grid_interval;
740 double proportion = (inter_timestamp - last_timestamp) / (timestamps(i) - last_timestamp);
741 double inter_gear_error = proportion * gear_error(i) + (1 - proportion) * last_gear_error;
742 double inter_variance = proportion * variances(i) + (1 - proportion) * last_variance;
744 gear_error_sum += (inter_timestamp - last_timestamp) * 0.5 * (last_gear_error + inter_gear_error);
745 variance_sum += (inter_timestamp - last_timestamp) * 0.5 * (last_variance + inter_variance);
747 reg_timestamps(j) = last_cell_end + 0.5 * grid_interval;
748 reg_gear_error(j) = gear_error_sum / grid_interval;
749 reg_variances(j) = variance_sum / grid_interval;
751 last_timestamp = inter_timestamp;
752 last_gear_error = inter_gear_error;
753 last_variance = inter_variance;
754 last_cell_end = inter_timestamp;
756 gear_error_sum = 0.0;
762 if (j > REGULAR_BUFFER_SIZE)
764 j = REGULAR_BUFFER_SIZE;
768 Eigen::MatrixXd
result(3, j);
769 result.row(0) = reg_timestamps.head(j);
770 result.row(1) = reg_gear_error.head(j);
771 result.row(2) = reg_variances.head(j);
779 size_t N = get_number_of_measurements();
787 Eigen::VectorXd timestamps(N - 1);
788 Eigen::VectorXd measurements(N - 1);
789 Eigen::VectorXd variances(N - 1);
790 Eigen::VectorXd controls(N - 1);
791 Eigen::VectorXd sum_controls(N - 1);
792 Eigen::VectorXd gear_error(N - 1);
793 Eigen::VectorXd linear_fit(N - 1);
796 for (
size_t i = 0; i < N - 1; i++)
798 timestamps(i) = circular_buffer_data_[i].timestamp;
799 measurements(i) = circular_buffer_data_[i].measurement;
800 variances(i) = circular_buffer_data_[i].variance;
801 controls(i) = circular_buffer_data_[i].control;
802 sum_controls(i) = circular_buffer_data_[i].control;
805 sum_controls(i) += sum_controls(i - 1);
808 gear_error = sum_controls + measurements;
811 Eigen::VectorXd locations = Eigen::VectorXd::LinSpaced(M, 0, get_second_last_point().timestamp + 1500);
813 Eigen::VectorXd vars(locations.size());
814 Eigen::VectorXd means = gp_.predictProjected(locations, &vars);
815 Eigen::VectorXd stds = vars.array().sqrt();
818 std::ofstream outfile;
819 outfile.open(
"measurement_data.csv", std::ios_base::out);
822 outfile <<
"location, output\n";
823 for(
int i = 0; i < timestamps.size(); ++i)
825 outfile << std::setw(8) << timestamps[i] <<
"," << std::setw(8) << gear_error[i] <<
"\n";
830 std::cout <<
"unable to write to file" << std::endl;
834 outfile.open(
"gp_data.csv", std::ios_base::out);
837 outfile <<
"location, mean, std\n";
838 for(
int i = 0; i < locations.size(); ++i)
840 outfile << std::setw(8) << locations[i] <<
"," << std::setw(8) << means[i] <<
"," << std::setw(8) << stds[i] <<
"\n";
845 std::cout <<
"unable to write to file" << std::endl;
853 learning_rate_ = learning_rate;
859class NullDebugLog :
public GPDebug
861 void Log(
const char *fmt, ...)
override
867class GPDebug *GPDebug =
new NullDebugLog();
876 GPDebug::SetGPDebug(
nullptr);
881void GPDebug::SetGPDebug(GPDebug *logger)
void UpdateGP(double prediction_point=std::numeric_limits< double >::quiet_NaN())
Runs the inference machinery on the GP.
void SetLearningRate(double learning_rate)
Sets the learning rate.
void GuidingDitherSettleDone(bool success)
This method tells the guider that dithering is finished.
void reset()
Clears the data from the circular buffer and clears the GP data.
void save_gp_data() const
Saves the GP data to a csv file for external analysis.
void GuidingDithered(double amt, double rate)
This method tells the guider that a dither command was issued.
void DirectMoveApplied(double amt, double rate)
This method tells the guider that a direct move was issued.
void UpdatePeriodLength(double period_length)
Does filtering and sets the period length of the GPGuider.
double deduceResult(double time_step, double prediction_point=-1.0)
This method provides predictive control if no measurement could be made.
void inject_data_point(double timestamp, double input, double SNR, double control)
This method is needed for automated testing.
Eigen::MatrixXd regularize_dataset(const Eigen::VectorXd ×tamps, const Eigen::VectorXd &gear_error, const Eigen::VectorXd &variances)
Takes timestamps, measurements and SNRs and returns them regularized in a matrix.
double result(double input, double SNR, double time_step, double prediction_point=-1.0)
Calculates the control value based on the current input.
Implementation of Open Astronomy Log (OAL) XML specifications to record observation logs.
QCA_EXPORT Logger * logger()
QString arg(Args &&... args) const const