1 /*******************************************************************************
2  Copyright(c) 2010-2018 Jasem Mutlaq. All rights reserved.
3 
4  Copyright(c) 2010, 2011 Gerry Rozema. All rights reserved.
5 
6  Rapid Guide support added by CloudMakers, s. r. o.
7  Copyright(c) 2013 CloudMakers, s. r. o. All rights reserved.
8 
9  Star detection algorithm is based on PHD Guiding by Craig Stark
10  Copyright (c) 2006-2010 Craig Stark. All rights reserved.
11 
12  This library is free software; you can redistribute it and/or
13  modify it under the terms of the GNU Library General Public
14  License version 2 as published by the Free Software Foundation.
15 
16  This library is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  Library General Public License for more details.
20 
21  You should have received a copy of the GNU Library General Public License
22  along with this library; see the file COPYING.LIB.  If not, write to
23  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  Boston, MA 02110-1301, USA.
25 *******************************************************************************/
26 
27 #pragma once
28 
29 #include "indiccdchip.h"
30 #include "defaultdevice.h"
31 #include "indiguiderinterface.h"
32 #include "indipropertynumber.h"
33 #include "inditimer.h"
34 #include "indielapsedtimer.h"
35 #include "dsp/manager.h"
36 #include "stream/streammanager.h"
37 
38 #ifdef HAVE_WEBSOCKET
39 #include "indiwsserver.h"
40 #endif
41 
42 #include <fitsio.h>
43 
44 #include <memory>
45 #include <cstring>
46 #include <chrono>
47 #include <stdint.h>
48 #include <mutex>
49 #include <thread>
50 
51 //JM 2019-01-17: Disabled until further notice
52 //#define WITH_EXPOSURE_LOOPING
53 
54 extern const char * IMAGE_SETTINGS_TAB;
55 extern const char * IMAGE_INFO_TAB;
56 extern const char * GUIDE_HEAD_TAB;
57 //extern const char * RAPIDGUIDE_TAB;
58 
59 namespace DSP
60 {
61 class Manager;
62 }
63 namespace INDI
64 {
65 
66 class StreamManager;
67 
68 /**
69  * \class CCD
70  * \brief Class to provide general functionality of CCD cameras with a single CCD sensor, or a
71  * primary CCD sensor in addition to a secondary CCD guide head.
72  *
73  * The CCD capabilities must be set to select which features are exposed to the clients.
74  * SetCCDCapability() is typically set in the constructor or initProperties(), but can also be
75  * called after connection is established with the CCD, but must be called /em before returning
76  * true in Connect().
77  *
78  * It also implements the interface to perform guiding. The class enable the ability to \e snoop
79  * on telescope equatorial coordinates and record them in the FITS file before upload. It also
80  * snoops Sky-Quality-Meter devices to record sky quality in units of Magnitudes-Per-Arcsecond-Squared
81  * (MPASS) in the FITS header.
82  *
83  * Support for streaming and recording is available and is handled by the StreamManager class.
84  *
85  * Developers need to subclass INDI::CCD to implement any driver for CCD cameras within INDI.
86  *
87  * Data binary transfers are supported using two methods:
88  * # INDI BLOBs: This is the and recommended default configuration.
89  * # Websockets: This requires INDI to be built with websocket support. There is marginal
90  * improvement in throughput with Websockets when compared with INDI base64 BLOB encoding.
91  * It requires the client to explicitly support websockets. It is not recommended to use this
92  * approach unless for the most demanding and FPS sensitive tasks.
93  *
94  * INDI::CCD and INDI::StreamManager both upload frames asynchrounously in a worker thread.
95  * The CCD Buffer data is protected by the ccdBufferLock mutex. When reading the camera data
96  * and writing to the buffer, it must be first locked by the mutex. After the write is complete
97  * release the lock. For example:
98  *
99  * \code{.cpp}
100  * std::unique_lock<std::mutex> guard(ccdBufferLock);
101  * get_ccd_frame(PrimaryCCD.getFrameBuffer);
102  * guard.unlock();
103  * ExposureComplete();
104  * \endcode
105  *
106  * Similiary, before calling Streamer->newFrame, the buffer needs to be protected in a similiar fashion using
107  * the same ccdBufferLock mutex.
108  *
109  * \example CCD Simulator
110  * \version 1.1
111  * \author Jasem Mutlaq
112  * \author Gerry Rozema
113  *
114  */
115 class CCD : public DefaultDevice, GuiderInterface
116 {
117     public:
118         CCD();
119         virtual ~CCD();
120 
121         enum
122         {
123             CCD_CAN_BIN        = 1 << 0, /*!< Does the CCD support binning?  */
124             CCD_CAN_SUBFRAME   = 1 << 1, /*!< Does the CCD support setting ROI?  */
125             CCD_CAN_ABORT      = 1 << 2, /*!< Can the CCD exposure be aborted?  */
126             CCD_HAS_GUIDE_HEAD = 1 << 3, /*!< Does the CCD have a guide head?  */
127             CCD_HAS_ST4_PORT   = 1 << 4, /*!< Does the CCD have an ST4 port?  */
128             CCD_HAS_SHUTTER    = 1 << 5, /*!< Does the CCD have a mechanical shutter?  */
129             CCD_HAS_COOLER     = 1 << 6, /*!< Does the CCD have a cooler and temperature control?  */
130             CCD_HAS_BAYER      = 1 << 7, /*!< Does the CCD send color data in bayer format?  */
131             CCD_HAS_STREAMING  = 1 << 8, /*!< Does the CCD support live video streaming?  */
132             CCD_HAS_WEB_SOCKET = 1 << 9, /*!< Does the CCD support web socket transfers?  */
133             CCD_HAS_DSP        = 1 << 10 /*!< Does the CCD support image processing?  */
134         } CCDCapability;
135 
136         typedef enum { UPLOAD_CLIENT, UPLOAD_LOCAL, UPLOAD_BOTH } CCD_UPLOAD_MODE;
137 
138         virtual bool initProperties() override;
139         virtual bool updateProperties() override;
140         virtual void ISGetProperties(const char * dev) override;
141         virtual bool ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n) override;
142         virtual bool ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n) override;
143         virtual bool ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n) override;
144         virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[],
145                                char *names[], int n) override;
146         virtual bool ISSnoopDevice(XMLEle * root) override;
147 
148         static void wsThreadHelper(void * context);
149 
150         /////////////////////////////////////////////////////////////////////////////
151         /// Group Names
152         /////////////////////////////////////////////////////////////////////////////
153         static constexpr const char *GUIDE_CONTROL_TAB  = "Guider Control";
154         static constexpr const char * WCS_TAB = "WCS";
155 
156 
157     protected:
158         /**
159          * @brief GetCCDCapability returns the CCD capabilities.
160          */
GetCCDCapability()161         uint32_t GetCCDCapability() const
162         {
163             return capability;
164         }
165 
166         /**
167          * @brief SetCCDCapability Set the CCD capabilities. Al fields must be initilized.
168          * @param cap pointer to CCDCapability struct.
169          */
170         void SetCCDCapability(uint32_t cap);
171 
172         /**
173          * @return True if CCD can abort exposure. False otherwise.
174          */
CanAbort()175         bool CanAbort()
176         {
177             return capability & CCD_CAN_ABORT;
178         }
179 
180         /**
181          * @return True if CCD supports binning. False otherwise.
182          */
CanBin()183         bool CanBin()
184         {
185             return capability & CCD_CAN_BIN;
186         }
187 
188         /**
189          * @return True if CCD supports subframing. False otherwise.
190          */
CanSubFrame()191         bool CanSubFrame()
192         {
193             return capability & CCD_CAN_SUBFRAME;
194         }
195 
196         /**
197          * @return True if CCD has guide head. False otherwise.
198          */
HasGuideHead()199         bool HasGuideHead()
200         {
201             return capability & CCD_HAS_GUIDE_HEAD;
202         }
203 
204         /**
205          * @return True if CCD has mechanical or electronic shutter. False otherwise.
206          */
HasShutter()207         bool HasShutter()
208         {
209             return capability & CCD_HAS_SHUTTER;
210         }
211 
212         /**
213          * @return True if CCD has ST4 port for guiding. False otherwise.
214          */
HasST4Port()215         bool HasST4Port()
216         {
217             return capability & CCD_HAS_ST4_PORT;
218         }
219 
220         /**
221          * @return True if CCD has cooler and temperature can be controlled. False otherwise.
222          */
HasCooler()223         bool HasCooler()
224         {
225             return capability & CCD_HAS_COOLER;
226         }
227 
228         /**
229          * @return True if CCD sends image data in bayer format. False otherwise.
230          */
HasBayer()231         bool HasBayer()
232         {
233             return capability & CCD_HAS_BAYER;
234         }
235 
236         /**
237          * @return  True if the CCD supports live video streaming. False otherwise.
238          */
HasStreaming()239         bool HasStreaming()
240         {
241             if (capability & CCD_HAS_STREAMING)
242             {
243                 if(Streamer.get() == nullptr)
244                 {
245                     Streamer.reset(new StreamManager(this));
246                     Streamer->initProperties();
247                 }
248                 return true;
249             }
250             return false;
251         }
252 
253         /**
254          * @return  True if the CCD supports native Web Socket transfers. False otherwise.
255          */
HasWebSocket()256         bool HasWebSocket()
257         {
258             return capability & CCD_HAS_WEB_SOCKET;
259         }
260 
261         /**
262          * @return  True if the CCD wants DSP processing. False otherwise.
263          */
HasDSP()264         bool HasDSP()
265         {
266             if (capability & CCD_HAS_DSP)
267             {
268                 if(DSP.get() == nullptr)
269                 {
270                     DSP.reset(new DSP::Manager(this));
271                 }
272                 return true;
273             }
274             return false;
275         }
276 
277         /**
278          * @brief Set CCD temperature
279          * @param temperature CCD temperature in degrees celcius.
280          * @return 0 or 1 if setting the temperature call to the hardware is successful. -1 if an
281          * error is encountered.
282          * Return 0 if setting the temperature to the requested value takes time.
283          * Return 1 if setting the temperature to the requested value is complete.
284          * \note Upon returning 0, the property becomes BUSY. Once the temperature reaches the requested
285          * value, change the state to OK.
286          * \note This function is not implemented in CCD, it must be implemented in the child class
287          */
288         virtual int SetTemperature(double temperature);
289 
290         /**
291          * \brief Start exposing primary CCD chip
292          * \param duration Duration in seconds
293          * \return true if OK and exposure will take some time to complete, false on error.
294          * \note This function is not implemented in CCD, it must be implemented in the child class
295          */
296         virtual bool StartExposure(float duration);
297 
298         /**
299          * \brief Uploads target Chip exposed buffer as FITS to the client. Dervied classes should class
300          * this function when an exposure is complete.
301          * @param targetChip chip that contains upload image data
302          * \note This function is not implemented in CCD, it must be implemented in the child class
303          */
304         virtual bool ExposureComplete(CCDChip * targetChip);
305 
306         /**
307          * \brief Abort ongoing exposure
308          * \return true is abort is successful, false otherwise.
309          * \note This function is not implemented in CCD, it must be implemented in the child class
310          */
311         virtual bool AbortExposure();
312 
313         /**
314          * \brief Start exposing guide CCD chip
315          * \param duration Duration in seconds
316          * \return true if OK and exposure will take some time to complete, false on error.
317          * \note This function is not implemented in CCD, it must be implemented in the child class
318          */
319         virtual bool StartGuideExposure(float duration);
320 
321         /**
322          * \brief Abort ongoing exposure
323          * \return true is abort is successful, false otherwise.
324          * \note This function is not implemented in CCD, it must be implemented in the child class
325          */
326         virtual bool AbortGuideExposure();
327 
328         /**
329          * \brief CCD calls this function when CCD Frame dimension needs to be updated in the
330          * hardware. Derived classes should implement this function
331          * \param x Subframe X coordinate in pixels.
332          * \param y Subframe Y coordinate in pixels.
333          * \param w Subframe width in pixels.
334          * \param h Subframe height in pixels.
335          * \note (0,0) is defined as most left, top pixel in the subframe.
336          * \return true is CCD chip update is successful, false otherwise.
337          * \note This function is not implemented in CCD, it must be implemented in the child class
338          */
339         virtual bool UpdateCCDFrame(int x, int y, int w, int h);
340 
341         /**
342          * \brief CCD calls this function when Guide head frame dimension is updated by the
343          * client. Derived classes should implement this function
344          * \param x Subframe X coordinate in pixels.
345          * \param y Subframe Y coordinate in pixels.
346          * \param w Subframe width in pixels.
347          * \param h Subframe height in pixels.
348          * \note (0,0) is defined as most left, top pixel in the subframe.
349          * \return true is CCD chip update is successful, false otherwise.
350          * \note This function is not implemented in CCD, it must be implemented in the child class
351          */
352         virtual bool UpdateGuiderFrame(int x, int y, int w, int h);
353 
354         /**
355          * \brief CCD calls this function when CCD Binning needs to be updated in the hardware.
356          * Derived classes should implement this function
357          * \param hor Horizontal binning.
358          * \param ver Vertical binning.
359          * \return true is CCD chip update is successful, false otherwise.
360          * \note This function is not implemented in CCD, it must be implemented in the child class
361          */
362         virtual bool UpdateCCDBin(int hor, int ver);
363 
364         /**
365          * \brief CCD calls this function when Guide head binning is updated by the client.
366          * Derived classes should implement this function
367          * \param hor Horizontal binning.
368          * \param ver Vertical binning.
369          * \return true is CCD chip update is successful, false otherwise.
370          * \note This function is not implemented in CCD, it must be implemented in the child class
371          */
372         virtual bool UpdateGuiderBin(int hor, int ver);
373 
374         /**
375          * \brief CCD calls this function when CCD frame type needs to be updated in the hardware.
376          * \param fType Frame type
377          * \return true is CCD chip update is successful, false otherwise.
378          * \note It is \e not mandatory to implement this function in the child class. The CCD hardware
379          * layer may either set the frame type when this function is called, or (optionally) before an
380          * exposure is started.
381          */
382         virtual bool UpdateCCDFrameType(CCDChip::CCD_FRAME fType);
383 
384         /**
385          * \brief CCD calls this function when client upload mode switch is updated.
386          * \param mode upload mode. UPLOAD_CLIENT only sends the upload the client application. UPLOAD_BOTH saves the frame and uploads it to the client. UPLOAD_LOCAL only saves
387          * the frame locally.
388          * \return true if mode is changed successfully, false otherwise.
389          * \note By default this function is implemented in the base class and returns true. Override if necessary.
390          */
UpdateCCDUploadMode(CCD_UPLOAD_MODE mode)391         virtual bool UpdateCCDUploadMode(CCD_UPLOAD_MODE mode)
392         {
393             INDI_UNUSED(mode);
394             return true;
395         }
396 
397         /**
398          * \brief CCD calls this function when Guide frame type is updated by the client.
399          * \param fType Frame type
400          * \return true is CCD chip update is successful, false otherwise.
401          * \note It is \e not mandatory to implement this function in the child class. The CCD hardware
402          * layer may either set the frame type when this function is called, or (optionally) before an
403          * exposure is started.
404          */
405         virtual bool UpdateGuiderFrameType(CCDChip::CCD_FRAME fType);
406 
407         /**
408          * \brief Setup CCD paramters for primary CCD. Child classes call this function to update
409          * CCD parameters
410          * \param x Frame X coordinates in pixels.
411          * \param y Frame Y coordinates in pixels.
412          * \param bpp Bits Per Pixels.
413          * \param xf X pixel size in microns.
414          * \param yf Y pixel size in microns.
415          */
416         virtual void SetCCDParams(int x, int y, int bpp, float xf, float yf);
417 
418         /**
419          * \brief Setup CCD paramters for guide head CCD. Child classes call this function to update
420          * CCD parameters
421          * \param x Frame X coordinates in pixels.
422          * \param y Frame Y coordinates in pixels.
423          * \param bpp Bits Per Pixels.
424          * \param xf X pixel size in microns.
425          * \param yf Y pixel size in microns.
426          */
427         virtual void SetGuiderParams(int x, int y, int bpp, float xf, float yf);
428 
429         /**
430          * \brief Guide northward for ms milliseconds
431          * \param ms Duration in milliseconds.
432          * \note This function is not implemented in CCD, it must be implemented in the child class
433          * \return True if successful, false otherwise.
434          */
435         virtual IPState GuideNorth(uint32_t ms) override;
436 
437         /**
438          * \brief Guide southward for ms milliseconds
439          * \param ms Duration in milliseconds.
440          * \note This function is not implemented in CCD, it must be implemented in the child class
441          * \return 0 if successful, -1 otherwise.
442          */
443         virtual IPState GuideSouth(uint32_t ms) override;
444 
445         /**
446          * \brief Guide easward for ms milliseconds
447          * \param ms Duration in milliseconds.
448          * \note This function is not implemented in CCD, it must be implemented in the child class
449          * \return 0 if successful, -1 otherwise.
450          */
451         virtual IPState GuideEast(uint32_t ms) override;
452 
453         /**
454          * \brief Guide westward for ms milliseconds
455          * \param ms Duration in milliseconds.
456          * \note This function is not implemented in CCD, it must be implemented in the child class
457          * \return 0 if successful, -1 otherwise.
458          */
459         virtual IPState GuideWest(uint32_t ms) override;
460 
461         /**
462          * @brief StartStreaming Start live video streaming
463          * @return True if successful, false otherwise.
464          */
465         virtual bool StartStreaming();
466 
467         /**
468          * @brief StopStreaming Stop live video streaming
469          * @return True if successful, false otherwise.
470          */
471         virtual bool StopStreaming();
472 
473         /**
474          * \brief Add FITS keywords to a fits file
475          * \param fptr pointer to a valid FITS file.
476          * \param targetChip The target chip to extract the keywords from.
477          * \note In additional to the standard FITS keywords, this function write the following
478          * keywords the FITS file:
479          * <ul>
480          * <li>EXPTIME: Total Exposure Time (s)</li>
481          * <li>DARKTIME (if applicable): Total Exposure Time (s)</li>
482          * <li>PIXSIZE1: Pixel Size 1 (microns)</li>
483          * <li>PIXSIZE2: Pixel Size 2 (microns)</li>
484          * <li>BINNING: Binning HOR x VER</li>
485          * <li>FRAME: Frame Type</li>
486          * <li>DATAMIN: Minimum value</li>
487          * <li>DATAMAX: Maximum value</li>
488          * <li>INSTRUME: CCD Name</li>
489          * <li>DATE-OBS: UTC start date of observation</li>
490          * </ul>
491          *
492          * To add additional information, override this function in the child class and ensure to call
493          * CCD::addFITSKeywords.
494          */
495         virtual void addFITSKeywords(fitsfile * fptr, CCDChip * targetChip);
496 
497         /** A function to just remove GCC warnings about deprecated conversion */
498         void fits_update_key_s(fitsfile * fptr, int type, std::string name, void * p, std::string explanation, int * status);
499 
500         /**
501          * @brief activeDevicesUpdated Inform children that ActiveDevices property was updated so they can
502          * snoop on the updated devices if desired.
503          */
activeDevicesUpdated()504         virtual void activeDevicesUpdated() {}
505 
506         /**
507          * @brief saveConfigItems Save configuration items in XML file.
508          * @param fp pointer to file to write to
509          * @return True if successful, false otherwise
510          */
511         virtual bool saveConfigItems(FILE * fp) override;
512 
513         /**
514          * @brief GuideComplete Signal guide pulse completion
515          * @param axis which axis the guide pulse was acting on
516          */
517         virtual void GuideComplete(INDI_EQ_AXIS axis) override;
518 
519         /**
520          * @brief checkTemperatureTarget Checks the current temperature against target temperature and calculates
521          * the next required temperature if there is a ramp. If the current temperature is within threshold of
522          * target temperature, it sets the state as OK.
523          */
524         virtual void checkTemperatureTarget();
525 
526 
527         // Epoch Position
528         double RA, Dec;
529 
530         // pier side, read from mount if available, set to -1 if not available
531         int pierSide;       // West = 0, East =1. No enum available
532 
533         // J2000 Position
534         double J2000RA;
535         double J2000DE;
536 
537         double primaryFocalLength, primaryAperture, guiderFocalLength, guiderAperture;
538         bool InExposure;
539         bool InGuideExposure;
540         //bool RapidGuideEnabled;
541         //bool GuiderRapidGuideEnabled;
542 
543         bool AutoLoop;
544         bool GuiderAutoLoop;
545         bool SendImage;
546         bool GuiderSendImage;
547         bool ShowMarker;
548         bool GuiderShowMarker;
549 
550         double ExposureTime;
551         double GuiderExposureTime;
552 
553         // Sky Quality
554         double MPSAS;
555 
556         // Rotator Angle
557         double RotatorAngle;
558 
559         // JJ ed 2019-12-10 current focuser position
560         long FocuserPos;
561         double FocuserTemp;
562 
563         // Airmass
564         double Airmass;
565         double Latitude;
566         double Longitude;
567         double Azimuth;
568         double Altitude;
569 
570         // Temperature Control
571         double m_TargetTemperature {0};
572         INDI::Timer m_TemperatureCheckTimer;
573         INDI::ElapsedTimer m_TemperatureElapsedTimer;
574 
575         // Threading
576         std::mutex ccdBufferLock;
577 
578         std::vector<std::string> FilterNames;
579         int CurrentFilterSlot {-1};
580 
581         std::unique_ptr<StreamManager> Streamer;
582         std::unique_ptr<DSP::Manager> DSP;
583         CCDChip PrimaryCCD;
584         CCDChip GuideCCD;
585 
586         ///////////////////////////////////////////////////////////////////////////////
587         /// Properties
588         ///////////////////////////////////////////////////////////////////////////////
589 
590 
591         /**
592          * @brief EqNP Snoop property to read the equatorial coordinates of the mount.
593          * ActiveDeviceTP defines snoop devices and the driver listens to this property emitted
594          * by the mount driver if specified. It is important to generate a proper FITS header.
595          */
596         INumberVectorProperty EqNP;
597         INumber EqN[2];
598 
599         /**
600          * @brief ActiveDeviceTP defines 4 devices the camera driver can listen to (snoop) for
601          * properties of interest so that it can generate a proper FITS header.
602          * + **Mount**: Listens for equatorial coordinates in JNow epoch.
603          * + **Rotator**: Listens for Rotator Absolute Rotation Angle (E of N) in degrees.
604          * + **Filter Wheel**: Listens for FILTER_SLOT and FILTER_NAME properties.
605          * + **SQM**: Listens for sky quality meter magnitude.
606          */
607         ITextVectorProperty ActiveDeviceTP;
608 
609         // JJ ed 2019-12-10
610         IText ActiveDeviceT[5] {};
611         enum
612         {
613             ACTIVE_TELESCOPE,
614             ACTIVE_ROTATOR,
615             ACTIVE_FOCUSER,
616             ACTIVE_FILTER,
617             ACTIVE_SKYQUALITY
618         };
619 
620         /**
621          * @brief TemperatureNP Camera Temperature in Celcius.
622          */
623         INumberVectorProperty TemperatureNP;
624         INumber TemperatureN[1];
625 
626         /**
627          * @brief Temperature Ramp in C/Min with configurable threshold
628         */
629         INDI::PropertyNumber TemperatureRampNP {2};
630         enum
631         {
632             RAMP_SLOPE,
633             RAMP_THRESHOLD
634         };
635 
636         /**
637          *@brief BayerTP Bayer pattern offset and type
638          */
639         ITextVectorProperty BayerTP;
640         IText BayerT[3] {};
641 
642         /**
643          *@brief FileNameTP File name of locally-saved images. By default, images are uploaded to the client
644          * but when upload option is set to either @a Both or @a Local, then they are saved on the local disk with
645          * this name.
646          */
647         ITextVectorProperty FileNameTP;
648         IText FileNameT[1] {};
649 
650         ISwitch UploadS[3];
651         ISwitchVectorProperty UploadSP;
652 
653         IText UploadSettingsT[2] {};
654         ITextVectorProperty UploadSettingsTP;
655         enum
656         {
657             UPLOAD_DIR,
658             UPLOAD_PREFIX
659         };
660 
661         ISwitch TelescopeTypeS[2];
662         ISwitchVectorProperty TelescopeTypeSP;
663         enum
664         {
665             TELESCOPE_PRIMARY,
666             TELESCOPE_GUIDE
667         };
668 
669         // Websocket Support
670         ISwitch WebSocketS[2];
671         ISwitchVectorProperty WebSocketSP;
672         enum
673         {
674             WEBSOCKET_ENABLED,
675             WEBSOCKET_DISABLED,
676         };
677 
678 
679         // Websocket Settings
680         INumber WebSocketSettingsN[1];
681         INumberVectorProperty WebSocketSettingsNP;
682         enum
683         {
684             WS_SETTINGS_PORT,
685         };
686 
687         // WCS
688         ISwitch WorldCoordS[2];
689         ISwitchVectorProperty WorldCoordSP;
690 
691         // WCS CCD Rotation
692         INumber CCDRotationN[1];
693         INumberVectorProperty CCDRotationNP;
694 
695 #ifdef WITH_EXPOSURE_LOOPING
696         // Exposure Looping
697         ISwitch ExposureLoopS[2];
698         ISwitchVectorProperty ExposureLoopSP;
699         enum
700         {
701             EXPOSURE_LOOP_ON,
702             EXPOSURE_LOOP_OFF
703         };
704 
705         // Exposure Looping Count
706         INumber ExposureLoopCountN[1];
707         INumberVectorProperty ExposureLoopCountNP;
708         double uploadTime = { 0 };
709         std::chrono::system_clock::time_point exposureLoopStartup;
710 #endif
711 
712         // FITS Header
713         IText FITSHeaderT[2] {};
714         ITextVectorProperty FITSHeaderTP;
715         enum
716         {
717             FITS_OBSERVER,
718             FITS_OBJECT
719         };
720 
721     private:
722         uint32_t capability;
723 
724         bool m_ValidCCDRotation;
725 
726         ///////////////////////////////////////////////////////////////////////////////
727         /// Utility Functions
728         ///////////////////////////////////////////////////////////////////////////////
729         bool uploadFile(CCDChip * targetChip, const void * fitsData, size_t totalBytes, bool sendImage, bool saveImage);
730         void getMinMax(double * min, double * max, CCDChip * targetChip);
731         int getFileIndex(const char * dir, const char * prefix, const char * ext);
732         bool ExposureCompletePrivate(CCDChip * targetChip);
733 
734         // Threading for Websocket
735 #ifdef HAVE_WEBSOCKET
736         std::thread wsThread;
737         void wsThreadEntry();
738         INDIWSServer wsServer;
739 #endif
740 
741         /////////////////////////////////////////////////////////////////////////////
742         /// Misc.
743         /////////////////////////////////////////////////////////////////////////////
744         friend class StreamManager;
745         friend class StreamManagerPrivate;
746 };
747 }
748