File indexing completed on 2024-04-21 14:47:26

0001 /*
0002     KStars UI tests for meridian flip - special cases.
0003 
0004     SPDX-FileCopyrightText: 2020 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "test_ekos_meridianflip_specials.h"
0010 
0011 #if defined(HAVE_INDI)
0012 
0013 #include "kstars_ui_tests.h"
0014 #include "Options.h"
0015 #include "ekos/capture/capture.h"
0016 
0017 TestEkosMeridianFlipSpecials::TestEkosMeridianFlipSpecials(QObject *parent) : TestEkosMeridianFlipBase(parent)
0018 {
0019 }
0020 
0021 TestEkosMeridianFlipSpecials::TestEkosMeridianFlipSpecials(QString guider,
0022         QObject *parent) : TestEkosMeridianFlipBase(guider, parent)
0023 {
0024 }
0025 
0026 void TestEkosMeridianFlipSpecials::testCaptureGuidingDeviationMF()
0027 {
0028     // set up the capture sequence
0029     QVERIFY(prepareCaptureTestcase(40, true, false));
0030 
0031     // start guiding
0032     QVERIFY(m_CaptureHelper->startGuiding(2.0));
0033 
0034     // start capturing
0035     QVERIFY(startCapturing());
0036 
0037     // wait until a flip is planned
0038     QVERIFY(QTest::qWaitFor([&]()
0039     {
0040         return m_CaptureHelper->expectedMeridianFlipStates.head() != Ekos::MeridianFlipState::MOUNT_FLIP_PLANNED;
0041     }, 60000));
0042 
0043     qCInfo(KSTARS_EKOS_TEST()) << "Meridian flip planned...";
0044     // guiding deviation leads to a suspended capture
0045     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_SUSPENDED);
0046 
0047     // now send motion north to create a guiding deviation
0048     Ekos::Manager::Instance()->mountModule()->doPulse(RA_INC_DIR, 2000, DEC_INC_DIR, 2000);
0049     qCInfo(KSTARS_EKOS_TEST()) << "Sent 2000ms RA+DEC guiding.";
0050     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 20000);
0051 
0052     // check if meridian flip runs and completes successfully
0053     QVERIFY(checkMFExecuted(25));
0054 
0055     // set guards for post MF checks
0056     // 1. dithering happen after first capture otherwise it is sufficient to wait for start of capturing
0057     if (dithering_checked)
0058         m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_IMAGE_RECEIVED);
0059     else
0060         m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING);
0061 
0062     // 2. ensure that focusing starts
0063     if (refocus_checked)
0064         m_CaptureHelper->expectedFocusStates.enqueue(Ekos::FOCUS_PROGRESS);
0065 
0066     // check if guiding is running
0067     if (m_CaptureHelper->use_guiding)
0068     {
0069         m_CaptureHelper->expectedGuidingStates.enqueue(Ekos::GUIDE_GUIDING);
0070         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedGuidingStates, 30000);
0071     }
0072 
0073     // check refocusing, that should happen immediately after the guiding calibration
0074     // both for in sequence and time based re-focusing
0075     QVERIFY(checkRefocusing());
0076 
0077     // check if capturing has been started
0078     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0079 
0080     // After the first capture dithering should take place
0081     QVERIFY(checkDithering());
0082 }
0083 
0084 void TestEkosMeridianFlipSpecials::testCaptureGuidingRecalibrationMF()
0085 {
0086     // use three steps in each direction for calibration
0087     Options::setAutoModeIterations(3);
0088 
0089     // set up the capture sequence
0090     QVERIFY(prepareCaptureTestcase(30, false, false));
0091 
0092     // start guiding
0093     QVERIFY(m_CaptureHelper->startGuiding(2.0));
0094 
0095     // now enable resetting guiding calibration
0096     Options::setResetGuideCalibration(true);
0097     Options::setReuseGuideCalibration(false);
0098 
0099     // start capturing
0100     QVERIFY(startCapturing());
0101 
0102     // check if meridian flip runs and completes successfully
0103     QVERIFY(checkMFExecuted(45));
0104 
0105     // check if guiding calibration is executed
0106     m_CaptureHelper->expectedGuidingStates.enqueue(Ekos::GUIDE_CALIBRATING);
0107     m_CaptureHelper->expectedGuidingStates.enqueue(Ekos::GUIDE_CALIBRATION_SUCCESS);
0108     m_CaptureHelper->expectedGuidingStates.enqueue(Ekos::GUIDE_GUIDING);
0109     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING);
0110     // check if capturing starts right now
0111     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.isEmpty(), 120000);
0112     // check if calibration was finished
0113     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedGuidingStates.isEmpty(), 30000);
0114 }
0115 
0116 
0117 void TestEkosMeridianFlipSpecials::testCaptureDitheringDelayedAfterMF()
0118 {
0119     // set up the capture sequence
0120     QVERIFY(prepareCaptureTestcase(15, false, false));
0121 
0122     // start guiding
0123     QVERIFY(m_CaptureHelper->startGuiding(2.0));
0124 
0125     // start capturing
0126     QVERIFY(startCapturing());
0127 
0128     // check if single capture completes correctly
0129     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_IMAGE_RECEIVED);
0130     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 21000);
0131 
0132     // check if meridian flip runs and completes successfully
0133     QVERIFY(checkMFExecuted(25));
0134 
0135     // Now check if everything continues as it should be
0136     QVERIFY(checkPostMFBehavior());
0137 }
0138 
0139 
0140 void TestEkosMeridianFlipSpecials::testCaptureAlignGuidingPausedMF()
0141 {
0142     // set up the capture sequence
0143     QVERIFY(prepareCaptureTestcase(40, false, false));
0144 
0145     // start alignment
0146     QVERIFY(executeAlignment(5.0));
0147 
0148     // start guiding
0149     QVERIFY(m_CaptureHelper->startGuiding(2.0));
0150 
0151     // start capturing
0152     QVERIFY(startCapturing());
0153 
0154     // Let capture run a little bit
0155     QTest::qWait(5000);
0156 
0157     // switch to capture module
0158     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000);
0159 
0160     // stop capturing
0161     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_PAUSED);
0162     KTRY_CLICK(Ekos::Manager::Instance()->captureModule(), pauseB);
0163     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 20000);
0164 
0165     // check if meridian flip runs and completes successfully
0166     QVERIFY(checkMFExecuted(40));
0167 
0168     // check if capture remains paused (after a meridian flip it is marked as idle - bug or feature?)
0169     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_PAUSED, 5000);
0170 
0171     // Lets wait a little bit
0172     QTest::qWait(5000);
0173 
0174     // now finish pause
0175     qCInfo(KSTARS_EKOS_TEST) << "Finishing paused capture... ";
0176     KTRY_CLICK(Ekos::Manager::Instance()->captureModule(), startB);
0177 
0178     // Now check if everything continues as it should be
0179     QVERIFY(checkPostMFBehavior());
0180 }
0181 
0182 
0183 void TestEkosMeridianFlipSpecials::testCaptureAlignGuidingPauseMFPlanned()
0184 {
0185     // set up the capture sequence
0186     QVERIFY(prepareCaptureTestcase(10, false, false));
0187 
0188     // set a high delay so that it does not start too early
0189     QVERIFY(enableMeridianFlip(120.0));
0190 
0191     // start alignment
0192     QVERIFY(executeAlignment(5.0));
0193 
0194     // start guiding
0195     QVERIFY(m_CaptureHelper->startGuiding(2.0));
0196 
0197     // switch to capture module
0198     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000);
0199 
0200     // start capturing
0201     QVERIFY(startCapturing());
0202 
0203     // reset the MF delay after capturing has started
0204     QVERIFY(enableMeridianFlip(0.0));
0205 
0206     // Wait until the meridian flip has been planned
0207     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getMeridianFlipStatus() == Ekos::MeridianFlipState::MOUNT_FLIP_PLANNED, 60000);
0208 
0209     // pause capturing
0210     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_PAUSED);
0211     KTRY_CLICK(Ekos::Manager::Instance()->captureModule(), pauseB);
0212     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 40000);
0213 
0214     // check if meridian flip runs and completes successfully
0215     QVERIFY(checkMFExecuted(40));
0216 
0217     // check if capture remains paused (after a meridian flip it is marked as idle - bug or feature?)
0218     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_PAUSED, 5000);
0219 
0220     // Lets wait a little bit
0221     QTest::qWait(5000);
0222 
0223     // now finish pause
0224     qCInfo(KSTARS_EKOS_TEST) << "Finishing paused capture... ";
0225     KTRY_CLICK(Ekos::Manager::Instance()->captureModule(), startB);
0226 
0227     // Now check if everything continues as it should be
0228     QVERIFY(checkPostMFBehavior());
0229 }
0230 
0231 void TestEkosMeridianFlipSpecials::testAbortRefocusMF()
0232 {
0233     // set up the capture sequence
0234     QVERIFY(prepareCaptureTestcase(20, false, false));
0235     // refocus every 1min
0236     KTRY_SET_SPINBOX(Ekos::Manager::Instance()->captureModule(), limitRefocusN, 1);
0237     // add additional 5 degrees for delay to prevent a meridian flip before focusing starts
0238     KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->mountModule(), meridianFlipOffsetDegrees, 5.0);
0239 
0240     // start guiding
0241     QVERIFY(m_CaptureHelper->startGuiding(2.0));
0242 
0243     // start capturing
0244     QVERIFY(startCapturing());
0245 
0246     // expect focusing starts and aborts
0247     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_PROGRESS);
0248     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_ABORTED);
0249 
0250     // wait until focusing starts
0251     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getFocusStatus() == Ekos::FOCUS_PROGRESS, 90000);
0252     // trigger the meridian flip by clearing the offset
0253     meridianFlipOffsetDegrees->setValue(0.0);
0254     qCInfo(KSTARS_EKOS_TEST) << "Meridian flip offset cleared.";
0255     // expect focus abort due to started meridian flip
0256     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedFocusStates, 90000);
0257     qCInfo(KSTARS_EKOS_TEST) << "Focusing aborted.";
0258 
0259     // check if meridian flip runs and completes successfully
0260     QVERIFY(checkMFExecuted(40));
0261     // do not expect focusing to restart after the flip
0262     refocus_checked = false;
0263     // Now check if everything continues as it should be
0264     QVERIFY(checkPostMFBehavior());
0265 }
0266 
0267 void TestEkosMeridianFlipSpecials::testSchedulerCaptureMF()
0268 {
0269     // setup the scheduler
0270     QVERIFY(prepareSchedulerTestcase(15, false, Ekos::FINISH_LOOP, 1));
0271     // start the scheduled procedure
0272     QVERIFY(startScheduler());
0273     // check if meridian flip runs and completes successfully
0274     QVERIFY(checkMFExecuted(120));
0275     // Now check if everything continues as it should be
0276     QVERIFY(checkPostMFBehavior());
0277 }
0278 
0279 void TestEkosMeridianFlipSpecials::testAbortSchedulerRefocusMF()
0280 {
0281     // setup the scheduler
0282     QVERIFY(prepareSchedulerTestcase(20, false, Ekos::FINISH_LOOP, 1));
0283     // update the initial focuser position
0284     KTRY_GADGET(Ekos::Manager::Instance()->focusModule(), QLineEdit, absTicksLabel);
0285     initialFocusPosition = absTicksLabel->text().toInt();
0286     // start the scheduled procedure
0287     QVERIFY(startScheduler());
0288     // add additional 5 degrees for delay to prevent a meridian flip before focusing starts
0289     KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->mountModule(), meridianFlipOffsetDegrees, 5.0);
0290 
0291     // expect focusing starts and aborts
0292     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_PROGRESS);
0293     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_ABORTED);
0294 
0295     // wait until focusing starts
0296     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getFocusStatus() == Ekos::FOCUS_PROGRESS, 90000);
0297     // trigger the meridian flip by clearing the offset after 1 sec
0298     QTest::qWait(1000.0);
0299     meridianFlipOffsetDegrees->setValue(0.0);
0300     qCInfo(KSTARS_EKOS_TEST) << "Meridian flip offset cleared.";
0301     // expect focus abort due to started meridian flip
0302     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedFocusStates, 90000);
0303     qCInfo(KSTARS_EKOS_TEST) << "Focusing aborted.";
0304     // check if the focuser moved back to the last known focus position
0305     // moving back should be finished 5 secs after focusing aborted
0306     QTRY_VERIFY2_WITH_TIMEOUT(initialFocusPosition == absTicksLabel->text().toInt(),
0307                               QString("Focuser is at position %1 instead of initial focus position %2")
0308                               .arg(absTicksLabel->text()).arg(initialFocusPosition).toLocal8Bit(), 5000);
0309 
0310     // check if meridian flip runs and completes successfully
0311     QVERIFY(checkMFExecuted(120));
0312 
0313     // Now check if everything continues as it should be
0314     QVERIFY(checkPostMFBehavior());
0315 }
0316 
0317 void TestEkosMeridianFlipSpecials::testSimpleRepeatedMF()
0318 {
0319     // slew close to the meridian
0320     QVERIFY(positionMountForMF(7.0));
0321 
0322     // set the HA to delay the meridian flip by 2 min = 360° / 24 / 30 = 0.5°
0323     QProcess *indi_setprop = new QProcess(this);
0324     indi_setprop->start(QString("indi_setprop"), {QString("-n"), QString("%1.FLIP_HA.FLIP_HA=%2").arg(m_CaptureHelper->m_MountDevice).arg(0.5)});
0325 
0326     // check if meridian flip runs and completes successfully
0327     QVERIFY(checkMFExecuted(10));
0328 
0329     // pier side should still be west pointing east, i.e. no meridian flip took place
0330     KTRY_GADGET(Ekos::Manager::Instance()->mountModule(), QLabel, pierSideLabel);
0331     QTRY_VERIFY(pierSideLabel->text() == "Pier Side: West (pointing East)");
0332 
0333     qCInfo(KSTARS_EKOS_TEST()) << "Waiting 4 minutes for a second meridian flip...";
0334     // expected beginning of the meridian flip
0335     m_CaptureHelper->expectedMeridianFlipStates.enqueue(Ekos::MeridianFlipState::MOUNT_FLIP_PLANNED);
0336     m_CaptureHelper->expectedMeridianFlipStates.enqueue(Ekos::MeridianFlipState::MOUNT_FLIP_RUNNING);
0337 
0338     // but the pier side should not change, so lets wait for 4 minutes for a second meridian flip
0339     QVERIFY(checkMFExecuted(4 * 60 + 10));
0340 
0341     // set back the HA to delay the meridian flip
0342     indi_setprop = new QProcess(this);
0343     indi_setprop->start(QString("indi_setprop"), {QString("-n"), QString("%1.FLIP_HA.FLIP_HA=%2").arg(m_CaptureHelper->m_MountDevice).arg(0)});
0344 }
0345 
0346 void TestEkosMeridianFlipSpecials::testCaptureRealignMF()
0347 {
0348     if (!astrometry_available)
0349         QSKIP("No astrometry files available to run test");
0350 
0351     // prepare for alignment tests
0352     m_CaptureHelper->prepareAlignmentModule();
0353     // enforce re-alignment
0354     Options::setAlignCheckFrequency(1);
0355     Options::setAlignCheckThreshold(0.0);
0356     // setup the scheduler
0357     QVERIFY(prepareSchedulerTestcase(17, true, Ekos::FINISH_REPEAT, 1));
0358     // start the scheduled procedure
0359     QVERIFY(startScheduler());
0360     // make the alignment exposure so long that the flip happens while capturing the frame for alignment
0361     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_CAPTURING, 60000);
0362     KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->alignModule(), alignExposure, 60.0);
0363     qCInfo(KSTARS_EKOS_TEST()) << "Setting alignment exposure to 60s.";
0364     // check if meridian flip has been started
0365     QVERIFY(checkMFStarted(120));
0366     // set the alignment exposure time back
0367     alignExposure->setValue(5.0);
0368     qCInfo(KSTARS_EKOS_TEST()) << "Setting alignment exposure back to 5s.";
0369     // check if meridian flip has been completed
0370     QVERIFY(checkMFExecuted(120));
0371     // Now check if after the flip everything continues as it should be
0372     QVERIFY(checkPostMFBehavior());
0373     // check if capturing starts right now
0374     QFETCH(double, exptime);
0375     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_CAPTURING, 2 * exptime * 1000);
0376     qCInfo(KSTARS_EKOS_TEST()) << "Capturing started.";
0377     // check if an image has been captured
0378     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_IMAGE_RECEIVED);
0379     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.isEmpty(), 30000);
0380 }
0381 
0382 void TestEkosMeridianFlipSpecials::testCapturePostRealignmentFailedHandling()
0383 {
0384     if (!astrometry_available)
0385         QSKIP("No astrometry files available to run test");
0386 
0387     // prepare for alignment tests
0388     m_CaptureHelper->prepareAlignmentModule();
0389     // setup the scheduler
0390     QVERIFY(prepareSchedulerTestcase(17, true, Ekos::FINISH_REPEAT, 1));
0391     // start the scheduled procedure
0392     QVERIFY(startScheduler());
0393     // check if meridian flip has been started
0394     QVERIFY(checkMFStarted(120));
0395     // Create massive noise such that solving fails
0396     KTRY_INDI_PROPERTY(m_CaptureHelper->m_CCDDevice, "Simulator Config", "SIMULATOR_SETTINGS", ccd_settings);
0397     INDI_E *noise_setting = ccd_settings->getElement("SIM_NOISE");
0398     QVERIFY(ccd_settings != nullptr);
0399     noise_setting->setValue(100.0);
0400     ccd_settings->processSetButton();
0401     // set the alignment exposure so low that alignment fails
0402     KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->alignModule(), alignExposure, 0.1);
0403     // check if meridian flip has been completed
0404     QVERIFY(checkMFExecuted(120));
0405     // expect 4 failed alignments (normal, blind solve + 2x retrying)
0406     for (int i = 0; i < 4; i++)
0407         m_CaptureHelper->expectedAlignStates.enqueue(Ekos::ALIGN_FAILED);
0408     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedAlignStates.isEmpty(), 30000);
0409 }
0410 
0411 
0412 /* *********************************************************************************
0413  *
0414  * Test data
0415  *
0416  * ********************************************************************************* */
0417 
0418 void TestEkosMeridianFlipSpecials::testCaptureGuidingDeviationMF_data()
0419 {
0420     prepareTestData(45.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {true}, {false, true});
0421 }
0422 
0423 void TestEkosMeridianFlipSpecials::testCaptureGuidingRecalibrationMF_data()
0424 {
0425     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 6}, {"Red,Green,Blue,Red,Green,Blue", 1}}, {0}, {true}, {false});
0426 }
0427 
0428 void TestEkosMeridianFlipSpecials::testCaptureDitheringDelayedAfterMF_data()
0429 {
0430     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Red,Green,Blue,Red,Green,Blue", 1}}, {0}, {true}, {true});
0431 }
0432 
0433 void TestEkosMeridianFlipSpecials::testCaptureAlignGuidingPausedMF_data()
0434 {
0435     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {true}, {false});
0436 }
0437 
0438 void TestEkosMeridianFlipSpecials::testCaptureAlignGuidingPauseMFPlanned_data()
0439 {
0440     prepareTestData(12.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {true}, {false});
0441 }
0442 
0443 void TestEkosMeridianFlipSpecials::testAbortRefocusMF_data()
0444 {
0445     prepareTestData(32.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {1}, {false}, {false});
0446 }
0447 
0448 void TestEkosMeridianFlipSpecials::testSchedulerCaptureMF_data()
0449 {
0450     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 1}}, {0}, {true, false}, {false});
0451 }
0452 
0453 void TestEkosMeridianFlipSpecials::testAbortSchedulerRefocusMF_data()
0454 {
0455     prepareTestData(30.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {1}, {true, false}, {false});
0456 }
0457 
0458 void TestEkosMeridianFlipSpecials::testSimpleRepeatedMF_data()
0459 {
0460     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {false}, {false});
0461 }
0462 
0463 void TestEkosMeridianFlipSpecials::testCaptureRealignMF_data()
0464 {
0465     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {false}, {false});
0466 }
0467 
0468 void TestEkosMeridianFlipSpecials::testCapturePostRealignmentFailedHandling_data()
0469 {
0470     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {false}, {false});
0471 }
0472 
0473 
0474 QTEST_KSTARS_WITH_GUIDER_MAIN(TestEkosMeridianFlipSpecials)
0475 
0476 #endif // HAVE_INDI