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);
44 m_DitherOrigin = QVector3D(0, 0, 0);
48 m_darkGuideTimer = std::make_unique<QTimer>(
this);
49 m_captureTimer = std::make_unique<QTimer>(
this);
51 setDarkGuideTimerInterval();
55 connect(
this, &Ekos::GuideInterface::frameCaptureRequested,
this, [ = ]()
57 this->m_captureTimer->start();
61void InternalGuider::setExposureTime()
63 Seconds seconds(Options::guideExposure());
64 setTimer(m_captureTimer, seconds);
67void InternalGuider::setTimer(std::unique_ptr<QTimer> &timer, Seconds seconds)
69 const std::chrono::duration<double, std::milli> inMilliseconds(seconds);
70 timer->setInterval((
int)(inMilliseconds.count()));
73void InternalGuider::setDarkGuideTimerInterval()
75 constexpr double kMinInterval = 0.5;
76 const Seconds seconds(std::max(kMinInterval, Options::gPGDarkGuidingInterval()));
77 setTimer(m_darkGuideTimer, seconds);
80void InternalGuider::resetDarkGuiding()
82 m_darkGuideTimer->stop();
83 m_captureTimer->stop();
86bool InternalGuider::isInferencePeriodFinished()
88 auto const contribution = pmath->getGPG().predictionContribution();
89 return contribution >= 0.99;
91bool InternalGuider::guide()
93 if (state >= GUIDE_GUIDING)
95 return processGuiding();
98 if (state == GUIDE_SUSPENDED)
102 m_GuideFrame->disconnect(
this);
107 m_starLostCounter = 0;
108 m_highRMSCounter = 0;
109 m_DitherOrigin = QVector3D(0, 0, 0);
111 m_isFirstFrame =
true;
113 if (state == GUIDE_IDLE)
115 if (Options::saveGuideLog())
117 GuideLog::GuideInfo info;
118 fillGuideInfo(&info);
119 guideLog.startGuiding(info);
121 state = GUIDE_GUIDING;
123 emit newStatus(state);
125 emit frameCaptureRequested();
139bool InternalGuider::abort()
144 guideLog.endGuiding();
147 if (state == GUIDE_CALIBRATING ||
148 state == GUIDE_GUIDING ||
149 state == GUIDE_DITHERING ||
150 state == GUIDE_MANUAL_DITHERING ||
151 state == GUIDE_REACQUIRE)
153 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
154 emit newStatus(GUIDE_DITHERING_ERROR);
155 emit newStatus(GUIDE_ABORTED);
157 qCDebug(KSTARS_EKOS_GUIDE) <<
"Aborting" << getGuideStatusString(state);
161 emit newStatus(GUIDE_IDLE);
162 qCDebug(KSTARS_EKOS_GUIDE) <<
"Stopping internal guider.";
166 disconnect(m_darkGuideTimer.get(),
nullptr,
nullptr,
nullptr);
172 m_ProgressiveDither.clear();
173 m_starLostCounter = 0;
174 m_highRMSCounter = 0;
176 m_DitherOrigin = QVector3D(0, 0, 0);
178 pmath->suspend(
false);
180 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding aborted.";
185bool InternalGuider::suspend()
187 guideLog.pauseInfo();
188 state = GUIDE_SUSPENDED;
191 emit newStatus(state);
193 pmath->suspend(
true);
199void InternalGuider::startDarkGuiding()
201 if (Options::gPGDarkGuiding())
206 m_darkGuideTimer->start();
208 qCDebug(KSTARS_EKOS_GUIDE) <<
"Starting dark guiding.";
212bool InternalGuider::resume()
214 qCDebug(KSTARS_EKOS_GUIDE) <<
"Resuming...";
216 guideLog.resumeInfo();
217 state = GUIDE_GUIDING;
218 emit newStatus(state);
220 pmath->suspend(
false);
226 emit frameCaptureRequested();
231bool InternalGuider::ditherXY(
double x,
double y)
233 m_ProgressiveDither.clear();
236 pmath->getTargetPosition(&cur_x, &cur_y);
241 double oneJump = (guideBoxSize / 4.0);
242 double targetX = cur_x, targetY = cur_y;
243 int xSign = (x >= cur_x) ? 1 : -1;
244 int ySign = (y >= cur_y) ? 1 : -1;
248 if (fabs(targetX - x) > oneJump)
249 targetX += oneJump * xSign;
250 else if (fabs(targetX - x) < oneJump)
253 if (fabs(targetY - y) > oneJump)
254 targetY += oneJump * ySign;
255 else if (fabs(targetY - y) < oneJump)
258 m_ProgressiveDither.enqueue(GuiderUtils::Vector(targetX, targetY, -1));
261 while (targetX != x || targetY != y);
263 m_DitherTargetPosition = m_ProgressiveDither.dequeue();
264 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
265 guideLog.ditherInfo(x, y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
267 state = GUIDE_MANUAL_DITHERING;
268 emit newStatus(state);
275bool InternalGuider::dither(
double pixels)
277 if (Options::ditherWithOnePulse() )
278 return onePulseDither(pixels);
281 pmath->getTargetPosition(&ret_x, &ret_y);
288 GuiderUtils::Vector star_position = pmath->findLocalStarPosition(m_ImageData, m_GuideFrame,
false);
289 if (pmath->isStarLost() || (star_position.x == -1) || (star_position.y == -1))
293 if (++m_starLostCounter > MAX_LOST_STAR_THRESHOLD)
295 qCDebug(KSTARS_EKOS_GUIDE) <<
"Too many consecutive lost stars." << m_starLostCounter <<
"Aborting dither.";
296 return abortDither();
298 qCDebug(KSTARS_EKOS_GUIDE) <<
"Dither lost star. Trying again.";
299 emit frameCaptureRequested();
303 m_starLostCounter = 0;
305 if (state != GUIDE_DITHERING)
309 auto seed = std::chrono::system_clock::now().time_since_epoch().count();
310 std::default_random_engine generator(seed);
311 std::uniform_real_distribution<double> angleMagnitude(0, 360);
314 double diff_x = pixels * cos(angle);
315 double diff_y = pixels * sin(angle);
317 if (pmath->getCalibration().declinationSwapEnabled())
320 if (m_DitherOrigin.x() == 0 && m_DitherOrigin.y() == 0)
322 m_DitherOrigin = QVector3D(ret_x, ret_y, 0);
324 double totalXOffset = ret_x - m_DitherOrigin.x();
325 double totalYOffset = ret_y - m_DitherOrigin.y();
330 if (((diff_x + totalXOffset > MAX_DITHER_TRAVEL) && (diff_x > 0)) ||
331 ((diff_x + totalXOffset < -MAX_DITHER_TRAVEL) && (diff_x < 0)))
333 qCDebug(KSTARS_EKOS_GUIDE)
334 << QString(
"Dithering target off by too much in X (abs(%1 + %2) > %3), adjust diff_x from %4 to %5")
335 .arg(diff_x).arg(totalXOffset).arg(MAX_DITHER_TRAVEL).arg(diff_x).arg(diff_x * -1.5);
338 if (((diff_y + totalYOffset > MAX_DITHER_TRAVEL) && (diff_y > 0)) ||
339 ((diff_y + totalYOffset < -MAX_DITHER_TRAVEL) && (diff_y < 0)))
341 qCDebug(KSTARS_EKOS_GUIDE)
342 << QString(
"Dithering target off by too much in Y (abs(%1 + %2) > %3), adjust diff_y from %4 to %5")
343 .arg(diff_y).arg(totalYOffset).arg(MAX_DITHER_TRAVEL).arg(diff_y).arg(diff_y * -1.5);
347 m_DitherTargetPosition = GuiderUtils::Vector(ret_x, ret_y, 0) + GuiderUtils::Vector(diff_x, diff_y, 0);
349 qCDebug(KSTARS_EKOS_GUIDE)
350 << QString(
"Dithering by %1 pixels. Target: %2,%3 Current: %4,%5 Move: %6,%7 Wander: %8,%9")
351 .arg(pixels, 3,
'f', 1)
352 .arg(m_DitherTargetPosition.x, 5,
'f', 1).arg(m_DitherTargetPosition.y, 5,
'f', 1)
353 .arg(ret_x, 5,
'f', 1).arg(ret_y, 5,
'f', 1)
354 .arg(diff_x, 4,
'f', 1).arg(diff_y, 4,
'f', 1)
355 .arg(totalXOffset + diff_x, 5,
'f', 1).arg(totalYOffset + diff_y, 5,
'f', 1);
356 guideLog.ditherInfo(diff_x, diff_y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
358 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
360 if (Options::gPGEnabled())
362 pmath->getGPG().startDithering(diff_x, diff_y, pmath->getCalibration());
364 state = GUIDE_DITHERING;
365 emit newStatus(state);
373 double driftRA, driftDEC;
374 pmath->getCalibration().computeDrift(
376 GuiderUtils::Vector(m_DitherTargetPosition.x, m_DitherTargetPosition.y, 0),
377 &driftRA, &driftDEC);
379 double pixelOffsetX = m_DitherTargetPosition.x - star_position.x;
380 double pixelOffsetY = m_DitherTargetPosition.y - star_position.y;
382 qCDebug(KSTARS_EKOS_GUIDE)
383 << QString(
"Dithering in progress. Current: %1,%2 Target: %3,%4 Diff: %5,%6 Wander: %8,%9")
384 .arg(star_position.x, 5,
'f', 1).arg(star_position.y, 5,
'f', 1)
385 .arg(m_DitherTargetPosition.x, 5,
'f', 1).arg(m_DitherTargetPosition.y, 5,
'f', 1)
386 .arg(pixelOffsetX, 4,
'f', 1).arg(pixelOffsetY, 4,
'f', 1)
387 .arg(star_position.x - m_DitherOrigin.x(), 5,
'f', 1)
388 .arg(star_position.y - m_DitherOrigin.y(), 5,
'f', 1);
390 if (Options::ditherWithOnePulse() || (fabs(driftRA) < 1 && fabs(driftDEC) < 1))
392 pmath->setTargetPosition(star_position.x, star_position.y);
397 if (Options::ditherWithOnePulse())
398 m_isFirstFrame =
true;
400 qCDebug(KSTARS_EKOS_GUIDE) <<
"Dither complete.";
402 if (Options::ditherSettle() > 0)
404 state = GUIDE_DITHERING_SETTLE;
405 guideLog.settleStartedInfo();
406 emit newStatus(state);
409 if (Options::gPGEnabled())
410 pmath->getGPG().ditheringSettled(
true);
416 if (++m_DitherRetries > Options::ditherMaxIterations())
417 return abortDither();
424bool InternalGuider::onePulseDither(
double pixels)
426 qCDebug(KSTARS_EKOS_GUIDE) <<
"OnePulseDither(" <<
"pixels" <<
")";
429 emit abortExposure();
432 pmath->getTargetPosition(&ret_x, &ret_y);
434 auto seed = std::chrono::system_clock::now().time_since_epoch().count();
435 std::default_random_engine generator(seed);
436 std::uniform_real_distribution<double> angleMagnitude(0, 360);
439 double diff_x = pixels * cos(angle);
440 double diff_y = pixels * sin(angle);
442 if (pmath->getCalibration().declinationSwapEnabled())
445 if (m_DitherOrigin.x() == 0 && m_DitherOrigin.y() == 0)
447 m_DitherOrigin = QVector3D(ret_x, ret_y, 0);
449 double totalXOffset = ret_x - m_DitherOrigin.x();
450 double totalYOffset = ret_y - m_DitherOrigin.y();
455 if (((diff_x + totalXOffset > MAX_DITHER_TRAVEL) && (diff_x > 0)) ||
456 ((diff_x + totalXOffset < -MAX_DITHER_TRAVEL) && (diff_x < 0)))
458 qCDebug(KSTARS_EKOS_GUIDE)
459 << QString(
"OPD: Dithering target off by too much in X (abs(%1 + %2) > %3), adjust diff_x from %4 to %5")
460 .arg(diff_x).arg(totalXOffset).arg(MAX_DITHER_TRAVEL).arg(diff_x).arg(diff_x * -1.5);
463 if (((diff_y + totalYOffset > MAX_DITHER_TRAVEL) && (diff_y > 0)) ||
464 ((diff_y + totalYOffset < -MAX_DITHER_TRAVEL) && (diff_y < 0)))
466 qCDebug(KSTARS_EKOS_GUIDE)
467 << QString(
"OPD: Dithering target off by too much in Y (abs(%1 + %2) > %3), adjust diff_y from %4 to %5")
468 .arg(diff_y).arg(totalYOffset).arg(MAX_DITHER_TRAVEL).arg(diff_y).arg(diff_y * -1.5);
472 m_DitherTargetPosition = GuiderUtils::Vector(ret_x, ret_y, 0) + GuiderUtils::Vector(diff_x, diff_y, 0);
474 qCDebug(KSTARS_EKOS_GUIDE)
475 << QString(
"OPD: Dithering by %1 pixels. Target: %2,%3 Current: %4,%5 Move: %6,%7 Wander: %8,%9")
476 .arg(pixels, 3,
'f', 1)
477 .arg(m_DitherTargetPosition.x, 5,
'f', 1).arg(m_DitherTargetPosition.y, 5,
'f', 1)
478 .arg(ret_x, 5,
'f', 1).arg(ret_y, 5,
'f', 1)
479 .arg(diff_x, 4,
'f', 1).arg(diff_y, 4,
'f', 1)
480 .arg(totalXOffset + diff_x, 5,
'f', 1).arg(totalYOffset + diff_y, 5,
'f', 1);
481 guideLog.ditherInfo(diff_x, diff_y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
483 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
485 if (Options::gPGEnabled())
487 pmath->getGPG().startDithering(diff_x, diff_y, pmath->getCalibration());
489 state = GUIDE_DITHERING;
490 emit newStatus(state);
492 const GuiderUtils::Vector xyMove(diff_x, diff_y, 0);
493 const GuiderUtils::Vector raDecMove = pmath->getCalibration().rotateToRaDec(xyMove);
494 double raPulse = fabs(raDecMove.x * pmath->getCalibration().raPulseMillisecondsPerArcsecond());
495 double decPulse = fabs(raDecMove.y * pmath->getCalibration().decPulseMillisecondsPerArcsecond());
496 auto raDir = raDecMove.x > 0 ? RA_INC_DIR : RA_DEC_DIR;
497 auto decDir = raDecMove.y > 0 ? DEC_DEC_DIR : DEC_INC_DIR;
499 m_isFirstFrame =
true;
502 QString raDirString = raDir == RA_DEC_DIR ?
"RA_DEC" :
"RA_INC";
503 QString decDirString = decDir == DEC_INC_DIR ?
"DEC_INC" :
"DEC_DEC";
505 qCDebug(KSTARS_EKOS_GUIDE) <<
"OnePulseDither RA: " << raPulse <<
"ms" << raDirString <<
" DEC: " << decPulse <<
"ms " <<
509 emit newMultiPulse(raDir, raPulse, decDir, decPulse, DontCaptureAfterPulses);
511 double totalMSecs = 1000.0 * Options::ditherSettle() + std::max(raPulse, decPulse) + 100;
513 state = GUIDE_DITHERING_SETTLE;
514 guideLog.settleStartedInfo();
515 emit newStatus(state);
517 if (Options::gPGEnabled())
518 pmath->getGPG().ditheringSettled(
true);
524bool InternalGuider::abortDither()
526 if (Options::ditherFailAbortsAutoGuide())
528 emit newStatus(Ekos::GUIDE_DITHERING_ERROR);
534 emit newLog(
i18n(
"Warning: Dithering failed. Autoguiding shall continue as set in the options in case "
535 "of dither failure."));
537 if (Options::ditherSettle() > 0)
539 state = GUIDE_DITHERING_SETTLE;
540 guideLog.settleStartedInfo();
541 emit newStatus(state);
544 if (Options::gPGEnabled())
545 pmath->getGPG().ditheringSettled(
false);
552bool InternalGuider::processManualDithering()
555 pmath->getTargetPosition(&cur_x, &cur_y);
556 pmath->getStarScreenPosition(&cur_x, &cur_y);
559 double driftRA, driftDEC;
560 pmath->getCalibration().computeDrift(
561 GuiderUtils::Vector(cur_x, cur_y, 0),
562 GuiderUtils::Vector(m_DitherTargetPosition.x, m_DitherTargetPosition.y, 0),
563 &driftRA, &driftDEC);
565 qCDebug(KSTARS_EKOS_GUIDE) <<
"Manual Dithering in progress. Diff star X:" << driftRA <<
"Y:" << driftDEC;
567 if (fabs(driftRA) < guideBoxSize / 5.0 && fabs(driftDEC) < guideBoxSize / 5.0)
569 if (m_ProgressiveDither.empty() ==
false)
571 m_DitherTargetPosition = m_ProgressiveDither.dequeue();
572 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
573 qCDebug(KSTARS_EKOS_GUIDE) <<
"Next Dither Jump X:" << m_DitherTargetPosition.x <<
"Jump Y:" << m_DitherTargetPosition.y;
581 if (fabs(driftRA) < 1 && fabs(driftDEC) < 1)
583 pmath->setTargetPosition(cur_x, cur_y);
584 qCDebug(KSTARS_EKOS_GUIDE) <<
"Manual Dither complete.";
586 if (Options::ditherSettle() > 0)
588 state = GUIDE_DITHERING_SETTLE;
589 guideLog.settleStartedInfo();
590 emit newStatus(state);
602 if (++m_DitherRetries > Options::ditherMaxIterations())
604 emit newLog(
i18n(
"Warning: Manual Dithering failed."));
606 if (Options::ditherSettle() > 0)
608 state = GUIDE_DITHERING_SETTLE;
609 guideLog.settleStartedInfo();
610 emit newStatus(state);
623void InternalGuider::setDitherSettled()
625 guideLog.settleCompletedInfo();
626 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
629 state = GUIDE_GUIDING;
632bool InternalGuider::calibrate()
634 bool ccdInfo =
true, scopeInfo =
true;
637 if (subW == 0 || subH == 0)
643 if (mountAperture == 0.0 || mountFocalLength == 0.0)
646 if (ccdInfo ==
false)
647 errMsg +=
" & Telescope";
649 errMsg +=
"Telescope";
652 if (ccdInfo ==
false || scopeInfo ==
false)
654 KSNotification::error(
i18n(
"%1 info are missing. Please set the values in INDI Control Panel.", errMsg),
655 i18n(
"Missing Information"));
659 if (state != GUIDE_CALIBRATING)
661 pmath->getTargetPosition(&calibrationStartX, &calibrationStartY);
662 calibrationProcess.reset(
663 new CalibrationProcess(calibrationStartX, calibrationStartY,
664 !Options::twoAxisEnabled()));
665 state = GUIDE_CALIBRATING;
666 emit newStatus(GUIDE_CALIBRATING);
669 if (calibrationProcess->inProgress())
671 iterateCalibration();
675 if (restoreCalibration())
677 calibrationProcess.reset();
678 emit newStatus(Ekos::GUIDE_CALIBRATION_SUCCESS);
679 KSNotification::event(QLatin1String(
"CalibrationRestored"),
680 i18n(
"Guiding calibration restored"), KSNotification::Guide);
687 pmath->getMutableCalibration()->setParameters(
688 ccdPixelSizeX / 1000.0, ccdPixelSizeY / 1000.0, mountFocalLength,
689 subBinX, subBinY, pierSide, mountRA, mountDEC);
691 calibrationProcess->useCalibration(pmath->getMutableCalibration());
693 m_GuideFrame->disconnect(
this);
696 emit DESwapChanged(
false);
697 pmath->setLostStar(
false);
699 if (Options::saveGuideLog())
701 GuideLog::GuideInfo info;
702 fillGuideInfo(&info);
703 guideLog.startCalibration(info);
705 calibrationProcess->startup();
706 calibrationProcess->setGuideLog(&guideLog);
707 iterateCalibration();
712void InternalGuider::iterateCalibration()
714 if (calibrationProcess->inProgress())
716 auto const timeStep = calculateGPGTimeStep();
717 pmath->performProcessing(GUIDE_CALIBRATING, m_ImageData, m_GuideFrame, timeStep);
720 if (pmath->usingSEPMultiStar())
722 auto gs = pmath->getGuideStars();
723 info = QString(
"%1 stars, %2/%3 refs")
724 .arg(gs.getNumStarsDetected())
725 .arg(gs.getNumReferencesFound())
726 .arg(gs.getNumReferences());
728 emit guideInfo(info);
730 if (pmath->isStarLost())
732 emit newLog(
i18n(
"Lost track of the guide star. Try increasing the square size or reducing pulse duration."));
733 emit newStatus(Ekos::GUIDE_CALIBRATION_ERROR);
734 emit calibrationUpdate(GuideInterface::CALIBRATION_MESSAGE_ONLY,
735 i18n(
"Guide Star lost."));
741 pmath->getStarScreenPosition(&starX, &starY);
742 calibrationProcess->iterate(starX, starY);
744 auto status = calibrationProcess->getStatus();
745 if (status != GUIDE_CALIBRATING)
746 emit newStatus(status);
748 QString logStatus = calibrationProcess->getLogStatus();
750 emit newLog(logStatus);
752 QString updateMessage;
754 GuideInterface::CalibrationUpdateType
type;
755 calibrationProcess->getCalibrationUpdate(&type, &updateMessage, &x, &y);
756 if (updateMessage.
length())
757 emit calibrationUpdate(type, updateMessage, x, y);
759 GuideDirection pulseDirection;
761 calibrationProcess->getPulse(&pulseDirection, &pulseMsecs);
762 if (pulseDirection != NO_DIR)
763 emit newSinglePulse(pulseDirection, pulseMsecs, StartCaptureAfterPulses);
765 if (status == GUIDE_CALIBRATION_ERROR)
767 KSNotification::event(QLatin1String(
"CalibrationFailed"),
i18n(
"Guiding calibration failed"),
768 KSNotification::Guide, KSNotification::Alert);
771 else if (status == GUIDE_CALIBRATION_SUCCESS)
773 KSNotification::event(QLatin1String(
"CalibrationSuccessful"),
774 i18n(
"Guiding calibration completed successfully"), KSNotification::Guide);
775 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
776 pmath->setTargetPosition(calibrationStartX, calibrationStartY);
781void InternalGuider::setGuideView(
const QSharedPointer<GuideView> &guideView)
783 m_GuideFrame = guideView;
786void InternalGuider::setImageData(
const QSharedPointer<FITSData> &data)
789 if (Options::saveGuideImages())
793 now.toString(
"yyyy-MM-dd"));
798 QString
name =
"guide_frame_" + now.toString(
"HH-mm-ss") +
".fits";
799 QString filename =
path + QStringLiteral(
"/") +
name;
800 m_ImageData->saveImage(filename);
804void InternalGuider::reset()
806 qCDebug(KSTARS_EKOS_GUIDE) <<
"Resetting internal guider...";
811 connect(m_GuideFrame.get(), &FITSView::trackingStarSelected,
this, &InternalGuider::trackingStarSelected,
813 calibrationProcess.reset();
816bool InternalGuider::clearCalibration()
818 Options::setSerializedCalibration(
"");
819 pmath->getMutableCalibration()->reset();
823bool InternalGuider::restoreCalibration()
825 bool success = Options::reuseGuideCalibration() &&
826 pmath->getMutableCalibration()->restore(
827 pierSide, Options::reverseDecOnPierSideChange(),
828 subBinX, subBinY, &mountDEC);
830 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
834void InternalGuider::setStarPosition(QVector3D &starCenter)
836 pmath->setTargetPosition(starCenter.
x(), starCenter.
y());
839void InternalGuider::trackingStarSelected(
int x,
int y)
857void InternalGuider::setDECSwap(
bool enable)
859 pmath->getMutableCalibration()->setDeclinationSwapEnabled(enable);
862void InternalGuider::setSquareAlgorithm(
int index)
864 if (index == SEP_MULTISTAR && !pmath->usingSEPMultiStar())
865 m_isFirstFrame =
true;
866 pmath->setAlgorithmIndex(index);
869bool InternalGuider::getReticleParameters(
double * x,
double * y)
871 return pmath->getTargetPosition(x, y);
874bool InternalGuider::setGuiderParams(
double ccdPixelSizeX,
double ccdPixelSizeY,
double mountAperture,
875 double mountFocalLength)
877 this->ccdPixelSizeX = ccdPixelSizeX;
878 this->ccdPixelSizeY = ccdPixelSizeY;
879 this->mountAperture = mountAperture;
880 this->mountFocalLength = mountFocalLength;
881 return pmath->setGuiderParameters(ccdPixelSizeX, ccdPixelSizeY, mountAperture, mountFocalLength);
884bool InternalGuider::setFrameParams(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t binX, uint16_t binY)
886 if (w <= 0 || h <= 0)
897 pmath->setVideoParameters(w, h, subBinX, subBinY);
902void InternalGuider::emitAxisPulse(
const cproc_out_params * out)
904 double raPulse = out->pulse_length[GUIDE_RA];
905 double dePulse = out->pulse_length[GUIDE_DEC];
908 if(out->pulse_dir[GUIDE_RA] == NO_DIR)
911 if(out->pulse_dir[GUIDE_DEC] == NO_DIR)
915 if(out->pulse_dir[GUIDE_RA] == RA_INC_DIR)
918 if(out->pulse_dir[GUIDE_DEC] == DEC_DEC_DIR)
921 emit newAxisPulse(raPulse, dePulse);
924bool InternalGuider::processGuiding()
926 const cproc_out_params *out;
934 m_isFirstFrame =
false;
935 if (state == GUIDE_GUIDING)
937 GuiderUtils::Vector star_pos = pmath->findLocalStarPosition(m_ImageData, m_GuideFrame,
true);
938 if (star_pos.x != -1 && star_pos.y != -1)
939 pmath->setTargetPosition(star_pos.x, star_pos.y);
944 m_isFirstFrame =
true;
951 auto const timeStep = calculateGPGTimeStep();
952 pmath->performProcessing(state, m_ImageData, m_GuideFrame, timeStep, &guideLog);
953 if (pmath->usingSEPMultiStar())
956 auto gs = pmath->getGuideStars();
957 info = QString(
"%1 stars, %2/%3 refs")
958 .arg(gs.getNumStarsDetected())
959 .arg(gs.getNumReferencesFound())
960 .arg(gs.getNumReferences());
962 emit guideInfo(info);
966 if (this->m_darkGuideTimer->isActive())
967 this->m_darkGuideTimer->start();
970 if (state == GUIDE_SUSPENDED)
972 if (Options::gPGEnabled())
973 emit frameCaptureRequested();
978 if (pmath->isStarLost())
981 m_starLostCounter = 0;
985 out = pmath->getOutputParameters();
987 if (isPoorGuiding(out))
992 if ((state == GUIDE_DITHERING_SETTLE || state == GUIDE_DITHERING) && Options::ditherWithOnePulse())
995 bool sendPulses = !pmath->isStarLost();
998 if (sendPulses && (out->pulse_dir[GUIDE_RA] != NO_DIR || out->pulse_dir[GUIDE_DEC] != NO_DIR))
1000 emit newMultiPulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA],
1001 out->pulse_dir[GUIDE_DEC], out->pulse_length[GUIDE_DEC], StartCaptureAfterPulses);
1004 emit frameCaptureRequested();
1006 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING || state == GUIDE_DITHERING_SETTLE)
1014 if (state < GUIDE_DITHERING)
1018 emit newAxisDelta(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
1021 emit newAxisSigma(out->sigma[GUIDE_RA], out->sigma[GUIDE_DEC]);
1022 if (SEPMultiStarEnabled())
1023 emit newSNR(pmath->getGuideStarSNR());
1030std::pair<Seconds, Seconds> InternalGuider::calculateGPGTimeStep()
1034 const Seconds guideDelay{(Options::guideDelay())};
1036 auto const captureInterval = Seconds(m_captureTimer->intervalAsDuration()) + guideDelay;
1037 auto const darkGuideInterval = Seconds(m_darkGuideTimer->intervalAsDuration());
1039 if (!Options::gPGDarkGuiding() || !isInferencePeriodFinished())
1041 return std::pair<Seconds, Seconds>(captureInterval, captureInterval);
1043 auto const captureTimeRemaining = Seconds(m_captureTimer->remainingTimeAsDuration()) + guideDelay;
1044 auto const darkGuideTimeRemaining = Seconds(m_darkGuideTimer->remainingTimeAsDuration());
1046 if (captureTimeRemaining <= Seconds::zero()
1047 && darkGuideTimeRemaining <= Seconds::zero())
1049 timeStep = std::min(captureInterval, darkGuideInterval);
1051 else if (captureTimeRemaining <= Seconds::zero())
1053 timeStep = std::min(captureInterval, darkGuideTimeRemaining);
1055 else if (darkGuideTimeRemaining <= Seconds::zero())
1057 timeStep = std::min(captureTimeRemaining, darkGuideInterval);
1061 timeStep = std::min(captureTimeRemaining, darkGuideTimeRemaining);
1063 return std::pair<Seconds, Seconds>(timeStep, captureInterval);
1068void InternalGuider::darkGuide()
1071 if (state != GUIDE_GUIDING)
1074 if(Options::gPGDarkGuiding() && isInferencePeriodFinished())
1076 const cproc_out_params *out;
1077 auto const timeStep = calculateGPGTimeStep();
1078 pmath->performDarkGuiding(state, timeStep);
1080 out = pmath->getOutputParameters();
1081 emit newSinglePulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA], DontCaptureAfterPulses);
1087bool InternalGuider::isPoorGuiding(
const cproc_out_params * out)
1089 double delta_rms = std::hypot(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
1090 if (delta_rms > Options::guideMaxDeltaRMS())
1093 m_highRMSCounter = 0;
1095 uint8_t abortStarLostThreshold = (state == GUIDE_DITHERING
1096 || state == GUIDE_MANUAL_DITHERING) ? MAX_LOST_STAR_THRESHOLD * 3 : MAX_LOST_STAR_THRESHOLD;
1097 uint8_t abortRMSThreshold = (state == GUIDE_DITHERING
1098 || state == GUIDE_MANUAL_DITHERING) ? MAX_RMS_THRESHOLD * 3 : MAX_RMS_THRESHOLD;
1099 if (m_starLostCounter > abortStarLostThreshold || m_highRMSCounter > abortRMSThreshold)
1101 qCDebug(KSTARS_EKOS_GUIDE) <<
"m_starLostCounter" << m_starLostCounter
1102 <<
"m_highRMSCounter" << m_highRMSCounter
1103 <<
"delta_rms" << delta_rms;
1105 if (m_starLostCounter > abortStarLostThreshold)
1106 emit newLog(
i18n(
"Lost track of the guide star. Searching for guide stars..."));
1108 emit newLog(
i18n(
"Delta RMS threshold value exceeded. Searching for guide stars..."));
1110 reacquireTimer.start();
1111 rememberState = state;
1112 state = GUIDE_REACQUIRE;
1113 emit newStatus(state);
1118bool InternalGuider::selectAutoStarSEPMultistar()
1120 m_GuideFrame->updateFrame();
1121 m_DitherOrigin = QVector3D(0, 0, 0);
1122 QVector3D newStarCenter = pmath->selectGuideStar(m_ImageData);
1123 if (newStarCenter.
x() >= 0)
1125 emit newStarPosition(newStarCenter,
true);
1131bool InternalGuider::SEPMultiStarEnabled()
1133 return Options::guideAlgorithm() == SEP_MULTISTAR;
1136bool InternalGuider::selectAutoStar()
1138 m_DitherOrigin = QVector3D(0, 0, 0);
1139 if (Options::guideAlgorithm() == SEP_MULTISTAR)
1140 return selectAutoStarSEPMultistar();
1142 bool useNativeDetection =
false;
1144 QList<Edge *> starCenters;
1146 if (Options::guideAlgorithm() != SEP_THRESHOLD)
1147 starCenters = GuideAlgorithms::detectStars(m_ImageData, m_GuideFrame->getTrackingBox());
1149 if (starCenters.
empty())
1151 QVariantMap settings;
1152 settings[
"maxStarsCount"] = 50;
1153 settings[
"optionsProfileIndex"] = Options::guideOptionsProfile();
1154 settings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::GuideProfiles);
1155 m_ImageData->setSourceExtractorSettings(settings);
1157 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1158 m_ImageData->findStars(ALGORITHM_SEP).waitForFinished();
1160 m_ImageData->findStars().waitForFinished();
1162 starCenters = m_ImageData->getStarCenters();
1163 if (starCenters.
empty())
1166 useNativeDetection =
true;
1168 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1169 std::sort(starCenters.
begin(), starCenters.
end(), [](
const Edge * a,
const Edge * b)
1171 return a->val > b->val;
1174 std::sort(starCenters.
begin(), starCenters.
end(), [](
const Edge * a,
const Edge * b)
1176 return a->width > b->width;
1179 m_GuideFrame->setStarsEnabled(
true);
1180 m_GuideFrame->updateFrame();
1183 int maxX = m_ImageData->width();
1184 int maxY = m_ImageData->height();
1186 int scores[MAX_GUIDE_STARS];
1188 int maxIndex = MAX_GUIDE_STARS < starCenters.
count() ? MAX_GUIDE_STARS : starCenters.
count();
1190 for (
int i = 0; i < maxIndex; i++)
1196 if (useNativeDetection)
1204 if (
center->width >
float(guideBoxSize) / subBinX)
1208 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1209 score += sqrt(
center->val);
1216 foreach (Edge *edge, starCenters)
1237 int maxScoreIndex = -1;
1238 for (
int i = 0; i < maxIndex; i++)
1240 if (scores[i] > maxScore)
1242 maxScore = scores[i];
1247 if (maxScoreIndex < 0)
1249 qCDebug(KSTARS_EKOS_GUIDE) <<
"No suitable star detected.";
1253 QVector3D newStarCenter(starCenters[maxScoreIndex]->x, starCenters[maxScoreIndex]->y, 0);
1255 if (useNativeDetection ==
false)
1256 qDeleteAll(starCenters);
1258 emit newStarPosition(newStarCenter,
true);
1263bool InternalGuider::reacquire()
1265 bool rc = selectAutoStar();
1268 m_highRMSCounter = m_starLostCounter = 0;
1269 m_isFirstFrame =
true;
1272 if (rememberState == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
1274 if (Options::ditherSettle() > 0)
1276 state = GUIDE_DITHERING_SETTLE;
1277 guideLog.settleStartedInfo();
1278 emit newStatus(state);
1285 state = GUIDE_GUIDING;
1286 emit newStatus(state);
1290 else if (reacquireTimer.elapsed() >
static_cast<int>(Options::guideLostStarTimeout() * 1000))
1292 emit newLog(
i18n(
"Failed to find any suitable guide stars. Aborting..."));
1297 emit frameCaptureRequested();
1301void InternalGuider::fillGuideInfo(GuideLog::GuideInfo * info)
1306 info->pixelScale = (206.26481 * this->ccdPixelSizeX * this->subBinX) / this->mountFocalLength;
1307 info->binning = this->subBinX;
1308 info->focalLength = this->mountFocalLength;
1309 info->ra = this->mountRA.Degrees();
1310 info->dec = this->mountDEC.Degrees();
1311 info->azimuth = this->mountAzimuth.Degrees();
1312 info->altitude = this->mountAltitude.Degrees();
1313 info->pierSide = this->pierSide;
1314 info->xangle = pmath->getCalibration().getRAAngle();
1315 info->yangle = pmath->getCalibration().getDECAngle();
1317 info->xrate = 1000.0 / pmath->getCalibration().raPulseMillisecondsPerArcsecond();
1318 info->yrate = 1000.0 / pmath->getCalibration().decPulseMillisecondsPerArcsecond();
1321void InternalGuider::updateGPGParameters()
1323 setDarkGuideTimerInterval();
1324 pmath->getGPG().updateParameters();
1327void InternalGuider::resetGPG()
1329 pmath->getGPG().reset();
1333const Calibration &InternalGuider::getCalibration()
const
1335 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 name(const QVariant &location)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QDateTime currentDateTime()
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)
qsizetype length() const const
QTextStream & center(QTextStream &stream)