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]; 
 
  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_); 
 
  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)
 
  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();
 
  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