File indexing completed on 2024-03-24 15:18:32
0001 /* 0002 SPDX-FileCopyrightText: 2020 Hy Murveit <hy@murveit.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "../indi/indiproperty.h" 0008 #include "ekos/guide/internalguide/guidestars.h" 0009 0010 #include <QTest> 0011 0012 #include <QObject> 0013 0014 // The high-level methods, selectGuideStar() and findGuideStar() are not yet tested. 0015 // Neither are the SEP-related EvaluateSEPStars, findTopStars, findAllSEPStars(). 0016 0017 class TestGuideStars : public QObject 0018 { 0019 Q_OBJECT 0020 0021 public: 0022 /** @short Constructor */ 0023 TestGuideStars(); 0024 0025 /** @short Destructor */ 0026 ~TestGuideStars() override = default; 0027 0028 private slots: 0029 void basicTest(); 0030 void calibrationTest(); 0031 }; 0032 0033 #include "testguidestars.moc" 0034 #include "Options.h" 0035 0036 TestGuideStars::TestGuideStars() : QObject() 0037 { 0038 } 0039 0040 Edge makeEdge(float x, float y) 0041 { 0042 Edge e; 0043 e.x = x; 0044 e.y = y; 0045 e.HFR = 2.0; 0046 e.numPixels = 20; 0047 e.sum = 2000; 0048 return e; 0049 } 0050 0051 #define CompareFloat(d1,d2) QVERIFY(fabs((d1) - (d2)) < .001) 0052 0053 void TestGuideStars::basicTest() 0054 { 0055 Options::setMinDetectionsSEPMultistar(5); 0056 Options::setMaxMultistarReferenceStars(10); 0057 GuideStars g; 0058 0059 // Test setCalibration() and calibration in general. 0060 0061 // angle in degrees, pixel size in mm, focal length in mm. 0062 const int binning = 1; 0063 double angle = 0.0; 0064 const double pixel_size = 3e-3, focal_length = 750.0; 0065 dms ra, dec; 0066 ra.setFromString("120:30:40"); 0067 dec.setFromString("10:20:30"); 0068 ISD::Mount::PierSide side = ISD::Mount::PIER_EAST; 0069 0070 Calibration cal; 0071 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, dec); 0072 cal.setAngle(angle); 0073 g.setCalibration(cal); 0074 0075 // arcseconds = 3600*180/pi * (pix*ccd_pix_sz) / focal_len 0076 // Then needs to be rotated by the angle. Start with angle = 0; 0077 double arcsecondsPerPixel = (3600.0 * 180.0 / M_PI) * binning * pixel_size / focal_length; 0078 0079 double x1 = 10, y1 = 50; 0080 GuiderUtils::Vector p(x1, y1, 0); 0081 GuiderUtils::Vector as = cal.convertToArcseconds(p); 0082 CompareFloat(as.x, arcsecondsPerPixel * x1); 0083 CompareFloat(as.y, arcsecondsPerPixel * y1); 0084 0085 // Test computeStarDrift(), computing the distance between two stars in arcseconds. 0086 double dx = 2.5, dy = -0.7; 0087 Edge refStar = makeEdge(x1, y1); 0088 Edge star = makeEdge(x1 + dx, y1 + dy); 0089 double dRa, dDec; 0090 g.computeStarDrift(star, refStar, &dRa, &dDec); 0091 // Since the angle is 0, x differences should be reflected in RA, and y in DEC. 0092 CompareFloat(dRa, dx * arcsecondsPerPixel); 0093 // Y is inverted, because y=0 is at the top. 0094 CompareFloat(dDec, -dy * arcsecondsPerPixel); 0095 0096 // Change the angle to 90, 180 and -90 degrees 0097 angle = 90.0; 0098 cal.setAngle(angle); 0099 g.setCalibration(cal); 0100 g.computeStarDrift(star, refStar, &dRa, &dDec); 0101 CompareFloat(-dDec, dx * arcsecondsPerPixel); 0102 CompareFloat(dRa, -dy * arcsecondsPerPixel); 0103 0104 angle = 180.0; 0105 cal.setAngle(angle); 0106 g.setCalibration(cal); 0107 g.computeStarDrift(star, refStar, &dRa, &dDec); 0108 CompareFloat(-dRa, dx * arcsecondsPerPixel); 0109 CompareFloat(-dDec, -dy * arcsecondsPerPixel); 0110 0111 angle = -90.0; 0112 cal.setAngle(angle); 0113 g.setCalibration(cal); 0114 g.computeStarDrift(star, refStar, &dRa, &dDec); 0115 CompareFloat(dDec, dx * arcsecondsPerPixel); 0116 CompareFloat(-dRa, -dy * arcsecondsPerPixel); 0117 0118 // Use angle -90 so changes in x are changes in RA, and y --> -DEC. 0119 angle = 0.0; 0120 cal.setAngle(angle); 0121 g.setCalibration(cal); 0122 0123 // Select the guide star. 0124 0125 // Setup with 5 reference stars--a guide star and 4 others. 0126 double gstarX = 100, gstarY = 70; 0127 double gstar1X = 75, gstar1Y = 150; 0128 double gstar2X = 200, gstar2Y = 500; 0129 QList<Edge> stars; 0130 stars.append(makeEdge(gstarX, gstarY)); 0131 stars.append(makeEdge(gstar1X, gstar1Y)); 0132 stars.append(makeEdge(gstar2X, gstar2Y)); 0133 stars.append(makeEdge(250, 800)); 0134 stars.append(makeEdge(300, 800)); 0135 QList<double> scores = { 40, 200, 70, 0, 0}; 0136 QList<double> distances = { 100, 100, 100, 100, 100 }; 0137 QVector3D gstar0 = g.selectGuideStar(stars, scores, 1000, 1000, distances); 0138 // It should select the highest scoring star (1), as it has the best score 0139 // and it isn't near an edge (near x,y = 0 or near x,y = 1000). 0140 CompareFloat(gstar0.x(), gstar1X); 0141 CompareFloat(gstar0.y(), gstar1Y); 0142 0143 // If the guide star (#1) position is moved close to the border, 0144 // the next best star (#2) should be chosen. 0145 gstar1X = 10; 0146 stars[1].x = gstar1X; 0147 QVector3D gstar = g.selectGuideStar(stars, scores, 1000, 1000, distances); 0148 CompareFloat(gstar.x(), gstar2X); 0149 CompareFloat(gstar.y(), gstar2Y); 0150 0151 // Now say that that star 2 has a close neighbor, it should go the #0 0152 distances[2] = 5; 0153 gstar = g.selectGuideStar(stars, scores, 1000, 1000, distances); 0154 CompareFloat(gstar.x(), gstarX); 0155 CompareFloat(gstar.y(), gstarY); 0156 0157 // We have a guide star at (100,70) and other stars at 0158 // (10, 150), (200,500), (250, 800), (300, 800). 0159 // Normally the detected stars and the mapping from stars to the reference stars 0160 // is done inside findGuideStar(). To test the internals, we set these directly. 0161 g.setDetectedStars(stars); 0162 QVector<int> map = {0, 1, 2, 3, 4}; 0163 g.setStarMap(map); 0164 0165 // This is needed so SNR calculations can work. Parameters aren't important 0166 // as long as the star SNR wind up above 8dB. 0167 SkyBackground bg(10, 10, 1000); 0168 g.setSkyBackground(bg); 0169 0170 bool success = g.getDrift(1, 100, 70, &dRa, &dDec); 0171 QVERIFY(success); 0172 // drift should be 0. 0173 CompareFloat(dRa, 0); 0174 CompareFloat(dDec, 0); 0175 0176 // Move the reticle 1 pixel in x, should result in a drift of "arcsecondsPerPixel" in RA 0177 // and 0 in DEC. 0178 success = g.getDrift(1, 99, 70, &dRa, &dDec); 0179 QVERIFY(success); 0180 CompareFloat(dRa, arcsecondsPerPixel); 0181 CompareFloat(dDec, 0); 0182 0183 // Similarly 2 pixels upward in y would affect DEC. 0184 success = g.getDrift(1, 100, 68, &dRa, &dDec); 0185 QVERIFY(success); 0186 CompareFloat(dRa, 0); 0187 CompareFloat(dDec, -2 * arcsecondsPerPixel); 0188 0189 // Finally, since the drift is the median drift of the guide stars, 0190 // we move half up and half down and the middle will control the drift. 0191 // We only consider drifts within 2a-s of the guidestar (star 0) so 0192 // these drifts are bunched together. 0193 stars[0].x += 1; 0194 stars[0].y += 1; 0195 stars[1].x += 1; 0196 stars[1].y += 1; 0197 stars[2].x += 0.5; // this controls the x-drift 0198 stars[2].y += 0.75; // this controls the y-drift 0199 stars[3].x -= -.25; 0200 stars[3].y -= -.25; 0201 stars[4].x -= -.25; 0202 stars[4].y -= -.25; 0203 g.setDetectedStars(stars); 0204 success = g.getDrift(1, 100, 70, &dRa, &dDec); 0205 QVERIFY(success); 0206 CompareFloat(dRa, 0.5 * arcsecondsPerPixel); 0207 CompareFloat(dDec, -0.75 * arcsecondsPerPixel); 0208 0209 // We don't accept multi-star drifts where the reference stars are too far (> 2 a-s) 0210 // from the guide-star drift. Here we move the guide star so it's different than 0211 // the rest, and it should control the drift. 0212 stars[0].x = 105; 0213 stars[0].y = 76; 0214 g.setDetectedStars(stars); 0215 success = g.getDrift(1, 100, 70, &dRa, &dDec); 0216 QVERIFY(success); 0217 CompareFloat(dRa, 5 * arcsecondsPerPixel); 0218 CompareFloat(dDec, -6 * arcsecondsPerPixel); 0219 0220 // This should fail if either there aren't enough reference stars (< 2) 0221 0222 // Test findMinDistance. 0223 double xx0 = 10, xx1 = 12, xx2 = 20; 0224 double yy0 = 7, yy1 = 13, yy2 = 4; 0225 Edge e0 = makeEdge(xx0, yy0); 0226 Edge e1 = makeEdge(xx1, yy1); 0227 Edge e2 = makeEdge(xx2, yy2); 0228 double d01 = hypot(xx0 - xx1, yy0 - yy1); 0229 double d02 = hypot(xx0 - xx2, yy0 - yy2); 0230 double d12 = hypot(xx1 - xx2, yy1 - yy2); 0231 QList<Edge *> edges; 0232 edges.append(&e0); 0233 edges.append(&e1); 0234 edges.append(&e2); 0235 CompareFloat(g.findMinDistance(0, edges), std::min(d01, d02)); 0236 CompareFloat(g.findMinDistance(1, edges), std::min(d01, d12)); 0237 CompareFloat(g.findMinDistance(2, edges), std::min(d02, d12)); 0238 } 0239 0240 // Takes the radians value input and converts to an angle in degrees 0 <= degrees < 360. 0241 double toDegrees(double radians) 0242 { 0243 double degrees = 360.0 * radians / (2.0 * M_PI); 0244 while (degrees < 0) degrees += 360.0; 0245 while (degrees >= 360.0) degrees -= 360.0; 0246 return degrees; 0247 } 0248 0249 // This tests the Calibration class' API. 0250 void TestGuideStars::calibrationTest() 0251 { 0252 const int binning = 1; 0253 double angle = 0.0; 0254 const double pixel_size = 3e-3, focal_length = 750.0; 0255 Calibration cal; 0256 dms ra, dec; 0257 ra.setFromString("120:30:40"); 0258 dec.setFromString("10:20:30"); 0259 ISD::Mount::PierSide side = ISD::Mount::PIER_EAST; 0260 0261 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, dec); 0262 0263 // arcseconds = 3600*180/pi * (pix*ccd_pix_sz) / focal_len 0264 // Then needs to be rotated by the angle. Start with angle = 0; 0265 double arcsecondsPerPixel = (3600.0 * 180.0 / M_PI) * binning * pixel_size / focal_length; 0266 0267 CompareFloat(angle, cal.getAngle()); 0268 CompareFloat(focal_length, cal.getFocalLength()); 0269 0270 for (int i = -10; i <= 10; ++i) 0271 { 0272 GuiderUtils::Vector input(i, 2 * i, 0); 0273 GuiderUtils::Vector as = cal.convertToArcseconds(input); 0274 GuiderUtils::Vector px = cal.convertToPixels(input); 0275 CompareFloat(as.x, i * arcsecondsPerPixel); 0276 CompareFloat(as.y, 2 * i * arcsecondsPerPixel); 0277 CompareFloat(px.x, i / arcsecondsPerPixel); 0278 CompareFloat(px.y, 2 * i / arcsecondsPerPixel); 0279 double x, y; 0280 cal.convertToPixels(input.x, input.y, &x, &y); 0281 CompareFloat(x, i / arcsecondsPerPixel); 0282 CompareFloat(y, 2 * i / arcsecondsPerPixel); 0283 } 0284 0285 CompareFloat(arcsecondsPerPixel, cal.xArcsecondsPerPixel()); 0286 CompareFloat(arcsecondsPerPixel, cal.yArcsecondsPerPixel()); 0287 CompareFloat(1.0 / arcsecondsPerPixel, cal.xPixelsPerArcsecond()); 0288 CompareFloat(1.0 / arcsecondsPerPixel, cal.yPixelsPerArcsecond()); 0289 0290 0291 // These are not yet estimated iniside Calibrate() so for now, just set them. 0292 //double raRate = 5.5, decRate = 3.3; 0293 double raMillisecondsPerPixel = 5.5; 0294 double raMillisecondsPerArcsecond = raMillisecondsPerPixel / arcsecondsPerPixel; 0295 double decMillisecondsPerPixel = 5.5; 0296 double decMillisecondsPerArcsecond = decMillisecondsPerPixel / arcsecondsPerPixel; 0297 0298 cal.setRaPulseMsPerArcsecond(raMillisecondsPerArcsecond); 0299 cal.setDecPulseMsPerArcsecond(decMillisecondsPerArcsecond); 0300 0301 CompareFloat(raMillisecondsPerArcsecond, cal.raPulseMillisecondsPerArcsecond()); 0302 CompareFloat(decMillisecondsPerArcsecond, cal.decPulseMillisecondsPerArcsecond()); 0303 0304 ///////////////////////////////////////////////////////////////////// 0305 // Check that conversions are right if binning is changed on the fly. 0306 ///////////////////////////////////////////////////////////////////// 0307 const double bFactor = 3.0; 0308 cal.setBinningUsed(bFactor * binning, bFactor * binning); 0309 // In the loop below, the conversions to pixels will be smaller by bFactor (since we have 0310 // larger pixels with the binning), and the conversions to arc-seconds will increase by a factor 0311 // of bFactor, because each binned pixel is more arc-seconds than before. 0312 for (int i = -10; i <= 10; ++i) 0313 { 0314 GuiderUtils::Vector input(i, 2 * i, 0); 0315 GuiderUtils::Vector as = cal.convertToArcseconds(input); 0316 GuiderUtils::Vector px = cal.convertToPixels(input); 0317 CompareFloat(as.x, bFactor * i * arcsecondsPerPixel); 0318 CompareFloat(as.y, bFactor * 2 * i * arcsecondsPerPixel); 0319 CompareFloat(px.x, i / (bFactor * arcsecondsPerPixel)); 0320 CompareFloat(px.y, 2 * i / (bFactor * arcsecondsPerPixel)); 0321 double x, y; 0322 cal.convertToPixels(input.x, input.y, &x, &y); 0323 CompareFloat(x, i / (bFactor * arcsecondsPerPixel)); 0324 CompareFloat(y, 2 * i / (bFactor * arcsecondsPerPixel)); 0325 } 0326 0327 CompareFloat(raMillisecondsPerArcsecond, cal.raPulseMillisecondsPerArcsecond()); 0328 CompareFloat(decMillisecondsPerArcsecond, cal.decPulseMillisecondsPerArcsecond()); 0329 0330 CompareFloat(arcsecondsPerPixel * bFactor, cal.xArcsecondsPerPixel()); 0331 CompareFloat(arcsecondsPerPixel * bFactor, cal.yArcsecondsPerPixel()); 0332 CompareFloat(1.0 / (bFactor * arcsecondsPerPixel), cal.xPixelsPerArcsecond()); 0333 CompareFloat(1.0 / (bFactor * arcsecondsPerPixel), cal.yPixelsPerArcsecond()); 0334 cal.setBinningUsed(binning, binning); 0335 ///////////////////////////////////////////////////////////////////// 0336 0337 GuiderUtils::Vector px(1.0, 0.0, 0.0); 0338 cal.setAngle(0); 0339 GuiderUtils::Vector raDec = cal.rotateToRaDec(px); 0340 CompareFloat(px.x, raDec.x); 0341 CompareFloat(px.y, raDec.y); 0342 double rdx, rdy; 0343 cal.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0344 CompareFloat(px.x, rdx); 0345 CompareFloat(px.y, rdy); 0346 0347 cal.setAngle(90); 0348 raDec = cal.rotateToRaDec(px); 0349 CompareFloat(px.y, raDec.x); 0350 CompareFloat(-px.x, raDec.y); 0351 cal.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0352 CompareFloat(px.y, rdx); 0353 CompareFloat(-px.x, rdy); 0354 0355 cal.setAngle(180); 0356 raDec = cal.rotateToRaDec(px); 0357 CompareFloat(-px.x, raDec.x); 0358 CompareFloat(-px.y, raDec.y); 0359 cal.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0360 CompareFloat(-px.x, rdx); 0361 CompareFloat(-px.y, rdy); 0362 0363 cal.setAngle(270); 0364 raDec = cal.rotateToRaDec(px); 0365 CompareFloat(-px.y, raDec.x); 0366 CompareFloat(px.x, raDec.y); 0367 cal.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0368 CompareFloat(-px.y, rdx); 0369 CompareFloat(px.x, rdy); 0370 0371 // Test saving and restoring the calibration. 0372 0373 // This should set the angle to 270 and keep raRate. 0374 cal.calculate1D(0, 10, raMillisecondsPerPixel * 10); 0375 angle = 270; 0376 int binX = 2, binY = 3; 0377 double pixSzW = .005, pixSzH = .006; 0378 cal.setParameters(pixSzW, pixSzH, focal_length, binX, binY, side, ra, dec); 0379 QString encodedCal = cal.serialize(); 0380 Calibration cal2; 0381 QVERIFY(cal2.getFocalLength() != focal_length); 0382 QVERIFY(cal2.ccd_pixel_width != pixSzW); 0383 QVERIFY(cal2.ccd_pixel_height != pixSzH); 0384 QVERIFY(cal2.getAngle() != angle); 0385 QVERIFY(cal2.subBinX != binX); 0386 QVERIFY(cal2.subBinY != binY); 0387 QVERIFY(cal2.raPulseMillisecondsPerArcsecond() != raMillisecondsPerArcsecond); 0388 QVERIFY(cal2.decPulseMillisecondsPerArcsecond() != decMillisecondsPerArcsecond); 0389 QVERIFY(cal2.calibrationPierSide != side); 0390 QVERIFY(!(cal2.calibrationRA == ra)); 0391 QVERIFY(!(cal2.calibrationDEC == dec)); 0392 // swap defaults to false, and we haven't done anything to change it. 0393 QVERIFY(cal2.declinationSwapEnabled() == false); 0394 0395 QVERIFY(!cal2.restore("")); 0396 QVERIFY(cal2.restore(encodedCal)); 0397 cal2.setBinningUsed(binX, binY); 0398 QCOMPARE(cal2.getFocalLength(), focal_length); 0399 QCOMPARE(cal2.ccd_pixel_width, pixSzW); 0400 QCOMPARE(cal2.ccd_pixel_height, pixSzH); 0401 QCOMPARE(cal2.getAngle(), angle); 0402 QCOMPARE(cal2.subBinX, binX); 0403 QCOMPARE(cal2.subBinY, binY); 0404 CompareFloat(cal2.raPulseMillisecondsPerArcsecond(), raMillisecondsPerArcsecond); 0405 CompareFloat(cal2.decPulseMillisecondsPerArcsecond(), decMillisecondsPerArcsecond); 0406 QCOMPARE(cal2.calibrationPierSide, side); 0407 QVERIFY(cal2.calibrationRA == ra); 0408 QVERIFY(cal2.calibrationDEC == dec); 0409 // Swap still should be false. 0410 QVERIFY(cal2.declinationSwapEnabled() == false); 0411 bool swap; 0412 0413 // This is an options checkbox that the user modifies depending on his/her mount. 0414 bool reverseDecOnPierChange = false; 0415 0416 // Test restoring with a pier side. 0417 // This is same as above, as the encoded pier side was east. 0418 QVERIFY(cal2.restore(encodedCal, ISD::Mount::PIER_EAST, reverseDecOnPierChange, binning, binning)); 0419 QCOMPARE(cal2.getAngle(), angle); 0420 QCOMPARE(cal2.declinationSwapEnabled(), false); 0421 // This tests that the rotation matrix got adjusted with the angle. 0422 cal2.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0423 CompareFloat(-px.y, rdx); 0424 CompareFloat(px.x, rdy); 0425 0426 // If we are now west, the angle should change by 180 degrees and dec-swap should invert. 0427 QVERIFY(cal2.restore(encodedCal, ISD::Mount::PIER_WEST, reverseDecOnPierChange, binning, binning)); 0428 QCOMPARE(cal2.getAngle(), angle - 180.0); 0429 QCOMPARE(cal2.declinationSwapEnabled(), true); 0430 cal2.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0431 CompareFloat(-px.y, -rdx); 0432 CompareFloat(px.x, -rdy); 0433 0434 // Set the user option to reverse DEC on pier-side change. 0435 reverseDecOnPierChange = true; 0436 QVERIFY(cal2.restore(encodedCal, ISD::Mount::PIER_WEST, reverseDecOnPierChange, binning, binning)); 0437 QCOMPARE(cal2.getAngle(), angle - 180.0); 0438 QCOMPARE(cal2.declinationSwapEnabled(), false); 0439 cal2.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0440 CompareFloat(-px.y, -rdx); 0441 CompareFloat(px.x, -rdy); 0442 reverseDecOnPierChange = false; 0443 0444 // If we go back east, the angle and decSwap should revert to their original values. 0445 QVERIFY(cal2.restore(encodedCal, ISD::Mount::PIER_EAST, reverseDecOnPierChange, binning, binning)); 0446 QCOMPARE(cal2.getAngle(), angle); 0447 QCOMPARE(cal2.declinationSwapEnabled(), false); 0448 cal2.rotateToRaDec(px.x, px.y, &rdx, &rdy); 0449 CompareFloat(-px.y, rdx); 0450 CompareFloat(px.x, rdy); 0451 0452 // Should not restore if the pier is unknown. 0453 QVERIFY(!cal2.restore(encodedCal, ISD::Mount::PIER_UNKNOWN, reverseDecOnPierChange, binning, binning)); 0454 0455 // Calculate the rotation. 0456 // Compute the angle the coordinates passed in make with the x-axis. 0457 // Oddly, though, the method first negates the y-coorainate (as images have y=0 on 0458 // top). So, account for that. Test in all 4 quadrents. Returns values 0-360 degrees. 0459 double x = 5.0, y = -7.0; 0460 QCOMPARE(Calibration::calculateRotation(x, y), toDegrees(atan2(-y, x))); 0461 x = -8.3, y = -2.4; 0462 QCOMPARE(Calibration::calculateRotation(x, y), toDegrees(atan2(-y, x))); 0463 x = -10.3, y = 8.2; 0464 QCOMPARE(Calibration::calculateRotation(x, y), toDegrees(atan2(-y, x))); 0465 x = 1.7, y = 8.2; 0466 QCOMPARE(Calibration::calculateRotation(x, y), toDegrees(atan2(-y, x))); 0467 x = 0, y = -8.0; 0468 QCOMPARE(Calibration::calculateRotation(x, y), toDegrees(atan2(-y, x))); 0469 x = 10, y = 0; 0470 QCOMPARE(Calibration::calculateRotation(x, y), toDegrees(atan2(-y, x))); 0471 // Short vectors (size less than 1.0) should return -1. 0472 x = .10, y = .20; 0473 QCOMPARE(Calibration::calculateRotation(x, y), -1.0); 0474 0475 // Similar to above, a 1-D calibration. 0476 // Distance was 5.0 pixels, took 10 seconds of pulse 0477 0478 // Set the pixels square for the below tests, since angles are in arc-seconds 0479 // and not pixel coordinates (this way both are equivalent). 0480 binX = 1.0; 0481 binY = 1.0; 0482 pixSzH = .005; 0483 pixSzW = .005; 0484 0485 x = 3.0, y = -4.0; 0486 int pulseLength = 10000; 0487 side = ISD::Mount::PIER_WEST; 0488 cal.setParameters(pixSzW, pixSzH, focal_length, binX, binY, side, ra, dec); 0489 cal.calculate1D(x, y, pulseLength); 0490 CompareFloat(cal.getAngle(), toDegrees(atan2(-y, x))); 0491 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), pulseLength / std::hypot(x, y)); 0492 0493 // 2-D calibrations take coordinates and pulse lengths for both axes. 0494 0495 // Made sure the ra and dec vectors were orthogonal and dec was 90-degrees greater 0496 // than ra (ignoring that y-flip). 0497 double ra_x = -16.0, ra_y = 16.0, dec_x = -12.0, dec_y = -12.0; 0498 double ra_factor = 750.0; 0499 int ra_pulse = std::hypot(ra_x, ra_y) * ra_factor; 0500 // Correct for rounding, since pulse is an integer. 0501 ra_factor = ra_pulse / std::hypot(ra_x, ra_y); 0502 double dec_factor = 300.0; 0503 int dec_pulse = std::hypot(dec_x, dec_y) * dec_factor; 0504 dec_factor = dec_pulse / std::hypot(dec_x, dec_y); 0505 cal.calculate2D(ra_x, ra_y, dec_x, dec_y, &swap, ra_pulse, dec_pulse); 0506 QCOMPARE(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), ra_factor); 0507 QCOMPARE(cal.decPulseMillisecondsPerArcsecond() * cal.yArcsecondsPerPixel(), dec_factor); 0508 QCOMPARE(cal.getAngle(), toDegrees(atan2(-ra_y, ra_x))); 0509 QCOMPARE(swap, false); 0510 0511 // Above we created a calibration where movement of size 1.0 along the x-axis in pixels 0512 // gets rotated towards -16,16 but keeps its original length. 0513 // Note, a 45-degree vector of length 1.0 has size length of sqrt(2)/2 in x and y. 0514 const double size1side = sqrt(2.0) / 2.0; 0515 cal.rotateToRaDec(1.0, 0.0, &rdx, &rdy); 0516 CompareFloat(rdx, -size1side); 0517 CompareFloat(rdy, size1side); 0518 // Similarly, a move (in dec) of size 1 down the y-axis would rotate toward -12,-12 0519 cal.rotateToRaDec(0.0, -1.0, &rdx, &rdy); 0520 CompareFloat(rdx, -size1side); 0521 CompareFloat(rdy, -size1side); 0522 0523 // If we restored this on the EAST side 0524 QVERIFY(cal.restore(cal.serialize(), ISD::Mount::PIER_EAST, reverseDecOnPierChange, binning, binning)); 0525 // ...RA moves should be inverted 0526 cal.rotateToRaDec(1.0, 0.0, &rdx, &rdy); 0527 CompareFloat(rdx, size1side); 0528 CompareFloat(rdy, -size1side); 0529 // ...and DEC moves should also invert. 0530 cal.rotateToRaDec(0.0, -1.0, &rdx, &rdy); 0531 CompareFloat(rdx, size1side); 0532 CompareFloat(rdy, size1side); 0533 0534 // If we then move back to the WEST side, we should get the original results. 0535 QVERIFY(cal.restore(cal.serialize(), ISD::Mount::PIER_WEST, reverseDecOnPierChange, binning, binning)); 0536 cal.rotateToRaDec(1.0, 0.0, &rdx, &rdy); 0537 CompareFloat(rdx, -size1side); 0538 CompareFloat(rdy, size1side); 0539 cal.rotateToRaDec(0.0, -1.0, &rdx, &rdy); 0540 CompareFloat(rdx, -size1side); 0541 CompareFloat(rdy, -size1side); 0542 0543 // Test adjusting the RA rate according to DEC. 0544 0545 dms calDec; 0546 calDec.setD(0, 0, 0); 0547 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, calDec); 0548 encodedCal = cal.serialize(); 0549 double raPulseRate = cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(); 0550 dms currDec; 0551 currDec.setD(0, 0, 0); 0552 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0553 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), raPulseRate); 0554 currDec.setD(70, 0, 0); 0555 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0556 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), 0557 raPulseRate / std::cos(70.0 * M_PI / 180.0)); 0558 currDec.setD(-45, 0, 0); 0559 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0560 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), 0561 raPulseRate / std::cos(45.0 * M_PI / 180.0)); 0562 currDec.setD(20, 0, 0); 0563 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0564 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), 0565 raPulseRate / std::cos(20.0 * M_PI / 180.0)); 0566 // Set the rate back to its original value. 0567 currDec.setD(0, 0, 0); 0568 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0569 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), raPulseRate); 0570 0571 // Change the calibration DEC. 0572 // A null calibration DEC should result in no-change to the ra pulse rate. 0573 dms nullDEC; 0574 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, nullDEC); 0575 encodedCal = cal.serialize(); 0576 raPulseRate = cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(); 0577 currDec.setD(20, 0, 0); 0578 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0579 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), raPulseRate); 0580 // Both 20 should result in no change. 0581 calDec.setD(20, 0, 0); 0582 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, calDec); 0583 encodedCal = cal.serialize(); 0584 currDec.setD(20, 0, 0); 0585 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0586 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), raPulseRate); 0587 // Cal 20 and current 45 should change the rate accordingly. 0588 currDec.setD(45, 0, 0); 0589 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0590 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), 0591 raPulseRate * std::cos(20.0 * M_PI / 180.0) / std::cos(45.0 * M_PI / 180.0)); 0592 // Changing cal dec to > 60-degrees or < -60 degrees results in no rate change. 0593 calDec.setD(65, 0, 0); 0594 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, calDec); 0595 encodedCal = cal.serialize(); 0596 raPulseRate = cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(); 0597 currDec.setD(20, 0, 0); 0598 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0599 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), raPulseRate); 0600 calDec.setD(-70, 0, 0); 0601 cal.setParameters(pixel_size, pixel_size, focal_length, binning, binning, side, ra, calDec); 0602 encodedCal = cal.serialize(); 0603 raPulseRate = cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(); 0604 currDec.setD(20, 0, 0); 0605 cal.restore(encodedCal, side, reverseDecOnPierChange, binning, binning, &currDec); 0606 CompareFloat(cal.raPulseMillisecondsPerArcsecond() * cal.xArcsecondsPerPixel(), raPulseRate); 0607 } 0608 0609 QTEST_GUILESS_MAIN(TestGuideStars)