File indexing completed on 2024-04-28 15:09:09

0001 /*
0002     SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "indi/indicamera.h"
0010 #include "indi/indidustcap.h"
0011 #include "darkview.h"
0012 #include "defectmap.h"
0013 #include "ekos/ekos.h"
0014 
0015 #include <QDialog>
0016 #include <QPointer>
0017 #include "ui_darklibrary.h"
0018 
0019 class QSqlTableModel;
0020 class QSortFilterProxyModel;
0021 class FITSHistogramView;
0022 
0023 namespace Ekos
0024 {
0025 
0026 class Capture;
0027 class SequenceJob;
0028 
0029 /**
0030  * @class DarkLibrary
0031  * @short Handles acquisition & loading of dark frames and defect map for cameras. If a suitable dark frame exists,
0032  * it is loaded from disk, otherwise it gets captured and saved for later use.
0033  *
0034  * Dark Frames:
0035  *
0036  * The user can generate dark frames from an average combination of the camera dark frames. By default, 5 dark frames
0037  * are captured to merged into a single master frame. Frame duration, binning, and temperature are all configurable.
0038  * If the user select "Dark" in any of the Ekos module, Dark Library can be queried if a suitable dark frame exists given
0039  * the current camera settings (binning, temperature..etc). If a suitable frame exists, it is loaded up and send to /class DarkProcessor
0040  * class along with the light frame to perform subtraction or defect map corrections.
0041  *
0042  * Defect Maps:
0043  *
0044  * Some CMOS cameras exhibit hot pixels that are better treated with a defect map. A defect map is a collection of "bad" pixels that
0045  * are above or below certain threshold controlled by the user. This should isolate the cold and hotpixels in frames so that they are
0046  * removed from the light frames once the defect map is applied against it. This is done using 3x3 median filter over the bad pixels.
0047  *
0048  * @author Jasem Mutlaq
0049  * @version 1.0
0050  */
0051 class DarkLibrary : public QDialog, public Ui::DarkLibrary
0052 {
0053         Q_OBJECT
0054 
0055     public:
0056         static DarkLibrary *Instance();
0057         static void Release();
0058 
0059         /**
0060          * @brief findDarkFrame Search for a dark frame that matches the passed paramters.
0061          * @param targetChip Camera chip pointer to lookup for relevant information (binning, ROI..etc).
0062          * @param duration Duration is second to match it against the database.
0063          * @param darkData If a frame is found, load it from disk and store it in a shared FITSData pointer.
0064          * @return True if a suitable frame was found the loaded successfully, false otherwise.
0065          */
0066         bool findDarkFrame(ISD::CameraChip *targetChip, double duration, QSharedPointer<FITSData> &darkData);
0067 
0068         /**
0069          * @brief findDefectMap Search for a defect map that matches the passed paramters.
0070          * @param targetChip Camera chip pointer to lookup for relevant information (binning, ROI..etc).
0071          * @param duration Duration is second to match it against the database.
0072          * @param defectMap If a frame is found, load it from disk and store it in a shared DefectMap pointer.
0073          * @return True if a suitable frame was found the loaded successfully, false otherwise.
0074          */
0075         bool findDefectMap(ISD::CameraChip *targetChip, double duration, QSharedPointer<DefectMap> &defectMap);        
0076 
0077         void refreshFromDB();
0078         bool setCamera(ISD::Camera *device);
0079         void removeDevice(const QSharedPointer<ISD::GenericDevice> &device);
0080         void checkCamera();
0081         //void reset();
0082         void setCaptureModule(Capture *instance);
0083 
0084         void start();
0085         void setCameraPresets(const QJsonObject &settings);
0086         QJsonObject getCameraPresets();        
0087         void setDefectPixels(const QJsonObject &payload);
0088         QJsonArray getViewMasters();
0089         void getloadDarkViewMasterFITS(int index);
0090 
0091         /**
0092          * @brief getDefectSettings Return Defect Map settings
0093          * @return JSON Object of defect map settings
0094          */
0095         QJsonObject getDefectSettings();
0096 
0097         void setDefectMapEnabled(bool enabled);
0098 
0099         // Settings
0100         QVariantMap getAllSettings() const;
0101         void setAllSettings(const QVariantMap &settings);
0102 
0103         QString opticalTrain() const
0104         {
0105             return opticalTrainCombo->currentText();
0106         }
0107         void setOpticalTrain(const QString &value)
0108         {
0109             opticalTrainCombo->setCurrentText(value);
0110         }
0111 
0112         /**
0113          * @brief stop Abort all dark job captures.
0114          */
0115         void stop();
0116     protected:
0117         virtual void closeEvent(QCloseEvent *ev) override;
0118 
0119     signals:
0120         void newLog(const QString &message);
0121         void newImage(const QSharedPointer<FITSData> &data);
0122         void newFrame(const QSharedPointer<FITSView> &view);
0123         // Settings
0124         void settingsUpdated(const QVariantMap &settings);
0125         void trainChanged();
0126 
0127     public slots:
0128         void processNewImage(SequenceJob *job, const QSharedPointer<FITSData> &data);
0129         void updateProperty(INDI::Property prop);
0130         void loadIndexInView(int row);
0131         void clearRow(int row = -1);
0132 
0133 
0134     private slots:
0135         void clearAll();
0136         void clearExpired();
0137         void openDarksFolder();
0138         void saveDefectMap();
0139         void setCompleted();
0140 
0141 
0142     private:
0143         explicit DarkLibrary(QWidget *parent);
0144         ~DarkLibrary() override;
0145 
0146         static DarkLibrary *_DarkLibrary;
0147 
0148         ////////////////////////////////////////////////////////////////////////////////////////////////
0149         /// Dark Frames Functions
0150         ////////////////////////////////////////////////////////////////////////////////////////////////
0151         /**
0152          * @brief countDarkTotalTime Given current settings, count how many minutes
0153          * are required to complete all the darks.
0154          */
0155         void countDarkTotalTime();
0156 
0157         /**
0158          * @brief generateDarkJobs Check the user frame parameters in the Darks tab and generate the corresponding
0159          * capture jobs. Populate capture module with the dark jobs.
0160          */
0161         void generateDarkJobs();
0162 
0163         /**
0164          * @brief execute Start executing the dark jobs in capture module.
0165          */
0166         void execute();
0167 
0168         /**
0169          * @brief generateMasterFrameHelper Calls templated generateMasterFrame with the correct data type.
0170          * @param data Passed dark frame data to generateMasterFrame
0171          * @param metadata passed metadata to generateMasterFrame
0172          */
0173         void generateMasterFrame(const QSharedPointer<FITSData> &data, const QJsonObject &metadata);
0174 
0175         /**
0176          * @brief generateMasterFrame After data aggregation is done, the selected stacking algorithm is applied and the master dark
0177          * frame is saved to disk and user database along with the metadata.
0178          * @param data last used data. This is not used for reading, but to simply apply the algorithm to the FITSData buffer
0179          * and then save it to disk.
0180          * @param metadata information on frame to help in the stacking process.
0181          */
0182         template <typename T>  void generateMasterFrameInternal(const QSharedPointer<FITSData> &data, const QJsonObject &metadata);
0183 
0184         /**
0185          * @brief aggregateHelper Calls tempelated aggregate function with the appropiate data type.
0186          * @param data Dark frame data to pass on to aggregate function.
0187          */
0188         void aggregate(const QSharedPointer<FITSData> &data);
0189 
0190         /**
0191          * @brief aggregate Aggregate the data as per the selected algorithm. Each time a new dark frame is received, this function
0192          * adds the frame data to the dark buffer.
0193          * @param data Dark frame data.
0194          */
0195         template <typename T> void aggregateInternal(const QSharedPointer<FITSData> &data);
0196 
0197         /**
0198          * @brief cacheDarkFrameFromFile Load dark frame from disk and saves it in the local dark frames cache
0199          * @param filename path of dark frame to load
0200          * @return True if file is successfully loaded, false otherwise.
0201          */
0202         bool cacheDarkFrameFromFile(const QString &filename);
0203 
0204 
0205         ////////////////////////////////////////////////////////////////////////////////////////////////
0206         /// Misc Functions
0207         ////////////////////////////////////////////////////////////////////////////////////////////////
0208         void initView();
0209         void setCaptureState(CaptureState state);
0210         void reloadDarksFromDatabase();
0211         void loadCurrentMasterDefectMap();
0212         void clearBuffers();
0213 
0214         ////////////////////////////////////////////////////////////////////////////////////////////////
0215         /// Camera Functions
0216         ////////////////////////////////////////////////////////////////////////////////////////////////
0217         double getGain();
0218 
0219         ////////////////////////////////////////////////////////////////////////////////////////////////
0220         /// Optical Train
0221         ////////////////////////////////////////////////////////////////////////////////////////////////
0222         void setupOpticalTrainManager();
0223         void refreshOpticalTrain();
0224 
0225         ////////////////////////////////////////////////////////////////////////////////////////////////
0226         /// Defect Map Functions
0227         ////////////////////////////////////////////////////////////////////////////////////////////////
0228         void refreshDefectMastersList(const QString &camera);
0229         void loadCurrentMasterDark(const QString &camera, int masterIndex = -1);
0230         void populateMasterMetedata();
0231         /**
0232          * @brief cacheDefectMapFromFile Load defect map from disk and saves it in the local defect maps cache
0233          * @param key dark file name that is used as the key in the defect map cache
0234          * @param filename path of dark frame to load
0235          * @return True if file is successfully loaded, false otherwise.
0236          */
0237         bool cacheDefectMapFromFile(const QString &key, const QString &filename);
0238 
0239         ////////////////////////////////////////////////////////////////////
0240         /// Settings
0241         ////////////////////////////////////////////////////////////////////
0242 
0243         /**
0244          * @brief Connect GUI elements to sync settings once updated.
0245          */
0246         void connectSettings();
0247         /**
0248          * @brief Stop updating settings when GUI elements are updated.
0249          */
0250         void disconnectSettings();
0251         /**
0252          * @brief loadSettings Load setting from Options and set them accordingly.
0253          */
0254         void loadGlobalSettings();
0255 
0256         /**
0257          * @brief syncSettings When checkboxes, comboboxes, or spin boxes are updated, save their values in the
0258          * global and per-train settings.
0259          */
0260         void syncSettings();
0261 
0262         /**
0263          * @brief syncControl Sync setting to widget. The value depends on the widget type.
0264          * @param settings Map of all settings
0265          * @param key name of widget to sync
0266          * @param widget pointer of widget to set
0267          * @return True if sync successful, false otherwise
0268          */
0269         bool syncControl(const QVariantMap &settings, const QString &key, QWidget * widget);
0270 
0271         ////////////////////////////////////////////////////////////////////////////////////////////////
0272         /// Member Variables
0273         ////////////////////////////////////////////////////////////////////////////////////////////////
0274 
0275         QList<QVariantMap> m_DarkFramesDatabaseList;
0276         QMap<QString, QSharedPointer<FITSData>> m_CachedDarkFrames;
0277         QMap<QString, QSharedPointer<DefectMap>> m_CachedDefectMaps;
0278 
0279         ISD::Camera *m_Camera {nullptr};
0280         ISD::CameraChip *m_TargetChip {nullptr};
0281         bool m_UseGuideHead {false};
0282         double GainSpinSpecialValue { INVALID_VALUE };
0283 
0284         Capture *m_CaptureModule {nullptr};
0285         QSqlTableModel *darkFramesModel = nullptr;
0286         QSortFilterProxyModel *sortFilter = nullptr;
0287 
0288         std::vector<uint32_t> m_DarkMasterBuffer;
0289         uint32_t m_DarkImagesCounter {0};
0290         bool m_RememberFITSViewer {true};
0291         bool m_RememberSummaryView {true};
0292         bool m_JobsGenerated {false};
0293         QJsonObject m_PresetSettings, m_FileSettings;
0294         QString m_DefectMapFilename, m_MasterDarkFrameFilename;        
0295         QSharedPointer<DarkView> m_DarkView;
0296         QPointer<QStatusBar> m_StatusBar;
0297         QPointer<QLabel> m_StatusLabel, m_FileLabel;
0298         QSharedPointer<DefectMap> m_CurrentDefectMap;
0299         QSharedPointer<FITSData> m_CurrentDarkFrame;
0300         QFutureWatcher<bool> m_DarkFrameFutureWatcher;
0301 
0302         // Settings
0303         QVariantMap m_Settings;
0304         QVariantMap m_GlobalSettings;
0305 
0306         // Do not add to cache if system memory falls below 250MB.
0307         static constexpr uint16_t CACHE_MEMORY_LIMIT {250};
0308 };
0309 }