1 /*
2     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "sequencejob.h"
8 
9 #include "kstars.h"
10 #include "kstarsdata.h"
11 #include "Options.h"
12 #include "indi/driverinfo.h"
13 #include "indi/clientmanager.h"
14 #include "ekos/scheduler/schedulerjob.h"
15 
16 #include <KNotifications/KNotification>
17 #include <ekos_capture_debug.h>
18 
19 #define MF_TIMER_TIMEOUT    90000
20 #define MF_RA_DIFF_LIMIT    4
21 
22 namespace Ekos
23 {
24 QString const &SequenceJob::ISOMarker("_ISO8601");
25 
SequenceJob()26 SequenceJob::SequenceJob()
27 {
28     statusStrings = QStringList() << i18n("Idle") << i18n("In Progress") << i18n("Error") << i18n("Aborted")
29                     << i18n("Complete");
30     currentTemperature = targetTemperature = Ekos::INVALID_VALUE;
31     currentGuiderDrift = targetStartGuiderDrift = Ekos::INVALID_VALUE;
32     targetRotation = currentRotation = Ekos::INVALID_VALUE;
33 
34     prepareActions[ACTION_FILTER] = true;
35     prepareActions[ACTION_TEMPERATURE] = true;
36     prepareActions[ACTION_ROTATOR] = true;
37     prepareActions[ACTION_GUIDER_DRIFT] = true;
38 }
39 
SequenceJob(XMLEle * root)40 SequenceJob::SequenceJob(XMLEle *root):
41     SequenceJob()
42 {
43     XMLEle *ep    = nullptr;
44     XMLEle *subEP = nullptr;
45 
46     // We expect all data read from the XML to be in the C locale - QLocale::c().
47     QLocale cLocale = QLocale::c();
48 
49     const QMap<QString, CCDFrameType> frameTypes =
50     {
51         { "Light", FRAME_LIGHT }, { "Dark", FRAME_DARK }, { "Bias", FRAME_BIAS }, { "Flat", FRAME_FLAT }
52     };
53 
54     frameType = FRAME_NONE;
55     exposure = 0;
56     /* Reset light frame presence flag before enumerating */
57     // JM 2018-09-14: If last sequence job is not LIGHT
58     // then scheduler job light frame is set to whatever last sequence job is
59     // so if it was non-LIGHT, this value is set to false which is wrong.
60     //if (nullptr != schedJob)
61     //    schedJob->setLightFramesRequired(false);
62 
63     for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
64     {
65         if (!strcmp(tagXMLEle(ep), "Exposure"))
66         {
67             exposure = atof(pcdataXMLEle(ep));
68         }
69         else if (!strcmp(tagXMLEle(ep), "Filter"))
70         {
71             filter = QString(pcdataXMLEle(ep));
72         }
73         else if (!strcmp(tagXMLEle(ep), "Type"))
74         {
75             /* Record frame type and mark presence of light frames for this sequence */
76             QString frameTypeStr = QString(pcdataXMLEle(ep));
77             if (frameTypes.contains(frameTypeStr))
78             {
79                 frameType = frameTypes[frameTypeStr];
80             }
81             //if (FRAME_LIGHT == frameType && nullptr != schedJob)
82             //schedJob->setLightFramesRequired(true);
83         }
84         else if (!strcmp(tagXMLEle(ep), "Prefix"))
85         {
86             subEP = findXMLEle(ep, "RawPrefix");
87             if (subEP)
88                 m_RawPrefix = QString(pcdataXMLEle(subEP));
89 
90             subEP = findXMLEle(ep, "FilterEnabled");
91             if (subEP)
92                 filterPrefixEnabled = !strcmp("1", pcdataXMLEle(subEP));
93 
94             subEP = findXMLEle(ep, "ExpEnabled");
95             if (subEP)
96                 expPrefixEnabled = (!strcmp("1", pcdataXMLEle(subEP)));
97 
98             subEP = findXMLEle(ep, "TimeStampEnabled");
99             if (subEP)
100                 timeStampPrefixEnabled = (!strcmp("1", pcdataXMLEle(subEP)));
101 
102         }
103         else if (!strcmp(tagXMLEle(ep), "Count"))
104         {
105             setCount(atoi(pcdataXMLEle(ep)));
106         }
107         else if (!strcmp(tagXMLEle(ep), "Delay"))
108         {
109             setDelay(atoi(pcdataXMLEle(ep)));
110         }
111         else if (!strcmp(tagXMLEle(ep), "FITSDirectory"))
112         {
113             setLocalDir(pcdataXMLEle(ep));
114         }
115         else if (!strcmp(tagXMLEle(ep), "RemoteDirectory"))
116         {
117             setRemoteDir(pcdataXMLEle(ep));
118         }
119         else if (!strcmp(tagXMLEle(ep), "UploadMode"))
120         {
121             setUploadMode(static_cast<ISD::CCD::UploadMode>(atoi(pcdataXMLEle(ep))));
122         }
123         else if (!strcmp(tagXMLEle(ep), "Calibration"))
124         {
125             subEP = findXMLEle(ep, "FlatSource");
126             if (subEP)
127             {
128                 XMLEle * typeEP = findXMLEle(subEP, "Type");
129                 if (typeEP)
130                 {
131                     if (!strcmp(pcdataXMLEle(typeEP), "Manual"))
132                         setFlatFieldSource(SOURCE_MANUAL);
133                     else if (!strcmp(pcdataXMLEle(typeEP), "FlatCap"))
134                         setFlatFieldSource(SOURCE_FLATCAP);
135                     else if (!strcmp(pcdataXMLEle(typeEP), "DarkCap"))
136                         setFlatFieldSource(SOURCE_DARKCAP);
137                     else if (!strcmp(pcdataXMLEle(typeEP), "Wall"))
138                     {
139                         XMLEle * azEP  = findXMLEle(subEP, "Az");
140                         XMLEle * altEP = findXMLEle(subEP, "Alt");
141 
142                         if (azEP && altEP)
143                         {
144                             setFlatFieldSource(SOURCE_WALL);
145                             SkyPoint wallCoord;
146                             wallCoord.setAz(dms::fromString(pcdataXMLEle(azEP), true));
147                             wallCoord.setAlt(dms::fromString(pcdataXMLEle(altEP), true));
148                             setWallCoord(wallCoord);
149                         }
150                     }
151                     else
152                         setFlatFieldSource(SOURCE_DAWN_DUSK);
153                 }
154             }
155 
156             subEP = findXMLEle(ep, "FlatDuration");
157             if (subEP)
158             {
159                 XMLEle * typeEP = findXMLEle(subEP, "Type");
160                 if (typeEP)
161                 {
162                     if (!strcmp(pcdataXMLEle(typeEP), "Manual"))
163                         setFlatFieldDuration(DURATION_MANUAL);
164                 }
165 
166                 XMLEle * aduEP = findXMLEle(subEP, "Value");
167                 if (aduEP)
168                 {
169                     setFlatFieldDuration(DURATION_ADU);
170                     setTargetADU(cLocale.toDouble(pcdataXMLEle(aduEP)));
171                 }
172 
173                 aduEP = findXMLEle(subEP, "Tolerance");
174                 if (aduEP)
175                 {
176                     setTargetADUTolerance(cLocale.toDouble(pcdataXMLEle(aduEP)));
177                 }
178             }
179 
180             subEP = findXMLEle(ep, "PreMountPark");
181             if (subEP)
182             {
183                 setPreMountPark(!strcmp(pcdataXMLEle(subEP), "True"));
184             }
185 
186             subEP = findXMLEle(ep, "PreDomePark");
187             if (subEP)
188             {
189                 setPreDomePark(!strcmp(pcdataXMLEle(subEP), "True"));
190             }
191         }
192     }
193 }
194 
resetStatus()195 void SequenceJob::resetStatus()
196 {
197     setStatus(JOB_IDLE);
198     setCompleted(0);
199     exposeLeft     = 0;
200     captureRetires = 0;
201     m_JobProgressIgnored = false;
202 }
203 
abort()204 void SequenceJob::abort()
205 {
206     setStatus(JOB_ABORTED);
207     if (activeChip->canAbort())
208         activeChip->abortExposure();
209     activeChip->setBatchMode(false);
210 }
211 
done()212 void SequenceJob::done()
213 {
214     setStatus(JOB_DONE);
215 }
216 
prepareCapture()217 void SequenceJob::prepareCapture()
218 {
219     status = JOB_BUSY;
220 
221     prepareReady = false;
222 
223     // Reset all prepare actions
224     setAllActionsReady();
225 
226     activeChip->setBatchMode(!preview);
227 
228     // Filter changes are actually done in capture();
229     prepareActions[ACTION_FILTER] = true;
230     if (targetFilter != -1 && activeFilter != nullptr &&
231             frameType == FRAME_LIGHT && targetFilter != currentFilter)
232         emit prepareState(CAPTURE_CHANGING_FILTER);
233 
234     // Check if we need to update temperature
235     if (enforceTemperature && fabs(targetTemperature - currentTemperature) > Options::maxTemperatureDiff())
236     {
237         prepareActions[ACTION_TEMPERATURE] = false;
238         emit prepareState(CAPTURE_SETTING_TEMPERATURE);
239         activeCCD->setTemperature(targetTemperature);
240     }
241 
242     // Check if we need to wait for the guider to settle.
243     if (!guiderDriftOK())
244     {
245         prepareActions[ACTION_GUIDER_DRIFT] = false;
246         emit prepareState(CAPTURE_GUIDER_DRIFT);
247     }
248 
249     // Check if we need to update rotator
250     if (targetRotation != Ekos::INVALID_VALUE
251             && fabs(currentRotation - targetRotation) * 60 > Options::astrometryRotatorThreshold())
252     {
253         // PA = RawAngle * Multiplier + Offset
254         double rawAngle = (targetRotation - Options::pAOffset()) / Options::pAMultiplier();
255         prepareActions[ACTION_ROTATOR] = false;
256         emit prepareState(CAPTURE_SETTING_ROTATOR);
257         activeRotator->runCommand(INDI_SET_ROTATOR_ANGLE, &rawAngle);
258     }
259 
260     if (prepareReady == false && areActionsReady())
261     {
262         prepareReady = true;
263         emit prepareComplete();
264     }
265 }
266 
setAllActionsReady()267 void SequenceJob::setAllActionsReady()
268 {
269     QMutableMapIterator<PrepareActions, bool> i(prepareActions);
270 
271     while (i.hasNext())
272     {
273         i.next();
274         i.setValue(true);
275     }
276 }
277 
setStatus(JOBStatus const in_status)278 void SequenceJob::setStatus(JOBStatus const in_status)
279 {
280     status = in_status;
281     if( !preview && nullptr != statusCell)
282         statusCell->setText(statusStrings[in_status]);
283 }
284 
setStatusCell(QTableWidgetItem * cell)285 void SequenceJob::setStatusCell(QTableWidgetItem* cell)
286 {
287     statusCell = cell;
288     if (nullptr != cell)
289     {
290         cell->setTextAlignment(Qt::AlignHCenter);
291         cell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
292         setStatus(getStatus());
293     }
294 }
295 
setCount(int in_count)296 void SequenceJob::setCount(int in_count)
297 {
298     count = in_count;
299     if( !preview && nullptr != countCell)
300         countCell->setText(QString("%L1/%L2").arg(completed).arg(in_count));
301 }
302 
setCompleted(int in_completed)303 void SequenceJob::setCompleted(int in_completed)
304 {
305     completed = in_completed;
306     if( !preview && nullptr != countCell)
307         countCell->setText(QString("%L1/%L2").arg(in_completed).arg(count));
308 }
309 
setCountCell(QTableWidgetItem * cell)310 void SequenceJob::setCountCell(QTableWidgetItem* cell)
311 {
312     countCell = cell;
313     if (nullptr != cell)
314     {
315         cell->setTextAlignment(Qt::AlignHCenter);
316         cell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
317         setCount(getCount());
318     }
319 }
320 
getJobProgressIgnored() const321 bool SequenceJob::getJobProgressIgnored() const
322 {
323     return m_JobProgressIgnored;
324 }
325 
setJobProgressIgnored(bool JobProgressIgnored)326 void SequenceJob::setJobProgressIgnored(bool JobProgressIgnored)
327 {
328     m_JobProgressIgnored = JobProgressIgnored;
329 }
330 
getDirectoryPostfix() const331 QString SequenceJob::getDirectoryPostfix() const
332 {
333     return directoryPostfix;
334 }
335 
setDirectoryPostfix(const QString & value)336 void SequenceJob::setDirectoryPostfix(const QString &value)
337 {
338     directoryPostfix = value;
339 }
340 
getCustomProperties() const341 QMap<QString, QMap<QString, double> > SequenceJob::getCustomProperties() const
342 {
343     return customProperties;
344 }
345 
setCustomProperties(const QMap<QString,QMap<QString,double>> & value)346 void SequenceJob::setCustomProperties(const QMap<QString, QMap<QString, double> > &value)
347 {
348     customProperties = value;
349 }
350 
areActionsReady()351 bool SequenceJob::areActionsReady()
352 {
353     for (bool &ready : prepareActions.values())
354     {
355         if (ready == false)
356             return false;
357     }
358 
359     return true;
360 }
361 
capture(bool autofocusReady)362 SequenceJob::CAPTUREResult SequenceJob::capture(bool autofocusReady)
363 {
364     activeChip->setBatchMode(!preview);
365 
366     activeCCD->setISOMode(timeStampPrefixEnabled);
367 
368     activeCCD->setSeqPrefix(fullPrefix);
369 
370     auto placeholderPath = Ekos::PlaceholderPath(localDirectory + "/sequence.esq");
371     placeholderPath.setGenerateFilenameSettings(*this);
372     activeCCD->setPlaceholderPath(placeholderPath);
373 
374     if (preview)
375     {
376         if (activeCCD->getUploadMode() != ISD::CCD::UPLOAD_CLIENT)
377             activeCCD->setUploadMode(ISD::CCD::UPLOAD_CLIENT);
378     }
379     else
380         activeCCD->setUploadMode(uploadMode);
381 
382     QMapIterator<QString, QMap<QString, double>> i(customProperties);
383     while (i.hasNext())
384     {
385         i.next();
386         INDI::Property *customProp = activeCCD->getProperty(i.key());
387         if (customProp)
388         {
389             QMap<QString, double> numbers = i.value();
390             QMapIterator<QString, double> j(numbers);
391             auto np = customProp->getNumber();
392             while (j.hasNext())
393             {
394                 j.next();
395                 auto oneNumber = np->findWidgetByName(j.key().toLatin1().data());
396                 if (oneNumber)
397                     oneNumber->setValue(j.value());
398             }
399 
400             activeCCD->getDriverInfo()->getClientManager()->sendNewNumber(np);
401         }
402     }
403 
404     if (activeChip->isBatchMode() && remoteDirectory.isEmpty() == false)
405         activeCCD->updateUploadSettings(remoteDirectory + directoryPostfix);
406 
407     if (isoIndex != -1)
408     {
409         if (isoIndex != activeChip->getISOIndex())
410             activeChip->setISOIndex(isoIndex);
411     }
412 
413     if (gain != -1)
414     {
415         activeCCD->setGain(gain);
416     }
417 
418     if (offset != -1)
419     {
420         activeCCD->setOffset(offset);
421     }
422 
423     if (targetFilter != -1 && activeFilter != nullptr)
424     {
425         if (targetFilter != currentFilter)
426         {
427             emit prepareState(CAPTURE_CHANGING_FILTER);
428 
429             FilterManager::FilterPolicy policy = FilterManager::ALL_POLICIES;
430             // Don't perform autofocus on preview or calibration frames or if Autofocus is not ready yet.
431             if (isPreview() || frameType != FRAME_LIGHT || autofocusReady == false)
432                 policy = static_cast<FilterManager::FilterPolicy>(policy & ~FilterManager::AUTOFOCUS_POLICY);
433 
434             filterManager->setFilterPosition(targetFilter, policy);
435             return CAPTURE_FILTER_BUSY;
436         }
437     }
438 
439     if (!guiderDriftOK())
440     {
441         emit prepareState(CAPTURE_GUIDER_DRIFT);
442         return CAPTURE_GUIDER_DRIFT_WAIT;
443     }
444 
445     // Only attempt to set ROI and Binning if CCD transfer format is FITS
446     if (activeCCD->getTransferFormat() == ISD::CCD::FORMAT_FITS)
447     {
448         int currentBinX = 1, currentBinY = 1;
449         activeChip->getBinning(&currentBinX, &currentBinY);
450 
451         // N.B. Always set binning _before_ setting frame because if the subframed image
452         // is problematic in 1x1 but works fine for 2x2, then it would fail it was set first
453         // So setting binning first always ensures this will work.
454         if (activeChip->canBin() && activeChip->setBinning(binX, binY) == false)
455         {
456             setStatus(JOB_ERROR);
457             return CAPTURE_BIN_ERROR;
458         }
459 
460         if ((w > 0 && h > 0) && activeChip->canSubframe() && activeChip->setFrame(x, y, w, h, currentBinX != binX) == false)
461         {
462             setStatus(JOB_ERROR);
463             return CAPTURE_FRAME_ERROR;
464         }
465     }
466 
467     activeChip->setFrameType(frameType);
468     activeChip->setCaptureMode(FITS_NORMAL);
469     activeChip->setCaptureFilter(FITS_NONE);
470 
471     // If filter is different that CCD, send the filter info
472     //    if (activeFilter && activeFilter != activeCCD)
473     //        activeCCD->setFilter(filter);
474 
475     //status = JOB_BUSY;
476     setStatus(getStatus());
477 
478     exposeLeft = exposure;
479 
480     activeChip->capture(exposure);
481 
482     return CAPTURE_OK;
483 }
484 
setTargetFilter(int pos,const QString & name)485 void SequenceJob::setTargetFilter(int pos, const QString &name)
486 {
487     targetFilter = pos;
488     filter       = name;
489 }
490 
setFrameType(CCDFrameType type)491 void SequenceJob::setFrameType(CCDFrameType type)
492 {
493     frameType = type;
494 }
495 
getExposeLeft() const496 double SequenceJob::getExposeLeft() const
497 {
498     return exposeLeft;
499 }
500 
setExposeLeft(double value)501 void SequenceJob::setExposeLeft(double value)
502 {
503     exposeLeft = value;
504 }
505 
setPrefixSettings(const QString & rawFilePrefix,bool filterEnabled,bool exposureEnabled,bool tsEnabled)506 void SequenceJob::setPrefixSettings(const QString &rawFilePrefix, bool filterEnabled, bool exposureEnabled,
507                                     bool tsEnabled)
508 {
509     m_RawPrefix            = rawFilePrefix;
510     filterPrefixEnabled    = filterEnabled;
511     expPrefixEnabled       = exposureEnabled;
512     timeStampPrefixEnabled = tsEnabled;
513 }
514 
getPrefixSettings(QString & rawFilePrefix,bool & filterEnabled,bool & exposureEnabled,bool & tsEnabled)515 void SequenceJob::getPrefixSettings(QString &rawFilePrefix, bool &filterEnabled, bool &exposureEnabled, bool &tsEnabled)
516 {
517     rawFilePrefix   = m_RawPrefix;
518     filterEnabled   = filterPrefixEnabled;
519     exposureEnabled = expPrefixEnabled;
520     tsEnabled       = timeStampPrefixEnabled;
521 }
522 
getCurrentTemperature() const523 double SequenceJob::getCurrentTemperature() const
524 {
525     return currentTemperature;
526 }
527 
setCurrentTemperature(double value)528 void SequenceJob::setCurrentTemperature(double value)
529 {
530     currentTemperature = value;
531 
532     if (enforceTemperature == false || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
533         prepareActions[ACTION_TEMPERATURE] = true;
534 
535     if (prepareReady == false && areActionsReady())
536     {
537         prepareReady = true;
538         emit prepareComplete();
539     }
540 }
541 
getTargetTemperature() const542 double SequenceJob::getTargetTemperature() const
543 {
544     return targetTemperature;
545 }
546 
setTargetTemperature(double value)547 void SequenceJob::setTargetTemperature(double value)
548 {
549     targetTemperature = value;
550 }
551 
setTargetStartGuiderDrift(double value)552 void SequenceJob::setTargetStartGuiderDrift(double value)
553 {
554     targetStartGuiderDrift = value;
555 }
556 
getTargetStartGuiderDrift() const557 double SequenceJob::getTargetStartGuiderDrift() const
558 {
559     return targetStartGuiderDrift;
560 }
561 
getTargetADU() const562 double SequenceJob::getTargetADU() const
563 {
564     return calibrationSettings.targetADU;
565 }
566 
setTargetADU(double value)567 void SequenceJob::setTargetADU(double value)
568 {
569     calibrationSettings.targetADU = value;
570 }
571 
getTargetADUTolerance() const572 double SequenceJob::getTargetADUTolerance() const
573 {
574     return calibrationSettings.targetADUTolerance;
575 }
576 
setTargetADUTolerance(double value)577 void SequenceJob::setTargetADUTolerance(double value)
578 {
579     calibrationSettings.targetADUTolerance = value;
580 }
581 
getCaptureRetires() const582 int SequenceJob::getCaptureRetires() const
583 {
584     return captureRetires;
585 }
586 
setCaptureRetires(int value)587 void SequenceJob::setCaptureRetires(int value)
588 {
589     captureRetires = value;
590 }
591 
getFlatFieldSource() const592 FlatFieldSource SequenceJob::getFlatFieldSource() const
593 {
594     return calibrationSettings.flatFieldSource;
595 }
596 
setFlatFieldSource(const FlatFieldSource & value)597 void SequenceJob::setFlatFieldSource(const FlatFieldSource &value)
598 {
599     calibrationSettings.flatFieldSource = value;
600 }
601 
getFlatFieldDuration() const602 FlatFieldDuration SequenceJob::getFlatFieldDuration() const
603 {
604     return calibrationSettings.flatFieldDuration;
605 }
606 
setFlatFieldDuration(const FlatFieldDuration & value)607 void SequenceJob::setFlatFieldDuration(const FlatFieldDuration &value)
608 {
609     calibrationSettings.flatFieldDuration = value;
610 }
611 
getWallCoord() const612 SkyPoint SequenceJob::getWallCoord() const
613 {
614     return calibrationSettings.wallCoord;
615 }
616 
setWallCoord(const SkyPoint & value)617 void SequenceJob::setWallCoord(const SkyPoint &value)
618 {
619     calibrationSettings.wallCoord = value;
620 }
621 
isPreMountPark() const622 bool SequenceJob::isPreMountPark() const
623 {
624     return calibrationSettings.preMountPark;
625 }
626 
setPreMountPark(bool value)627 void SequenceJob::setPreMountPark(bool value)
628 {
629     calibrationSettings.preMountPark = value;
630 }
631 
isPreDomePark() const632 bool SequenceJob::isPreDomePark() const
633 {
634     return calibrationSettings.preDomePark;
635 }
636 
setPreDomePark(bool value)637 void SequenceJob::setPreDomePark(bool value)
638 {
639     calibrationSettings.preDomePark = value;
640 }
641 
getEnforceTemperature() const642 bool SequenceJob::getEnforceTemperature() const
643 {
644     return enforceTemperature;
645 }
646 
setEnforceTemperature(bool value)647 void SequenceJob::setEnforceTemperature(bool value)
648 {
649     enforceTemperature = value;
650 }
651 
getEnforceStartGuiderDrift() const652 bool SequenceJob::getEnforceStartGuiderDrift() const
653 {
654     return enforceStartGuiderDrift;
655 }
656 
setEnforceStartGuiderDrift(bool value)657 void SequenceJob::setEnforceStartGuiderDrift(bool value)
658 {
659     enforceStartGuiderDrift = value;
660 }
661 
getUploadMode() const662 ISD::CCD::UploadMode SequenceJob::getUploadMode() const
663 {
664     return uploadMode;
665 }
666 
setUploadMode(const ISD::CCD::UploadMode & value)667 void SequenceJob::setUploadMode(const ISD::CCD::UploadMode &value)
668 {
669     uploadMode = value;
670 }
671 
getRemoteDir() const672 QString SequenceJob::getRemoteDir() const
673 {
674     return remoteDirectory;
675 }
676 
setRemoteDir(const QString & value)677 void SequenceJob::setRemoteDir(const QString &value)
678 {
679     remoteDirectory = value;
680     if (remoteDirectory.endsWith('/'))
681         remoteDirectory.chop(1);
682 }
683 
getTransforFormat() const684 ISD::CCD::TransferFormat SequenceJob::getTransforFormat() const
685 {
686     return transforFormat;
687 }
688 
setTransforFormat(const ISD::CCD::TransferFormat & value)689 void SequenceJob::setTransforFormat(const ISD::CCD::TransferFormat &value)
690 {
691     transforFormat = value;
692 }
693 
getGain() const694 double SequenceJob::getGain() const
695 {
696     return gain;
697 }
698 
setGain(double value)699 void SequenceJob::setGain(double value)
700 {
701     gain = value;
702 }
703 
getOffset() const704 double SequenceJob::getOffset() const
705 {
706     return offset;
707 }
708 
setOffset(double value)709 void SequenceJob::setOffset(double value)
710 {
711     offset = value;
712 }
713 
getTargetRotation() const714 double SequenceJob::getTargetRotation() const
715 {
716     return targetRotation;
717 }
718 
setTargetRotation(double value)719 void SequenceJob::setTargetRotation(double value)
720 {
721     targetRotation = value;
722 }
723 
getISOIndex() const724 int SequenceJob::getISOIndex() const
725 {
726     return isoIndex;
727 }
728 
setISOIndex(int value)729 void SequenceJob::setISOIndex(int value)
730 {
731     isoIndex = value;
732 }
733 
getCurrentFilter() const734 int SequenceJob::getCurrentFilter() const
735 {
736     return currentFilter;
737 }
738 
setCurrentFilter(int value)739 void SequenceJob::setCurrentFilter(int value)
740 {
741     currentFilter = value;
742 
743     if (currentFilter == targetFilter)
744         prepareActions[ACTION_FILTER] = true;
745 
746     if (prepareReady == false && areActionsReady())
747     {
748         prepareReady = true;
749         emit prepareComplete();
750     }
751 }
752 
setCurrentRotation(double value)753 void SequenceJob::setCurrentRotation(double value)
754 {
755     currentRotation = value;
756 
757     if (fabs(currentRotation - targetRotation) * 60 <= Options::astrometryRotatorThreshold())
758         prepareActions[ACTION_ROTATOR] = true;
759 
760     if (prepareReady == false && areActionsReady())
761     {
762         prepareReady = true;
763         emit prepareComplete();
764     }
765 
766 }
767 
getCurrentGuiderDrift() const768 double SequenceJob::getCurrentGuiderDrift() const
769 {
770     return currentGuiderDrift;
771 }
772 
resetCurrentGuiderDrift()773 void SequenceJob::resetCurrentGuiderDrift()
774 {
775     setCurrentGuiderDrift(1e8);
776 }
777 
guiderDriftOK() const778 bool SequenceJob::guiderDriftOK() const
779 {
780     return (!guiderActive ||
781             !enforceStartGuiderDrift ||
782             frameType != FRAME_LIGHT ||
783             currentGuiderDrift <= targetStartGuiderDrift);
784 }
785 
setCurrentGuiderDrift(double value)786 void SequenceJob::setCurrentGuiderDrift(double value)
787 {
788     currentGuiderDrift = value;
789     prepareActions[ACTION_GUIDER_DRIFT] = guiderDriftOK();
790 
791     if (prepareReady == false && areActionsReady())
792     {
793         prepareReady = true;
794         emit prepareComplete();
795     }
796 }
797 
798 }
799