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)