1 /* KStars UI tests
2 SPDX-FileCopyrightText: 2020 Eric Dejouhanet <eric.dejouhanet@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7
8 #include "test_ekos_focus.h"
9
10 #if defined(HAVE_INDI)
11
12 #include "kstars_ui_tests.h"
13 #include "test_ekos.h"
14 #include "test_ekos_simulator.h"
15 #include "test_ekos_mount.h"
16 #include "Options.h"
17
18 class KFocusProcedureSteps: public QObject
19 {
20 public:
21 QMetaObject::Connection starting;
22 QMetaObject::Connection aborting;
23 QMetaObject::Connection completing;
24 QMetaObject::Connection notguiding;
25 QMetaObject::Connection guiding;
26 QMetaObject::Connection quantifying;
27
28 public:
29 bool started { false };
30 bool aborted { false };
31 bool complete { false };
32 bool unguided { false };
33 double hfr { -2 };
34
35 public:
KFocusProcedureSteps()36 KFocusProcedureSteps():
37 starting (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusStarting, this, [&]() { started = true; }, Qt::UniqueConnection)),
__anon130cd9be0202() 38 aborting (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusAborted, this, [&]() { started = false; aborted = true; }, Qt::UniqueConnection)),
__anon130cd9be0302() 39 completing (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusComplete, this, [&]() { started = false; complete = true; }, Qt::UniqueConnection)),
__anon130cd9be0402() 40 notguiding (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::suspendGuiding, this, [&]() { unguided = true; }, Qt::UniqueConnection)),
__anon130cd9be0502() 41 guiding (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::resumeGuiding, this, [&]() { unguided = false; }, Qt::UniqueConnection)),
__anon130cd9be0602(double _hfr) 42 quantifying (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::newHFR, this, [&](double _hfr) { hfr = _hfr; }, Qt::UniqueConnection))
43 {};
~KFocusProcedureSteps()44 virtual ~KFocusProcedureSteps() {
45 disconnect(starting);
46 disconnect(aborting);
47 disconnect(completing);
48 disconnect(notguiding);
49 disconnect(guiding);
50 disconnect(quantifying);
51 };
52 };
53
54 class KFocusStateList: public QObject, public QList <Ekos::FocusState>
55 {
56 public:
57 QMetaObject::Connection handler;
58
59 public:
KFocusStateList()60 KFocusStateList():
61 handler (connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::newStatus, this, [&](Ekos::FocusState s) { append(s); }, Qt::UniqueConnection))
62 {};
~KFocusStateList()63 virtual ~KFocusStateList() {};
64 };
65
TestEkosFocus(QObject * parent)66 TestEkosFocus::TestEkosFocus(QObject *parent) : QObject(parent)
67 {
68 }
69
initTestCase()70 void TestEkosFocus::initTestCase()
71 {
72 KVERIFY_EKOS_IS_HIDDEN();
73 KTRY_OPEN_EKOS();
74 KVERIFY_EKOS_IS_OPENED();
75 KTRY_EKOS_START_SIMULATORS();
76
77 // We can't use this here because of the meridian flip test
78 // HACK: Reset clock to initial conditions
79 // KHACK_RESET_EKOS_TIME();
80
81 KTELL_BEGIN();
82 }
83
cleanupTestCase()84 void TestEkosFocus::cleanupTestCase()
85 {
86 KTELL_END();
87 KTRY_EKOS_STOP_SIMULATORS();
88 KTRY_CLOSE_EKOS();
89 KVERIFY_EKOS_IS_HIDDEN();
90 }
91
init()92 void TestEkosFocus::init()
93 {
94 }
95
cleanup()96 void TestEkosFocus::cleanup()
97 {
98 if (Ekos::Manager::Instance())
99 if (Ekos::Manager::Instance()->focusModule())
100 Ekos::Manager::Instance()->focusModule()->abort();
101 KTELL_HIDE();
102 }
103
testCaptureStates()104 void TestEkosFocus::testCaptureStates()
105 {
106 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.");
107 KTRY_MOUNT_SYNC(60.0, true, -1);
108
109 // Prepare to detect state change
110 KFocusStateList state_list;
111 QVERIFY(state_list.handler);
112
113 KTELL("Configure fields.\nCapture a frame.\nExpect PROGRESS, IDLE.");
114 KTRY_FOCUS_MOVETO(40000);
115 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
116 KTRY_FOCUS_DETECT(2, 1, 99);
117 QTRY_COMPARE_WITH_TIMEOUT(state_list.count(), 2, 5000);
118 QCOMPARE(state_list[0], Ekos::FocusState::FOCUS_PROGRESS);
119 QCOMPARE(state_list[1], Ekos::FocusState::FOCUS_IDLE);
120 state_list.clear();
121
122 KTELL("Move focuser.\nExpect no capture triggered.");
123 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
124 KTRY_FOCUS_MOVETO(43210);
125 QTest::qWait(1000);
126 QCOMPARE(state_list.count(), 0);
127
128 KTRY_FOCUS_GADGET(QPushButton, startLoopB);
129 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
130
131 KTELL("Loop captures.\nAbort loop.\nExpect FRAMING, PROGRESS, ABORTED, ABORTED.");
132 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
133 KTRY_FOCUS_CLICK(startLoopB);
134 QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 1, 5000);
135 KTRY_FOCUS_CLICK(stopFocusB);
136 QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 4, 5000);
137 QCOMPARE((int)state_list[0], (int)Ekos::FocusState::FOCUS_FRAMING);
138 QCOMPARE((int)state_list[1], (int)Ekos::FocusState::FOCUS_PROGRESS);
139 QCOMPARE((int)state_list[2], (int)Ekos::FocusState::FOCUS_ABORTED);
140 QCOMPARE((int)state_list[3], (int)Ekos::FocusState::FOCUS_ABORTED);
141 state_list.clear();
142
143 KTRY_FOCUS_GADGET(QCheckBox, useAutoStar);
144 KTRY_FOCUS_GADGET(QPushButton, resetFrameB);
145
146 QWARN("This test does not wait for the hardcoded timeout to select a star.");
147
148 KTELL("Use a successful automatic star selection (not full-field).\nCapture a frame.\nExpect PROGRESS, IDLE\nCheck star selection.");
149 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.0, 3.0);
150 useAutoStar->setCheckState(Qt::CheckState::Checked);
151 KTRY_FOCUS_DETECT(2, 1, 99);
152 QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 2, 5000);
153 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
154 KTRY_FOCUS_CLICK(resetFrameB);
155 QCOMPARE(state_list[0], Ekos::FocusState::FOCUS_PROGRESS);
156 QCOMPARE(state_list[1], Ekos::FocusState::FOCUS_IDLE);
157 useAutoStar->setCheckState(Qt::CheckState::Unchecked);
158 state_list.clear();
159
160 KTELL("Use an unsuccessful automatic star selection (not full-field).\nCapture a frame\nExpect PROGRESS, WAITING.\nCheck star selection.");
161 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.0, 3.0);
162 useAutoStar->setCheckState(Qt::CheckState::Checked);
163 KTRY_FOCUS_DETECT(0.01, 1, 1);
164 QTRY_VERIFY_WITH_TIMEOUT(state_list.count() >= 2, 5000);
165 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
166 KTRY_FOCUS_CLICK(resetFrameB);
167 QCOMPARE(state_list[0], Ekos::FocusState::FOCUS_PROGRESS);
168 QCOMPARE(state_list[1], Ekos::FocusState::FOCUS_WAITING);
169 useAutoStar->setCheckState(Qt::CheckState::Unchecked);
170 state_list.clear();
171 }
172
testDuplicateFocusRequest()173 void TestEkosFocus::testDuplicateFocusRequest()
174 {
175 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a fast autofocus.");
176 KTRY_FOCUS_SHOW();
177 KTRY_MOUNT_SYNC(60.0, true, -1);
178 KTRY_FOCUS_MOVETO(35000);
179 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.0, 30);
180 KTRY_FOCUS_EXPOSURE(3, 99);
181
182 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
183 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
184 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
185 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
186
187 // Prepare to detect the beginning of the autofocus_procedure
188 KFocusProcedureSteps autofocus;
189 QVERIFY(autofocus.starting);
190
191 KTELL("Click the autofocus button\nExpect a signal that the procedure starts.\nExpect state change and disabled button.");
192 KTRY_FOCUS_CLICK(startFocusB);
193 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
194 QVERIFY(Ekos::Manager::Instance()->focusModule()->status() != Ekos::FOCUS_IDLE);
195 QVERIFY(!startFocusB->isEnabled());
196 QVERIFY(stopFocusB->isEnabled());
197
198 KTELL("Issue a few autofocus commands at that point through the d-bus entry point\nExpect no parallel procedure start.");
199 for (int i = 0; i < 5; i++)
200 {
201 autofocus.started = false;
202 Ekos::Manager::Instance()->focusModule()->start();
203 QTest::qWait(500);
204 QVERIFY(!autofocus.started);
205 }
206
207 KTELL("Stop the running autofocus.");
208 KTRY_FOCUS_CLICK(stopFocusB);
209 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
210 }
211
testAutofocusSignalEmission()212 void TestEkosFocus::testAutofocusSignalEmission()
213 {
214 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure fast autofocus.");
215 KTRY_FOCUS_SHOW();
216 KTRY_MOUNT_SYNC(60.0,true, -1);
217
218 KTRY_FOCUS_MOVETO(35000);
219 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 30);
220 KTRY_FOCUS_EXPOSURE(3, 99);
221
222 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
223 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
224 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
225 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
226
227 // Prepare to detect the beginning of the autofocus_procedure
228 KFocusProcedureSteps autofocus;
229 QVERIFY(autofocus.starting);
230
231 KTELL("Configure to restart autofocus when it finishes, like Scheduler does.");
232 volatile bool ran_once = false;
233 autofocus.completing = connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusComplete, &autofocus, [&]() {
234 autofocus.complete = true;
235 autofocus.started = false;
236 if (!ran_once)
237 {
238 Ekos::Manager::Instance()->focusModule()->start();
239 ran_once = true;
240 }
241 }, Qt::UniqueConnection);
242 QVERIFY(autofocus.completing);
243
244 KTELL("Run autofocus, wait for completion.\nHandler restarts a second one immediately.");
245 QVERIFY(!autofocus.started);
246 QVERIFY(!autofocus.complete);
247 KTRY_FOCUS_CLICK(startFocusB);
248 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
249 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
250
251 KTELL("Wait for the second run to finish.\nNo other autofocus started.");
252 autofocus.complete = false;
253 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
254 QVERIFY(!autofocus.started);
255 }
256
testFocusAbort()257 void TestEkosFocus::testFocusAbort()
258 {
259 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure fast autofocus.");
260 KTRY_FOCUS_SHOW();
261 KTRY_MOUNT_SYNC(60.0, true, -1);
262 KTRY_FOCUS_MOVETO(35000);
263 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 30);
264 KTRY_FOCUS_EXPOSURE(3, 99);
265
266 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
267 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
268 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
269 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
270
271 // Prepare to detect the beginning of the autofocus_procedure
272 KFocusProcedureSteps autofocus;
273 QVERIFY(autofocus.starting);
274 QVERIFY(autofocus.completing);
275
276 KTELL("Configure to restart autofocus when it finishes, like Scheduler does.");
277 volatile bool ran_once = false;
278 autofocus.aborting = connect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::autofocusAborted, this, [&]() {
279 autofocus.aborted = true;
280 autofocus.started = false;
281 if (!ran_once)
282 {
283 Ekos::Manager::Instance()->focusModule()->start();
284 ran_once = true;
285 }
286 }, Qt::UniqueConnection);
287 QVERIFY(autofocus.aborting);
288
289 KTELL("Run autofocus, don't wait for the completion signal and abort it.\nHandler restarts a second one immediately.");
290 QVERIFY(!autofocus.started);
291 QVERIFY(!autofocus.aborted);
292 QVERIFY(!autofocus.complete);
293 KTRY_FOCUS_CLICK(startFocusB);
294 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
295 KTRY_FOCUS_CLICK(stopFocusB);
296 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 1000);
297
298 KTELL("Wait for the second run to finish.\nNo other autofocus started.");
299 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
300 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
301 QVERIFY(!autofocus.started);
302 }
303
testGuidingSuspendWhileFocusing()304 void TestEkosFocus::testGuidingSuspendWhileFocusing()
305 {
306 KTELL("Sync high on meridian to avoid jitter in CCD Simulator\nConfigure a fast autofocus.");
307 KTRY_FOCUS_SHOW();
308 KTRY_MOUNT_SYNC(60.0, true, -1);
309 KTRY_FOCUS_MOVETO(35000);
310 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 30);
311 KTRY_FOCUS_EXPOSURE(3, 99);
312
313 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
314 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
315 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
316 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
317
318 // Prepare to detect the beginning of the autofocus_procedure
319 KFocusProcedureSteps autofocus;
320 QVERIFY(autofocus.starting);
321 QVERIFY(autofocus.aborting);
322 QVERIFY(autofocus.completing);
323 QVERIFY(autofocus.notguiding);
324 QVERIFY(autofocus.guiding);
325
326 KTRY_FOCUS_GADGET(QCheckBox, suspendGuideCheck);
327
328 KTELL("Abort the autofocus with guiding set to suspend\nGuiding required to suspend, then required to resume");
329 suspendGuideCheck->setCheckState(Qt::CheckState::Checked);
330 QVERIFY(!autofocus.started);
331 QVERIFY(!autofocus.aborted);
332 QVERIFY(!autofocus.complete);
333 QVERIFY(!autofocus.unguided);
334 KTRY_FOCUS_CLICK(startFocusB);
335 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
336 QVERIFY(autofocus.unguided);
337 Ekos::Manager::Instance()->focusModule()->abort();
338 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
339 QVERIFY(!autofocus.unguided);
340
341 KTELL("Run the autofocus to completion with guiding set to suspend\nGuiding required to suspend, then required to resume\nNo other autofocus started.");
342 autofocus.started = autofocus.aborted = false;
343 KTRY_FOCUS_CLICK(startFocusB);
344 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
345 QVERIFY(autofocus.unguided);
346 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
347 QVERIFY(!autofocus.unguided);
348 QVERIFY(!autofocus.started);
349
350 KTELL("Abort the autofocus with guiding set to continue\nNo guiding signal emitted");
351 suspendGuideCheck->setCheckState(Qt::CheckState::Unchecked);
352 autofocus.started = autofocus.aborted = autofocus.complete = autofocus.unguided = false;
353 KTRY_FOCUS_CLICK(startFocusB);
354 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
355 QVERIFY(!autofocus.unguided);
356 Ekos::Manager::Instance()->focusModule()->abort();
357 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
358 QVERIFY(!autofocus.unguided);
359
360 KTELL("Run the autofocus to completion with guiding set to continue\nNo guiding signal emitted\nNo other autofocus started.");
361 autofocus.started = autofocus.aborted = false;
362 KTRY_FOCUS_CLICK(startFocusB);
363 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
364 QVERIFY(!autofocus.unguided);
365 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 30000);
366 QVERIFY(!autofocus.unguided);
367 QVERIFY(!autofocus.started);
368 }
369
testFocusWhenMountFlips()370 void TestEkosFocus::testFocusWhenMountFlips()
371 {
372 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a fast autofocus.");
373 KTRY_FOCUS_SHOW();
374 KTRY_MOUNT_SYNC(60.0, true, +10.0/3600);
375 KTRY_FOCUS_MOVETO(35000);
376 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 5);
377 KTRY_FOCUS_EXPOSURE(3, 99);
378
379 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
380 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
381 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
382 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
383
384 // Prepare to detect the states of the autofocus_procedure
385 KFocusProcedureSteps autofocus;
386 QVERIFY(autofocus.starting);
387 QVERIFY(autofocus.aborting);
388 QVERIFY(autofocus.completing);
389
390 KTELL("Ensure flip is enabled on meridian.\n.Start a standard autofocus.");
391 Ekos::Manager::Instance()->mountModule()->setMeridianFlipValues(true, 0);
392 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->meridianFlipEnabled(), 1000);
393 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->meridianFlipValue() == 0, 1000);
394 QVERIFY(!autofocus.started);
395 QVERIFY(!autofocus.aborted);
396 QVERIFY(!autofocus.complete);
397 KTRY_FOCUS_CLICK(startFocusB);
398 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 5000);
399
400 KTELL("Wait for the meridian flip to occur.\nCheck procedure aborts while flipping.");
401 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->status() == ISD::Telescope::MOUNT_SLEWING, 15000);
402 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 5000);
403
404 KTELL("Wait for the flip to end.");
405 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->status() == ISD::Telescope::MOUNT_TRACKING, 120000);
406
407 KTELL("Start the procedure again.\nExpect the procedure to succeed.");
408 autofocus.started = false;
409 autofocus.aborted = false;
410 autofocus.complete = false;
411 KTRY_FOCUS_CLICK(startFocusB);
412 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 60000);
413 }
414
testFocusWhenHFRChecking()415 void TestEkosFocus::testFocusWhenHFRChecking()
416 {
417 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a fast autofocus.");
418 KTRY_FOCUS_SHOW();
419 KTRY_MOUNT_SYNC(60.0, true, -1);
420 int initialFocusPosition = 35000;
421 KTRY_FOCUS_MOVETO(initialFocusPosition);
422 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 50);
423 KTRY_FOCUS_EXPOSURE(3, 99);
424
425 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
426 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
427 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
428 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
429
430 // Prepare to detect the beginning of the autofocus_procedure
431 KFocusProcedureSteps autofocus;
432 QVERIFY(autofocus.starting);
433 QVERIFY(autofocus.aborting);
434 QVERIFY(autofocus.completing);
435
436 KTELL("Run a standard autofocus.\nRun a HFR check.\nExpect no effect on the procedure.");
437 QVERIFY(!autofocus.started);
438 QVERIFY(!autofocus.aborted);
439 QVERIFY(!autofocus.complete);
440 KTRY_FOCUS_CLICK(startFocusB);
441 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
442
443 KTELL("Wait a little, run a first HFR check while the procedure runs.");
444 QTest::qWait(3000);
445 Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
446
447 KTELL("Expect procedure to succeed nonetheless.");
448 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 60000);
449
450 KTELL("Run a second HFR check that would start an autofocus.");
451 autofocus.complete = false;
452 Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
453
454 KTELL("Expect procedure to start properly.\nAbort the procedure manually.\nRun a third HFR check.");
455 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
456 KTRY_FOCUS_CLICK(stopFocusB);
457 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 10000);
458
459 KTELL("Expect autofocus to start properly.\nChange settings so that the procedure fails now.\nExpect a failure.");
460 autofocus.aborted = autofocus.complete = false;
461 Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
462 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
463 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 0.1, 0.1);
464 KTRY_FOCUS_EXPOSURE(0.1, 1);
465 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 90000);
466 KTRY_FOCUS_CHECK_POSITION_WITH_TIMEOUT(initialFocusPosition, 5000);
467
468 KTELL("Run a fourth HFR check.\nExpect autofocus to complete.");
469 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 50);
470 KTRY_FOCUS_EXPOSURE(3, 99);
471 autofocus.aborted = autofocus.complete = false;
472 Ekos::Manager::Instance()->focusModule()->checkFocus(0.1);
473 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 10000);
474 QTRY_VERIFY_WITH_TIMEOUT(autofocus.complete, 60000);
475 }
476
testFocusFailure()477 void TestEkosFocus::testFocusFailure()
478 {
479 KTELL("Sync high on meridian to avoid jitter in CCD Simulator");
480 KTRY_FOCUS_SHOW();
481 KTRY_MOUNT_SYNC(60.0, true, -1);
482
483 KTELL("Configure an autofocus that cannot see any star, so that the initial setup fails.");
484 KTRY_FOCUS_MOVETO(10000);
485 KTRY_FOCUS_CONFIGURE("SEP", "Polynomial", 0.0, 1.0, 0.1);
486 KTRY_FOCUS_EXPOSURE(0.01, 1);
487
488 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
489 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
490 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
491 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
492
493 // Prepare to detect the beginning of the autofocus_procedure
494 KFocusProcedureSteps autofocus;
495 QVERIFY(autofocus.starting);
496 QVERIFY(autofocus.aborting);
497 QVERIFY(autofocus.completing);
498
499 KTELL("Run the autofocus, wait for the completion signal.\nExpect no further autofocus started as we are not running a sequence.");
500 QVERIFY(!autofocus.started);
501 QVERIFY(!autofocus.aborted);
502 QVERIFY(!autofocus.complete);
503 KTRY_FOCUS_CLICK(startFocusB);
504 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
505 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 30000);
506 QVERIFY(!autofocus.started);
507
508 QSKIP("Skipping abort test for device limits, focus algorithms are too sensitive to CCD Sim noise.");
509
510 KTELL("Configure an autofocus that can see stars but is too far off and cannot achieve focus, so that the procedure fails.");
511 KTRY_FOCUS_MOVETO(25000);
512 QWARN("Iterative and Polynomial are too easily successful for this test.");
513 KTRY_FOCUS_CONFIGURE("SEP", "Linear", 0.0, 100.0, 1.0);
514 KTRY_FOCUS_EXPOSURE(5, 99);
515 KTRY_FOCUS_GADGET(QDoubleSpinBox, maxTravelIN);
516 maxTravelIN->setValue(2000);
517
518 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
519 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
520 autofocus.started = false;
521 autofocus.aborted = false;
522 autofocus.complete = false;
523
524 KTELL("Run the autofocus, wait for the completion signal.\nNo further autofocus started as we are not running a sequence.");
525 QVERIFY(!autofocus.started);
526 QVERIFY(!autofocus.aborted);
527 QVERIFY(!autofocus.complete);
528 KTRY_FOCUS_CLICK(startFocusB);
529 QTRY_VERIFY_WITH_TIMEOUT(autofocus.started, 500);
530 QTRY_VERIFY_WITH_TIMEOUT(autofocus.aborted, 240000);
531 QVERIFY(!autofocus.started);
532 }
533
testFocusOptions()534 void TestEkosFocus::testFocusOptions()
535 {
536 KTELL("Sync high on meridian to avoid jitter in CCD Simulator.\nConfigure a standard autofocus.");
537 KTRY_FOCUS_SHOW();
538 KTRY_MOUNT_SYNC(60.0, true, -1);
539 KTRY_FOCUS_MOVETO(40000);
540 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3);
541 KTRY_FOCUS_EXPOSURE(1, 99);
542
543 // Prepare to detect a new HFR
544 KFocusProcedureSteps autofocus;
545 QVERIFY(autofocus.quantifying);
546
547 KTRY_FOCUS_GADGET(QPushButton, captureB);
548
549 KTELL("Validate filter to apply to frame after capture.");
550 // This option is tricky: it follows the FITSViewer::filterTypes filter list, but
551 // is used as a list of filter that may be applied in the FITS view.
552 // In the Focus view, it also requires the "--" item as no-op filter, present in the
553 // combobox stating which filter to apply to frames before analysing them.
554 {
555 // Validate the default Ekos option is recognised as a filter
556 int const fe = Options::focusEffect();
557 QVERIFY(0 <= fe && fe < FITSViewer::filterTypes.count() + 1);
558 QWARN(qPrintable(QString("Default filtering option is %1/%2").arg(fe).arg(FITSViewer::filterTypes.value(fe - 1, "--"))));
559
560 // Validate the UI changes the Ekos option
561 for (int i = 0; i < FITSViewer::filterTypes.count(); i++)
562 {
563 QTRY_VERIFY_WITH_TIMEOUT(captureB->isEnabled(), 5000);
564
565 QString const & filterType = FITSViewer::filterTypes.value(i, "Unknown image filter");
566 QWARN(qPrintable(QString("Testing filtering option %1/%2").arg(i + 1).arg(filterType)));
567
568 // Set filter to apply in the UI, verify impact on Ekos option
569 Options::setFocusEffect(fe);
570 KTRY_FOCUS_COMBO_SET(filterCombo, filterType);
571 QTRY_COMPARE_WITH_TIMEOUT(Options::focusEffect(), (uint) i + 1, 1000);
572
573 // Set filter to apply with the d-bus entry point, verify impact on Ekos option
574 Options::setFocusEffect(fe);
575 Ekos::Manager::Instance()->focusModule()->setImageFilter(filterType);
576 QTRY_COMPARE_WITH_TIMEOUT(Options::focusEffect(), (uint) i + 1, 1000);
577
578 // Run a capture with detection for coverage
579 autofocus.hfr = -2;
580 KTRY_FOCUS_CLICK(captureB);
581 QTRY_VERIFY_WITH_TIMEOUT(-1 <= autofocus.hfr, 5000);
582 }
583
584 // Set no-op filter to apply in the UI, verify impact on Ekos option
585 Options::setFocusEffect(0);
586 KTRY_FOCUS_COMBO_SET(filterCombo, "--");
587 QTRY_COMPARE_WITH_TIMEOUT(Options::focusEffect(), (uint) 0, 1000);
588
589 // Set no-op filter to apply with the d-bus entry point, verify impact on Ekos option
590 Options::setFocusEffect(0);
591 Ekos::Manager::Instance()->focusModule()->setImageFilter("--");
592 QTRY_COMPARE_WITH_TIMEOUT(Options::focusEffect(), (uint) 0, 1000);
593
594 // Restore the original Ekos option
595 KTRY_FOCUS_COMBO_SET(filterCombo, FITSViewer::filterTypes.value(fe - 1, "--"));
596 QTRY_COMPARE_WITH_TIMEOUT(Options::focusEffect(), (uint) fe, 1000);
597 }
598 }
599
testStarDetection_data()600 void TestEkosFocus::testStarDetection_data()
601 {
602 #if QT_VERSION < 0x050900
603 QSKIP("Skipping fixture-based test on old QT version.");
604 #else
605 QTest::addColumn<QString>("NAME");
606 QTest::addColumn<QString>("RA");
607 QTest::addColumn<QString>("DEC");
608
609 // Altitude computation taken from SchedulerJob::findAltitude
610 GeoLocation * const geo = KStarsData::Instance()->geo();
611 KStarsDateTime const now(KStarsData::Instance()->lt());
612 KSNumbers const numbers(now.djd());
613 CachingDms const LST = geo->GSTtoLST(geo->LTtoUT(now).gst());
614
615 std::list<char const *> Objects = { "Polaris", "Mizar", "M 51", "M 13", "M 47", "Vega", "NGC 2238", "M 81" };
616 size_t count = 0;
617
618 foreach (char const *name, Objects)
619 {
620 SkyObject const * const so = KStars::Instance()->data()->objectNamed(name);
621 if (so != nullptr)
622 {
623 SkyObject o(*so);
624 o.updateCoordsNow(&numbers);
625 o.EquatorialToHorizontal(&LST, geo->lat());
626 if (10.0 < o.alt().Degrees())
627 {
628 QTest::addRow("%s", name)
629 << name
630 << o.ra().toHMSString()
631 << o.dec().toDMSString();
632 count++;
633 }
634 else QWARN(QString("Fixture '%1' altitude is '%2' degrees, discarding.").arg(name).arg(so->alt().Degrees()).toStdString().c_str());
635 }
636 }
637
638 if (!count)
639 QSKIP("No usable fixture objects, bypassing test.");
640 #endif
641 }
642
testStarDetection()643 void TestEkosFocus::testStarDetection()
644 {
645
646 #if QT_VERSION < 0x050900
647 QSKIP("Skipping fixture-based test on old QT version.");
648 #else
649 Ekos::Manager * const ekos = Ekos::Manager::Instance();
650 QVERIFY(ekos);
651
652 QFETCH(QString, NAME);
653 QFETCH(QString, RA);
654 QFETCH(QString, DEC);
655
656 KTELL(QString(NAME+"\nSync to %1/%2 to make the mount teleport to the object.").arg(qPrintable(RA)).arg(qPrintable(DEC)));
657 QTRY_VERIFY_WITH_TIMEOUT(ekos->mountModule() != nullptr, 5000);
658 ekos->mountModule()->setMeridianFlipValues(false, 0);
659 QVERIFY(ekos->mountModule()->sync(RA, DEC));
660 ekos->mountModule()->setTrackEnabled(true);
661
662 KTELL(NAME+"\nWait for Focus to come up\nSwitch to Focus tab.");
663 KTRY_FOCUS_SHOW();
664
665 KTRY_FOCUS_GADGET(QPushButton, startFocusB);
666 KTRY_FOCUS_GADGET(QPushButton, stopFocusB);
667 QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 1000);
668 QTRY_VERIFY_WITH_TIMEOUT(!stopFocusB->isEnabled(), 1000);
669
670 KTRY_FOCUS_GADGET(QLineEdit, starsOut);
671
672 KTELL(NAME+"\nMove focuser to see stars.");
673 KTRY_FOCUS_MOVETO(35000);
674
675 KTELL(NAME+"\nRun the detection with SEP.");
676 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
677 KTRY_FOCUS_DETECT(1, 3, 99);
678 QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
679
680 KTELL(NAME+"\nRun the detection with Centroid.");
681 KTRY_FOCUS_CONFIGURE("Centroid", "Iterative", 0.0, 100.0, 3.0);
682 KTRY_FOCUS_DETECT(1, 3, 99);
683 QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
684
685 KTELL(NAME+"\nRun the detection with Threshold (no full-field).");
686 KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0.0, 0.0, 3.0);
687 KTRY_FOCUS_DETECT(1, 3, 99);
688 QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
689
690 KTELL(NAME+"\nRun the detection with Gradient (no full-field).");
691 KTRY_FOCUS_CONFIGURE("Gradient", "Iterative", 0.0, 0.0, 3.0);
692 KTRY_FOCUS_DETECT(1, 3, 99);
693 QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
694
695 KTELL(NAME+"\nRun the detection with SEP (8s capture).");
696 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, 3.0);
697 KTRY_FOCUS_DETECT(8, 1, 99);
698 QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000);
699
700 KTELL(NAME+"\nRun the detection with SEP\nFull-field with various values\nHFR averaged on 3 frames.");
701 for (double inner = 0.0; inner < 100.0; inner += 43.0)
702 {
703 for (double outer = 100.0; inner < outer; outer -= 42.0)
704 {
705 KTRY_FOCUS_CONFIGURE("SEP", "Iterative", inner, outer, 3.0);
706 KTRY_FOCUS_DETECT(1, 2, 99);
707 }
708 }
709
710 KTELL(NAME+"\nRun the detection with Threshold, full-field.");
711 for (double threshold = 80.0; threshold < 99.0; threshold += 13.3)
712 {
713 KTRY_FOCUS_GADGET(QDoubleSpinBox, thresholdSpin);
714 thresholdSpin->setValue(threshold);
715 KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0, 0.0, 3.0);
716 KTRY_FOCUS_DETECT(1, 1, 99);
717 }
718 #endif
719 }
720
721 QTEST_KSTARS_MAIN(TestEkosFocus)
722
723 #endif // HAVE_INDI
724