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