1 /*
2  *  camera.cpp
3  *  PHD Guiding
4  *
5  *  Created by Craig Stark.
6  *  Copyright (c) 2006-2010 Craig Stark.
7  *  All rights reserved.
8  *
9  *  This source code is distributed under the following "BSD" license
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions are met:
12  *    Redistributions of source code must retain the above copyright notice,
13  *     this list of conditions and the following disclaimer.
14  *    Redistributions in binary form must reproduce the above copyright notice,
15  *     this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *    Neither the name of Craig Stark, Stark Labs nor the names of its
18  *     contributors may be used to endorse or promote products derived from
19  *     this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  *  POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 // General camera routines not specific to any one cam
36 
37 #include "phd.h"
38 
39 #include "camera.h"
40 #include "gear_simulator.h"
41 
42 #include <wx/stdpaths.h>
43 
44 static const int DefaultGuideCameraGain = 95;
45 static const int DefaultGuideCameraTimeoutMs = 15000;
46 static const bool DefaultUseSubframes = false;
47 static const int DefaultReadDelay = 150;
48 
49 const double GuideCamera::UnknownPixelSize = 0.0;
50 
51 wxSize UNDEFINED_FRAME_SIZE = wxSize(0, 0);
52 
53 #if defined (ATIK16)
54 # include "cam_atik16.h"
55 #endif
56 
57 #if defined (IOPTRON_CAMERA)
58 # include "cam_ioptron.h"
59 #endif
60 
61 #if defined (LE_SERIAL_CAMERA)
62 # include "cam_LESerialWebcam.h"
63 #endif
64 
65 #if defined (LE_PARALLEL_CAMERA)
66 # include "cam_LEParallelwebcam.h"
67 #endif
68 
69 #if defined (LE_LXUSB_CAMERA)
70 # include "cam_LELXUSBwebcam.h"
71 #endif
72 
73 #if defined (SAC42)
74 # include "cam_SAC42.h"
75 #endif
76 
77 #if defined (QGUIDE)
78 # include "cam_qguide.h"
79 #endif
80 
81 #if defined (CAM_QHY5)
82 # include "cam_qhy5.h"
83 #endif
84 
85 #if defined (QHY_CAMERA)
86 # include "cam_qhy.h"
87 #endif
88 
89 #if defined (SVB_CAMERA)
90 # include "cam_svb.h"
91 #endif
92 
93 #if defined (ZWO_ASI)
94 # include "cam_zwo.h"
95 #endif
96 
97 #if defined (TOUPTEK_CAMERA)
98 # include "cam_touptek.h"
99 #endif
100 
101 #if defined (SKYRAIDER_CAMERA)
102 # include "cam_skyraider.h"
103 #endif
104 
105 #if defined (ALTAIR)
106 # include "cam_altair.h"
107 #endif
108 
109 #if defined (ORION_DSCI)
110 # include "cam_StarShootDSCI.h"
111 #endif
112 
113 #if defined (OS_PL130)
114 # include "cam_OSPL130.h"
115 #endif
116 
117 #if defined (VFW_CAMERA)
118 # include "cam_vfw.h"
119 #endif
120 
121 #if defined (OPENCV_CAMERA)
122 # include "cam_opencv.h"
123 #endif
124 
125 #if defined (WDM_CAMERA)
126 # include "cam_wdm.h"
127 #endif
128 
129 #if defined (STARFISH_CAMERA)
130 # include "cam_starfish.h"
131 #endif
132 
133 #if defined (SXV)
134 # include "cam_sxv.h"
135 #endif
136 
137 #if defined (SBIG)
138 # include "cam_sbig.h"
139 #endif
140 
141 #if defined (NEB_SBIG)
142 # include "cam_NebSBIG.h"
143 #endif
144 
145 #if defined (FIREWIRE_CAMERA)
146 # include "cam_firewire.h"
147 #endif
148 
149 #if defined (MEADE_DSI_CAMERA)
150 # include "cam_MeadeDSI.h"
151 #endif
152 
153 #if defined (MORAVIAN_CAMERA)
154 # include "cam_moravian.h"
155 #endif
156 
157 #if defined (SSAG)
158 # include "cam_ssag.h"
159 #endif
160 
161 #if defined (OPENSSAG_CAMERA)
162 # include "cam_openssag.h"
163 #endif
164 
165 #if defined (KWIQGUIDER_CAMERA)
166 # include "cam_KWIQGuider.h"
167 #endif
168 
169 #if defined (SSPIAG)
170 # include "cam_sspiag.h"
171 #endif
172 
173 #if defined (INOVA_PLC)
174 # include "cam_INovaPLC.h"
175 #endif
176 
177 #if defined (ASCOM_CAMERA)
178 # include "cam_ascom.h"
179 #endif
180 
181 #if defined (INDI_CAMERA)
182 # include "cam_indi.h"
183 #endif
184 
185 #if defined(SBIGROTATOR_CAMERA)
186 # include "cam_sbigrotator.h"
187 #endif
188 
189 #if defined (V4L_CAMERA)
190 # include "cam_VIDEODEVICE.h"
191 extern "C" {
192 #include <libudev.h>
193 }
194 #endif
195 
196 const wxString GuideCamera::DEFAULT_CAMERA_ID = wxEmptyString;
197 
GetProfilePixelSize()198 double GuideCamera::GetProfilePixelSize()
199 {
200     return pConfig->Profile.GetDouble("/camera/pixelsize", UnknownPixelSize);
201 }
202 
GuideCamera()203 GuideCamera::GuideCamera()
204 {
205     Connected = false;
206     m_hasGuideOutput = false;
207     PropertyDialogType = PROPDLG_NONE;
208     HasPortNum = false;
209     HasDelayParam = false;
210     HasGainControl = false;
211     HasShutter = false;
212     ShutterClosed = false;
213     HasSubframes = false;
214     HasCooler = false;
215     FullSize = UNDEFINED_FRAME_SIZE;
216     UseSubframes = pConfig->Profile.GetBoolean("/camera/UseSubframes", DefaultUseSubframes);
217     ReadDelay = pConfig->Profile.GetInt("/camera/ReadDelay", DefaultReadDelay);
218     GuideCameraGain = pConfig->Profile.GetInt("/camera/gain", DefaultGuideCameraGain);
219     m_timeoutMs = pConfig->Profile.GetInt("/camera/TimeoutMs", DefaultGuideCameraTimeoutMs);
220     m_saturationADU = (unsigned short) wxMin(pConfig->Profile.GetInt("/camera/SaturationADU", 0), 65535);
221     m_saturationByADU = pConfig->Profile.GetBoolean("/camera/SaturationByADU", true);
222     m_pixelSize = GetProfilePixelSize();
223     MaxBinning = 1;
224     Binning = pConfig->Profile.GetInt("/camera/binning", 1);
225     CurrentDarkFrame = nullptr;
226     CurrentDefectMap = nullptr;
227 }
228 
~GuideCamera()229 GuideCamera::~GuideCamera()
230 {
231     ClearDarks();
232     ClearDefectMap();
233 }
234 
CompareNoCase(const wxString & first,const wxString & second)235 static int CompareNoCase(const wxString& first, const wxString& second)
236 {
237     return first.CmpNoCase(second);
238 }
239 
INDICamName()240 static wxString INDICamName()
241 {
242     wxString indicam = pConfig->Profile.GetString("/indi/INDIcam", wxEmptyString);
243     return indicam.empty() ? _T("INDI Camera") : wxString::Format("INDI Camera [%s]", indicam);
244 }
245 
GuideCameraList()246 wxArrayString GuideCamera::GuideCameraList()
247 {
248     wxArrayString CameraList;
249 
250     CameraList.Add(_("None"));
251 #if defined (ASCOM_CAMERA)
252     wxArrayString ascomCameras = ASCOMCameraFactory::EnumAscomCameras();
253     for (unsigned int i = 0; i < ascomCameras.Count(); i++)
254         CameraList.Add(ascomCameras[i]);
255 #endif
256 #if defined (ATIK16)
257     CameraList.Add(_T("Atik 16 series, mono"));
258     CameraList.Add(_T("Atik 16 series, color"));
259 #endif
260 #if defined (ATIK_GEN3)
261     CameraList.Add(_T("Atik Gen3, mono"));
262     CameraList.Add(_T("Atik Gen3, color"));
263 #endif
264 #if defined (QGUIDE)
265     CameraList.Add(_T("CCD Labs Q-Guider"));
266 #endif
267 #if defined (STARFISH_CAMERA)
268     CameraList.Add(_T("Fishcamp Starfish"));
269 #endif
270 #if defined (INOVA_PLC)
271     CameraList.Add(_T("i-Nova PLC-M"));
272 #endif
273 #if defined (IOPTRON_CAMERA)
274     CameraList.Add(_T("iOptron iGuider"));
275 #endif
276 #if defined (SSAG)
277     CameraList.Add(_T("StarShoot Autoguider"));
278 #endif
279 #if defined (SSPIAG)
280     CameraList.Add(_T("StarShoot Planetary Imager & Autoguider"));
281 #endif
282 #if defined (OS_PL130)
283     CameraList.Add(_T("Opticstar PL-130M"));
284     CameraList.Add(_T("Opticstar PL-130C"));
285 #endif
286 #if defined (ORION_DSCI)
287     CameraList.Add(_T("Orion StarShoot DSCI"));
288 #endif
289 #if defined (OPENSSAG_CAMERA)
290     CameraList.Add(_T("Orion StarShoot Autoguider"));
291 #endif
292 #if defined (KWIQGUIDER_CAMERA)
293     CameraList.Add(_T("KWIQGuider"));
294 #endif
295 #if defined (QGUIDE)
296     CameraList.Add(_T("MagZero MZ-5"));
297 #endif
298 #if defined (MEADE_DSI_CAMERA)
299     CameraList.Add(_T("Meade DSI I, II, or III"));
300 #endif
301 #if defined (MORAVIAN_CAMERA)
302     CameraList.Add(_T("Moravian Camera"));
303 #endif
304 #if defined (CAM_QHY5)
305     CameraList.Add(_T("QHY 5"));
306 #endif
307 #if defined (QHY_CAMERA)
308     CameraList.Add(_T("QHY Camera"));
309 #endif
310 #if defined (ALTAIR)
311 	CameraList.Add(_T("Altair Camera"));
312     CameraList.Add(_T("Altair Camera (2015/2016)"));
313 #endif
314 #if defined (ZWO_ASI)
315     CameraList.Add(_T("ZWO ASI Camera"));
316 #endif
317 #if defined (TOUPTEK_CAMERA)
318     CameraList.Add(_T("ToupTek Camera"));
319     CameraList.Add(_T("Omegon Pro Camera"));
320 #endif
321 #if defined (SKYRAIDER_CAMERA)
322     CameraList.Add(_T("MallinCam SkyRaider"));
323 #endif
324 #if defined (SAC42)
325     CameraList.Add(_T("SAC4-2"));
326 #endif
327 #if defined (SBIG)
328     CameraList.Add(_T("SBIG"));
329 #endif
330 #if defined (SBIGROTATOR_CAMERA)
331     CameraList.Add(_T("SBIG Rotator"));
332 #endif
333 #if defined (SVB_CAMERA)
334     CameraList.Add(_T("Svbony Camera"));
335 #endif
336 #if defined (SXV)
337     CameraList.Add(_T("Starlight Xpress SXV"));
338 #endif
339 #if defined (FIREWIRE_CAMERA)
340     CameraList.Add(_T("The Imaging Source (DCAM Firewire)"));
341 #endif
342 #if defined (OPENCV_CAMERA)
343     CameraList.Add(_T("OpenCV webcam 1"));
344     CameraList.Add(_T("OpenCV webcam 2"));
345 #endif
346 #if defined (WDM_CAMERA)
347     CameraList.Add(_T("Windows WDM-style webcam camera"));
348 #endif
349 #if defined (VFW_CAMERA)
350     CameraList.Add(_T("Windows VFW-style webcam camera (older & SAC8)"));
351 #endif
352 #if defined (LE_LXUSB_CAMERA)
353     CameraList.Add(_T("Long exposure LXUSB webcam"));
354 #endif
355 #if defined (LE_PARALLEL_CAMERA)
356     CameraList.Add(_T("Long exposure Parallel webcam"));
357 #endif
358 #if defined (LE_SERIAL_CAMERA)
359     CameraList.Add(_T("Long exposure Serial webcam"));
360 #endif
361 #if defined (INDI_CAMERA)
362     CameraList.Add(INDICamName());
363 #endif
364 #if defined (V4L_CAMERA)
365     if (true == Camera_VIDEODEVICE.ProbeDevices()) {
366         CameraList.Add(_T("V4L(2) Camera"));
367     }
368 #endif
369 #if defined (SIMULATOR)
370     CameraList.Add(_T("Simulator"));
371 #endif
372 
373 #if defined (NEB_SBIG)
374     CameraList.Add(_T("Guide chip on SBIG cam in Nebulosity"));
375 #endif
376 
377     CameraList.Sort(&CompareNoCase);
378 
379     return CameraList;
380 }
381 
Factory(const wxString & choice)382 GuideCamera *GuideCamera::Factory(const wxString& choice)
383 {
384     GuideCamera *pReturn = nullptr;
385 
386     try
387     {
388         if (choice.IsEmpty())
389         {
390             throw ERROR_INFO("CameraFactory called with choice.IsEmpty()");
391         }
392 
393         Debug.AddLine(wxString::Format("CameraFactory(%s)", choice));
394 
395         if (false) // so else ifs can follow
396         {
397         }
398 
399         // Chack ASCOM and INDI first since those choices may match match other choices below (like Simulator)
400 #if defined (ASCOM_CAMERA)
401         else if (choice.Contains(_T("ASCOM"))) {
402             pReturn = ASCOMCameraFactory::MakeASCOMCamera(choice);
403         }
404 #endif
405 #if defined (INDI_CAMERA)
406         else if (choice.Contains(_T("INDI"))) {
407             pReturn = INDICameraFactory::MakeINDICamera();
408         }
409 #endif
410 #if defined (IOPTRON_CAMERA)
411         else if (choice == _T("iOptron iGuider"))
412             pReturn = IoptronCameraFactory::MakeIoptronCamera();
413 #endif
414         else if (choice == _("None"))
415             pReturn = nullptr;
416         else if (choice == _T("Simulator"))
417             pReturn = GearSimulator::MakeCamSimulator();
418 #if defined (SAC42)
419         else if (choice.Contains(_T("SAC4-2")))
420             pReturn = new CameraSAC42();
421 #endif
422 #if defined (ATIK16)
423         else if (choice.StartsWith("Atik 16 series"))
424         {
425             bool hsmodel = false;
426             bool color = choice.Find(_T("color")) != wxNOT_FOUND;
427             pReturn = AtikCameraFactory::MakeAtikCamera(hsmodel, color);
428         }
429 #endif
430 #if defined (ATIK_GEN3)
431         else if (choice.StartsWith(_T("Atik Gen3")))
432         {
433             bool hsmodel = true;
434             bool color = choice.Find(_T("color")) != wxNOT_FOUND;
435             pReturn = AtikCameraFactory::MakeAtikCamera(hsmodel, color);
436         }
437 #endif
438 #if defined (QGUIDE)
439         else if (choice.Contains(_T("CCD Labs Q-Guider")))
440         {
441             pReturn = new CameraQGuider();
442             pReturn->Name = _T("Q-Guider");
443         }
444         else if (choice.Contains(_T("MagZero MZ-5")))
445         {
446             pReturn = new CameraQGuider();
447             pReturn->Name = _T("MagZero MZ-5");
448         }
449 #endif
450 #if defined (QHY_CAMERA)
451         else if (choice == _T("QHY Camera"))
452             pReturn = QHYCameraFactory::MakeQHYCamera();
453 #endif
454 #if defined(ALTAIR)
455         else if (choice == _T("Altair Camera"))
456             pReturn = AltairCameraFactory::MakeAltairCamera(ALTAIR_CAM_CURRENT);
457         else if (choice == _T("Altair Camera (2015/2016)"))
458             pReturn = AltairCameraFactory::MakeAltairCamera(ALTAIR_CAM_LEGACY);
459 #endif
460 #if defined(ZWO_ASI)
461         else if (choice == _T("ZWO ASI Camera"))
462             pReturn = ZWOCameraFactory::MakeZWOCamera();
463 #endif
464 #if defined(TOUPTEK_CAMERA)
465         else if (choice == _T("ToupTek Camera") ||
466                  choice == _T("Omegon Pro Camera"))
467         {
468             pReturn = ToupTekCameraFactory::MakeToupTekCamera();
469         }
470 #endif
471 #if defined(SKYRAIDER_CAMERA)
472         else if (choice == _T("MallinCam SkyRaider"))
473             pReturn = SkyraiderCameraFactory::MakeSkyraiderCamera();
474 #endif
475 #if defined (CAM_QHY5) // must come afer other QHY 5's since this pattern would match them
476         else if (choice.Contains(_T("QHY 5")))
477             pReturn = new CameraQHY5();
478 #endif
479 #if defined (OPENSSAG_CAMERA)
480         else if (choice.Contains(_T("Orion StarShoot Autoguider")))
481             pReturn = new CameraOpenSSAG();
482 #endif
483 #if defined (KWIQGUIDER_CAMERA)
484         else if (choice.Contains(_T("KWIQGuider")))
485             pReturn = KWIQGuiderCameraFactory::MakeKWIQGuiderCamera();
486 #endif
487 #if defined (SSAG)
488         else if (choice.Contains(_T("StarShoot Autoguider")))
489             pReturn = SSAGCameraFactory::MakeSSAGCamera();
490 #endif
491 #if defined (SSPIAG)
492         else if (choice.Contains(_T("StarShoot Planetary Imager & Autoguider")))
493             pReturn = new CameraSSPIAG();
494 #endif
495 #if defined (ORION_DSCI)
496         else if (choice.Contains(_T("Orion StarShoot DSCI")))
497             pReturn = new CameraStarShootDSCI();
498 #endif
499 #if defined(SVB_CAMERA)
500         else if (choice == _T("Svbony Camera"))
501             pReturn = SVBCameraFactory::MakeSVBCamera();
502 #endif
503 #if defined (OPENCV_CAMERA)
504         else if (choice.Contains(_T("OpenCV webcam")))
505         {
506             int dev = 0;
507             if (choice.Contains(_T("2")))
508             {
509                 dev = 1;
510             }
511             pReturn = new CameraOpenCV(dev);
512         }
513 #endif
514 #if defined (WDM_CAMERA)
515         else if (choice.Contains(_T("Windows WDM")))
516             pReturn = WDMCameraFactory::MakeWDMCamera();
517 #endif
518 #if defined (VFW_CAMERA)
519         else if (choice.Contains(_T("Windows VFW")))
520             pReturn = new CameraVFW();
521 #endif
522 #if defined (LE_SERIAL_CAMERA)
523         else if (choice.Contains(_T("Long exposure Serial webcam")))
524             pReturn = LESerialWebcamCameraFactory::MakeLESerialWebcamCamera();
525 #endif
526 #if defined (LE_PARALLEL_CAMERA)
527         else if (choice.Contains( _T("Long exposure Parallel webcam")))
528             pReturn = LEParallelWebcamCameraFactory::MakeLEParallelWebcamCamera();
529 #endif
530 #if defined (LE_LXUSB_CAMERA)
531         else if (choice.Contains(_T("Long exposure LXUSB webcam")))
532             pReturn = LELxUsbWebcamCameraFactory::MakeLELxUsbWebcamCamera();
533 #endif
534 #if defined (MEADE_DSI_CAMERA)
535         else if (choice.Contains(_T("Meade DSI I, II, or III")))
536             pReturn = DSICameraFactory::MakeDSICamera();
537 #endif
538 #if defined(MORAVIAN_CAMERA)
539         else if (choice == _T("Moravian Camera"))
540             pReturn = MoravianCameraFactory::MakeMoravianCamera();
541 #endif
542 #if defined (STARFISH_CAMERA)
543         else if (choice.Contains(_T("Fishcamp Starfish")))
544             pReturn = StarfishCameraFactory::MakeStarfishCamera();
545 #endif
546 #if defined (SXV)
547         else if (choice.Contains(_T("Starlight Xpress SXV")))
548             pReturn = SXVCameraFactory::MakeSXVCamera();
549 #endif
550 #if defined (OS_PL130)
551         else if (choice.Contains(_T("Opticstar PL-130M")))
552         {
553             Camera_OSPL130.Color = false;
554             Camera_OSPL130.Name = _T("Opticstar PL-130M");
555             pReturn = new Camera_OSPL130Class();
556         }
557         else if (choice.Contains(_T("Opticstar PL-130C")))
558         {
559             Camera_OSPL130.Color = true;
560             Camera_OSPL130.Name = _T("Opticstar PL-130C");
561             pReturn = new Camera_OSPL130Class();
562         }
563 #endif
564 #if defined (NEB_SBIG)
565         else if (choice.Contains(_T("Nebulosity")))
566             pReturn = new CameraNebSBIG();
567 #endif
568 #if defined (SBIGROTATOR_CAMERA)
569         // must go above SBIG
570         else if (choice.Contains(_T("SBIG Rotator")))
571             pReturn = SBIGRotatorCameraFactory::MakeSBIGRotatorCamera();
572 #endif
573 #if defined (SBIG)
574         else if (choice.Contains(_T("SBIG")))
575             pReturn = SBIGCameraFactory::MakeSBIGCamera();
576 #endif
577 #if defined (FIREWIRE_CAMERA)
578         else if (choice.Contains(_T("The Imaging Source (DCAM Firewire)")))
579             pReturn = new CameraFirewire();
580 #endif
581 #if defined (INOVA_PLC)
582         else if (choice.Contains(_T("i-Nova PLC-M")))
583             pReturn = new CameraINovaPLC();
584 #endif
585 #if defined (V4L_CAMERA)
586         else if (choice.Contains(_T("V4L(2) Camera")))
587         {
588             // There is at least ONE V4L(2) device ... let's find out exactly
589             DeviceInfo *deviceInfo = nullptr;
590 
591             if (Camera_VIDEODEVICE.NumberOfDevices() == 1)
592             {
593                 deviceInfo = Camera_VIDEODEVICE.GetDeviceAtIndex(0);
594 
595                 Camera_VIDEODEVICE.SetDevice(deviceInfo->getDeviceName());
596                 Camera_VIDEODEVICE.SetVendor(deviceInfo->getVendorId());
597                 Camera_VIDEODEVICE.SetModel(deviceInfo->getModelId());
598 
599                 Camera_VIDEODEVICE.Name = deviceInfo->getProduct();
600             }
601             else
602             {
603                 wxArrayString choices;
604                 int choice = 0;
605 
606                 if ((choice = wxGetSinglechoiceIndex(_("Select your camera"), _T("V4L(2) devices"), Camera_VIDEODEVICE.GetProductArray(choices))) != -1)
607                 {
608                     deviceInfo = Camera_VIDEODEVICE.GetDeviceAtIndex(choice);
609 
610                     Camera_VIDEODEVICE.SetDevice(deviceInfo->getDeviceName());
611                     Camera_VIDEODEVICE.SetVendor(deviceInfo->getVendorId());
612                     Camera_VIDEODEVICE.SetModel(deviceInfo->getModelId());
613 
614                     Camera_VIDEODEVICE.Name = deviceInfo->getProduct();
615                 }
616                 else
617                 {
618                     throw ERROR_INFO("CameraFactory invalid V4L choice");
619                 }
620             }
621 
622             pReturn = new Camera_VIDEODEVICEClass();
623         }
624 #endif
625         else
626         {
627             throw ERROR_INFO("CameraFactory: Unknown camera choice");
628         }
629     }
630     catch (const wxString& Msg)
631     {
632         POSSIBLY_UNUSED(Msg);
633         if (pReturn)
634         {
635             delete pReturn;
636             pReturn = nullptr;
637         }
638     }
639 
640     return pReturn;
641 }
642 
HandleSelectCameraButtonClick(wxCommandEvent &)643 bool GuideCamera::HandleSelectCameraButtonClick(wxCommandEvent&)
644 {
645     return false; // not handled
646 }
647 
EnumCameras(wxArrayString & names,wxArrayString & ids)648 bool GuideCamera::EnumCameras(wxArrayString& names, wxArrayString& ids)
649 {
650     return true; // error
651 }
652 
CamConnectFailed(const wxString & errorMessage)653 bool GuideCamera::CamConnectFailed(const wxString& errorMessage)
654 {
655     pFrame->Alert(errorMessage);
656     return true; // error
657 }
658 
SetCameraGain(int cameraGain)659 bool GuideCamera::SetCameraGain(int cameraGain)
660 {
661     bool bError = false;
662 
663     try
664     {
665         if (cameraGain < 0)
666         {
667             throw ERROR_INFO("cameraGain < 0");
668         }
669         else if (cameraGain > 100)
670         {
671             throw ERROR_INFO("cameraGain > 100");
672         }
673         GuideCameraGain = cameraGain;
674     }
675     catch (const wxString& Msg)
676     {
677         POSSIBLY_UNUSED(Msg);
678         bError = true;
679         GuideCameraGain = DefaultGuideCameraGain;
680     }
681 
682     pConfig->Profile.SetInt("/camera/gain", GuideCameraGain);
683 
684     return bError;
685 }
686 
GetDefaultCameraGain()687 int GuideCamera::GetDefaultCameraGain()
688 {
689     return DefaultGuideCameraGain;
690 }
691 
SetBinning(int binning)692 bool GuideCamera::SetBinning(int binning)
693 {
694     if (binning < 1)
695         binning = 1;
696     if (binning > MaxBinning)
697         binning = MaxBinning;
698 
699     Debug.Write(wxString::Format("camera: set binning = %u\n", (unsigned int) binning));
700 
701     Binning = binning;
702     pConfig->Profile.SetInt("/camera/binning", binning);
703 
704     return false;
705 }
706 
SetTimeoutMs(int ms)707 void GuideCamera::SetTimeoutMs(int ms)
708 {
709     static const int MIN_TIMEOUT_MS = 5000;
710 
711     m_timeoutMs = wxMax(ms, MIN_TIMEOUT_MS);
712 
713     pConfig->Profile.SetInt("/camera/TimeoutMs", m_timeoutMs);
714 }
715 
SetSaturationByADU(bool saturationByADU,unsigned short saturationADU)716 void GuideCamera::SetSaturationByADU(bool saturationByADU, unsigned short saturationADU)
717 {
718     m_saturationByADU = saturationByADU;
719     pConfig->Profile.SetBoolean("/camera/SaturationByADU", saturationByADU);
720 
721     if (saturationByADU)
722     {
723         m_saturationADU = saturationADU;
724         pConfig->Profile.SetInt("/camera/SaturationADU", saturationADU);
725         Debug.Write(wxString::Format("Saturation detection set to Max-ADU value %d\n", saturationADU));
726     }
727     else
728         Debug.Write("Saturation detection set to star-profile-mode\n");
729 }
730 
SetCameraPixelSize(double pixel_size)731 bool GuideCamera::SetCameraPixelSize(double pixel_size)
732 {
733     bool bError = false;
734 
735     try
736     {
737         if (pixel_size <= 0.0)
738         {
739             throw ERROR_INFO("pixel_size <= 0");
740         }
741 
742         m_pixelSize = pixel_size;
743         if (pFrame->pStatsWin)
744             pFrame->pStatsWin->ResetImageSize();
745     }
746     catch (const wxString& Msg)
747     {
748         POSSIBLY_UNUSED(Msg);
749         bError = true;
750         m_pixelSize = UnknownPixelSize;
751     }
752 
753     pConfig->Profile.SetDouble("/camera/pixelsize", m_pixelSize);
754 
755     return bError;
756 }
757 
SetCoolerOn(bool on)758 bool GuideCamera::SetCoolerOn(bool on)
759 {
760     return true; // error
761 }
762 
SetCoolerSetpoint(double temperature)763 bool GuideCamera::SetCoolerSetpoint(double temperature)
764 {
765     return true; // error
766 }
767 
GetCoolerStatus(bool * on,double * setpoint,double * power,double * temperature)768 bool GuideCamera::GetCoolerStatus(bool *on, double *setpoint, double *power, double *temperature)
769 {
770     return true; // error
771 }
772 
GetSensorTemperature(double * temperature)773 bool GuideCamera::GetSensorTemperature(double *temperature)
774 {
775     return true; // error
776 }
777 
GetConfigDialogPane(wxWindow * pParent)778 CameraConfigDialogPane *GuideCamera::GetConfigDialogPane(wxWindow *pParent)
779 {
780     return new CameraConfigDialogPane(pParent, this);
781 }
782 
NewSpinnerInt(wxWindow * parent,int width,int val,int minval,int maxval,int inc)783 static wxSpinCtrl *NewSpinnerInt(wxWindow *parent, int width, int val, int minval, int maxval, int inc)
784 {
785     wxSpinCtrl *pNewCtrl = pFrame->MakeSpinCtrl(parent, wxID_ANY, _T(" "), wxDefaultPosition,
786         wxSize(width, -1), wxSP_ARROW_KEYS, minval, maxval, val);
787     pNewCtrl->SetValue(val);
788     return pNewCtrl;
789 }
790 
NewSpinnerDouble(wxWindow * parent,int width,double val,double minval,double maxval,double inc,const wxString & tooltip)791 static wxSpinCtrlDouble *NewSpinnerDouble(wxWindow *parent, int width, double val, double minval, double maxval, double inc,
792                                           const wxString& tooltip)
793 {
794     wxSpinCtrlDouble *pNewCtrl = pFrame->MakeSpinCtrlDouble(parent, wxID_ANY, _T(" "), wxDefaultPosition,
795         wxSize(width, -1), wxSP_ARROW_KEYS, minval, maxval, val, inc);
796     pNewCtrl->SetDigits(2);
797     pNewCtrl->SetToolTip(tooltip);
798     return pNewCtrl;
799 }
800 
CameraConfigDialogPane(wxWindow * pParent,GuideCamera * pCamera)801 CameraConfigDialogPane::CameraConfigDialogPane(wxWindow *pParent, GuideCamera *pCamera)
802     : ConfigDialogPane(_("Camera Settings"), pParent)
803 {
804     m_pParent = pParent;
805 }
806 
MakeBold(wxControl * ctrl)807 static void MakeBold(wxControl *ctrl)
808 {
809     wxFont font = ctrl->GetFont();
810     font.SetWeight(wxFONTWEIGHT_BOLD);
811     ctrl->SetFont(font);
812 }
813 
LayoutControls(GuideCamera * pCamera,BrainCtrlIdMap & CtrlMap)814 void CameraConfigDialogPane::LayoutControls(GuideCamera *pCamera, BrainCtrlIdMap& CtrlMap)
815 {
816     wxStaticBoxSizer *pGenGroup = new wxStaticBoxSizer(wxVERTICAL, m_pParent, _("General Properties"));
817     wxFlexGridSizer *pTopline = new wxFlexGridSizer(1, 3, 10, 10);
818     // Generic controls
819     wxSizerFlags def_flags = wxSizerFlags(0).Border(wxALL, 10).Expand();
820     pTopline->Add(GetSizerCtrl(CtrlMap, AD_szNoiseReduction));
821     pTopline->Add(GetSizerCtrl(CtrlMap, AD_szTimeLapse), wxSizerFlags(0).Border(wxLEFT, 110).Expand());
822     pGenGroup->Add(pTopline, def_flags);
823     pGenGroup->Add(GetSizerCtrl(CtrlMap, AD_szAutoExposure), def_flags);
824     pGenGroup->Layout();
825 
826     // Specific controls
827     wxStaticBoxSizer *pSpecGroup = new wxStaticBoxSizer(wxVERTICAL, m_pParent, _("Camera-Specific Properties"));
828     if (pCamera)
829     {
830         int numItems = 3;
831         if (pCamera->HasGainControl) ++numItems;
832         if (pCamera->HasDelayParam)  ++numItems;
833         if (pCamera->HasPortNum)     ++numItems;
834         if (pCamera->MaxBinning > 1) ++numItems;
835         if (pCamera->HasCooler)      ++numItems;
836         wxFlexGridSizer *pDetailsSizer = new wxFlexGridSizer((numItems + 1) / 2, 3, 15, 15);
837 
838         wxSizerFlags spec_flags = wxSizerFlags(0).Border(wxALL, 10).Align(wxVERTICAL).Expand();
839         pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szPixelSize));
840         if (pCamera->HasGainControl)
841             pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szGain));
842         pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szCameraTimeout));
843         if (pCamera->HasDelayParam)
844             pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szDelay));
845         if (pCamera->HasPortNum)
846             pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szPort));
847         if (pCamera->MaxBinning > 1)
848             pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szBinning));
849         if (pCamera->HasSubframes)
850             pDetailsSizer->Add(GetSingleCtrl(CtrlMap, AD_cbUseSubFrames), wxSizerFlags().Border(wxTOP, 3));
851         if (pCamera->HasCooler)
852             pDetailsSizer->Add(GetSizerCtrl(CtrlMap, AD_szCooler));
853         pSpecGroup->Add(pDetailsSizer, spec_flags);
854         pSpecGroup->Layout();
855     }
856     else
857     {
858         wxStaticText *pNoCam = new wxStaticText(m_pParent, wxID_ANY, _("No camera specified"));
859         pSpecGroup->Add(pNoCam, wxSizerFlags().Align(wxALIGN_CENTER_HORIZONTAL));
860         pSpecGroup->Layout();
861 
862     }
863     this->Add(pGenGroup, def_flags);
864     if (pCamera && !pCamera->Connected)
865     {
866         wxStaticText *pNotConnected = new wxStaticText(m_pParent, wxID_ANY,
867             _("Camera is not connected.  Additional camera properties may be available if you connect to it first."));
868         MakeBold(pNotConnected);
869         this->Add(pNotConnected, wxSizerFlags().Align(wxALIGN_CENTER_HORIZONTAL).Border(wxALL, 10));
870     }
871     if (pCamera)
872     {
873         wxStaticBoxSizer *szSaturationGroup = new wxStaticBoxSizer(wxVERTICAL, m_pParent, _("Star Saturation Detection"));
874         szSaturationGroup->Add(GetSizerCtrl(CtrlMap, AD_szSaturationOptions), wxSizerFlags(0).Border(wxALL, 2).Expand());
875         szSaturationGroup->Layout();
876         this->Add(szSaturationGroup, def_flags);
877     }
878     this->Add(pSpecGroup, wxSizerFlags(0).Border(wxALL, 10).Expand());
879     this->Layout();
880     Fit(m_pParent);
881 }
882 
GetConfigDlgCtrlSet(wxWindow * pParent,GuideCamera * pCamera,AdvancedDialog * pAdvancedDialog,BrainCtrlIdMap & CtrlMap)883 CameraConfigDialogCtrlSet* GuideCamera::GetConfigDlgCtrlSet(wxWindow *pParent, GuideCamera *pCamera, AdvancedDialog *pAdvancedDialog, BrainCtrlIdMap& CtrlMap)
884 {
885     return new CameraConfigDialogCtrlSet(pParent, pCamera, pAdvancedDialog, CtrlMap);
886 }
887 
CameraConfigDialogCtrlSet(wxWindow * pParent,GuideCamera * pCamera,AdvancedDialog * pAdvancedDialog,BrainCtrlIdMap & CtrlMap)888 CameraConfigDialogCtrlSet::CameraConfigDialogCtrlSet(wxWindow *pParent, GuideCamera *pCamera, AdvancedDialog *pAdvancedDialog, BrainCtrlIdMap& CtrlMap)
889     : ConfigDialogCtrlSet(pParent, pAdvancedDialog, CtrlMap),
890       m_pUseSubframes(nullptr)
891 {
892     int textWidth = StringWidth(_T("0000"));
893     assert(pCamera);
894 
895     m_pCamera = pCamera;
896 
897     if (m_pCamera->HasSubframes)
898     {
899         m_pUseSubframes = new wxCheckBox(GetParentWindow(AD_cbUseSubFrames), wxID_ANY, _("Use Subframes"));
900         AddCtrl(CtrlMap, AD_cbUseSubFrames, m_pUseSubframes, _("Check to only download subframes (ROIs). Sub-frame size is equal to search region size."));
901     }
902 
903     // Pixel size always
904     m_pPixelSize = NewSpinnerDouble(GetParentWindow(AD_szPixelSize), textWidth, m_pCamera->GetCameraPixelSize(), 0.0, 99.9, 0.1,
905         _("Guide camera un-binned pixel size in microns. Used with the guide telescope focal length to display guiding error in arc-seconds."));
906     AddLabeledCtrl(CtrlMap, AD_szPixelSize, _("Pixel size"), m_pPixelSize, "");
907 
908     // Gain control
909     if (m_pCamera->HasGainControl)
910     {
911         wxWindow *parent = GetParentWindow(AD_szGain);
912         wxStaticText *label = new wxStaticText(parent, wxID_ANY, _("Camera gain") + _(": "));
913         m_pCameraGain = NewSpinnerInt(parent, textWidth, 100, 0, 100, 1);
914         m_pCameraGain->SetToolTip(
915             /* xgettext:no-c-format */ _("Camera gain, default = 95%, lower if you experience noise or wish to guide on a very bright star. Not available on all cameras."));
916         m_resetGain = new wxButton(GetParentWindow(AD_szGain), wxID_ANY, _("Reset"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
917         m_resetGain->SetToolTip(_("Reset gain to camera's default value (disabled when camera is not connected)"));
918         m_resetGain->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& evt) {
919                 m_pCameraGain->SetValue(::pCamera->GetDefaultCameraGain());
920             }
921         );
922         wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
923         sizer->Add(label, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
924         sizer->Add(m_pCameraGain, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
925         sizer->Add(m_resetGain, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
926         AddGroup(CtrlMap, AD_szGain, sizer);
927     }
928 
929     // Binning
930     m_binning = 0;
931     if (m_pCamera->MaxBinning > 1)
932     {
933         wxArrayString opts;
934         m_pCamera->GetBinningOpts(&opts);
935         int width = StringArrayWidth(opts);
936         m_binning = new wxChoice(GetParentWindow(AD_szBinning), wxID_ANY, wxDefaultPosition,
937             wxSize(width + 35, -1), opts);
938         AddLabeledCtrl(CtrlMap, AD_szBinning, _("Binning"), m_binning, _("Camera pixel binning"));
939     }
940 
941     // Delay parameter
942     if (m_pCamera->HasDelayParam)
943     {
944         m_pDelay = NewSpinnerInt(GetParentWindow(AD_szDelay), textWidth, 5, 0, 250, 150);
945         AddLabeledCtrl(CtrlMap, AD_szDelay, _("Delay"), m_pDelay, _("LE Read Delay (ms) , Adjust if you get dropped frames"));
946     }
947 
948     // Port number
949     if (m_pCamera->HasPortNum)
950     {
951         wxString port_choices[] = {
952             _T("Port 378"), _T("Port 3BC"), _T("Port 278"), _T("COM1"), _T("COM2"), _T("COM3"), _T("COM4"),
953             _T("COM5"), _T("COM6"), _T("COM7"), _T("COM8"), _T("COM9"), _T("COM10"), _T("COM11"), _T("COM12"),
954             _T("COM13"), _T("COM14"), _T("COM15"), _T("COM16"),
955         };
956 
957         int width = StringArrayWidth(port_choices, WXSIZEOF(port_choices));
958         m_pPortNum = new wxChoice(GetParentWindow(AD_szPort), wxID_ANY, wxDefaultPosition,
959             wxSize(width + 35, -1), WXSIZEOF(port_choices), port_choices);
960         AddLabeledCtrl(CtrlMap, AD_szPort, _("LE Port"), m_pPortNum, _("Port number for long-exposure control"));
961     }
962 
963     // Cooler settings
964     if (m_pCamera->HasCooler)
965     {
966         wxSizer *sz = new wxBoxSizer(wxHORIZONTAL);
967         m_coolerOn = new wxCheckBox(GetParentWindow(AD_szCooler), wxID_ANY, _("Cooler On"));
968         m_coolerOn->SetToolTip(_("Turn camera cooler on or off"));
969         sz->Add(m_coolerOn, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Border(wxRIGHT));
970         m_coolerSetpt = NewSpinnerInt(GetParentWindow(AD_szDelay), textWidth, 5, -99, 99, 1);
971         wxSizer *szt = MakeLabeledControl(AD_szCooler, _("Set Temperature"), m_coolerSetpt, _("Cooler setpoint temperature"));
972         sz->Add(szt, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
973         AddGroup(CtrlMap, AD_szCooler, sz);
974     }
975 
976     // Max ADU and related saturation choices in a single group
977     int width = StringWidth(_T("65535"));
978     wxWindow* parent = GetParentWindow(AD_szSaturationOptions);
979     m_camSaturationADU = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(1.5 * width, -1));
980     m_camSaturationADU->SetToolTip(_("ADU level to determine saturation - 65535 for most 16-bit cameras, or 255 for 8-bit cameras."));
981     m_SaturationByADU = new wxRadioButton(parent, wxID_ANY, _("Saturation by Max-ADU value:"));
982     m_SaturationByADU->SetToolTip(_("Identify star saturation based on camera maximum-ADU value (recommended)"));
983     m_SaturationByADU->Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED, &CameraConfigDialogCtrlSet::OnSaturationChoiceChanged, this);
984     wxStaticBoxSizer* szADUGroup = new wxStaticBoxSizer(wxHORIZONTAL, parent,
985         wxEmptyString);
986     szADUGroup->Add(m_SaturationByADU, wxSizerFlags().Border(wxTOP, 2));
987     szADUGroup->Add(m_camSaturationADU, wxSizerFlags().Border(wxLEFT, 6));
988 
989     m_SaturationByProfile = new wxRadioButton(parent, wxID_ANY, _("Saturation via star-profile"));
990     m_SaturationByProfile->SetToolTip(_("Identify star saturation based on flat-topped profile, regardless of brightness"));
991     m_SaturationByProfile->Bind(wxEVT_COMMAND_RADIOBUTTON_SELECTED, &CameraConfigDialogCtrlSet::OnSaturationChoiceChanged, this);
992     wxFlexGridSizer* szSaturationGroup = new wxFlexGridSizer(1, 2, 5, 15);
993 
994     szSaturationGroup->Add(szADUGroup, wxSizerFlags().Border(wxALL, 3).Align(wxALIGN_CENTER_VERTICAL));
995     szSaturationGroup->Add(m_SaturationByProfile, wxSizerFlags(0).Border(wxLEFT, 70).Expand().Align(wxALIGN_CENTER_VERTICAL));
996     AddGroup(CtrlMap, AD_szSaturationOptions, szSaturationGroup);
997 
998     // Watchdog timeout
999     m_timeoutVal = NewSpinnerInt(GetParentWindow(AD_szCameraTimeout), textWidth, 5, 5, 9999, 1);
1000     AddLabeledCtrl(CtrlMap, AD_szCameraTimeout, _("Disconnect nonresponsive          \ncamera after (seconds)"), m_timeoutVal,
1001         wxString::Format(_("The camera will be disconnected if it fails to respond for this long. "
1002         "The default value, %d seconds, should be appropriate for most cameras."), DefaultGuideCameraTimeoutMs / 1000));
1003 }
1004 
OnSaturationChoiceChanged(wxCommandEvent & event)1005 void CameraConfigDialogCtrlSet::OnSaturationChoiceChanged(wxCommandEvent& event)
1006 {
1007     m_camSaturationADU->Enable(m_SaturationByADU->GetValue());
1008 }
1009 
SaturationValFromBPP(GuideCamera * cam)1010 static unsigned short SaturationValFromBPP(GuideCamera *cam)
1011 {
1012     return (unsigned short) ((1U << cam->BitsPerPixel()) - 1);
1013 }
1014 
LoadValues()1015 void CameraConfigDialogCtrlSet::LoadValues()
1016 {
1017     assert(m_pCamera);
1018 
1019     if (m_pCamera->HasSubframes)
1020     {
1021         m_pUseSubframes->SetValue(m_pCamera->UseSubframes);
1022     }
1023 
1024     if (m_pCamera->HasGainControl)
1025     {
1026         m_pCameraGain->SetValue(m_pCamera->GetCameraGain());
1027         m_resetGain->Enable(m_pCamera->Connected);
1028     }
1029 
1030     if (m_binning)
1031     {
1032         int idx = m_pCamera->Binning - 1;
1033         m_binning->Select(idx);
1034         m_prevBinning = idx + 1;
1035         // don't allow binning change when calibrating or guiding
1036         m_binning->Enable(!pFrame->pGuider || !pFrame->pGuider->IsCalibratingOrGuiding());
1037     }
1038 
1039     m_timeoutVal->SetValue(m_pCamera->GetTimeoutMs() / 1000);
1040 
1041     bool saturationByADU = m_pCamera->IsSaturationByADU();
1042     m_SaturationByADU->SetValue(saturationByADU);
1043     m_SaturationByProfile->SetValue(!saturationByADU);
1044 
1045     if (pConfig->Profile.HasEntry("/camera/SaturationADU"))
1046     {
1047         unsigned int maxADU = wxMin(pConfig->Profile.GetInt("/camera/SaturationADU", 0), 65535);
1048         m_camSaturationADU->SetValue(wxString::Format("%u", maxADU));
1049     }
1050     else
1051     {
1052         // first time initialization
1053         int val = SaturationValFromBPP(m_pCamera);
1054         Debug.Write(wxString::Format("initializing cam saturation ADU val to %d\n", val));
1055         m_camSaturationADU->SetValue(wxString::Format("%d", val));
1056     }
1057     wxCommandEvent dummy;
1058     OnSaturationChoiceChanged(dummy);
1059 
1060     // do not allow saturation detection changes unless the camera is connected.
1061     // The Max ADU value needs to know the camera's BPP which may not be available
1062     // unless the camera is connected
1063     if (!m_pCamera->Connected)
1064     {
1065         m_SaturationByADU->Enable(false);
1066         m_SaturationByProfile->Enable(false);
1067         m_camSaturationADU->Enable(false);
1068     }
1069 
1070     if (m_pCamera->HasDelayParam)
1071     {
1072         m_pDelay->SetValue(m_pCamera->ReadDelay);
1073     }
1074 
1075     if (m_pCamera->HasPortNum)
1076     {
1077         switch (m_pCamera->Port)
1078         {
1079         case 0x3BC:
1080             m_pPortNum->SetSelection(1);
1081             break;
1082         case 0x278:
1083             m_pPortNum->SetSelection(2);
1084             break;
1085         case 1:  // COM1
1086             m_pPortNum->SetSelection(3);
1087             break;
1088         case 2:  // COM2
1089             m_pPortNum->SetSelection(4);
1090             break;
1091         case 3:  // COM3
1092             m_pPortNum->SetSelection(5);
1093             break;
1094         case 4:  // COM4
1095             m_pPortNum->SetSelection(6);
1096             break;
1097         case 5:  // COM5
1098             m_pPortNum->SetSelection(7);
1099             break;
1100         case 6:  // COM6
1101             m_pPortNum->SetSelection(8);
1102             break;
1103         case 7:  // COM7
1104             m_pPortNum->SetSelection(9);
1105             break;
1106         case 8:  // COM8
1107             m_pPortNum->SetSelection(10);
1108             break;
1109         case 9:  // COM9
1110             m_pPortNum->SetSelection(11);
1111             break;
1112         case 10:  // COM10
1113             m_pPortNum->SetSelection(12);
1114             break;
1115         case 11:  // COM11
1116             m_pPortNum->SetSelection(13);
1117             break;
1118         case 12:  // COM12
1119             m_pPortNum->SetSelection(14);
1120             break;
1121         case 13:  // COM13
1122             m_pPortNum->SetSelection(15);
1123             break;
1124         case 14:  // COM14
1125             m_pPortNum->SetSelection(16);
1126             break;
1127         case 15:  // COM15
1128             m_pPortNum->SetSelection(17);
1129             break;
1130         case 16:  // COM16
1131             m_pPortNum->SetSelection(18);
1132             break;
1133         default:
1134             m_pPortNum->SetSelection(0);
1135             break;
1136         }
1137 
1138         m_pPortNum->Enable(!pFrame->CaptureActive);
1139     }
1140 
1141     double pxSize;
1142     if (m_pCamera->GetDevicePixelSize(&pxSize))         // true=>error
1143     {
1144         pxSize = m_pCamera->GetCameraPixelSize();
1145         m_pPixelSize->Enable(!pFrame->CaptureActive);
1146     }
1147     else
1148         m_pPixelSize->Enable(false);                // Got a device-level pixel size, disable the control
1149 
1150     m_pPixelSize->SetValue(pxSize);
1151 
1152     if (m_pCamera->HasCooler)
1153     {
1154         bool ok = false;
1155         bool on;
1156         double setpt;
1157 
1158         if (m_pCamera->Connected)
1159         {
1160             double power, temp;
1161             bool err = m_pCamera->GetCoolerStatus(&on, &setpt, &power, &temp);
1162             if (!err)
1163                 ok = true;
1164         }
1165 
1166         if (ok)
1167         {
1168             m_coolerOn->SetValue(on);
1169             if (!on)
1170             {
1171                 setpt = pConfig->Profile.GetDouble("/camera/CoolerSetpt", 10.0);
1172             }
1173             m_coolerSetpt->SetValue((int)floor(setpt));
1174         }
1175 
1176         m_coolerOn->Enable(ok);
1177         m_coolerSetpt->Enable(ok);
1178     }
1179 }
1180 
UnloadValues()1181 void CameraConfigDialogCtrlSet::UnloadValues()
1182 {
1183     assert(m_pCamera);
1184 
1185     if (m_pCamera->HasSubframes)
1186     {
1187         m_pCamera->UseSubframes = m_pUseSubframes->GetValue();
1188         pConfig->Profile.SetBoolean("/camera/UseSubframes", m_pCamera->UseSubframes);
1189     }
1190 
1191     if (m_pCamera->HasGainControl)
1192     {
1193         m_pCamera->SetCameraGain(m_pCameraGain->GetValue());
1194     }
1195 
1196     if (m_binning)
1197     {
1198         int oldBin = m_pCamera->Binning;
1199         int newBin = m_binning->GetSelection() + 1;
1200         if (oldBin != newBin)
1201             pFrame->pAdvancedDialog->MakeImageScaleAdjustments();           // Do this now to preserve old (device value) and new (UI value) for scale adjustment
1202         m_pCamera->SetBinning(m_binning->GetSelection() + 1);
1203     }
1204 
1205     m_pCamera->SetTimeoutMs(m_timeoutVal->GetValue() * 1000);
1206 
1207     if (m_pCamera->HasDelayParam)
1208     {
1209         m_pCamera->ReadDelay = m_pDelay->GetValue();
1210         pConfig->Profile.SetInt("/camera/ReadDelay", m_pCamera->ReadDelay);
1211     }
1212 
1213     if (m_pCamera->HasPortNum)
1214     {
1215         switch (m_pPortNum->GetSelection())
1216         {
1217         case 0:
1218             m_pCamera->Port = 0x378;
1219             break;
1220         case 1:
1221             m_pCamera->Port = 0x3BC;
1222             break;
1223         case 2:
1224             m_pCamera->Port = 0x278;
1225             break;
1226         case 3:
1227             m_pCamera->Port = 1;
1228             break;
1229         case 4:
1230             m_pCamera->Port = 2;
1231             break;
1232         case 5:
1233             m_pCamera->Port = 3;
1234             break;
1235         case 6:
1236             m_pCamera->Port = 4;
1237             break;
1238         }
1239     }
1240 
1241     double oldPxSz = m_pCamera->GetCameraPixelSize();
1242     double newPxSz = m_pPixelSize->GetValue();
1243     if (oldPxSz != newPxSz)
1244         pFrame->pAdvancedDialog->MakeImageScaleAdjustments();       // Preserve old (device value) and new (UI value) for scale adjustment
1245     m_pCamera->SetCameraPixelSize(m_pPixelSize->GetValue());
1246 
1247     bool saturationByADU = m_SaturationByADU->GetValue();
1248     unsigned short saturationVal = 0;
1249 
1250     if (saturationByADU)
1251     {
1252         long val = 0;
1253         m_camSaturationADU->GetValue().ToLong(&val);
1254         if (val > 0)
1255         {
1256             saturationVal = wxMin(val, SaturationValFromBPP(m_pCamera));
1257         }
1258         else
1259         {
1260             // user-entered zero treated as 'set to default'
1261             saturationVal = SaturationValFromBPP(m_pCamera);
1262         }
1263     }
1264 
1265     m_pCamera->SetSaturationByADU(saturationByADU, saturationVal);
1266 
1267     if (m_pCamera->HasCooler)
1268     {
1269         bool on = m_coolerOn->GetValue();
1270         m_pCamera->SetCoolerOn(on);
1271         double setpt = (double) m_coolerSetpt->GetValue();
1272         m_pCamera->SetCoolerSetpoint(setpt);
1273         pConfig->Profile.SetDouble("/camera/CoolerSetpt", setpt);
1274     }
1275 
1276     pFrame->pStatsWin->UpdateCooler();
1277 }
1278 
GetPixelSize()1279 double CameraConfigDialogCtrlSet::GetPixelSize()
1280 {
1281     return m_pPixelSize->GetValue();
1282 }
1283 
SetPixelSize(double val)1284 void CameraConfigDialogCtrlSet::SetPixelSize(double val)
1285 {
1286     m_pPixelSize->SetValue(val);
1287 }
1288 
GetBinning()1289 int CameraConfigDialogCtrlSet::GetBinning()
1290 {
1291     return m_binning ? m_binning->GetSelection() + 1 : 1;
1292 }
1293 
SetBinning(int binning)1294 void CameraConfigDialogCtrlSet::SetBinning(int binning)
1295 {
1296     if (m_binning)
1297         m_binning->Select(binning - 1);
1298 }
1299 
GetBinningOpts(int maxBin,wxArrayString * opts)1300 void GuideCamera::GetBinningOpts(int maxBin, wxArrayString *opts)
1301 {
1302     for (int i = 1; i <= maxBin; i++)
1303         opts->Add(wxString::Format("%d", i));
1304 }
1305 
GetSettingsSummary()1306 wxString GuideCamera::GetSettingsSummary()
1307 {
1308     int darkDur;
1309 
1310     { // lock scope
1311         wxCriticalSectionLocker lck(DarkFrameLock);
1312         darkDur = CurrentDarkFrame ? CurrentDarkFrame->ImgExpDur : 0;
1313     } // lock scope
1314 
1315     // return a loggable summary of current camera settings
1316     wxString pixelSizeStr;
1317     if (m_pixelSize == UnknownPixelSize)
1318         pixelSizeStr = _("unspecified");
1319     else
1320         pixelSizeStr = wxString::Format(_("%0.1f um"), m_pixelSize);
1321 
1322     return wxString::Format("Camera = %s%s%s%s, full size = %d x %d, %s, %s, pixel size = %s\n",
1323                             Name,
1324                             HasGainControl ? wxString::Format(", gain = %d", GuideCameraGain) : "",
1325                             HasDelayParam ? wxString::Format(", delay = %d", ReadDelay) : "",
1326                             HasPortNum ? wxString::Format(", port = 0x%hx", Port) : "",
1327                             FullSize.GetWidth(), FullSize.GetHeight(),
1328                             darkDur ? wxString::Format("have dark, dark dur = %d", darkDur) : "no dark",
1329                             CurrentDefectMap ? "defect map in use" : "no defect map",
1330                             pixelSizeStr);
1331 }
1332 
AddDark(usImage * dark)1333 void GuideCamera::AddDark(usImage *dark)
1334 {
1335     int const expdur = dark->ImgExpDur;
1336 
1337     { // lock scope
1338         wxCriticalSectionLocker lck(DarkFrameLock);
1339 
1340         // free the prior dark with this exposure duration
1341         ExposureImgMap::iterator pos = Darks.find(expdur);
1342         if (pos != Darks.end())
1343         {
1344             usImage *prior = pos->second;
1345             if (prior == CurrentDarkFrame)
1346                 CurrentDarkFrame = dark;
1347             delete prior;
1348         }
1349 
1350     } // lock scope
1351 
1352     Darks[expdur] = dark;
1353 }
1354 
SelectDark(int exposureDuration)1355 void GuideCamera::SelectDark(int exposureDuration)
1356 {
1357     // select the dark frame with the smallest exposure >= the requested exposure.
1358     // if there are no darks with exposures > the select exposure, select the dark with the greatest exposure
1359 
1360     wxCriticalSectionLocker lck(DarkFrameLock);
1361 
1362     CurrentDarkFrame = 0;
1363     for (ExposureImgMap::const_iterator it = Darks.begin(); it != Darks.end(); ++it)
1364     {
1365         CurrentDarkFrame = it->second;
1366         if (it->first >= exposureDuration)
1367             break;
1368     }
1369 }
1370 
GetDarklibProperties(int * pNumDarks,double * pMinExp,double * pMaxExp)1371 void GuideCamera::GetDarklibProperties(int *pNumDarks, double *pMinExp, double *pMaxExp)
1372 {
1373     double minExp = 9999.0;
1374     double maxExp = -9999.0;
1375     int ct = 0;
1376 
1377     { // lock scope
1378         wxCriticalSectionLocker lck(DarkFrameLock);
1379 
1380         for (auto it = Darks.begin(); it != Darks.end(); ++it)
1381         {
1382             if (it->first < minExp)
1383                 minExp = it->first;
1384             if (it->first > maxExp)
1385                 maxExp = it->first;
1386             ++ct;
1387         }
1388     } // lock scope
1389 
1390     *pNumDarks = ct;
1391     *pMinExp = minExp;
1392     *pMaxExp = maxExp;
1393 }
1394 
ClearDefectMap()1395 void GuideCamera::ClearDefectMap()
1396 {
1397     wxCriticalSectionLocker lck(DarkFrameLock);
1398 
1399     if (CurrentDefectMap)
1400     {
1401         Debug.AddLine("Clearing defect map...");
1402         delete CurrentDefectMap;
1403         CurrentDefectMap = nullptr;
1404     }
1405 }
1406 
SetDefectMap(DefectMap * defectMap)1407 void GuideCamera::SetDefectMap(DefectMap *defectMap)
1408 {
1409     wxCriticalSectionLocker lck(DarkFrameLock);
1410     delete CurrentDefectMap;
1411     CurrentDefectMap = defectMap;
1412 }
1413 
ClearDarks()1414 void GuideCamera::ClearDarks()
1415 {
1416     wxCriticalSectionLocker lck(DarkFrameLock);
1417     while (!Darks.empty())
1418     {
1419         ExposureImgMap::iterator it = Darks.begin();
1420         delete it->second;
1421         Darks.erase(it);
1422     }
1423     CurrentDarkFrame = nullptr;
1424 }
1425 
SubtractDark(usImage & img)1426 void GuideCamera::SubtractDark(usImage& img)
1427 {
1428     // dark subtraction is done in the camera worker thread, so we need to acquire the
1429     // DarkFrameLock to protect against the dark frame disappearing when the main
1430     // thread does "Load Darks" or "Clear Darks"
1431 
1432     wxCriticalSectionLocker lck(DarkFrameLock);
1433 
1434     if (CurrentDefectMap)
1435     {
1436         RemoveDefects(img, *CurrentDefectMap);
1437     }
1438     else if (CurrentDarkFrame)
1439     {
1440         Subtract(img, *CurrentDarkFrame);
1441     }
1442 }
1443 
InitiateReconnect()1444 static void InitiateReconnect()
1445 {
1446     WorkerThread *thr = WorkerThread::This();
1447     if (thr)
1448     {
1449         // Defer sending the completion of exposure message until after
1450         // the camera re-connecttion attempt
1451         thr->SetSkipExposeComplete();
1452     }
1453     pFrame->TryReconnect();
1454 }
1455 
DisconnectWithAlert(CaptureFailType type)1456 void GuideCamera::DisconnectWithAlert(CaptureFailType type)
1457 {
1458     switch (type)
1459     {
1460     case CAPT_FAIL_MEMORY:
1461         DisconnectWithAlert(_("Memory allocation error during capture"), NO_RECONNECT);
1462         break;
1463 
1464     case CAPT_FAIL_TIMEOUT:
1465         {
1466             wxString msg(wxString::Format(_("After %.1f sec the camera has not completed a %.1f sec exposure, so "
1467                 "it has been disconnected to prevent other problems. "
1468                 "If you think the hardware is working correctly, you can increase the "
1469                 "timeout period on the Camera tab of the Advanced Settings Dialog."),
1470                 (pFrame->RequestedExposureDuration() + m_timeoutMs) / 1000.,
1471                 pFrame->RequestedExposureDuration() / 1000.));
1472 
1473             DisconnectWithAlert(msg, RECONNECT);
1474         }
1475         break;
1476     }
1477 }
1478 
DisconnectWithAlert(const wxString & msg,ReconnectType reconnect)1479 void GuideCamera::DisconnectWithAlert(const wxString& msg, ReconnectType reconnect)
1480 {
1481     Disconnect();
1482 
1483     // CAUTION: this function can be called from the worker thread, so
1484     // care must be taken not to make any direct UI updates
1485 
1486     pFrame->UpdateStatusBarStateLabels();
1487     pFrame->NotifyUpdateButtonsStatus(); // in case camera dialog button depends on connected state
1488 
1489     if (reconnect == RECONNECT)
1490     {
1491         pFrame->Alert(msg + "\n" + _("PHD will make several attempts to re-connect the camera."));
1492         InitiateReconnect();
1493     }
1494     else
1495     {
1496         pFrame->Alert(msg + "\n" + _("The camera has been disconnected. Please resolve the problem and re-connect the camera."));
1497     }
1498 }
1499 
InitCapture()1500 void GuideCamera::InitCapture()
1501 {
1502 }
1503 
Capture(GuideCamera * camera,int duration,usImage & img,int captureOptions,const wxRect & subframe)1504 bool GuideCamera::Capture(GuideCamera *camera, int duration, usImage& img, int captureOptions, const wxRect& subframe)
1505 {
1506     img.InitImgStartTime();
1507     img.BitsPerPixel = camera->BitsPerPixel();
1508     img.ImgExpDur = duration;
1509     bool err = camera->Capture(duration, img, captureOptions, subframe);
1510     return err;
1511 }
1512 
ST4HasGuideOutput()1513 bool GuideCamera::ST4HasGuideOutput()
1514 {
1515     return m_hasGuideOutput;
1516 }
1517 
ST4HostConnected()1518 bool GuideCamera::ST4HostConnected()
1519 {
1520     return Connected;
1521 }
1522 
ST4HasNonGuiMove()1523 bool GuideCamera::ST4HasNonGuiMove()
1524 {
1525     // should never be called
1526 
1527     assert(false);
1528     return true;
1529 }
1530 
ST4PulseGuideScope(int direction,int duration)1531 bool GuideCamera::ST4PulseGuideScope(int direction, int duration)
1532 {
1533     // should never be called
1534 
1535     assert(false);
1536     return true;
1537 }
1538