File indexing completed on 2024-04-14 14:12:00

0001 /*
0002     Tests for scheduler job state machine.
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_sequencejobstate.h"
0010 
0011 #include "Options.h"
0012 
0013 TestSequenceJobState::TestSequenceJobState() : QObject() {}
0014 
0015 
0016 void TestSequenceJobState::testPrepareLightFrames()
0017 {
0018     QFETCH(bool, isPreview);
0019     QFETCH(bool, enforce_rotate);
0020     QFETCH(bool, enforce_temperature);
0021     QFETCH(bool, enforce_guiding);
0022     QFETCH(double, temperature_delta);
0023     QFETCH(double, angle_delta);
0024 
0025     double current_temp = 10.0;
0026     double current_angle = 10.0;
0027     double current_guide_dev = 100.0, target_guide_dev = 2.0;
0028 
0029     // initialize the test processor, but do not inform the state machine
0030     m_adapter->init(current_temp, current_angle, current_guide_dev);
0031 
0032     // set target values
0033     if (enforce_temperature)
0034         m_stateMachine->setTargetCCDTemperature(current_temp + temperature_delta);
0035     if (enforce_rotate)
0036         m_stateMachine->setTargetRotatorAngle(current_angle + angle_delta);
0037     if (enforce_guiding)
0038         Options::setStartGuideDeviation(target_guide_dev);
0039 
0040     // connect the processor
0041     connect(m_stateMachine, &Ekos::SequenceJobState::setRotatorAngle, m_adapter, &TestAdapter::setRotatorAngle);
0042     connect(m_stateMachine, &Ekos::SequenceJobState::setCCDTemperature, m_adapter, &TestAdapter::setCCDTemperature);
0043     connect(m_stateMachine, &Ekos::SequenceJobState::setCCDBatchMode, m_adapter, &TestAdapter::setCCDBatchMode);
0044     connect(m_adapter, &TestAdapter::newRotatorAngle, m_stateMachine, &Ekos::SequenceJobState::setCurrentRotatorPositionAngle);
0045     connect(m_adapter, &TestAdapter::newCCDTemperature, m_stateMachine, &Ekos::SequenceJobState::setCurrentCCDTemperature);
0046 
0047     // start the capture preparation
0048     m_stateMachine->prepareLightFrameCapture(enforce_temperature, isPreview);
0049 
0050     // The test adapter is simulates the behavior of real devices
0051     QTRY_VERIFY_WITH_TIMEOUT(m_adapter->isCapturePreparationComplete, 5000);
0052 }
0053 
0054 /* *********************************************************************************
0055  * Test data
0056  * ********************************************************************************* */
0057 
0058 void TestSequenceJobState::testPrepareLightFrames_data()
0059 {
0060     QTest::addColumn<bool>("isPreview");           /*!< preview capture? */
0061     QTest::addColumn<bool>("enforce_rotate");      /*!< enforce rotating? */
0062     QTest::addColumn<bool>("enforce_temperature"); /*!< enforce temperature? */
0063     QTest::addColumn<bool>("enforce_guiding");     /*!< enforce maximal initial guiding deviation? */
0064     QTest::addColumn<double>("temperature_delta"); /*!< difference between current and target temperature */
0065     QTest::addColumn<double>("angle_delta");     /*!< difference between current and target rotator angle */
0066 
0067     // iterate over all combinations
0068     for (bool preview :
0069             {
0070                 false, true
0071             })
0072         for (bool rotate :
0073                 {
0074                     false, true
0075                 })
0076             for (bool temperature :
0077                     {
0078                         false, true
0079                     })
0080                 for (bool guiding :
0081                         {
0082                             false, true
0083                         })
0084                     for (double delta_t :
0085                             {
0086                                 -21.0, 0.0
0087                                 })
0088                         for (double delta_r :
0089                     {
0090                         45.0, 0.0
0091                     })
0092     QTest::newRow(QString("preview=%4 enforce rotate=%1, temperature=%2, guiding=%3, delta_t=%5, delta_rot=%6")
0093                   .arg(rotate).arg(temperature).arg(guiding).arg(preview).arg(delta_t).arg(delta_r).toLocal8Bit())
0094             << preview << rotate << temperature << guiding << delta_t << delta_r;
0095 }
0096 
0097 /* *********************************************************************************
0098  * Test infrastructure
0099  * ********************************************************************************* */
0100 void TestSequenceJobState::initTestCase()
0101 {
0102     qDebug() << "initTestCase() started.";
0103 }
0104 
0105 void TestSequenceJobState::cleanupTestCase()
0106 {
0107     qDebug() << "cleanupTestCase() started.";
0108 }
0109 
0110 void TestSequenceJobState::init()
0111 {
0112     QSharedPointer<Ekos::CaptureModuleState> captureModuleState;
0113     captureModuleState.reset(new Ekos::CaptureModuleState());
0114     m_stateMachine = new Ekos::SequenceJobState(captureModuleState);
0115     // currently all tests are for light frames
0116     m_stateMachine->setFrameType(FRAME_LIGHT);
0117     QVERIFY(m_stateMachine->getStatus() == Ekos::JOB_IDLE);
0118     m_adapter = new TestAdapter();
0119     QVERIFY(m_adapter->isCapturePreparationComplete == false);
0120     // forward signals to the sequence job
0121     connect(m_adapter, &TestAdapter::prepareCapture, m_stateMachine, &Ekos::SequenceJobState::prepareLightFrameCapture);
0122     // react upon sequence job signals
0123     connect(m_stateMachine, &Ekos::SequenceJobState::prepareComplete, m_adapter, &TestAdapter::setCapturePreparationComplete);
0124     connect(m_stateMachine, &Ekos::SequenceJobState::readCurrentState, m_adapter, &TestAdapter::readCurrentState);
0125 
0126 }
0127 
0128 void TestSequenceJobState::cleanup()
0129 {
0130     disconnect(m_adapter, nullptr, m_stateMachine, nullptr);
0131     disconnect(m_stateMachine, nullptr, m_adapter, nullptr);
0132     delete m_adapter;
0133     delete m_stateMachine;
0134     m_adapter = nullptr;
0135     m_stateMachine = nullptr;
0136 }
0137 
0138 
0139 
0140 TestAdapter::TestAdapter()
0141 {
0142     // simulate a CCD cooler that stepwise reduces the temperature to a target value
0143     temperatureTimer = new QTimer(this);
0144     temperatureTimer->setInterval(200);
0145     temperatureTimer->setSingleShot(false);
0146     connect(temperatureTimer, &QTimer::timeout, this, [this]()
0147     {
0148         if (std::abs(m_ccdtemperature - m_targetccdtemp) > std::numeric_limits<double>::epsilon())
0149         {
0150             // decrease gap by 1 deg or less
0151             double delta = std::min(1.0, std::abs(m_ccdtemperature - m_targetccdtemp));
0152             m_ccdtemperature += (m_targetccdtemp > m_ccdtemperature) ? delta : -delta;
0153             // publish new value
0154             emit newCCDTemperature(m_ccdtemperature);
0155         }
0156         else
0157             // finish if temperature has been reached
0158             temperatureTimer->stop();
0159     });
0160     // simulate a rotator that stepwise rotates to the target value
0161     rotatorTimer = new QTimer(this);
0162     rotatorTimer->setInterval(200);
0163     rotatorTimer->setSingleShot(false);
0164     connect(rotatorTimer, &QTimer::timeout, this, [this]()
0165     {
0166         if (std::abs(m_rotatorangle - m_targetrotatorangle) > std::numeric_limits<double>::epsilon())
0167         {
0168             // decrease gap by 10 deg or less
0169             double delta = std::min(10.0, std::abs(m_rotatorangle - m_targetrotatorangle));
0170             m_rotatorangle += (m_rotatorangle < m_targetrotatorangle) ? delta : -delta;
0171             // publish new value
0172             emit newRotatorAngle(m_rotatorangle, std::abs(m_rotatorangle - m_targetrotatorangle) > 1.0 ? IPS_BUSY : IPS_OK);
0173         }
0174         else
0175             // finish if target angle has been reached
0176             rotatorTimer->stop();
0177     });
0178     // simulate guiding bringing the deviation stepwise below the threshold
0179     guidingTimer = new QTimer(this);
0180     guidingTimer->setInterval(200);
0181     guidingTimer->setSingleShot(false);
0182     connect(guidingTimer, &QTimer::timeout, this, [this]()
0183     {
0184         if (m_guiding_dev > m_target_guiding_dev)
0185         {
0186             m_guiding_dev /= 2;
0187             // publish new value
0188             emit newGuiderDrift(m_guiding_dev);
0189         }
0190         else
0191             // finish if target angle has been reached
0192             guidingTimer->stop();
0193     });
0194 }
0195 
0196 void TestAdapter::init(double temp, double angle, double guiding_limit)
0197 {
0198     m_ccdtemperature = temp;
0199     m_rotatorangle   = angle;
0200     m_guiding_dev    = guiding_limit;
0201 }
0202 
0203 void TestAdapter::setCCDBatchMode(bool m_preview)
0204 {
0205     m_ispreview = m_preview;
0206 }
0207 
0208 void TestAdapter::setCCDTemperature(double value)
0209 {
0210     // simulate behaviour of the INDI server
0211     emit newCCDTemperature(value);
0212     // set the new target
0213     m_targetccdtemp = value;
0214     // start timer if not already running
0215     if (! temperatureTimer->isActive())
0216         temperatureTimer->start();
0217 }
0218 
0219 void TestAdapter::setRotatorAngle(double value)
0220 {
0221     // set the new target
0222     m_targetrotatorangle = value;
0223     // start timer if not already running
0224     if (! rotatorTimer->isActive())
0225         rotatorTimer->start();
0226 }
0227 
0228 void TestAdapter::readCurrentState(Ekos::CaptureState state)
0229 {
0230     // signal the current device value
0231     switch (state)
0232     {
0233         case Ekos::CAPTURE_SETTING_TEMPERATURE:
0234             emit newCCDTemperature(m_ccdtemperature);
0235             break;
0236         case Ekos::CAPTURE_SETTING_ROTATOR:
0237             emit newRotatorAngle(m_rotatorangle, IPS_OK);
0238             break;
0239         default:
0240             // do nothing
0241             break;
0242     }
0243 }
0244 
0245 QTEST_GUILESS_MAIN(TestSequenceJobState)