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

0001 /*
0002     KStars UI tests for capture workflows (re-focus, dithering, guiding, ...)
0003 
0004     SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "test_ekos_capture_workflow.h"
0010 
0011 #include "kstars_ui_tests.h"
0012 #include "ui_calibrationoptions.h"
0013 #include "indicom.h"
0014 #include "Options.h"
0015 #include "skymapcomposite.h"
0016 #include "ekos/capture/sequencejobstate.h"
0017 #include "ekos/capture/capture.h"
0018 
0019 #define SHUTTER_UNKNOWN -1
0020 #define SHUTTER_NO       0
0021 #define SHUTTER_YES      1
0022 
0023 
0024 TestEkosCaptureWorkflow::TestEkosCaptureWorkflow(QObject *parent) :
0025     TestEkosCaptureWorkflow::TestEkosCaptureWorkflow("Internal", parent) {}
0026 
0027 TestEkosCaptureWorkflow::TestEkosCaptureWorkflow(QString guider, QObject *parent) : QObject(parent)
0028 {
0029     m_CaptureHelper = new TestEkosCaptureHelper(guider);
0030     m_CaptureHelper->m_GuiderDevice = "CCD Simulator";
0031     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0032 }
0033 
0034 void TestEkosCaptureWorkflow::testCaptureRefocusDelay()
0035 {
0036     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0037     // default initialization
0038     QVERIFY(prepareTestCase());
0039 
0040     Ekos::Manager *manager = Ekos::Manager::Instance();
0041     QVERIFY(prepareCapture(1));
0042     QVERIFY(m_CaptureHelper->executeFocusing());
0043 
0044     // start capturing, expect focus after first captured frame
0045     Ekos::Capture *capture = manager->captureModule();
0046     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0047     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0048     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_FOCUSING);
0049     KTRY_CLICK(capture, startB);
0050     // focusing must have started 60 secs + exposure time + 10 secs delay
0051     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates,
0052                                      60000 + 10000 + 1000 * capture->captureExposureN->value());
0053 }
0054 
0055 void TestEkosCaptureWorkflow::testCaptureRefocusHFR()
0056 {
0057     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0058     // default initialization
0059     QVERIFY(prepareTestCase());
0060 
0061     Ekos::Manager *manager = Ekos::Manager::Instance();
0062     QVERIFY(prepareCapture(0, 1.2));
0063     QVERIFY(m_CaptureHelper->executeFocusing());
0064 
0065     // start capturing
0066     Ekos::Capture *capture = manager->captureModule();
0067     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0068     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_PROGRESS);
0069     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0070     KTRY_CLICK(capture, startB);
0071     // wait for one frame has been captured:   exposure time + 10 secs delay
0072     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates,
0073                                      10000 +  1000 * capture->captureExposureN->value());
0074     // now move the focuser twice to increase the HFR
0075     KTRY_CLICK(manager->focusModule(), focusOutB);
0076     KTRY_CLICK(manager->focusModule(), focusOutB);
0077     // check if focusing has started, latest after two more frames
0078     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedFocusStates,
0079                                      10000 + 2 * 1000 * capture->captureExposureN->value());
0080 }
0081 
0082 
0083 void TestEkosCaptureWorkflow::testCaptureRefocusTemperature()
0084 {
0085     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0086     // default initialization
0087     QVERIFY(prepareTestCase());
0088 
0089     Ekos::Manager *manager = Ekos::Manager::Instance();
0090     Ekos::Capture *capture = manager->captureModule();
0091     // select temperature threshold
0092     double deltaT = 2.0;
0093     QVERIFY(prepareCapture(0, 0, deltaT));
0094     // set the focuser temperature
0095     SET_INDI_VALUE_DOUBLE(m_CaptureHelper->m_FocuserDevice, "FOCUS_TEMPERATURE", "TEMPERATURE", 0);
0096     // select the focuser as temperature source
0097     KTRY_SET_COMBO(manager->focusModule(), defaultFocusTemperatureSource, m_CaptureHelper->m_FocuserDevice);
0098 
0099     QVERIFY(m_CaptureHelper->executeFocusing());
0100 
0101     // start capturing
0102     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0103     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_PROGRESS);
0104     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0105     KTRY_CLICK(capture, startB);
0106     // wait for one frame has been captured:   exposure time + 10 secs delay
0107     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates,
0108                                      10000 +  1000 * capture->captureExposureN->value());
0109     // now change the temperature on the focuser
0110     SET_INDI_VALUE_DOUBLE(m_CaptureHelper->m_FocuserDevice, "FOCUS_TEMPERATURE", "TEMPERATURE", -2 * deltaT);
0111 
0112     KTRY_CLICK(manager->focusModule(), focusOutB);
0113     KTRY_CLICK(manager->focusModule(), focusOutB);
0114     // check if focusing has started, latest after two more frames
0115     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedFocusStates,
0116                                      10000 + 2 * 1000 * capture->captureExposureN->value());
0117 
0118 }
0119 
0120 
0121 void TestEkosCaptureWorkflow::testCaptureRefocusAbort()
0122 {
0123     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0124     // default initialization
0125     QVERIFY(prepareTestCase());
0126 
0127     Ekos::Manager *manager = Ekos::Manager::Instance();
0128     QVERIFY(prepareCapture(1));
0129     QVERIFY(m_CaptureHelper->executeFocusing());
0130 
0131     // start capturing, expect focus after first captured frame
0132     Ekos::Capture *capture = manager->captureModule();
0133     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0134     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_PROGRESS);
0135     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_FOCUSING);
0136     KTRY_CLICK(capture, startB);
0137     // focusing must have started 60 secs + exposure time + 10 secs delay
0138     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedFocusStates,
0139                                      60000 + 10000 + 1000 * capture->captureExposureN->value());
0140     // the capture module must change to focusing subsequently
0141     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 5000);
0142     // now abort capturing
0143     m_CaptureHelper->expectedFocusStates.append(Ekos::FOCUS_ABORTED);
0144     capture->abort();
0145     // expect that focusing aborts
0146     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedFocusStates, 10000);
0147 }
0148 
0149 void TestEkosCaptureWorkflow::testCaptureScriptsExecution()
0150 {
0151     // default initialization
0152     QVERIFY(prepareTestCase());
0153 
0154     QFETCH(bool, pausing);
0155     // static test with three exposures
0156     int count = 3;
0157     // switch to capture module
0158     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0159     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0160 
0161     // add target to path to emulate the behavior of the scheduler
0162     QString imagepath = getImageLocation()->path() + "/test";
0163 
0164     // create executable scripts
0165     m_CaptureHelper->createAllCaptureScripts(destination);
0166 
0167     // setup scripts - starts as thread since clicking on capture blocks
0168     bool success = false;
0169     QTimer::singleShot(1000, capture, [&] {success = m_CaptureHelper->fillScriptManagerDialog(m_CaptureHelper->getScripts());});
0170     // open script manager
0171     KTRY_CLICK(capture, scriptManagerB);
0172     // verify if script configuration succeeded
0173     QVERIFY2(success, "Scripts set up failed!");
0174 
0175     // create capture sequences
0176     KTRY_CAPTURE_CONFIGURE_LIGHT(2.0, count, 0.0, "Luminance", "test", imagepath);
0177     KTRY_CAPTURE_CLICK(addToQueueB);
0178     KTRY_CAPTURE_CONFIGURE_LIGHT(2.0, count, 0.0, "Red", "test", imagepath);
0179     KTRY_CAPTURE_CLICK(addToQueueB);
0180     // check if row has been added
0181     KTRY_CAPTURE_GADGET(QTableWidget, queueTable);
0182     QTRY_VERIFY_WITH_TIMEOUT(queueTable->rowCount() == 2, 1000);
0183     // wait to pause when second frame is running
0184     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0185     // start capture
0186     KTRY_CLICK(capture, startB);
0187     if (pausing)
0188     {
0189         // wait for first frame to be completed
0190         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 15000);
0191         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_PAUSED);
0192         QTest::qWait(1500);
0193         // press pause
0194         KTRY_CLICK(capture, pauseB);
0195         // wait until paused
0196         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 15000);
0197         // prepare next stage
0198         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0199         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_PAUSED);
0200         QTest::qWait(3000);
0201         // press continue
0202         KTRY_CLICK(capture, startB);
0203         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() <= 1, 15000);
0204         QTest::qWait(500);
0205         // press pause
0206         KTRY_CLICK(capture, pauseB);
0207         // wait until paused
0208         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 15000);
0209         // press continue
0210         QTest::qWait(3000);
0211         KTRY_CLICK(capture, startB);
0212     }
0213     // wait until completed
0214     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0215     // wait that all frames have been taken
0216     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 30000);
0217 
0218     // check the log file if it holds the expected number
0219     QVERIFY(m_CaptureHelper->checkScriptRuns(count, 2));
0220 }
0221 
0222 void TestEkosCaptureWorkflow::testGuidingDeviationSuspendingCapture()
0223 {
0224     // default initialization
0225     QVERIFY(prepareTestCase());
0226 
0227     const double deviation_limit = 2.0;
0228     // switch to capture module
0229     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0230     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0231     // set guide deviation guard to < 2"
0232     KTRY_SET_CHECKBOX(capture, limitGuideDeviationS, true);
0233     KTRY_SET_DOUBLESPINBOX(capture, limitGuideDeviationN, deviation_limit);
0234 
0235     // add target to path to emulate the behavior of the scheduler
0236     QString imagepath = getImageLocation()->path() + "/test";
0237     // build a LRGB sequence
0238     KTRY_CAPTURE_ADD_LIGHT(30.0, 1, 5.0, "Luminance", "test", imagepath);
0239     KTRY_CAPTURE_ADD_LIGHT(30.0, 1, 5.0, "Red", "test", imagepath);
0240     KTRY_CAPTURE_ADD_LIGHT(30.0, 1, 5.0, "Green", "test", imagepath);
0241     KTRY_CAPTURE_ADD_LIGHT(30.0, 1, 5.0, "Blue", "test", imagepath);
0242 
0243     // set a position in the west
0244     SkyPoint *target = new SkyPoint();
0245     target->setAz(270.0);
0246     target->setAlt(KStarsData::Instance()->geo()->lat()->Degrees() / 2.0);
0247     // translate to equatorial coordinates
0248     const dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
0249     const dms *lat = KStarsData::Instance()->geo()->lat();
0250     target->HorizontalToEquatorial(&lst, lat);
0251 
0252     m_CaptureHelper->slewTo(target->ra().Hours(), target->dec().Degrees(), true);
0253 
0254     // clear calibration to ensure proper guiding
0255     KTRY_CLICK(Ekos::Manager::Instance()->guideModule(), clearCalibrationB);
0256 
0257     // start guiding
0258     m_CaptureHelper->startGuiding(2.0);
0259 
0260     // start capture
0261     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0262     KTRY_CLICK(capture, startB);
0263     // wait until capturing starts
0264     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 10000);
0265     // wait for settling
0266     QTest::qWait(2000);
0267     // create a guide drift
0268     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_SUSPENDED);
0269     Ekos::Manager::Instance()->mountModule()->doPulse(RA_INC_DIR, 2000, DEC_INC_DIR, 2000);
0270     qCInfo(KSTARS_EKOS_TEST()) << "Sent 2000ms RA+DEC guiding pulses.";
0271     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0272     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 30000);
0273     // expect that capturing continues
0274     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_CAPTURING, 60000);
0275     // verify that capture starts only once
0276     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_PROGRESS);
0277     QTest::qWait(20000);
0278     QVERIFY2(m_CaptureHelper->expectedCaptureStates.size() > 0, "Multiple capture starts.");
0279 }
0280 
0281 void TestEkosCaptureWorkflow::testGuidingDeviationAbortCapture()
0282 {
0283     // default initialization
0284     QVERIFY(prepareTestCase());
0285 
0286     const double deviation_limit = 2.0;
0287     // switch to capture module
0288     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0289     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0290     // set guide deviation guard to < 2"
0291     KTRY_SET_CHECKBOX(capture, limitGuideDeviationS, true);
0292     KTRY_SET_DOUBLESPINBOX(capture, limitGuideDeviationN, deviation_limit);
0293 
0294     // add target to path to emulate the behavior of the scheduler
0295     QString imagepath = getImageLocation()->path() + "/test";
0296     // build a simple 5xL sequence
0297     KTRY_CAPTURE_ADD_LIGHT(45.0, 5, 5.0, "Luminance", "", imagepath);
0298     // set Dubhe as target and slew there
0299     SkyObject *target = KStars::Instance()->data()->skyComposite()->findByName("Dubhe");
0300     m_CaptureHelper->slewTo(target->ra().Hours(), target->dec().Degrees(), true);
0301 
0302     // clear calibration to ensure proper guiding
0303     KTRY_CLICK(Ekos::Manager::Instance()->guideModule(), clearCalibrationB);
0304 
0305     // start guiding
0306     m_CaptureHelper->startGuiding(2.0);
0307 
0308     // start capture
0309     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0310     KTRY_CLICK(capture, startB);
0311     // wait until capturing starts
0312     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 10000);
0313     // wait for settling
0314     QTest::qWait(2000);
0315     // create a guide drift
0316     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_SUSPENDED);
0317     Ekos::Manager::Instance()->mountModule()->doPulse(RA_INC_DIR, 2000, DEC_INC_DIR, 2000);
0318     qCInfo(KSTARS_EKOS_TEST()) << "Sent 2000ms RA+DEC guiding pulses.";
0319     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0320     // wait that capturing gets suspended
0321     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 15000);
0322     // abort capturing
0323     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_ABORTED);
0324     KTRY_CLICK(capture, startB);
0325     // check that it has been aborted
0326     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 10000);
0327     // wait that the guiding deviation is below the limit and
0328     // verify that capture does not start
0329     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_PROGRESS);
0330     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getGuideDeviation() < deviation_limit, 60000);
0331 
0332     QTest::qWait(20000);
0333     QVERIFY2(m_CaptureHelper->expectedCaptureStates.size() > 0, "Capture has been restarted although aborted.");
0334 }
0335 
0336 void TestEkosCaptureWorkflow::testInitialGuidingLimitCapture()
0337 {
0338     // default initialization
0339     QVERIFY(prepareTestCase());
0340 
0341     const double deviation_limit = 2.0;
0342     QFETCH(double, exptime);
0343     // switch to capture module
0344     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0345     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0346     // set start guide deviation guard to < 2" but disable the other one
0347     KTRY_SET_CHECKBOX(capture, startGuiderDriftS, true);
0348     KTRY_SET_DOUBLESPINBOX(capture, startGuiderDriftN, deviation_limit);
0349     // create sequence with 10 sec delay
0350     QVERIFY(prepareCapture(0, 0, 0, 10));
0351     // set Dubhe as target and slew there
0352     SkyObject *target = KStars::Instance()->data()->skyComposite()->findByName("Dubhe");
0353     m_CaptureHelper->slewTo(target->ra().Hours(), target->dec().Degrees(), true);
0354 
0355     // start guiding
0356     m_CaptureHelper->startGuiding(2.0);
0357 
0358     for (int i = 1; i <= 2; i++)
0359     {
0360         // wait intially 5 seconds
0361         if (i == 1)
0362             QTest::qWait(5000);
0363 
0364         // prepare to expect that capturing will start
0365         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0366 
0367         // ensure that guiding is running
0368         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getGuidingStatus() == Ekos::GUIDE_GUIDING, 10000);
0369 
0370         // create a guide drift
0371         Ekos::Manager::Instance()->mountModule()->doPulse(RA_INC_DIR, 2000, DEC_INC_DIR, 2000);
0372         qCInfo(KSTARS_EKOS_TEST()) << "Sent 2000ms RA+DEC guiding pulses.";
0373 
0374         // wait until guide deviation is present
0375         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getGuideDeviation() > deviation_limit, 15000);
0376 
0377         if (i == 1)
0378         {
0379             // start capture but expect it being suspended first
0380             KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0381             KTRY_CLICK(capture, startB);
0382         }
0383         // verify that capturing does not start before the guide deviation is below the limit
0384         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getGuideDeviation() >= deviation_limit, 60000);
0385         // wait 3 seconds and then ensure that capture did not start
0386         QTest::qWait(3000);
0387         QTRY_VERIFY(m_CaptureHelper->expectedCaptureStates.size() > 0);
0388         // wait until guiding deviation is below the limit
0389         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getGuideDeviation() < deviation_limit, 60000);
0390         // wait until capturing starts
0391         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 30000);
0392         if (i < 2)
0393         {
0394             // in the first iteration wait until the capture completes
0395             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0396             KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 1500 * exptime);
0397         }
0398     }
0399 }
0400 
0401 void TestEkosCaptureWorkflow::testCaptureWaitingForTemperature()
0402 {
0403     // default initialization
0404     QVERIFY(prepareTestCase());
0405 
0406     // switch to capture module
0407     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0408     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0409 
0410     // initial and target temperature
0411     QFETCH(double, initTemp);
0412     QFETCH(double, targetTemp);
0413 
0414     // initialize the CCD temperature and wait until it is reached
0415     SET_INDI_VALUE_DOUBLE(m_CaptureHelper->m_CCDDevice, "CCD_TEMPERATURE", "CCD_TEMPERATURE_VALUE", initTemp);
0416     QTRY_VERIFY_WITH_TIMEOUT(std::abs(capture->temperatureOUT->text().toDouble()) <= Options::maxTemperatureDiff(), 60000);
0417 
0418     // set target temperature
0419     KTRY_SET_DOUBLESPINBOX(capture, cameraTemperatureN, targetTemp);
0420     KTRY_SET_CHECKBOX(capture, cameraTemperatureS, true);
0421 
0422     // build a simple 1xL sequence
0423     KTRY_CAPTURE_ADD_LIGHT(10.0, 5, 5.0, "Luminance", "test", getImageLocation()->path() + " / test");
0424     // expect capturing state
0425     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0426 
0427     // start capturing
0428     KTRY_CLICK(capture, startB);
0429     // check if capturing has started
0430     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0431     // check if the temperature is at the expected level
0432     QTRY_VERIFY2(std::abs(capture->temperatureOUT->text().toDouble() - targetTemp) <= Options::maxTemperatureDiff(),
0433                  QString("Temperature %1°C not at the expected level of %2°C").arg(capture->temperatureOUT->text()).arg(
0434                      targetTemp).toLocal8Bit());
0435 
0436     // stop capturing
0437     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_ABORTED);
0438     KTRY_CLICK(capture, startB);
0439     // check if capturing has stopped
0440     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 10000);
0441 
0442     // restart again to check whether an already reached temperature is handled properly
0443     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0444     KTRY_CLICK(capture, startB);
0445     // check if capturing has started
0446     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 20000);
0447     // check if the temperature is at the expected level
0448     QTRY_VERIFY2(std::abs(capture->temperatureOUT->text().toDouble() - targetTemp) <= Options::maxTemperatureDiff(),
0449                  QString("Temperature %1°C not at the expected level of %2°C").arg(capture->temperatureOUT->text()).arg(
0450                      targetTemp).toLocal8Bit());
0451 
0452     // stop capturing
0453     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_ABORTED);
0454     KTRY_CLICK(capture, startB);
0455     // check if capturing has stopped
0456     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 10000);
0457 
0458     // change temperature back to initial value
0459     SET_INDI_VALUE_DOUBLE(m_CaptureHelper->m_CCDDevice, "CCD_TEMPERATURE", "CCD_TEMPERATURE_VALUE", initTemp);
0460     QTRY_VERIFY_WITH_TIMEOUT(std::abs(capture->temperatureOUT->text().toDouble()) <= Options::maxTemperatureDiff(), 60000);
0461 
0462     // start capturing for a second time
0463     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0464     KTRY_CLICK(capture, startB);
0465     // check if capturing has started
0466     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0467     // check if the temperature is at the expected level
0468     QTRY_VERIFY2(std::abs(capture->temperatureOUT->text().toDouble() - targetTemp) <= Options::maxTemperatureDiff(),
0469                  QString("Temperature %1°C not at the expected level of %2°C").arg(capture->temperatureOUT->text()).arg(
0470                      targetTemp).toLocal8Bit());
0471 
0472 }
0473 
0474 void TestEkosCaptureWorkflow::testCaptureWaitingForRotator()
0475 {
0476     // use the rotator simulator
0477     m_CaptureHelper->m_RotatorDevice = "Rotator Simulator";
0478 
0479     // default initialization
0480     QVERIFY(prepareTestCase());
0481     QSKIP("Skipping test after UI rework");
0482 
0483     // switch to capture module
0484     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0485     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0486 
0487     // open the rotator dialog
0488     KTRY_CLICK(capture, rotatorB);
0489 
0490     // ensure that the rotator dialog appears
0491     QWidget *rotatorDialog = nullptr;
0492     QTRY_VERIFY_WITH_TIMEOUT((rotatorDialog = Ekos::Manager::Instance()->findChild<QWidget *>("RotatorDialog")) != nullptr,
0493                              10000);
0494 
0495     // target angle rotation
0496     double targetAngle = 90.0;
0497     // set the target rotation angle
0498     KTRY_SET_DOUBLESPINBOX(rotatorDialog, CameraPA, targetAngle);
0499     // save it to the sequence job
0500     KTRY_SET_CHECKBOX(rotatorDialog, enforceJobPA, true);
0501 
0502     // build a simple 1xL sequence
0503     KTRY_CAPTURE_ADD_LIGHT(30.0, 1, 5.0, "Luminance", "test", getImageLocation()->path() + "/test");
0504     // expect capturing state
0505     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0506 
0507     // set the rotation angle back
0508     CameraPA->setValue(0);
0509     // let the rotator rotate back for at least 3sec
0510     QTest::qWait(3000);
0511 
0512     // start capturing
0513     KTRY_CLICK(capture, startB);
0514     // check if capturing has started
0515     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0516 
0517     // KTRY_GADGET(rotatorDialog, QLineEdit, rawAngleOut);
0518     // QTRY_VERIFY2(fabs(rawAngleOut->text().toDouble() - targetAngle) * 60 <= Options::astrometryRotatorThreshold(),
0519     //              QString("Rotator angle %1° not at the expected value of %2°").arg(rawAngleOut->text()).arg(targetAngle).toLocal8Bit());
0520     QWARN("Since the rotator interface has changed, the correct rotator angle cannot be checked from the test case.");
0521 }
0522 
0523 void TestEkosCaptureWorkflow::testFlatManualSource()
0524 {
0525     // default initialization
0526     QVERIFY(prepareTestCase());
0527 
0528     // switch to capture module
0529     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0530     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0531 
0532     // use a test directory
0533     QString imagepath = getImageLocation()->path() + "/test";
0534 
0535     // switch capture type to flat so that we can set the calibration
0536     KTRY_SET_COMBO(capture, captureTypeS, "Flat");
0537     // ensure that the filter wheel is configured
0538     KTRY_CAPTURE_GADGET(QComboBox, FilterPosCombo);
0539     // wait until filter combo box is filled
0540     QTRY_VERIFY_WITH_TIMEOUT(FilterPosCombo->count() > 0, 60000);
0541     // build a simple 1xL flat sequence
0542     KTRY_CAPTURE_ADD_FRAME("Flat", 1, 1, 0.0, "Luminance", "test", imagepath);
0543     // build a simple 1xL light sequence
0544     KTRY_CAPTURE_ADD_LIGHT(1, 1, 0.0, "Red", "test", imagepath);
0545     // click OK or Cancel?
0546     QFETCH(bool, clickModalOK);
0547     QFETCH(bool, clickModal2OK);
0548     if (clickModalOK)
0549     {
0550         // Expect a capture sequence
0551         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0552         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IDLE);
0553         KTRY_CLICK(capture, startB);
0554         // click OK in the modal dialog for covering the telescope
0555         CLOSE_MODAL_DIALOG(0);
0556         // check if one single frame is captured
0557         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0558         if (clickModal2OK)
0559         {
0560             // expect the light sequence
0561             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0562             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0563             // click OK in the modal dialog for uncovering the telescope
0564             CLOSE_MODAL_DIALOG(0);
0565             // check if one single light is captured
0566             KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0567         }
0568         else
0569         {
0570             // this must not happen
0571             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0572             // click Cancel in the modal dialog for uncovering the telescope
0573             CLOSE_MODAL_DIALOG(1);
0574             // check if capturing has not been started
0575             KTRY_CAPTURE_GADGET(QPushButton, startB);
0576             // within 5 secs the job must be stopped ...
0577             QTRY_VERIFY_WITH_TIMEOUT(startB->icon().name() == QString("media-playback-start"), 5000);
0578             // ... and capturing has not been started
0579             QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() > 0, 5000);
0580         }
0581     }
0582     else
0583     {
0584         // this must not happen
0585         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0586         // start flats capturing
0587         KTRY_CLICK(capture, startB);
0588         // click Cancel in the modal dialog for covering the telescope
0589         CLOSE_MODAL_DIALOG(1);
0590         // check if capturing has not been started
0591         KTRY_CAPTURE_GADGET(QPushButton, startB);
0592         // within 5 secs the job mus be stopped ...
0593         QTRY_VERIFY_WITH_TIMEOUT(startB->icon().name() == QString("media-playback-start"), 5000);
0594         // ... and capturing has not been started
0595         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() > 0, 5000);
0596     }
0597 }
0598 
0599 
0600 void TestEkosCaptureWorkflow::testLightPanelSource()
0601 {
0602     // use the light panel simulator
0603     m_CaptureHelper->m_LightPanelDevice = "Light Panel Simulator";
0604     // default initialization
0605     QVERIFY(prepareTestCase());
0606 
0607     // switch to capture module
0608     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0609     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0610 
0611     // use a test directory for flats
0612     QString imagepath = getImageLocation()->path() + "/test";
0613 
0614     // switch capture type to the selected type so that we can set the calibration
0615     QFETCH(QString, frametype);
0616     KTRY_SET_COMBO(capture, captureTypeS, frametype);
0617 
0618     // select internal or external flat light
0619     // build a simple 1xL sequence
0620     KTRY_CAPTURE_ADD_FRAME(frametype, 1, 1, 0.0, "Luminance", "test", imagepath);
0621     // build a simple 1xL light sequence
0622     KTRY_CAPTURE_ADD_LIGHT(1, 1, 0.0, "Red", "test", imagepath);
0623 
0624     // start the sequence
0625     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0626     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IDLE);
0627     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0628     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0629     KTRY_CLICK(capture, startB);
0630     // check if one single flat is captured
0631     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0632 }
0633 
0634 
0635 void TestEkosCaptureWorkflow::testDustcapSource()
0636 {
0637     // use the Flip Flat in simulator mode both as light source and as dust cap
0638     m_CaptureHelper->m_DustCapDevice    = "Flip Flat";
0639     m_CaptureHelper->m_LightPanelDevice = "Flip Flat";
0640 
0641     Ekos::Manager * const ekos = Ekos::Manager::Instance();
0642 
0643     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(ekos->setupTab, 1000);
0644     QVERIFY(m_CaptureHelper->setupEkosProfile("Simulators", false));
0645     // start the profile
0646     KTRY_EKOS_CLICK(processINDIB);
0647 
0648     // wait until the disconnect button is enabled which shows that INDI has started
0649     KTRY_GADGET(ekos, QAbstractButton, disconnectB);
0650     QTRY_VERIFY_WITH_TIMEOUT(disconnectB->isEnabled(), 10000);
0651 
0652     // enable simulation
0653     SET_INDI_VALUE_SWITCH("Flip Flat", "SIMULATION", "ENABLE", true);
0654     // connect
0655     SET_INDI_VALUE_SWITCH("Flip Flat", "CONNECTION", "CONNECT", true);
0656     // park
0657     SET_INDI_VALUE_SWITCH("Flip Flat", "CAP_PARK", "PARK", true);
0658     // turn light off
0659     SET_INDI_VALUE_SWITCH("Flip Flat", "FLAT_LIGHT_CONTROL", "FLAT_LIGHT_OFF", true);
0660 
0661     // Now all devices should be up and running
0662     QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->indiStatus() == Ekos::Success, 10000);
0663 
0664     // init the helper
0665     m_CaptureHelper->init();
0666 
0667     // prepare optical trains for testing
0668     m_CaptureHelper->prepareOpticalTrains();
0669 
0670     // receive status updates from all devices
0671     m_CaptureHelper->connectModules();
0672 
0673     // switch to capture module
0674     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0675     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0676 
0677     // use a test directory for flats
0678     QString imagepath = getImageLocation()->path() + "/test";
0679 
0680     // switch capture type to flat so that we can set the calibration
0681     KTRY_SET_COMBO(capture, captureTypeS, "Flat");
0682     QTRY_VERIFY_WITH_TIMEOUT(captureTypeS->findText("Flat", Qt::MatchExactly) >= 0, 5000);
0683 
0684     // select frame type and internal or external flat light
0685     QFETCH(QString, frametype);
0686 
0687     // build a simple 1xL sequence
0688     KTRY_CAPTURE_ADD_FRAME(frametype, 1, 1, 0.0, "Luminance", "test", imagepath);
0689     // build a simple 1xL light sequence
0690     KTRY_CAPTURE_ADD_LIGHT(1, 1, 0.0, "Red", "test", imagepath);
0691 
0692     // start the sequence
0693     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0694     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IDLE);
0695     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0696     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0697     KTRY_CLICK(capture, startB);
0698     // check if one single flat is captured
0699     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0700 }
0701 
0702 
0703 void TestEkosCaptureWorkflow::testWallSource()
0704 {
0705     // use the light panel simulator
0706     m_CaptureHelper->m_LightPanelDevice = "Light Panel Simulator";
0707     // default initialization
0708     QVERIFY(prepareTestCase());
0709 
0710     // switch to capture module
0711     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0712     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0713 
0714     // use a test directory for flats
0715     QString imagepath = getImageLocation()->path() + "/test";
0716 
0717     // switch capture type to flat so that we can set the calibration
0718     KTRY_SET_COMBO(capture, captureTypeS, "Flat");
0719 
0720     // select the wall as flat light source (az=90°, alt=0)
0721     KTRY_SELECT_FLAT_WALL(capture, "90", "0");
0722 
0723     // determine frame type
0724     QFETCH(QString, frametype);
0725     // build a simple 1xL sequence
0726     KTRY_CAPTURE_ADD_FRAME(frametype, 2, 1, 2.0, "Luminance", "test", imagepath);
0727     // build a simple 1xL light sequence
0728     KTRY_CAPTURE_ADD_LIGHT(1, 1, 0.0, "Red", "test", imagepath);
0729     // switch capture type to flat so that we can set the calibration
0730     captureTypeS->setCurrentText("Flat");
0731     // add another sequence to check if wall source may be used twice
0732     // select another wall position as flat light source (az=0°, alt=0)
0733     KTRY_SELECT_FLAT_WALL(capture, "0", "0");
0734     KTRY_CAPTURE_ADD_FRAME(frametype, 2, 1, 2.0, "Luminance", "test", imagepath);
0735 
0736     // start the sequence
0737     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0738     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IDLE);
0739     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0740     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0741     m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_SLEWING);
0742     m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_IDLE);
0743     KTRY_CLICK(capture, startB);
0744     // check if mount has reached the expected position
0745     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedMountStates, 60000);
0746     // check if one single flat and one red frame is captured
0747     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0748 
0749     // reset the sequence
0750     KTRY_CLICK(capture, resetB);
0751     // restart the sequence
0752     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0753     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IDLE);
0754     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0755     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0756     m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_SLEWING);
0757     m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_IDLE);
0758     KTRY_CLICK(capture, startB);
0759     // check if mount has reached the expected position
0760     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedMountStates, 60000);
0761     // check if one single flat and one red frame is captured
0762     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0763 
0764 }
0765 
0766 
0767 //void TestEkosCaptureWorkflow::testPreMountAndDomePark()
0768 //{
0769 //    // use the light panel simulator
0770 //    m_CaptureHelper->m_LightPanelDevice = "Light Panel Simulator";
0771 //    // use the dome simulator
0772 //    m_CaptureHelper->m_DomeDevice = "Dome Simulator";
0773 //    // default initialization
0774 //    QVERIFY(prepareTestCase());
0775 
0776 //    // QSKIP("Observatory refactoring needs to be completed until this test can be activated.");
0777 
0778 //    // switch to capture module
0779 //    Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0780 //    KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0781 
0782 //    // use a test directory for flats
0783 //    QString imagepath = getImageLocation()->path() + "/test";
0784 
0785 //    // switch capture type to flat so that we can set the calibration
0786 //    KTRY_SET_COMBO(capture, captureTypeS, "Flat");
0787 
0788 //    // select internal flat light, pre-mount and but not pre-dome park
0789 //    KTRY_SELECT_FLAT_METHOD(flatDeviceSourceC, true, false);
0790 //    // determine frame type
0791 //    QFETCH(QString, frametype);
0792 //    // build a simple 1xL sequence
0793 //    KTRY_CAPTURE_ADD_FRAME(frametype, 2, 1, 2.0, "Luminance", imagepath);
0794 
0795 //    // start the sequence
0796 //    // m_CaptureHelper->expectedDomeStates.append(ISD::Dome::DOME_PARKED);
0797 //    m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_PARKED);
0798 //    m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0799 //    m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0800 //    KTRY_CLICK(capture, startB);
0801 //    // check if mount has reached the expected position
0802 //    KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedMountStates, 30000);
0803 //    // check if dome has reached the expected position
0804 //    KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedDomeStates, 30000);
0805 //    // check if one single flat is captured
0806 //    KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0807 //}
0808 
0809 void TestEkosCaptureWorkflow::testFlatSyncFocus()
0810 {
0811     // use the light panel simulator and focuser simulator
0812     m_CaptureHelper->m_LightPanelDevice = "Light Panel Simulator";
0813     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0814     // default initialization
0815     QVERIFY(prepareTestCase());
0816     // set flat sync
0817     Options::setFlatSyncFocus(true);
0818     // run autofocus
0819     QVERIFY(m_CaptureHelper->executeFocusing());
0820 
0821     Ekos::Focus *focus = Ekos::Manager::Instance()->focusModule();
0822     // update the initial focuser position
0823     KTRY_GADGET(Ekos::Manager::Instance()->focusModule(), QLineEdit, absTicksLabel);
0824     int focusPosition = absTicksLabel->text().toInt();
0825     // move the focuser 100 steps out
0826     KTRY_SET_SPINBOX(focus, absTicksSpin, focusPosition + 100);
0827     // click goto
0828     KTRY_CLICK(focus, startGotoB);
0829     // check if new position has been reached
0830     QTRY_VERIFY_WITH_TIMEOUT(absTicksLabel->text().toInt() == focusPosition + 100, 5000);
0831 
0832     // capture flats with light panel source (simplest way to test)
0833     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0834     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0835 
0836     // use a test directory for flats
0837     QString imagepath = getImageLocation()->path() + "/test";
0838 
0839     // switch capture type to flat so that we can set the calibration
0840     KTRY_SET_COMBO(capture, captureTypeS, "Flat");
0841 
0842     // build a simple 5xL sequence
0843     KTRY_CAPTURE_CONFIGURE_FLAT(2, 1, 2.0, "Luminance", imagepath);
0844 
0845     // start the sequence
0846     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0847     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0848     KTRY_CLICK(capture, startB);
0849     // check if one single flat is captured
0850     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0851     // check if the focuser is at the determined focus position
0852     QTRY_VERIFY_WITH_TIMEOUT(absTicksLabel->text().toInt() == focusPosition, 5000);
0853 }
0854 
0855 void TestEkosCaptureWorkflow::testDarkManualCovering()
0856 {
0857     // default initialization
0858     QVERIFY(prepareTestCase());
0859 
0860     // switch to capture module
0861     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0862     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0863 
0864     // use a test directory for darks
0865     QString imagepath = getImageLocation()->path() + "/test";
0866 
0867     // switch capture type to flat so that we can set the calibration
0868     KTRY_CAPTURE_GADGET(QComboBox, captureTypeS);
0869     KTRY_CAPTURE_COMBO_SET(captureTypeS, "Dark");
0870 
0871     // build a simple 1xBlue dark sequence
0872     KTRY_CAPTURE_ADD_FRAME("Dark", 1, 1, 0.0, "Blue", "test", imagepath);
0873     // build a simple 1xL light sequence
0874     KTRY_CAPTURE_ADD_LIGHT(1, 1, 0.0, "Red", "test", imagepath);
0875     // shutter type
0876     QFETCH(int, shutter);
0877     // click OK or Cancel?
0878     QFETCH(bool, clickModalOK);
0879     QFETCH(bool, clickModal2OK);
0880 
0881     // prepare shutter settings
0882     QStringList shutterfulCCDs;
0883     QStringList shutterlessCCDs;
0884     if (shutter == SHUTTER_NO)
0885         shutterlessCCDs.append(m_CaptureHelper->m_CCDDevice);
0886     else if (shutter == SHUTTER_YES)
0887         shutterfulCCDs.append(m_CaptureHelper->m_CCDDevice);
0888     // set the option values
0889     Options::setShutterfulCCDs(shutterfulCCDs);
0890     Options::setShutterlessCCDs(shutterlessCCDs);
0891 
0892     // if the camera has a shutter, no manual cover is required
0893     if (clickModalOK)
0894     {
0895         // start the flat sequence
0896         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0897         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IDLE);
0898         KTRY_CLICK(capture, startB);
0899         // if shutter type unknown, answer the the modal dialog with "No"
0900         if (shutter == SHUTTER_UNKNOWN)
0901             CLOSE_MODAL_DIALOG(1);
0902         // click OK in the modal dialog for covering the telescope if camera has no shutter
0903         if (shutter != SHUTTER_YES)
0904             CLOSE_MODAL_DIALOG(0);
0905         // check if one single flat is captured
0906         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0907         if (clickModal2OK)
0908         {
0909             // expect the light sequence
0910             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0911             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0912             // click OK in the modal dialog for uncovering the telescope
0913             CLOSE_MODAL_DIALOG(0);
0914             // check if one single light is captured
0915             KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates, 60000);
0916         }
0917         else
0918         {
0919             // this must not happen
0920             m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0921             // click Cancel in the modal dialog for uncovering the telescope
0922             CLOSE_MODAL_DIALOG(1);
0923             // check if capturing has not been started
0924             KTRY_CAPTURE_GADGET(QPushButton, startB);
0925             // within 5 secs the job must be stopped ...
0926             QTRY_VERIFY_WITH_TIMEOUT(startB->icon().name() == QString("media-playback-start"), 5000);
0927             // ... and capturing has not been started
0928             QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() > 0, 5000);
0929         }
0930     }
0931     else
0932     {
0933         // this must not happen
0934         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0935         // start flats capturing
0936         KTRY_CLICK(capture, startB);
0937         // click Cancel in the modal dialog for covering the telescope
0938         CLOSE_MODAL_DIALOG(1);
0939         // check if capturing has not been started
0940         KTRY_CAPTURE_GADGET(QPushButton, startB);
0941         // within 5 secs the job mus be stopped ...
0942         QTRY_VERIFY_WITH_TIMEOUT(startB->icon().name() == QString("media-playback-start"), 5000);
0943         // ... and capturing has not been started
0944         QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() > 0, 5000);
0945     }
0946 }
0947 
0948 
0949 void TestEkosCaptureWorkflow::testDarksLibrary()
0950 {
0951     // default initialization
0952     QVERIFY(prepareTestCase());
0953 
0954     // ensure that we know that the CCD has a shutter
0955     m_CaptureHelper->ensureCCDShutter(true);
0956 
0957     // switch to capture module
0958     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0959     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0960 
0961     // open the darks library dialog
0962     KTRY_CLICK(capture, darkLibraryB);
0963     QWidget *darkLibraryDialog = nullptr;
0964     QTRY_VERIFY_WITH_TIMEOUT(darkLibraryDialog = Ekos::Manager::Instance()->findChild<QWidget *>("DarkLibrary"), 2000);
0965 
0966     // select the primary train for darks capturing
0967     KTRY_SET_COMBO(darkLibraryDialog, opticalTrainCombo, m_CaptureHelper->m_primaryTrain);
0968 
0969     // set dark library values to 3x1s darks
0970     KTRY_SET_DOUBLESPINBOX(darkLibraryDialog, maxExposureSpin, 1);
0971     KTRY_SET_SPINBOX(darkLibraryDialog, countSpin, 3);
0972     // expect exactly 3 frames
0973     for (int i = 0; i < 3; i++)
0974         m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_IMAGE_RECEIVED);
0975     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_COMPLETE);
0976     // start
0977     KTRY_CLICK(darkLibraryDialog, startB);
0978     // wait until completion
0979     QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.isEmpty(), 10000);
0980     // check if master frame has been created
0981     QFileInfo destinationInfo(QStandardPaths::writableLocation(QStandardPaths::DataLocation), "darks");
0982     QDir destination(destinationInfo.absoluteFilePath());
0983     QVERIFY(m_CaptureHelper->searchFITS(destination).size() == 1);
0984 }
0985 
0986 void TestEkosCaptureWorkflow::testLoadEsqFileGeneral()
0987 {
0988     // default initialization
0989     QVERIFY(prepareTestCase());
0990 
0991     // switch to capture module
0992     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
0993     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
0994 
0995     // initialize the capture settings
0996     QFETCH(uint, esqVersion);
0997     QFETCH(QString, observer);
0998     QFETCH(bool, guideDeviation);
0999     QFETCH(bool, startGuideDeviation);
1000     QFETCH(bool, inSequenceFocus);
1001     QFETCH(bool, autofocusOnTemperature);
1002     QFETCH(bool, refocusEveryN);
1003     QFETCH(bool, refocusAfterMeridianFlip);
1004     TestEkosCaptureHelper::CaptureSettings settings;
1005     settings.observer = observer;
1006     settings.guideDeviation = {guideDeviation, 2.0};
1007     settings.startGuideDeviation = {startGuideDeviation, 1.0};
1008     settings.inSequenceFocus = {inSequenceFocus, 1.5};
1009     settings.autofocusOnTemperature = {autofocusOnTemperature, 3.3};
1010     settings.refocusEveryN = {refocusEveryN, 5};
1011     settings.refocusAfterMeridianFlip = refocusAfterMeridianFlip;
1012 
1013     // clear current values to ensure that we observe a change
1014     QString oldObserver("unknown");
1015     capture->setObserverName(oldObserver);
1016     KTRY_SET_DOUBLESPINBOX(capture, limitGuideDeviationN, 0.0);
1017     KTRY_SET_CHECKBOX(capture, limitGuideDeviationS, !settings.guideDeviation.enabled);
1018     KTRY_SET_DOUBLESPINBOX(capture, startGuiderDriftN, 0.0);
1019     KTRY_SET_CHECKBOX(capture, startGuiderDriftS, !settings.startGuideDeviation.enabled);
1020     KTRY_SET_DOUBLESPINBOX(capture, limitFocusHFRN, 0.1);
1021     KTRY_SET_CHECKBOX(capture, limitFocusHFRS, !settings.guideDeviation.enabled);
1022     KTRY_SET_DOUBLESPINBOX(capture, limitFocusDeltaTN, 0.2);
1023     KTRY_SET_CHECKBOX(capture, limitFocusDeltaTS, !settings.autofocusOnTemperature.enabled);
1024     KTRY_SET_SPINBOX(capture, limitRefocusN, 100);
1025     KTRY_SET_CHECKBOX(capture, limitRefocusS, !settings.refocusEveryN.enabled);
1026     KTRY_SET_CHECKBOX(capture, meridianRefocusS, !settings.refocusAfterMeridianFlip);
1027 
1028     // create capture sequence file
1029     TestEkosCaptureHelper::SimpleCaptureLightsJob job;
1030     QVector<TestEkosCaptureHelper::SimpleCaptureLightsJob> jobs;
1031     jobs.append(job);
1032     QStringList content = m_CaptureHelper->getSimpleEsqContent(settings, jobs,
1033                           static_cast<TestEkosCaptureHelper::ESQVersion>(esqVersion));
1034     QString esqFilename = destination->filePath("test.esq");
1035     qCInfo(KSTARS_EKOS_TEST) << "Sequence file name: " << esqFilename;
1036     m_CaptureHelper->writeFile(esqFilename, content);
1037 
1038     // load the capture sequence file
1039     QVERIFY2(capture->loadSequenceQueue(esqFilename), "Loading capture sequence file failed!");
1040 
1041     // Verify the results
1042     QCOMPARE(capture->getObserverName(), settings.observer);
1043     QCOMPARE(limitGuideDeviationS->isChecked(), settings.guideDeviation.enabled);
1044     QCOMPARE(limitGuideDeviationN->value(), settings.guideDeviation.value);
1045     QCOMPARE(startGuiderDriftS->isChecked(), settings.startGuideDeviation.enabled);
1046     QCOMPARE(startGuiderDriftN->value(), settings.startGuideDeviation.value);
1047     QCOMPARE(limitFocusHFRS->isChecked(), settings.inSequenceFocus.enabled);
1048     QCOMPARE(limitFocusHFRN->value(), settings.inSequenceFocus.value);
1049     QCOMPARE(limitFocusDeltaTS->isChecked(), settings.autofocusOnTemperature.enabled);
1050     QCOMPARE(limitFocusDeltaTN->value(), settings.autofocusOnTemperature.value);
1051     QCOMPARE(limitRefocusS->isChecked(), settings.refocusEveryN.enabled);
1052     QCOMPARE(limitRefocusN->value(), settings.refocusEveryN.value);
1053     QCOMPARE(meridianRefocusS->isChecked(), settings.refocusAfterMeridianFlip);
1054 }
1055 
1056 void TestEkosCaptureWorkflow::testLoadEsqFileBasicJobSettings()
1057 {
1058     // default initialization
1059     QVERIFY(prepareTestCase());
1060 
1061     // switch to capture module
1062     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
1063     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
1064 
1065     // initialize default settings
1066     TestEkosCaptureHelper::CaptureSettings settings = {"Test Observer", {true, 2.0},  {true, 1.0},  {false, 1.5}, {true, 1.0}, {false, 5}, false};
1067 
1068     // clear the UI settings
1069     KTRY_SET_DOUBLESPINBOX(capture, captureExposureN, 1.0);
1070     KTRY_SET_SPINBOX(capture, captureCountN, 1);
1071     KTRY_SET_SPINBOX(capture, captureDelayN, 0);
1072     KTRY_SET_SPINBOX(capture, captureBinHN, 1);
1073     KTRY_SET_SPINBOX(capture, captureBinVN, 1);
1074     KTRY_SET_SPINBOX(capture, captureFrameXN, 0);
1075     KTRY_SET_SPINBOX(capture, captureFrameYN, 0);
1076     KTRY_SET_SPINBOX(capture, captureFrameWN, 500);
1077     KTRY_SET_SPINBOX(capture, captureFrameHN, 500);
1078     KTRY_SET_COMBO(capture, FilterPosCombo, "Blue");
1079     KTRY_SET_COMBO(capture, captureEncodingS, "FITS");
1080     KTRY_SET_COMBO(capture, captureTypeS, "Dark");
1081     KTRY_SET_LINEEDIT(capture, targetNameT, "nothing");
1082     KTRY_SET_LINEEDIT(capture, fileDirectoryT, "/home/pi");
1083     KTRY_SET_LINEEDIT(capture, placeholderFormatT, "/%T");
1084     KTRY_SET_SPINBOX(capture, formatSuffixN, 1);
1085     KTRY_SET_DOUBLESPINBOX(capture, cameraTemperatureN, 10.0);
1086     KTRY_SET_CHECKBOX(capture, cameraTemperatureS, false);
1087     KTRY_SET_COMBO_INDEX(capture, fileUploadModeS, 1);
1088 
1089     // create the job
1090     TestEkosCaptureHelper::SimpleCaptureLightsJob job;
1091     QFETCH(uint, esqVersion);
1092     job.version = static_cast<TestEkosCaptureHelper::ESQVersion>(esqVersion);
1093     QFETCH(double, exposureTime);
1094     job.exposureTime = exposureTime;
1095     QFETCH(QString, targetName);
1096     job.targetName = targetName;
1097     QFETCH(int, count);
1098     job.count = count;
1099     QFETCH(int, delay);
1100     job.delayMS = delay;
1101     QFETCH(int, binX);
1102     job.binX = binX;
1103     QFETCH(int, binY);
1104     job.binY = binY;
1105     QFETCH(int, x);
1106     job.x = x;
1107     QFETCH(int, y);
1108     job.y = y;
1109     QFETCH(int, w);
1110     job.w = w;
1111     QFETCH(int, h);
1112     job.h = h;
1113     QFETCH(QString, filter);
1114     job.filterName = filter;
1115     QFETCH(QString, type);
1116     job.type = type;
1117     QFETCH(QString, encoding);
1118     job.encoding = encoding;
1119     QFETCH(QString, fitsDirectory);
1120     job.fitsDirectory = fitsDirectory;
1121     QFETCH(int, formatSuffix);
1122     job.formatSuffix = formatSuffix;
1123     QFETCH(QString, placeholderFormat);
1124     job.placeholderFormat = placeholderFormat;
1125     QFETCH(double, cameraTemperature);
1126     job.cameraTemperature.value = cameraTemperature;
1127     QFETCH(bool, cameraCooling);
1128     job.cameraTemperature.enabled = cameraCooling;
1129     QFETCH(int, fileUploadMode);
1130     job.uploadMode = fileUploadMode;
1131 
1132     QVector<TestEkosCaptureHelper::SimpleCaptureLightsJob> jobs;
1133     jobs.append(job);
1134     // create capture sequence file
1135     QStringList content = m_CaptureHelper->getSimpleEsqContent(settings, jobs);
1136     QString esqFilename = destination->filePath("test.esq");
1137     qCInfo(KSTARS_EKOS_TEST) << "Sequence file name: " << esqFilename;
1138     m_CaptureHelper->writeFile(esqFilename, content);
1139 
1140     // load the capture sequence file
1141     QVERIFY2(capture->loadSequenceQueue(esqFilename), "Loading capture sequence file failed!");
1142 
1143     // Verify the results
1144     QTRY_COMPARE(captureExposureN->value(), exposureTime);
1145     QTRY_COMPARE(captureCountN->value(), count);
1146     QTRY_COMPARE(captureDelayN->value(), delay / 1000);
1147     QTRY_COMPARE(captureBinHN->value(), binX);
1148     QTRY_COMPARE(captureBinVN->value(), binY);
1149     QTRY_COMPARE(captureFrameXN->value(), x);
1150     QTRY_COMPARE(captureFrameYN->value(), y);
1151     QTRY_COMPARE(captureFrameWN->value(), w);
1152     QTRY_COMPARE(captureFrameHN->value(), h);
1153     QTRY_COMPARE(FilterPosCombo->currentText(), filter);
1154     QTRY_COMPARE(captureTypeS->currentText(), type);
1155     QTRY_COMPARE(captureEncodingS->currentText(), encoding);
1156     QTRY_COMPARE(targetNameT->text(), esqVersion == TestEkosCaptureHelper::ESQ_VERSION_2_5 ? "Test Target" : targetName);
1157     QTRY_COMPARE(fileDirectoryT->text(), fitsDirectory);
1158     QTRY_COMPARE(placeholderFormatT->text(), placeholderFormat);
1159     QTRY_COMPARE(formatSuffixN->value(), formatSuffix);
1160     QTRY_COMPARE(cameraTemperatureN->value(), cameraTemperature);
1161     QTRY_COMPARE(cameraTemperatureS->isChecked(), cameraCooling);
1162     QTRY_COMPARE(fileUploadModeS->currentIndex(), fileUploadMode);
1163 }
1164 
1165 
1166 void TestEkosCaptureWorkflow::testLoadEsqFileCalibrationSettings()
1167 {
1168     // default initialization
1169     QVERIFY(prepareTestCase());
1170 
1171     // switch to capture module
1172     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
1173     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000);
1174 
1175     // initialize default settings
1176     TestEkosCaptureHelper::CaptureSettings settings = {"Test Observer", {true, 2.0},  {true, 1.0},  {false, 1.5}, {true, 1.0}, {false, 5}, false};
1177 
1178     // retrieve test data
1179     QFETCH(uint, esqVersion);
1180     QFETCH(double, exposureTime);
1181     QFETCH(int, count);
1182     QFETCH(QString, type);
1183     QFETCH(uint, pre_action);
1184     QFETCH(double, wall_az);
1185     QFETCH(double, wall_alt);
1186     QFETCH(bool, duration_manual);
1187     QFETCH(bool, duration_adu);
1188     QFETCH(int, adu);
1189     QFETCH(int, tolerance);
1190 
1191     // clear the UI settings
1192     KTRY_SET_DOUBLESPINBOX(capture, captureExposureN, 5.4);
1193     KTRY_SET_COMBO(capture, captureTypeS, (type == "Flat" ? "Dark" : "Flat"));
1194     KTRY_SET_SPINBOX(capture, captureCountN, 2 + count);
1195 
1196     // create the job
1197     TestEkosCaptureHelper::SimpleCaptureCalibratingJob job;
1198     job.version = static_cast<TestEkosCaptureHelper::ESQVersion>(esqVersion);
1199     job.exposureTime = exposureTime;
1200     job.type = type;
1201     job.count = count;
1202     job.preAction = pre_action;
1203     job.wall_az = wall_az;
1204     job.wall_alt = wall_alt;
1205     job.duration_manual = duration_manual;
1206     job.duration_adu = duration_adu;
1207     job.adu = adu;
1208     job.tolerance = tolerance;
1209 
1210     QVector<TestEkosCaptureHelper::SimpleCaptureCalibratingJob> jobs;
1211     jobs.append(job);
1212     // create capture sequence file
1213     QStringList content = m_CaptureHelper->getSimpleEsqContent(settings, jobs,
1214                           static_cast<TestEkosCaptureHelper::ESQVersion>(esqVersion));
1215     QString esqFilename = destination->filePath("test.esq");
1216     qCInfo(KSTARS_EKOS_TEST) << "Sequence file name: " << esqFilename;
1217     m_CaptureHelper->writeFile(esqFilename, content);
1218 
1219     // load the capture sequence file
1220     QVERIFY2(capture->loadSequenceQueue(esqFilename), "Loading capture sequence file failed!");
1221 
1222     // Verify the results on the main tab
1223     QTRY_COMPARE(captureExposureN->value(), exposureTime);
1224     QTRY_COMPARE(captureCountN->value(), count);
1225     QTRY_COMPARE(captureTypeS->currentText(), type);
1226 
1227     // Verify the results on the calibration dialog
1228     bool success = false;
1229     QTimer::singleShot(1000, capture, [&] { success = verifyCalibrationSettings(); });
1230     KTRY_CLICK(capture, calibrationB);
1231 
1232     QTRY_VERIFY_WITH_TIMEOUT(success, 10000);
1233 }
1234 
1235 bool TestEkosCaptureWorkflow::verifyCalibrationSettings()
1236 {
1237     bool passed = false;
1238     QDialog *calibrationDialog = nullptr;
1239     KTRY_VERIFY_WITH_TIMEOUT_SUB(calibrationDialog = Ekos::Manager::Instance()->findChild<QDialog *>("calibrationOptions"),
1240                                  2000);
1241     // ensure that the cancel button is pressed in any case
1242     [&]()
1243     {
1244         QFETCH(uint, pre_action);
1245         QFETCH(double, wall_az);
1246         QFETCH(double, wall_alt);
1247         QFETCH(bool, duration_manual);
1248         QFETCH(bool, duration_adu);
1249         QFETCH(int, adu);
1250         QFETCH(int, tolerance);
1251         KTRY_GADGET(calibrationDialog, QCheckBox, gotoWallC);
1252         KTRY_GADGET(calibrationDialog, QCheckBox, parkMountC);
1253         KTRY_GADGET(calibrationDialog, QCheckBox, parkDomeC);
1254         KTRY_GADGET(calibrationDialog, dmsBox, azBox);
1255         KTRY_GADGET(calibrationDialog, dmsBox, altBox);
1256         KTRY_GADGET(calibrationDialog, QRadioButton, manualDurationC);
1257         KTRY_GADGET(calibrationDialog, QRadioButton, ADUC);
1258         KTRY_GADGET(calibrationDialog, QSpinBox, ADUValue);
1259         KTRY_GADGET(calibrationDialog, QSpinBox, ADUTolerance);
1260         QTRY_COMPARE(gotoWallC->isChecked(), (pre_action & ACTION_WALL) > 0);
1261         QTRY_COMPARE(parkMountC->isChecked(), (pre_action & ACTION_PARK_MOUNT) > 0);
1262         QTRY_COMPARE(parkDomeC->isChecked(), (pre_action & ACTION_PARK_DOME) > 0);
1263 
1264         if (pre_action & ACTION_WALL)
1265         {
1266             dms wallAz, wallAlt;
1267             bool azOk = false, altOk = false;
1268 
1269             wallAz  = azBox->createDms(&azOk);
1270             wallAlt = altBox->createDms(&altOk);
1271 
1272             QTRY_COMPARE(wallAz.Degrees(), wall_az);
1273             QTRY_COMPARE(wallAlt.Degrees(), wall_alt);
1274         }
1275         QTRY_COMPARE(manualDurationC->isChecked(), duration_manual);
1276         QTRY_COMPARE(ADUC->isChecked(), duration_adu);
1277         if (duration_adu)
1278         {
1279             QTRY_COMPARE(ADUValue->value(), adu);
1280             QTRY_COMPARE(ADUTolerance->value(), tolerance);
1281         }
1282         passed = true;
1283     }
1284     ();
1285     // cancel the dialog
1286     calibrationDialog->done(QDialog::Rejected);
1287 
1288     return passed;
1289 }
1290 
1291 
1292 /* *********************************************************************************
1293  *
1294  * Test data
1295  *
1296  * ********************************************************************************* */
1297 
1298 void TestEkosCaptureWorkflow::testCaptureRefocusDelay_data()
1299 {
1300     prepareTestData(31.0, {"Luminance:3"});
1301 }
1302 
1303 void TestEkosCaptureWorkflow::testCaptureRefocusHFR_data()
1304 {
1305     prepareTestData(10.0, {"Luminance:6"});
1306 }
1307 
1308 void TestEkosCaptureWorkflow::testCaptureRefocusTemperature_data()
1309 {
1310     prepareTestData(10.0, {"Luminance:6"});
1311 }
1312 
1313 void TestEkosCaptureWorkflow::testCaptureRefocusAbort_data()
1314 {
1315     prepareTestData(31.0, {"Luminance:3"});
1316 }
1317 
1318 void TestEkosCaptureWorkflow::testCaptureScriptsExecution_data()
1319 {
1320     QTest::addColumn<bool>("pausing");             /*!< pause between capturing */
1321     QTest::newRow("pausing=false") << false;
1322     QTest::newRow("pausing=true") << true;
1323 }
1324 
1325 void TestEkosCaptureWorkflow::testInitialGuidingLimitCapture_data()
1326 {
1327     prepareTestData(20.0, {"Luminance:5"});
1328 }
1329 
1330 void TestEkosCaptureWorkflow::testFlatManualSource_data()
1331 {
1332     QTest::addColumn<bool>("clickModalOK");             /*!< click "OK" on the modal dialog        */
1333     QTest::addColumn<bool>("clickModal2OK");            /*!< click "OK" on the second modal dialog */
1334 
1335     // both variants for second click: click OK and click Cancel
1336     QTest::newRow("Flat, modal=true/true")  << true << true;
1337     QTest::newRow("Flat, modal=true/false")  << true << false;
1338     // first click Cancel
1339     QTest::newRow("Flat, modal=false") << false << true;
1340 }
1341 
1342 void TestEkosCaptureWorkflow::testLightPanelSource_data()
1343 {
1344     QTest::addColumn<QString>("frametype");              /*!< frame type (Bias, Dark, Flat)       */
1345     QTest::addColumn<bool>("internalLight");             /*!< use internal or external flat light */
1346 
1347     for (auto frametype :
1348             {
1349                 "Flat", "Dark", "Bias"
1350             })
1351         for (auto internalLight : // light source integrated into the light panel?
1352                 {
1353                     true, false
1354                 })
1355             QTest::newRow(QString("%1, light=%2").arg(frametype).arg(internalLight ? "internal" : "external").toLatin1())
1356                     << frametype << internalLight;
1357 }
1358 
1359 void TestEkosCaptureWorkflow::testDustcapSource_data()
1360 {
1361     QTest::addColumn<QString>("frametype");              /*!< frame type (Dark or Flat)           */
1362 
1363     //    QTest::newRow("Flat, light=internal") << "Flat" << true;   // flat + light source integrated into the light panel
1364     QTest::newRow("Flat, light=internal") << "Flat";  // flat + internal light source used
1365     QTest::newRow("Dark, light=internal") << "Dark";  // dark + external light source turned off
1366     QTest::newRow("Bias, light=internal") << "Bias";  // dark + external light source turned off
1367     //    QTest::newRow("Dark") << "Dark" << false;  // dark
1368 }
1369 
1370 void TestEkosCaptureWorkflow::testWallSource_data()
1371 {
1372     QTest::addColumn<QString>("frametype");              /*!< frame type (Dark, Flat or bias)           */
1373 
1374     QTest::newRow("Flat") << "Flat";
1375     QTest::newRow("Dark") << "Dark";
1376     QTest::newRow("Bias") << "Bias";
1377 }
1378 
1379 
1380 //void TestEkosCaptureWorkflow::testPreMountAndDomePark_data()
1381 //{
1382 //    testWallSource_data();
1383 //}
1384 
1385 void TestEkosCaptureWorkflow::testDarkManualCovering_data()
1386 {
1387     QTest::addColumn<int>("shutter");                   /*!< does the CCD have a shutter?          */
1388     QTest::addColumn<bool>("clickModalOK");             /*!< click "OK" on the modal dialog        */
1389     QTest::addColumn<bool>("clickModal2OK");            /*!< click "OK" on the second modal dialog */
1390 
1391     // all shutter types plus both variants: click OK and click Cancel
1392     QTest::newRow("shutter=? modal=true") << SHUTTER_UNKNOWN << true << true;
1393     QTest::newRow("shutter=yes modal=true") << SHUTTER_YES << true << true;
1394     QTest::newRow("shutter=no modal=true") << SHUTTER_NO << true << true;
1395     QTest::newRow("modal=true") << SHUTTER_NO << true << false;
1396     QTest::newRow("modal=false") << SHUTTER_NO << false << true;
1397 }
1398 
1399 void TestEkosCaptureWorkflow::testCaptureWaitingForTemperature_data()
1400 {
1401     QTest::addColumn<double>("initTemp");             /*!< Initial temperature value */
1402     QTest::addColumn<double>("targetTemp");           /*!< Target temperature value  */
1403 
1404     QTest::newRow("init=0 target=-5") << 0.0 << -5.0;
1405     QTest::newRow("init=0 target=0")  << 0.0 <<  0.0;
1406 }
1407 
1408 void TestEkosCaptureWorkflow::testLoadEsqFileGeneral_data()
1409 {
1410     QTest::addColumn<uint>("esqVersion");               /*!< ESQ XML version                         */
1411     QTest::addColumn<QString>("observer");              /*!< Set the observer value                  */
1412     QTest::addColumn<bool>("guideDeviation");           /*!< Enable guide deviation                  */
1413     QTest::addColumn<bool>("startGuideDeviation");      /*!< Enable starting guide deviation         */
1414     QTest::addColumn<bool>("inSequenceFocus");          /*!< Enable in sequence focusing (HFR based) */
1415     QTest::addColumn<bool>("autofocusOnTemperature");   /*!< Enable temperature based autofocus      */
1416     QTest::addColumn<bool>("refocusEveryN");            /*!< Enable focusing after every n capture   */
1417     QTest::addColumn<bool>("refocusAfterMeridianFlip"); /*!< Enable refocus after a meridian flip    */
1418 
1419     uint version = TestEkosCaptureHelper::ESQ_VERSION_2_6;
1420     QTest::newRow(QString("observer v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit())
1421             << version << "KStars Freak" << false << false << false << false << false << false;
1422     QTest::newRow(QString("guideDeviation v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) << version <<
1423             "KStars Freak" << true << false << false << false << false << false;
1424     QTest::newRow(QString("startGuideDeviation v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) << version
1425             << "KStars Freak" << false << true << false << false << false << false;
1426     QTest::newRow(QString("inSequenceFocus v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) << version <<
1427             "KStars Freak" << false << false << true << false << false << false;
1428     QTest::newRow(QString("autofocusOnTemperature v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) <<
1429             version << "KStars Freak" << false << false << false << true << false << false;
1430     QTest::newRow(QString("refocusEveryN v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) << version <<
1431             "KStars Freak" << false << false << false << false << true << false;
1432     QTest::newRow(QString("refocusAfterMeridianFlip v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) <<
1433             version << "KStars Freak" << false << false << false << false << false << true;
1434 }
1435 
1436 void TestEkosCaptureWorkflow::testLoadEsqFileBasicJobSettings_data()
1437 {
1438     QTest::addColumn<uint>("esqVersion");             /*!< ESQ XML version             */
1439     QTest::addColumn<double>("exposureTime");         /*!< Exposure time               */
1440     QTest::addColumn<QString>("targetName");          /*!< Capture target              */
1441     QTest::addColumn<int>("count");                   /*!< Number of frames            */
1442     QTest::addColumn<int>("delay");                   /*!< Delay between captures      */
1443     QTest::addColumn<QString>("filter");              /*!< Filter name                 */
1444     QTest::addColumn<QString>("type");                /*!< Frame type (Light etc.)     */
1445     QTest::addColumn<QString>("encoding");            /*!< Encoding (FITS etc.)        */
1446     QTest::addColumn<int>("binX");                    /*!< Binning (X value)           */
1447     QTest::addColumn<int>("binY");                    /*!< Binning (Y value)           */
1448     QTest::addColumn<int>("x");                       /*!< ROI left                    */
1449     QTest::addColumn<int>("y");                       /*!< ROI top                     */
1450     QTest::addColumn<int>("w");                       /*!< ROI width                   */
1451     QTest::addColumn<int>("h");                       /*!< ROI height                  */
1452     QTest::addColumn<QString>("fitsDirectory");       /*!< Base directory for frames   */
1453     QTest::addColumn<QString>("placeholderFormat");   /*!< Placeholder format string   */
1454     QTest::addColumn<int>("formatSuffix");            /*!< Digits of the number suffix */
1455     QTest::addColumn<double>("cameraTemperature");    /*!< Cooling temperature         */
1456     QTest::addColumn<bool>("cameraCooling");          /*!< Cooling necessary           */
1457     QTest::addColumn<int>("fileUploadMode");          /*!< Upload mode (local/remote)  */
1458 
1459     double exposureTime = 2.0, cameraTemperature = -20.0;
1460     int count = 2, delay = 5000, binX = 2, binY = 2;
1461     int x = 10, y = 10, w = 480, h = 360;
1462     int formatSuffix = 4, fileUploadMode = 2;
1463     bool cameraCooling = true;
1464     QString target("Test Target");
1465     QString filter("Green");
1466     QString type("Light");
1467     QString encoding("Native");
1468     QString fitsDirectory("/home/astro");
1469     QString placeholderFormat("/%t/%T/%T_%t_%e");
1470 
1471     for (uint version :
1472             {
1473                 TestEkosCaptureHelper::ESQ_VERSION_2_4, TestEkosCaptureHelper::ESQ_VERSION_2_5, TestEkosCaptureHelper::ESQ_VERSION_2_6
1474             })
1475     {
1476         QTest::newRow(QString("%2x %5 %3 %1s bin=%6x%7 dir=%4 v=%8").arg(exposureTime).arg(count).arg(filter)
1477                       .arg(fitsDirectory).arg(type).arg(binX).arg(binY).arg(m_CaptureHelper->esqVersionNames[version]).toLatin1())
1478                 << version << exposureTime << target << count << delay << filter << type << encoding << binX << binY << x << y << w << h <<
1479                 fitsDirectory << placeholderFormat << formatSuffix << cameraTemperature << cameraCooling << fileUploadMode;
1480     }
1481 }
1482 
1483 void TestEkosCaptureWorkflow::testLoadEsqFileCalibrationSettings_data()
1484 {
1485     QTest::addColumn<uint>("esqVersion");             /*!< ESQ XML version              */
1486     QTest::addColumn<double>("exposureTime");         /*!< Exposure time                */
1487     QTest::addColumn<int>("count");                   /*!< Number of frames             */
1488     QTest::addColumn<QString>("type");                /*!< Frame type (Flat etc.)       */
1489     QTest::addColumn<uint>("pre_action");             /*!< Calibration Pre-Actions      */
1490     QTest::addColumn<double>("wall_az");              /*!< Az position for wall source  */
1491     QTest::addColumn<double>("wall_alt");             /*!< Alt position for wall source */
1492     QTest::addColumn<bool>("duration_manual");        /*!< Manual exposure time         */
1493     QTest::addColumn<bool>("duration_adu");           /*!< ADU based exposure           */
1494     QTest::addColumn<int>("adu");                     /*!< Target ADU                   */
1495     QTest::addColumn<int>("tolerance");               /*!< ADU tolerance                */
1496 
1497     for (uint version :
1498             {
1499                 TestEkosCaptureHelper::ESQ_VERSION_2_5, TestEkosCaptureHelper::ESQ_VERSION_2_6
1500             })
1501     {
1502         QTest::newRow(QString("Flat pre_action=wall adu=manual v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit())
1503                 << version << 1.0 << 2 <<
1504                 "Flat" << static_cast<uint>(ACTION_WALL) << 180.0 << 85.0 << true << false << 12345 << 1234;
1505         QTest::newRow(QString("Dark pre_action=none adu=automatic v=%1").arg(
1506                           m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) << version  << 1.0 << 2 << "Dark" <<
1507                                   static_cast<uint>(ACTION_NONE) << 180.0 << 85.0 << false << true <<  12345 << 1234;
1508         QTest::newRow(QString("Bias pre_action=park_mount v=%1").arg(m_CaptureHelper->esqVersionNames[version]).toLocal8Bit()) <<
1509                 version  << 1.0 << 2 << "Bias" << static_cast<uint>(ACTION_PARK_MOUNT) << 180.0 << 85.0 << false << true << 12345 << 1234;
1510     }
1511 }
1512 
1513 /* *********************************************************************************
1514  *
1515  * Test infrastructure
1516  *
1517  * ********************************************************************************* */
1518 
1519 void TestEkosCaptureWorkflow::initTestCase()
1520 {
1521     KVERIFY_EKOS_IS_HIDDEN();
1522     // limit guiding pulses to ensure that guiding deviations lead to aborted capture
1523     Options::setRAMaximumPulseArcSec(5.0);
1524     Options::setDECMaximumPulseArcSec(5.0);
1525 
1526     QStandardPaths::setTestModeEnabled(true);
1527 
1528     QFileInfo test_dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation), "test");
1529     destination = new QTemporaryDir(test_dir.absolutePath());
1530     QVERIFY(destination->isValid());
1531     QVERIFY(destination->autoRemove());
1532 }
1533 
1534 void TestEkosCaptureWorkflow::cleanupTestCase()
1535 {
1536     // nothing to do since we start the INDI service for each test case
1537 }
1538 
1539 bool TestEkosCaptureWorkflow::prepareTestCase()
1540 {
1541     // set logging defaults for alignment
1542     Options::setVerboseLogging(false);
1543     Options::setLogToFile(false);
1544 
1545     // turn off altitude limits
1546     Options::setEnableAltitudeLimits(false);
1547 
1548     // use the helper to start the profile
1549     KVERIFY_SUB(m_CaptureHelper->startEkosProfile());
1550     // prepare optical trains for testing
1551     m_CaptureHelper->prepareOpticalTrains();
1552     // prepare the mount module for testing with OAG guiding
1553     m_CaptureHelper->prepareMountModule(TestEkosCaptureHelper::SCOPE_FSQ85, TestEkosCaptureHelper::SCOPE_FSQ85);
1554     // prepare for focusing tests
1555     m_CaptureHelper->prepareFocusModule();
1556     // prepare for alignment tests
1557     m_CaptureHelper->prepareAlignmentModule();
1558     // prepare for guiding tests
1559     m_CaptureHelper->prepareGuidingModule();
1560     // prepare for capturing tests
1561     m_CaptureHelper->prepareCaptureModule();
1562 
1563     m_CaptureHelper->init();
1564 
1565     // clear image directory
1566     KVERIFY_SUB(m_CaptureHelper->getImageLocation()->removeRecursively());
1567 
1568     // ensure that the scope is unparked
1569     Ekos::Mount *mount = Ekos::Manager::Instance()->mountModule();
1570     if (mount->parkStatus() == ISD::PARK_PARKED)
1571         mount->unpark();
1572     KTRY_VERIFY_WITH_TIMEOUT_SUB(mount->parkStatus() == ISD::PARK_UNPARKED, 30000);
1573 
1574     // ensure that the dome is unparked
1575     //    if (m_CaptureHelper->m_DomeDevice != nullptr)
1576     //    {
1577     //        Ekos::Dome *dome = Ekos::Manager::Instance()->domeModule();
1578     //        KVERIFY_SUB(dome != nullptr);
1579     //        if (dome->parkStatus() == ISD::PARK_PARKED)
1580     //            dome->unpark();
1581     //        KTRY_VERIFY_WITH_TIMEOUT_SUB(dome->parkStatus() == ISD::PARK_UNPARKED, 30000);
1582     //    }
1583 
1584     // preparation successful
1585     return true;
1586 }
1587 
1588 void TestEkosCaptureWorkflow::init()
1589 {
1590     // reset counters
1591     image_count  = 0;
1592     // reset calibration
1593     Options::setCalibrationPreActionIndex(ACTION_NONE);
1594 
1595     KTRY_OPEN_EKOS();
1596     KVERIFY_EKOS_IS_OPENED();
1597     // clear light panel
1598     m_CaptureHelper->m_LightPanelDevice = nullptr;
1599     // clear rotator
1600     m_CaptureHelper->m_RotatorDevice = nullptr;
1601     // disable reset jobs warning
1602     KMessageBox::saveDontShowAgainYesNo("reset_job_status_warning", KMessageBox::ButtonCode::No);
1603 }
1604 
1605 void TestEkosCaptureWorkflow::cleanup()
1606 {
1607     if (Ekos::Manager::Instance()->focusModule() != nullptr)
1608         Ekos::Manager::Instance()->focusModule()->abort();
1609 
1610     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
1611     if (capture != nullptr)
1612     {
1613         capture->abort();
1614         capture->clearSequenceQueue();
1615         KTRY_SET_CHECKBOX(capture, limitRefocusS, false);
1616     }
1617 
1618     m_CaptureHelper->cleanup();
1619     QVERIFY(m_CaptureHelper->shutdownEkosProfile());
1620     KTRY_CLOSE_EKOS();
1621     KVERIFY_EKOS_IS_HIDDEN();
1622 }
1623 
1624 
1625 bool TestEkosCaptureWorkflow::prepareCapture(int refocusLimitTime, double refocusHFR, double refocusTemp, int delay)
1626 {
1627     QFETCH(double, exptime);
1628     QFETCH(QString, sequence);
1629     // test data
1630     // switch to capture module
1631     Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule();
1632     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000));
1633 
1634     // add target to path to emulate the behavior of the scheduler
1635     QString imagepath = getImageLocation()->path() + "/test";
1636 
1637     // create the destination for images
1638     qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << imagepath;
1639 
1640     // set refocusing limits
1641     KTRY_SET_CHECKBOX_SUB(capture, limitRefocusS, (refocusLimitTime > 0));
1642     if (refocusLimitTime > 0)
1643         KTRY_SET_SPINBOX_SUB(capture, limitRefocusN, refocusLimitTime);
1644 
1645     KTRY_SET_CHECKBOX_SUB(capture, limitFocusHFRS, (refocusHFR > 0));
1646     if (refocusHFR > 0)
1647         KTRY_SET_DOUBLESPINBOX_SUB(capture, limitFocusHFRN, refocusHFR);
1648 
1649     KTRY_SET_CHECKBOX_SUB(capture, limitFocusDeltaTS, (refocusTemp > 0));
1650     if (refocusTemp > 0)
1651         KTRY_SET_DOUBLESPINBOX_SUB(capture, limitFocusDeltaTN, refocusTemp);
1652 
1653     // create capture sequences
1654     KVERIFY_SUB(m_CaptureHelper->fillCaptureSequences(target, sequence, exptime, imagepath, delay));
1655 
1656     // everything successfully completed
1657     return true;
1658 }
1659 
1660 void TestEkosCaptureWorkflow::prepareTestData(double exptime, QList<QString> sequenceList)
1661 {
1662 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
1663     QSKIP("Bypassing fixture test on old Qt");
1664     Q_UNUSED(exptime)
1665     Q_UNUSED(sequence)
1666 #else
1667     QTest::addColumn<double>("exptime");             /*!< exposure time */
1668     QTest::addColumn<QString>("sequence");           /*!< list of filters */
1669 
1670     for (QString sequence : sequenceList)
1671         QTest::newRow(QString("seq=%2, exp=%1").arg(exptime).arg(sequence).toStdString().c_str()) << exptime << sequence;
1672 #endif
1673 }
1674 
1675 QDir *TestEkosCaptureWorkflow::getImageLocation()
1676 {
1677     if (imageLocation == nullptr || imageLocation->exists())
1678         imageLocation = new QDir(destination->path() + "/images");
1679 
1680     return imageLocation;
1681 }
1682 
1683 /* *********************************************************************************
1684  *
1685  * Main function
1686  *
1687  * ********************************************************************************* */
1688 
1689 QTEST_KSTARS_WITH_GUIDER_MAIN(TestEkosCaptureWorkflow)