Kstars

filtermanager.cpp
1/*
2 SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "buildfilteroffsets.h"
8#include <kstars_debug.h>
9
10#include "indi_debug.h"
11#include "kstarsdata.h"
12#include "kstars.h"
13#include "ekos/focus/focus.h"
14#include "ekos/manager.h"
15#include "Options.h"
16#include "auxiliary/kspaths.h"
17#include "auxiliary/ksmessagebox.h"
18#include "ekos/auxiliary/tabledelegate.h"
19
20#include <QTimer>
21#include <QSqlTableModel>
22#include <QSqlDatabase>
23#include <QSqlRecord>
24
25#include <basedevice.h>
26
27#include <algorithm>
28
29namespace Ekos
30{
31
32FilterManager::FilterManager(QWidget *parent) : QDialog(parent)
33{
34#ifdef Q_OS_OSX
35 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
36#endif
37
38 setupUi(this);
39
40 connect(buttonBox, SIGNAL(accepted()), this, SLOT(close()));
41 connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
42
43 connect(buildOffsetsButton, &QPushButton::clicked, this, &FilterManager::buildFilterOffsets);
44
45 kcfg_FlatSyncFocus->setChecked(Options::flatSyncFocus());
47 {
48 Options::setFlatSyncFocus(kcfg_FlatSyncFocus->isChecked());
49 });
50
51 createFilterModel();
52
53 // No Edit delegate
54 noEditDelegate = new NotEditableDelegate(m_FilterView);
55 m_FilterView->setItemDelegateForColumn(FM_LABEL, noEditDelegate);
56
57 // Exposure delegate
58 exposureDelegate = new DoubleDelegate(m_FilterView, 0.001, 3600, 1);
59 m_FilterView->setItemDelegateForColumn(FM_EXPOSURE, exposureDelegate);
60
61 // Offset delegate
62 offsetDelegate = new IntegerDelegate(m_FilterView, -10000, 10000, 1);
63 m_FilterView->setItemDelegateForColumn(FM_OFFSET, offsetDelegate);
64
65 // Auto Focus delegate
66 useAutoFocusDelegate = new ToggleDelegate(m_FilterView);
67 m_FilterView->setItemDelegateForColumn(FM_AUTO_FOCUS, useAutoFocusDelegate);
68
69 // Set Delegates
70 lockDelegate = new ComboDelegate(m_FilterView);
71 m_FilterView->setItemDelegateForColumn(FM_LOCK_FILTER, lockDelegate);
72 lockDelegate->setValues(getLockDelegates());
73
74 // Last AF solution delegate. Set by Autofocus but make editable in case bad data
75 // corrections need to be made
76 lastAFSolutionDelegate = new IntegerDelegate(m_FilterView, 0, 1000000, 1);
77 m_FilterView->setItemDelegateForColumn(FM_LAST_AF_SOLUTION, lastAFSolutionDelegate);
78
79 // Last AF solution temperature delegate
80 lastAFTempDelegate = new DoubleDelegate(m_FilterView, -60.0, 60.0, 1.0);
81 m_FilterView->setItemDelegateForColumn(FM_LAST_AF_TEMP, lastAFTempDelegate);
82
83 // Last AF solution altitude delegate
84 lastAFAltDelegate = new DoubleDelegate(m_FilterView, 0.0, 90.0, 1.0);
85 m_FilterView->setItemDelegateForColumn(FM_LAST_AF_ALT, lastAFAltDelegate);
86
87 // Ticks / °C delegate
88 ticksPerTempDelegate = new DoubleDelegate(m_FilterView, -10000.0, 10000.0, 1.0);
89 m_FilterView->setItemDelegateForColumn(FM_TICKS_PER_TEMP, ticksPerTempDelegate);
90
91 // Ticks / °Altitude delegate
92 ticksPerAltDelegate = new DoubleDelegate(m_FilterView, -10000.0, 10000.0, 1.0);
93 m_FilterView->setItemDelegateForColumn(FM_TICKS_PER_ALT, ticksPerAltDelegate);
94
95 // Wavelength delegate
96 wavelengthDelegate = new IntegerDelegate(m_FilterView, 200, 1000, 50);
97 m_FilterView->setItemDelegateForColumn(FM_WAVELENGTH, wavelengthDelegate);
98}
99
100void FilterManager::createFilterModel()
101{
102 QSqlDatabase userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
103
104 m_FilterModel = new QSqlTableModel(this, userdb);
105 m_FilterView->setModel(m_FilterModel);
106 m_FilterModel->setTable("filter");
107 m_FilterModel->setEditStrategy(QSqlTableModel::OnFieldChange);
108
109 m_FilterModel->setHeaderData(FM_LABEL, Qt::Horizontal, i18n("Filter"));
110
111 m_FilterModel->setHeaderData(FM_EXPOSURE, Qt::Horizontal, i18n("Filter exposure time during focus"), Qt::ToolTipRole);
112 m_FilterModel->setHeaderData(FM_EXPOSURE, Qt::Horizontal, i18n("Exposure"));
113
114 m_FilterModel->setHeaderData(FM_OFFSET, Qt::Horizontal, i18n("Relative offset in steps"), Qt::ToolTipRole);
115 m_FilterModel->setHeaderData(FM_OFFSET, Qt::Horizontal, i18n("Offset"));
116
117 m_FilterModel->setHeaderData(FM_AUTO_FOCUS, Qt::Horizontal, i18n("Start Auto Focus when filter is activated"),
119 m_FilterModel->setHeaderData(FM_AUTO_FOCUS, Qt::Horizontal, i18n("Auto Focus"));
120
121 m_FilterModel->setHeaderData(FM_LOCK_FILTER, Qt::Horizontal, i18n("Lock specific filter when running Auto Focus"),
123 m_FilterModel->setHeaderData(FM_LOCK_FILTER, Qt::Horizontal, i18n("Lock Filter"));
124
125 m_FilterModel->setHeaderData(FM_LAST_AF_SOLUTION, Qt::Horizontal,
126 i18n("Last AF solution. Updated automatically by the autofocus process."), Qt::ToolTipRole);
127 m_FilterModel->setHeaderData(FM_LAST_AF_SOLUTION, Qt::Horizontal, i18n("Last AF Solution"));
128
129 m_FilterModel->setHeaderData(FM_LAST_AF_TEMP, Qt::Horizontal,
130 i18n("The temperature of the last AF solution. Updated automatically by the autofocus process."), Qt::ToolTipRole);
131 m_FilterModel->setHeaderData(FM_LAST_AF_TEMP, Qt::Horizontal, i18n("Last AF Temp (°C)"));
132
133 m_FilterModel->setHeaderData(FM_LAST_AF_ALT, Qt::Horizontal,
134 i18n("The altitude of the last AF solution. Updated automatically by the autofocus process."), Qt::ToolTipRole);
135 m_FilterModel->setHeaderData(FM_LAST_AF_ALT, Qt::Horizontal, i18n("Last AF Alt (°Alt)"));
136
137 m_FilterModel->setHeaderData(FM_TICKS_PER_TEMP, Qt::Horizontal,
138 i18n("The number of ticks per °C increase in temperature. +ve for outward focuser movement"), Qt::ToolTipRole);
139 m_FilterModel->setHeaderData(FM_TICKS_PER_TEMP, Qt::Horizontal, i18n("Ticks / °C"));
140
141 m_FilterModel->setHeaderData(FM_TICKS_PER_ALT, Qt::Horizontal,
142 i18n("The number of ticks per degree increase in altitude. +ve for outward focuser movement"), Qt::ToolTipRole);
143 m_FilterModel->setHeaderData(FM_TICKS_PER_ALT, Qt::Horizontal, i18n("Ticks / °Alt"));
144
145 m_FilterModel->setHeaderData(FM_WAVELENGTH, Qt::Horizontal, i18n("Mid-point wavelength of filter in nm"), Qt::ToolTipRole);
146 m_FilterModel->setHeaderData(FM_WAVELENGTH, Qt::Horizontal, i18n("Wavelength"));
147
148 connect(m_FilterModel, &QSqlTableModel::dataChanged, this, &FilterManager::updated);
149
150 connect(m_FilterModel, &QSqlTableModel::dataChanged, this, [this](const QModelIndex & topLeft, const QModelIndex &,
151 const QVector<int> &)
152 {
153 reloadFilters();
154 if (topLeft.column() == FM_EXPOSURE)
155 emit exposureChanged(m_FilterModel->data(topLeft).toDouble());
156 else if (topLeft.column() == FM_LOCK_FILTER)
157 {
158 // Don't allow the lock filter to be set to the current filter - circular dependancy
159 // Don't allow the lock to be set if this filter is a lock for another filter - nested dependancy
160 QString lock = m_FilterModel->data(m_FilterModel->index(topLeft.row(), topLeft.column())).toString();
161 QString filter = m_FilterModel->data(m_FilterModel->index(topLeft.row(), FM_LABEL)).toString();
162 bool alreadyALock = false;
163 for (int i = 0; i < m_ActiveFilters.count(); i++)
164 {
165 if (m_ActiveFilters[i]->lockedFilter() == filter)
166 {
167 alreadyALock = true;
168 break;
169 }
170 }
171 if (alreadyALock || (lock == filter))
172 {
173 m_FilterModel->setData(m_FilterModel->index(topLeft.row(), topLeft.column()), NULL_FILTER);
174 }
175 // Update the acceptable values in the lockDelegate
176 lockDelegate->setValues(getLockDelegates());
177 }
178 else if (topLeft.column() == FM_TICKS_PER_TEMP)
179 emit ticksPerTempChanged();
180 else if (topLeft.column() == FM_TICKS_PER_ALT)
181 emit ticksPerAltChanged();
182 else if (topLeft.column() == FM_WAVELENGTH)
183 emit wavelengthChanged();
184 });
185}
186
187void FilterManager::refreshFilterModel()
188{
189 if (m_FilterWheel == nullptr || m_currentFilterLabels.empty())
190 return;
191
192 // In case filter model was cleared due to a device disconnect
193 if (m_FilterModel == nullptr)
194 createFilterModel();
195
196 QString vendor(m_FilterWheel->getDeviceName());
197 m_FilterModel->setFilter(QString("vendor='%1'").arg(vendor));
198 m_FilterModel->select();
199
200 m_FilterView->hideColumn(0);
201 m_FilterView->hideColumn(1);
202 m_FilterView->hideColumn(2);
203 m_FilterView->hideColumn(3);
204
205 // If we have an existing table but it doesn't match the number of current filters
206 // then we remove it.
207 if (m_FilterModel->rowCount() > 0 && m_FilterModel->rowCount() != m_currentFilterLabels.count())
208 {
209 for (int i = 0; i < m_FilterModel->rowCount(); i++)
210 m_FilterModel->removeRow(i);
211
212 m_FilterModel->select();
213 }
214
215 // If it is first time, let's populate data
216 if (m_FilterModel->rowCount() == 0)
217 {
218 filterProperties *fp = new filterProperties(vendor, "", "", "");
219 for (QString &filter : m_currentFilterLabels)
220 {
221 fp->color = filter;
222 KStarsData::Instance()->userdb()->AddFilter(fp);
223 }
224
225 m_FilterModel->select();
226 }
227 // Make sure all the filter colors match DB. If not update model to sync with INDI filter values
228 else
229 {
230 for (int i = 0; i < m_FilterModel->rowCount(); ++i)
231 {
232 QModelIndex index = m_FilterModel->index(i, FM_LABEL);
233 if (m_FilterModel->data(index).toString() != m_currentFilterLabels[i])
234 {
235 m_FilterModel->setData(index, m_currentFilterLabels[i]);
236 }
237 }
238 }
239
240 lockDelegate->setValues(getLockDelegates());
241
242 reloadFilters();
243 resizeDialog();
244}
245
246void FilterManager::resizeDialog()
247{
248 // Resize the columns to the data and then the dialog to the rows and columns
249 m_FilterView->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
250 int width = m_FilterView->horizontalHeader()->length() + 50;
251 int height = label->height() + m_FilterView->verticalHeader()->length() + label_2->height() + buttonBox->height() + 100;
252 this->resize(width, height);
253}
254// This function processes the list of active filters and returns a list of filters that could be lock filters
255// i.e. that don't themselves have locks.
256QStringList FilterManager::getLockDelegates()
257{
259
260 for (int i = 0; i < m_ActiveFilters.count(); i++)
261 {
262 if (m_ActiveFilters[i]->lockedFilter() == NULL_FILTER)
263 lockDelegates.append(m_ActiveFilters[i]->color());
264 }
265 return lockDelegates;
266}
267
268void FilterManager::reloadFilters()
269{
270 qDeleteAll(m_ActiveFilters);
271 currentFilter = nullptr;
272 targetFilter = nullptr;
273 m_ActiveFilters.clear();
274 operationQueue.clear();
275
276 filterProperties *fp = new filterProperties("", "", "", "");
277
278 for (int i = 0; i < m_FilterModel->rowCount(); ++i)
279 {
280 QSqlRecord record = m_FilterModel->record(i);
281 QString id = record.value("id").toString();
282
283 fp->vendor = record.value("Vendor").toString();
284 fp->model = record.value("Model").toString();
285 fp->type = record.value("Type").toString();
286 fp->color = record.value("Color").toString();
287 fp->exposure = record.value("Exposure").toDouble();
288 fp->offset = record.value("Offset").toInt();
289 fp->lockedFilter = record.value("LockedFilter").toString();
290 fp->useAutoFocus = record.value("UseAutoFocus").toInt() == 1;
291 fp->absFocusPos = record.value("AbsoluteFocusPosition").toInt();
292 fp->focusTemperature = record.value("FocusTemperature").toDouble();
293 fp->focusAltitude = record.value("FocusAltitude").toDouble();
294 fp->focusTicksPerTemp = record.value("FocusTicksPerTemp").toDouble();
295 fp->focusTicksPerAlt = record.value("FocusTicksPerAlt").toDouble();
296 fp->wavelength = record.value("Wavelength").toInt();
297 OAL::Filter *o = new OAL::Filter(id, fp);
298 m_ActiveFilters.append(o);
299 }
300}
301
302void FilterManager::setFilterWheel(ISD::FilterWheel *filter)
303{
304 // Return if same device and we already initialized the properties.
305 if (m_FilterWheel == filter && m_FilterNameProperty && m_FilterPositionProperty)
306 return;
307 else if (m_FilterWheel)
308 m_FilterWheel->disconnect(this);
309
310 m_FilterWheel = filter;
311
312 m_FilterNameProperty = nullptr;
313 m_FilterPositionProperty = nullptr;
314 m_FilterConfirmSet = nullptr;
315
316 if (!m_FilterWheel)
317 return;
318
319 connect(m_FilterWheel, &ISD::ConcreteDevice::propertyUpdated, this, &FilterManager::updateProperty);
320 connect(m_FilterWheel, &ISD::ConcreteDevice::Disconnected, this, &FilterManager::processDisconnect);
321
322 refreshFilterProperties();
323}
324
325void FilterManager::refreshFilterProperties()
326{
327 if (m_FilterNameProperty && m_FilterPositionProperty)
328 {
329 if (m_FilterConfirmSet == nullptr)
330 m_FilterConfirmSet = m_FilterWheel->getSwitch("CONFIRM_FILTER_SET");
331
332 // All filters are synced up?
333 if (m_currentFilterLabels.count() == m_FilterNameProperty->ntp)
334 return;
335 }
336
337 filterNameLabel->setText(m_FilterWheel->getDeviceName());
338
339 m_currentFilterLabels.clear();
340
341 m_FilterNameProperty = m_FilterWheel->getText("FILTER_NAME");
342 m_FilterPositionProperty = m_FilterWheel->getNumber("FILTER_SLOT");
343 m_FilterConfirmSet = m_FilterWheel->getSwitch("CONFIRM_FILTER_SET");
344
345 refreshFilterLabels();
346 refreshFilterPosition();
347
348 if (m_currentFilterPosition >= 1 && m_currentFilterPosition <= m_ActiveFilters.count())
349 lastFilterOffset = m_ActiveFilters[m_currentFilterPosition - 1]->offset();
350}
351
352QStringList FilterManager::getFilterLabels(bool forceRefresh)
353{
354 if ((!m_currentFilterLabels.empty() && forceRefresh == false) || !m_FilterNameProperty || !m_FilterPositionProperty)
355 return m_currentFilterLabels;
356
357 QStringList filterList;
358
359 for (int i = 0; i < m_FilterPositionProperty->np[0].max; i++)
360 {
361 if (m_FilterNameProperty != nullptr && (i < m_FilterNameProperty->ntp))
362 filterList.append(m_FilterNameProperty->tp[i].text);
363 }
364
365 return filterList;
366}
367
368int FilterManager::getFilterPosition(bool forceRefresh)
369{
370 if (forceRefresh == false || m_FilterPositionProperty == nullptr)
371 return m_currentFilterPosition;
372
373 return static_cast<int>(m_FilterPositionProperty->np[0].value);
374}
375
376void FilterManager::refreshFilterLabels()
377{
378 QList filters = getFilterLabels(true);
379
380 if (filters != m_currentFilterLabels)
381 {
382 m_currentFilterLabels = filters;
383 refreshFilterModel();
384
385 emit labelsChanged(filters);
386
387 // refresh position after filter changes
388 refreshFilterPosition();
389 }
390}
391
392void FilterManager::refreshFilterPosition()
393{
394
395 int pos = getFilterPosition(true);
396 if (pos != m_currentFilterPosition)
397 {
398 m_currentFilterPosition = pos;
399 emit positionChanged(pos);
400 }
401}
402
403bool FilterManager::setFilterPosition(uint8_t position, FilterPolicy policy)
404{
405 // Position 1 to Max
406 if (position > m_ActiveFilters.count())
407 return false;
408
409 m_Policy = policy;
410 currentFilter = m_ActiveFilters[m_currentFilterPosition - 1];
411 targetFilter = m_ActiveFilters[position - 1];
412
413 if (currentFilter == targetFilter)
414 {
415 emit ready();
416 return true;
417 }
418
419 buildOperationQueue(FILTER_CHANGE);
420
421 executeOperationQueue();
422
423 return true;
424}
425
426
427void FilterManager::updateProperty(INDI::Property prop)
428{
429 if (m_FilterWheel == nullptr || m_FilterWheel->getDeviceName() != prop.getDeviceName())
430 return;
431
432 if (prop.isNameMatch("FILTER_NAME"))
433 {
434 auto tvp = prop.getText();
435 m_FilterNameProperty = tvp;
436
437 refreshFilterLabels();
438 }
439 else if (prop.isNameMatch("FILTER_SLOT"))
440 {
441 auto nvp = prop.getNumber();
442 if (nvp->s != IPS_OK)
443 return;
444
445 m_FilterPositionProperty = nvp;
446 refreshFilterPosition();
447
448 if (state == FILTER_CHANGE)
449 executeOperationQueue();
450 // If filter is changed externally, record its current offset as the starting offset.
451 else if (state == FILTER_IDLE && m_ActiveFilters.count() >= m_currentFilterPosition)
452 lastFilterOffset = m_ActiveFilters[m_currentFilterPosition - 1]->offset();
453 }
454}
455
456
457void FilterManager::processDisconnect()
458{
459 m_currentFilterLabels.clear();
460 m_currentFilterPosition = -1;
461 m_FilterNameProperty = nullptr;
462 m_FilterPositionProperty = nullptr;
463}
464
465void FilterManager::buildOperationQueue(FilterState operation)
466{
467 operationQueue.clear();
468 m_useTargetFilter = false;
469
470 switch (operation)
471 {
472 case FILTER_CHANGE:
473 {
474 if ( (m_Policy & CHANGE_POLICY) && targetFilter != currentFilter)
475 m_useTargetFilter = true;
476
477 if (m_useTargetFilter)
478 {
479 operationQueue.enqueue(FILTER_CHANGE);
480 if (m_FocusReady && (m_Policy & OFFSET_POLICY))
481 operationQueue.enqueue(FILTER_OFFSET);
482 else
483 {
484 // Keep track of filter and offset either here or after the offset has been processed
485 lastFilterOffset = targetFilter->offset();
486 currentFilter = targetFilter;
487 }
488
489 }
490
491 if (m_FocusReady && (m_Policy & AUTOFOCUS_POLICY) && targetFilter->useAutoFocus())
492 operationQueue.enqueue(FILTER_AUTOFOCUS);
493 }
494 break;
495
496 default:
497 break;
498 }
499}
500
501bool FilterManager::executeOperationQueue()
502{
503 if (operationQueue.isEmpty())
504 {
505 state = FILTER_IDLE;
506 emit newStatus(state);
507 emit ready();
508 return false;
509 }
510
511 FilterState nextOperation = operationQueue.dequeue();
512
513 bool actionRequired = true;
514
515 switch (nextOperation)
516 {
517 case FILTER_CHANGE:
518 {
519 if (m_ConfirmationPending)
520 return true;
521
522 state = FILTER_CHANGE;
523 if (m_useTargetFilter)
524 targetFilterPosition = m_ActiveFilters.indexOf(targetFilter) + 1;
525 m_FilterWheel->setPosition(targetFilterPosition);
526
527 emit newStatus(state);
528
529 if (m_FilterConfirmSet)
530 {
531 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this]()
532 {
533 KSMessageBox::Instance()->disconnect(this);
534 m_ConfirmationPending = false;
535 m_FilterWheel->confirmFilter();
536 });
537 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
538 {
539 KSMessageBox::Instance()->disconnect(this);
540 m_ConfirmationPending = false;
541 });
542
543 m_ConfirmationPending = true;
544
545 KSMessageBox::Instance()->questionYesNo(i18n("Set filter to %1. Is filter set?", targetFilter->color()),
546 i18n("Confirm Filter"));
547 }
548 }
549 break;
550
551 case FILTER_OFFSET:
552 {
553 state = FILTER_OFFSET;
554 if (m_useTargetFilter)
555 {
556 targetFilterOffset = targetFilter->offset() - lastFilterOffset;
557 lastFilterOffset = targetFilter->offset();
558 currentFilter = targetFilter;
559 m_useTargetFilter = false;
560 }
561 if (targetFilterOffset == 0)
562 actionRequired = false;
563 else
564 {
565 emit newFocusOffset(targetFilterOffset, false);
566 emit newStatus(state);
567 }
568 }
569 break;
570
571 case FILTER_AUTOFOCUS:
572 state = FILTER_AUTOFOCUS;
573 qCDebug(KSTARS) << "FilterManager.cpp is triggering autofocus.";
574 emit newStatus(state);
575 emit runAutoFocus(AutofocusReason::FOCUS_FILTER, "");
576 break;
577
578 default:
579 break;
580 }
581
582 // If an additional action is required, return return and continue later
583 if (actionRequired)
584 return true;
585 // Otherwise, continue processing the queue
586 else
587 return executeOperationQueue();
588}
589
590bool FilterManager::executeOneOperation(FilterState operation)
591{
592 bool actionRequired = false;
593
594 switch (operation)
595 {
596 default:
597 break;
598 }
599
600 return actionRequired;
601}
602
603void FilterManager::setFocusOffsetComplete()
604{
605 if (state == FILTER_OFFSET)
606 executeOperationQueue();
607}
608
609double FilterManager::getFilterExposure(const QString &name) const
610{
611 auto filterDetails = getFilterByName(name);
612 if (filterDetails)
613 return filterDetails->exposure();
614
615 // Default value
616 return 1;
617}
618
619bool FilterManager::setFilterExposure(int index, double exposure)
620{
621 if (m_currentFilterLabels.empty())
622 return false;
623
624 QString color = m_currentFilterLabels[index];
625 for (int i = 0; i < m_ActiveFilters.count(); i++)
626 {
627 if (color == m_ActiveFilters[i]->color())
628 {
629 m_FilterModel->setData(m_FilterModel->index(i, FM_EXPOSURE), exposure);
630 m_FilterModel->submitAll();
631 refreshFilterModel();
632 return true;
633 }
634 }
635
636 return false;
637}
638
639int FilterManager::getFilterOffset(const QString &name) const
640{
641 int offset = INVALID_VALUE;
642 auto filterDetails = getFilterByName(name);
643 if (filterDetails)
644 offset = filterDetails->offset();
645
646 return offset;
647}
648
649bool FilterManager::setFilterOffset(QString color, int offset)
650{
651 if (m_currentFilterLabels.empty())
652 return false;
653
654 for (int i = 0; i < m_ActiveFilters.count(); i++)
655 {
656 if (color == m_ActiveFilters[i]->color())
657 {
658 m_FilterModel->setData(m_FilterModel->index(i, FM_OFFSET), offset);
659 m_FilterModel->submitAll();
660 refreshFilterModel();
661 return true;
662 }
663 }
664
665 return false;
666}
667
668bool FilterManager::getFilterAbsoluteFocusDetails(const QString &name, int &focusPos, double &focusTemp,
669 double &focusAlt) const
670{
671 auto filterDetails = getFilterByName(name);
672 if (filterDetails)
673 {
674 focusPos = filterDetails->absoluteFocusPosition();
675 focusTemp = filterDetails->focusTemperature();
676 focusAlt = filterDetails->focusAltitude();
677 return true;
678 }
679
680 return false;
681}
682
683bool FilterManager::setFilterAbsoluteFocusDetails(int index, int focusPos, double focusTemp, double focusAlt)
684{
685 if (index < 0 || index >= m_currentFilterLabels.count())
686 return false;
687
688 QString color = m_currentFilterLabels[index];
689 for (int i = 0; i < m_ActiveFilters.count(); i++)
690 {
691 if (color == m_ActiveFilters[i]->color())
692 {
693 m_FilterModel->setData(m_FilterModel->index(i, FM_LAST_AF_SOLUTION), focusPos);
694 m_FilterModel->setData(m_FilterModel->index(i, FM_LAST_AF_TEMP), focusTemp);
695 m_FilterModel->setData(m_FilterModel->index(i, FM_LAST_AF_ALT), focusAlt);
696 m_FilterModel->submitAll();
697 refreshFilterModel();
698 return true;
699 }
700 }
701
702 return false;
703}
704
705QString FilterManager::getFilterLock(const QString &name) const
706{
707 auto filterDetails = getFilterByName(name);
708 if (filterDetails)
709 return filterDetails->lockedFilter();
710
711 // Default value
712 return NULL_FILTER;
713}
714
715bool FilterManager::setFilterLock(int index, QString name)
716{
717 if (m_currentFilterLabels.empty())
718 return false;
719
720 QString color = m_currentFilterLabels[index];
721 for (int i = 0; i < m_ActiveFilters.count(); i++)
722 {
723 if (color == m_ActiveFilters[i]->color())
724 {
725 m_FilterModel->setData(m_FilterModel->index(i, FM_LOCK_FILTER), name);
726 m_FilterModel->submitAll();
727 refreshFilterModel();
728 return true;
729 }
730 }
731
732 return false;
733}
734
735int FilterManager::getFilterWavelength(const QString &name) const
736{
737 auto filterDetails = getFilterByName(name);
738 if (filterDetails)
739 return filterDetails->wavelength();
740
741 // Default value
742 return 500;
743}
744
745double FilterManager::getFilterTicksPerTemp(const QString &name) const
746{
747 auto filterDetails = getFilterByName(name);
748 if (filterDetails)
749 return filterDetails->focusTicksPerTemp();
750
751 // Something's wrong so return 0
752 return 0.0;
753}
754
755double FilterManager::getFilterTicksPerAlt(const QString &name) const
756{
757 auto filterDetails = getFilterByName(name);
758 if (filterDetails)
759 return filterDetails->focusTicksPerAlt();
760
761 // Something's wrong so return 0
762 return 0.0;
763}
764
765OAL::Filter * FilterManager::getFilterByName(const QString &name) const
766{
767 if (m_currentFilterLabels.empty() ||
768 m_currentFilterPosition < 1 ||
769 m_currentFilterPosition > m_currentFilterLabels.count())
770 return nullptr;
771
772 QString color = name;
773 if (color.isEmpty())
774 color = m_currentFilterLabels[m_currentFilterPosition - 1];
775
776 auto pos = std::find_if(m_ActiveFilters.begin(), m_ActiveFilters.end(), [color](OAL::Filter * oneFilter)
777 {
778 return (oneFilter->color() == color);
779 });
780
781 if (pos != m_ActiveFilters.end())
782 return (*pos);
783 else
784 return nullptr;
785}
786
787void FilterManager::removeDevice(const QSharedPointer<ISD::GenericDevice> &device)
788{
789 if (m_FilterWheel && (m_FilterWheel->getDeviceName() == device->getDeviceName()))
790 {
791 m_FilterNameProperty = nullptr;
792 m_FilterPositionProperty = nullptr;
793 m_FilterWheel = nullptr;
794 m_currentFilterLabels.clear();
795 m_currentFilterPosition = 0;
796 qDeleteAll(m_ActiveFilters);
797 m_ActiveFilters.clear();
798 delete(m_FilterModel);
799 m_FilterModel = nullptr;
800 }
801}
802
803void FilterManager::setFocusStatus(Ekos::FocusState focusState)
804{
805 if (state == FILTER_AUTOFOCUS)
806 {
807 switch (focusState)
808 {
809 case FOCUS_COMPLETE:
810 executeOperationQueue();
811 break;
812
813 case FOCUS_FAILED:
814 if (++retries == 3)
815 {
816 retries = 0;
817 emit failed();
818 return;
819 }
820 // Restart again
821 emit runAutoFocus(AutofocusReason::FOCUS_FILTER, "");
822 break;
823
824 default:
825 break;
826
827 }
828 }
829}
830
831bool FilterManager::syncAbsoluteFocusPosition(int index)
832{
833 if (index < 0 || index > m_ActiveFilters.count())
834 {
835 // We've been asked to set the focus position but something's wrong because
836 // the passed in filter index is bad. Give up and return true - returning false
837 // just results in an infinite retry loop.
838 qCWarning(KSTARS_INDI) << __FUNCTION__ << "index" << index << "is out of bounds.";
839 return true;
840 }
841
842 // By default filter absolute focus offset is zero
843 // JM 2023.07.03: So if it is zero, we return immediately.
844 auto absFocusPos = m_ActiveFilters[index]->absoluteFocusPosition();
845
846 if (m_FocusAbsPosition == absFocusPos || absFocusPos <= 0)
847 {
848 m_FocusAbsPositionPending = false;
849 return true;
850 }
851 else if (m_FocusAbsPositionPending == false)
852 {
853 m_FocusAbsPositionPending = true;
854 emit newFocusOffset(absFocusPos, true);
855 }
856
857 return false;
858}
859
860bool FilterManager::setFilterNames(const QStringList &newLabels)
861{
862 if (m_FilterWheel == nullptr || m_currentFilterLabels.empty())
863 return false;
864
865 m_FilterWheel->setLabels(newLabels);
866 return true;
867}
868
869QJsonObject FilterManager::toJSON()
870{
871 if (!m_FilterWheel)
872 return QJsonObject();
873
874 QJsonArray filters;
875
876 for (int i = 0; i < m_FilterModel->rowCount(); ++i)
877 {
879 {
880 {"index", i},
881 {"label", m_FilterModel->data(m_FilterModel->index(i, FM_LABEL)).toString()},
882 {"exposure", m_FilterModel->data(m_FilterModel->index(i, FM_EXPOSURE)).toDouble()},
883 {"offset", m_FilterModel->data(m_FilterModel->index(i, FM_OFFSET)).toInt()},
884 {"autofocus", m_FilterModel->data(m_FilterModel->index(i, FM_AUTO_FOCUS)).toBool()},
885 {"lock", m_FilterModel->data(m_FilterModel->index(i, FM_LOCK_FILTER)).toString()},
886 {"lastafsolution", m_FilterModel->data(m_FilterModel->index(i, FM_LAST_AF_SOLUTION)).toInt()},
887 {"lastaftemp", m_FilterModel->data(m_FilterModel->index(i, FM_LAST_AF_TEMP)).toDouble()},
888 {"lastafalt", m_FilterModel->data(m_FilterModel->index(i, FM_LAST_AF_ALT)).toDouble()},
889 {"tickspertemp", m_FilterModel->data(m_FilterModel->index(i, FM_TICKS_PER_TEMP)).toDouble()},
890 {"ticksperalt", m_FilterModel->data(m_FilterModel->index(i, FM_TICKS_PER_ALT)).toDouble()},
891 {"wavelength", m_FilterModel->data(m_FilterModel->index(i, FM_WAVELENGTH)).toInt()},
892 };
893
894 filters.append(oneFilter);
895 }
896
897 QJsonObject data =
898 {
899 {"device", m_FilterWheel->getDeviceName()},
900 {"filters", filters}
901 };
902
903 return data;
904
905}
906
907void FilterManager::setFilterData(const QJsonObject &settings)
908{
909 if (!m_FilterWheel)
910 return;
911
912 if (settings["device"].toString() != m_FilterWheel->getDeviceName())
913 return;
914
915 QJsonArray filters = settings["filters"].toArray();
916 QStringList labels = getFilterLabels();
917
918 for (auto oneFilterRef : filters)
919 {
921 int row = oneFilter["index"].toInt();
922
923 labels[row] = oneFilter["label"].toString();
924 m_FilterModel->setData(m_FilterModel->index(row, FM_LABEL), oneFilter["label"].toString());
925 m_FilterModel->setData(m_FilterModel->index(row, FM_EXPOSURE), oneFilter["exposure"].toDouble());
926 m_FilterModel->setData(m_FilterModel->index(row, FM_OFFSET), oneFilter["offset"].toInt());
927 m_FilterModel->setData(m_FilterModel->index(row, FM_AUTO_FOCUS), oneFilter["autofocus"].toBool());
928 m_FilterModel->setData(m_FilterModel->index(row, FM_LOCK_FILTER), oneFilter["lock"].toString());
929 m_FilterModel->setData(m_FilterModel->index(row, FM_LAST_AF_SOLUTION), oneFilter["lastafsolution"].toInt());
930 m_FilterModel->setData(m_FilterModel->index(row, FM_LAST_AF_TEMP), oneFilter["lastaftemp"].toDouble());
931 m_FilterModel->setData(m_FilterModel->index(row, FM_LAST_AF_ALT), oneFilter["lastafalt"].toDouble());
932 m_FilterModel->setData(m_FilterModel->index(row, FM_TICKS_PER_TEMP), oneFilter["tickspertemp"].toDouble());
933 m_FilterModel->setData(m_FilterModel->index(row, FM_TICKS_PER_ALT), oneFilter["ticksperalt"].toDouble());
934 m_FilterModel->setData(m_FilterModel->index(row, FM_WAVELENGTH), oneFilter["wavelength"].toInt());
935 }
936
937 m_FilterModel->submitAll();
938 setFilterNames(labels);
939
940 refreshFilterModel();
941}
942
943void FilterManager::buildFilterOffsets()
944{
945 // Launch the Build Filter Offsets utility. The utility uses a sync call to launch the dialog
946 QSharedPointer<FilterManager> filterManager;
947 Ekos::Manager::Instance()->getFilterManager(m_FilterWheel->getDeviceName(), filterManager);
948 BuildFilterOffsets bfo(filterManager);
949}
950
951void FilterManager::signalRunAutoFocus(AutofocusReason autofocusReason, const QString &reasonInfo)
952{
953 // BuildFilterOffsets signalled runAutoFocus so pass signal to Focus
954 emit runAutoFocus(autofocusReason, reasonInfo);
955}
956
957void FilterManager::autoFocusComplete(FocusState completionState, int currentPosition, double currentTemperature,
958 double currentAlt)
959{
960 // Focus signalled Autofocus completed so pass signal to BuildFilterOffsets
961 emit autoFocusDone(completionState, currentPosition, currentTemperature, currentAlt);
962}
963
964void FilterManager::signalAbortAutoFocus()
965{
966 // BuildFilterOffsets signalled abortAutoFocus so pass signal to Focus
967 emit abortAutoFocus();
968}
969
970}
Information of user filters.
Definition filter.h:48
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:78
const QList< QKeySequence > & close()
QString label(StandardShortcut id)
QString name(StandardShortcut id)
void clicked(bool checked)
void toggled(bool checked)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void accepted()
void rejected()
Int toInt() const const
void append(const QJsonValue &value)
void append(QList< T > &&value)
int column() const const
int row() const const
QSqlDatabase database(const QString &connectionName, bool open)
QVariant value(const QString &name) const const
QChar * data()
bool isEmpty() const const
ToolTipRole
Horizontal
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.