1 /*
2     SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "indi/indiccd.h"
10 #include "indi/indicap.h"
11 #include "darkview.h"
12 #include "defectmap.h"
13 #include "ekos/ekos.h"
14 
15 #include <QDialog>
16 #include <QPointer>
17 #include "ui_darklibrary.h"
18 
19 class QSqlTableModel;
20 class QSortFilterProxyModel;
21 class FITSHistogramView;
22 
23 namespace Ekos
24 {
25 
26 class Capture;
27 class SequenceJob;
28 
29 /**
30  * @class DarkLibrary
31  * @short Handles acquisition & loading of dark frames and defect map for cameras. If a suitable dark frame exists,
32  * it is loaded from disk, otherwise it gets captured and saved for later use.
33  *
34  * Dark Frames:
35  *
36  * The user can generate dark frames from an average combination of the camera dark frames. By default, 5 dark frames
37  * are captured to merged into a single master frame. Frame duration, binning, and temperature are all configurable.
38  * If the user select "Dark" in any of the Ekos module, Dark Library can be queried if a suitable dark frame exists given
39  * the current camera settings (binning, temperature..etc). If a suitable frame exists, it is loaded up and send to /class DarkProcessor
40  * class along with the light frame to perform subtraction or defect map corrections.
41  *
42  * Defect Maps:
43  *
44  * Some CMOS cameras exhibit hot pixels that are better treated with a defect map. A defect map is a collection of "bad" pixels that
45  * are above or below certain threshold controlled by the user. This should isolate the cold and hotpixels in frames so that they are
46  * removed from the light frames once the defect map is applied against it. This is done using 3x3 median filter over the bad pixels.
47  *
48  * @author Jasem Mutlaq
49  * @version 1.0
50  */
51 class DarkLibrary : public QDialog, public Ui::DarkLibrary
52 {
53         Q_OBJECT
54 
55     public:
56         static DarkLibrary *Instance();
57         static void Release();
58 
59         /**
60          * @brief findDarkFrame Search for a dark frame that matches the passed paramters.
61          * @param targetChip Camera chip pointer to lookup for relevant information (binning, ROI..etc).
62          * @param duration Duration is second to match it against the database.
63          * @param darkData If a frame is found, load it from disk and store it in a shared FITSData pointer.
64          * @return True if a suitable frame was found the loaded successfully, false otherwise.
65          */
66         bool findDarkFrame(ISD::CCDChip *targetChip, double duration, QSharedPointer<FITSData> &darkData);
67 
68         /**
69          * @brief findDefectMap Search for a defect map that matches the passed paramters.
70          * @param targetChip Camera chip pointer to lookup for relevant information (binning, ROI..etc).
71          * @param duration Duration is second to match it against the database.
72          * @param defectMap If a frame is found, load it from disk and store it in a shared DefectMap pointer.
73          * @return True if a suitable frame was found the loaded successfully, false otherwise.
74          */
75         bool findDefectMap(ISD::CCDChip *targetChip, double duration, QSharedPointer<DefectMap> &defectMap);
76 
77         /**
78          * @brief cameraHasDefectMaps Check if camera has any defect maps available.
79          * @param name Camera name
80          * @return True if at least one defect maps exists for this camera, false otherwise.
81          */
cameraHasDefectMaps(const QString & name)82         bool cameraHasDefectMaps(const QString &name) const
83         {
84             return m_DefectCameras.contains(name);
85         }
86 
87         void refreshFromDB();
88         void addCamera(ISD::GDInterface * newCCD);
89         void removeCamera(ISD::GDInterface * newCCD);
90         void checkCamera(int ccdNum = -1);
91         //void reset();
92         void setCaptureModule(Capture *instance);
93 
94     protected:
95         virtual void closeEvent(QCloseEvent *ev) override;
96 
97     signals:
98         void newLog(const QString &message);
99 
100     public slots:
101         void processNewImage(SequenceJob *job, const QSharedPointer<FITSData> &data);
102         void processNewBLOB(IBLOB *bp);
103 
104     private slots:
105         void clearAll();
106         void clearRow();
107         void clearExpired();
108         void openDarksFolder();
109         void saveDefectMap();
110         void setCompleted();
111         void loadDarkFITS(QModelIndex index);
112 
113     private:
114         explicit DarkLibrary(QWidget *parent);
115         ~DarkLibrary() override;
116 
117         static DarkLibrary *_DarkLibrary;
118 
119         ////////////////////////////////////////////////////////////////////////////////////////////////
120         /// Dark Frames Functions
121         ////////////////////////////////////////////////////////////////////////////////////////////////
122         /**
123          * @brief countDarkTotalTime Given current settings, count how many minutes
124          * are required to complete all the darks.
125          */
126         void countDarkTotalTime();
127 
128         /**
129          * @brief generateDarkJobs Check the user frame parameters in the Darks tab and generate the corresponding
130          * capture jobs. Populate capture module with the dark jobs.
131          */
132         void generateDarkJobs();
133 
134         /**
135          * @brief executeDarkJobs Start executing the dark jobs in capture module.
136          */
137         void executeDarkJobs();
138 
139         /**
140          * @brief stopDarkJobs Abort all dark job captures.
141          */
142         void stopDarkJobs();
143 
144         /**
145          * @brief generateMasterFrameHelper Calls templated generateMasterFrame with the correct data type.
146          * @param data Passed dark frame data to generateMasterFrame
147          * @param metadata passed metadata to generateMasterFrame
148          */
149         void generateMasterFrame(const QSharedPointer<FITSData> &data, const QJsonObject &metadata);
150 
151         /**
152          * @brief generateMasterFrame After data aggregation is done, the selected stacking algorithm is applied and the master dark
153          * frame is saved to disk and user database along with the metadata.
154          * @param data last used data. This is not used for reading, but to simply apply the algorithm to the FITSData buffer
155          * and then save it to disk.
156          * @param metadata information on frame to help in the stacking process.
157          */
158         template <typename T>  void generateMasterFrameInternal(const QSharedPointer<FITSData> &data, const QJsonObject &metadata);
159 
160         /**
161          * @brief aggregateHelper Calls tempelated aggregate function with the appropiate data type.
162          * @param data Dark frame data to pass on to aggregate function.
163          */
164         void aggregate(const QSharedPointer<FITSData> &data);
165 
166         /**
167          * @brief aggregate Aggregate the data as per the selected algorithm. Each time a new dark frame is received, this function
168          * adds the frame data to the dark buffer.
169          * @param data Dark frame data.
170          */
171         template <typename T> void aggregateInternal(const QSharedPointer<FITSData> &data);
172 
173         /**
174          * @brief cacheDarkFrameFromFile Load dark frame from disk and saves it in the local dark frames cache
175          * @param filename path of dark frame to load
176          * @return True if file is successfully loaded, false otherwise.
177          */
178         bool cacheDarkFrameFromFile(const QString &filename);
179 
180 
181         ////////////////////////////////////////////////////////////////////////////////////////////////
182         /// Misc Functions
183         ////////////////////////////////////////////////////////////////////////////////////////////////
184         void initView();
185         void setCaptureState(CaptureState state);
186         void reloadDarksFromDatabase();
187         void loadCurrentMasterDefectMap();
188         void clearBuffers();
189 
190         ////////////////////////////////////////////////////////////////////////////////////////////////
191         /// Defect Map Functions
192         ////////////////////////////////////////////////////////////////////////////////////////////////
193         void refreshDefectMastersList(const QString &camera);
194         void loadCurrentMasterDark(const QString &camera, int masterIndex = -1);
195         void populateMasterMetedata();
196         /**
197          * @brief cacheDefectMapFromFile Load defect map from disk and saves it in the local defect maps cache
198          * @param key dark file name that is used as the key in the defect map cache
199          * @param filename path of dark frame to load
200          * @return True if file is successfully loaded, false otherwise.
201          */
202         bool cacheDefectMapFromFile(const QString &key, const QString &filename);
203 
204         ////////////////////////////////////////////////////////////////////////////////////////////////
205         /// Member Variables
206         ////////////////////////////////////////////////////////////////////////////////////////////////
207 
208         QList<QVariantMap> m_DarkFramesDatabaseList;
209         QMap<QString, QSharedPointer<FITSData>> m_CachedDarkFrames;
210         QMap<QString, QSharedPointer<DefectMap>> m_CachedDefectMaps;
211 
212         ISD::CCD *m_CurrentCamera {nullptr};
213         ISD::CCDChip *m_TargetChip {nullptr};
214         QList<ISD::CCD *> m_Cameras;
215         bool m_UseGuideHead {false};
216 
217         Capture *m_CaptureModule {nullptr};
218         QSqlTableModel *darkFramesModel = nullptr;
219         QSortFilterProxyModel *sortFilter = nullptr;
220 
221         std::vector<uint32_t> m_DarkMasterBuffer;
222         uint32_t m_DarkImagesCounter {0};
223         bool m_RememberFITSViewer {true};
224         bool m_RememberSummaryView {true};
225         QString m_RememberFITSDirectory;
226         bool m_JobsGenerated {false};
227         QJsonObject m_PresetSettings;
228         QString m_DefectMapFilename, m_MasterDarkFrameFilename;
229         QStringList m_DarkCameras, m_DefectCameras;
230         QPointer<DarkView> m_DarkView;
231         QPointer<QStatusBar> m_StatusBar;
232         QPointer<QLabel> m_StatusLabel, m_FileLabel;
233         QSharedPointer<DefectMap> m_CurrentDefectMap;
234         QSharedPointer<FITSData> m_CurrentDarkFrame;
235         QFutureWatcher<bool> m_DarkFrameFutureWatcher;
236 };
237 }
238