1 /*
2  *  cam_ssag.cpp
3  *  PHD Guiding
4  *
5  *  Created by Craig Stark.
6  *  Copyright (c) 2006, 2007, 2008, 2009, 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 #include "phd.h"
35 
36 #if defined (SSAG)
37 
38 #include "camera.h"
39 #include "time.h"
40 #include "image_math.h"
41 
42 #include <wx/stdpaths.h>
43 #include <wx/textfile.h>
44 //wxTextFile *qglogfile;
45 
46 #include "cam_ssag.h"
47 
48 class CameraSSAG : public GuideCamera
49 {
50 public:
51     bool    Capture(int duration, usImage& img, int options, const wxRect& subframe) override;
52     bool    Connect(const wxString& camId) override;
53     bool    Disconnect() override;
54     void    InitCapture() override;
55 
56     bool    ST4PulseGuideScope(int direction, int duration) override;
57     void    ClearGuidePort();
58 
HasNonGuiCapture()59     bool HasNonGuiCapture() override { return true; }
ST4HasNonGuiMove()60     bool ST4HasNonGuiMove() override { return true; }
61     wxByte BitsPerPixel() override;
62     bool    GetDevicePixelSize(double *devPixelSize) override;
63 
64     CameraSSAG();
65 };
66 
67 // QHY CMOS guide camera version
68 // Tom's driver
69 
70 #include <Setupapi.h>
71 
72 #define INITGUID
73 #include <devpkey.h>
74 
75 extern int ushort_compare(const void * a, const void * b);
76 
77 #if DONE_SUPPORTING_XP // this is the newer API, but it does not work on Windows XP
78 
GetDiPropStr(HDEVINFO h,SP_DEVINFO_DATA * data,const DEVPROPKEY & key,WCHAR * buf,DWORD size)79 static bool GetDiPropStr(HDEVINFO h, SP_DEVINFO_DATA *data, const DEVPROPKEY& key, WCHAR *buf, DWORD size)
80 {
81     DEVPROPTYPE proptype;
82     return SetupDiGetDeviceProperty(h, data, &key, &proptype, (PBYTE)buf, size, &size, 0) &&
83         proptype == DEVPROP_TYPE_STRING;
84 }
85 
86 #else
87 
QueryValue(const wxRegKey & rk,const wxString & key,wxString & val)88 static bool QueryValue(const wxRegKey& rk, const wxString& key, wxString& val)
89 {
90     // prevent pop-up message if key does not exist
91     bool save = wxLog::EnableLogging(false);
92     bool ok = rk.QueryValue(key, val, false);
93     wxLog::EnableLogging(save);
94     return ok;
95 }
96 
GetDiPropStr(HDEVINFO h,SP_DEVINFO_DATA * data,const wxString & key)97 static wxString GetDiPropStr(HDEVINFO h, SP_DEVINFO_DATA *data, const wxString& key)
98 {
99     wxString val;
100 
101     WCHAR buf[4096];
102     DWORD size = sizeof(buf);
103     DWORD proptype;
104     if (SetupDiGetDeviceRegistryProperty(h, data, SPDRP_DRIVER, &proptype, (PBYTE)&buf[0], size, &size))
105     {
106         wxRegKey k(wxString("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\") + buf);
107         if (!QueryValue(k, key, val))
108         {
109             Debug.AddLine("SSAG failed to get " + key + " driver property value");
110         }
111     }
112     else
113     {
114         Debug.AddLine("SSAG failed to get SDRP_DRIVER registry property for SSAG");
115     }
116 
117     return val;
118 }
119 
120 #endif // XP
121 
GetSSAGDriverVersion()122 static unsigned int GetSSAGDriverVersion()
123 {
124     // check to see if the SSAG driver is v2 ("1.2.0.0") or v4 ("3.0.0.0")
125 
126     Debug.AddLine("Checking SSAG driver version");
127 
128     bool found = false;
129     unsigned int driverVersion = 2; // assume v2
130 
131     HDEVINFO h = SetupDiGetClassDevs(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
132     if (h != INVALID_HANDLE_VALUE)
133     {
134         DWORD idx = 0;
135         SP_DEVINFO_DATA data;
136         memset(&data, 0, sizeof(data));
137         data.cbSize = sizeof(data);
138 
139         while (SetupDiEnumDeviceInfo(h, idx, &data))
140         {
141 #if DONE_SUPPORTING_XP
142             WCHAR buf[4096];
143 
144             if (GetDiPropStr(h, &data, DEVPKEY_Device_InstanceId, buf, sizeof(buf)) &&
145                 (wcsncmp(buf, L"USB\\VID_1856&PID_0012\\", 22) == 0) ||
146                 (wcsncmp(buf, L"USB\\VID_1856&PID_0011\\", 22) == 0))
147             {
148                 Debug.AddLine(wxString::Format("Found SSAG device %s", buf));
149                 if (GetDiPropStr(h, &data, DEVPKEY_Device_DriverVersion, buf, sizeof(buf)))
150                 {
151                     Debug.AddLine(wxString::Format("SSAG driver version is %s", wxString(buf)));
152                     int v;
153                     if (swscanf(buf, L"%d", &v) == 1 && v >= 3)
154                     {
155                         driverVersion = 4;
156                     }
157                 }
158                 found = true;
159                 break;
160             }
161 #else
162             WCHAR buf[4096];
163             DWORD size = sizeof(buf);
164             DWORD proptype;
165 
166             if (SetupDiGetDeviceRegistryProperty(h, &data, SPDRP_HARDWAREID, &proptype, (PBYTE)&buf[0], size, &size) &&
167                 (wcsncmp(buf, L"USB\\VID_1856&PID_0012", 21) == 0 ||
168                  wcsncmp(buf, L"USB\\VID_1856&PID_0011", 21) == 0))
169             {
170                 Debug.AddLine(wxString::Format("Found SSAG device %s", buf));
171                 wxString ver = GetDiPropStr(h, &data, "DriverVersion");
172                 if (ver.Length())
173                 {
174                     Debug.AddLine(wxString::Format("SSAG driver version is %s", ver));
175                     int v;
176                     if (wxSscanf(ver, "%d", &v) == 1 && v >= 3)
177                     {
178                         driverVersion = 4;
179                     }
180                 }
181                 found = true;
182                 break;
183             }
184 #endif // XP
185 
186             ++idx;
187         }
188 
189         SetupDiDestroyDeviceInfoList(h);
190     }
191 
192     if (!found)
193         Debug.AddLine("No SSAG device was found");
194 
195     return driverVersion;
196 }
197 
198 // declare function pointers for imported functions
199 
200 #define F1(r,f,a1)             typedef r (_stdcall *f##_t)(a1);             static f##_t f;
201 #define F2(r,f,a1,a2)          typedef r (_stdcall *f##_t)(a1,a2);          static f##_t f;
202 #define F5(r,f,a1,a2,a3,a4,a5) typedef r (_stdcall *f##_t)(a1,a2,a3,a4,a5); static f##_t f;
203 #include "cameras/_SSAGIF.h"
204 #undef F1
205 #undef F2
206 #undef F5
207 
208 // initialize the function pointers
209 
InitProcs(HINSTANCE hinst)210 static bool InitProcs(HINSTANCE hinst)
211 {
212     bool ok = false;
213 
214     try
215     {
216 
217 #define F(f,n) \
218     if ((f = (f##_t) GetProcAddress(hinst, n)) == NULL) \
219         throw ERROR_INFO("SSAGIF DLL missing " n);
220 #define F1(r,f,a1) F(f,#f)
221 #define F2(r,f,a1,a2) F(f,#f)
222 #define F5(r,f,a1,a2,a3,a4,a5) F(f,#f)
223 #include "cameras/_SSAGIF.h"
224 #undef F1
225 #undef F2
226 #undef F5
227 #undef F
228 
229         ok = true;
230     }
231     catch (const wxString& msg)
232     {
233         POSSIBLY_UNUSED(msg);
234     }
235 
236     return ok;
237 }
238 
239 static HINSTANCE s_dllinst;
240 
241 //
242 // load the SSAGv2 or SSAGv4 DLL based on the SSAG driver version and load the addresses of the imported functions
243 //
LoadSSAGIFDll()244 static bool LoadSSAGIFDll()
245 {
246     bool ok = false;
247 
248     try
249     {
250         unsigned int driverVersion = GetSSAGDriverVersion();
251 
252         LPCWSTR libname;
253         if (driverVersion == 2)
254             libname = _T("SSAGIFv2.dll");
255         else if (driverVersion == 4)
256             libname = _T("SSAGIFv4.dll");
257         else
258             throw ERROR_INFO("unexpected SSAG driver version!");
259 
260         wxASSERT(s_dllinst == NULL);
261         Debug.AddLine(wxString::Format("Loading SSAG dll %s", libname));
262         s_dllinst = LoadLibrary(libname);
263         if (s_dllinst == NULL)
264             throw ERROR_INFO("SSAG LoadLibrary failed");
265 
266         if (!InitProcs(s_dllinst))
267             throw ERROR_INFO("SSAG failed to load required symbols");
268 
269         ok = true;
270     }
271     catch (const wxString& msg)
272     {
273         POSSIBLY_UNUSED(msg);
274         if (s_dllinst != NULL)
275         {
276             FreeLibrary(s_dllinst);
277             s_dllinst = NULL;
278         }
279     }
280 
281     return ok;
282 }
283 
UnloadSSAGIFDll()284 static void UnloadSSAGIFDll()
285 {
286     if (s_dllinst != NULL)
287     {
288         Debug.AddLine("Unloading SSAG DLL");
289         FreeLibrary(s_dllinst);
290         s_dllinst = NULL;
291     }
292 }
293 
CameraSSAG()294 CameraSSAG::CameraSSAG()
295 {
296     Connected = false;
297     Name = _T("StarShoot Autoguider");
298     FullSize = wxSize(1280, 1024);
299     m_hasGuideOutput = true;
300     HasGainControl = true;
301 }
302 
GetDevicePixelSize(double * devPixelSize)303 bool CameraSSAG::GetDevicePixelSize(double *devPixelSize)
304 {
305     *devPixelSize = 5.2;
306     return false;
307 }
308 
BitsPerPixel()309 wxByte CameraSSAG::BitsPerPixel()
310 {
311     return 16;
312 }
313 
Connect(const wxString & camId)314 bool CameraSSAG::Connect(const wxString& camId)
315 {
316     // returns true on error
317 
318     if (!LoadSSAGIFDll())
319         return true;
320 
321     wxYield();
322 
323     if (!_SSAG_openUSB())
324     {
325         UnloadSSAGIFDll();
326         return true;
327     }
328 
329     wxYield();
330 
331     _SSAG_SETBUFFERMODE(0);
332     Connected = true;
333 
334     //qglogfile = new wxTextFile(Debug.GetLogDir() + PATHSEPSTR + _T("PHD_QGuide_log.txt"));
335     //qglogfile->AddLine(wxNow() + ": QGuide connected"); //qglogfile->Write();
336 
337     wxYield();
338 
339     return false;
340 }
341 
ST4PulseGuideScope(int direction,int duration)342 bool CameraSSAG::ST4PulseGuideScope(int direction, int duration)
343 {
344     int reg = 0;
345     int dur = duration / 10;
346 
347     //qglogfile->AddLine(wxString::Format("Sending guide dur %d",dur)); //qglogfile->Write();
348 
349     if (dur >= 255) dur = 254; // Max guide pulse is 2.54s -- 255 keeps it on always
350 
351     // Output pins are NC, Com, RA+(W), Dec+(N), Dec-(S), RA-(E) ??  http://www.starlight-xpress.co.uk/faq.htm
352     switch (direction) {
353         case WEST: reg = 0x80; break;   // 0111 0000
354         case NORTH: reg = 0x40; break;  // 1011 0000
355         case SOUTH: reg = 0x20; break;  // 1101 0000
356         case EAST: reg = 0x10;  break;  // 1110 0000
357         default: return true; // bad direction passed in
358     }
359     _SSAG_GuideCommand(reg, dur);
360 
361     WorkerThread::MilliSleep(duration + 10);
362 
363     //qglogfile->AddLine("Done"); //qglogfile->Write();
364 
365     return false;
366 }
367 
ClearGuidePort()368 void CameraSSAG::ClearGuidePort()
369 {
370 }
371 
InitCapture()372 void CameraSSAG::InitCapture()
373 {
374     _SSAG_ProgramCamera(0, 0, 1280, 1024, (GuideCameraGain * 63 / 100));
375     _SSAG_SetNoiseReduction(0);
376 }
377 
Disconnect()378 bool CameraSSAG::Disconnect()
379 {
380     _SSAG_closeUSB();
381     UnloadSSAGIFDll();
382     Connected = false;
383     //qglogfile->AddLine(wxNow() + ": Disconnecting"); //qglogfile->Write(); //qglogfile->Close();
384     return false;
385 }
386 
StopExposure()387 static bool StopExposure()
388 {
389     // the v2 DLL has a function _SSAG_CancelExposure, and v4 has CancelExposure
390     // though I am not sure if they have any parameters or return values. Testing
391     // my SSAG with the v4 lib seems to work fine without calling this, so I'm
392     // leaving it alone for now.
393     Debug.AddLine("SSAG: StopExposure");
394     // _SSAG_CancelExposure();
395     return true;
396 }
397 
Capture(int duration,usImage & img,int options,const wxRect & subframe)398 bool CameraSSAG::Capture(int duration, usImage& img, int options, const wxRect& subframe)
399 {
400     // Only does full frames
401 
402     unsigned short *dptr;
403     int xsize = FullSize.GetWidth();
404     int ysize = FullSize.GetHeight();
405     bool firstimg = true;
406 
407     //qglogfile->AddLine(wxString::Format("Capturing dur %d",duration)); //qglogfile->Write();
408 
409     _SSAG_ProgramCamera(0, 0, 1280, 1024, (GuideCameraGain * 63 / 100));
410 
411     if (img.Init(FullSize))
412     {
413         DisconnectWithAlert(CAPT_FAIL_MEMORY);
414         return true;
415     }
416 
417     CameraWatchdog watchdog(duration, GetTimeoutMs());
418 
419     _SSAG_ThreadedExposure(duration, NULL);
420 
421     //qglogfile->AddLine("Exposure programmed"); //qglogfile->Write();
422 
423     if (duration > 100)
424     {
425         if (WorkerThread::MilliSleep(duration - 100, WorkerThread::INT_ANY) &&
426             (WorkerThread::TerminateRequested() || StopExposure()))
427         {
428             return true;
429         }
430     }
431 
432     while (_SSAG_isExposing())
433     {
434         wxMilliSleep(50);
435         if (WorkerThread::InterruptRequested() &&
436             (WorkerThread::TerminateRequested() || StopExposure()))
437         {
438             return true;
439         }
440         if (watchdog.Expired())
441         {
442             DisconnectWithAlert(CAPT_FAIL_TIMEOUT);
443             return true;
444         }
445     }
446 
447     //qglogfile->AddLine("Exposure done"); //qglogfile->Write();
448 
449     dptr = img.ImageData;
450     _SSAG_GETBUFFER(dptr, img.NPixels * 2);
451 
452     if (options & CAPTURE_SUBTRACT_DARK) SubtractDark(img);
453 
454     //qglogfile->AddLine("Image loaded"); //qglogfile->Write();
455 
456     return false;
457 }
458 
MakeSSAGCamera()459 GuideCamera *SSAGCameraFactory::MakeSSAGCamera()
460 {
461     return new CameraSSAG();
462 }
463 
464 #endif
465