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