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

0001 /*  KStars UI tests
0002     SPDX-FileCopyrightText: 2020 Eric Dejouhanet <eric.dejouhanet@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 
0008 #include "test_ekos_focus.h"
0009 
0010 #if defined(HAVE_INDI)
0011 
0012 #include "kstars_ui_tests.h"
0013 #include "test_ekos.h"
0014 #include "test_ekos_simulator.h"
0015 #include "test_ekos_mount.h"
0016 #include "Options.h"
0017 
0018 class KFocusProcedureSteps: public QObject
0019 {
0020     public:
0021         QMetaObject::Connection starting;
0022         QMetaObject::Connection aborting;
0023         QMetaObject::Connection completing;
0024         QMetaObject::Connection notguiding;
0025         QMetaObject::Connection guiding;
0026         QMetaObject::Connection quantifying;
0027 
0028     public:
0029         bool started { false };
0030         bool aborted { false };
0031         bool complete { false };
0032         bool unguided { false };
0033         double hfr { -2 };
0034 
0035     public:
0036         KFocusProcedureSteps():
0037             starting (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusStarting, this, [ & ]()
0038         {
0039             started = true;
0040         }, Qt::UniqueConnection)),
0041         aborting (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusAborted, this, [&]()
0042         {
0043             started = false;
0044             aborted = true;
0045         }, Qt::UniqueConnection)),
0046         completing (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusComplete, this, [&]()
0047         {
0048             started = false;
0049             complete = true;
0050         }, Qt::UniqueConnection)),
0051         notguiding (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::suspendGuiding, this, [&]()
0052         {
0053             unguided = true;
0054         }, Qt::UniqueConnection)),
0055         guiding (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::resumeGuiding, this, [&]()
0056         {
0057             unguided = false;
0058         }, Qt::UniqueConnection)),
0059         quantifying (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::newHFR, this, [&](double _hfr)
0060         {
0061             hfr = _hfr;
0062         }, Qt::UniqueConnection))
0063         {};
0064         virtual ~KFocusProcedureSteps()
0065         {
0066             disconnect(starting);
0067             disconnect(aborting);
0068             disconnect(completing);
0069             disconnect(notguiding);
0070             disconnect(guiding);
0071             disconnect(quantifying);
0072         };
0073 };
0074 
0075 class KFocusStateList: public QObject, public QList <Ekos::FocusState>
0076 {
0077     public:
0078         QMetaObject::Connection handler;
0079 
0080     public:
0081         KFocusStateList():
0082             handler (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::newStatus, this, [ & ](Ekos::FocusState s)
0083         {
0084             append(s);
0085         }, Qt::UniqueConnection))
0086         {};
0087         virtual ~KFocusStateList() {};
0088 };
0089 
0090 TestEkosFocus::TestEkosFocus(QObject *parent) : QObject(parent)
0091 {
0092 }
0093 
0094 void TestEkosFocus::initTestCase()
0095 {
0096     KVERIFY_EKOS_IS_HIDDEN();
0097     KTRY_OPEN_EKOS();
0098     KVERIFY_EKOS_IS_OPENED();
0099     KTRY_EKOS_START_SIMULATORS();
0100 
0101     // We can't use this here because of the meridian flip test
0102     // HACK: Reset clock to initial conditions
0103     // KHACK_RESET_EKOS_TIME();
0104 
0105     KTELL_BEGIN();
0106 }
0107 
0108 void TestEkosFocus::cleanupTestCase()
0109 {
0110     KTELL_END();
0111     KTRY_EKOS_STOP_SIMULATORS();
0112     KTRY_CLOSE_EKOS();
0113     KVERIFY_EKOS_IS_HIDDEN();
0114 }
0115 
0116 void TestEkosFocus::init()
0117 {
0118 }
0119 
0120 void TestEkosFocus::cleanup()
0121 {
0122     if (Ekos::Manager::Instance())
0123         if (Ekos::Manager::Instance()->focusModule())
0124             Ekos::Manager::Instance()->focusModule()->abort();
0125     KTELL_HIDE();
0126 }
0127 
0128 void TestEkosFocus::testCaptureStates()
0129 {
0130     KTELL("Sync high on meridian to avoid jitter in CCD Simulator.");
0131     KTRY_MOUNT_SYNC(60.0, true, -1);
0132 
0133     // Prepare to detect state change
0134     KFocusStateList state_list;
0135     QVERIFY(state_list.handler);
0136 
0137     KTELL("Configure fields.\nCapture a frame.\nExpect PROGRESS, IDLE.");
0138     KTRY_FOCUS_MOVETO(40000);
0139     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
0140     KTRY_FOCUS_DETECT(2, 1, 99);
0141     QTRY_COMPARE_WITH_TIMEOUT(state_list.count(), 2, 5000);
0142     QCOMPARE(state_list[0], Ekos::FocusState::FOCUS_PROGRESS);
0143     QCOMPARE(state_list[1], Ekos::FocusState::FOCUS_IDLE);
0144     state_list.clear();
0145 
0146     KTELL("Move focuser.\nExpect no capture triggered.");
0147     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
0148     KTRY_FOCUS_MOVETO(43210);
0149     QTest::qWait(1000);
0150     QCOMPARE(state_list.count(), 0);
0151 
0152     KTRY_FOCUS_GADGET(QPushButton, startLoopB);
0153     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0154 
0155     KTELL("Loop captures.\nAbort loop.\nExpect FRAMING, PROGRESS, ABORTED, ABORTED.");
0156     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
0157     KTRY_FOCUS_CLICK(startLoopB);
0158     QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 1, 5000);
0159     KTRY_FOCUS_CLICK(stopFocusB);
0160     QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 4, 5000);
0161     QCOMPARE((int)state_list[0], (int)Ekos::FocusState::FOCUS_FRAMING);
0162     QCOMPARE((int)state_list[1], (int)Ekos::FocusState::FOCUS_PROGRESS);
0163     QCOMPARE((int)state_list[2], (int)Ekos::FocusState::FOCUS_ABORTED);
0164     QCOMPARE((int)state_list[3], (int)Ekos::FocusState::FOCUS_ABORTED);
0165     state_list.clear();
0166 
0167     KTRY_FOCUS_GADGET(QCheckBox, useAutoStar);
0168     KTRY_FOCUS_GADGET(QPushButton, resetFrameB);
0169 
0170     QWARN("This test does not wait for the hardcoded timeout to select a star.");
0171 
0172     KTELL("Use a successful automatic star selection (not full-field).\nCapture a frame.\nExpect PROGRESS, IDLE\nCheck star selection.");
0173     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.0, 3.0);
0174     useAutoStar->setCheckState(Qt::CheckState::Checked);
0175     KTRY_FOCUS_DETECT(2, 1, 99);
0176     QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 2, 5000);
0177     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0178     KTRY_FOCUS_CLICK(resetFrameB);
0179     QCOMPARE(state_list[0], Ekos::FocusState::FOCUS_PROGRESS);
0180     QCOMPARE(state_list[1], Ekos::FocusState::FOCUS_IDLE);
0181     useAutoStar->setCheckState(Qt::CheckState::Unchecked);
0182     state_list.clear();
0183 
0184     KTELL("Use an unsuccessful automatic star selection (not full-field).\nCapture a frame\nExpect PROGRESS, WAITING.\nCheck star selection.");
0185     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.0, 3.0);
0186     useAutoStar->setCheckState(Qt::CheckState::Checked);
0187     KTRY_FOCUS_DETECT(0.01, 1, 1);
0188     QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 2, 5000);
0189     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0190     KTRY_FOCUS_CLICK(resetFrameB);
0191     QCOMPARE(state_list[0], Ekos::FocusState::FOCUS_PROGRESS);
0192     QCOMPARE(state_list[1], Ekos::FocusState::FOCUS_WAITING);
0193     useAutoStar->setCheckState(Qt::CheckState::Unchecked);
0194     state_list.clear();
0195 }
0196 
0197 void TestEkosFocus::testDuplicateFocusRequest()
0198 {
0199     KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a fast autofocus.");
0200     KTRY_FOCUS_SHOW();
0201     KTRY_MOUNT_SYNC(60.0, true, -1);
0202     KTRY_FOCUS_MOVETO(35000);
0203     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.0, 30);
0204     KTRY_FOCUS_EXPOSURE(3, 99);
0205 
0206     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0207     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0208     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0209     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0210 
0211     // Prepare to detect the beginning of the autofocus_procedure
0212     KFocusProcedureSteps autofocus;
0213     QVERIFY(autofocus.starting);
0214 
0215     KTELL("Click the autofocus button\nExpect a signal that the procedure starts.\nExpect state change and disabled button.");
0216     KTRY_FOCUS_CLICK(startFocusB);
0217     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0218     QVERIFY(Ekos::Manager::Instance()->focusModule()->status() != Ekos::FOCUS_IDLE);
0219     QVERIFY(!startFocusB->isEnabled());
0220     QVERIFY(stopFocusB->isEnabled());
0221 
0222     KTELL("Issue a few autofocus commands at that point through the d-bus entry point\nExpect no parallel procedure start.");
0223     for (int i = 0; i < 5; i++)
0224     {
0225         autofocus.started = false;
0226         Ekos::Manager::Instance()->focusModule()->start();
0227         QTest::qWait(500);
0228         QVERIFY(!autofocus.started);
0229     }
0230 
0231     KTELL("Stop the running autofocus.");
0232     KTRY_FOCUS_CLICK(stopFocusB);
0233     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
0234 }
0235 
0236 void TestEkosFocus::testAutofocusSignalEmission()
0237 {
0238     KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure fast autofocus.");
0239     KTRY_FOCUS_SHOW();
0240     KTRY_MOUNT_SYNC(60.0, true, -1);
0241 
0242     KTRY_FOCUS_MOVETO(35000);
0243     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 30);
0244     KTRY_FOCUS_EXPOSURE(3, 99);
0245 
0246     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0247     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0248     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0249     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0250 
0251     // Prepare to detect the beginning of the autofocus_procedure
0252     KFocusProcedureSteps autofocus;
0253     QVERIFY(autofocus.starting);
0254 
0255     KTELL("Configure to restart autofocus when it finishes, like Scheduler does.");
0256     volatile bool ran_once = false;
0257     autofocus.completing = connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusComplete, &autofocus, [&]()
0258     {
0259         autofocus.complete = true;
0260         autofocus.started = false;
0261         if (!ran_once)
0262         {
0263             Ekos::Manager::Instance()->focusModule()->start();
0264             ran_once = true;
0265         }
0266     }, Qt::UniqueConnection);
0267     QVERIFY(autofocus.completing);
0268 
0269     KTELL("Run autofocus, wait for completion.\nHandler restarts a second one immediately.");
0270     QVERIFY(!autofocus.started);
0271     QVERIFY(!autofocus.complete);
0272     KTRY_FOCUS_CLICK(startFocusB);
0273     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0274     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
0275 
0276     KTELL("Wait for the second run to finish.\nNo other autofocus started.");
0277     autofocus.complete = false;
0278     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
0279     QVERIFY(!autofocus.started);
0280 }
0281 
0282 void TestEkosFocus::testFocusAbort()
0283 {
0284     KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure fast autofocus.");
0285     KTRY_FOCUS_SHOW();
0286     KTRY_MOUNT_SYNC(60.0, true, -1);
0287     KTRY_FOCUS_MOVETO(35000);
0288     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 30);
0289     KTRY_FOCUS_EXPOSURE(3, 99);
0290 
0291     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0292     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0293     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0294     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0295 
0296     // Prepare to detect the beginning of the autofocus_procedure
0297     KFocusProcedureSteps autofocus;
0298     QVERIFY(autofocus.starting);
0299     QVERIFY(autofocus.completing);
0300 
0301     KTELL("Configure to restart autofocus when it finishes, like Scheduler does.");
0302     volatile bool ran_once = false;
0303     autofocus.aborting = connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusAborted, this, [&]()
0304     {
0305         autofocus.aborted = true;
0306         autofocus.started = false;
0307         if (!ran_once)
0308         {
0309             Ekos::Manager::Instance()->focusModule()->start();
0310             ran_once = true;
0311         }
0312     }, Qt::UniqueConnection);
0313     QVERIFY(autofocus.aborting);
0314 
0315     KTELL("Run autofocus, don't wait for the completion signal and abort it.\nHandler restarts a second one immediately.");
0316     QVERIFY(!autofocus.started);
0317     QVERIFY(!autofocus.aborted);
0318     QVERIFY(!autofocus.complete);
0319     KTRY_FOCUS_CLICK(startFocusB);
0320     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0321     KTRY_FOCUS_CLICK(stopFocusB);
0322     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 1000);
0323 
0324     KTELL("Wait for the second run to finish.\nNo other autofocus started.");
0325     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0326     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
0327     QVERIFY(!autofocus.started);
0328 }
0329 
0330 void TestEkosFocus::testGuidingSuspendWhileFocusing()
0331 {
0332     KTELL("Sync high on meridian to avoid jitter in CCD Simulator\nConfigure a fast autofocus.");
0333     KTRY_FOCUS_SHOW();
0334     KTRY_MOUNT_SYNC(60.0, true, -1);
0335     KTRY_FOCUS_MOVETO(35000);
0336     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 30);
0337     KTRY_FOCUS_EXPOSURE(3, 99);
0338 
0339     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0340     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0341     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0342     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0343 
0344     // Prepare to detect the beginning of the autofocus_procedure
0345     KFocusProcedureSteps autofocus;
0346     QVERIFY(autofocus.starting);
0347     QVERIFY(autofocus.aborting);
0348     QVERIFY(autofocus.completing);
0349     QVERIFY(autofocus.notguiding);
0350     QVERIFY(autofocus.guiding);
0351 
0352     KTRY_FOCUS_GADGET(QCheckBox, suspendGuideCheck);
0353 
0354     KTELL("Abort the autofocus with guiding set to suspend\nGuiding required to suspend, then required to resume");
0355     suspendGuideCheck->setCheckState(Qt::CheckState::Checked);
0356     QVERIFY(!autofocus.started);
0357     QVERIFY(!autofocus.aborted);
0358     QVERIFY(!autofocus.complete);
0359     QVERIFY(!autofocus.unguided);
0360     KTRY_FOCUS_CLICK(startFocusB);
0361     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0362     QVERIFY(autofocus.unguided);
0363     Ekos::Manager::Instance()->focusModule()->abort();
0364     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
0365     QVERIFY(!autofocus.unguided);
0366 
0367     KTELL("Run the autofocus to completion with guiding set to suspend\nGuiding required to suspend, then required to resume\nNo other autofocus started.");
0368     autofocus.started = autofocus.aborted = false;
0369     KTRY_FOCUS_CLICK(startFocusB);
0370     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0371     QVERIFY(autofocus.unguided);
0372     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
0373     QVERIFY(!autofocus.unguided);
0374     QVERIFY(!autofocus.started);
0375 
0376     KTELL("Abort the autofocus with guiding set to continue\nNo guiding signal emitted");
0377     suspendGuideCheck->setCheckState(Qt::CheckState::Unchecked);
0378     autofocus.started = autofocus.aborted = autofocus.complete = autofocus.unguided = false;
0379     KTRY_FOCUS_CLICK(startFocusB);
0380     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0381     QVERIFY(!autofocus.unguided);
0382     Ekos::Manager::Instance()->focusModule()->abort();
0383     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
0384     QVERIFY(!autofocus.unguided);
0385 
0386     KTELL("Run the autofocus to completion with guiding set to continue\nNo guiding signal emitted\nNo other autofocus started.");
0387     autofocus.started = autofocus.aborted = false;
0388     KTRY_FOCUS_CLICK(startFocusB);
0389     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0390     QVERIFY(!autofocus.unguided);
0391     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
0392     QVERIFY(!autofocus.unguided);
0393     QVERIFY(!autofocus.started);
0394 }
0395 
0396 void TestEkosFocus::testFocusWhenMountFlips()
0397 {
0398     KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a fast autofocus.");
0399     KTRY_FOCUS_SHOW();
0400     KTRY_MOUNT_SYNC(60.0, true, +10.0 / 3600);
0401     KTRY_FOCUS_MOVETO(35000);
0402     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 5);
0403     KTRY_FOCUS_EXPOSURE(3, 99);
0404 
0405     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0406     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0407     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0408     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0409 
0410     // Prepare to detect the states of the autofocus_procedure
0411     KFocusProcedureSteps autofocus;
0412     QVERIFY(autofocus.starting);
0413     QVERIFY(autofocus.aborting);
0414     QVERIFY(autofocus.completing);
0415 
0416     KTELL("Ensure flip is enabled on meridian.\n.Start a standard autofocus.");
0417     Ekos::Manager::Instance()->mountModule()->setMeridianFlipValues(true, 0);
0418     QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->meridianFlipEnabled(), 1000);
0419     QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->meridianFlipValue() == 0, 1000);
0420     QVERIFY(!autofocus.started);
0421     QVERIFY(!autofocus.aborted);
0422     QVERIFY(!autofocus.complete);
0423     KTRY_FOCUS_CLICK(startFocusB);
0424     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 5000);
0425 
0426     KTELL("Wait for the meridian flip to occur.\nCheck procedure aborts while flipping.");
0427     QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->status() == ISD::Mount::MOUNT_SLEWING, 15000);
0428     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
0429 
0430     KTELL("Wait for the flip to end.");
0431     QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->status() == ISD::Mount::MOUNT_TRACKING, 120000);
0432 
0433     KTELL("Start the procedure again.\nExpect the procedure to succeed.");
0434     autofocus.started = false;
0435     autofocus.aborted = false;
0436     autofocus.complete = false;
0437     KTRY_FOCUS_CLICK(startFocusB);
0438     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 60000);
0439 }
0440 
0441 void TestEkosFocus::testFocusWhenHFRChecking()
0442 {
0443     KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a fast autofocus.");
0444     KTRY_FOCUS_SHOW();
0445     KTRY_MOUNT_SYNC(60.0, true, -1);
0446     int initialFocusPosition = 35000;
0447     KTRY_FOCUS_MOVETO(initialFocusPosition);
0448     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 50);
0449     KTRY_FOCUS_EXPOSURE(3, 99);
0450 
0451     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0452     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0453     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0454     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0455 
0456     // Prepare to detect the beginning of the autofocus_procedure
0457     KFocusProcedureSteps autofocus;
0458     QVERIFY(autofocus.starting);
0459     QVERIFY(autofocus.aborting);
0460     QVERIFY(autofocus.completing);
0461 
0462     KTELL("Run a standard autofocus.\nRun a HFR check.\nExpect no effect on the procedure.");
0463     QVERIFY(!autofocus.started);
0464     QVERIFY(!autofocus.aborted);
0465     QVERIFY(!autofocus.complete);
0466     KTRY_FOCUS_CLICK(startFocusB);
0467     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
0468 
0469     KTELL("Wait a little, run a first HFR check while the procedure runs.");
0470     QTest::qWait(3000);
0471     Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
0472 
0473     KTELL("Expect procedure to succeed nonetheless.");
0474     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 60000);
0475 
0476     KTELL("Run a second HFR check that would start an autofocus.");
0477     autofocus.complete = false;
0478     Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
0479 
0480     KTELL("Expect procedure to start properly.\nAbort the procedure manually.\nRun a third HFR check.");
0481     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
0482     KTRY_FOCUS_CLICK(stopFocusB);
0483     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 10000);
0484 
0485     KTELL("Expect autofocus to start properly.\nChange settings so that the procedure fails now.\nExpect a failure.");
0486     autofocus.aborted = autofocus.complete = false;
0487     Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
0488     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
0489     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.1, 0.1);
0490     KTRY_FOCUS_EXPOSURE(0.1, 1);
0491     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 90000);
0492     KTRY_FOCUS_CHECK_POSITION_WITH_TIMEOUT(initialFocusPosition, 5000);
0493 
0494     KTELL("Run a fourth HFR check.\nExpect autofocus to complete.");
0495     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 50);
0496     KTRY_FOCUS_EXPOSURE(3, 99);
0497     autofocus.aborted = autofocus.complete = false;
0498     Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
0499     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
0500     QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 60000);
0501 }
0502 
0503 void TestEkosFocus::testFocusFailure()
0504 {
0505     KTELL("Sync high on meridian to avoid jitter in CCD Simulator");
0506     KTRY_FOCUS_SHOW();
0507     KTRY_MOUNT_SYNC(60.0, true, -1);
0508 
0509     KTELL("Configure an autofocus that cannot see any star, so that the initial setup fails.");
0510     KTRY_FOCUS_MOVETO(10000);
0511     KTRY_FOCUS_CONFIGURE("SEP", "Polynomial", 0.0, 1.0, 0.1);
0512     KTRY_FOCUS_EXPOSURE(0.01, 1);
0513 
0514     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0515     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0516     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0517     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0518 
0519     // Prepare to detect the beginning of the autofocus_procedure
0520     KFocusProcedureSteps autofocus;
0521     QVERIFY(autofocus.starting);
0522     QVERIFY(autofocus.aborting);
0523     QVERIFY(autofocus.completing);
0524 
0525     KTELL("Run the autofocus, wait for the completion signal.\nExpect no further autofocus started as we are not running a sequence.");
0526     QVERIFY(!autofocus.started);
0527     QVERIFY(!autofocus.aborted);
0528     QVERIFY(!autofocus.complete);
0529     KTRY_FOCUS_CLICK(startFocusB);
0530     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0531     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 30000);
0532     QVERIFY(!autofocus.started);
0533 
0534     QSKIP("Skipping abort test for device limits, focus algorithms are too sensitive to CCD Sim noise.");
0535 
0536     KTELL("Configure an autofocus that can see stars but is too far off and cannot achieve focus, so that the procedure fails.");
0537     KTRY_FOCUS_MOVETO(25000);
0538     QWARN("Iterative and Polynomial are too easily successful for this test.");
0539     KTRY_FOCUS_CONFIGURE("SEP", "Linear", 0.0, 100.0, 1.0);
0540     KTRY_FOCUS_EXPOSURE(5, 99);
0541     KTRY_FOCUS_GADGET(QDoubleSpinBox, maxTravelIN);
0542     maxTravelIN->setValue(2000);
0543 
0544     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0545     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0546     autofocus.started = false;
0547     autofocus.aborted = false;
0548     autofocus.complete = false;
0549 
0550     KTELL("Run the autofocus, wait for the completion signal.\nNo further autofocus started as we are not running a sequence.");
0551     QVERIFY(!autofocus.started);
0552     QVERIFY(!autofocus.aborted);
0553     QVERIFY(!autofocus.complete);
0554     KTRY_FOCUS_CLICK(startFocusB);
0555     QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
0556     QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 240000);
0557     QVERIFY(!autofocus.started);
0558 }
0559 
0560 void TestEkosFocus::testStarDetection_data()
0561 {
0562 #if QT_VERSION < 0x050900
0563     QSKIP("Skipping fixture-based test on old QT version.");
0564 #else
0565     QTest::addColumn<QString>("NAME");
0566     QTest::addColumn<QString>("RA");
0567     QTest::addColumn<QString>("DEC");
0568 
0569     // Altitude computation taken from SchedulerJob::findAltitude
0570     GeoLocation * const geo = KStarsData::Instance()->geo();
0571     KStarsDateTime const now(KStarsData::Instance()->lt());
0572     KSNumbers const numbers(now.djd());
0573     CachingDms const LST = geo->GSTtoLST(geo->LTtoUT(now).gst());
0574 
0575     std::list<char const *> Objects = { "Polaris", "Mizar", "M 51", "M 13", "M 47", "Vega", "NGC 2238", "M 81" };
0576     size_t count = 0;
0577 
0578     foreach (char const *name, Objects)
0579     {
0580         SkyObject const * const so = KStars::Instance()->data()->objectNamed(name);
0581         if (so != nullptr)
0582         {
0583             SkyObject o(*so);
0584             o.updateCoordsNow(&numbers);
0585             o.EquatorialToHorizontal(&LST, geo->lat());
0586             if (10.0 < o.alt().Degrees())
0587             {
0588                 QTest::addRow("%s", name)
0589                         << name
0590                         << o.ra().toHMSString()
0591                         << o.dec().toDMSString();
0592                 count++;
0593             }
0594             else QWARN(QString("Fixture '%1' altitude is '%2' degrees, discarding.").arg(name).arg(
0595                                so->alt().Degrees()).toStdString().c_str());
0596         }
0597     }
0598 
0599     if (!count)
0600         QSKIP("No usable fixture objects, bypassing test.");
0601 #endif
0602 }
0603 
0604 void TestEkosFocus::testStarDetection()
0605 {
0606 
0607 #if QT_VERSION < 0x050900
0608     QSKIP("Skipping fixture-based test on old QT version.");
0609 #else
0610     Ekos::Manager * const ekos = Ekos::Manager::Instance();
0611     QVERIFY(ekos);
0612 
0613     QFETCH(QString, NAME);
0614     QFETCH(QString, RA);
0615     QFETCH(QString, DEC);
0616 
0617     KTELL(QString(NAME + "\nSync to %1/%2 to make the mount teleport to the object.").arg(qPrintable(RA)).arg(qPrintable(DEC)));
0618     QTRY_VERIFY_WITH_TIMEOUT(ekos->mountModule() != nullptr, 5000);
0619     ekos->mountModule()->setMeridianFlipValues(false, 0);
0620     QVERIFY(ekos->mountModule()->sync(RA, DEC));
0621     ekos->mountModule()->setTrackEnabled(true);
0622 
0623     KTELL(NAME + "\nWait for Focus to come up\nSwitch to Focus tab.");
0624     KTRY_FOCUS_SHOW();
0625 
0626     KTRY_FOCUS_GADGET(QPushButton, startFocusB);
0627     KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
0628     QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
0629     QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
0630 
0631     KTRY_FOCUS_GADGET(QLineEdit, starsOut);
0632 
0633     KTELL(NAME + "\nMove focuser to see stars.");
0634     KTRY_FOCUS_MOVETO(35000);
0635 
0636     KTELL(NAME + "\nRun the detection with SEP.");
0637     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
0638     KTRY_FOCUS_DETECT(1, 3, 99);
0639     QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
0640 
0641     KTELL(NAME + "\nRun the detection with Centroid.");
0642     KTRY_FOCUS_CONFIGURE("Centroid", "Iterative", 0.0, 100.0, 3.0);
0643     KTRY_FOCUS_DETECT(1, 3, 99);
0644     QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
0645 
0646     KTELL(NAME + "\nRun the detection with Threshold (no full-field).");
0647     KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0.0, 0.0, 3.0);
0648     KTRY_FOCUS_DETECT(1, 3, 99);
0649     QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
0650 
0651     KTELL(NAME + "\nRun the detection with Gradient (no full-field).");
0652     KTRY_FOCUS_CONFIGURE("Gradient", "Iterative", 0.0, 0.0, 3.0);
0653     KTRY_FOCUS_DETECT(1, 3, 99);
0654     QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
0655 
0656     KTELL(NAME + "\nRun the detection with SEP (8s capture).");
0657     KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
0658     KTRY_FOCUS_DETECT(8, 1, 99);
0659     QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
0660 
0661     KTELL(NAME + "\nRun the detection with SEP\nFull-field with various values\nHFR averaged on 3 frames.");
0662     for (double inner = 0.0; inner < 100.0; inner += 43.0)
0663     {
0664         for (double outer = 100.0; inner < outer; outer -= 42.0)
0665         {
0666             KTRY_FOCUS_CONFIGURE("SEP", "Iterative", inner, outer, 3.0);
0667             KTRY_FOCUS_DETECT(1, 2, 99);
0668         }
0669     }
0670 
0671     KTELL(NAME + "\nRun the detection with Threshold, full-field.");
0672     for (double threshold = 80.0; threshold < 99.0; threshold += 13.3)
0673     {
0674         KTRY_FOCUS_GADGET(QDoubleSpinBox, thresholdSpin);
0675         thresholdSpin->setValue(threshold);
0676         KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0, 0.0, 3.0);
0677         KTRY_FOCUS_DETECT(1, 1, 99);
0678     }
0679 #endif
0680 }
0681 
0682 QTEST_KSTARS_MAIN(TestEkosFocus)
0683 
0684 #endif // HAVE_INDI