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"
20#include "../guideview.h"
29#include <QRandomGenerator>
33double getNoise(
double maxAbsVal)
35 return (maxAbsVal - generator.
bounded(maxAbsVal * 2));
41 GuiderUtils::Vector position;
42 if (usingSEPMultiStar())
44 QRect trackingBox = guideView->getTrackingBox();
45 position = guideStars.findGuideStar(imageData, trackingBox, guideView, firstFrame);
49 position = GuideAlgorithms::findLocalStarPosition(
50 imageData, algorithm, video_width, video_height,
51 guideView->getTrackingBox());
53 if (position.x == -1 || position.y == -1)
62 starPosition = GuiderUtils::Vector(0);
63 targetPosition = GuiderUtils::Vector(0);
68 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
69 drift[GUIDE_RA] =
new double[CIRCULAR_BUFFER_SIZE];
70 drift[GUIDE_DEC] =
new double[CIRCULAR_BUFFER_SIZE];
71 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
72 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
73 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
81 delete[] drift[GUIDE_RA];
82 delete[] drift[GUIDE_DEC];
85bool cgmath::setVideoParameters(
int vid_wd,
int vid_ht,
int binX,
int binY)
87 if (vid_wd <= 0 || vid_ht <= 0)
90 video_width = vid_wd / binX;
91 video_height = vid_ht / binY;
93 calibration.setBinningUsed(binX, binY);
94 guideStars.setCalibration(calibration);
99bool cgmath::setGuiderParameters(
double ccd_pix_wd,
double ccd_pix_ht,
double guider_aperture,
double guider_focal)
105 if (guider_focal <= 0)
108 aperture = guider_aperture;
109 guideStars.setCalibration(calibration);
115void cgmath::createGuideLog()
119 QTextStream out(&logFile);
121 out <<
"Guiding rate,x15 arcsec/sec: " <<
Qt::endl;
122 out <<
"Focal,mm: " << calibration.getFocalLength() <<
Qt::endl;
123 out <<
"Aperture,mm: " << aperture <<
Qt::endl;
124 out <<
"F/D: " << calibration.getFocalLength() / aperture <<
Qt::endl;
125 out <<
"Frame #, Time Elapsed (ms), RA Error (arcsec), RA Correction (ms), RA Correction Direction, DEC Error "
126 "(arcsec), DEC Correction (ms), DEC Correction Direction"
132bool cgmath::setTargetPosition(
double x,
double y)
139 if (x >= (
double)video_width - 1)
140 x = (double)video_width - 1;
141 if (y >= (
double)video_height - 1)
142 y = (double)video_height - 1;
144 targetPosition = GuiderUtils::Vector(x, y, 0);
146 guideStars.setCalibration(calibration);
151bool cgmath::getTargetPosition(
double *x,
double *y)
const
153 *x = targetPosition.x;
154 *y = targetPosition.y;
158int cgmath::getAlgorithmIndex(
void)
const
163void cgmath::getStarScreenPosition(
double *dx,
double *dy)
const
165 *dx = starPosition.x;
166 *dy = starPosition.y;
171 iterationCounter = 0;
172 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
173 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
176 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
177 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
182void cgmath::setAlgorithmIndex(
int algorithmIndex)
184 if (algorithmIndex < 0 || algorithmIndex > SEP_MULTISTAR)
187 algorithm = algorithmIndex;
190bool cgmath::usingSEPMultiStar()
const
192 return algorithm == SEP_MULTISTAR;
195void cgmath::updateCircularBuffers(
void)
199 driftUpto[GUIDE_RA]++;
200 driftUpto[GUIDE_DEC]++;
201 if (driftUpto[GUIDE_RA] >= CIRCULAR_BUFFER_SIZE)
202 driftUpto[GUIDE_RA] = 0;
203 if (driftUpto[GUIDE_DEC] >= CIRCULAR_BUFFER_SIZE)
204 driftUpto[GUIDE_DEC] = 0;
210 iterationCounter = 0;
211 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0;
212 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0;
215 memset(drift[GUIDE_RA], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
216 memset(drift[GUIDE_DEC], 0,
sizeof(
double) * CIRCULAR_BUFFER_SIZE);
218 if (calibration.getFocalLength() > 0 && aperture > 0)
229void cgmath::suspend(
bool mode)
234bool cgmath::isSuspended()
const
239bool cgmath::isStarLost()
const
244void cgmath::setLostStar(
bool is_lost)
253 if (raDEC == GUIDE_RA)
255 else if (raDEC == GUIDE_DEC)
261const QString directionStr(GuideDirection dir)
266 return "Decrease RA";
268 return "Increase RA";
270 return "Decrease DEC";
272 return "Increase DEC";
279bool cgmath::configureInParams(Ekos::GuideState state)
281 const bool dithering = state == Ekos::GuideState::GUIDE_DITHERING;
285 in_params.proportional_gain[0] = Options::rAProportionalGain();
286 in_params.proportional_gain[1] = Options::dECProportionalGain();
288 in_params.integral_gain[0] = Options::rAIntegralGain();
289 in_params.integral_gain[1] = Options::dECIntegralGain();
292 in_params.enabled[0] = Options::rAGuideEnabled();
293 in_params.enabled[1] = Options::dECGuideEnabled();
295 in_params.min_pulse_arcsec[0] = Options::rAMinimumPulseArcSec();
296 in_params.min_pulse_arcsec[1] = Options::dECMinimumPulseArcSec();
298 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
299 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
303 in_params.enabled_axis1[0] = Options::eastRAGuideEnabled();
305 in_params.enabled_axis2[0] = Options::westRAGuideEnabled();
309 in_params.enabled_axis1[1] = Options::northDECGuideEnabled();
311 in_params.enabled_axis2[1] = Options::southDECGuideEnabled();
316 in_params.proportional_gain[0] = 1.0;
317 in_params.proportional_gain[1] = 1.0;
318 in_params.integral_gain[0] = 0.0;
319 in_params.integral_gain[1] = 0.0;
320 in_params.min_pulse_arcsec[0] = 0.0;
321 in_params.min_pulse_arcsec[1] = 0.0;
322 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec();
323 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec();
324 in_params.enabled[0] =
true;
325 in_params.enabled[1] =
true;
326 in_params.enabled_axis1[0] =
true;
327 in_params.enabled_axis2[0] =
true;
328 in_params.enabled_axis1[1] =
true;
329 in_params.enabled_axis2[1] =
true;
335void cgmath::updateOutParams(
int k,
const double arcsecDrift,
int pulseLength, GuideDirection pulseDirection)
337 out_params.pulse_dir[k] = pulseDirection;
338 out_params.pulse_length[k] = pulseLength;
339 out_params.delta[k] = arcsecDrift;
342void cgmath::outputGuideLog()
344 if (Options::guideLogging())
346 QTextStream out(&logFile);
347 out << iterationCounter <<
"," << logTime.elapsed() <<
"," << out_params.delta[0] <<
"," << out_params.pulse_length[0] <<
349 << directionStr(out_params.pulse_dir[0]) <<
"," << out_params.delta[1] <<
","
350 << out_params.pulse_length[1] <<
"," << directionStr(out_params.pulse_dir[1]) <<
Qt::endl;
354void cgmath::processAxis(
const int k,
const bool dithering,
const bool darkGuide,
const Seconds &timeStep,
358 GuideDirection pulseDirection = NO_DIR;
363 const int idx = driftUpto[k];
364 const double arcsecDrift = drift[k][idx];
366 const double pulseConverter = (k == GUIDE_RA) ?
367 calibration.raPulseMillisecondsPerArcsecond() :
368 calibration.decPulseMillisecondsPerArcsecond();
369 const double maxPulseMilliseconds = in_params.max_pulse_arcsec[k] * pulseConverter;
372 bool useGPG = !dithering && Options::gPGEnabled() && (k == GUIDE_RA) && in_params.enabled[k];
373 if (useGPG && darkGuide)
375 gpg->darkGuiding(&pulseLength, &dir, calibration, timeStep);
376 pulseDirection =
dir;
381 qCDebug(KSTARS_EKOS_GUIDE) <<
"Warning: dark guiding without GPG or while dithering.";
384 else if (useGPG && gpg->computePulse(arcsecDrift,
385 usingSEPMultiStar() ? &guideStars :
nullptr, &pulseLength, &dir, calibration, timeStep))
387 pulseDirection =
dir;
388 pulseLength = std::min(pulseLength,
static_cast<int>(maxPulseMilliseconds + 0.5));
397 drift_integral[k] = 0;
398 for (
int i = 0; i < CIRCULAR_BUFFER_SIZE; ++i)
399 drift_integral[k] += drift[k][i];
400 drift_integral[k] /= (double)CIRCULAR_BUFFER_SIZE;
402 if (in_params.integral_gain[k] > 0)
403 qCDebug(KSTARS_EKOS_GUIDE) <<
label <<
"drift[" << axisStr(k) <<
"] = " << arcsecDrift
404 <<
" integral[" << axisStr(k) <<
"] = " << drift_integral[k];
406 const double arcsecPerMsPulse = k == GUIDE_RA ? calibration.raPulseMillisecondsPerArcsecond() :
407 calibration.decPulseMillisecondsPerArcsecond();
408 const double proportionalResponse = arcsecDrift * in_params.proportional_gain[k] * arcsecPerMsPulse;
409 const double integralResponse = drift_integral[k] * in_params.integral_gain[k] * arcsecPerMsPulse;
410 pulseLength = std::min(fabs(proportionalResponse + integralResponse), maxPulseMilliseconds);
414 if (!in_params.enabled[k] ||
416 (arcsecDrift > 0 && !in_params.enabled_axis1[k]) ||
418 (arcsecDrift < 0 && !in_params.enabled_axis2[k]))
420 pulseDirection = NO_DIR;
426 const double pulseArcSec = pulseConverter > 0 ? pulseLength / pulseConverter : 0;
427 if (pulseArcSec >= in_params.min_pulse_arcsec[k])
432 pulseDirection = arcsecDrift > 0 ? RA_DEC_DIR : RA_INC_DIR;
434 pulseDirection = arcsecDrift > 0 ? DEC_DEC_DIR : DEC_INC_DIR;
437 pulseDirection = NO_DIR;
441 updateOutParams(k, arcsecDrift, pulseLength, pulseDirection);
444void cgmath::calculatePulses(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
446 const bool dithering = configureInParams(state);
448 processAxis(GUIDE_RA, dithering,
false, timeStep.first,
"Guiding:");
449 processAxis(GUIDE_DEC, dithering,
false, timeStep.second,
"Guiding:");
451 qCDebug(KSTARS_EKOS_GUIDE)
452 << QString(
"Guiding pulses: RA: %1ms %2 DEC: %3ms %4")
453 .arg(out_params.pulse_length[GUIDE_RA]).arg(directionStr(out_params.pulse_dir[GUIDE_RA]))
454 .arg(out_params.pulse_length[GUIDE_DEC]).arg(directionStr(out_params.pulse_dir[GUIDE_DEC]));
461 const std::pair<Seconds, Seconds> &timeStep, GuideLog * logger)
465 if (Options::gPGEnabled())
467 GuiderUtils::Vector guideStarPosition = findLocalStarPosition(imageData, guideView,
false);
468 if (guideStarPosition.x != -1 && !std::isnan(guideStarPosition.x))
470 gpg->suspended(guideStarPosition, targetPosition,
471 usingSEPMultiStar() ? &guideStars :
nullptr, calibration);
480 GuiderUtils::Vector starPositionArcSec, targetPositionArcSec;
483 starPosition = findLocalStarPosition(imageData, guideView,
false);
486 if (starPosition.x == -1 || std::isnan(starPosition.x))
489 if (logger !=
nullptr && state == Ekos::GUIDE_GUIDING)
491 GuideLog::GuideData data;
492 data.code = GuideLog::GuideData::NO_STAR_FOUND;
493 data.type = GuideLog::GuideData::DROP;
494 logger->addGuideData(data);
502 QVector3D starCenter(starPosition.x, starPosition.y, 0);
503 emit newStarPosition(starCenter,
true);
506 if (state == Ekos::GUIDE_CALIBRATING)
509 if (state == Ekos::GUIDE_GUIDING && (targetPosition.x <= 0.0 || targetPosition.y <= 0.0))
511 qCDebug(KSTARS_EKOS_GUIDE) <<
"Guiding with target 0.0 -- something's wrong!!!!!!!!!!!";
512 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
514 out_params.pulse_dir[k] = NO_DIR;
515 out_params.pulse_length[k] = 0;
516 out_params.delta[k] = 0;
524 starPositionArcSec = calibration.convertToArcseconds(starPosition);
525 targetPositionArcSec = calibration.convertToArcseconds(targetPosition);
528 const GuiderUtils::Vector star_xy_arcsec_drift = starPositionArcSec - targetPositionArcSec;
529 const GuiderUtils::Vector star_drift = calibration.rotateToRaDec(star_xy_arcsec_drift);
535 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = star_drift.x;
536 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = star_drift.y;
538 qCDebug(KSTARS_EKOS_GUIDE)
539 << QString(
"Star %1 %2 a-s %3 %4 Target %5 %6 a-s %7 %8 Drift: RA %9 DEC %10")
540 .arg(starPosition.x, 0,
'f', 1) .arg(starPosition.y, 0,
'f', 1)
541 .arg(starPositionArcSec.x, 0,
'f', 1) .arg(starPositionArcSec.y, 0,
'f', 1)
542 .arg(targetPosition.x, 0,
'f', 1) .arg(targetPosition.y, 0,
'f', 1)
543 .arg(targetPositionArcSec.x, 0,
'f', 1).arg(targetPositionArcSec.y, 0,
'f', 1)
544 .arg(star_drift.x, 0,
'f', 2) .arg(star_drift.y, 0,
'f', 2);
546 if (state == Ekos::GUIDE_GUIDING && usingSEPMultiStar())
548 double multiStarRADrift, multiStarDECDrift;
549 if (guideStars.getDrift(sqrt(star_drift.x * star_drift.x + star_drift.y * star_drift.y),
550 targetPosition.x, targetPosition.y,
551 &multiStarRADrift, &multiStarDECDrift))
553#ifdef DEBUG_ADD_NOISE
554 const double raNoise = getNoise(.75);
555 const double decNoise = getNoise(.75);
556 multiStarRADrift += raNoise;
557 multiStarDECDrift += decNoise;
558 qCDebug(KSTARS_EKOS_GUIDE)
559 <<
"Added guide noise arcsecs" <<
"RA" << raNoise <<
"DEC" << decNoise;
560 fprintf(stderr,
"Add guide noise arcsecs %.1f %.1f\n", raNoise, decNoise);
562 qCDebug(KSTARS_EKOS_GUIDE) << QString(
"MultiStar drift: RA %1 DEC %2")
563 .arg(multiStarRADrift, 0,
'f', 2)
564 .arg(multiStarDECDrift, 0,
'f', 2);
565 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = multiStarRADrift;
566 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = multiStarDECDrift;
570 qCDebug(KSTARS_EKOS_GUIDE) <<
"MultiStar: failed, fell back to guide star";
576 const double raDrift = drift[GUIDE_RA][driftUpto[GUIDE_RA]];
577 const double decDrift = drift[GUIDE_DEC][driftUpto[GUIDE_DEC]];
580 calculatePulses(state, timeStep);
582 if (state == Ekos::GUIDE_GUIDING)
586 updateCircularBuffers();
588 qCDebug(KSTARS_EKOS_GUIDE) << QString(
"performProcessing took %1s").arg(timer.
elapsed() / 1000.0, 0,
'f', 3);
590 if (logger !=
nullptr)
592 GuideLog::GuideData data;
593 data.type = GuideLog::GuideData::MOUNT;
596 data.dx = starPosition.x - targetPosition.x;
597 data.dy = starPosition.y - targetPosition.y;
599 calibration.convertToPixels(-raDrift, -decDrift, &data.raDistance, &data.decDistance);
601 const double raGuideFactor = out_params.pulse_dir[GUIDE_RA] == NO_DIR ?
602 0 : (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR ? -1.0 : 1.0);
603 const double decGuideFactor = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ?
604 0 : (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR ? 1.0 : -1.0);
608 data.raGuideDistance = calibration.xPixelsPerArcsecond() * raGuideFactor * out_params.pulse_length[GUIDE_RA] /
609 calibration.raPulseMillisecondsPerArcsecond();
610 data.decGuideDistance = calibration.yPixelsPerArcsecond() * decGuideFactor * out_params.pulse_length[GUIDE_DEC] /
611 calibration.decPulseMillisecondsPerArcsecond();
613 data.raDuration = out_params.pulse_dir[GUIDE_RA] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_RA];
614 data.raDirection = out_params.pulse_dir[GUIDE_RA];
615 data.decDuration = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_DEC];
616 data.decDirection = out_params.pulse_dir[GUIDE_DEC];
617 data.code = GuideLog::GuideData::NO_ERRORS;
618 data.snr = guideStars.getGuideStarSNR();
619 data.mass = guideStars.getGuideStarMass();
621 logger->addGuideData(data);
625void cgmath::performDarkGuiding(Ekos::GuideState state,
const std::pair<Seconds, Seconds> &timeStep)
628 const bool dithering = configureInParams(state);
631 processAxis(GUIDE_RA, dithering,
true, timeStep.first,
"Dark Guiding:");
632 qCDebug(KSTARS_EKOS_GUIDE)
633 << QString(
"Dark Guiding pulses: RA: %1ms %2")
634 .arg(out_params.pulse_length[GUIDE_RA]).arg(directionStr(out_params.pulse_dir[GUIDE_RA]));
638 updateOutParams(GUIDE_DEC, 0, 0, NO_DIR);
643void cgmath::emitStats()
646 if (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR)
647 pulseRA = out_params.pulse_length[GUIDE_RA];
648 else if (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR)
649 pulseRA = -out_params.pulse_length[GUIDE_RA];
651 if (out_params.pulse_dir[GUIDE_DEC] == DEC_DEC_DIR)
652 pulseDEC = -out_params.pulse_length[GUIDE_DEC];
653 else if (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR)
654 pulseDEC = out_params.pulse_length[GUIDE_DEC];
656 const bool hasGuidestars = usingSEPMultiStar();
657 const double snr = hasGuidestars ? guideStars.getGuideStarSNR() : 0;
658 const double skyBG = hasGuidestars ? guideStars.skybackground().mean : 0;
659 const int numStars = hasGuidestars ? guideStars.skybackground().starsDetected : 0;
662 emit guideStats(-out_params.delta[GUIDE_RA], out_params.delta[GUIDE_DEC],
663 pulseRA, pulseDEC, snr, skyBG, numStars);
666void cgmath::calculateRmsError(
void)
671 if (iterationCounter == 0)
674 int count = std::min(iterationCounter,
static_cast<unsigned int>(CIRCULAR_BUFFER_SIZE));
675 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
681 for (
int i = 0; i < count; ++i)
684 sumYSq += drift[k][i] * drift[k][i];
688 double variance = (count * sumYSq - sumY * sumY) / (count * count);
690 out_params.sigma[k] = 2 * sqrt(variance);
692 out_params.sigma[k] = 0.0;
699 return guideStars.selectGuideStar(imageData);
702double cgmath::getGuideStarSNR()
704 return guideStars.getGuideStarSNR();
708cproc_in_params::cproc_in_params()
713void cproc_in_params::reset(
void)
717 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
720 integral_gain[k] = 0;
721 max_pulse_arcsec[k] = 5000;
722 min_pulse_arcsec[k] = 0;
726cproc_out_params::cproc_out_params()
731void cproc_out_params::reset(
void)
733 for (
int k = GUIDE_RA; k <= GUIDE_DEC; k++)
736 pulse_dir[k] = NO_DIR;
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString label(StandardShortcut id)
QCA_EXPORT Logger * logger()
qint64 elapsed() const const
double bounded(double highest)
QTextStream & endl(QTextStream &stream)