File indexing completed on 2024-05-12 15:23:41
0001 /* 0002 SPDX-FileCopyrightText: 2012 Andrew Stepanenko 0003 0004 Modified by Jasem Mutlaq <mutlaqja@ikarustech.com> for KStars: 0005 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "gmath.h" 0011 0012 #include "Options.h" 0013 #include "fitsviewer/fitsdata.h" 0014 #include "fitsviewer/fitsview.h" 0015 #include "auxiliary/kspaths.h" 0016 #include "ekos_guide_debug.h" 0017 #include "ekos/auxiliary/stellarsolverprofileeditor.h" 0018 #include "guidealgorithms.h" 0019 #include "guidelog.h" 0020 #include "../guideview.h" 0021 0022 #include <QVector3D> 0023 #include <cmath> 0024 #include <set> 0025 0026 // Qt version calming 0027 #include <qtendl.h> 0028 0029 GuiderUtils::Vector cgmath::findLocalStarPosition(QSharedPointer<FITSData> &imageData, 0030 QSharedPointer<GuideView> &guideView, bool firstFrame) 0031 { 0032 GuiderUtils::Vector position; 0033 if (usingSEPMultiStar()) 0034 { 0035 QRect trackingBox = guideView->getTrackingBox(); 0036 position = guideStars.findGuideStar(imageData, trackingBox, guideView, firstFrame); 0037 0038 } 0039 else 0040 position = GuideAlgorithms::findLocalStarPosition( 0041 imageData, algorithm, video_width, video_height, 0042 guideView->getTrackingBox()); 0043 0044 if (position.x == -1 || position.y == -1) 0045 setLostStar(true); 0046 return position; 0047 } 0048 0049 0050 cgmath::cgmath() : QObject() 0051 { 0052 // sky coord. system vars. 0053 starPosition = GuiderUtils::Vector(0); 0054 targetPosition = GuiderUtils::Vector(0); 0055 0056 // processing 0057 in_params.reset(); 0058 out_params.reset(); 0059 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0; 0060 drift[GUIDE_RA] = new double[CIRCULAR_BUFFER_SIZE]; 0061 drift[GUIDE_DEC] = new double[CIRCULAR_BUFFER_SIZE]; 0062 memset(drift[GUIDE_RA], 0, sizeof(double) * CIRCULAR_BUFFER_SIZE); 0063 memset(drift[GUIDE_DEC], 0, sizeof(double) * CIRCULAR_BUFFER_SIZE); 0064 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0; 0065 0066 logFile.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("guide_log.txt")); 0067 gpg.reset(new GPG()); 0068 } 0069 0070 cgmath::~cgmath() 0071 { 0072 delete[] drift[GUIDE_RA]; 0073 delete[] drift[GUIDE_DEC]; 0074 } 0075 0076 bool cgmath::setVideoParameters(int vid_wd, int vid_ht, int binX, int binY) 0077 { 0078 if (vid_wd <= 0 || vid_ht <= 0) 0079 return false; 0080 0081 video_width = vid_wd / binX; 0082 video_height = vid_ht / binY; 0083 0084 calibration.setBinningUsed(binX, binY); 0085 guideStars.setCalibration(calibration); 0086 0087 return true; 0088 } 0089 0090 bool cgmath::setGuiderParameters(double ccd_pix_wd, double ccd_pix_ht, double guider_aperture, double guider_focal) 0091 { 0092 if (ccd_pix_wd < 0) 0093 ccd_pix_wd = 0; 0094 if (ccd_pix_ht < 0) 0095 ccd_pix_ht = 0; 0096 if (guider_focal <= 0) 0097 guider_focal = 1; 0098 0099 aperture = guider_aperture; 0100 guideStars.setCalibration(calibration); 0101 0102 return true; 0103 } 0104 0105 // This logging will be removed in favor of guidelog.h. 0106 void cgmath::createGuideLog() 0107 { 0108 logFile.close(); 0109 logFile.open(QIODevice::WriteOnly | QIODevice::Text); 0110 QTextStream out(&logFile); 0111 0112 out << "Guiding rate,x15 arcsec/sec: " << Qt::endl; 0113 out << "Focal,mm: " << calibration.getFocalLength() << Qt::endl; 0114 out << "Aperture,mm: " << aperture << Qt::endl; 0115 out << "F/D: " << calibration.getFocalLength() / aperture << Qt::endl; 0116 out << "Frame #, Time Elapsed (ms), RA Error (arcsec), RA Correction (ms), RA Correction Direction, DEC Error " 0117 "(arcsec), DEC Correction (ms), DEC Correction Direction" 0118 << Qt::endl; 0119 0120 logTime.restart(); 0121 } 0122 0123 bool cgmath::setTargetPosition(double x, double y) 0124 { 0125 // check frame ranges 0126 if (x < 0) 0127 x = 0; 0128 if (y < 0) 0129 y = 0; 0130 if (x >= (double)video_width - 1) 0131 x = (double)video_width - 1; 0132 if (y >= (double)video_height - 1) 0133 y = (double)video_height - 1; 0134 0135 targetPosition = GuiderUtils::Vector(x, y, 0); 0136 0137 guideStars.setCalibration(calibration); 0138 0139 return true; 0140 } 0141 0142 bool cgmath::getTargetPosition(double *x, double *y) const 0143 { 0144 *x = targetPosition.x; 0145 *y = targetPosition.y; 0146 return true; 0147 } 0148 0149 int cgmath::getAlgorithmIndex(void) const 0150 { 0151 return algorithm; 0152 } 0153 0154 void cgmath::getStarScreenPosition(double *dx, double *dy) const 0155 { 0156 *dx = starPosition.x; 0157 *dy = starPosition.y; 0158 } 0159 0160 bool cgmath::reset() 0161 { 0162 iterationCounter = 0; 0163 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0; 0164 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0; 0165 out_params.reset(); 0166 0167 memset(drift[GUIDE_RA], 0, sizeof(double) * CIRCULAR_BUFFER_SIZE); 0168 memset(drift[GUIDE_DEC], 0, sizeof(double) * CIRCULAR_BUFFER_SIZE); 0169 0170 return true; 0171 } 0172 0173 void cgmath::setAlgorithmIndex(int algorithmIndex) 0174 { 0175 if (algorithmIndex < 0 || algorithmIndex > SEP_MULTISTAR) 0176 return; 0177 0178 algorithm = algorithmIndex; 0179 } 0180 0181 bool cgmath::usingSEPMultiStar() const 0182 { 0183 return algorithm == SEP_MULTISTAR; 0184 } 0185 0186 void cgmath::updateCircularBuffers(void) 0187 { 0188 iterationCounter++; 0189 0190 driftUpto[GUIDE_RA]++; 0191 driftUpto[GUIDE_DEC]++; 0192 if (driftUpto[GUIDE_RA] >= CIRCULAR_BUFFER_SIZE) 0193 driftUpto[GUIDE_RA] = 0; 0194 if (driftUpto[GUIDE_DEC] >= CIRCULAR_BUFFER_SIZE) 0195 driftUpto[GUIDE_DEC] = 0; 0196 } 0197 0198 //-------------------- Processing --------------------------- 0199 void cgmath::start() 0200 { 0201 iterationCounter = 0; 0202 driftUpto[GUIDE_RA] = driftUpto[GUIDE_DEC] = 0; 0203 drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0; 0204 out_params.reset(); 0205 0206 memset(drift[GUIDE_RA], 0, sizeof(double) * CIRCULAR_BUFFER_SIZE); 0207 memset(drift[GUIDE_DEC], 0, sizeof(double) * CIRCULAR_BUFFER_SIZE); 0208 0209 if (calibration.getFocalLength() > 0 && aperture > 0) 0210 createGuideLog(); 0211 0212 gpg->reset(); 0213 } 0214 0215 void cgmath::abort() 0216 { 0217 guideStars.reset(); 0218 } 0219 0220 void cgmath::suspend(bool mode) 0221 { 0222 suspended = mode; 0223 } 0224 0225 bool cgmath::isSuspended() const 0226 { 0227 return suspended; 0228 } 0229 0230 bool cgmath::isStarLost() const 0231 { 0232 return lost_star; 0233 } 0234 0235 void cgmath::setLostStar(bool is_lost) 0236 { 0237 lost_star = is_lost; 0238 } 0239 0240 namespace 0241 { 0242 QString axisStr(int raDEC) 0243 { 0244 if (raDEC == GUIDE_RA) 0245 return "RA"; 0246 else if (raDEC == GUIDE_DEC) 0247 return "DEC"; 0248 else 0249 return "???"; 0250 } 0251 0252 const QString directionStr(GuideDirection dir) 0253 { 0254 switch (dir) 0255 { 0256 case RA_DEC_DIR: 0257 return "Decrease RA"; 0258 case RA_INC_DIR: 0259 return "Increase RA"; 0260 case DEC_DEC_DIR: 0261 return "Decrease DEC"; 0262 case DEC_INC_DIR: 0263 return "Increase DEC"; 0264 default: 0265 return "NO DIR"; 0266 } 0267 } 0268 } // namespace 0269 0270 bool cgmath::configureInParams(Ekos::GuideState state) 0271 { 0272 const bool dithering = state == Ekos::GuideState::GUIDE_DITHERING; 0273 0274 if (!dithering) 0275 { 0276 in_params.proportional_gain[0] = Options::rAProportionalGain(); 0277 in_params.proportional_gain[1] = Options::dECProportionalGain(); 0278 0279 in_params.integral_gain[0] = Options::rAIntegralGain(); 0280 in_params.integral_gain[1] = Options::dECIntegralGain(); 0281 0282 // Always pulse if we're dithering. 0283 in_params.enabled[0] = Options::rAGuideEnabled(); 0284 in_params.enabled[1] = Options::dECGuideEnabled(); 0285 0286 in_params.min_pulse_arcsec[0] = Options::rAMinimumPulseArcSec(); 0287 in_params.min_pulse_arcsec[1] = Options::dECMinimumPulseArcSec(); 0288 0289 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec(); 0290 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec(); 0291 0292 // RA W/E enable (but always pulse if dithering). 0293 // East RA+ enabled? 0294 in_params.enabled_axis1[0] = Options::eastRAGuideEnabled(); 0295 // West RA- enabled? 0296 in_params.enabled_axis2[0] = Options::westRAGuideEnabled(); 0297 0298 // DEC N/S enable (but always pulse if dithering). 0299 // North DEC+ enabled? 0300 in_params.enabled_axis1[1] = Options::northDECGuideEnabled(); 0301 // South DEC- enabled? 0302 in_params.enabled_axis2[1] = Options::southDECGuideEnabled(); 0303 } 0304 else 0305 { 0306 // If we're dithering, enable all axes and use full pulses. 0307 in_params.proportional_gain[0] = 1.0; 0308 in_params.proportional_gain[1] = 1.0; 0309 in_params.integral_gain[0] = 0.0; 0310 in_params.integral_gain[1] = 0.0; 0311 in_params.min_pulse_arcsec[0] = 0.0; 0312 in_params.min_pulse_arcsec[1] = 0.0; 0313 in_params.max_pulse_arcsec[0] = Options::rAMaximumPulseArcSec(); 0314 in_params.max_pulse_arcsec[1] = Options::dECMaximumPulseArcSec(); 0315 in_params.enabled[0] = true; 0316 in_params.enabled[1] = true; 0317 in_params.enabled_axis1[0] = true; 0318 in_params.enabled_axis2[0] = true; 0319 in_params.enabled_axis1[1] = true; 0320 in_params.enabled_axis2[1] = true; 0321 } 0322 0323 return dithering; 0324 } 0325 0326 void cgmath::updateOutParams(int k, const double arcsecDrift, int pulseLength, GuideDirection pulseDirection) 0327 { 0328 out_params.pulse_dir[k] = pulseDirection; 0329 out_params.pulse_length[k] = pulseLength; 0330 out_params.delta[k] = arcsecDrift; 0331 } 0332 0333 void cgmath::outputGuideLog() 0334 { 0335 if (Options::guideLogging()) 0336 { 0337 QTextStream out(&logFile); 0338 out << iterationCounter << "," << logTime.elapsed() << "," << out_params.delta[0] << "," << out_params.pulse_length[0] << 0339 "," 0340 << directionStr(out_params.pulse_dir[0]) << "," << out_params.delta[1] << "," 0341 << out_params.pulse_length[1] << "," << directionStr(out_params.pulse_dir[1]) << Qt::endl; 0342 } 0343 } 0344 0345 void cgmath::processAxis(const int k, const bool dithering, const bool darkGuide, const Seconds &timeStep, 0346 const QString &label) 0347 { 0348 // zero all out commands 0349 GuideDirection pulseDirection = NO_DIR; 0350 int pulseLength = 0; // milliseconds 0351 GuideDirection dir; 0352 0353 // Get the drift for this axis 0354 const int idx = driftUpto[k]; 0355 const double arcsecDrift = drift[k][idx]; 0356 0357 const double pulseConverter = (k == GUIDE_RA) ? 0358 calibration.raPulseMillisecondsPerArcsecond() : 0359 calibration.decPulseMillisecondsPerArcsecond(); 0360 const double maxPulseMilliseconds = in_params.max_pulse_arcsec[k] * pulseConverter; 0361 0362 // GPG pulse computation 0363 bool useGPG = !dithering && Options::gPGEnabled() && (k == GUIDE_RA) && in_params.enabled[k]; 0364 if (useGPG && darkGuide) 0365 { 0366 gpg->darkGuiding(&pulseLength, &dir, calibration, timeStep); 0367 pulseDirection = dir; 0368 } 0369 else if (darkGuide) 0370 { 0371 // We should not be dark guiding without GPG 0372 qCDebug(KSTARS_EKOS_GUIDE) << "Warning: dark guiding without GPG or while dithering."; 0373 return; 0374 } 0375 else if (useGPG && gpg->computePulse(arcsecDrift, 0376 usingSEPMultiStar() ? &guideStars : nullptr, &pulseLength, &dir, calibration, timeStep)) 0377 { 0378 pulseDirection = dir; 0379 pulseLength = std::min(pulseLength, static_cast<int>(maxPulseMilliseconds + 0.5)); 0380 } 0381 else 0382 { 0383 // This is the main non-GPG guide-pulse computation. 0384 // Traditionally it was hardwired so that proportional_gain=133 was about a control gain of 1.0 0385 // This is now in the 0.0 - 1.0 range, and multiplies the calibrated mount performance. 0386 0387 // Compute the average drift in the recent past for the integral control term. 0388 drift_integral[k] = 0; 0389 for (int i = 0; i < CIRCULAR_BUFFER_SIZE; ++i) 0390 drift_integral[k] += drift[k][i]; 0391 drift_integral[k] /= (double)CIRCULAR_BUFFER_SIZE; 0392 0393 if (in_params.integral_gain[k] > 0) 0394 qCDebug(KSTARS_EKOS_GUIDE) << label << "drift[" << axisStr(k) << "] = " << arcsecDrift 0395 << " integral[" << axisStr(k) << "] = " << drift_integral[k]; 0396 0397 const double arcsecPerMsPulse = k == GUIDE_RA ? calibration.raPulseMillisecondsPerArcsecond() : 0398 calibration.decPulseMillisecondsPerArcsecond(); 0399 const double proportionalResponse = arcsecDrift * in_params.proportional_gain[k] * arcsecPerMsPulse; 0400 const double integralResponse = drift_integral[k] * in_params.integral_gain[k] * arcsecPerMsPulse; 0401 pulseLength = std::min(fabs(proportionalResponse + integralResponse), maxPulseMilliseconds); 0402 0403 // calc direction 0404 // We do not send pulse if direction is disabled completely, or if direction in a specific axis (e.g. N or S) is disabled 0405 if (!in_params.enabled[k] || // This axis not enabled 0406 // Positive direction of this axis not enabled. 0407 (arcsecDrift > 0 && !in_params.enabled_axis1[k]) || 0408 // Negative direction of this axis not enabled. 0409 (arcsecDrift < 0 && !in_params.enabled_axis2[k])) 0410 { 0411 pulseDirection = NO_DIR; 0412 pulseLength = 0; 0413 } 0414 else 0415 { 0416 // Check the min pulse value, and assign the direction. 0417 const double pulseArcSec = pulseConverter > 0 ? pulseLength / pulseConverter : 0; 0418 if (pulseArcSec >= in_params.min_pulse_arcsec[k]) 0419 { 0420 if (k == GUIDE_RA) 0421 pulseDirection = arcsecDrift > 0 ? RA_DEC_DIR : RA_INC_DIR; 0422 else 0423 pulseDirection = arcsecDrift > 0 ? DEC_INC_DIR : DEC_DEC_DIR; // GUIDE_DEC. 0424 } 0425 else 0426 pulseDirection = NO_DIR; 0427 } 0428 0429 } 0430 updateOutParams(k, arcsecDrift, pulseLength, pulseDirection); 0431 } 0432 0433 void cgmath::calculatePulses(Ekos::GuideState state, const std::pair<Seconds, Seconds> &timeStep) 0434 { 0435 const bool dithering = configureInParams(state); 0436 0437 processAxis(GUIDE_RA, dithering, false, timeStep.first, "Guiding:"); 0438 processAxis(GUIDE_DEC, dithering, false, timeStep.second, "Guiding:"); 0439 0440 qCDebug(KSTARS_EKOS_GUIDE) 0441 << QString("Guiding pulses: RA: %1ms %2 DEC: %3ms %4") 0442 .arg(out_params.pulse_length[GUIDE_RA]).arg(directionStr(out_params.pulse_dir[GUIDE_RA])) 0443 .arg(out_params.pulse_length[GUIDE_DEC]).arg(directionStr(out_params.pulse_dir[GUIDE_DEC])); 0444 0445 outputGuideLog(); 0446 } 0447 0448 void cgmath::performProcessing(Ekos::GuideState state, QSharedPointer<FITSData> &imageData, 0449 QSharedPointer<GuideView> &guideView, 0450 const std::pair<Seconds, Seconds> &timeStep, GuideLog * logger) 0451 { 0452 if (suspended) 0453 { 0454 if (Options::gPGEnabled()) 0455 { 0456 GuiderUtils::Vector guideStarPosition = findLocalStarPosition(imageData, guideView, false); 0457 if (guideStarPosition.x != -1 && !std::isnan(guideStarPosition.x)) 0458 { 0459 gpg->suspended(guideStarPosition, targetPosition, 0460 usingSEPMultiStar() ? &guideStars : nullptr, calibration); 0461 } 0462 } 0463 // do nothing if suspended 0464 return; 0465 } 0466 0467 GuiderUtils::Vector starPositionArcSec, targetPositionArcSec; 0468 0469 // find guiding star location in the image 0470 starPosition = findLocalStarPosition(imageData, guideView, false); 0471 0472 // If no star found, mark as lost star. 0473 if (starPosition.x == -1 || std::isnan(starPosition.x)) 0474 { 0475 setLostStar(true); 0476 if (logger != nullptr && state == Ekos::GUIDE_GUIDING) 0477 { 0478 GuideLog::GuideData data; 0479 data.code = GuideLog::GuideData::NO_STAR_FOUND; 0480 data.type = GuideLog::GuideData::DROP; 0481 logger->addGuideData(data); 0482 } 0483 return; 0484 } 0485 else 0486 setLostStar(false); 0487 0488 // Emit the detected star center 0489 QVector3D starCenter(starPosition.x, starPosition.y, 0); 0490 emit newStarPosition(starCenter, true); 0491 0492 // If we're only calibrating, then we're done. 0493 if (state == Ekos::GUIDE_CALIBRATING) 0494 return; 0495 0496 if (state == Ekos::GUIDE_GUIDING && (targetPosition.x <= 0.0 || targetPosition.y <= 0.0)) 0497 { 0498 qCDebug(KSTARS_EKOS_GUIDE) << "Guiding with target 0.0 -- something's wrong!!!!!!!!!!!"; 0499 for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) 0500 { 0501 out_params.pulse_dir[k] = NO_DIR; 0502 out_params.pulse_length[k] = 0; 0503 out_params.delta[k] = 0; 0504 setLostStar(true); 0505 } 0506 return; 0507 } 0508 // translate star coords into sky coord. system 0509 0510 // convert from pixels into arcsecs 0511 starPositionArcSec = calibration.convertToArcseconds(starPosition); 0512 targetPositionArcSec = calibration.convertToArcseconds(targetPosition); 0513 0514 // Compute RA & DEC drift in arcseconds. 0515 const GuiderUtils::Vector star_xy_arcsec_drift = starPositionArcSec - targetPositionArcSec; 0516 const GuiderUtils::Vector star_drift = calibration.rotateToRaDec(star_xy_arcsec_drift); 0517 0518 // both coords are ready for math processing 0519 // put coord to drift list 0520 // Note: if we're not guiding, these will be overwritten, 0521 // as driftUpto is only incremented when guiding. 0522 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = star_drift.x; 0523 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = star_drift.y; 0524 0525 qCDebug(KSTARS_EKOS_GUIDE) 0526 << QString("Star %1 %2 a-s %3 %4 Target %5 %6 a-s %7 %8 Drift: RA %9 DEC %10") 0527 .arg(starPosition.x, 0, 'f', 1) .arg(starPosition.y, 0, 'f', 1) 0528 .arg(starPositionArcSec.x, 0, 'f', 1) .arg(starPositionArcSec.y, 0, 'f', 1) 0529 .arg(targetPosition.x, 0, 'f', 1) .arg(targetPosition.y, 0, 'f', 1) 0530 .arg(targetPositionArcSec.x, 0, 'f', 1).arg(targetPositionArcSec.y, 0, 'f', 1) 0531 .arg(star_drift.x, 0, 'f', 2) .arg(star_drift.y, 0, 'f', 2); 0532 0533 if (state == Ekos::GUIDE_GUIDING && usingSEPMultiStar()) 0534 { 0535 double multiStarRADrift, multiStarDECDrift; 0536 if (guideStars.getDrift(sqrt(star_drift.x * star_drift.x + star_drift.y * star_drift.y), 0537 targetPosition.x, targetPosition.y, 0538 &multiStarRADrift, &multiStarDECDrift)) 0539 { 0540 qCDebug(KSTARS_EKOS_GUIDE) << QString("MultiStar drift: RA %1 DEC %2") 0541 .arg(multiStarRADrift, 0, 'f', 2) 0542 .arg(multiStarDECDrift, 0, 'f', 2); 0543 drift[GUIDE_RA][driftUpto[GUIDE_RA]] = multiStarRADrift; 0544 drift[GUIDE_DEC][driftUpto[GUIDE_DEC]] = multiStarDECDrift; 0545 } 0546 else 0547 { 0548 qCDebug(KSTARS_EKOS_GUIDE) << "MultiStar: failed, fell back to guide star"; 0549 } 0550 } 0551 0552 // driftUpto will change when the circular buffer is updated, 0553 // so save the values for logging. 0554 const double raDrift = drift[GUIDE_RA][driftUpto[GUIDE_RA]]; 0555 const double decDrift = drift[GUIDE_DEC][driftUpto[GUIDE_DEC]]; 0556 0557 // make decision by axes 0558 calculatePulses(state, timeStep); 0559 0560 if (state == Ekos::GUIDE_GUIDING) 0561 { 0562 calculateRmsError(); 0563 emitStats(); 0564 updateCircularBuffers(); 0565 } 0566 0567 if (logger != nullptr) 0568 { 0569 GuideLog::GuideData data; 0570 data.type = GuideLog::GuideData::MOUNT; 0571 // These are distances in pixels. 0572 // Note--these don't include the multistar algorithm, but the below ra/dec ones do. 0573 data.dx = starPosition.x - targetPosition.x; 0574 data.dy = starPosition.y - targetPosition.y; 0575 // Above computes position - reticle. Should the reticle-position, so negate. 0576 calibration.convertToPixels(-raDrift, -decDrift, &data.raDistance, &data.decDistance); 0577 0578 const double raGuideFactor = out_params.pulse_dir[GUIDE_RA] == NO_DIR ? 0579 0 : (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR ? -1.0 : 1.0); 0580 const double decGuideFactor = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ? 0581 0 : (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR ? -1.0 : 1.0); 0582 0583 // Phd2LogViewer wants these in pixels instead of arcseconds, so normalizing them, but 0584 // that will be wrong for non-square pixels. They should really accept arcsecond units. 0585 data.raGuideDistance = calibration.xPixelsPerArcsecond() * raGuideFactor * out_params.pulse_length[GUIDE_RA] / 0586 calibration.raPulseMillisecondsPerArcsecond(); 0587 data.decGuideDistance = calibration.yPixelsPerArcsecond() * decGuideFactor * out_params.pulse_length[GUIDE_DEC] / 0588 calibration.decPulseMillisecondsPerArcsecond(); 0589 0590 data.raDuration = out_params.pulse_dir[GUIDE_RA] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_RA]; 0591 data.raDirection = out_params.pulse_dir[GUIDE_RA]; 0592 data.decDuration = out_params.pulse_dir[GUIDE_DEC] == NO_DIR ? 0 : out_params.pulse_length[GUIDE_DEC]; 0593 data.decDirection = out_params.pulse_dir[GUIDE_DEC]; 0594 data.code = GuideLog::GuideData::NO_ERRORS; 0595 data.snr = guideStars.getGuideStarSNR(); 0596 data.mass = guideStars.getGuideStarMass(); 0597 // Add SNR and MASS from SEP stars. 0598 logger->addGuideData(data); 0599 } 0600 } 0601 0602 void cgmath::performDarkGuiding(Ekos::GuideState state, const std::pair<Seconds, Seconds> &timeStep) 0603 { 0604 0605 const bool dithering = configureInParams(state); 0606 //out_params.sigma[GUIDE_RA] = 0; 0607 0608 processAxis(GUIDE_RA, dithering, true, timeStep.first, "Dark Guiding:"); 0609 qCDebug(KSTARS_EKOS_GUIDE) 0610 << QString("Dark Guiding pulses: RA: %1ms %2") 0611 .arg(out_params.pulse_length[GUIDE_RA]).arg(directionStr(out_params.pulse_dir[GUIDE_RA])); 0612 0613 0614 // Don't guide in DEC when dark guiding 0615 updateOutParams(GUIDE_DEC, 0, 0, NO_DIR); 0616 0617 outputGuideLog(); 0618 } 0619 0620 void cgmath::emitStats() 0621 { 0622 double pulseRA = 0; 0623 if (out_params.pulse_dir[GUIDE_RA] == RA_DEC_DIR) 0624 pulseRA = out_params.pulse_length[GUIDE_RA]; 0625 else if (out_params.pulse_dir[GUIDE_RA] == RA_INC_DIR) 0626 pulseRA = -out_params.pulse_length[GUIDE_RA]; 0627 double pulseDEC = 0; 0628 if (out_params.pulse_dir[GUIDE_DEC] == DEC_DEC_DIR) 0629 pulseDEC = -out_params.pulse_length[GUIDE_DEC]; 0630 else if (out_params.pulse_dir[GUIDE_DEC] == DEC_INC_DIR) 0631 pulseDEC = out_params.pulse_length[GUIDE_DEC]; 0632 0633 const bool hasGuidestars = usingSEPMultiStar(); 0634 const double snr = hasGuidestars ? guideStars.getGuideStarSNR() : 0; 0635 const double skyBG = hasGuidestars ? guideStars.skybackground().mean : 0; 0636 const int numStars = hasGuidestars ? guideStars.skybackground().starsDetected : 0; // wait for rob's release 0637 0638 emit guideStats(-out_params.delta[GUIDE_RA], -out_params.delta[GUIDE_DEC], 0639 pulseRA, pulseDEC, snr, skyBG, numStars); 0640 } 0641 0642 void cgmath::calculateRmsError(void) 0643 { 0644 if (!do_statistics) 0645 return; 0646 0647 if (iterationCounter == 0) 0648 return; 0649 0650 int count = std::min(iterationCounter, static_cast<unsigned int>(CIRCULAR_BUFFER_SIZE)); 0651 for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) 0652 { 0653 double sqr_avg = 0; 0654 for (int i = 0; i < count; ++i) 0655 sqr_avg += drift[k][i] * drift[k][i]; 0656 0657 out_params.sigma[k] = sqrt(sqr_avg / (double)count); 0658 } 0659 } 0660 0661 0662 QVector3D cgmath::selectGuideStar(const QSharedPointer<FITSData> &imageData) 0663 { 0664 return guideStars.selectGuideStar(imageData); 0665 } 0666 0667 double cgmath::getGuideStarSNR() 0668 { 0669 return guideStars.getGuideStarSNR(); 0670 } 0671 0672 //--------------------------------------------------------------------------------------- 0673 cproc_in_params::cproc_in_params() 0674 { 0675 reset(); 0676 } 0677 0678 void cproc_in_params::reset(void) 0679 { 0680 average = true; 0681 0682 for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) 0683 { 0684 enabled[k] = true; 0685 integral_gain[k] = 0; 0686 max_pulse_arcsec[k] = 5000; 0687 min_pulse_arcsec[k] = 0; 0688 } 0689 } 0690 0691 cproc_out_params::cproc_out_params() 0692 { 0693 reset(); 0694 } 0695 0696 void cproc_out_params::reset(void) 0697 { 0698 for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) 0699 { 0700 delta[k] = 0; 0701 pulse_dir[k] = NO_DIR; 0702 pulse_length[k] = 0; 0703 sigma[k] = 0; 0704 } 0705 } 0706