Kstars

internalguider.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com>.
3
4 Based on lin_guider
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7*/
8
9#include "internalguider.h"
10
11#include "ekos_guide_debug.h"
12#include "gmath.h"
13#include "Options.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"
22
23#include <KMessageBox>
24
25#include <random>
26#include <chrono>
27#include <QTimer>
28#include <QString>
29
30#define MAX_GUIDE_STARS 10
31
32using namespace std::chrono_literals;
33
34namespace Ekos
35{
36InternalGuider::InternalGuider()
37{
38 // Create math object
39 pmath.reset(new cgmath());
40 connect(pmath.get(), &cgmath::newStarPosition, this, &InternalGuider::newStarPosition);
41 connect(pmath.get(), &cgmath::guideStats, this, &InternalGuider::guideStats);
42
43 // Do this so that stored calibration will be visible on the
44 // guide options menu. Calibration will get restored again when needed.
45 pmath->getMutableCalibration()->restore(
46 pierSide, Options::reverseDecOnPierSideChange(), subBinX, subBinY, nullptr);
47
48 state = GUIDE_IDLE;
49 m_DitherOrigin = QVector3D(0, 0, 0);
50
51 emit guideInfo("");
52
53 m_darkGuideTimer = std::make_unique<QTimer>(this);
54 m_captureTimer = std::make_unique<QTimer>(this);
55
56 setDarkGuideTimerInterval();
57
58 setExposureTime();
59
60 connect(this, &Ekos::GuideInterface::frameCaptureRequested, this, [ = ]()
61 {
62 this->m_captureTimer->start();
63 });
64}
65
66void InternalGuider::setExposureTime()
67{
68 Seconds seconds(Options::guideExposure());
69 setTimer(m_captureTimer, seconds);
70}
71
72void InternalGuider::setTimer(std::unique_ptr<QTimer> &timer, Seconds seconds)
73{
74 const std::chrono::duration<double, std::milli> inMilliseconds(seconds);
75 timer->setInterval((int)(inMilliseconds.count()));
76}
77
78void InternalGuider::setDarkGuideTimerInterval()
79{
80 constexpr double kMinInterval = 0.5; // 0.5s is the shortest allowed dark-guiding period.
81 const Seconds seconds(std::max(kMinInterval, Options::gPGDarkGuidingInterval()));
82 setTimer(m_darkGuideTimer, seconds);
83}
84
85void InternalGuider::resetDarkGuiding()
86{
87 m_darkGuideTimer->stop();
88 m_captureTimer->stop();
89}
90
91bool InternalGuider::isInferencePeriodFinished()
92{
93 auto const contribution = pmath->getGPG().predictionContribution();
94 return contribution >= 0.99;
95}
96bool InternalGuider::guide()
97{
98 if (state >= GUIDE_GUIDING)
99 {
100 return processGuiding();
101 }
102
103 if (state == GUIDE_SUSPENDED)
104 {
105 return true;
106 }
107 m_GuideFrame->disconnect(this);
108
109 pmath->start();
110 emit guideInfo("");
111
112 m_starLostCounter = 0;
113 m_highRMSCounter = 0;
114 m_DitherOrigin = QVector3D(0, 0, 0);
115
116 m_isFirstFrame = true;
117
118 if (state == GUIDE_IDLE)
119 {
120 if (Options::saveGuideLog())
121 guideLog.enable();
122 GuideLog::GuideInfo info;
123 fillGuideInfo(&info);
124 guideLog.startGuiding(info);
125 }
126 state = GUIDE_GUIDING;
127
128 emit newStatus(state);
129
130 emit frameCaptureRequested();
131
132 startDarkGuiding();
133
134 return true;
135}
136
137/**
138 * @brief InternalGuider::abort Abort all internal guider operations.
139 * This includes calibration, dithering, guiding, capturing, and reaquiring.
140 * The state is set to IDLE or ABORTED depending on the current state since
141 * ABORTED can lead to different behavior by external actors than IDLE
142 * @return True if abort succeeds, false otherwise.
143 */
144bool InternalGuider::abort()
145{
146 // calibrationStage = CAL_IDLE; remove totally when understand trackingStarSelected
147
148 logFile.close();
149 guideLog.endGuiding();
150 emit guideInfo("");
151
152 if (state == GUIDE_CALIBRATING ||
153 state == GUIDE_GUIDING ||
154 state == GUIDE_DITHERING ||
155 state == GUIDE_MANUAL_DITHERING ||
156 state == GUIDE_REACQUIRE)
157 {
158 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
159 emit newStatus(GUIDE_DITHERING_ERROR);
160 emit newStatus(GUIDE_ABORTED);
161
162 qCDebug(KSTARS_EKOS_GUIDE) << "Aborting" << getGuideStatusString(state);
163 }
164 else
165 {
166 emit newStatus(GUIDE_IDLE);
167 qCDebug(KSTARS_EKOS_GUIDE) << "Stopping internal guider.";
168 }
169
170 resetDarkGuiding();
171 disconnect(m_darkGuideTimer.get(), nullptr, nullptr, nullptr);
172
173 pmath->abort();
174
175
176
177 m_ProgressiveDither.clear();
178 m_starLostCounter = 0;
179 m_highRMSCounter = 0;
180
181 m_DitherOrigin = QVector3D(0, 0, 0);
182
183 pmath->suspend(false);
184 state = GUIDE_IDLE;
185 qCDebug(KSTARS_EKOS_GUIDE) << "Guiding aborted.";
186
187 return true;
188}
189
190bool InternalGuider::suspend()
191{
192 guideLog.pauseInfo();
193 state = GUIDE_SUSPENDED;
194
195 resetDarkGuiding();
196 emit newStatus(state);
197
198 pmath->suspend(true);
199 emit guideInfo("");
200
201 return true;
202}
203
204void InternalGuider::startDarkGuiding()
205{
206 if (Options::gPGDarkGuiding())
207 {
208 connect(m_darkGuideTimer.get(), &QTimer::timeout, this, &InternalGuider::darkGuide, Qt::UniqueConnection);
209
210 // Start the two dark guide timers. The capture timer is started automatically by a signal.
211 m_darkGuideTimer->start();
212
213 qCDebug(KSTARS_EKOS_GUIDE) << "Starting dark guiding.";
214 }
215}
216
217bool InternalGuider::resume()
218{
219 qCDebug(KSTARS_EKOS_GUIDE) << "Resuming...";
220 emit guideInfo("");
221 guideLog.resumeInfo();
222 state = GUIDE_GUIDING;
223 emit newStatus(state);
224
225 pmath->suspend(false);
226
227 startDarkGuiding();
228
229 setExposureTime();
230
231 emit frameCaptureRequested();
232
233 return true;
234}
235
236bool InternalGuider::ditherXY(double x, double y)
237{
238 m_ProgressiveDither.clear();
239 m_DitherRetries = 0;
240 double cur_x, cur_y;
241 pmath->getTargetPosition(&cur_x, &cur_y);
242
243 // Find out how many "jumps" we need to perform in order to get to target.
244 // The current limit is now 1/4 of the box size to make sure the star stays within detection
245 // threashold inside the window.
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;
250
251 do
252 {
253 if (fabs(targetX - x) > oneJump)
254 targetX += oneJump * xSign;
255 else if (fabs(targetX - x) < oneJump)
256 targetX = x;
257
258 if (fabs(targetY - y) > oneJump)
259 targetY += oneJump * ySign;
260 else if (fabs(targetY - y) < oneJump)
261 targetY = y;
262
263 m_ProgressiveDither.enqueue(GuiderUtils::Vector(targetX, targetY, -1));
264
265 }
266 while (targetX != x || targetY != y);
267
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);
271
272 state = GUIDE_MANUAL_DITHERING;
273 emit newStatus(state);
274
275 processGuiding();
276
277 return true;
278}
279
280bool InternalGuider::dither(double pixels)
281{
282 if (Options::ditherWithOnePulse() )
283 return onePulseDither(pixels);
284
285 double ret_x, ret_y;
286 pmath->getTargetPosition(&ret_x, &ret_y);
287
288 // Just calling getStarScreenPosition() will get the position at the last time the guide star
289 // was found, which is likely before the most recent guide pulse.
290 // Instead we call findLocalStarPosition() which does the analysis from the image.
291 // Unfortunately, processGuiding() will repeat that computation.
292 // We currently don't cache it.
293 GuiderUtils::Vector star_position = pmath->findLocalStarPosition(m_ImageData, m_GuideFrame, false);
294 if (pmath->isStarLost() || (star_position.x == -1) || (star_position.y == -1))
295 {
296 // If the star position is lost, just lose this iteration.
297 // If it happens too many time, abort.
298 if (++m_starLostCounter > MAX_LOST_STAR_THRESHOLD)
299 {
300 qCDebug(KSTARS_EKOS_GUIDE) << "Too many consecutive lost stars." << m_starLostCounter << "Aborting dither.";
301 return abortDither();
302 }
303 qCDebug(KSTARS_EKOS_GUIDE) << "Dither lost star. Trying again.";
304 emit frameCaptureRequested();
305 return true;
306 }
307 else
308 m_starLostCounter = 0;
309
310 if (state != GUIDE_DITHERING)
311 {
312 m_DitherRetries = 0;
313
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);
317
318 double angle = angleMagnitude(generator) * dms::DegToRad;
319 double diff_x = pixels * cos(angle);
320 double diff_y = pixels * sin(angle);
321
322 if (pmath->getCalibration().declinationSwapEnabled())
323 diff_y *= -1;
324
325 if (m_DitherOrigin.x() == 0 && m_DitherOrigin.y() == 0)
326 {
327 m_DitherOrigin = QVector3D(ret_x, ret_y, 0);
328 }
329 double totalXOffset = ret_x - m_DitherOrigin.x();
330 double totalYOffset = ret_y - m_DitherOrigin.y();
331
332 // if we've dithered too far, and diff_x or diff_y is pushing us even further away, then change its direction.
333 // Note: it is possible that we've dithered too far, but diff_x/y is pointing in the right direction.
334 // Don't change it in that 2nd case.
335 if (((diff_x + totalXOffset > MAX_DITHER_TRAVEL) && (diff_x > 0)) ||
336 ((diff_x + totalXOffset < -MAX_DITHER_TRAVEL) && (diff_x < 0)))
337 {
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);
341 diff_x *= -1.5;
342 }
343 if (((diff_y + totalYOffset > MAX_DITHER_TRAVEL) && (diff_y > 0)) ||
344 ((diff_y + totalYOffset < -MAX_DITHER_TRAVEL) && (diff_y < 0)))
345 {
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);
349 diff_y *= -1.5;
350 }
351
352 m_DitherTargetPosition = GuiderUtils::Vector(ret_x, ret_y, 0) + GuiderUtils::Vector(diff_x, diff_y, 0);
353
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);
362
363 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
364
365 if (Options::gPGEnabled())
366 // This is the offset in image coordinates, but needs to be converted to RA.
367 pmath->getGPG().startDithering(diff_x, diff_y, pmath->getCalibration());
368
369 state = GUIDE_DITHERING;
370 emit newStatus(state);
371
372 processGuiding();
373
374 return true;
375 }
376
377 // These will be the RA & DEC drifts of the current star position from the reticle position in pixels.
378 double driftRA, driftDEC;
379 pmath->getCalibration().computeDrift(
380 star_position,
381 GuiderUtils::Vector(m_DitherTargetPosition.x, m_DitherTargetPosition.y, 0),
382 &driftRA, &driftDEC);
383
384 double pixelOffsetX = m_DitherTargetPosition.x - star_position.x;
385 double pixelOffsetY = m_DitherTargetPosition.y - star_position.y;
386
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);
394
395 if (Options::ditherWithOnePulse() || (fabs(driftRA) < 1 && fabs(driftDEC) < 1))
396 {
397 pmath->setTargetPosition(star_position.x, star_position.y);
398
399 // In one-pulse dithering we want the target to be whereever we end up
400 // after the pulse. So, the first guide frame should not send any pulses
401 // and should reset the reticle to the position it finds.
402 if (Options::ditherWithOnePulse())
403 m_isFirstFrame = true;
404
405 qCDebug(KSTARS_EKOS_GUIDE) << "Dither complete.";
406
407 if (Options::ditherSettle() > 0)
408 {
409 state = GUIDE_DITHERING_SETTLE;
410 guideLog.settleStartedInfo();
411 emit newStatus(state);
412 }
413
414 if (Options::gPGEnabled())
415 pmath->getGPG().ditheringSettled(true);
416
417 QTimer::singleShot(Options::ditherSettle() * 1000, this, SLOT(setDitherSettled()));
418 }
419 else
420 {
421 if (++m_DitherRetries > Options::ditherMaxIterations())
422 return abortDither();
423
424 processGuiding();
425 }
426
427 return true;
428}
429bool InternalGuider::onePulseDither(double pixels)
430{
431 qCDebug(KSTARS_EKOS_GUIDE) << "OnePulseDither(" << "pixels" << ")";
432
433 // Cancel any current guide exposures.
434 emit abortExposure();
435
436 double ret_x, ret_y;
437 pmath->getTargetPosition(&ret_x, &ret_y);
438
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);
442
443 double angle = angleMagnitude(generator) * dms::DegToRad;
444 double diff_x = pixels * cos(angle);
445 double diff_y = pixels * sin(angle);
446
447 if (pmath->getCalibration().declinationSwapEnabled())
448 diff_y *= -1;
449
450 if (m_DitherOrigin.x() == 0 && m_DitherOrigin.y() == 0)
451 {
452 m_DitherOrigin = QVector3D(ret_x, ret_y, 0);
453 }
454 double totalXOffset = ret_x - m_DitherOrigin.x();
455 double totalYOffset = ret_y - m_DitherOrigin.y();
456
457 // If we've dithered too far, and diff_x or diff_y is pushing us even further away, then change its direction.
458 // Note: it is possible that we've dithered too far, but diff_x/y is pointing in the right direction.
459 // Don't change it in that 2nd case.
460 if (((diff_x + totalXOffset > MAX_DITHER_TRAVEL) && (diff_x > 0)) ||
461 ((diff_x + totalXOffset < -MAX_DITHER_TRAVEL) && (diff_x < 0)))
462 {
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);
466 diff_x *= -1.5;
467 }
468 if (((diff_y + totalYOffset > MAX_DITHER_TRAVEL) && (diff_y > 0)) ||
469 ((diff_y + totalYOffset < -MAX_DITHER_TRAVEL) && (diff_y < 0)))
470 {
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);
474 diff_y *= -1.5;
475 }
476
477 m_DitherTargetPosition = GuiderUtils::Vector(ret_x, ret_y, 0) + GuiderUtils::Vector(diff_x, diff_y, 0);
478
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);
487
488 pmath->setTargetPosition(m_DitherTargetPosition.x, m_DitherTargetPosition.y);
489
490 if (Options::gPGEnabled())
491 // This is the offset in image coordinates, but needs to be converted to RA.
492 pmath->getGPG().startDithering(diff_x, diff_y, pmath->getCalibration());
493
494 state = GUIDE_DITHERING;
495 emit newStatus(state);
496
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;
503
504 m_isFirstFrame = true;
505
506 // Send pulse if we have one active direction at least.
507 QString raDirString = raDir == RA_DEC_DIR ? "RA_DEC" : "RA_INC";
508 QString decDirString = decDir == DEC_INC_DIR ? "DEC_INC" : "DEC_DEC";
509
510 qCDebug(KSTARS_EKOS_GUIDE) << "OnePulseDither RA: " << raPulse << "ms" << raDirString << " DEC: " << decPulse << "ms " <<
511 decDirString;
512
513 // Don't capture because the single shot timer below will trigger a capture.
514 emit newMultiPulse(raDir, raPulse, decDir, decPulse, DontCaptureAfterPulses);
515
516 double totalMSecs = 1000.0 * Options::ditherSettle() + std::max(raPulse, decPulse) + 100;
517
518 state = GUIDE_DITHERING_SETTLE;
519 guideLog.settleStartedInfo();
520 emit newStatus(state);
521
522 if (Options::gPGEnabled())
523 pmath->getGPG().ditheringSettled(true);
524
525 QTimer::singleShot(totalMSecs, this, SLOT(setDitherSettled()));
526 return true;
527}
528
529bool InternalGuider::abortDither()
530{
531 if (Options::ditherFailAbortsAutoGuide())
532 {
533 emit newStatus(Ekos::GUIDE_DITHERING_ERROR);
534 abort();
535 return false;
536 }
537 else
538 {
539 emit newLog(i18n("Warning: Dithering failed. Autoguiding shall continue as set in the options in case "
540 "of dither failure."));
541
542 if (Options::ditherSettle() > 0)
543 {
544 state = GUIDE_DITHERING_SETTLE;
545 guideLog.settleStartedInfo();
546 emit newStatus(state);
547 }
548
549 if (Options::gPGEnabled())
550 pmath->getGPG().ditheringSettled(false);
551
552 QTimer::singleShot(Options::ditherSettle() * 1000, this, SLOT(setDitherSettled()));
553 return true;
554 }
555}
556
557bool InternalGuider::processManualDithering()
558{
559 double cur_x, cur_y;
560 pmath->getTargetPosition(&cur_x, &cur_y);
561 pmath->getStarScreenPosition(&cur_x, &cur_y);
562
563 // These will be the RA & DEC drifts of the current star position from the reticle position in pixels.
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);
569
570 qCDebug(KSTARS_EKOS_GUIDE) << "Manual Dithering in progress. Diff star X:" << driftRA << "Y:" << driftDEC;
571
572 if (fabs(driftRA) < guideBoxSize / 5.0 && fabs(driftDEC) < guideBoxSize / 5.0)
573 {
574 if (m_ProgressiveDither.empty() == false)
575 {
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;
579 m_DitherRetries = 0;
580
581 processGuiding();
582
583 return true;
584 }
585
586 if (fabs(driftRA) < 1 && fabs(driftDEC) < 1)
587 {
588 pmath->setTargetPosition(cur_x, cur_y);
589 qCDebug(KSTARS_EKOS_GUIDE) << "Manual Dither complete.";
590
591 if (Options::ditherSettle() > 0)
592 {
593 state = GUIDE_DITHERING_SETTLE;
594 guideLog.settleStartedInfo();
595 emit newStatus(state);
596 }
597
598 QTimer::singleShot(Options::ditherSettle() * 1000, this, SLOT(setDitherSettled()));
599 }
600 else
601 {
602 processGuiding();
603 }
604 }
605 else
606 {
607 if (++m_DitherRetries > Options::ditherMaxIterations())
608 {
609 emit newLog(i18n("Warning: Manual Dithering failed."));
610
611 if (Options::ditherSettle() > 0)
612 {
613 state = GUIDE_DITHERING_SETTLE;
614 guideLog.settleStartedInfo();
615 emit newStatus(state);
616 }
617
618 QTimer::singleShot(Options::ditherSettle() * 1000, this, SLOT(setDitherSettled()));
619 return true;
620 }
621
622 processGuiding();
623 }
624
625 return true;
626}
627
628void InternalGuider::setDitherSettled()
629{
630 guideLog.settleCompletedInfo();
631 emit newStatus(Ekos::GUIDE_DITHERING_SUCCESS);
632
633 // Back to guiding
634 state = GUIDE_GUIDING;
635}
636
637bool InternalGuider::calibrate()
638{
639 bool ccdInfo = true, scopeInfo = true;
640 QString errMsg;
641
642 if (subW == 0 || subH == 0)
643 {
644 errMsg = "CCD";
645 ccdInfo = false;
646 }
647
648 if (mountAperture == 0.0 || mountFocalLength == 0.0)
649 {
650 scopeInfo = false;
651 if (ccdInfo == false)
652 errMsg += " & Telescope";
653 else
654 errMsg += "Telescope";
655 }
656
657 if (ccdInfo == false || scopeInfo == false)
658 {
659 KSNotification::error(i18n("%1 info are missing. Please set the values in INDI Control Panel.", errMsg),
660 i18n("Missing Information"));
661 return false;
662 }
663
664 if (state != GUIDE_CALIBRATING)
665 {
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);
672 }
673
674 if (calibrationProcess->inProgress())
675 {
676 iterateCalibration();
677 return true;
678 }
679
680 if (restoreCalibration())
681 {
682 calibrationProcess.reset();
683 emit newStatus(Ekos::GUIDE_CALIBRATION_SUCCESS);
684 KSNotification::event(QLatin1String("CalibrationRestored"),
685 i18n("Guiding calibration restored"), KSNotification::Guide);
686 reset();
687 return true;
688 }
689
690 // Initialize the calibration parameters.
691 // CCD pixel values comes in in microns and we want mm.
692 pmath->getMutableCalibration()->setParameters(
693 ccdPixelSizeX / 1000.0, ccdPixelSizeY / 1000.0, mountFocalLength,
694 subBinX, subBinY, pierSide, mountRA, mountDEC);
695
696 calibrationProcess->useCalibration(pmath->getMutableCalibration());
697
698 m_GuideFrame->disconnect(this);
699
700 // Must reset dec swap before we run any calibration procedure!
701 emit DESwapChanged(false);
702 pmath->setLostStar(false);
703
704 if (Options::saveGuideLog())
705 guideLog.enable();
706 GuideLog::GuideInfo info;
707 fillGuideInfo(&info);
708 guideLog.startCalibration(info);
709
710 calibrationProcess->startup();
711 calibrationProcess->setGuideLog(&guideLog);
712 iterateCalibration();
713
714 return true;
715}
716
717void InternalGuider::iterateCalibration()
718{
719 if (calibrationProcess->inProgress())
720 {
721 auto const timeStep = calculateGPGTimeStep();
722 pmath->performProcessing(GUIDE_CALIBRATING, m_ImageData, m_GuideFrame, timeStep);
723
724 QString info = "";
725 if (pmath->usingSEPMultiStar())
726 {
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());
732 }
733 emit guideInfo(info);
734
735 if (pmath->isStarLost())
736 {
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."));
741 reset();
742 return;
743 }
744 }
745 double starX, starY;
746 pmath->getStarScreenPosition(&starX, &starY);
747 calibrationProcess->iterate(starX, starY);
748
749 auto status = calibrationProcess->getStatus();
750 if (status != GUIDE_CALIBRATING)
751 emit newStatus(status);
752
753 QString logStatus = calibrationProcess->getLogStatus();
754 if (logStatus.length())
755 emit newLog(logStatus);
756
757 QString updateMessage;
758 double x, y;
759 GuideInterface::CalibrationUpdateType type;
760 calibrationProcess->getCalibrationUpdate(&type, &updateMessage, &x, &y);
761 if (updateMessage.length())
762 emit calibrationUpdate(type, updateMessage, x, y);
763
764 GuideDirection pulseDirection;
765 int pulseMsecs;
766 calibrationProcess->getPulse(&pulseDirection, &pulseMsecs);
767 if (pulseDirection != NO_DIR)
768 emit newSinglePulse(pulseDirection, pulseMsecs, StartCaptureAfterPulses);
769
770 if (status == GUIDE_CALIBRATION_ERROR)
771 {
772 KSNotification::event(QLatin1String("CalibrationFailed"), i18n("Guiding calibration failed"),
773 KSNotification::Guide, KSNotification::Alert);
774 reset();
775 }
776 else if (status == GUIDE_CALIBRATION_SUCCESS)
777 {
778 KSNotification::event(QLatin1String("CalibrationSuccessful"),
779 i18n("Guiding calibration completed successfully"), KSNotification::Guide);
780 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
781 pmath->setTargetPosition(calibrationStartX, calibrationStartY);
782 reset();
783 }
784}
785
786void InternalGuider::setGuideView(const QSharedPointer<GuideView> &guideView)
787{
788 m_GuideFrame = guideView;
789}
790
791void InternalGuider::setImageData(const QSharedPointer<FITSData> &data)
792{
793 m_ImageData = data;
794 if (Options::saveGuideImages())
795 {
797 QString path = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("guide/" +
798 now.toString("yyyy-MM-dd"));
799 QDir dir;
800 dir.mkpath(path);
801 // IS8601 contains colons but they are illegal under Windows OS, so replacing them with '-'
802 // The timestamp is no longer ISO8601 but it should solve interoperality issues between different OS hosts
803 QString name = "guide_frame_" + now.toString("HH-mm-ss") + ".fits";
804 QString filename = path + QStringLiteral("/") + name;
805 m_ImageData->saveImage(filename);
806 }
807}
808
809void InternalGuider::reset()
810{
811 qCDebug(KSTARS_EKOS_GUIDE) << "Resetting internal guider...";
812 state = GUIDE_IDLE;
813
814 resetDarkGuiding();
815
816 connect(m_GuideFrame.get(), &FITSView::trackingStarSelected, this, &InternalGuider::trackingStarSelected,
818 calibrationProcess.reset();
819}
820
821bool InternalGuider::clearCalibration()
822{
823 Options::setSerializedCalibration("");
824 pmath->getMutableCalibration()->reset();
825 return true;
826}
827
828bool InternalGuider::restoreCalibration()
829{
830 bool success = Options::reuseGuideCalibration() &&
831 pmath->getMutableCalibration()->restore(
832 pierSide, Options::reverseDecOnPierSideChange(),
833 subBinX, subBinY, &mountDEC);
834 if (success)
835 emit DESwapChanged(pmath->getCalibration().declinationSwapEnabled());
836 return success;
837}
838
839void InternalGuider::setStarPosition(QVector3D &starCenter)
840{
841 pmath->setTargetPosition(starCenter.x(), starCenter.y());
842}
843
844void InternalGuider::trackingStarSelected(int x, int y)
845{
846 Q_UNUSED(x);
847 Q_UNUSED(y);
848 /*
849
850 Not sure what's going on here--manual star selection for calibration?
851 Don't really see how the logic works.
852
853 if (calibrationStage == CAL_IDLE)
854 return;
855
856 pmath->setTargetPosition(x, y);
857
858 calibrationStage = CAL_START;
859 */
860}
861
862void InternalGuider::setDECSwap(bool enable)
863{
864 pmath->getMutableCalibration()->setDeclinationSwapEnabled(enable);
865}
866
867void InternalGuider::setSquareAlgorithm(int index)
868{
869 if (index == SEP_MULTISTAR && !pmath->usingSEPMultiStar())
870 m_isFirstFrame = true;
871 pmath->setAlgorithmIndex(index);
872}
873
874bool InternalGuider::getReticleParameters(double * x, double * y)
875{
876 return pmath->getTargetPosition(x, y);
877}
878
879bool InternalGuider::setGuiderParams(double ccdPixelSizeX, double ccdPixelSizeY, double mountAperture,
880 double mountFocalLength)
881{
882 this->ccdPixelSizeX = ccdPixelSizeX;
883 this->ccdPixelSizeY = ccdPixelSizeY;
884 this->mountAperture = mountAperture;
885 this->mountFocalLength = mountFocalLength;
886 return pmath->setGuiderParameters(ccdPixelSizeX, ccdPixelSizeY, mountAperture, mountFocalLength);
887}
888
889bool InternalGuider::setFrameParams(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t binX, uint16_t binY)
890{
891 if (w <= 0 || h <= 0)
892 return false;
893
894 subX = x;
895 subY = y;
896 subW = w;
897 subH = h;
898
899 subBinX = binX;
900 subBinY = binY;
901
902 pmath->setVideoParameters(w, h, subBinX, subBinY);
903
904 return true;
905}
906
907void InternalGuider::emitAxisPulse(const cproc_out_params * out)
908{
909 double raPulse = out->pulse_length[GUIDE_RA];
910 double dePulse = out->pulse_length[GUIDE_DEC];
911
912 //If the pulse was not sent to the mount, it should have 0 value
913 if(out->pulse_dir[GUIDE_RA] == NO_DIR)
914 raPulse = 0;
915 //If the pulse was not sent to the mount, it should have 0 value
916 if(out->pulse_dir[GUIDE_DEC] == NO_DIR)
917 dePulse = 0;
918 //If the pulse was in the Negative direction, it should have a negative sign.
919 if(out->pulse_dir[GUIDE_RA] == RA_INC_DIR)
920 raPulse = -raPulse;
921 //If the pulse was in the Negative direction, it should have a negative sign.
922 if(out->pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
923 dePulse = -dePulse;
924
925 emit newAxisPulse(raPulse, dePulse);
926}
927
928bool InternalGuider::processGuiding()
929{
930 const cproc_out_params *out;
931
932 // On first frame, center the box (reticle) around the star so we do not start with an offset the results in
933 // unnecessary guiding pulses.
934 bool process = true;
935
936 if (m_isFirstFrame)
937 {
938 m_isFirstFrame = false;
939 if (state == GUIDE_GUIDING)
940 {
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);
944 else
945 {
946 // We were not able to get started.
947 process = false;
948 m_isFirstFrame = true;
949 }
950 }
951 }
952
953 if (process)
954 {
955 auto const timeStep = calculateGPGTimeStep();
956 pmath->performProcessing(state, m_ImageData, m_GuideFrame, timeStep, &guideLog);
957 if (pmath->usingSEPMultiStar())
958 {
959 QString info = "";
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());
965
966 emit guideInfo(info);
967 }
968
969 // Restart the dark-guiding timer, so we get the full interval on its 1st timeout.
970 if (this->m_darkGuideTimer->isActive())
971 this->m_darkGuideTimer->start();
972 }
973
974 if (state == GUIDE_SUSPENDED)
975 {
976 if (Options::gPGEnabled())
977 emit frameCaptureRequested();
978 return true;
979 }
980 else
981 {
982 if (pmath->isStarLost())
983 m_starLostCounter++;
984 else
985 m_starLostCounter = 0;
986 }
987
988 // do pulse
989 out = pmath->getOutputParameters();
990
991 if (isPoorGuiding(out))
992 return true;
993
994 // This is an old guide computaion that should be ignored.
995 // One-pulse-dither sends out its own pulse signal.
996 if ((state == GUIDE_DITHERING_SETTLE || state == GUIDE_DITHERING) && Options::ditherWithOnePulse())
997 return true;
998
999 bool sendPulses = !pmath->isStarLost();
1000
1001 // Send pulse if we have one active direction at least.
1002 if (sendPulses && (out->pulse_dir[GUIDE_RA] != NO_DIR || out->pulse_dir[GUIDE_DEC] != NO_DIR))
1003 {
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);
1006 }
1007 else
1008 emit frameCaptureRequested();
1009
1010 if (state == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING || state == GUIDE_DITHERING_SETTLE)
1011 return true;
1012
1013 // Hy 9/13/21: Check above just looks for GUIDE_DITHERING or GUIDE_MANUAL_DITHERING or GUIDE_DITHERING_SETTLE
1014 // but not the other dithering possibilities (error, success).
1015 // Not sure if they should be included above, so conservatively not changing the
1016 // code, but don't think they should broadcast the newAxisDelta which might
1017 // interrup a capture.
1018 if (state < GUIDE_DITHERING)
1019 emit newAxisDelta(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
1020
1021 emitAxisPulse(out);
1022 emit newAxisSigma(out->sigma[GUIDE_RA], out->sigma[GUIDE_DEC]);
1023 if (SEPMultiStarEnabled())
1024 emit newSNR(pmath->getGuideStarSNR());
1025
1026 return true;
1027}
1028
1029
1030// Here we calculate the time until the next time we will be emitting guiding corrections.
1031std::pair<Seconds, Seconds> InternalGuider::calculateGPGTimeStep()
1032{
1033 Seconds timeStep;
1034
1035 const Seconds guideDelay{(Options::guideDelay())};
1036
1037 auto const captureInterval = Seconds(m_captureTimer->intervalAsDuration()) + guideDelay;
1038 auto const darkGuideInterval = Seconds(m_darkGuideTimer->intervalAsDuration());
1039
1040 if (!Options::gPGDarkGuiding() || !isInferencePeriodFinished())
1041 {
1042 return std::pair<Seconds, Seconds>(captureInterval, captureInterval);
1043 }
1044 auto const captureTimeRemaining = Seconds(m_captureTimer->remainingTimeAsDuration()) + guideDelay;
1045 auto const darkGuideTimeRemaining = Seconds(m_darkGuideTimer->remainingTimeAsDuration());
1046 // Are both firing at the same time (or at least, both due)?
1047 if (captureTimeRemaining <= Seconds::zero()
1048 && darkGuideTimeRemaining <= Seconds::zero())
1049 {
1050 timeStep = std::min(captureInterval, darkGuideInterval);
1051 }
1052 else if (captureTimeRemaining <= Seconds::zero())
1053 {
1054 timeStep = std::min(captureInterval, darkGuideTimeRemaining);
1055 }
1056 else if (darkGuideTimeRemaining <= Seconds::zero())
1057 {
1058 timeStep = std::min(captureTimeRemaining, darkGuideInterval);
1059 }
1060 else
1061 {
1062 timeStep = std::min(captureTimeRemaining, darkGuideTimeRemaining);
1063 }
1064 return std::pair<Seconds, Seconds>(timeStep, captureInterval);
1065}
1066
1067
1068
1069void InternalGuider::darkGuide()
1070{
1071 // Only dark guide when guiding--e.g. don't dark guide if dithering.
1072 if (state != GUIDE_GUIDING)
1073 return;
1074
1075 if(Options::gPGDarkGuiding() && isInferencePeriodFinished())
1076 {
1077 const cproc_out_params *out;
1078 auto const timeStep = calculateGPGTimeStep();
1079 pmath->performDarkGuiding(state, timeStep);
1080
1081 out = pmath->getOutputParameters();
1082 emit newSinglePulse(out->pulse_dir[GUIDE_RA], out->pulse_length[GUIDE_RA], DontCaptureAfterPulses);
1083
1084 emitAxisPulse(out);
1085 }
1086}
1087
1088bool InternalGuider::isPoorGuiding(const cproc_out_params * out)
1089{
1090 double delta_rms = std::hypot(out->delta[GUIDE_RA], out->delta[GUIDE_DEC]);
1091 if (delta_rms > Options::guideMaxDeltaRMS())
1092 m_highRMSCounter++;
1093 else
1094 m_highRMSCounter = 0;
1095
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)
1101 {
1102 qCDebug(KSTARS_EKOS_GUIDE) << "m_starLostCounter" << m_starLostCounter
1103 << "m_highRMSCounter" << m_highRMSCounter
1104 << "delta_rms" << delta_rms;
1105
1106 if (m_starLostCounter > abortStarLostThreshold)
1107 emit newLog(i18n("Lost track of the guide star. Searching for guide stars..."));
1108 else
1109 emit newLog(i18n("Delta RMS threshold value exceeded. Searching for guide stars..."));
1110
1111 reacquireTimer.start();
1112 rememberState = state;
1113 state = GUIDE_REACQUIRE;
1114 emit newStatus(state);
1115 return true;
1116 }
1117 return false;
1118}
1119bool InternalGuider::selectAutoStarSEPMultistar()
1120{
1121 m_GuideFrame->updateFrame();
1122 m_DitherOrigin = QVector3D(0, 0, 0);
1123 QVector3D newStarCenter = pmath->selectGuideStar(m_ImageData);
1124 if (newStarCenter.x() >= 0)
1125 {
1126 emit newStarPosition(newStarCenter, true);
1127 return true;
1128 }
1129 return false;
1130}
1131
1132bool InternalGuider::SEPMultiStarEnabled()
1133{
1134 return Options::guideAlgorithm() == SEP_MULTISTAR;
1135}
1136
1137bool InternalGuider::selectAutoStar()
1138{
1139 m_DitherOrigin = QVector3D(0, 0, 0);
1140 if (Options::guideAlgorithm() == SEP_MULTISTAR)
1141 return selectAutoStarSEPMultistar();
1142
1143 bool useNativeDetection = false;
1144
1145 QList<Edge *> starCenters;
1146
1147 if (Options::guideAlgorithm() != SEP_THRESHOLD)
1148 starCenters = GuideAlgorithms::detectStars(m_ImageData, m_GuideFrame->getTrackingBox());
1149
1150 if (starCenters.empty())
1151 {
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);
1157
1158 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1159 m_ImageData->findStars(ALGORITHM_SEP).waitForFinished();
1160 else
1161 m_ImageData->findStars().waitForFinished();
1162
1163 starCenters = m_ImageData->getStarCenters();
1164 if (starCenters.empty())
1165 return false;
1166
1167 useNativeDetection = true;
1168 // For SEP, prefer flux total
1169 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1170 std::sort(starCenters.begin(), starCenters.end(), [](const Edge * a, const Edge * b)
1171 {
1172 return a->val > b->val;
1173 });
1174 else
1175 std::sort(starCenters.begin(), starCenters.end(), [](const Edge * a, const Edge * b)
1176 {
1177 return a->width > b->width;
1178 });
1179
1180 m_GuideFrame->setStarsEnabled(true);
1181 m_GuideFrame->updateFrame();
1182 }
1183
1184 int maxX = m_ImageData->width();
1185 int maxY = m_ImageData->height();
1186
1187 int scores[MAX_GUIDE_STARS];
1188
1189 int maxIndex = MAX_GUIDE_STARS < starCenters.count() ? MAX_GUIDE_STARS : starCenters.count();
1190
1191 for (int i = 0; i < maxIndex; i++)
1192 {
1193 int score = 100;
1194
1195 Edge *center = starCenters.at(i);
1196
1197 if (useNativeDetection)
1198 {
1199 // Severely reject stars close to edges
1200 if (center->x < (center->width * 5) || center->y < (center->width * 5) ||
1201 center->x > (maxX - center->width * 5) || center->y > (maxY - center->width * 5))
1202 score -= 1000;
1203
1204 // Reject stars bigger than square
1205 if (center->width > float(guideBoxSize) / subBinX)
1206 score -= 1000;
1207 else
1208 {
1209 if (Options::guideAlgorithm() == SEP_THRESHOLD)
1210 score += sqrt(center->val);
1211 else
1212 // Moderately favor brighter stars
1213 score += center->width * center->width;
1214 }
1215
1216 // Moderately reject stars close to other stars
1217 foreach (Edge *edge, starCenters)
1218 {
1219 if (edge == center)
1220 continue;
1221
1222 if (fabs(center->x - edge->x) < center->width * 2 && fabs(center->y - edge->y) < center->width * 2)
1223 {
1224 score -= 15;
1225 break;
1226 }
1227 }
1228 }
1229 else
1230 {
1231 score = center->val;
1232 }
1233
1234 scores[i] = score;
1235 }
1236
1237 int maxScore = -1;
1238 int maxScoreIndex = -1;
1239 for (int i = 0; i < maxIndex; i++)
1240 {
1241 if (scores[i] > maxScore)
1242 {
1243 maxScore = scores[i];
1244 maxScoreIndex = i;
1245 }
1246 }
1247
1248 if (maxScoreIndex < 0)
1249 {
1250 qCDebug(KSTARS_EKOS_GUIDE) << "No suitable star detected.";
1251 return false;
1252 }
1253
1254 QVector3D newStarCenter(starCenters[maxScoreIndex]->x, starCenters[maxScoreIndex]->y, 0);
1255
1256 if (useNativeDetection == false)
1257 qDeleteAll(starCenters);
1258
1259 emit newStarPosition(newStarCenter, true);
1260
1261 return true;
1262}
1263
1264bool InternalGuider::reacquire()
1265{
1266 bool rc = selectAutoStar();
1267 if (rc)
1268 {
1269 m_highRMSCounter = m_starLostCounter = 0;
1270 m_isFirstFrame = true;
1271 pmath->reset();
1272 // If we were in the process of dithering, wait until settle and resume
1273 if (rememberState == GUIDE_DITHERING || state == GUIDE_MANUAL_DITHERING)
1274 {
1275 if (Options::ditherSettle() > 0)
1276 {
1277 state = GUIDE_DITHERING_SETTLE;
1278 guideLog.settleStartedInfo();
1279 emit newStatus(state);
1280 }
1281
1282 QTimer::singleShot(Options::ditherSettle() * 1000, this, SLOT(setDitherSettled()));
1283 }
1284 else
1285 {
1286 state = GUIDE_GUIDING;
1287 emit newStatus(state);
1288 }
1289
1290 }
1291 else if (reacquireTimer.elapsed() > static_cast<int>(Options::guideLostStarTimeout() * 1000))
1292 {
1293 emit newLog(i18n("Failed to find any suitable guide stars. Aborting..."));
1294 abort();
1295 return false;
1296 }
1297
1298 emit frameCaptureRequested();
1299 return rc;
1300}
1301
1302void InternalGuider::fillGuideInfo(GuideLog::GuideInfo * info)
1303{
1304 // NOTE: just using the X values, phd2logview assumes x & y the same.
1305 // pixel scale in arc-sec / pixel. The 2nd and 3rd values seem redundent, but are
1306 // in the phd2 logs.
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();
1317 // Calibration values in ms/pixel, xrate is in pixels/second.
1318 info->xrate = 1000.0 / pmath->getCalibration().raPulseMillisecondsPerArcsecond();
1319 info->yrate = 1000.0 / pmath->getCalibration().decPulseMillisecondsPerArcsecond();
1320}
1321
1322void InternalGuider::updateGPGParameters()
1323{
1324 setDarkGuideTimerInterval();
1325 pmath->getGPG().updateParameters();
1326}
1327
1328void InternalGuider::resetGPG()
1329{
1330 pmath->getGPG().reset();
1331 resetDarkGuiding();
1332}
1333
1334const Calibration &InternalGuider::getCalibration() const
1335{
1336 return pmath->getCalibration();
1337}
1338}
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
Definition dms.h:390
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:83
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
iterator begin()
qsizetype count() const const
bool empty() const const
iterator end()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T dequeue()
void enqueue(const T &t)
T * get() const const
QString arg(Args &&... args) const const
qsizetype length() const const
UniqueConnection
QTextStream & center(QTextStream &stream)
void timeout()
float x() const const
float y() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.