9#include "internalguider.h"
11#include "ekos_guide_debug.h"
14#include "auxiliary/kspaths.h"
15#include "fitsviewer/fitsdata.h"
16#include "fitsviewer/fitsview.h"
17#include "guidealgorithms.h"
18#include "ksnotification.h"
19#include "ekos/auxiliary/stellarsolverprofileeditor.h"
20#include "fitsviewer/fitsdata.h"
21#include "../guideview.h"
30#define MAX_GUIDE_STARS 10
32using namespace std::chrono_literals;
36InternalGuider::InternalGuider()
39 pmath.reset(
new cgmath());
40 connect(pmath.get(), &cgmath::newStarPosition,
this, &InternalGuider::newStarPosition);
41 connect(pmath.get(), &cgmath::guideStats,
this, &InternalGuider::guideStats);
45 pmath->getMutableCalibration()->restore(
46 pierSide, Options::reverseDecOnPierSideChange(), subBinX, subBinY,
nullptr);
53 m_darkGuideTimer = std::make_unique<QTimer>(
this);
54 m_captureTimer = std::make_unique<QTimer>(
this);
56 setDarkGuideTimerInterval();
60 connect(
this, &Ekos::GuideInterface::frameCaptureRequested,
this, [ = ]()
62 this->m_captureTimer->start();
66void InternalGuider::setExposureTime()
68 Seconds seconds(Options::guideExposure());
69 setTimer(m_captureTimer, seconds);
72void InternalGuider::setTimer(std::unique_ptr<QTimer> &timer, Seconds seconds)
74 const std::chrono::duration<double, std::milli> inMilliseconds(seconds);
75 timer->setInterval((
int)(inMilliseconds.count()));
78void InternalGuider::setDarkGuideTimerInterval()
80 constexpr double kMinInterval = 0.5;
81 const Seconds seconds(std::max(kMinInterval, Options::gPGDarkGuidingInterval()));
82 setTimer(m_darkGuideTimer, seconds);
85void InternalGuider::resetDarkGuiding()
87 m_darkGuideTimer->stop();
88 m_captureTimer->stop();
91bool InternalGuider::isInferencePeriodFinished()
93 auto const contribution = pmath->getGPG().predictionContribution();
94 return contribution >= 0.99;
96bool InternalGuider::guide()
98 if (state >= GUIDE_GUIDING)
100 return processGuiding();
103 if (state == GUIDE_SUSPENDED)
107 m_GuideFrame->disconnect(
this);
112 m_starLostCounter = 0;
113 m_highRMSCounter = 0;
116 m_isFirstFrame =
true;
118 if (state == GUIDE_IDLE)
120 if (Options::saveGuideLog())
122 GuideLog::GuideInfo info;
123 fillGuideInfo(&info);
124 guideLog.startGuiding(info);
126 state = GUIDE_GUIDING;
128 emit newStatus(state);
130 emit frameCaptureRequested();
144bool InternalGuider::abort()
149 guideLog.endGuiding();
152 if (state == GUIDE_CALIBRATING ||
153 state == GUIDE_GUIDING ||
154 state == GUIDE_DITHERING ||
155 state == GUIDE_MANUAL_DITHERING ||
156 state == GUIDE_REACQUIRE)
158 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
159 emit newStatus(GUIDE_DITHERING_ERROR);
160 emit newStatus(GUIDE_ABORTED);
162 qCDebug(KSTARS_EKOS_GUIDE) <<
"Aborting" << getGuideStatusString(state);
166 emit newStatus(GUIDE_IDLE);
167 qCDebug(KSTARS_EKOS_GUIDE) <<
"Stopping internal guider.";
171 disconnect(m_darkGuideTimer.get(),
nullptr,
nullptr,
nullptr);
177 m_ProgressiveDither.clear();
178 m_starLostCounter = 0;
179 m_highRMSCounter = 0;
183 pmath->suspend(
false);
185 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding aborted.";
190bool InternalGuider::suspend()
192 guideLog.pauseInfo();
193 state = GUIDE_SUSPENDED;
196 emit newStatus(state);
198 pmath->suspend(
true);
204void InternalGuider::startDarkGuiding()
206 if (Options::gPGDarkGuiding())
211 m_darkGuideTimer->start();
213 qCDebug(KSTARS_EKOS_GUIDE) <<
"Starting dark guiding.";
217bool InternalGuider::resume()
219 qCDebug(KSTARS_EKOS_GUIDE) <<
"Resuming...";
221 guideLog.resumeInfo();
222 state = GUIDE_GUIDING;
223 emit newStatus(state);
225 pmath->suspend(
false);
231 emit frameCaptureRequested();
236bool InternalGuider::ditherXY(
double x,
double y)
238 m_ProgressiveDither.clear();
241 pmath->getTargetPosition(&cur_x, &cur_y);
246 double oneJump = (guideBoxSize / 4.0);
247 double targetX = cur_x, targetY = cur_y;
248 int xSign = (x >= cur_x) ? 1 : -1;
249 int ySign = (y >= cur_y) ? 1 : -1;
253 if (fabs(targetX - x) > oneJump)
254 targetX += oneJump * xSign;
255 else if (fabs(targetX - x) < oneJump)
258 if (fabs(targetY - y) > oneJump)
259 targetY += oneJump * ySign;
260 else if (fabs(targetY - y) < oneJump)
263 m_ProgressiveDither.
enqueue(GuiderUtils::Vector(targetX, targetY, -1));
266 while (targetX != x || targetY != y);
268 m_DitherTargetPosition = m_ProgressiveDither.
dequeue();
269 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
270 guideLog.ditherInfo(x, y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
272 state = GUIDE_MANUAL_DITHERING;
273 emit newStatus(state);
280bool InternalGuider::dither(
double pixels)
282 if (Options::ditherWithOnePulse() )
283 return onePulseDither(pixels);
286 pmath->getTargetPosition(&ret_x, &ret_y);
293 GuiderUtils::Vector star_position = pmath->findLocalStarPosition(m_ImageData, m_GuideFrame,
false);
294 if (pmath->isStarLost() || (star_position.x == -1) || (star_position.y == -1))
298 if (++m_starLostCounter > MAX_LOST_STAR_THRESHOLD)
300 qCDebug(KSTARS_EKOS_GUIDE) <<
"Too many consecutive lost stars." << m_starLostCounter <<
"Aborting dither.";
301 return abortDither();
303 qCDebug(KSTARS_EKOS_GUIDE) <<
"Dither lost star. Trying again.";
304 emit frameCaptureRequested();
308 m_starLostCounter = 0;
310 if (state != GUIDE_DITHERING)
314 auto seed = std::chrono::system_clock::now().time_since_epoch().count();
315 std::default_random_engine generator(seed);
316 std::uniform_real_distribution<double> angleMagnitude(0, 360);
319 double diff_x = pixels * cos(angle);
320 double diff_y = pixels * sin(angle);
322 if (pmath->getCalibration().declinationSwapEnabled())
325 if (m_DitherOrigin.
x() == 0 && m_DitherOrigin.
y() == 0)
327 m_DitherOrigin =
QVector3D(ret_x, ret_y, 0);
329 double totalXOffset = ret_x - m_DitherOrigin.
x();
330 double totalYOffset = ret_y - m_DitherOrigin.
y();
335 if (((diff_x + totalXOffset > MAX_DITHER_TRAVEL) && (diff_x > 0)) ||
336 ((diff_x + totalXOffset < -MAX_DITHER_TRAVEL) && (diff_x < 0)))
338 qCDebug(KSTARS_EKOS_GUIDE)
339 <<
QString(
"Dithering target off by too much in X (abs(%1 + %2) > %3), adjust diff_x from %4 to %5")
340 .
arg(diff_x).
arg(totalXOffset).
arg(MAX_DITHER_TRAVEL).
arg(diff_x).
arg(diff_x * -1.5);
343 if (((diff_y + totalYOffset > MAX_DITHER_TRAVEL) && (diff_y > 0)) ||
344 ((diff_y + totalYOffset < -MAX_DITHER_TRAVEL) && (diff_y < 0)))
346 qCDebug(KSTARS_EKOS_GUIDE)
347 <<
QString(
"Dithering target off by too much in Y (abs(%1 + %2) > %3), adjust diff_y from %4 to %5")
348 .
arg(diff_y).
arg(totalYOffset).
arg(MAX_DITHER_TRAVEL).
arg(diff_y).
arg(diff_y * -1.5);
352 m_DitherTargetPosition = GuiderUtils::Vector(ret_x, ret_y, 0) + GuiderUtils::Vector(diff_x, diff_y, 0);
354 qCDebug(KSTARS_EKOS_GUIDE)
355 <<
QString(
"Dithering by %1 pixels. Target: %2,%3 Current: %4,%5 Move: %6,%7 Wander: %8,%9")
356 .
arg(pixels, 3,
'f', 1)
357 .
arg(m_DitherTargetPosition.x, 5,
'f', 1).
arg(m_DitherTargetPosition.y, 5,
'f', 1)
358 .
arg(ret_x, 5,
'f', 1).
arg(ret_y, 5,
'f', 1)
359 .
arg(diff_x, 4,
'f', 1).
arg(diff_y, 4,
'f', 1)
360 .
arg(totalXOffset + diff_x, 5,
'f', 1).
arg(totalYOffset + diff_y, 5,
'f', 1);
361 guideLog.ditherInfo(diff_x, diff_y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
363 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
365 if (Options::gPGEnabled())
367 pmath->getGPG().startDithering(diff_x, diff_y, pmath->getCalibration());
369 state = GUIDE_DITHERING;
370 emit newStatus(state);
378 double driftRA, driftDEC;
379 pmath->getCalibration().computeDrift(
381 GuiderUtils::Vector(m_DitherTargetPosition.x, m_DitherTargetPosition.y, 0),
382 &driftRA, &driftDEC);
384 double pixelOffsetX = m_DitherTargetPosition.x - star_position.x;
385 double pixelOffsetY = m_DitherTargetPosition.y - star_position.y;
387 qCDebug(KSTARS_EKOS_GUIDE)
388 <<
QString(
"Dithering in progress. Current: %1,%2 Target: %3,%4 Diff: %5,%6 Wander: %8,%9")
389 .
arg(star_position.x, 5,
'f', 1).
arg(star_position.y, 5,
'f', 1)
390 .
arg(m_DitherTargetPosition.x, 5,
'f', 1).
arg(m_DitherTargetPosition.y, 5,
'f', 1)
391 .
arg(pixelOffsetX, 4,
'f', 1).
arg(pixelOffsetY, 4,
'f', 1)
392 .
arg(star_position.x - m_DitherOrigin.
x(), 5,
'f', 1)
393 .
arg(star_position.y - m_DitherOrigin.
y(), 5,
'f', 1);
395 if (Options::ditherWithOnePulse() || (fabs(driftRA) < 1 && fabs(driftDEC) < 1))
397 pmath->setTargetPosition(star_position.x, star_position.y);
402 if (Options::ditherWithOnePulse())
403 m_isFirstFrame =
true;
405 qCDebug(KSTARS_EKOS_GUIDE) <<
"Dither complete.";
407 if (Options::ditherSettle() > 0)
409 state = GUIDE_DITHERING_SETTLE;
410 guideLog.settleStartedInfo();
411 emit newStatus(state);
414 if (Options::gPGEnabled())
415 pmath->getGPG().ditheringSettled(
true);
421 if (++m_DitherRetries > Options::ditherMaxIterations())
422 return abortDither();
429bool InternalGuider::onePulseDither(
double pixels)
431 qCDebug(KSTARS_EKOS_GUIDE) <<
"OnePulseDither(" <<
"pixels" <<
")";
434 emit abortExposure();
437 pmath->getTargetPosition(&ret_x, &ret_y);
439 auto seed = std::chrono::system_clock::now().time_since_epoch().count();
440 std::default_random_engine generator(seed);
441 std::uniform_real_distribution<double> angleMagnitude(0, 360);
444 double diff_x = pixels * cos(angle);
445 double diff_y = pixels * sin(angle);
447 if (pmath->getCalibration().declinationSwapEnabled())
450 if (m_DitherOrigin.
x() == 0 && m_DitherOrigin.
y() == 0)
452 m_DitherOrigin =
QVector3D(ret_x, ret_y, 0);
454 double totalXOffset = ret_x - m_DitherOrigin.
x();
455 double totalYOffset = ret_y - m_DitherOrigin.
y();
460 if (((diff_x + totalXOffset > MAX_DITHER_TRAVEL) && (diff_x > 0)) ||
461 ((diff_x + totalXOffset < -MAX_DITHER_TRAVEL) && (diff_x < 0)))
463 qCDebug(KSTARS_EKOS_GUIDE)
464 <<
QString(
"OPD: Dithering target off by too much in X (abs(%1 + %2) > %3), adjust diff_x from %4 to %5")
465 .
arg(diff_x).
arg(totalXOffset).
arg(MAX_DITHER_TRAVEL).
arg(diff_x).
arg(diff_x * -1.5);
468 if (((diff_y + totalYOffset > MAX_DITHER_TRAVEL) && (diff_y > 0)) ||
469 ((diff_y + totalYOffset < -MAX_DITHER_TRAVEL) && (diff_y < 0)))
471 qCDebug(KSTARS_EKOS_GUIDE)
472 <<
QString(
"OPD: Dithering target off by too much in Y (abs(%1 + %2) > %3), adjust diff_y from %4 to %5")
473 .
arg(diff_y).
arg(totalYOffset).
arg(MAX_DITHER_TRAVEL).
arg(diff_y).
arg(diff_y * -1.5);
477 m_DitherTargetPosition = GuiderUtils::Vector(ret_x, ret_y, 0) + GuiderUtils::Vector(diff_x, diff_y, 0);
479 qCDebug(KSTARS_EKOS_GUIDE)
480 <<
QString(
"OPD: Dithering by %1 pixels. Target: %2,%3 Current: %4,%5 Move: %6,%7 Wander: %8,%9")
481 .
arg(pixels, 3,
'f', 1)
482 .
arg(m_DitherTargetPosition.x, 5,
'f', 1).
arg(m_DitherTargetPosition.y, 5,
'f', 1)
483 .
arg(ret_x, 5,
'f', 1).
arg(ret_y, 5,
'f', 1)
484 .
arg(diff_x, 4,
'f', 1).
arg(diff_y, 4,
'f', 1)
485 .
arg(totalXOffset + diff_x, 5,
'f', 1).
arg(totalYOffset + diff_y, 5,
'f', 1);
486 guideLog.ditherInfo(diff_x, diff_y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
488 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
490 if (Options::gPGEnabled())
492 pmath->getGPG().startDithering(diff_x, diff_y, pmath->getCalibration());
494 state = GUIDE_DITHERING;
495 emit newStatus(state);
497 const GuiderUtils::Vector xyMove(diff_x, diff_y, 0);
498 const GuiderUtils::Vector raDecMove = pmath->getCalibration().rotateToRaDec(xyMove);
499 double raPulse = fabs(raDecMove.x * pmath->getCalibration().raPulseMillisecondsPerArcsecond());
500 double decPulse = fabs(raDecMove.y * pmath->getCalibration().decPulseMillisecondsPerArcsecond());
501 auto raDir = raDecMove.x > 0 ? RA_INC_DIR : RA_DEC_DIR;
502 auto decDir = raDecMove.y > 0 ? DEC_DEC_DIR : DEC_INC_DIR;
504 m_isFirstFrame =
true;
507 QString raDirString = raDir == RA_DEC_DIR ?
"RA_DEC" :
"RA_INC";
508 QString decDirString = decDir == DEC_INC_DIR ?
"DEC_INC" :
"DEC_DEC";
510 qCDebug(KSTARS_EKOS_GUIDE) <<
"OnePulseDither RA: " << raPulse <<
"ms" << raDirString <<
" DEC: " << decPulse <<
"ms " <<
514 emit newMultiPulse(raDir, raPulse, decDir, decPulse, DontCaptureAfterPulses);
516 double totalMSecs = 1000.0 * Options::ditherSettle() + std::max(raPulse, decPulse) + 100;
518 state = GUIDE_DITHERING_SETTLE;
519 guideLog.settleStartedInfo();
520 emit newStatus(state);
522 if (Options::gPGEnabled())
523 pmath->getGPG().ditheringSettled(
true);
529bool InternalGuider::abortDither()
531 if (Options::ditherFailAbortsAutoGuide())
533 emit newStatus(Ekos::GUIDE_DITHERING_ERROR);
539 emit newLog(
i18n(
"Warning: Dithering failed. Autoguiding shall continue as set in the options in case "
540 "of dither failure."));
542 if (Options::ditherSettle() > 0)
544 state = GUIDE_DITHERING_SETTLE;
545 guideLog.settleStartedInfo();
546 emit newStatus(state);
549 if (Options::gPGEnabled())
550 pmath->getGPG().ditheringSettled(
false);
557bool InternalGuider::processManualDithering()
560 pmath->getTargetPosition(&cur_x, &cur_y);
561 pmath->getStarScreenPosition(&cur_x, &cur_y);
564 double driftRA, driftDEC;
565 pmath->getCalibration().computeDrift(
566 GuiderUtils::Vector(cur_x, cur_y, 0),
567 GuiderUtils::Vector(m_DitherTargetPosition.x, m_DitherTargetPosition.y, 0),
568 &driftRA, &driftDEC);
570 qCDebug(KSTARS_EKOS_GUIDE) <<
"Manual Dithering in progress. Diff star X:" << driftRA <<
"Y:" << driftDEC;
572 if (fabs(driftRA) < guideBoxSize / 5.0 && fabs(driftDEC) < guideBoxSize / 5.0)
574 if (m_ProgressiveDither.empty() ==
false)
576 m_DitherTargetPosition = m_ProgressiveDither.
dequeue();
577 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
578 qCDebug(KSTARS_EKOS_GUIDE) <<
"Next Dither Jump X:" << m_DitherTargetPosition.x <<
"Jump Y:" << m_DitherTargetPosition.y;
586 if (fabs(driftRA) < 1 && fabs(driftDEC) < 1)
588 pmath->setTargetPosition(cur_x, cur_y);
589 qCDebug(KSTARS_EKOS_GUIDE) <<
"Manual Dither complete.";
591 if (Options::ditherSettle() > 0)
593 state = GUIDE_DITHERING_SETTLE;
594 guideLog.settleStartedInfo();
595 emit newStatus(state);
607 if (++m_DitherRetries > Options::ditherMaxIterations())
609 emit newLog(
i18n(
"Warning: Manual Dithering failed."));
611 if (Options::ditherSettle() > 0)
613 state = GUIDE_DITHERING_SETTLE;
614 guideLog.settleStartedInfo();
615 emit newStatus(state);
628void InternalGuider::setDitherSettled()
630 guideLog.settleCompletedInfo();
631 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
634 state = GUIDE_GUIDING;
637bool InternalGuider::calibrate()
639 bool ccdInfo =
true, scopeInfo =
true;
642 if (subW == 0 || subH == 0)
648 if (mountAperture == 0.0 || mountFocalLength == 0.0)
651 if (ccdInfo ==
false)
652 errMsg +=
" & Telescope";
654 errMsg +=
"Telescope";
657 if (ccdInfo ==
false || scopeInfo ==
false)
659 KSNotification::error(
i18n(
"%1 info are missing. Please set the values in INDI Control Panel.", errMsg),
660 i18n(
"Missing Information"));
664 if (state != GUIDE_CALIBRATING)
666 pmath->getTargetPosition(&calibrationStartX, &calibrationStartY);
667 calibrationProcess.reset(
668 new CalibrationProcess(calibrationStartX, calibrationStartY,
669 !Options::twoAxisEnabled()));
670 state = GUIDE_CALIBRATING;
671 emit newStatus(GUIDE_CALIBRATING);
674 if (calibrationProcess->inProgress())
676 iterateCalibration();
680 if (restoreCalibration())
682 calibrationProcess.reset();
683 emit newStatus(Ekos::GUIDE_CALIBRATION_SUCCESS);
685 i18n(
"Guiding calibration restored"), KSNotification::Guide);
692 pmath->getMutableCalibration()->setParameters(
693 ccdPixelSizeX / 1000.0, ccdPixelSizeY / 1000.0, mountFocalLength,
694 subBinX, subBinY, pierSide, mountRA, mountDEC);
696 calibrationProcess->useCalibration(pmath->getMutableCalibration());
698 m_GuideFrame->disconnect(
this);
701 emit DESwapChanged(
false);
702 pmath->setLostStar(
false);
704 if (Options::saveGuideLog())
706 GuideLog::GuideInfo info;
707 fillGuideInfo(&info);
708 guideLog.startCalibration(info);
710 calibrationProcess->startup();
711 calibrationProcess->setGuideLog(&guideLog);
712 iterateCalibration();
717void InternalGuider::iterateCalibration()
719 if (calibrationProcess->inProgress())
721 auto const timeStep = calculateGPGTimeStep();
722 pmath->performProcessing(GUIDE_CALIBRATING, m_ImageData, m_GuideFrame, timeStep);
725 if (pmath->usingSEPMultiStar())
727 auto gs = pmath->getGuideStars();
728 info =
QString(
"%1 stars, %2/%3 refs")
729 .
arg(gs.getNumStarsDetected())
730 .
arg(gs.getNumReferencesFound())
731 .
arg(gs.getNumReferences());
733 emit guideInfo(info);
735 if (pmath->isStarLost())
737 emit newLog(
i18n(
"Lost track of the guide star. Try increasing the square size or reducing pulse duration."));
738 emit newStatus(Ekos::GUIDE_CALIBRATION_ERROR);
739 emit calibrationUpdate(GuideInterface::CALIBRATION_MESSAGE_ONLY,
740 i18n(
"Guide Star lost."));
746 pmath->getStarScreenPosition(&starX, &starY);
747 calibrationProcess->iterate(starX, starY);
749 auto status = calibrationProcess->getStatus();
750 if (status != GUIDE_CALIBRATING)
751 emit newStatus(status);
753 QString logStatus = calibrationProcess->getLogStatus();
755 emit newLog(logStatus);
759 GuideInterface::CalibrationUpdateType
type;
760 calibrationProcess->getCalibrationUpdate(&type, &updateMessage, &x, &y);
761 if (updateMessage.
length())
762 emit calibrationUpdate(type, updateMessage, x, y);
764 GuideDirection pulseDirection;
766 calibrationProcess->getPulse(&pulseDirection, &pulseMsecs);
767 if (pulseDirection != NO_DIR)
768 emit newSinglePulse(pulseDirection, pulseMsecs, StartCaptureAfterPulses);
770 if (status == GUIDE_CALIBRATION_ERROR)
772 KSNotification::event(
QLatin1String(
"CalibrationFailed"),
i18n(
"Guiding calibration failed"),
773 KSNotification::Guide, KSNotification::Alert);
776 else if (status == GUIDE_CALIBRATION_SUCCESS)
778 KSNotification::event(
QLatin1String(
"CalibrationSuccessful"),
779 i18n(
"Guiding calibration completed successfully"), KSNotification::Guide);
780 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
781 pmath->setTargetPosition(calibrationStartX, calibrationStartY);
788 m_GuideFrame = guideView;
794 if (Options::saveGuideImages())
798 now.toString(
"yyyy-MM-dd"));
803 QString name =
"guide_frame_" + now.toString(
"HH-mm-ss") +
".fits";
805 m_ImageData->saveImage(filename);
809void InternalGuider::reset()
811 qCDebug(KSTARS_EKOS_GUIDE) <<
"Resetting internal guider...";
816 connect(m_GuideFrame.
get(), &FITSView::trackingStarSelected,
this, &InternalGuider::trackingStarSelected,
818 calibrationProcess.reset();
821bool InternalGuider::clearCalibration()
823 Options::setSerializedCalibration(
"");
824 pmath->getMutableCalibration()->reset();
828bool InternalGuider::restoreCalibration()
830 bool success = Options::reuseGuideCalibration() &&
831 pmath->getMutableCalibration()->restore(
832 pierSide, Options::reverseDecOnPierSideChange(),
833 subBinX, subBinY, &mountDEC);
835 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
839void InternalGuider::setStarPosition(
QVector3D &starCenter)
841 pmath->setTargetPosition(starCenter.
x(), starCenter.
y());
844void InternalGuider::trackingStarSelected(
int x,
int y)
862void InternalGuider::setDECSwap(
bool enable)
864 pmath->getMutableCalibration()->setDeclinationSwapEnabled(enable);
867void InternalGuider::setSquareAlgorithm(
int index)
869 if (index == SEP_MULTISTAR && !pmath->usingSEPMultiStar())
870 m_isFirstFrame =
true;
871 pmath->setAlgorithmIndex(index);
874bool InternalGuider::getReticleParameters(
double * x,
double * y)
876 return pmath->getTargetPosition(x, y);
879bool InternalGuider::setGuiderParams(
double ccdPixelSizeX,
double ccdPixelSizeY,
double mountAperture,
880 double mountFocalLength)
882 this->ccdPixelSizeX = ccdPixelSizeX;
883 this->ccdPixelSizeY = ccdPixelSizeY;
884 this->mountAperture = mountAperture;
885 this->mountFocalLength = mountFocalLength;
886 return pmath->setGuiderParameters(ccdPixelSizeX, ccdPixelSizeY, mountAperture, mountFocalLength);
889bool InternalGuider::setFrameParams(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t binX, uint16_t binY)
891 if (w <= 0 || h <= 0)
902 pmath->setVideoParameters(w, h, subBinX, subBinY);
907void InternalGuider::emitAxisPulse(
const cproc_out_params * out)
909 double raPulse = out->pulse_length[GUIDE_RA];
910 double dePulse = out->pulse_length[GUIDE_DEC];
913 if(out->pulse_dir[GUIDE_RA] == NO_DIR)
916 if(out->pulse_dir[GUIDE_DEC] == NO_DIR)
919 if(out->pulse_dir[GUIDE_RA] == RA_INC_DIR)
922 if(out->pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
925 emit newAxisPulse(raPulse, dePulse);
928bool InternalGuider::processGuiding()
930 const cproc_out_params *out;
938 m_isFirstFrame =
false;
939 if (state == GUIDE_GUIDING)
941 GuiderUtils::Vector star_pos = pmath->findLocalStarPosition(m_ImageData, m_GuideFrame,
true);
942 if (star_pos.x != -1 && star_pos.y != -1)
943 pmath->setTargetPosition(star_pos.x, star_pos.y);
948 m_isFirstFrame =
true;
955 auto const timeStep = calculateGPGTimeStep();
956 pmath->performProcessing(state, m_ImageData, m_GuideFrame, timeStep, &guideLog);
957 if (pmath->usingSEPMultiStar())
960 auto gs = pmath->getGuideStars();
961 info =
QString(
"%1 stars, %2/%3 refs")
962 .
arg(gs.getNumStarsDetected())
963 .
arg(gs.getNumReferencesFound())
964 .
arg(gs.getNumReferences());
966 emit guideInfo(info);
970 if (this->m_darkGuideTimer->isActive())
971 this->m_darkGuideTimer->start();
974 if (state == GUIDE_SUSPENDED)
976 if (Options::gPGEnabled())
977 emit frameCaptureRequested();
982 if (pmath->isStarLost())
985 m_starLostCounter = 0;
989 out = pmath->getOutputParameters();
991 if (isPoorGuiding(out))
996 if ((state == GUIDE_DITHERING_SETTLE || state == GUIDE_DITHERING) && Options::ditherWithOnePulse())
999 bool sendPulses = !pmath->isStarLost();
1002 if (sendPulses && (out->pulse_dir[GUIDE_RA] != NO_DIR || out->pulse_dir[GUIDE_DEC] != NO_DIR))
1004 emit newMultiPulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA],
1005 out->pulse_dir[GUIDE_DEC], out->pulse_length[GUIDE_DEC], StartCaptureAfterPulses);
1008 emit frameCaptureRequested();
1010 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING || state == GUIDE_DITHERING_SETTLE)
1018 if (state < GUIDE_DITHERING)
1019 emit newAxisDelta(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
1022 emit newAxisSigma(out->sigma[GUIDE_RA], out->sigma[GUIDE_DEC]);
1023 if (SEPMultiStarEnabled())
1024 emit newSNR(pmath->getGuideStarSNR());
1031std::pair<Seconds, Seconds> InternalGuider::calculateGPGTimeStep()
1035 const Seconds guideDelay{(Options::guideDelay())};
1037 auto const captureInterval = Seconds(m_captureTimer->intervalAsDuration()) + guideDelay;
1038 auto const darkGuideInterval = Seconds(m_darkGuideTimer->intervalAsDuration());
1040 if (!Options::gPGDarkGuiding() || !isInferencePeriodFinished())
1042 return std::pair<Seconds, Seconds>(captureInterval, captureInterval);
1044 auto const captureTimeRemaining = Seconds(m_captureTimer->remainingTimeAsDuration()) + guideDelay;
1045 auto const darkGuideTimeRemaining = Seconds(m_darkGuideTimer->remainingTimeAsDuration());
1047 if (captureTimeRemaining <= Seconds::zero()
1048 && darkGuideTimeRemaining <= Seconds::zero())
1050 timeStep = std::min(captureInterval, darkGuideInterval);
1052 else if (captureTimeRemaining <= Seconds::zero())
1054 timeStep = std::min(captureInterval, darkGuideTimeRemaining);
1056 else if (darkGuideTimeRemaining <= Seconds::zero())
1058 timeStep = std::min(captureTimeRemaining, darkGuideInterval);
1062 timeStep = std::min(captureTimeRemaining, darkGuideTimeRemaining);
1064 return std::pair<Seconds, Seconds>(timeStep, captureInterval);
1069void InternalGuider::darkGuide()
1072 if (state != GUIDE_GUIDING)
1075 if(Options::gPGDarkGuiding() && isInferencePeriodFinished())
1077 const cproc_out_params *out;
1078 auto const timeStep = calculateGPGTimeStep();
1079 pmath->performDarkGuiding(state, timeStep);
1081 out = pmath->getOutputParameters();
1082 emit newSinglePulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA], DontCaptureAfterPulses);
1088bool InternalGuider::isPoorGuiding(
const cproc_out_params * out)
1090 double delta_rms = std::hypot(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
1091 if (delta_rms > Options::guideMaxDeltaRMS())
1094 m_highRMSCounter = 0;
1096 uint8_t abortStarLostThreshold = (state == GUIDE_DITHERING
1097 || state == GUIDE_MANUAL_DITHERING) ? MAX_LOST_STAR_THRESHOLD * 3 : MAX_LOST_STAR_THRESHOLD;
1098 uint8_t abortRMSThreshold = (state == GUIDE_DITHERING
1099 || state == GUIDE_MANUAL_DITHERING) ? MAX_RMS_THRESHOLD * 3 : MAX_RMS_THRESHOLD;
1100 if (m_starLostCounter > abortStarLostThreshold || m_highRMSCounter > abortRMSThreshold)
1102 qCDebug(KSTARS_EKOS_GUIDE) <<
"m_starLostCounter" << m_starLostCounter
1103 <<
"m_highRMSCounter" << m_highRMSCounter
1104 <<
"delta_rms" << delta_rms;
1106 if (m_starLostCounter > abortStarLostThreshold)
1107 emit newLog(
i18n(
"Lost track of the guide star. Searching for guide stars..."));
1109 emit newLog(
i18n(
"Delta RMS threshold value exceeded. Searching for guide stars..."));
1111 reacquireTimer.
start();
1112 rememberState = state;
1113 state = GUIDE_REACQUIRE;
1114 emit newStatus(state);
1119bool InternalGuider::selectAutoStarSEPMultistar()
1121 m_GuideFrame->updateFrame();
1123 QVector3D newStarCenter = pmath->selectGuideStar(m_ImageData);
1124 if (newStarCenter.
x() >= 0)
1126 emit newStarPosition(newStarCenter,
true);
1132bool InternalGuider::SEPMultiStarEnabled()
1134 return Options::guideAlgorithm() == SEP_MULTISTAR;
1137bool InternalGuider::selectAutoStar()
1140 if (Options::guideAlgorithm() == SEP_MULTISTAR)
1141 return selectAutoStarSEPMultistar();
1143 bool useNativeDetection =
false;
1147 if (Options::guideAlgorithm() != SEP_THRESHOLD)
1148 starCenters = GuideAlgorithms::detectStars(m_ImageData, m_GuideFrame->getTrackingBox());
1150 if (starCenters.
empty())
1152 QVariantMap settings;
1153 settings[
"maxStarsCount"] = 50;
1154 settings[
"optionsProfileIndex"] = Options::guideOptionsProfile();
1155 settings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::GuideProfiles);
1156 m_ImageData->setSourceExtractorSettings(settings);
1158 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1159 m_ImageData->findStars(ALGORITHM_SEP).waitForFinished();
1161 m_ImageData->findStars().waitForFinished();
1163 starCenters = m_ImageData->getStarCenters();
1164 if (starCenters.
empty())
1167 useNativeDetection =
true;
1169 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1170 std::sort(starCenters.
begin(), starCenters.
end(), [](
const Edge * a,
const Edge * b)
1172 return a->val > b->val;
1175 std::sort(starCenters.
begin(), starCenters.
end(), [](
const Edge * a,
const Edge * b)
1177 return a->width > b->width;
1180 m_GuideFrame->setStarsEnabled(
true);
1181 m_GuideFrame->updateFrame();
1184 int maxX = m_ImageData->width();
1185 int maxY = m_ImageData->height();
1187 int scores[MAX_GUIDE_STARS];
1189 int maxIndex = MAX_GUIDE_STARS < starCenters.
count() ? MAX_GUIDE_STARS : starCenters.
count();
1191 for (
int i = 0; i < maxIndex; i++)
1197 if (useNativeDetection)
1205 if (
center->width >
float(guideBoxSize) / subBinX)
1209 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1210 score += sqrt(
center->val);
1217 foreach (Edge *edge, starCenters)
1238 int maxScoreIndex = -1;
1239 for (
int i = 0; i < maxIndex; i++)
1241 if (scores[i] > maxScore)
1243 maxScore = scores[i];
1248 if (maxScoreIndex < 0)
1250 qCDebug(KSTARS_EKOS_GUIDE) <<
"No suitable star detected.";
1254 QVector3D newStarCenter(starCenters[maxScoreIndex]->x, starCenters[maxScoreIndex]->y, 0);
1256 if (useNativeDetection ==
false)
1257 qDeleteAll(starCenters);
1259 emit newStarPosition(newStarCenter,
true);
1264bool InternalGuider::reacquire()
1266 bool rc = selectAutoStar();
1269 m_highRMSCounter = m_starLostCounter = 0;
1270 m_isFirstFrame =
true;
1273 if (rememberState == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
1275 if (Options::ditherSettle() > 0)
1277 state = GUIDE_DITHERING_SETTLE;
1278 guideLog.settleStartedInfo();
1279 emit newStatus(state);
1286 state = GUIDE_GUIDING;
1287 emit newStatus(state);
1291 else if (reacquireTimer.
elapsed() >
static_cast<int>(Options::guideLostStarTimeout() * 1000))
1293 emit newLog(
i18n(
"Failed to find any suitable guide stars. Aborting..."));
1298 emit frameCaptureRequested();
1302void InternalGuider::fillGuideInfo(GuideLog::GuideInfo * info)
1307 info->pixelScale = (206.26481 * this->ccdPixelSizeX * this->subBinX) / this->mountFocalLength;
1308 info->binning = this->subBinX;
1309 info->focalLength = this->mountFocalLength;
1310 info->ra = this->mountRA.Degrees();
1311 info->dec = this->mountDEC.Degrees();
1312 info->azimuth = this->mountAzimuth.Degrees();
1313 info->altitude = this->mountAltitude.Degrees();
1314 info->pierSide = this->pierSide;
1315 info->xangle = pmath->getCalibration().getRAAngle();
1316 info->yangle = pmath->getCalibration().getDECAngle();
1318 info->xrate = 1000.0 / pmath->getCalibration().raPulseMillisecondsPerArcsecond();
1319 info->yrate = 1000.0 / pmath->getCalibration().decPulseMillisecondsPerArcsecond();
1322void InternalGuider::updateGPGParameters()
1324 setDarkGuideTimerInterval();
1325 pmath->getGPG().updateParameters();
1328void InternalGuider::resetGPG()
1330 pmath->getGPG().reset();
1334const Calibration &InternalGuider::getCalibration()
const
1336 return pmath->getCalibration();
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
Ekos is an advanced Astrophotography tool for Linux.
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString name(StandardAction id)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QDateTime currentDateTime()
QString filePath(const QString &fileName) const const
qint64 elapsed() const const
virtual void close() override
const_reference at(qsizetype i) const const
qsizetype count() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QString arg(Args &&... args) const const
qsizetype length() const const
QTextStream & center(QTextStream &stream)