1 /*
2 * cam_sbig.cpp
3 * PHD Guiding
4 *
5 * Created by Craig Stark.
6 * Copyright (c) 2007-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 #include "phd.h"
36
37 #if defined(SBIG)
38
39 #include "cam_sbig.h"
40 #include "camera.h"
41 #include "image_math.h"
42
43 #include <time.h>
44 #include <wx/textdlg.h>
45
46 #if defined (__APPLE__)
47 #include <SBIGUDrv/sbigudrv.h>
48 #elif defined(__LINUX__)
49 #include <sbigudrv.h>
50 #else
51 #include "cameras/Sbigudrv.h"
52 #endif
53
54 class CameraSBIG : public GuideCamera
55 {
56 bool m_useTrackingCCD;
57 bool m_driverLoaded;
58 wxSize m_imageSize[2]; // 0=>bin1, 1=>bin2
59 double m_devicePixelSize;
60 bool IsColor;
61
62 public:
63 CameraSBIG();
64 ~CameraSBIG();
65
CanSelectCamera() const66 bool CanSelectCamera() const override { return true; }
67 bool HandleSelectCameraButtonClick(wxCommandEvent& evt) override;
68 bool Capture(int duration, usImage& img, int options, const wxRect& subframe) override;
69 bool Connect(const wxString& camId) override;
70 bool Disconnect() override;
71 void InitCapture() override;
72 bool ST4PulseGuideScope(int direction, int duration) override;
ST4HasNonGuiMove()73 bool ST4HasNonGuiMove() override { return true; }
HasNonGuiCapture()74 bool HasNonGuiCapture() override { return true; }
75 wxByte BitsPerPixel() override;
76 bool GetDevicePixelSize(double *devPixelSize) override;
77
78 private:
79 bool LoadDriver();
80 };
81
bcd2long(unsigned long bcd)82 static unsigned long bcd2long(unsigned long bcd)
83 {
84 int pos = sizeof(bcd) * 8;
85 int digit;
86 unsigned long val = 0;
87
88 do {
89 pos -= 4;
90 digit = (bcd >> pos) & 0xf;
91 val = val * 10 + digit;
92 } while (pos > 0);
93
94 return val;
95 }
96
CameraSBIG()97 CameraSBIG::CameraSBIG()
98 : m_driverLoaded(false)
99 {
100 Connected = false;
101 Name = _T("SBIG");
102 //FullSize = wxSize(1280,1024);
103 //HasGainControl = true;
104 m_hasGuideOutput = true;
105 m_useTrackingCCD = false;
106 HasShutter = true;
107 HasSubframes = true;
108 IsColor = false;
109 }
110
~CameraSBIG()111 CameraSBIG::~CameraSBIG()
112 {
113 if (m_driverLoaded)
114 SBIGUnivDrvCommand(CC_CLOSE_DRIVER, NULL, NULL);
115 }
116
BitsPerPixel()117 wxByte CameraSBIG::BitsPerPixel()
118 {
119 return 16;
120 }
121
_LoadDriver()122 static bool _LoadDriver()
123 {
124 short err;
125
126 #if defined (__WINDOWS__)
127 __try {
128 err = SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL);
129 }
130 __except (EXCEPTION_EXECUTE_HANDLER) {
131 err = CE_DRIVER_NOT_FOUND;
132 }
133 #else
134 err = SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL);
135 #endif
136
137 return err == CE_NO_ERROR;
138 }
139
LoadDriver()140 bool CameraSBIG::LoadDriver()
141 {
142 if (m_driverLoaded)
143 return true;
144
145 bool ok = _LoadDriver();
146 if (ok)
147 m_driverLoaded = true;
148 else
149 wxMessageBox(_("Error loading SBIG driver and/or DLL"));
150
151 return ok;
152 }
153
SelectInterfaceAndDevice()154 static bool SelectInterfaceAndDevice()
155 {
156 // select which cam interface
157 wxArrayString interf;
158
159 interf.Add("USB");
160 interf.Add("Ethernet");
161 #if defined (__WINDOWS__)
162 interf.Add("LPT 0x378");
163 interf.Add("LPT 0x278");
164 interf.Add("LPT 0x3BC");
165 #else
166 interf.Add("USB1 direct");
167 interf.Add("USB2 direct");
168 interf.Add("USB3 direct");
169 #endif
170
171 int resp = pConfig->Profile.GetInt("/camera/sbig/interface", 0);
172 resp = wxGetSingleChoiceIndex(_("Select interface"), _("Interface"), interf,
173 NULL, wxDefaultCoord, wxDefaultCoord, true, wxCHOICE_WIDTH, wxCHOICE_HEIGHT,
174 resp);
175
176 if (resp == -1)
177 {
178 // user hit cancel
179 return true;
180 }
181
182 pConfig->Profile.SetInt("/camera/sbig/interface", resp);
183
184 OpenDeviceParams odp = { 0 };
185
186 short err;
187
188 switch (resp) {
189 case 0:
190 odp.deviceType = DEV_USB;
191 QueryUSBResults2 usbp;
192 err = SBIGUnivDrvCommand(CC_QUERY_USB2, 0, &usbp);
193 Debug.Write(wxString::Format("SBIG: CC_QUERY_USB2 returns %hd, camerasFound = %hu\n", err, usbp.camerasFound));
194 if (usbp.camerasFound > 1)
195 {
196 wxArrayString USBNames;
197 int i;
198 for (i = 0; i < usbp.camerasFound; i++)
199 {
200 Debug.Write(wxString::Format("SBIG: [%d] %s\n", i, usbp.usbInfo[i].name));
201 USBNames.Add(usbp.usbInfo[i].name);
202 }
203 i = wxGetSingleChoiceIndex(_("Select USB camera"), _("Camera name"), USBNames);
204 Debug.Write(wxString::Format("SBIG: selected index %d\n", i));
205 if (i == -1)
206 return true;
207 odp.deviceType = DEV_USB1 + i;
208 }
209 break;
210 case 1: {
211 odp.deviceType = DEV_ETH;
212 wxString IPstr = wxGetTextFromUser(_("IP address"), _("Enter IP address"),
213 pConfig->Profile.GetString("/camera/sbig/ipaddr", _T("")));
214 Debug.Write(wxString::Format("SBIG: selected ipaddr %s\n", IPstr));
215 if (IPstr.length() == 0)
216 return true;
217 pConfig->Profile.SetString("/camera/sbig/ipaddr", IPstr);
218 wxString tmpstr = IPstr.BeforeFirst('.');
219 unsigned long tmp;
220 tmpstr.ToULong(&tmp);
221 unsigned long ip = tmp << 24;
222 IPstr = IPstr.AfterFirst('.');
223 tmpstr = IPstr.BeforeFirst('.');
224 tmpstr.ToULong(&tmp);
225 ip = ip | (tmp << 16);
226 IPstr = IPstr.AfterFirst('.');
227 tmpstr = IPstr.BeforeFirst('.');
228 tmpstr.ToULong(&tmp);
229 ip = ip | (tmp << 8);
230 IPstr = IPstr.AfterFirst('.');
231 tmpstr = IPstr.BeforeFirst('.');
232 tmpstr.ToULong(&tmp);
233 ip = ip | tmp;
234 odp.ipAddress = ip;
235 break;
236 }
237 #ifdef __WINDOWS__
238 case 2:
239 Debug.Write("SBIG: selected LPT1\n");
240 odp.deviceType = DEV_LPT1;
241 odp.lptBaseAddress = 0x378;
242 break;
243 case 3:
244 Debug.Write("SBIG: selected LPT2\n");
245 odp.deviceType = DEV_LPT2;
246 odp.lptBaseAddress = 0x278;
247 break;
248 case 4:
249 Debug.Write("SBIG: selected LPT3\n");
250 odp.deviceType = DEV_LPT3;
251 odp.lptBaseAddress = 0x3BC;
252 break;
253 #else
254 case 2:
255 Debug.Write("SBIG: selected USB1\n");
256 odp.deviceType = DEV_USB1;
257 break;
258 case 3:
259 Debug.Write("SBIG: selected USB2\n");
260 odp.deviceType = DEV_USB2;
261 break;
262 case 4:
263 Debug.Write("SBIG: selected USB3\n");
264 odp.deviceType = DEV_USB3;
265 break;
266 #endif
267 }
268
269 pConfig->Profile.SetInt("/camera/sbig/deviceType", odp.deviceType);
270 pConfig->Profile.SetInt("/camera/sbig/ipAddress", odp.ipAddress);
271 pConfig->Profile.SetInt("/camera/sbig/lptBaseAddress", odp.lptBaseAddress);
272 pConfig->Profile.SetInt("/camera/sbig/useTrackingCCD", -1); // force prompt for tracking CCD
273
274 return false;
275 }
276
LoadOpenDeviceParams(OpenDeviceParams * odp)277 static bool LoadOpenDeviceParams(OpenDeviceParams *odp)
278 {
279 int deviceType = pConfig->Profile.GetInt("/camera/sbig/deviceType", -1);
280 if (deviceType == -1)
281 return false;
282
283 odp->deviceType = deviceType;
284 odp->ipAddress = pConfig->Profile.GetInt("/camera/sbig/ipAddress", 0);
285 odp->lptBaseAddress = pConfig->Profile.GetInt("/camera/sbig/lptBaseAddress", 0);
286
287 return true;
288 }
289
HandleSelectCameraButtonClick(wxCommandEvent & evt)290 bool CameraSBIG::HandleSelectCameraButtonClick(wxCommandEvent& evt)
291 {
292 if (LoadDriver())
293 SelectInterfaceAndDevice();
294 return true; // handled
295 }
296
Connect(const wxString & camId)297 bool CameraSBIG::Connect(const wxString& camId)
298 {
299 // DEAL WITH PIXEL ASPECT RATIO
300
301 if (!LoadDriver())
302 return true;
303
304 OpenDeviceParams odp;
305
306 if (!LoadOpenDeviceParams(&odp))
307 {
308 bool err = SelectInterfaceAndDevice();
309 if (err)
310 {
311 Disconnect();
312 return true;
313 }
314 LoadOpenDeviceParams(&odp);
315 }
316
317 short err;
318
319 // Attempt connection
320 err = SBIGUnivDrvCommand(CC_OPEN_DEVICE, &odp, NULL);
321 if (err != CE_NO_ERROR)
322 {
323 Debug.Write(wxString::Format("SBIG: CC_OPEN_DEVICE err %d\n", err));
324 wxMessageBox(wxString::Format(_("Cannot open SBIG camera: Code %d"), err), _("Error"));
325 Disconnect();
326 return true;
327 }
328
329 // Establish link
330 EstablishLinkResults elr;
331 err = SBIGUnivDrvCommand(CC_ESTABLISH_LINK, NULL, &elr);
332 if (err != CE_NO_ERROR)
333 {
334 Debug.Write(wxString::Format("SBIG: CC_ESTABLISH_LINK err %d\n", err));
335 wxMessageBox(wxString::Format(_("Link to SBIG camera failed: Code %d"), err), _("Error"));
336 Disconnect();
337 return true;
338 }
339
340 // Determine if there is a tracking CCD
341 m_useTrackingCCD = false;
342 GetCCDInfoParams gcip;
343 GetCCDInfoResults0 gcir0;
344 gcip.request = CCD_INFO_TRACKING;
345 err = SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gcip, &gcir0);
346 if (err == CE_NO_ERROR)
347 {
348 int val = pConfig->Profile.GetInt("/camera/sbig/useTrackingCCD", -1);
349 if (val == -1)
350 {
351 int resp = wxMessageBox(wxString::Format(_("Tracking CCD found, use it?\n\nNo = use main image CCD")),
352 _("CCD Choice"), wxYES_NO | wxICON_QUESTION);
353 if (resp == wxYES)
354 m_useTrackingCCD = true;
355 pConfig->Profile.SetInt("/camera/sbig/useTrackingCCD", m_useTrackingCCD ? 1 : 0);
356 }
357 else
358 {
359 m_useTrackingCCD = !!val;
360 Debug.Write(wxString::Format("SBIG: using saved val m_useTrackingCCD = %d\n", m_useTrackingCCD));
361 }
362 }
363 if (!m_useTrackingCCD)
364 {
365 gcip.request = CCD_INFO_IMAGING;
366 err = SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gcip, &gcir0);
367 if (err != CE_NO_ERROR)
368 {
369 Debug.Write(wxString::Format("SBIG: CC_GET_CCD_INFO err %d\n", err));
370 wxMessageBox(_("Error getting info on main CCD"), _("Error"));
371 Disconnect();
372 return true;
373 }
374 }
375
376 MaxBinning = 1;
377 m_devicePixelSize = 0.0;
378 for (int i = 0; i < gcir0.readoutModes; i++)
379 {
380 int mode = gcir0.readoutInfo[i].mode;
381 if (mode == RM_1X1 || mode == RM_2X2)
382 {
383 int idx = mode == RM_1X1 ? 0 : 1;
384 m_imageSize[idx] = wxSize(gcir0.readoutInfo[i].width, gcir0.readoutInfo[i].height);
385 if (mode == RM_1X1)
386 {
387 unsigned long bcd = wxMax(gcir0.readoutInfo[i].pixelWidth, gcir0.readoutInfo[i].pixelHeight);
388 m_devicePixelSize = (double) bcd2long(bcd) / 100.0;
389 }
390 else // RM_2x2
391 MaxBinning = 2;
392 }
393 }
394
395 if (Binning > MaxBinning)
396 Binning = MaxBinning;
397
398 FullSize = m_imageSize[Binning - 1];
399
400 IsColor = false;
401
402 if (!m_useTrackingCCD)
403 {
404 GetCCDInfoResults6 gcir6;
405 gcip.request = CCD_INFO_EXTENDED3;
406 err = SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gcip, &gcir6);
407 if (err == CE_NO_ERROR)
408 {
409 IsColor = gcir6.ccdBits & 1; // b0 set indicates color CCD
410 }
411 }
412
413 Name = gcir0.name;
414 if (Name.Find("Color") != wxNOT_FOUND)
415 {
416 IsColor = true;
417 }
418
419 Debug.Write(wxString::Format("SBIG: %s type=%u, UseTrackingCCD=%d, MaxBin = %hu, 1x1 size %d x %d, 2x2 size %d x %d IsColor %d\n",
420 gcir0.name, gcir0.cameraType, m_useTrackingCCD, MaxBinning, m_imageSize[0].x, m_imageSize[0].y, m_imageSize[1].x, m_imageSize[1].y,
421 IsColor));
422
423 Connected = true;
424 return false;
425 }
426
Disconnect()427 bool CameraSBIG::Disconnect()
428 {
429 SBIGUnivDrvCommand(CC_CLOSE_DEVICE, NULL, NULL);
430 SBIGUnivDrvCommand(CC_CLOSE_DRIVER, NULL, NULL);
431 m_driverLoaded = false;
432 Connected = false;
433 return false;
434 }
435
GetDevicePixelSize(double * devPixelSize)436 bool CameraSBIG::GetDevicePixelSize(double *devPixelSize)
437 {
438 if (!Connected)
439 return true;
440
441 *devPixelSize = m_devicePixelSize;
442 return false;
443 }
444
InitCapture()445 void CameraSBIG::InitCapture()
446 {
447 // Set gain
448 }
449
StopExposure(EndExposureParams * eep)450 static bool StopExposure(EndExposureParams *eep)
451 {
452 short err = SBIGUnivDrvCommand(CC_END_EXPOSURE, eep, NULL);
453 return err == CE_NO_ERROR;
454 }
455
Capture(int duration,usImage & img,int options,const wxRect & subframe)456 bool CameraSBIG::Capture(int duration, usImage& img, int options, const wxRect& subframe)
457 {
458 bool TakeSubframe = UseSubframes;
459
460 FullSize = m_imageSize[Binning - 1];
461
462 if (subframe.width <= 0 || subframe.height <= 0 || subframe.GetRight() >= FullSize.GetWidth() || subframe.GetBottom() >= FullSize.GetHeight())
463 {
464 TakeSubframe = false;
465 }
466
467 StartExposureParams2 sep;
468 EndExposureParams eep;
469 QueryCommandStatusParams qcsp;
470 QueryCommandStatusResults qcsr;
471 ReadoutLineParams rlp;
472 DumpLinesParams dlp;
473
474 if (m_useTrackingCCD)
475 {
476 sep.ccd = CCD_TRACKING;
477 sep.abgState = ABG_CLK_LOW7;
478 eep.ccd = CCD_TRACKING;
479 rlp.ccd = CCD_TRACKING;
480 dlp.ccd = CCD_TRACKING;
481 }
482 else
483 {
484 sep.ccd = CCD_IMAGING;
485 sep.abgState = ABG_LOW7;
486 eep.ccd = CCD_IMAGING;
487 rlp.ccd = CCD_IMAGING;
488 dlp.ccd = CCD_IMAGING;
489 }
490
491 sep.exposureTime = (unsigned long) duration / 10;
492 sep.openShutter = ShutterClosed ? SC_CLOSE_SHUTTER : SC_OPEN_SHUTTER;
493 sep.readoutMode = rlp.readoutMode = dlp.readoutMode =
494 Binning == 1 ? RM_1X1 : RM_2X2;
495
496 if (TakeSubframe)
497 {
498 sep.top = subframe.x;
499 sep.width = subframe.width;
500 sep.left = subframe.y;
501 sep.height = subframe.height;
502 }
503 else
504 {
505 sep.top = 0;
506 sep.left = 0;
507 sep.width = (unsigned short) FullSize.GetWidth();
508 sep.height = (unsigned short) FullSize.GetHeight();
509 }
510
511 // init memory
512 if (img.Init(FullSize))
513 {
514 DisconnectWithAlert(CAPT_FAIL_MEMORY);
515 return true;
516 }
517
518 // Start exposure
519
520 short err = SBIGUnivDrvCommand(CC_START_EXPOSURE2, &sep, NULL);
521 if (err != CE_NO_ERROR)
522 {
523 DisconnectWithAlert(_("Cannot start exposure"), NO_RECONNECT);
524 return true;
525 }
526
527 CameraWatchdog watchdog(duration, GetTimeoutMs());
528
529 if (duration > 100)
530 {
531 // wait until near end of exposure
532 if (WorkerThread::MilliSleep(duration - 100, WorkerThread::INT_ANY) &&
533 (WorkerThread::TerminateRequested() || StopExposure(&eep)))
534 {
535 return true;
536 }
537 }
538
539 qcsp.command = CC_START_EXPOSURE;
540 while (true)
541 {
542 // wait for image to finish and d/l
543 wxMilliSleep(20);
544 err = SBIGUnivDrvCommand(CC_QUERY_COMMAND_STATUS, &qcsp, &qcsr);
545 if (err != CE_NO_ERROR)
546 {
547 DisconnectWithAlert(_("Cannot poll exposure"), NO_RECONNECT);
548 return true;
549 }
550 if (m_useTrackingCCD)
551 qcsr.status = qcsr.status >> 2;
552 if (qcsr.status == CS_INTEGRATION_COMPLETE)
553 break;
554 if (WorkerThread::InterruptRequested())
555 {
556 StopExposure(&eep);
557 return true;
558 }
559 if (watchdog.Expired())
560 {
561 StopExposure(&eep);
562 DisconnectWithAlert(CAPT_FAIL_TIMEOUT);
563 return true;
564 }
565 }
566
567 // End exposure
568 if (!StopExposure(&eep))
569 {
570 DisconnectWithAlert(_("Cannot stop exposure"), NO_RECONNECT);
571 return true;
572 }
573
574 // Get data
575
576 if (TakeSubframe)
577 {
578 img.Subframe = subframe;
579
580 // dump the lines above the one we want
581 dlp.lineLength = subframe.y;
582 SBIGUnivDrvCommand(CC_DUMP_LINES, &dlp, NULL);
583
584 // set up to read the part of the lines we do want
585 rlp.pixelStart = subframe.x;
586 rlp.pixelLength = subframe.width;
587
588 img.Clear();
589
590 for (int y = 0; y < subframe.height; y++)
591 {
592 unsigned short *dataptr = img.ImageData + subframe.x + (y + subframe.y) * FullSize.GetWidth();
593 err = SBIGUnivDrvCommand(CC_READOUT_LINE, &rlp, dataptr);
594 if (err != CE_NO_ERROR)
595 {
596 DisconnectWithAlert(_("Error downloading data"), NO_RECONNECT);
597 return true;
598 }
599 }
600 }
601 else
602 {
603 rlp.pixelStart = 0;
604 rlp.pixelLength = (unsigned short) FullSize.GetWidth();
605 unsigned short *dataptr = img.ImageData;
606 for (int y = 0; y < FullSize.GetHeight(); y++)
607 {
608 err = SBIGUnivDrvCommand(CC_READOUT_LINE, &rlp, dataptr);
609 dataptr += FullSize.GetWidth();
610 if (err != CE_NO_ERROR)
611 {
612 DisconnectWithAlert(_("Error downloading data"), NO_RECONNECT);
613 return true;
614 }
615 }
616 }
617
618 if (options & CAPTURE_SUBTRACT_DARK)
619 SubtractDark(img);
620 if (IsColor && Binning == 1 && (options & CAPTURE_RECON))
621 QuickLRecon(img);
622
623 return false;
624 }
625
ST4PulseGuideScope(int direction,int duration)626 bool CameraSBIG::ST4PulseGuideScope(int direction, int duration)
627 {
628 ActivateRelayParams rp;
629 rp.tXMinus = rp.tXPlus = rp.tYMinus = rp.tYPlus = 0;
630 unsigned short dur = duration / 10;
631 switch (direction) {
632 case WEST: rp.tXMinus = dur; break;
633 case EAST: rp.tXPlus = dur; break;
634 case NORTH: rp.tYMinus = dur; break;
635 case SOUTH: rp.tYPlus = dur; break;
636 }
637
638 short err = SBIGUnivDrvCommand(CC_ACTIVATE_RELAY, &rp, NULL);
639 if (err != CE_NO_ERROR) return true;
640
641 if (duration > 60) wxMilliSleep(duration - 50);
642
643 QueryCommandStatusParams qcsp;
644 qcsp.command = CC_ACTIVATE_RELAY;
645
646 MountWatchdog watchdog(duration, 5000);
647
648 while (true) { // wait for pulse to finish
649 wxMilliSleep(10);
650 QueryCommandStatusResults qcsr;
651 err = SBIGUnivDrvCommand(CC_QUERY_COMMAND_STATUS, &qcsp, &qcsr);
652 if (err != CE_NO_ERROR) {
653 pFrame->Alert(_("Cannot check SBIG relay status"));
654 return true;
655 }
656 if (!qcsr.status)
657 break;
658 if (WorkerThread::TerminateRequested())
659 return true;
660 if (watchdog.Expired())
661 {
662 pFrame->Alert(_("Timeout expired waiting for guide pulse to complete."));
663 return true;
664 }
665 }
666
667 return false;
668 }
669
MakeSBIGCamera()670 GuideCamera *SBIGCameraFactory::MakeSBIGCamera()
671 {
672 return new CameraSBIG();
673 }
674
675 #endif
676