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"
21 #include <KMessageBox>
28 #define MAX_GUIDE_STARS 10
30 using namespace std::chrono_literals;
34 InternalGuider::InternalGuider()
37 pmath.reset(
new cgmath());
38 connect(pmath.get(), &cgmath::newStarPosition,
this, &InternalGuider::newStarPosition);
39 connect(pmath.get(), &cgmath::guideStats,
this, &InternalGuider::guideStats);
43 pmath->getMutableCalibration()->restore(
44 pierSide, Options::reverseDecOnPierSideChange(), subBinX, subBinY,
nullptr);
51 m_darkGuideTimer = std::make_unique<QTimer>(
this);
52 m_captureTimer = std::make_unique<QTimer>(
this);
54 setDarkGuideTimerInterval();
58 connect(
this, &Ekos::GuideInterface::frameCaptureRequested,
this, [ = ]()
60 this->m_captureTimer->start();
64 void InternalGuider::setExposureTime()
66 Seconds seconds(Options::guideExposure());
67 setTimer(m_captureTimer, seconds);
70 void InternalGuider::setTimer(std::unique_ptr<QTimer> &timer, Seconds seconds)
72 const std::chrono::duration<double, std::milli> inMilliseconds(seconds);
73 timer->setInterval((
int)(inMilliseconds.count()));
76 void InternalGuider::setDarkGuideTimerInterval()
78 constexpr
double kMinInterval = 0.5;
79 const Seconds seconds(std::max(kMinInterval, Options::gPGDarkGuidingInterval()));
80 setTimer(m_darkGuideTimer, seconds);
83 void InternalGuider::resetDarkGuiding()
85 m_darkGuideTimer->stop();
86 m_captureTimer->stop();
89 bool InternalGuider::isInferencePeriodFinished()
91 auto const contribution = pmath->getGPG().predictionContribution();
92 return contribution >= 0.99;
94 bool InternalGuider::guide()
96 if (state >= GUIDE_GUIDING)
98 return processGuiding();
101 if (state == GUIDE_SUSPENDED)
105 m_GuideFrame->disconnect(
this);
110 m_starLostCounter = 0;
111 m_highRMSCounter = 0;
114 m_isFirstFrame =
true;
116 if (state == GUIDE_IDLE)
118 if (Options::saveGuideLog())
120 GuideLog::GuideInfo info;
121 fillGuideInfo(&info);
122 guideLog.startGuiding(info);
124 state = GUIDE_GUIDING;
126 emit newStatus(state);
128 emit frameCaptureRequested();
142 bool InternalGuider::abort()
147 guideLog.endGuiding();
150 if (state == GUIDE_CALIBRATING ||
151 state == GUIDE_GUIDING ||
152 state == GUIDE_DITHERING ||
153 state == GUIDE_MANUAL_DITHERING ||
154 state == GUIDE_REACQUIRE)
156 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
157 emit newStatus(GUIDE_DITHERING_ERROR);
158 emit newStatus(GUIDE_ABORTED);
160 qCDebug(KSTARS_EKOS_GUIDE) <<
"Aborting" << getGuideStatusString(state);
164 emit newStatus(GUIDE_IDLE);
165 qCDebug(KSTARS_EKOS_GUIDE) <<
"Stopping internal guider.";
169 disconnect(m_darkGuideTimer.get(),
nullptr,
nullptr,
nullptr);
175 m_ProgressiveDither.clear();
176 m_starLostCounter = 0;
177 m_highRMSCounter = 0;
181 pmath->suspend(
false);
183 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding aborted.";
188 bool InternalGuider::suspend()
190 guideLog.pauseInfo();
191 state = GUIDE_SUSPENDED;
194 emit newStatus(state);
196 pmath->suspend(
true);
202 void InternalGuider::startDarkGuiding()
204 if (Options::gPGDarkGuiding())
209 m_darkGuideTimer->start();
211 qCDebug(KSTARS_EKOS_GUIDE) <<
"Starting dark guiding.";
215 bool InternalGuider::resume()
217 qCDebug(KSTARS_EKOS_GUIDE) <<
"Resuming...";
219 guideLog.resumeInfo();
220 state = GUIDE_GUIDING;
221 emit newStatus(state);
223 pmath->suspend(
false);
229 emit frameCaptureRequested();
234 bool InternalGuider::ditherXY(
double x,
double y)
236 m_ProgressiveDither.clear();
239 pmath->getTargetPosition(&cur_x, &cur_y);
244 double oneJump = (guideBoxSize / 4.0);
245 double targetX = cur_x, targetY = cur_y;
246 int xSign = (x >= cur_x) ? 1 : -1;
247 int ySign = (y >= cur_y) ? 1 : -1;
251 if (fabs(targetX - x) > oneJump)
252 targetX += oneJump * xSign;
253 else if (fabs(targetX - x) < oneJump)
256 if (fabs(targetY - y) > oneJump)
257 targetY += oneJump * ySign;
258 else if (fabs(targetY - y) < oneJump)
261 m_ProgressiveDither.enqueue(GuiderUtils::Vector(targetX, targetY, -1));
264 while (targetX != x || targetY != y);
266 m_DitherTargetPosition = m_ProgressiveDither.dequeue();
267 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
268 guideLog.ditherInfo(x, y, m_DitherTargetPosition.x, m_DitherTargetPosition.y);
270 state = GUIDE_MANUAL_DITHERING;
271 emit newStatus(state);
278 bool InternalGuider::dither(
double 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();
425 bool InternalGuider::abortDither()
427 if (Options::ditherFailAbortsAutoGuide())
429 emit newStatus(Ekos::GUIDE_DITHERING_ERROR);
435 emit newLog(
i18n(
"Warning: Dithering failed. Autoguiding shall continue as set in the options in case "
436 "of dither failure."));
438 if (Options::ditherSettle() > 0)
440 state = GUIDE_DITHERING_SETTLE;
441 guideLog.settleStartedInfo();
442 emit newStatus(state);
445 if (Options::gPGEnabled())
446 pmath->getGPG().ditheringSettled(
false);
453 bool InternalGuider::processManualDithering()
456 pmath->getTargetPosition(&cur_x, &cur_y);
457 pmath->getStarScreenPosition(&cur_x, &cur_y);
460 double driftRA, driftDEC;
461 pmath->getCalibration().computeDrift(
462 GuiderUtils::Vector(cur_x, cur_y, 0),
463 GuiderUtils::Vector(m_DitherTargetPosition.x, m_DitherTargetPosition.y, 0),
464 &driftRA, &driftDEC);
466 qCDebug(KSTARS_EKOS_GUIDE) <<
"Manual Dithering in progress. Diff star X:" << driftRA <<
"Y:" << driftDEC;
468 if (fabs(driftRA) < guideBoxSize / 5.0 && fabs(driftDEC) < guideBoxSize / 5.0)
470 if (m_ProgressiveDither.empty() ==
false)
472 m_DitherTargetPosition = m_ProgressiveDither.dequeue();
473 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
474 qCDebug(KSTARS_EKOS_GUIDE) <<
"Next Dither Jump X:" << m_DitherTargetPosition.x <<
"Jump Y:" << m_DitherTargetPosition.y;
482 if (fabs(driftRA) < 1 && fabs(driftDEC) < 1)
484 pmath->setTargetPosition(cur_x, cur_y);
485 qCDebug(KSTARS_EKOS_GUIDE) <<
"Manual Dither complete.";
487 if (Options::ditherSettle() > 0)
489 state = GUIDE_DITHERING_SETTLE;
490 guideLog.settleStartedInfo();
491 emit newStatus(state);
503 if (++m_DitherRetries > Options::ditherMaxIterations())
505 emit newLog(
i18n(
"Warning: Manual Dithering failed."));
507 if (Options::ditherSettle() > 0)
509 state = GUIDE_DITHERING_SETTLE;
510 guideLog.settleStartedInfo();
511 emit newStatus(state);
524 void InternalGuider::setDitherSettled()
526 guideLog.settleCompletedInfo();
527 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
530 state = GUIDE_GUIDING;
533 bool InternalGuider::calibrate()
535 bool ccdInfo =
true, scopeInfo =
true;
538 if (subW == 0 || subH == 0)
544 if (mountAperture == 0.0 || mountFocalLength == 0.0)
547 if (ccdInfo ==
false)
548 errMsg +=
" & Telescope";
550 errMsg +=
"Telescope";
553 if (ccdInfo ==
false || scopeInfo ==
false)
555 KSNotification::error(
i18n(
"%1 info are missing. Please set the values in INDI Control Panel.", errMsg),
556 i18n(
"Missing Information"));
560 if (state != GUIDE_CALIBRATING)
562 pmath->getTargetPosition(&calibrationStartX, &calibrationStartY);
563 calibrationProcess.reset(
564 new CalibrationProcess(calibrationStartX, calibrationStartY,
565 !Options::twoAxisEnabled()));
566 state = GUIDE_CALIBRATING;
567 emit newStatus(GUIDE_CALIBRATING);
570 if (calibrationProcess->inProgress())
572 iterateCalibration();
576 if (restoreCalibration())
578 calibrationProcess.reset();
579 emit newStatus(Ekos::GUIDE_CALIBRATION_SUCCESS);
581 i18n(
"Guiding calibration restored"), KSNotification::Guide);
588 pmath->getMutableCalibration()->setParameters(
589 ccdPixelSizeX / 1000.0, ccdPixelSizeY / 1000.0, mountFocalLength,
590 subBinX, subBinY, pierSide, mountRA, mountDEC);
592 calibrationProcess->useCalibration(pmath->getMutableCalibration());
594 m_GuideFrame->disconnect(
this);
597 emit DESwapChanged(
false);
598 pmath->setLostStar(
false);
600 if (Options::saveGuideLog())
602 GuideLog::GuideInfo info;
603 fillGuideInfo(&info);
604 guideLog.startCalibration(info);
606 calibrationProcess->startup();
607 calibrationProcess->setGuideLog(&guideLog);
608 iterateCalibration();
613 void InternalGuider::iterateCalibration()
615 if (calibrationProcess->inProgress())
617 auto const timeStep = calculateGPGTimeStep();
618 pmath->performProcessing(GUIDE_CALIBRATING, m_ImageData, m_GuideFrame, timeStep);
621 if (pmath->usingSEPMultiStar())
623 auto gs = pmath->getGuideStars();
624 info =
QString(
"%1 stars, %2/%3 refs")
625 .
arg(gs.getNumStarsDetected())
626 .
arg(gs.getNumReferencesFound())
627 .
arg(gs.getNumReferences());
629 emit guideInfo(info);
631 if (pmath->isStarLost())
633 emit newLog(
i18n(
"Lost track of the guide star. Try increasing the square size or reducing pulse duration."));
634 emit newStatus(Ekos::GUIDE_CALIBRATION_ERROR);
635 emit calibrationUpdate(GuideInterface::CALIBRATION_MESSAGE_ONLY,
636 i18n(
"Guide Star lost."));
642 pmath->getStarScreenPosition(&starX, &starY);
643 calibrationProcess->iterate(starX, starY);
645 auto status = calibrationProcess->getStatus();
646 if (status != GUIDE_CALIBRATING)
647 emit newStatus(status);
649 QString logStatus = calibrationProcess->getLogStatus();
651 emit newLog(logStatus);
655 GuideInterface::CalibrationUpdateType
type;
656 calibrationProcess->getCalibrationUpdate(&type, &updateMessage, &x, &y);
657 if (updateMessage.
length())
658 emit calibrationUpdate(type, updateMessage, x, y);
660 GuideDirection pulseDirection;
662 calibrationProcess->getPulse(&pulseDirection, &pulseMsecs);
663 if (pulseDirection != NO_DIR)
664 emit newSinglePulse(pulseDirection, pulseMsecs, StartCaptureAfterPulses);
666 if (status == GUIDE_CALIBRATION_ERROR)
668 KSNotification::event(
QLatin1String(
"CalibrationFailed"),
i18n(
"Guiding calibration failed"),
669 KSNotification::Guide, KSNotification::Alert);
672 else if (status == GUIDE_CALIBRATION_SUCCESS)
674 KSNotification::event(
QLatin1String(
"CalibrationSuccessful"),
675 i18n(
"Guiding calibration completed successfully"), KSNotification::Guide);
676 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
677 pmath->setTargetPosition(calibrationStartX, calibrationStartY);
684 m_GuideFrame = guideView;
690 if (Options::saveGuideImages())
694 now.toString(
"yyyy-MM-dd"));
699 QString name =
"guide_frame_" + now.toString(
"HH-mm-ss") +
".fits";
700 QString filename =
path + QStringLiteral(
"/") +
name + QStringLiteral(
"[compress R 100,100]");
701 m_ImageData->saveImage(filename);
705 void InternalGuider::reset()
707 qCDebug(KSTARS_EKOS_GUIDE) <<
"Resetting internal guider...";
712 connect(m_GuideFrame.get(), &FITSView::trackingStarSelected,
this, &InternalGuider::trackingStarSelected,
714 calibrationProcess.reset();
717 bool InternalGuider::clearCalibration()
719 Options::setSerializedCalibration(
"");
720 pmath->getMutableCalibration()->reset();
724 bool InternalGuider::restoreCalibration()
726 bool success = Options::reuseGuideCalibration() &&
727 pmath->getMutableCalibration()->restore(
728 pierSide, Options::reverseDecOnPierSideChange(),
729 subBinX, subBinY, &mountDEC);
731 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
735 void InternalGuider::setStarPosition(
QVector3D &starCenter)
737 pmath->setTargetPosition(starCenter.
x(), starCenter.
y());
740 void InternalGuider::trackingStarSelected(
int x,
int y)
758 void InternalGuider::setDECSwap(
bool enable)
760 pmath->getMutableCalibration()->setDeclinationSwapEnabled(enable);
763 void InternalGuider::setSquareAlgorithm(
int index)
765 if (index == SEP_MULTISTAR && !pmath->usingSEPMultiStar())
766 m_isFirstFrame =
true;
767 pmath->setAlgorithmIndex(index);
770 bool InternalGuider::getReticleParameters(
double *x,
double *y)
772 return pmath->getTargetPosition(x, y);
775 bool InternalGuider::setGuiderParams(
double ccdPixelSizeX,
double ccdPixelSizeY,
double mountAperture,
776 double mountFocalLength)
778 this->ccdPixelSizeX = ccdPixelSizeX;
779 this->ccdPixelSizeY = ccdPixelSizeY;
780 this->mountAperture = mountAperture;
781 this->mountFocalLength = mountFocalLength;
782 return pmath->setGuiderParameters(ccdPixelSizeX, ccdPixelSizeY, mountAperture, mountFocalLength);
785 bool InternalGuider::setFrameParams(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t binX, uint16_t binY)
787 if (w <= 0 || h <= 0)
798 pmath->setVideoParameters(w, h, subBinX, subBinY);
803 void InternalGuider::emitAxisPulse(
const cproc_out_params *out)
805 double raPulse = out->pulse_length[GUIDE_RA];
806 double dePulse = out->pulse_length[GUIDE_DEC];
809 if(out->pulse_dir[GUIDE_RA] == NO_DIR)
812 if(out->pulse_dir[GUIDE_DEC] == NO_DIR)
815 if(out->pulse_dir[GUIDE_RA] == RA_INC_DIR)
818 if(out->pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
821 emit newAxisPulse(raPulse, dePulse);
824 bool InternalGuider::processGuiding()
826 const cproc_out_params *out;
834 m_isFirstFrame =
false;
835 if (state == GUIDE_GUIDING)
837 GuiderUtils::Vector star_pos = pmath->findLocalStarPosition(m_ImageData, m_GuideFrame,
true);
838 if (star_pos.x != -1 && star_pos.y != -1)
839 pmath->setTargetPosition(star_pos.x, star_pos.y);
844 m_isFirstFrame =
true;
851 auto const timeStep = calculateGPGTimeStep();
852 pmath->performProcessing(state, m_ImageData, m_GuideFrame, timeStep, &guideLog);
853 if (pmath->usingSEPMultiStar())
856 auto gs = pmath->getGuideStars();
857 info =
QString(
"%1 stars, %2/%3 refs")
858 .
arg(gs.getNumStarsDetected())
859 .
arg(gs.getNumReferencesFound())
860 .
arg(gs.getNumReferences());
862 emit guideInfo(info);
866 if (this->m_darkGuideTimer->isActive())
867 this->m_darkGuideTimer->start();
870 if (state == GUIDE_SUSPENDED)
872 if (Options::gPGEnabled())
873 emit frameCaptureRequested();
878 if (pmath->isStarLost())
881 m_starLostCounter = 0;
885 out = pmath->getOutputParameters();
887 if (isPoorGuiding(out))
890 bool sendPulses = !pmath->isStarLost();
894 if (sendPulses && (out->pulse_dir[GUIDE_RA] != NO_DIR || out->pulse_dir[GUIDE_DEC] != NO_DIR))
896 emit newMultiPulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA],
897 out->pulse_dir[GUIDE_DEC], out->pulse_length[GUIDE_DEC], StartCaptureAfterPulses);
900 emit frameCaptureRequested();
902 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
910 if (state < GUIDE_DITHERING)
911 emit newAxisDelta(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
914 emit newAxisSigma(out->sigma[GUIDE_RA], out->sigma[GUIDE_DEC]);
915 if (SEPMultiStarEnabled())
916 emit newSNR(pmath->getGuideStarSNR());
923 std::pair<Seconds, Seconds> InternalGuider::calculateGPGTimeStep()
927 const Seconds guideDelay{(Options::guideDelay())};
929 auto const captureInterval = Seconds(m_captureTimer->intervalAsDuration()) + guideDelay;
930 auto const darkGuideInterval = Seconds(m_darkGuideTimer->intervalAsDuration());
932 if (!Options::gPGDarkGuiding() || !isInferencePeriodFinished())
934 return std::pair<Seconds, Seconds>(captureInterval, captureInterval);
936 auto const captureTimeRemaining = Seconds(m_captureTimer->remainingTimeAsDuration()) + guideDelay;
937 auto const darkGuideTimeRemaining = Seconds(m_darkGuideTimer->remainingTimeAsDuration());
939 if (captureTimeRemaining <= Seconds::zero()
940 && darkGuideTimeRemaining <= Seconds::zero())
942 timeStep = std::min(captureInterval, darkGuideInterval);
944 else if (captureTimeRemaining <= Seconds::zero())
946 timeStep = std::min(captureInterval, darkGuideTimeRemaining);
948 else if (darkGuideTimeRemaining <= Seconds::zero())
950 timeStep = std::min(captureTimeRemaining, darkGuideInterval);
954 timeStep = std::min(captureTimeRemaining, darkGuideTimeRemaining);
956 return std::pair<Seconds, Seconds>(timeStep, captureInterval);
961 void InternalGuider::darkGuide()
964 if (state != GUIDE_GUIDING)
967 if(Options::gPGDarkGuiding() && isInferencePeriodFinished())
969 const cproc_out_params *out;
970 auto const timeStep = calculateGPGTimeStep();
971 pmath->performDarkGuiding(state, timeStep);
973 out = pmath->getOutputParameters();
974 emit newSinglePulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA], DontCaptureAfterPulses);
980 bool InternalGuider::isPoorGuiding(
const cproc_out_params* out)
982 double delta_rms = std::hypot(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
983 if (delta_rms > Options::guideMaxDeltaRMS())
986 m_highRMSCounter = 0;
988 uint8_t abortStarLostThreshold = (state == GUIDE_DITHERING
989 || state == GUIDE_MANUAL_DITHERING) ? MAX_LOST_STAR_THRESHOLD * 3 : MAX_LOST_STAR_THRESHOLD;
990 uint8_t abortRMSThreshold = (state == GUIDE_DITHERING
991 || state == GUIDE_MANUAL_DITHERING) ? MAX_RMS_THRESHOLD * 3 : MAX_RMS_THRESHOLD;
992 if (m_starLostCounter > abortStarLostThreshold || m_highRMSCounter > abortRMSThreshold)
994 qCDebug(KSTARS_EKOS_GUIDE) <<
"m_starLostCounter" << m_starLostCounter
995 <<
"m_highRMSCounter" << m_highRMSCounter
996 <<
"delta_rms" << delta_rms;
998 if (m_starLostCounter > abortStarLostThreshold)
999 emit newLog(
i18n(
"Lost track of the guide star. Searching for guide stars..."));
1001 emit newLog(
i18n(
"Delta RMS threshold value exceeded. Searching for guide stars..."));
1003 reacquireTimer.start();
1004 rememberState = state;
1005 state = GUIDE_REACQUIRE;
1006 emit newStatus(state);
1011 bool InternalGuider::selectAutoStarSEPMultistar()
1013 m_GuideFrame->updateFrame();
1015 QVector3D newStarCenter = pmath->selectGuideStar(m_ImageData);
1016 if (newStarCenter.
x() >= 0)
1018 emit newStarPosition(newStarCenter,
true);
1024 bool InternalGuider::SEPMultiStarEnabled()
1026 return Options::guideAlgorithm() == SEP_MULTISTAR;
1029 bool InternalGuider::selectAutoStar()
1032 if (Options::guideAlgorithm() == SEP_MULTISTAR)
1033 return selectAutoStarSEPMultistar();
1035 bool useNativeDetection =
false;
1039 if (Options::guideAlgorithm() != SEP_THRESHOLD)
1040 starCenters = GuideAlgorithms::detectStars(m_ImageData, m_GuideFrame->getTrackingBox());
1042 if (starCenters.
empty())
1044 QVariantMap settings;
1045 settings[
"maxStarsCount"] = 50;
1046 settings[
"optionsProfileIndex"] = Options::guideOptionsProfile();
1047 settings[
"optionsProfileGroup"] =
static_cast<int>(Ekos::GuideProfiles);
1048 m_ImageData->setSourceExtractorSettings(settings);
1050 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1051 m_ImageData->findStars(ALGORITHM_SEP).waitForFinished();
1053 m_ImageData->findStars().waitForFinished();
1055 starCenters = m_ImageData->getStarCenters();
1056 if (starCenters.
empty())
1059 useNativeDetection =
true;
1061 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1062 std::sort(starCenters.
begin(), starCenters.
end(), [](
const Edge * a,
const Edge * b)
1064 return a->val > b->val;
1067 std::sort(starCenters.
begin(), starCenters.
end(), [](
const Edge * a,
const Edge * b)
1069 return a->width > b->width;
1072 m_GuideFrame->setStarsEnabled(
true);
1073 m_GuideFrame->updateFrame();
1076 int maxX = m_ImageData->width();
1077 int maxY = m_ImageData->height();
1079 int scores[MAX_GUIDE_STARS];
1081 int maxIndex = MAX_GUIDE_STARS < starCenters.
count() ? MAX_GUIDE_STARS : starCenters.
count();
1083 for (
int i = 0; i < maxIndex; i++)
1089 if (useNativeDetection)
1097 if (
center->width >
float(guideBoxSize) / subBinX)
1101 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1102 score += sqrt(
center->val);
1109 foreach (Edge *edge, starCenters)
1130 int maxScoreIndex = -1;
1131 for (
int i = 0; i < maxIndex; i++)
1133 if (scores[i] > maxScore)
1135 maxScore = scores[i];
1140 if (maxScoreIndex < 0)
1142 qCDebug(KSTARS_EKOS_GUIDE) <<
"No suitable star detected.";
1146 QVector3D newStarCenter(starCenters[maxScoreIndex]->x, starCenters[maxScoreIndex]->y, 0);
1148 if (useNativeDetection ==
false)
1149 qDeleteAll(starCenters);
1151 emit newStarPosition(newStarCenter,
true);
1156 bool InternalGuider::reacquire()
1158 bool rc = selectAutoStar();
1161 m_highRMSCounter = m_starLostCounter = 0;
1162 m_isFirstFrame =
true;
1165 if (rememberState == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
1167 if (Options::ditherSettle() > 0)
1169 state = GUIDE_DITHERING_SETTLE;
1170 guideLog.settleStartedInfo();
1171 emit newStatus(state);
1178 state = GUIDE_GUIDING;
1179 emit newStatus(state);
1183 else if (reacquireTimer.elapsed() >
static_cast<int>(Options::guideLostStarTimeout() * 1000))
1185 emit newLog(
i18n(
"Failed to find any suitable guide stars. Aborting..."));
1190 emit frameCaptureRequested();
1194 void InternalGuider::fillGuideInfo(GuideLog::GuideInfo *info)
1199 info->pixelScale = (206.26481 * this->ccdPixelSizeX * this->subBinX) / this->mountFocalLength;
1200 info->binning = this->subBinX;
1201 info->focalLength = this->mountFocalLength;
1202 info->ra = this->mountRA.Degrees();
1203 info->dec = this->mountDEC.Degrees();
1204 info->azimuth = this->mountAzimuth.Degrees();
1205 info->altitude = this->mountAltitude.Degrees();
1206 info->pierSide = this->pierSide;
1207 info->xangle = pmath->getCalibration().getRAAngle();
1208 info->yangle = pmath->getCalibration().getDECAngle();
1210 info->xrate = 1000.0 / pmath->getCalibration().raPulseMillisecondsPerArcsecond();
1211 info->yrate = 1000.0 / pmath->getCalibration().decPulseMillisecondsPerArcsecond();
1214 void InternalGuider::updateGPGParameters()
1216 setDarkGuideTimerInterval();
1217 pmath->getGPG().updateParameters();
1220 void InternalGuider::resetGPG()
1222 pmath->getGPG().reset();
1226 const Calibration &InternalGuider::getCalibration()
const
1228 return pmath->getCalibration();