13#include "fitsviewer/fitsdata.h"
14#include "fitsviewer/fitsview.h"
15#include "auxiliary/kspaths.h"
16#include "ekos_guide_debug.h"
17#include "ekos/auxiliary/stellarsolverprofileeditor.h"
18#include "guidealgorithms.h"
20#include "../guideview.h"
29#include <QRandomGenerator>
33double getNoise(
double maxAbsVal)
35 return (maxAbsVal - generator.
bounded(maxAbsVal * 2));
41 GuiderUtils::Vector position;
42 if (usingSEPMultiStar())
44 QRect trackingBox = guideView->getTrackingBox();
45 position = guideStars.findGuideStar(imageData, trackingBox, guideView, firstFrame);
49 position = GuideAlgorithms::findLocalStarPosition(
50 imageData, algorithm, video_width, video_height,
51 guideView->getTrackingBox());
53 if (position.x == -1 || position.y == -1)
62 starPosition = GuiderUtils::Vector(0);
63 targetPosition = GuiderUtils::Vector(0);
68 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
69 drift[GUIDE_RA] =
new double[CIRCULAR_BUFFER_SIZE];
70 drift[GUIDE_DEC] =
new double[CIRCULAR_BUFFER_SIZE];
71 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
72 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
73 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
81 delete[] drift[GUIDE_RA];
82 delete[] drift[GUIDE_DEC];
85bool cgmath::setVideoParameters(
int vid_wd,
int vid_ht,
int binX,
int binY)
87 if (vid_wd <= 0 || vid_ht <= 0)
90 video_width = vid_wd / binX;
91 video_height = vid_ht / binY;
93 calibration.setBinningUsed(binX, binY);
94 guideStars.setCalibration(calibration);
99bool cgmath::setGuiderParameters(
double ccd_pix_wd,
double ccd_pix_ht,
double guider_aperture,
double guider_focal)
105 if (guider_focal <= 0)
108 aperture = guider_aperture;
109 guideStars.setCalibration(calibration);
115void cgmath::createGuideLog()
119 QTextStream out(&logFile);
121 out <<
"Guiding rate,x15 arcsec/sec: " <<
Qt::endl;
122 out <<
"Focal,mm: " << calibration.getFocalLength() <<
Qt::endl;
123 out <<
"Aperture,mm: " << aperture <<
Qt::endl;
124 out <<
"F/D: " << calibration.getFocalLength() / aperture <<
Qt::endl;
125 out <<
"Frame #, Time Elapsed (ms), RA Error (arcsec), RA Correction (ms), RA Correction Direction, DEC Error "
126 "(arcsec), DEC Correction (ms), DEC Correction Direction"
132bool cgmath::setTargetPosition(
double x,
double y)
139 if (x >= (
double)video_width - 1)
140 x = (double)video_width - 1;
141 if (y >= (
double)video_height - 1)
142 y = (double)video_height - 1;
144 targetPosition = GuiderUtils::Vector(x, y, 0);
146 guideStars.setCalibration(calibration);
151bool cgmath::getTargetPosition(
double *x,
double *y)
const
153 *x = targetPosition.x;
154 *y = targetPosition.y;
158int cgmath::getAlgorithmIndex(
void)
const
163void cgmath::getStarScreenPosition(
double *dx,
double *dy)
const
165 *dx = starPosition.x;
166 *dy = starPosition.y;
171 iterationCounter = 0;
172 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
173 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
176 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
177 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
182void cgmath::setAlgorithmIndex(
int algorithmIndex)
184 if (algorithmIndex < 0 || algorithmIndex > SEP_MULTISTAR)
187 algorithm = algorithmIndex;
190bool cgmath::usingSEPMultiStar()
const
192 return algorithm == SEP_MULTISTAR;
195void cgmath::updateCircularBuffers(
void)
199 driftUpto[GUIDE_RA]++;
200 driftUpto[GUIDE_DEC]++;
201 if (driftUpto[GUIDE_RA] >= CIRCULAR_BUFFER_SIZE)
202 driftUpto[GUIDE_RA] = 0;
203 if (driftUpto[GUIDE_DEC] >= CIRCULAR_BUFFER_SIZE)
204 driftUpto[GUIDE_DEC] = 0;
210 iterationCounter = 0;
211 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
212 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
215 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
216 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
218 if (calibration.getFocalLength() > 0 && aperture > 0)
229void cgmath::suspend(
bool mode)
234bool cgmath::isSuspended()
const
239bool cgmath::isStarLost()
const
244void cgmath::setLostStar(
bool is_lost)
253 if (raDEC == GUIDE_RA)
255 else if (raDEC == GUIDE_DEC)
261const QString directionStr(GuideDirection dir)
266 return "Decrease RA";
268 return "Increase RA";
270 return "Decrease DEC";
272 return "Increase DEC";
279bool cgmath::configureInParams(Ekos::GuideState state)
281 const bool dithering = state == Ekos::GuideState::GUIDE_DITHERING;
285 in_params.proportional_gain[0] = Options::rAProportionalGain();
286 in_params.proportional_gain[1] = Options::dECProportionalGain();
288 in_params.integral_gain[0] = Options::rAIntegralGain();
289 in_params.integral_gain[1] = Options::dECIntegralGain();
292 in_params.enabled[0] = Options::rAGuideEnabled();
293 in_params.enabled[1] = Options::dECGuideEnabled();
295 in_params.min_pulse_arcsec[0] = Options::rAMinimumPulseArcSec();
296 in_params.min_pulse_arcsec[1] = Options::dECMinimumPulseArcSec();
298 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
299 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
303 in_params.enabled_axis1[0] = Options::eastRAGuideEnabled();
305 in_params.enabled_axis2[0] = Options::westRAGuideEnabled();
309 in_params.enabled_axis1[1] = Options::northDECGuideEnabled();
311 in_params.enabled_axis2[1] = Options::southDECGuideEnabled();
316 in_params.proportional_gain[0] = 1.0;
317 in_params.proportional_gain[1] = 1.0;
318 in_params.integral_gain[0] = 0.0;
319 in_params.integral_gain[1] = 0.0;
320 in_params.min_pulse_arcsec[0] = 0.0;
321 in_params.min_pulse_arcsec[1] = 0.0;
322 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
323 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
324 in_params.enabled[0] =
true;
325 in_params.enabled[1] =
true;
326 in_params.enabled_axis1[0] =
true;
327 in_params.enabled_axis2[0] =
true;
328 in_params.enabled_axis1[1] =
true;
329 in_params.enabled_axis2[1] =
true;
335void cgmath::updateOutParams(
int k,
const double arcsecDrift,
int pulseLength, GuideDirection pulseDirection)
337 out_params.pulse_dir[k] = pulseDirection;
338 out_params.pulse_length[k] = pulseLength;
339 out_params.delta[k] = arcsecDrift;
342void cgmath::outputGuideLog()
344 if (Options::guideLogging())
346 QTextStream out(&logFile);
347 out << iterationCounter <<
"," << logTime.elapsed() <<
"," << out_params.delta[0] <<
"," << out_params.pulse_length[0] <<
349 << directionStr(out_params.pulse_dir[0]) <<
"," << out_params.delta[1] <<
","
350 << out_params.pulse_length[1] <<
"," << directionStr(out_params.pulse_dir[1]) <<
Qt::endl;
354void cgmath::processAxis(
const int k,
const bool dithering,
const bool darkGuide,
const Seconds &timeStep,
358 GuideDirection pulseDirection = NO_DIR;
363 const int idx = driftUpto[k];
364 const double arcsecDrift = drift[k][idx];
366 const double pulseConverter = (k == GUIDE_RA) ?
367 calibration.raPulseMillisecondsPerArcsecond() :
368 calibration.decPulseMillisecondsPerArcsecond();
369 const double maxPulseMilliseconds = in_params.max_pulse_arcsec[k] * pulseConverter;
372 bool useGPG = !dithering && Options::gPGEnabled() && (k == GUIDE_RA) && in_params.enabled[k];
373 if (useGPG && darkGuide)
375 gpg->darkGuiding(&pulseLength, &dir, calibration, timeStep);
376 pulseDirection =
dir;
381 qCDebug(KSTARS_EKOS_GUIDE) <<
"Warning: dark guiding without GPG or while dithering.";
384 else if (useGPG && gpg->computePulse(arcsecDrift,
385 usingSEPMultiStar() ? &guideStars :
nullptr, &pulseLength, &dir, calibration, timeStep))
387 pulseDirection =
dir;
388 pulseLength = std::min(pulseLength,
static_cast<int>(maxPulseMilliseconds + 0.5));
397 drift_integral[k] = 0;
398 for (
int i = 0; i < CIRCULAR_BUFFER_SIZE; ++i)
399 drift_integral[k] += drift[k][i];
400 drift_integral[k] /= (double)CIRCULAR_BUFFER_SIZE;
402 if (in_params.integral_gain[k] > 0)
403 qCDebug(KSTARS_EKOS_GUIDE) <<
label <<
"drift[" << axisStr(k) <<
"] = " << arcsecDrift
404 <<
" integral[" << axisStr(k) <<
"] = " << drift_integral[k];
406 const double arcsecPerMsPulse = k == GUIDE_RA ? calibration.raPulseMillisecondsPerArcsecond() :
407 calibration.decPulseMillisecondsPerArcsecond();
408 const double proportionalResponse = arcsecDrift * in_params.proportional_gain[k] * arcsecPerMsPulse;
409 const double integralResponse = drift_integral[k] * in_params.integral_gain[k] * arcsecPerMsPulse;
410 pulseLength = std::min(fabs(proportionalResponse + integralResponse), maxPulseMilliseconds);
414 if (!in_params.enabled[k] ||
416 (arcsecDrift > 0 && !in_params.enabled_axis1[k]) ||
418 (arcsecDrift < 0 && !in_params.enabled_axis2[k]))
420 pulseDirection = NO_DIR;
426 const double pulseArcSec = pulseConverter > 0 ? pulseLength / pulseConverter : 0;
427 if (pulseArcSec >= in_params.min_pulse_arcsec[k])
430 pulseDirection = arcsecDrift > 0 ? RA_DEC_DIR : RA_INC_DIR;
432 pulseDirection = arcsecDrift > 0 ? DEC_DEC_DIR : DEC_INC_DIR;
435 pulseDirection = NO_DIR;
439 updateOutParams(k, arcsecDrift, pulseLength, pulseDirection);
442void cgmath::calculatePulses(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
444 const bool dithering = configureInParams(state);
446 processAxis(GUIDE_RA, dithering,
false, timeStep.first,
"Guiding:");
447 processAxis(GUIDE_DEC, dithering,
false, timeStep.second,
"Guiding:");
449 qCDebug(KSTARS_EKOS_GUIDE)
450 << QString(
"Guiding pulses: RA: %1ms %2 DEC: %3ms %4")
451 .arg(out_params.pulse_length[GUIDE_RA]).arg(directionStr(out_params.pulse_dir[GUIDE_RA]))
452 .arg(out_params.pulse_length[GUIDE_DEC]).arg(directionStr(out_params.pulse_dir[GUIDE_DEC]));
459 const std::pair<Seconds, Seconds> &timeStep, GuideLog * logger)
463 if (Options::gPGEnabled())
465 GuiderUtils::Vector guideStarPosition = findLocalStarPosition(imageData, guideView,
false);
466 if (guideStarPosition.x != -1 && !std::isnan(guideStarPosition.x))
468 gpg->suspended(guideStarPosition, targetPosition,
469 usingSEPMultiStar() ? &guideStars :
nullptr, calibration);
478 GuiderUtils::Vector starPositionArcSec, targetPositionArcSec;
481 starPosition = findLocalStarPosition(imageData, guideView,
false);
484 if (starPosition.x == -1 || std::isnan(starPosition.x))
487 if (logger !=
nullptr && state == Ekos::GUIDE_GUIDING)
489 GuideLog::GuideData data;
490 data.code = GuideLog::GuideData::NO_STAR_FOUND;
491 data.type = GuideLog::GuideData::DROP;
492 logger->addGuideData(data);
500 QVector3D starCenter(starPosition.x, starPosition.y, 0);
501 emit newStarPosition(starCenter,
true);
504 if (state == Ekos::GUIDE_CALIBRATING)
507 if (state == Ekos::GUIDE_GUIDING && (targetPosition.x <= 0.0 || targetPosition.y <= 0.0))
509 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding with target 0.0 -- something's wrong!!!!!!!!!!!";
510 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
512 out_params.pulse_dir[k] = NO_DIR;
513 out_params.pulse_length[k] = 0;
514 out_params.delta[k] = 0;
522 starPositionArcSec = calibration.convertToArcseconds(starPosition);
523 targetPositionArcSec = calibration.convertToArcseconds(targetPosition);
526 const GuiderUtils::Vector star_xy_arcsec_drift = starPositionArcSec - targetPositionArcSec;
527 const GuiderUtils::Vector star_drift = calibration.rotateToRaDec(star_xy_arcsec_drift);
533 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = star_drift.x;
534 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = star_drift.y;
536 qCDebug(KSTARS_EKOS_GUIDE)
537 << QString(
"Star %1 %2 a-s %3 %4 Target %5 %6 a-s %7 %8 Drift: RA %9 DEC %10")
538 .arg(starPosition.x, 0,
'f', 1) .arg(starPosition.y, 0,
'f', 1)
539 .arg(starPositionArcSec.x, 0,
'f', 1) .arg(starPositionArcSec.y, 0,
'f', 1)
540 .arg(targetPosition.x, 0,
'f', 1) .arg(targetPosition.y, 0,
'f', 1)
541 .arg(targetPositionArcSec.x, 0,
'f', 1).arg(targetPositionArcSec.y, 0,
'f', 1)
542 .arg(star_drift.x, 0,
'f', 2) .arg(star_drift.y, 0,
'f', 2);
544 if (state == Ekos::GUIDE_GUIDING && usingSEPMultiStar())
546 double multiStarRADrift, multiStarDECDrift;
547 if (guideStars.getDrift(sqrt(star_drift.x * star_drift.x + star_drift.y * star_drift.y),
548 targetPosition.x, targetPosition.y,
549 &multiStarRADrift, &multiStarDECDrift))
551#ifdef DEBUG_ADD_NOISE
552 const double raNoise = getNoise(.75);
553 const double decNoise = getNoise(.75);
554 multiStarRADrift += raNoise;
555 multiStarDECDrift += decNoise;
556 qCDebug(KSTARS_EKOS_GUIDE)
557 <<
"Added guide noise arcsecs" <<
"RA" << raNoise <<
"DEC" << decNoise;
558 fprintf(stderr,
"Add guide noise arcsecs %.1f %.1f\n", raNoise, decNoise);
560 qCDebug(KSTARS_EKOS_GUIDE) << QString(
"MultiStar drift: RA %1 DEC %2")
561 .arg(multiStarRADrift, 0,
'f', 2)
562 .arg(multiStarDECDrift, 0,
'f', 2);
563 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = multiStarRADrift;
564 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = multiStarDECDrift;
568 qCDebug(KSTARS_EKOS_GUIDE) <<
"MultiStar: failed, fell back to guide star";
574 const double raDrift = drift[GUIDE_RA][driftUpto[GUIDE_RA]];
575 const double decDrift = drift[GUIDE_DEC][driftUpto[GUIDE_DEC]];
578 calculatePulses(state, timeStep);
580 if (state == Ekos::GUIDE_GUIDING)
584 updateCircularBuffers();
586 qCDebug(KSTARS_EKOS_GUIDE) << QString(
"performProcessing took %1s").arg(timer.
elapsed() / 1000.0, 0,
'f', 3);
588 if (logger !=
nullptr)
590 GuideLog::GuideData data;
591 data.type = GuideLog::GuideData::MOUNT;
594 data.dx = starPosition.x - targetPosition.x;
595 data.dy = starPosition.y - targetPosition.y;
597 calibration.convertToPixels(-raDrift, -decDrift, &data.raDistance, &data.decDistance);
599 const double raGuideFactor = out_params.pulse_dir[GUIDE_RA] == NO_DIR ?
600 0 : (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR ? 1.0 : -1.0);
601 const double decGuideFactor = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ?
602 0 : (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR ? 1.0 : -1.0);
606 data.raGuideDistance = calibration.xPixelsPerArcsecond() * raGuideFactor * out_params.pulse_length[GUIDE_RA] /
607 calibration.raPulseMillisecondsPerArcsecond();
608 data.decGuideDistance = calibration.yPixelsPerArcsecond() * decGuideFactor * out_params.pulse_length[GUIDE_DEC] /
609 calibration.decPulseMillisecondsPerArcsecond();
611 data.raDuration = out_params.pulse_dir[GUIDE_RA] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_RA];
612 data.raDirection = out_params.pulse_dir[GUIDE_RA];
613 data.decDuration = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_DEC];
614 data.decDirection = out_params.pulse_dir[GUIDE_DEC];
615 data.code = GuideLog::GuideData::NO_ERRORS;
616 data.snr = guideStars.getGuideStarSNR();
617 data.mass = guideStars.getGuideStarMass();
619 logger->addGuideData(data);
623void cgmath::performDarkGuiding(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
626 const bool dithering = configureInParams(state);
629 processAxis(GUIDE_RA, dithering,
true, timeStep.first,
"Dark Guiding:");
630 qCDebug(KSTARS_EKOS_GUIDE)
631 << QString(
"Dark Guiding pulses: RA: %1ms %2")
632 .arg(out_params.pulse_length[GUIDE_RA]).arg(directionStr(out_params.pulse_dir[GUIDE_RA]));
636 updateOutParams(GUIDE_DEC, 0, 0, NO_DIR);
641void cgmath::emitStats()
644 if (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR)
645 pulseRA = out_params.pulse_length[GUIDE_RA];
646 else if (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR)
647 pulseRA = -out_params.pulse_length[GUIDE_RA];
649 if (out_params.pulse_dir[GUIDE_DEC] == DEC_DEC_DIR)
650 pulseDEC = -out_params.pulse_length[GUIDE_DEC];
651 else if (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
652 pulseDEC = out_params.pulse_length[GUIDE_DEC];
654 const bool hasGuidestars = usingSEPMultiStar();
655 const double snr = hasGuidestars ? guideStars.getGuideStarSNR() : 0;
656 const double skyBG = hasGuidestars ? guideStars.skybackground().mean : 0;
657 const int numStars = hasGuidestars ? guideStars.skybackground().starsDetected : 0;
659 emit guideStats(-out_params.delta[GUIDE_RA], -out_params.delta[GUIDE_DEC],
660 pulseRA, pulseDEC, snr, skyBG, numStars);
663void cgmath::calculateRmsError(
void)
668 if (iterationCounter == 0)
671 int count = std::min(iterationCounter,
static_cast<unsigned int>(CIRCULAR_BUFFER_SIZE));
672 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
675 for (
int i = 0; i < count; ++i)
676 sqr_avg += drift[k][i] * drift[k][i];
678 out_params.sigma[k] = sqrt(sqr_avg / (
double)count);
685 return guideStars.selectGuideStar(imageData);
688double cgmath::getGuideStarSNR()
690 return guideStars.getGuideStarSNR();
694cproc_in_params::cproc_in_params()
699void cproc_in_params::reset(
void)
703 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
706 integral_gain[k] = 0;
707 max_pulse_arcsec[k] = 5000;
708 min_pulse_arcsec[k] = 0;
712cproc_out_params::cproc_out_params()
717void cproc_out_params::reset(
void)
719 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
722 pulse_dir[k] = NO_DIR;
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString label(StandardShortcut id)
QCA_EXPORT Logger * logger()
qint64 elapsed() const const
double bounded(double highest)
QTextStream & endl(QTextStream &stream)