Kstars

sequencejobstate.cpp
1 /* Ekos state machine for a single capture job sequence
2  SPDX-FileCopyrightText: Wolfgang Reissenberger <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "sequencejobstate.h"
8 
9 #include "Options.h"
10 #include "kstarsdata.h"
11 #include "indicom.h"
12 
13 namespace Ekos
14 {
15 SequenceJobState::SequenceJobState(const QSharedPointer<SequenceJobState::CaptureState> &sharedState)
16 {
17  m_CaptureState = sharedState;
18 }
19 
20 void SequenceJobState::setFrameType(CCDFrameType frameType)
21 {
22  // set the frame type
23  m_frameType = frameType;
24  // reset the preparation state
25  m_PreparationState = PREP_NONE;
26 }
27 
28 void SequenceJobState::prepareLightFrameCapture(bool enforceCCDTemp, bool enforceInitialGuidingDrift, bool isPreview)
29 {
30  // precondition: do not start while already being busy and conditions haven't changed
31  if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature && enforceInitialGuidingDrift == m_enforceInitialGuiding)
32  return;
33 
34  m_status = JOB_BUSY;
35  m_isPreview = isPreview;
36 
37  // Reset all prepare actions
38  setAllActionsReady();
39 
40  // disable batch mode for previews
41  emit setCCDBatchMode(!isPreview);
42 
43  // Filter changes are actually done in capture(), therefore prepareActions are always true
44  prepareActions[ACTION_FILTER] = true;
45  // nevertheless, emit an event so that Capture changes m_state
46  if (targetFilterID != -1 && targetFilterID != m_CaptureState->currentFilterID)
47  emit prepareState(CAPTURE_CHANGING_FILTER);
48 
49 
50  // Check if we need to update temperature (only skip if the value is initialized and within the limits)
51  prepareTemperatureCheck(enforceCCDTemp);
52 
53  // Check if we need to update rotator (only skip if the value is initialized and within the limits)
54  prepareRotatorCheck();
55 
56  // Check if we need to wait for guiding being initially below the target value
57  m_enforceInitialGuiding = enforceInitialGuidingDrift;
58  if (enforceInitialGuidingDrift && !isPreview)
59  prepareActions[ACTION_GUIDER_DRIFT] = false;
60 
61  // Hint: Filter changes are actually done in SequenceJob::capture();
62 
63  // preparation started
64  m_PreparationState = PREP_BUSY;
65  // check if the preparations are already completed
66  checkAllActionsReady();
67 }
68 
69 void SequenceJobState::prepareFlatFrameCapture(bool enforceCCDTemp, bool isPreview)
70 {
71  // precondition: do not start while already being busy and conditions haven't changed
72  if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
73  return;
74 
75  m_status = JOB_BUSY;
76  m_isPreview = isPreview;
77 
78  // Reset all prepare actions
79  setAllActionsReady();
80 
81  // disable batch mode for previews
82  emit setCCDBatchMode(!isPreview);
83 
84  // Filter changes are actually done in capture(), therefore prepareActions are always true
85  prepareActions[ACTION_FILTER] = true;
86  // nevertheless, emit an event so that Capture changes m_state
87  if (targetFilterID != -1 && targetFilterID != m_CaptureState->currentFilterID)
88  emit prepareState(CAPTURE_CHANGING_FILTER);
89 
90  // Check if we need to update temperature (only skip if the value is initialized and within the limits)
91  prepareTemperatureCheck(enforceCCDTemp);
92 
93  // preparation started
94  m_PreparationState = PREP_BUSY;
95  // check if the preparations are already completed
96  checkAllActionsReady();
97 }
98 
99 void SequenceJobState::prepareDarkFrameCapture(bool enforceCCDTemp, bool isPreview)
100 {
101  // precondition: do not start while already being busy and conditions haven't changed
102  if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
103  return;
104 
105  m_status = JOB_BUSY;
106  m_isPreview = isPreview;
107 
108  // Reset all prepare actions
109  setAllActionsReady();
110 
111  // disable batch mode for previews
112  emit setCCDBatchMode(!isPreview);
113 
114  // Filter changes are actually done in capture(), therefore prepareActions are always true
115  prepareActions[ACTION_FILTER] = true;
116 
117  // Check if we need to update temperature (only skip if the value is initialized and within the limits)
118  prepareTemperatureCheck(enforceCCDTemp);
119 
120  // preparation started
121  m_PreparationState = PREP_BUSY;
122  // check if the preparations are already completed
123  checkAllActionsReady();
124 }
125 
126 void SequenceJobState::prepareBiasFrameCapture(bool enforceCCDTemp, bool isPreview)
127 {
128  prepareDarkFrameCapture(enforceCCDTemp, isPreview);
129 }
130 
131 bool SequenceJobState::areActionsReady()
132 {
133  for (bool &ready : prepareActions.values())
134  {
135  if (ready == false)
136  return false;
137  }
138 
139  return true;
140 }
141 
142 void SequenceJobState::checkAllActionsReady()
143 {
144  // do nothing if preparation is not running
145  if (m_PreparationState != PREP_BUSY)
146  return;
147 
148  switch (m_frameType)
149  {
150  case FRAME_LIGHT:
151  if (areActionsReady())
152  {
153  // as last step ensure that the scope is uncovered
154  if (checkLightFrameScopeCoverOpen() != IPS_OK)
155  return;
156 
157  m_PreparationState = PREP_COMPLETED;
158  emit prepareComplete();
159  }
160  break;
161  case FRAME_FLAT:
162  if (!areActionsReady())
163  return;
164 
165  // 1. Check if the selected flats light source is ready
166  if (checkFlatsLightSourceReady() != IPS_OK)
167  return;
168 
169  // 2. Light source ready, now check if we need to perform mount prepark
170  if (checkPreMountParkReady() != IPS_OK)
171  return;
172 
173  // 3. Check if we need to perform dome prepark
174  if (checkPreDomeParkReady() != IPS_OK)
175  return;
176 
177  // 4. If we used AUTOFOCUS before for a specific frame (e.g. Lum)
178  // then the absolute focus position for Lum is recorded in the filter manager
179  // when we take flats again, we always go back to the same focus position as the light frames to ensure
180  // near identical focus for both frames.
181  if (checkFlatSyncFocus() != IPS_OK)
182  return;
183 
184  // all preparations ready, avoid doubled events
185  if (m_PreparationState == PREP_BUSY)
186  {
187  m_PreparationState = PREP_COMPLETED;
188  emit prepareComplete();
189  }
190  break;
191  case FRAME_DARK:
192  if (!areActionsReady())
193  return;
194 
195  // 1. check if the CCD has a shutter
196  if (checkHasShutter() != IPS_OK)
197  return;
198  switch (flatFieldSource)
199  {
200  // All these are manual when it comes to dark frames
201  case SOURCE_MANUAL:
202  case SOURCE_DAWN_DUSK:
203  // For cameras without a shutter, we need to ask the user to cover the telescope
204  // if the telescope is not already covered.
205  if (checkManualCover() != IPS_OK)
206  return;
207  break;
208  case SOURCE_FLATCAP:
209  case SOURCE_DARKCAP:
210  if (checkDustCapReady(FRAME_DARK) != IPS_OK)
211  return;
212  break;
213 
214  case SOURCE_WALL:
215  if (checkWallPositionReady(FRAME_DARK) != IPS_OK)
216  return;
217  break;
218  }
219 
220  // avoid doubled events
221  if (m_PreparationState == PREP_BUSY)
222  {
223  m_PreparationState = PREP_COMPLETED;
224  emit prepareComplete();
225  }
226  break;
227  case FRAME_BIAS:
228  if (areActionsReady())
229  {
230  // avoid doubled events
231  if (m_PreparationState == PREP_BUSY)
232  {
233  m_PreparationState = PREP_COMPLETED;
234  emit prepareComplete();
235  }
236  }
237  break;
238  default:
239  // all other cases not refactored yet, preparation immediately completed
240  emit prepareComplete();
241  break;
242  }
243 }
244 
245 void SequenceJobState::setAllActionsReady()
246 {
248 
249  while (it.hasNext())
250  {
251  it.next();
252  it.setValue(true);
253  }
254  // reset the initialization state
255  for (PrepareActions action :
256  {
257  ACTION_FILTER, ACTION_ROTATOR, ACTION_TEMPERATURE, ACTION_GUIDER_DRIFT
258  })
259  setInitialized(action, false);
260 }
261 
262 void SequenceJobState::prepareTemperatureCheck(bool enforceCCDTemp)
263 {
264  // turn on CCD temperature enforcing if required
265  m_enforceTemperature = enforceCCDTemp;
266 
267  if (m_enforceTemperature)
268  {
269  prepareActions[ACTION_TEMPERATURE] = false;
270  if (isInitialized(ACTION_TEMPERATURE))
271  {
272  // ignore the next value since after setting temperature the next received value will be
273  // exactly this value no matter what the CCD temperature
274  ignoreNextValue[ACTION_TEMPERATURE] = true;
275  // request setting temperature
276  emit setCCDTemperature(targetTemperature);
277  emit prepareState(CAPTURE_SETTING_TEMPERATURE);
278  }
279  // trigger setting current value first if not initialized
280  else
281  emit readCurrentState(CAPTURE_SETTING_TEMPERATURE);
282 
283  }
284 }
285 
286 void SequenceJobState::prepareRotatorCheck()
287 {
288  if (targetPositionAngle > Ekos::INVALID_VALUE)
289  {
290  if (isInitialized(ACTION_ROTATOR) == false)
291  {
292  prepareActions[ACTION_ROTATOR] = false;
293  // PA = RawAngle * Multiplier + Offset
294  double rawAngle = (targetPositionAngle - Options::pAOffset()) / Options::pAMultiplier();
295  emit prepareState(CAPTURE_SETTING_ROTATOR);
296  emit setRotatorAngle(&rawAngle);
297  }
298  // trigger setting current value first if not initialized
299  else
300  emit readCurrentState(CAPTURE_SETTING_ROTATOR);
301  }
302 }
303 
304 IPState SequenceJobState::checkFlatsLightSourceReady()
305 {
306  IPState result = IPS_OK;
307 
308  switch (flatFieldSource)
309  {
310  case SOURCE_MANUAL:
311  result = checkManualFlatsCoverReady();
312  break;
313  case SOURCE_DAWN_DUSK:
314  // Not implemented.
315  result = IPS_ALERT;
316  break;
317  case SOURCE_FLATCAP:
318  result = checkFlatCapReady();
319  break;
320  case SOURCE_WALL:
321  result = checkWallPositionReady(FRAME_FLAT);
322  break;
323  case SOURCE_DARKCAP:
324  result = checkDustCapReady(FRAME_FLAT);
325  break;
326  }
327  return result;
328 }
329 
330 IPState SequenceJobState::checkManualFlatsCoverReady()
331 {
332  // Manual mode we need to cover mount with evenly illuminated field.
333  if (m_CaptureState->telescopeCovered == false)
334  {
335  if (coverQueryState == CAL_CHECK_CONFIRMATION)
336  return IPS_BUSY;
337 
338  // request asking the user to cover the scope manually with a light source
339  emit askManualScopeLightCover(i18n("Cover telescope with an evenly illuminated light source."),
340  i18n("Flat Frame"));
341  coverQueryState = CAL_CHECK_CONFIRMATION;
342 
343  return IPS_BUSY;
344  }
345  return IPS_OK;
346 }
347 
348 IPState SequenceJobState::checkFlatCapReady()
349 {
350  // flat light is on
351  if (lightBoxLightStatus == CAP_LIGHT_ON)
352  return IPS_OK;
353  // turning on flat light running
354  if (lightBoxLightStatus == CAP_LIGHT_BUSY || dustCapStatus == CAP_PARKING)
355  return IPS_BUSY;
356  // error occured
357  if (dustCapStatus == CAP_ERROR)
358  return IPS_ALERT;
359 
360  if (m_CaptureState->hasLightBox == true && lightBoxLightStatus != CAP_LIGHT_ON)
361  {
362  lightBoxLightStatus = CAP_LIGHT_BUSY;
363  emit setLightBoxLight(true);
364  emit newLog(i18n("Turn light box light on..."));
365  return IPS_BUSY;
366  }
367 
368  if (m_CaptureState->hasDustCap == false)
369  {
370  if (m_CaptureState->hasLightBox == false)
371  emit newLog("Skipping flat/dark cap since it is not connected.");
372  return IPS_OK;
373  }
374 
375  // if using the dust cap, first park the dust cap
376  if (dustCapStatus != CAP_PARKED)
377  {
378  dustCapStatus = CAP_PARKING;
379  emit parkDustCap(true);
380  emit newLog(i18n("Parking dust cap..."));
381  return IPS_BUSY;
382  }
383  // nothing more to do
384  return IPS_OK;
385 }
386 
387 IPState SequenceJobState::checkDustCapReady(CCDFrameType frameType)
388 {
389  // turning on flat light running
390  if (lightBoxLightStatus == CAP_LIGHT_BUSY || dustCapStatus == CAP_PARKING || dustCapStatus == CAP_UNPARKING)
391  return IPS_BUSY;
392  // error occured
393  if (dustCapStatus == CAP_ERROR)
394  return IPS_ALERT;
395 
396  bool captureFlats = (frameType == FRAME_FLAT);
397 
398  LightStatus targetLightBoxStatus = (frameType == FRAME_FLAT) ? CAP_LIGHT_ON : CAP_LIGHT_OFF;
399 
400  if (m_CaptureState->hasLightBox == true && lightBoxLightStatus != targetLightBoxStatus)
401  {
402  lightBoxLightStatus = CAP_LIGHT_BUSY;
403  emit setLightBoxLight(captureFlats);
404  emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
405  return IPS_BUSY;
406  }
407 
408  if (m_CaptureState->hasDustCap == false)
409  {
410  // Only warn if we don't have already light box.
411  if (m_CaptureState->hasLightBox == false)
412  emit newLog("Skipping flat/dark cap since it is not connected.");
413  return IPS_OK;
414  }
415 
416  // for flats open the cap and close it otherwise
417  CapState targetCapState = captureFlats ? CAP_IDLE : CAP_PARKED;
418  // If cap is parked, unpark it since dark cap uses external light source.
419  if (dustCapStatus != targetCapState)
420  {
421  dustCapStatus = captureFlats ? CAP_UNPARKING : CAP_PARKING;
422  emit parkDustCap(!captureFlats);
423  emit newLog(captureFlats ? i18n("Unparking dust cap...") : i18n("Parking dust cap..."));
424  return IPS_BUSY;
425  }
426 
427  // nothing more to do
428  return IPS_OK;
429 }
430 
431 IPState SequenceJobState::checkWallPositionReady(CCDFrameType frametype)
432 {
433  if (m_CaptureState->hasTelescope)
434  {
435  if (wpScopeStatus < WP_SLEWING)
436  {
437  wallCoord.HorizontalToEquatorial(KStarsData::Instance()->lst(),
438  KStarsData::Instance()->geo()->lat());
439  wpScopeStatus = WP_SLEWING;
440  emit slewTelescope(wallCoord);
441  emit newLog(i18n("Mount slewing to wall position..."));
442  return IPS_BUSY;
443  }
444  // wait until actions completed
445  else if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
446  return IPS_BUSY;
447  // Check if slewing is complete
448  else if (wpScopeStatus == WP_SLEW_COMPLETED)
449  {
450  wpScopeStatus = WP_TRACKING_BUSY;
451  emit setScopeTracking(false);
452  emit newLog(i18n("Slew to wall position complete, stop tracking."));
453  return IPS_BUSY;
454  }
455  else if (wpScopeStatus == WP_TRACKING_OFF)
456  emit newLog(i18n("Slew to wall position complete, stop tracking."));
457 
458  // wall position reached, check if we have a light box to turn on for flats and off otherwise
459  bool captureFlats = (frametype == FRAME_FLAT);
460  LightStatus targetLightState = (captureFlats ? CAP_LIGHT_ON : CAP_LIGHT_OFF);
461 
462  if (m_CaptureState->hasLightBox == true)
463  {
464  if (lightBoxLightStatus != targetLightState)
465  {
466  lightBoxLightStatus = CAP_LIGHT_BUSY;
467  emit setLightBoxLight(captureFlats);
468  emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
469  return IPS_BUSY;
470  }
471  }
472  }
473  // everything ready
474  return IPS_OK;
475 }
476 
477 IPState SequenceJobState::checkPreMountParkReady()
478 {
479  if (preMountPark && m_CaptureState->hasTelescope && flatFieldSource != SOURCE_WALL)
480  {
481  if (scopeParkStatus == ISD::PARK_ERROR)
482  {
483  emit newLog(i18n("Parking mount failed, aborting..."));
484  emit abortCapture();
485  return IPS_ALERT;
486  }
487  else if (scopeParkStatus == ISD::PARK_PARKING)
488  return IPS_BUSY;
489  else if (scopeParkStatus != ISD::PARK_PARKED)
490  {
491  scopeParkStatus = ISD::PARK_PARKING;
492  emit setScopeParked(true);
493  emit newLog(i18n("Parking mount prior to calibration frames capture..."));
494  return IPS_BUSY;
495  }
496  }
497  // everything ready
498  return IPS_OK;
499 }
500 
501 IPState SequenceJobState::checkPreDomeParkReady()
502 {
503  if (preDomePark && m_CaptureState->hasDome)
504  {
505  if (domeStatus == ISD::Dome::DOME_ERROR)
506  {
507  emit newLog(i18n("Parking dome failed, aborting..."));
508  emit abortCapture();
509  return IPS_ALERT;
510  }
511  else if (domeStatus == ISD::Dome::DOME_PARKING)
512  return IPS_BUSY;
513  else if (domeStatus != ISD::Dome::DOME_PARKED)
514  {
515  domeStatus = ISD::Dome::DOME_PARKING;
516  emit setDomeParked(true);
517  emit newLog(i18n("Parking dome prior to calibration frames capture..."));
518  return IPS_BUSY;
519  }
520  }
521  // everything ready
522  return IPS_OK;
523 }
524 
525 IPState SequenceJobState::checkFlatSyncFocus()
526 {
527  // check already running?
528  if (flatSyncStatus == FS_BUSY)
529  {
530  QTimer::singleShot(1000, [&]
531  {
532  // wait for one second and repeat the request again
533  emit flatSyncFocus(targetFilterID);
534  });
535  return IPS_BUSY;
536  }
537 
538  if (m_frameType == FRAME_FLAT && autoFocusReady && Options::flatSyncFocus() &&
539  flatSyncStatus != FS_COMPLETED)
540  {
541  flatSyncStatus = FS_BUSY;
542  emit flatSyncFocus(targetFilterID);
543  return IPS_BUSY;
544  }
545  // everything ready
546  return IPS_OK;
547 }
548 
549 IPState SequenceJobState::checkHasShutter()
550 {
551  if (m_CaptureState->shutterStatus == SHUTTER_BUSY)
552  return IPS_BUSY;
553  if (m_CaptureState->shutterStatus != SHUTTER_UNKNOWN)
554  return IPS_OK;
555  // query the status
556  m_CaptureState->shutterStatus = SHUTTER_BUSY;
557  emit queryHasShutter();
558  return IPS_BUSY;
559 }
560 
561 IPState SequenceJobState::checkManualCover()
562 {
563  if (m_CaptureState->shutterStatus == SHUTTER_NO && m_CaptureState->telescopeCovered == false)
564  {
565  // Already asked for confirmation? Then wait.
566  if (coverQueryState == CAL_CHECK_CONFIRMATION)
567  return IPS_BUSY;
568 
569  // Otherwise, we ask user to confirm manually
570  coverQueryState = CAL_CHECK_CONFIRMATION;
571 
572  emit askManualScopeLightCover(i18n("Cover the telescope in order to take a dark exposure."),
573  i18n("Dark Exposure"));
574  return IPS_BUSY;
575  }
576  // everything ready
577  return IPS_OK;
578 }
579 
580 IPState SequenceJobState::checkLightFrameScopeCoverOpen()
581 {
582  switch (flatFieldSource)
583  {
584  // All these are considered MANUAL when it comes to light frames
585  case SOURCE_MANUAL:
586  case SOURCE_DAWN_DUSK:
587  case SOURCE_WALL:
588  // If telescopes were MANUALLY covered before
589  // we need to manually uncover them.
590  if (m_CaptureState->telescopeCovered)
591  {
592  // If we already asked for confirmation and waiting for it
593  // let us see if the confirmation is fulfilled
594  // otherwise we return.
595  if (coverQueryState == CAL_CHECK_CONFIRMATION)
596  return IPS_BUSY;
597 
598  // Otherwise, we ask user to confirm manually
599  coverQueryState = CAL_CHECK_CONFIRMATION;
600  emit askManualScopeLightOpen();
601 
602  return IPS_BUSY;
603  }
604  break;
605  case SOURCE_FLATCAP:
606  case SOURCE_DARKCAP:
607  // if no state update happened, wait.
608  if (lightBoxLightStatus == CAP_LIGHT_BUSY || dustCapStatus == CAP_UNPARKING)
609  return IPS_BUSY;
610 
611  // Account for light box only (no dust cap)
612  if (m_CaptureState->hasLightBox && lightBoxLightStatus != CAP_LIGHT_OFF)
613  {
614  lightBoxLightStatus = CAP_LIGHT_BUSY;
615  emit setLightBoxLight(false);
616  emit newLog(i18n("Turn light box light off..."));
617  return IPS_BUSY;
618  }
619 
620  if (m_CaptureState->hasDustCap == false)
621  {
622  emit newLog("Skipping flat/dark cap since it is not connected.");
623  return IPS_OK;
624  }
625 
626  // If cap is parked, we need to unpark it
627  if (dustCapStatus != CAP_IDLE)
628  {
629  dustCapStatus = CAP_UNPARKING;
630  emit parkDustCap(false);
631  emit newLog(i18n("Unparking dust cap..."));
632  return IPS_BUSY;
633  }
634  break;
635  }
636  // scope cover open (or no scope cover)
637  return IPS_OK;
638 }
639 
640 bool SequenceJobState::isInitialized(SequenceJobState::PrepareActions action)
641 {
642  return m_CaptureState.data()->isInitialized[action];
643 }
644 
645 void SequenceJobState::setInitialized(SequenceJobState::PrepareActions action, bool init)
646 {
647  m_CaptureState.data()->isInitialized[action] = init;
648 }
649 
650 void SequenceJobState::setCurrentFilterID(int value)
651 {
652  m_CaptureState->currentFilterID = value;
653  setInitialized(ACTION_FILTER, true);
654 
655  // TODO introduce settle time
656  if (m_CaptureState->currentFilterID == targetFilterID)
657  prepareActions[SequenceJobState::ACTION_FILTER] = true;
658 
659  checkAllActionsReady();
660 }
661 
662 void SequenceJobState::setCurrentCCDTemperature(double currentTemperature)
663 {
664  // skip if next value should be ignored
665  if (ignoreNextValue[ACTION_TEMPERATURE])
666  {
667  ignoreNextValue[ACTION_TEMPERATURE] = false;
668  return;
669  }
670 
671  if (isInitialized(ACTION_TEMPERATURE))
672  {
673  if (m_enforceTemperature == false
674  || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
675  prepareActions[SequenceJobState::ACTION_TEMPERATURE] = true;
676 
677  checkAllActionsReady();
678  }
679  else
680  {
681  setInitialized(ACTION_TEMPERATURE, true);
682  if (m_enforceTemperature == false
683  || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
684  {
685  prepareActions[SequenceJobState::ACTION_TEMPERATURE] = true;
686  checkAllActionsReady();
687  }
688  else
689  {
690  prepareTemperatureCheck(m_enforceTemperature);
691  }
692  }
693 }
694 
695 void SequenceJobState::setCurrentRotatorPositionAngle(double rotatorAngle, IPState state)
696 {
697  // position angle = rotatorAngle * paMul + paOffset
698  // -180 < position angle <= 180
699  double currentPositionAngle = range360(rotatorAngle * Options::pAMultiplier() + Options::pAOffset());
700  if (currentPositionAngle > 180)
701  currentPositionAngle -= 360.0;
702 
703  if (isInitialized(ACTION_ROTATOR))
704  {
705  // TODO introduce settle time
706  // TODO make sure rotator has fully stopped
707  if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
708  && state != IPS_BUSY)
709  prepareActions[SequenceJobState::ACTION_ROTATOR] = true;
710 
711  checkAllActionsReady();
712  }
713  else
714  {
715  setInitialized(ACTION_ROTATOR, true);
716  prepareRotatorCheck();
717  }
718 }
719 
720 void SequenceJobState::setCurrentGuiderDrift(double value)
721 {
722  setInitialized(ACTION_GUIDER_DRIFT, true);
723  if (value <= targetStartGuiderDrift)
724  prepareActions[ACTION_GUIDER_DRIFT] = true;
725 
726  checkAllActionsReady();
727 }
728 
729 void SequenceJobState::manualScopeLightCover(bool closed, bool success)
730 {
731  // covering confirmed
732  if (success == true)
733  {
734  m_CaptureState->telescopeCovered = closed;
735  coverQueryState = CAL_CHECK_TASK;
736  // re-run checks
737  checkAllActionsReady();
738  }
739  // cancelled
740  else
741  {
742  coverQueryState = CAL_CHECK_TASK;
743  // abort, no further checks
744  emit abortCapture();
745  }
746 }
747 
748 void SequenceJobState::lightBoxLight(bool on)
749 {
750  lightBoxLightStatus = on ? CAP_LIGHT_ON : CAP_LIGHT_OFF;
751  emit newLog(i18n(on ? "Light box on." : "Light box off."));
752  // re-run checks
753  checkAllActionsReady();
754 }
755 
756 void SequenceJobState::dustCapStatusChanged(ISD::DustCap::Status status)
757 {
758  switch (status)
759  {
760  case ISD::DustCap::CAP_ERROR:
761  dustCapStatus = CAP_ERROR;
762  emit abortCapture();
763  break;
764  case ISD::DustCap::CAP_PARKED:
765  dustCapStatus = CAP_PARKED;
766  emit newLog(i18n("Dust cap parked."));
767  break;
768  case ISD::DustCap::CAP_IDLE:
769  dustCapStatus = CAP_IDLE;
770  emit newLog(i18n("Dust cap unparked."));
771  break;
772  case ISD::DustCap::CAP_UNPARKING:
773  dustCapStatus = CAP_UNPARKING;
774  break;
775  case ISD::DustCap::CAP_PARKING:
776  dustCapStatus = CAP_PARKING;
777  break;
778  }
779 
780  // re-run checks
781  checkAllActionsReady();
782 }
783 
784 void SequenceJobState::scopeStatusChanged(ISD::Mount::Status status)
785 {
786  // handle wall position
787  switch (status)
788  {
789  case ISD::Mount::MOUNT_TRACKING:
790  if (wpScopeStatus == WP_SLEWING)
791  wpScopeStatus = WP_SLEW_COMPLETED;
792  break;
793  case ISD::Mount::MOUNT_IDLE:
794  if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
795  wpScopeStatus = WP_TRACKING_OFF;
796  break;
797  default:
798  // do nothing
799  break;
800  }
801  scopeStatus = status;
802  // re-run checks
803  checkAllActionsReady();
804 }
805 
806 void SequenceJobState::scopeParkStatusChanged(ISD::ParkStatus status)
807 {
808  scopeParkStatus = status;
809  // re-run checks
810  checkAllActionsReady();
811 }
812 
813 void SequenceJobState::domeStatusChanged(ISD::Dome::Status status)
814 {
815  domeStatus = status;
816  // re-run checks
817  checkAllActionsReady();
818 }
819 
820 void SequenceJobState::flatSyncFocusChanged(bool completed)
821 {
822  flatSyncStatus = (completed ? FS_COMPLETED : FS_BUSY);
823  // re-run checks
824  checkAllActionsReady();
825 }
826 
827 void SequenceJobState::hasShutter(bool present)
828 {
829  if (present == true)
830  m_CaptureState->shutterStatus = SHUTTER_YES;
831  else
832  m_CaptureState->shutterStatus = SHUTTER_NO;
833 
834  // re-run checks
835  checkAllActionsReady();
836 }
837 
838 void SequenceJobState::setEnforceInitialGuidingDrift(bool enforceInitialGuidingDrift)
839 {
840  m_enforceInitialGuiding = enforceInitialGuidingDrift;
841  // update the preparation action
842  prepareActions[ACTION_GUIDER_DRIFT] = !enforceInitialGuidingDrift || m_isPreview;
843  // re-run checks
844  checkAllActionsReady();
845 }
846 
847 SequenceJobState::PreparationState SequenceJobState::getPreparationState() const
848 {
849  return m_PreparationState;
850 }
851 } // namespace
Ekos is an advanced Astrophotography tool for Linux. It is based on a modular extensible framework to...
Definition: align.cpp:70
QCA_EXPORT void init()
@ CAPTURE_SETTING_ROTATOR
Definition: ekos.h:108
@ CAPTURE_SETTING_TEMPERATURE
Definition: ekos.h:107
QString i18n(const char *text, const TYPE &arg...)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
GeoCoordinates geo(const QVariant &location)
@ CAPTURE_CHANGING_FILTER
Definition: ekos.h:105
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Aug 11 2022 04:00:05 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.