Kstars

capture.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "capture.h"
8
9#include "cameraprocess.h"
10#include "camerastate.h"
11#include "capturedeviceadaptor.h"
12#include "captureadaptor.h"
13#include "refocusstate.h"
14#include "kstars.h"
15#include "kstarsdata.h"
16#include "Options.h"
17#include "sequencejob.h"
18#include "placeholderpath.h"
19#include "ekos/manager.h"
20#include "ekos/auxiliary/darklibrary.h"
21#include "ekos/auxiliary/profilesettings.h"
22#include "auxiliary/ksmessagebox.h"
23
24#include "scriptsmanager.h"
25#include "fitsviewer/fitsdata.h"
26#include "indi/driverinfo.h"
27#include "indi/indifilterwheel.h"
28#include "indi/indicamera.h"
29#include "indi/indirotator.h"
30#include "ekos/guide/guide.h"
31#include <basedevice.h>
32
33#include <ekos_capture_debug.h>
34#include <qlineedit.h>
35
36#define MF_TIMER_TIMEOUT 90000
37#define MF_RA_DIFF_LIMIT 4
38
39// Qt version calming
40#include <qtendl.h>
41
42#define KEY_FILTERS "filtersList"
43#define TAB_BUTTON_SIZE 20
44
45namespace Ekos
46{
47
48Capture::Capture()
49{
50 setupUi(this);
51
52 qRegisterMetaType<CaptureState>("CaptureState");
53 qDBusRegisterMetaType<CaptureState>();
54 new CaptureAdaptor(this);
55
56 // Adding the "New Tab" tab
57 QWidget *newTab = new QWidget;
58 QPushButton *addButton = new QPushButton;
59 addButton->setIcon(QIcon::fromTheme("list-add"));
60 addButton->setFixedSize(TAB_BUTTON_SIZE, TAB_BUTTON_SIZE);
61 addButton->setToolTip(i18n("<p>Add additional camera</p><p><b>WARNING</b>: This feature is experimental!</p>"));
62 connect(addButton, &QPushButton::clicked, this, &Capture::addCamera);
63
64 cameraTabs->addTab(newTab, "");
65 cameraTabs->tabBar()->setTabButton(0, QTabBar::RightSide, addButton);
66
67 // Create main camera
68 addCamera();
69
70 QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Capture", this);
71 QPointer<QDBusInterface> ekosInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos",
73
74 // Connecting DBus signals
75 QDBusConnection::sessionBus().connect("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos", "newModule", this,
77
78 // ensure that the mount interface is present
79 registerNewModule("Mount");
80
81 DarkLibrary::Instance()->setCaptureModule(this);
82}
83
84
85QSharedPointer<Camera> Capture::addCamera()
86{
87 QSharedPointer<Camera> newCamera;
88 newCamera.reset(new Camera(m_Cameras.count()));
89
90 // create the new tab and bring it to front
91 const int tabIndex = cameraTabs->insertTab(std::max(0, cameraTabs->count() - 1), newCamera.get(), "new Camera");
92 cameraTabs->setCurrentIndex(tabIndex);
93 // make the tab closeable if it's not the first one
94 if (tabIndex > 0)
95 {
96 QPushButton *closeButton = new QPushButton();
97 closeButton->setIcon(QIcon::fromTheme("window-close"));
98 closeButton->setFixedSize(TAB_BUTTON_SIZE, TAB_BUTTON_SIZE);
99 cameraTabs->tabBar()->setTabButton(tabIndex, QTabBar::RightSide, closeButton);
100 connect(closeButton, &QPushButton::clicked, this, [this, tabIndex]()
101 {
102 checkCloseCameraTab(tabIndex);
103 });
104 }
105
106 // forward signals from the camera
107 connect(newCamera.get(), &Camera::newLog, this, &Capture::appendLogText);
108 connect(newCamera.get(), &Camera::refreshCamera, this, &Capture::updateCamera);
109 connect(newCamera.get(), &Camera::sequenceChanged, this, &Capture::sequenceChanged);
110 connect(newCamera.get(), &Camera::newLocalPreview, this, &Capture::newLocalPreview);
111 connect(newCamera.get(), &Camera::dslrInfoRequested, this, &Capture::dslrInfoRequested);
112 connect(newCamera.get(), &Camera::trainChanged, this, &Capture::trainChanged);
113 connect(newCamera.get(), &Camera::settingsUpdated, this, &Capture::settingsUpdated);
114 connect(newCamera.get(), &Camera::filterManagerUpdated, this, &Capture::filterManagerUpdated);
115 connect(newCamera.get(), &Camera::newFilterStatus, this, &Capture::newFilterStatus);
116 connect(newCamera.get(), &Camera::ready, this, &Capture::ready);
117 connect(newCamera.get(), &Camera::newExposureProgress, this, &Capture::newExposureProgress);
118 connect(newCamera.get(), &Camera::captureComplete, this, &Capture::captureComplete);
119 connect(newCamera.get(), &Camera::captureStarting, this, &Capture::captureStarting);
120 connect(newCamera.get(), &Camera::captureAborted, this, &Capture::captureAborted);
121 connect(newCamera.get(), &Camera::checkFocus, this, &Capture::checkFocus);
122 connect(newCamera.get(), &Camera::newImage, this, &Capture::newImage);
123 connect(newCamera.get(), &Camera::runAutoFocus, this, &Capture::runAutoFocus);
124 connect(newCamera.get(), &Camera::resetFocus, this, &Capture::resetFocus);
125 connect(newCamera.get(), &Camera::abortFocus, this, &Capture::abortFocus);
126 connect(newCamera.get(), &Camera::adaptiveFocus, this, &Capture::adaptiveFocus);
127 connect(newCamera.get(), &Camera::captureTarget, this, &Capture::captureTarget);
128 connect(newCamera.get(), &Camera::guideAfterMeridianFlip, this, &Capture::guideAfterMeridianFlip);
129 connect(newCamera.get(), &Camera::newStatus, this, &Capture::newStatus);
130 connect(newCamera.get(), &Camera::suspendGuiding, this, &Capture::suspendGuiding);
131 connect(newCamera.get(), &Camera::resumeGuiding, this, &Capture::resumeGuiding);
132 connect(newCamera.get(), &Camera::driverTimedout, this, &Capture::driverTimedout);
133
134 m_Cameras.append(newCamera);
135 // update the tab text
136 updateCamera(tabIndex, true);
137 return newCamera;
138}
139
140void Capture::updateCamera(int tabID, bool isValid)
141{
142 if (isValid)
143 {
144 if (tabID < cameraTabs->count() && tabID < m_Cameras.count() && m_Cameras[tabID]->activeCamera() != nullptr)
145 {
146 auto name = m_Cameras[tabID]->activeCamera()->getDeviceName();
147 cameraTabs->setTabText(tabID, name);
148 }
149 else
150 qCWarning(KSTARS_EKOS_CAPTURE) << "Unknown camera ID:" << tabID;
151 }
152 else
153 cameraTabs->setTabText(cameraTabs->currentIndex(), "no camera");
154}
155
156
158{
159 return process()->setDome(device);
160}
161
163{
164 if (mainCamera()->m_standAlone)
165 return;
166 if (name == "Mount" && mountInterface == nullptr)
167 {
168 qCDebug(KSTARS_EKOS_CAPTURE) << "Registering new Module (" << name << ")";
169 mountInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Mount",
170 "org.kde.kstars.Ekos.Mount", QDBusConnection::sessionBus(), this);
171 }
172}
173
174QString Capture::camera()
175{
176 if (devices()->getActiveCamera())
177 return devices()->getActiveCamera()->getDeviceName();
178
179 return QString();
180}
181
182void Capture::setGuideChip(ISD::CameraChip * guideChip)
183{
184 // We should suspend guide in two scenarios:
185 // 1. If guide chip is within the primary CCD, then we cannot download any data from guide chip while primary CCD is downloading.
186 // 2. If we have two CCDs running from ONE driver (Multiple-Devices-Per-Driver mpdp is true). Same issue as above, only one download
187 // at a time.
188 // After primary CCD download is complete, we resume guiding.
189 if (!devices()->getActiveCamera())
190 return;
191
192 state()->setSuspendGuidingOnDownload((devices()->getActiveCamera()->getChip(
193 ISD::CameraChip::GUIDE_CCD) == guideChip) ||
194 (guideChip->getCCD() == devices()->getActiveCamera() &&
195 devices()->getActiveCamera()->getDriverInfo()->getAuxInfo().value("mdpd", false).toBool()));
196}
197
198QString Capture::filterWheel()
199{
200 if (devices()->filterWheel())
201 return devices()->filterWheel()->getDeviceName();
202
203 return QString();
204}
205
206bool Capture::setFilter(const QString &filter)
207{
208 if (devices()->filterWheel())
209 {
210 mainCamera()->FilterPosCombo->setCurrentText(filter);
211 return true;
212 }
213
214 return false;
215}
216
217QString Capture::filter()
218{
219 return mainCamera()->FilterPosCombo->currentText();
220}
221
222
223
224void Capture::appendLogText(const QString &text)
225{
226 m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2",
227 KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text));
228
229 qCInfo(KSTARS_EKOS_CAPTURE) << text;
230
231 emit newLog(text);
232}
233
234void Capture::clearLog()
235{
236 m_LogText.clear();
237 emit newLog(QString());
238}
239
240void Capture::setFocusTemperatureDelta(double focusTemperatureDelta, double absTemperture)
241{
242 Q_UNUSED(absTemperture);
243 // This produces too much log spam
244 // Maybe add a threshold to report later?
245 //qCDebug(KSTARS_EKOS_CAPTURE) << "setFocusTemperatureDelta: " << focusTemperatureDelta;
246 state()->getRefocusState()->setFocusTemperatureDelta(focusTemperatureDelta);
247}
248
249void Capture::setGuideDeviation(double delta_ra, double delta_dec)
250{
251 const double deviation_rms = std::hypot(delta_ra, delta_dec);
252
253 // forward it to the state machine
254 state()->setGuideDeviation(deviation_rms);
255
256}
257
259{
260 // This function is called independently from the Scheduler or the UI, so honor the change
261 state()->setIgnoreJobProgress(true);
262}
263
264void Capture::setAlignStatus(AlignState newstate)
265{
266 // forward it directly to the state machine
267 state()->setAlignState(newstate);
268}
269
270void Capture::setGuideStatus(GuideState newstate)
271{
272 // forward it directly to the state machine
273 state()->setGuideState(newstate);
274}
275
276bool Capture::setVideoLimits(uint16_t maxBufferSize, uint16_t maxPreviewFPS)
277{
278 if (devices()->getActiveCamera() == nullptr)
279 return false;
280
281 return devices()->getActiveCamera()->setStreamLimits(maxBufferSize, maxPreviewFPS);
282}
283
284QSharedPointer<Camera> &Capture::camera(int i)
285{
286 if (i < m_Cameras.count())
287 return m_Cameras[i];
288 else
289 {
290 qCWarning(KSTARS_EKOS_CAPTURE) << "Unknown camera ID:" << i;
291 return m_Cameras[0];
292 }
293}
294
295void Ekos::Capture::closeCameraTab(int tabIndex)
296{
297 cameraTabs->removeTab(tabIndex);
298 camera(tabIndex).clear();
299 m_Cameras.removeAt(tabIndex);
300 // select the next one on the left
301 cameraTabs->setCurrentIndex(std::max(0, tabIndex - 1));
302}
303
304void Capture::checkCloseCameraTab(int tabIndex)
305{
306 if (m_Cameras[tabIndex]->state()->isBusy())
307 {
308 // if accept has been clicked, abort and close the tab
309 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, &tabIndex]()
310 {
311 KSMessageBox::Instance()->disconnect(this);
312 m_Cameras[tabIndex]->abort();
313 closeCameraTab(tabIndex);
314 });
315 // if cancel has been clicked, do not close the tab
316 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
317 {
318 KSMessageBox::Instance()->disconnect(this);
319 });
320
321 KSMessageBox::Instance()->warningContinueCancel(i18n("Camera %1 is busy. Abort to close?",
322 m_Cameras[tabIndex]->activeCamera()->getDeviceName()), i18n("Stop capturing"), 30, false, i18n("Abort"));
323 }
324 else
325 {
326 closeCameraTab(tabIndex);
327 }
328}
329
330QSharedPointer<Camera> Capture::mainCamera() const
331{
332 if (m_Cameras.size() > 0)
333 return m_Cameras[0];
334 else
335 return QSharedPointer<Camera>(new Camera());
336}
337
338void Capture::setMountStatus(ISD::Mount::Status newState)
339{
340 switch (newState)
341 {
342 case ISD::Mount::MOUNT_PARKING:
343 case ISD::Mount::MOUNT_SLEWING:
344 case ISD::Mount::MOUNT_MOVING:
345 mainCamera()->previewB->setEnabled(false);
346 mainCamera()->liveVideoB->setEnabled(false);
347 // Only disable when button is "Start", and not "Stopped"
348 // If mount is in motion, Stopped button should always be enabled to terminate
349 // the sequence
350 if (state()->isBusy() == false)
351 mainCamera()->startB->setEnabled(false);
352 break;
353
354 default:
355 if (state()->isBusy() == false)
356 {
357 mainCamera()->previewB->setEnabled(true);
358 if (devices()->getActiveCamera())
359 mainCamera()->liveVideoB->setEnabled(devices()->getActiveCamera()->hasVideoStream());
360 mainCamera()->startB->setEnabled(true);
361 }
362
363 break;
364 }
365}
366
367void Capture::setAlignResults(double solverPA, double ra, double de, double pixscale)
368{
369 Q_UNUSED(ra)
370 Q_UNUSED(de)
371 Q_UNUSED(pixscale)
372 if (devices()->rotator() && mainCamera()->m_RotatorControlPanel)
373 mainCamera()->m_RotatorControlPanel->refresh(solverPA);
374}
375
376void Capture::setMeridianFlipState(QSharedPointer<MeridianFlipState> newstate)
377{
378 state()->setMeridianFlipState(newstate);
379 connect(state()->getMeridianFlipState().get(), &MeridianFlipState::newLog, this, &Capture::appendLogText);
380}
381
383{
384 return process()->hasCoolerControl();
385}
386
388{
389 return process()->setCoolerControl(enable);
390}
391
393{
394 process()->removeDevice(device);
395}
396
397QString Capture::getTargetName()
398{
399 if (activeJob())
400 return activeJob()->getCoreProperty(SequenceJob::SJ_TargetName).toString();
401 else
402 return "";
403}
404
405void Capture::setHFR(double newHFR, int, bool inAutofocus)
406{
407 state()->getRefocusState()->setFocusHFR(newHFR, inAutofocus);
408}
409}
bool setVideoLimits(uint16_t maxBufferSize, uint16_t maxPreviewFPS)
setVideoLimits sets the buffer size and max preview fps for live preview
Definition capture.cpp:276
bool setDome(ISD::Dome *device)
setDome Set dome device
Definition capture.cpp:157
void setFocusTemperatureDelta(double focusTemperatureDelta, double absTemperature)
updateAdaptiveFocusStatus Handle new focus state
Definition capture.cpp:240
void updateCamera(int tabID, bool isValid)
Update the camera.
Definition capture.cpp:140
void setHFR(double newHFR, int position, bool inAutofocus)
setHFR Receive the measured HFR value of the latest frame
Definition capture.cpp:405
QSharedPointer< CameraProcess > process() const
process shortcut for the process engine
Definition capture.h:582
void setGuideDeviation(double delta_ra, double delta_dec)
setGuideDeviation Set the guiding deviation as measured by the guiding module.
Definition capture.cpp:249
void registerNewModule(const QString &name)
registerNewModule Register an Ekos module as it arrives via DBus and create the appropriate DBus inte...
Definition capture.cpp:162
void removeDevice(const QSharedPointer< ISD::GenericDevice > &device)
Generic method for removing any connected device.
Definition capture.cpp:392
CameraChip class controls a particular chip in camera.
Class handles control of INDI dome devices.
Definition indidome.h:25
Q_SCRIPTABLE bool hasCoolerControl()
DBUS interface function.
Definition capture.cpp:382
Q_SCRIPTABLE Q_NOREPLY void ignoreSequenceHistory()
DBUS interface function.
Definition capture.cpp:258
Q_SCRIPTABLE bool setCoolerControl(bool enable)
DBUS interface function.
Definition capture.cpp:387
Q_SCRIPTABLE bool setFilter(const QString &filter)
DBUS interface function.
Definition capture.cpp:206
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:79
AlignState
Definition ekos.h:145
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
void clicked(bool checked)
void setIcon(const QIcon &icon)
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
void accepted()
void rejected()
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
void clear()
qsizetype count() const const
iterator insert(const_iterator before, parameter_type value)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T * get() const const
QString toString() const const
QWidget(QWidget *parent, Qt::WindowFlags f)
void setFixedSize(const QSize &s)
void setupUi(QWidget *widget)
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:59:51 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.