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"
32 GuiderUtils::Vector position;
33 if (usingSEPMultiStar())
35 QRect trackingBox = guideView->getTrackingBox();
36 position = guideStars.findGuideStar(imageData, trackingBox, guideView, firstFrame);
40 position = GuideAlgorithms::findLocalStarPosition(
41 imageData, algorithm, video_width, video_height,
42 guideView->getTrackingBox());
44 if (position.x == -1 || position.y == -1)
53 starPosition = GuiderUtils::Vector(0);
54 targetPosition = GuiderUtils::Vector(0);
59 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
60 drift[GUIDE_RA] =
new double[CIRCULAR_BUFFER_SIZE];
61 drift[GUIDE_DEC] =
new double[CIRCULAR_BUFFER_SIZE];
62 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
63 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
64 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
72 delete[] drift[GUIDE_RA];
73 delete[] drift[GUIDE_DEC];
76bool cgmath::setVideoParameters(
int vid_wd,
int vid_ht,
int binX,
int binY)
78 if (vid_wd <= 0 || vid_ht <= 0)
81 video_width = vid_wd / binX;
82 video_height = vid_ht / binY;
84 calibration.setBinningUsed(binX, binY);
85 guideStars.setCalibration(calibration);
90bool cgmath::setGuiderParameters(
double ccd_pix_wd,
double ccd_pix_ht,
double guider_aperture,
double guider_focal)
96 if (guider_focal <= 0)
99 aperture = guider_aperture;
100 guideStars.setCalibration(calibration);
106void cgmath::createGuideLog()
112 out <<
"Guiding rate,x15 arcsec/sec: " <<
Qt::endl;
113 out <<
"Focal,mm: " << calibration.getFocalLength() <<
Qt::endl;
114 out <<
"Aperture,mm: " << aperture <<
Qt::endl;
115 out <<
"F/D: " << calibration.getFocalLength() / aperture <<
Qt::endl;
116 out <<
"Frame #, Time Elapsed (ms), RA Error (arcsec), RA Correction (ms), RA Correction Direction, DEC Error "
117 "(arcsec), DEC Correction (ms), DEC Correction Direction"
123bool cgmath::setTargetPosition(
double x,
double y)
130 if (x >= (
double)video_width - 1)
131 x = (double)video_width - 1;
132 if (y >= (
double)video_height - 1)
133 y = (double)video_height - 1;
135 targetPosition = GuiderUtils::Vector(x, y, 0);
137 guideStars.setCalibration(calibration);
142bool cgmath::getTargetPosition(
double *x,
double *y)
const
144 *x = targetPosition.x;
145 *y = targetPosition.y;
149int cgmath::getAlgorithmIndex(
void)
const
154void cgmath::getStarScreenPosition(
double *dx,
double *dy)
const
156 *dx = starPosition.x;
157 *dy = starPosition.y;
162 iterationCounter = 0;
163 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
164 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
167 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
168 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
173void cgmath::setAlgorithmIndex(
int algorithmIndex)
175 if (algorithmIndex < 0 || algorithmIndex > SEP_MULTISTAR)
178 algorithm = algorithmIndex;
181bool cgmath::usingSEPMultiStar()
const
183 return algorithm == SEP_MULTISTAR;
186void cgmath::updateCircularBuffers(
void)
190 driftUpto[GUIDE_RA]++;
191 driftUpto[GUIDE_DEC]++;
192 if (driftUpto[GUIDE_RA] >= CIRCULAR_BUFFER_SIZE)
193 driftUpto[GUIDE_RA] = 0;
194 if (driftUpto[GUIDE_DEC] >= CIRCULAR_BUFFER_SIZE)
195 driftUpto[GUIDE_DEC] = 0;
201 iterationCounter = 0;
202 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
203 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
206 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
207 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
209 if (calibration.getFocalLength() > 0 && aperture > 0)
220void cgmath::suspend(
bool mode)
225bool cgmath::isSuspended()
const
230bool cgmath::isStarLost()
const
235void cgmath::setLostStar(
bool is_lost)
244 if (raDEC == GUIDE_RA)
246 else if (raDEC == GUIDE_DEC)
252const QString directionStr(GuideDirection dir)
257 return "Decrease RA";
259 return "Increase RA";
261 return "Decrease DEC";
263 return "Increase DEC";
270bool cgmath::configureInParams(Ekos::GuideState state)
272 const bool dithering = state == Ekos::GuideState::GUIDE_DITHERING;
276 in_params.proportional_gain[0] = Options::rAProportionalGain();
277 in_params.proportional_gain[1] = Options::dECProportionalGain();
279 in_params.integral_gain[0] = Options::rAIntegralGain();
280 in_params.integral_gain[1] = Options::dECIntegralGain();
283 in_params.enabled[0] = Options::rAGuideEnabled();
284 in_params.enabled[1] = Options::dECGuideEnabled();
286 in_params.min_pulse_arcsec[0] = Options::rAMinimumPulseArcSec();
287 in_params.min_pulse_arcsec[1] = Options::dECMinimumPulseArcSec();
289 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
290 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
294 in_params.enabled_axis1[0] = Options::eastRAGuideEnabled();
296 in_params.enabled_axis2[0] = Options::westRAGuideEnabled();
300 in_params.enabled_axis1[1] = Options::northDECGuideEnabled();
302 in_params.enabled_axis2[1] = Options::southDECGuideEnabled();
307 in_params.proportional_gain[0] = 1.0;
308 in_params.proportional_gain[1] = 1.0;
309 in_params.integral_gain[0] = 0.0;
310 in_params.integral_gain[1] = 0.0;
311 in_params.min_pulse_arcsec[0] = 0.0;
312 in_params.min_pulse_arcsec[1] = 0.0;
313 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
314 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
315 in_params.enabled[0] =
true;
316 in_params.enabled[1] =
true;
317 in_params.enabled_axis1[0] =
true;
318 in_params.enabled_axis2[0] =
true;
319 in_params.enabled_axis1[1] =
true;
320 in_params.enabled_axis2[1] =
true;
326void cgmath::updateOutParams(
int k,
const double arcsecDrift,
int pulseLength, GuideDirection pulseDirection)
328 out_params.pulse_dir[k] = pulseDirection;
329 out_params.pulse_length[k] = pulseLength;
330 out_params.delta[k] = arcsecDrift;
333void cgmath::outputGuideLog()
335 if (Options::guideLogging())
338 out << iterationCounter <<
"," << logTime.
elapsed() <<
"," << out_params.delta[0] <<
"," << out_params.pulse_length[0] <<
340 << directionStr(out_params.pulse_dir[0]) <<
"," << out_params.delta[1] <<
","
341 << out_params.pulse_length[1] <<
"," << directionStr(out_params.pulse_dir[1]) <<
Qt::endl;
345void cgmath::processAxis(
const int k,
const bool dithering,
const bool darkGuide,
const Seconds &timeStep,
349 GuideDirection pulseDirection = NO_DIR;
354 const int idx = driftUpto[k];
355 const double arcsecDrift = drift[k][idx];
357 const double pulseConverter = (k == GUIDE_RA) ?
358 calibration.raPulseMillisecondsPerArcsecond() :
359 calibration.decPulseMillisecondsPerArcsecond();
360 const double maxPulseMilliseconds = in_params.max_pulse_arcsec[k] * pulseConverter;
363 bool useGPG = !dithering && Options::gPGEnabled() && (k == GUIDE_RA) && in_params.enabled[k];
364 if (useGPG && darkGuide)
366 gpg->darkGuiding(&pulseLength, &dir, calibration, timeStep);
367 pulseDirection =
dir;
372 qCDebug(KSTARS_EKOS_GUIDE) <<
"Warning: dark guiding without GPG or while dithering.";
375 else if (useGPG && gpg->computePulse(arcsecDrift,
376 usingSEPMultiStar() ? &guideStars :
nullptr, &pulseLength, &dir, calibration, timeStep))
378 pulseDirection =
dir;
379 pulseLength = std::min(pulseLength,
static_cast<int>(maxPulseMilliseconds + 0.5));
388 drift_integral[k] = 0;
389 for (
int i = 0; i < CIRCULAR_BUFFER_SIZE; ++i)
390 drift_integral[k] += drift[k][i];
391 drift_integral[k] /= (double)CIRCULAR_BUFFER_SIZE;
393 if (in_params.integral_gain[k] > 0)
394 qCDebug(KSTARS_EKOS_GUIDE) <<
label <<
"drift[" << axisStr(k) <<
"] = " << arcsecDrift
395 <<
" integral[" << axisStr(k) <<
"] = " << drift_integral[k];
397 const double arcsecPerMsPulse = k == GUIDE_RA ? calibration.raPulseMillisecondsPerArcsecond() :
398 calibration.decPulseMillisecondsPerArcsecond();
399 const double proportionalResponse = arcsecDrift * in_params.proportional_gain[k] * arcsecPerMsPulse;
400 const double integralResponse = drift_integral[k] * in_params.integral_gain[k] * arcsecPerMsPulse;
401 pulseLength = std::min(fabs(proportionalResponse + integralResponse), maxPulseMilliseconds);
405 if (!in_params.enabled[k] ||
407 (arcsecDrift > 0 && !in_params.enabled_axis1[k]) ||
409 (arcsecDrift < 0 && !in_params.enabled_axis2[k]))
411 pulseDirection = NO_DIR;
417 const double pulseArcSec = pulseConverter > 0 ? pulseLength / pulseConverter : 0;
418 if (pulseArcSec >= in_params.min_pulse_arcsec[k])
421 pulseDirection = arcsecDrift > 0 ? RA_DEC_DIR : RA_INC_DIR;
423 pulseDirection = arcsecDrift > 0 ? DEC_INC_DIR : DEC_DEC_DIR;
426 pulseDirection = NO_DIR;
430 updateOutParams(k, arcsecDrift, pulseLength, pulseDirection);
433void cgmath::calculatePulses(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
435 const bool dithering = configureInParams(state);
437 processAxis(GUIDE_RA, dithering,
false, timeStep.first,
"Guiding:");
438 processAxis(GUIDE_DEC, dithering,
false, timeStep.second,
"Guiding:");
440 qCDebug(KSTARS_EKOS_GUIDE)
441 <<
QString(
"Guiding pulses: RA: %1ms %2 DEC: %3ms %4")
442 .
arg(out_params.pulse_length[GUIDE_RA]).
arg(directionStr(out_params.pulse_dir[GUIDE_RA]))
443 .
arg(out_params.pulse_length[GUIDE_DEC]).
arg(directionStr(out_params.pulse_dir[GUIDE_DEC]));
450 const std::pair<Seconds, Seconds> &timeStep, GuideLog * logger)
454 if (Options::gPGEnabled())
456 GuiderUtils::Vector guideStarPosition = findLocalStarPosition(imageData, guideView,
false);
457 if (guideStarPosition.x != -1 && !std::isnan(guideStarPosition.x))
459 gpg->suspended(guideStarPosition, targetPosition,
460 usingSEPMultiStar() ? &guideStars :
nullptr, calibration);
469 GuiderUtils::Vector starPositionArcSec, targetPositionArcSec;
472 starPosition = findLocalStarPosition(imageData, guideView,
false);
475 if (starPosition.x == -1 || std::isnan(starPosition.x))
478 if (logger !=
nullptr && state == Ekos::GUIDE_GUIDING)
480 GuideLog::GuideData data;
481 data.code = GuideLog::GuideData::NO_STAR_FOUND;
482 data.type = GuideLog::GuideData::DROP;
483 logger->addGuideData(data);
491 QVector3D starCenter(starPosition.x, starPosition.y, 0);
492 emit newStarPosition(starCenter,
true);
495 if (state == Ekos::GUIDE_CALIBRATING)
498 if (state == Ekos::GUIDE_GUIDING && (targetPosition.x <= 0.0 || targetPosition.y <= 0.0))
500 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding with target 0.0 -- something's wrong!!!!!!!!!!!";
501 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
503 out_params.pulse_dir[k] = NO_DIR;
504 out_params.pulse_length[k] = 0;
505 out_params.delta[k] = 0;
513 starPositionArcSec = calibration.convertToArcseconds(starPosition);
514 targetPositionArcSec = calibration.convertToArcseconds(targetPosition);
517 const GuiderUtils::Vector star_xy_arcsec_drift = starPositionArcSec - targetPositionArcSec;
518 const GuiderUtils::Vector star_drift = calibration.rotateToRaDec(star_xy_arcsec_drift);
524 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = star_drift.x;
525 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = star_drift.y;
527 qCDebug(KSTARS_EKOS_GUIDE)
528 <<
QString(
"Star %1 %2 a-s %3 %4 Target %5 %6 a-s %7 %8 Drift: RA %9 DEC %10")
529 .
arg(starPosition.x, 0,
'f', 1) .
arg(starPosition.y, 0,
'f', 1)
530 .
arg(starPositionArcSec.x, 0,
'f', 1) .
arg(starPositionArcSec.y, 0,
'f', 1)
531 .
arg(targetPosition.x, 0,
'f', 1) .
arg(targetPosition.y, 0,
'f', 1)
532 .
arg(targetPositionArcSec.x, 0,
'f', 1).
arg(targetPositionArcSec.y, 0,
'f', 1)
533 .
arg(star_drift.x, 0,
'f', 2) .
arg(star_drift.y, 0,
'f', 2);
535 if (state == Ekos::GUIDE_GUIDING && usingSEPMultiStar())
537 double multiStarRADrift, multiStarDECDrift;
538 if (guideStars.getDrift(sqrt(star_drift.x * star_drift.x + star_drift.y * star_drift.y),
539 targetPosition.x, targetPosition.y,
540 &multiStarRADrift, &multiStarDECDrift))
542 qCDebug(KSTARS_EKOS_GUIDE) <<
QString(
"MultiStar drift: RA %1 DEC %2")
543 .
arg(multiStarRADrift, 0,
'f', 2)
544 .
arg(multiStarDECDrift, 0,
'f', 2);
545 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = multiStarRADrift;
546 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = multiStarDECDrift;
550 qCDebug(KSTARS_EKOS_GUIDE) <<
"MultiStar: failed, fell back to guide star";
556 const double raDrift = drift[GUIDE_RA][driftUpto[GUIDE_RA]];
557 const double decDrift = drift[GUIDE_DEC][driftUpto[GUIDE_DEC]];
560 calculatePulses(state, timeStep);
562 if (state == Ekos::GUIDE_GUIDING)
566 updateCircularBuffers();
568 qCDebug(KSTARS_EKOS_GUIDE) <<
QString(
"performProcessing took %1s").
arg(timer.elapsed() / 1000.0, 0,
'f', 3);
570 if (logger !=
nullptr)
572 GuideLog::GuideData data;
573 data.type = GuideLog::GuideData::MOUNT;
576 data.dx = starPosition.x - targetPosition.x;
577 data.dy = starPosition.y - targetPosition.y;
579 calibration.convertToPixels(-raDrift, -decDrift, &data.raDistance, &data.decDistance);
581 const double raGuideFactor = out_params.pulse_dir[GUIDE_RA] == NO_DIR ?
582 0 : (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR ? -1.0 : 1.0);
583 const double decGuideFactor = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ?
584 0 : (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR ? -1.0 : 1.0);
588 data.raGuideDistance = calibration.xPixelsPerArcsecond() * raGuideFactor * out_params.pulse_length[GUIDE_RA] /
589 calibration.raPulseMillisecondsPerArcsecond();
590 data.decGuideDistance = calibration.yPixelsPerArcsecond() * decGuideFactor * out_params.pulse_length[GUIDE_DEC] /
591 calibration.decPulseMillisecondsPerArcsecond();
593 data.raDuration = out_params.pulse_dir[GUIDE_RA] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_RA];
594 data.raDirection = out_params.pulse_dir[GUIDE_RA];
595 data.decDuration = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_DEC];
596 data.decDirection = out_params.pulse_dir[GUIDE_DEC];
597 data.code = GuideLog::GuideData::NO_ERRORS;
598 data.snr = guideStars.getGuideStarSNR();
599 data.mass = guideStars.getGuideStarMass();
601 logger->addGuideData(data);
605void cgmath::performDarkGuiding(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
608 const bool dithering = configureInParams(state);
611 processAxis(GUIDE_RA, dithering,
true, timeStep.first,
"Dark Guiding:");
612 qCDebug(KSTARS_EKOS_GUIDE)
613 <<
QString(
"Dark Guiding pulses: RA: %1ms %2")
614 .
arg(out_params.pulse_length[GUIDE_RA]).
arg(directionStr(out_params.pulse_dir[GUIDE_RA]));
618 updateOutParams(GUIDE_DEC, 0, 0, NO_DIR);
623void cgmath::emitStats()
626 if (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR)
627 pulseRA = out_params.pulse_length[GUIDE_RA];
628 else if (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR)
629 pulseRA = -out_params.pulse_length[GUIDE_RA];
631 if (out_params.pulse_dir[GUIDE_DEC] == DEC_DEC_DIR)
632 pulseDEC = -out_params.pulse_length[GUIDE_DEC];
633 else if (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
634 pulseDEC = out_params.pulse_length[GUIDE_DEC];
636 const bool hasGuidestars = usingSEPMultiStar();
637 const double snr = hasGuidestars ? guideStars.getGuideStarSNR() : 0;
638 const double skyBG = hasGuidestars ? guideStars.skybackground().mean : 0;
639 const int numStars = hasGuidestars ? guideStars.skybackground().starsDetected : 0;
641 emit guideStats(-out_params.delta[GUIDE_RA], -out_params.delta[GUIDE_DEC],
642 pulseRA, pulseDEC, snr, skyBG, numStars);
645void cgmath::calculateRmsError(
void)
650 if (iterationCounter == 0)
653 int count = std::min(iterationCounter,
static_cast<unsigned int>(CIRCULAR_BUFFER_SIZE));
654 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
657 for (
int i = 0; i < count; ++i)
658 sqr_avg += drift[k][i] * drift[k][i];
660 out_params.sigma[k] = sqrt(sqr_avg / (
double)count);
667 return guideStars.selectGuideStar(imageData);
670double cgmath::getGuideStarSNR()
672 return guideStars.getGuideStarSNR();
676cproc_in_params::cproc_in_params()
681void cproc_in_params::reset(
void)
685 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
688 integral_gain[k] = 0;
689 max_pulse_arcsec[k] = 5000;
690 min_pulse_arcsec[k] = 0;
694cproc_out_params::cproc_out_params()
699void cproc_out_params::reset(
void)
701 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
704 pulse_dir[k] = NO_DIR;
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString label(StandardShortcut id)
QCA_EXPORT Logger * logger()
qint64 elapsed() const const
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
virtual void close() override
QString arg(Args &&... args) const const
QTextStream & endl(QTextStream &stream)