13 #include "fitsviewer/fitsdata.h"
14 #include "fitsviewer/fitsview.h"
15 #include "auxiliary/kspaths.h"
16 #include "ekos_guide_debug.h"
17 #include "ekos/auxiliary/stellarsolverprofileeditor.h"
18 #include "guidealgorithms.h"
30 GuiderUtils::Vector position;
31 if (usingSEPMultiStar())
33 QRect trackingBox = guideView->getTrackingBox();
34 position = guideStars.findGuideStar(imageData, trackingBox, guideView, firstFrame);
38 position = GuideAlgorithms::findLocalStarPosition(
39 imageData, algorithm, video_width, video_height,
40 guideView->getTrackingBox());
42 if (position.x == -1 || position.y == -1)
51 starPosition = GuiderUtils::Vector(0);
52 targetPosition = GuiderUtils::Vector(0);
57 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
58 drift[GUIDE_RA] =
new double[CIRCULAR_BUFFER_SIZE];
59 drift[GUIDE_DEC] =
new double[CIRCULAR_BUFFER_SIZE];
60 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
61 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
62 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
70 delete[] drift[GUIDE_RA];
71 delete[] drift[GUIDE_DEC];
74 bool cgmath::setVideoParameters(
int vid_wd,
int vid_ht,
int binX,
int binY)
76 if (vid_wd <= 0 || vid_ht <= 0)
79 video_width = vid_wd / binX;
80 video_height = vid_ht / binY;
82 calibration.setBinningUsed(binX, binY);
83 guideStars.setCalibration(calibration);
88 bool cgmath::setGuiderParameters(
double ccd_pix_wd,
double ccd_pix_ht,
double guider_aperture,
double guider_focal)
94 if (guider_focal <= 0)
97 aperture = guider_aperture;
98 guideStars.setCalibration(calibration);
104 void cgmath::createGuideLog()
110 out <<
"Guiding rate,x15 arcsec/sec: " <<
Qt::endl;
111 out <<
"Focal,mm: " << calibration.getFocalLength() <<
Qt::endl;
112 out <<
"Aperture,mm: " << aperture <<
Qt::endl;
113 out <<
"F/D: " << calibration.getFocalLength() / aperture <<
Qt::endl;
114 out <<
"Frame #, Time Elapsed (ms), RA Error (arcsec), RA Correction (ms), RA Correction Direction, DEC Error "
115 "(arcsec), DEC Correction (ms), DEC Correction Direction"
121 bool cgmath::setTargetPosition(
double x,
double y)
128 if (x >= (
double)video_width - 1)
129 x = (double)video_width - 1;
130 if (y >= (
double)video_height - 1)
131 y = (
double)video_height - 1;
133 targetPosition = GuiderUtils::Vector(x, y, 0);
135 guideStars.setCalibration(calibration);
140 bool cgmath::getTargetPosition(
double *x,
double *y)
const
142 *x = targetPosition.x;
143 *y = targetPosition.y;
147 int cgmath::getAlgorithmIndex(
void)
const
152 void cgmath::getStarScreenPosition(
double *dx,
double *dy)
const
154 *dx = starPosition.x;
155 *dy = starPosition.y;
160 iterationCounter = 0;
161 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
162 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
165 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
166 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
171 void cgmath::setAlgorithmIndex(
int algorithmIndex)
173 if (algorithmIndex < 0 || algorithmIndex > SEP_MULTISTAR)
176 algorithm = algorithmIndex;
179 bool cgmath::usingSEPMultiStar()
const
181 return algorithm == SEP_MULTISTAR;
184 void cgmath::updateCircularBuffers(
void)
188 driftUpto[GUIDE_RA]++;
189 driftUpto[GUIDE_DEC]++;
190 if (driftUpto[GUIDE_RA] >= CIRCULAR_BUFFER_SIZE)
191 driftUpto[GUIDE_RA] = 0;
192 if (driftUpto[GUIDE_DEC] >= CIRCULAR_BUFFER_SIZE)
193 driftUpto[GUIDE_DEC] = 0;
199 iterationCounter = 0;
200 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
201 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
204 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
205 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
207 if (calibration.getFocalLength() > 0 && aperture > 0)
218 void cgmath::suspend(
bool mode)
223 bool cgmath::isSuspended()
const
228 bool cgmath::isStarLost()
const
233 void cgmath::setLostStar(
bool is_lost)
242 if (raDEC == GUIDE_RA)
244 else if (raDEC == GUIDE_DEC)
250 const QString directionStr(GuideDirection dir)
255 return "Decrease RA";
257 return "Increase RA";
259 return "Decrease DEC";
261 return "Increase DEC";
268 bool cgmath::configureInParams(Ekos::GuideState state)
270 const bool dithering = state == Ekos::GuideState::GUIDE_DITHERING;
274 in_params.proportional_gain[0] = Options::rAProportionalGain();
275 in_params.proportional_gain[1] = Options::dECProportionalGain();
277 in_params.integral_gain[0] = Options::rAIntegralGain();
278 in_params.integral_gain[1] = Options::dECIntegralGain();
281 in_params.enabled[0] = Options::rAGuideEnabled();
282 in_params.enabled[1] = Options::dECGuideEnabled();
284 in_params.min_pulse_arcsec[0] = Options::rAMinimumPulseArcSec();
285 in_params.min_pulse_arcsec[1] = Options::dECMinimumPulseArcSec();
287 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
288 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
292 in_params.enabled_axis1[0] = Options::eastRAGuideEnabled();
294 in_params.enabled_axis2[0] = Options::westRAGuideEnabled();
298 in_params.enabled_axis1[1] = Options::northDECGuideEnabled();
300 in_params.enabled_axis2[1] = Options::southDECGuideEnabled();
305 in_params.proportional_gain[0] = 1.0;
306 in_params.proportional_gain[1] = 1.0;
307 in_params.integral_gain[0] = 0.0;
308 in_params.integral_gain[1] = 0.0;
309 in_params.min_pulse_arcsec[0] = 0.0;
310 in_params.min_pulse_arcsec[1] = 0.0;
311 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
312 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
313 in_params.enabled[0] =
true;
314 in_params.enabled[1] =
true;
315 in_params.enabled_axis1[0] =
true;
316 in_params.enabled_axis2[0] =
true;
317 in_params.enabled_axis1[1] =
true;
318 in_params.enabled_axis2[1] =
true;
324 void cgmath::updateOutParams(
int k,
const double arcsecDrift,
int pulseLength, GuideDirection pulseDirection)
326 out_params.pulse_dir[k] = pulseDirection;
327 out_params.pulse_length[k] = pulseLength;
328 out_params.delta[k] = arcsecDrift;
331 void cgmath::outputGuideLog()
333 if (Options::guideLogging())
336 out << iterationCounter <<
"," << logTime.elapsed() <<
"," << out_params.delta[0] <<
"," << out_params.pulse_length[0] <<
338 << directionStr(out_params.pulse_dir[0]) <<
"," << out_params.delta[1] <<
","
339 << out_params.pulse_length[1] <<
"," << directionStr(out_params.pulse_dir[1]) <<
Qt::endl;
343 void cgmath::processAxis(
const int k,
const bool dithering,
const bool darkGuide,
const Seconds &timeStep,
347 GuideDirection pulseDirection = NO_DIR;
352 const int idx = driftUpto[k];
353 const double arcsecDrift = drift[k][idx];
355 const double pulseConverter = (k == GUIDE_RA) ?
356 calibration.raPulseMillisecondsPerArcsecond() :
357 calibration.decPulseMillisecondsPerArcsecond();
358 const double maxPulseMilliseconds = in_params.max_pulse_arcsec[k] * pulseConverter;
361 bool useGPG = !dithering && Options::gPGEnabled() && (k == GUIDE_RA) && in_params.enabled[k];
362 if (useGPG && darkGuide)
364 gpg->darkGuiding(&pulseLength, &dir, calibration, timeStep);
365 pulseDirection =
dir;
370 qCDebug(KSTARS_EKOS_GUIDE) <<
"Warning: dark guiding without GPG or while dithering.";
373 else if (useGPG && gpg->computePulse(arcsecDrift,
374 usingSEPMultiStar() ? &guideStars :
nullptr, &pulseLength, &dir, calibration, timeStep))
376 pulseDirection =
dir;
377 pulseLength = std::min(pulseLength,
static_cast<int>(maxPulseMilliseconds + 0.5));
386 drift_integral[k] = 0;
387 for (
int i = 0; i < CIRCULAR_BUFFER_SIZE; ++i)
388 drift_integral[k] += drift[k][i];
389 drift_integral[k] /= (double)CIRCULAR_BUFFER_SIZE;
391 if (in_params.integral_gain[k] > 0)
392 qCDebug(KSTARS_EKOS_GUIDE) <<
label <<
"drift[" << axisStr(k) <<
"] = " << arcsecDrift
393 <<
" integral[" << axisStr(k) <<
"] = " << drift_integral[k];
395 const double arcsecPerMsPulse = k == GUIDE_RA ? calibration.raPulseMillisecondsPerArcsecond() :
396 calibration.decPulseMillisecondsPerArcsecond();
397 const double proportionalResponse = arcsecDrift * in_params.proportional_gain[k] * arcsecPerMsPulse;
398 const double integralResponse = drift_integral[k] * in_params.integral_gain[k] * arcsecPerMsPulse;
399 pulseLength = std::min(fabs(proportionalResponse + integralResponse), maxPulseMilliseconds);
403 if (!in_params.enabled[k] ||
405 (arcsecDrift > 0 && !in_params.enabled_axis1[k]) ||
407 (arcsecDrift < 0 && !in_params.enabled_axis2[k]))
409 pulseDirection = NO_DIR;
415 const double pulseArcSec = pulseConverter > 0 ? pulseLength / pulseConverter : 0;
416 if (pulseArcSec >= in_params.min_pulse_arcsec[k])
419 pulseDirection = arcsecDrift > 0 ? RA_DEC_DIR : RA_INC_DIR;
421 pulseDirection = arcsecDrift > 0 ? DEC_INC_DIR : DEC_DEC_DIR;
424 pulseDirection = NO_DIR;
428 updateOutParams(k, arcsecDrift, pulseLength, pulseDirection);
431 void cgmath::calculatePulses(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
433 const bool dithering = configureInParams(state);
435 processAxis(GUIDE_RA, dithering,
false, timeStep.first,
"Guiding:");
436 processAxis(GUIDE_DEC, dithering,
false, timeStep.second,
"Guiding:");
438 qCDebug(KSTARS_EKOS_GUIDE)
439 <<
QString(
"Guiding pulses: RA: %1ms %2 DEC: %3ms %4")
440 .
arg(out_params.pulse_length[GUIDE_RA]).
arg(directionStr(out_params.pulse_dir[GUIDE_RA]))
441 .
arg(out_params.pulse_length[GUIDE_DEC]).
arg(directionStr(out_params.pulse_dir[GUIDE_DEC]));
448 const std::pair<Seconds, Seconds> &timeStep, GuideLog * logger)
452 if (Options::gPGEnabled())
454 GuiderUtils::Vector guideStarPosition = findLocalStarPosition(imageData, guideView,
false);
455 if (guideStarPosition.x != -1 && !std::isnan(guideStarPosition.x))
457 gpg->suspended(guideStarPosition, targetPosition,
458 usingSEPMultiStar() ? &guideStars :
nullptr, calibration);
465 GuiderUtils::Vector starPositionArcSec, targetPositionArcSec;
468 starPosition = findLocalStarPosition(imageData, guideView,
false);
471 if (starPosition.x == -1 || std::isnan(starPosition.x))
474 if (logger !=
nullptr && state == Ekos::GUIDE_GUIDING)
476 GuideLog::GuideData data;
477 data.code = GuideLog::GuideData::NO_STAR_FOUND;
478 data.type = GuideLog::GuideData::DROP;
479 logger->addGuideData(data);
487 QVector3D starCenter(starPosition.x, starPosition.y, 0);
488 emit newStarPosition(starCenter,
true);
491 if (state == Ekos::GUIDE_CALIBRATING)
494 if (state == Ekos::GUIDE_GUIDING && (targetPosition.x <= 0.0 || targetPosition.y <= 0.0))
496 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding with target 0.0 -- something's wrong!!!!!!!!!!!";
497 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
499 out_params.pulse_dir[k] = NO_DIR;
500 out_params.pulse_length[k] = 0;
501 out_params.delta[k] = 0;
509 starPositionArcSec = calibration.convertToArcseconds(starPosition);
510 targetPositionArcSec = calibration.convertToArcseconds(targetPosition);
513 const GuiderUtils::Vector star_xy_arcsec_drift = starPositionArcSec - targetPositionArcSec;
514 const GuiderUtils::Vector star_drift = calibration.rotateToRaDec(star_xy_arcsec_drift);
520 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = star_drift.x;
521 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = star_drift.y;
523 qCDebug(KSTARS_EKOS_GUIDE)
524 <<
QString(
"Star %1 %2 a-s %3 %4 Target %5 %6 a-s %7 %8 Drift: RA %9 DEC %10")
525 .
arg(starPosition.x, 0,
'f', 1) .
arg(starPosition.y, 0,
'f', 1)
526 .
arg(starPositionArcSec.x, 0,
'f', 1) .
arg(starPositionArcSec.y, 0,
'f', 1)
527 .
arg(targetPosition.x, 0,
'f', 1) .
arg(targetPosition.y, 0,
'f', 1)
528 .
arg(targetPositionArcSec.x, 0,
'f', 1).
arg(targetPositionArcSec.y, 0,
'f', 1)
529 .
arg(star_drift.x, 0,
'f', 2) .
arg(star_drift.y, 0,
'f', 2);
531 if (state == Ekos::GUIDE_GUIDING && usingSEPMultiStar())
533 double multiStarRADrift, multiStarDECDrift;
534 if (guideStars.getDrift(sqrt(star_drift.x * star_drift.x + star_drift.y * star_drift.y),
535 targetPosition.x, targetPosition.y,
536 &multiStarRADrift, &multiStarDECDrift))
538 qCDebug(KSTARS_EKOS_GUIDE) <<
QString(
"MultiStar drift: RA %1 DEC %2")
539 .
arg(multiStarRADrift, 0,
'f', 2)
540 .
arg(multiStarDECDrift, 0,
'f', 2);
541 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = multiStarRADrift;
542 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = multiStarDECDrift;
546 qCDebug(KSTARS_EKOS_GUIDE) <<
"MultiStar: failed, fell back to guide star";
552 const double raDrift = drift[GUIDE_RA][driftUpto[GUIDE_RA]];
553 const double decDrift = drift[GUIDE_DEC][driftUpto[GUIDE_DEC]];
556 calculatePulses(state, timeStep);
558 if (state == Ekos::GUIDE_GUIDING)
562 updateCircularBuffers();
565 if (logger !=
nullptr)
567 GuideLog::GuideData data;
568 data.type = GuideLog::GuideData::MOUNT;
571 data.dx = starPosition.x - targetPosition.x;
572 data.dy = starPosition.y - targetPosition.y;
574 calibration.convertToPixels(-raDrift, -decDrift, &data.raDistance, &data.decDistance);
576 const double raGuideFactor = out_params.pulse_dir[GUIDE_RA] == NO_DIR ?
577 0 : (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR ? -1.0 : 1.0);
578 const double decGuideFactor = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ?
579 0 : (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR ? -1.0 : 1.0);
583 data.raGuideDistance = calibration.xPixelsPerArcsecond() * raGuideFactor * out_params.pulse_length[GUIDE_RA] /
584 calibration.raPulseMillisecondsPerArcsecond();
585 data.decGuideDistance = calibration.yPixelsPerArcsecond() * decGuideFactor * out_params.pulse_length[GUIDE_DEC] /
586 calibration.decPulseMillisecondsPerArcsecond();
588 data.raDuration = out_params.pulse_dir[GUIDE_RA] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_RA];
589 data.raDirection = out_params.pulse_dir[GUIDE_RA];
590 data.decDuration = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_DEC];
591 data.decDirection = out_params.pulse_dir[GUIDE_DEC];
592 data.code = GuideLog::GuideData::NO_ERRORS;
593 data.snr = guideStars.getGuideStarSNR();
594 data.mass = guideStars.getGuideStarMass();
596 logger->addGuideData(data);
600 void cgmath::performDarkGuiding(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
603 const bool dithering = configureInParams(state);
606 processAxis(GUIDE_RA, dithering,
true, timeStep.first,
"Dark Guiding:");
607 qCDebug(KSTARS_EKOS_GUIDE)
608 <<
QString(
"Dark Guiding pulses: RA: %1ms %2")
609 .
arg(out_params.pulse_length[GUIDE_RA]).
arg(directionStr(out_params.pulse_dir[GUIDE_RA]));
613 updateOutParams(GUIDE_DEC, 0, 0, NO_DIR);
618 void cgmath::emitStats()
621 if (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR)
622 pulseRA = out_params.pulse_length[GUIDE_RA];
623 else if (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR)
624 pulseRA = -out_params.pulse_length[GUIDE_RA];
626 if (out_params.pulse_dir[GUIDE_DEC] == DEC_DEC_DIR)
627 pulseDEC = -out_params.pulse_length[GUIDE_DEC];
628 else if (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
629 pulseDEC = out_params.pulse_length[GUIDE_DEC];
631 const bool hasGuidestars = usingSEPMultiStar();
632 const double snr = hasGuidestars ? guideStars.getGuideStarSNR() : 0;
633 const double skyBG = hasGuidestars ? guideStars.skybackground().mean : 0;
634 const int numStars = hasGuidestars ? guideStars.skybackground().starsDetected : 0;
636 emit guideStats(-out_params.delta[GUIDE_RA], -out_params.delta[GUIDE_DEC],
637 pulseRA, pulseDEC, snr, skyBG, numStars);
640 void cgmath::calculateRmsError(
void)
645 if (iterationCounter == 0)
648 int count = std::min(iterationCounter,
static_cast<unsigned int>(CIRCULAR_BUFFER_SIZE));
649 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
652 for (
int i = 0; i < count; ++i)
653 sqr_avg += drift[k][i] * drift[k][i];
655 out_params.sigma[k] = sqrt(sqr_avg / (
double)count);
662 return guideStars.selectGuideStar(imageData);
665 double cgmath::getGuideStarSNR()
667 return guideStars.getGuideStarSNR();
671 cproc_in_params::cproc_in_params()
676 void cproc_in_params::reset(
void)
680 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
683 integral_gain[k] = 0;
684 max_pulse_arcsec[k] = 5000;
685 min_pulse_arcsec[k] = 0;
689 cproc_out_params::cproc_out_params()
694 void cproc_out_params::reset(
void)
696 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
699 pulse_dir[k] = NO_DIR;