Instrument Neutral Distributed Interface INDI  2.0.2
perfectstar.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2015 Jasem Mutlaq. All rights reserved.
3 
4  PerfectStar Focuser
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9  .
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14  .
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 *******************************************************************************/
20 
21 #include "perfectstar.h"
22 
23 #include <cmath>
24 #include <cstring>
25 #include <memory>
26 
27 #define PERFECTSTAR_TIMEOUT 1000 /* 1000 ms */
28 
29 #define FOCUS_SETTINGS_TAB "Settings"
30 
31 // We declare an auto pointer to PerfectStar.
32 static std::unique_ptr<PerfectStar> perfectStar(new PerfectStar());
33 
35 {
38 }
39 
41 {
42  sim = isSimulation();
43 
44  if (sim)
45  {
47  return true;
48  }
49 
50  handle = hid_open(0x04D8, 0xF812, nullptr);
51 
52  if (handle == nullptr)
53  {
54  LOG_ERROR("No PerfectStar focuser found.");
55  return false;
56  }
57  else
59 
60  return (handle != nullptr);
61 }
62 
64 {
65  if (!sim)
66  {
67  hid_close(handle);
68  hid_exit();
69  }
70 
71  return true;
72 }
73 
75 {
76  return (const char *)"PerfectStar";
77 }
78 
80 {
82 
83  // Max Position
84  // IUFillNumber(&MaxPositionN[0], "Steps", "", "%.f", 0, 500000, 0., 10000);
85  // IUFillNumberVector(&MaxPositionNP, MaxPositionN, 1, getDeviceName(), "Max Position", "", FOCUS_SETTINGS_TAB, IP_RW,
86  // 0, IPS_IDLE);
87 
88  // // Sync to a particular position
89  // IUFillNumber(&SyncN[0], "Ticks", "", "%.f", 0, 100000, 100., 0.);
90  // IUFillNumberVector(&SyncNP, SyncN, 1, getDeviceName(), "Sync", "", MAIN_CONTROL_TAB, IP_RW, 0, IPS_IDLE);
91 
92  // FocusAbsPosN[0].min = SyncN[0].min = 0;
93  // FocusAbsPosN[0].max = SyncN[0].max = MaxPositionN[0].value;
94  // FocusAbsPosN[0].step = SyncN[0].step = MaxPositionN[0].value / 50.0;
95  // FocusAbsPosN[0].value = 0;
96 
97  // FocusRelPosN[0].max = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 2;
98  // FocusRelPosN[0].step = FocusRelPosN[0].max / 100.0;
99  // FocusRelPosN[0].value = 100;
100 
102 
103  return true;
104 }
105 
107 {
109 
110  // if (isConnected())
111  // {
112  // defineProperty(&SyncNP);
113  // defineProperty(&MaxPositionNP);
114  // }
115  // else
116  // {
117  // deleteProperty(SyncNP.name);
118  // deleteProperty(MaxPositionNP.name);
119  // }
120 
121  return true;
122 }
123 
125 {
126  if (!isConnected())
127  return;
128 
129  uint32_t currentTicks = 0;
130 
131  bool rc = getPosition(&currentTicks);
132 
133  if (rc)
134  FocusAbsPosN[0].value = currentTicks;
135 
136  getStatus(&status);
137 
139  {
140  if (sim)
141  {
142  if (FocusAbsPosN[0].value < targetPosition)
143  simPosition += 500;
144  else
145  simPosition -= 500;
146 
147  if (std::abs((int64_t)simPosition - (int64_t)targetPosition) < 500)
148  {
149  FocusAbsPosN[0].value = targetPosition;
150  simPosition = FocusAbsPosN[0].value;
151  status = PS_NOOP;
152  }
153 
154  FocusAbsPosN[0].value = simPosition;
155  }
156 
157  if (status == PS_HALT && targetPosition == FocusAbsPosN[0].value)
158  {
159  if (FocusRelPosNP.s == IPS_BUSY)
160  {
162  IDSetNumber(&FocusRelPosNP, nullptr);
163  }
164 
166  LOG_DEBUG("Focuser reached target position.");
167  }
168  else if (status == PS_NOOP)
169  {
170  if (FocusRelPosNP.s == IPS_BUSY)
171  {
173  IDSetNumber(&FocusRelPosNP, nullptr);
174  }
175 
177  LOG_INFO("Focuser reached home position.");
178  }
179  }
180 
181  IDSetNumber(&FocusAbsPosNP, nullptr);
182 
184 }
185 
186 bool PerfectStar::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
187 {
188  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
189  {
190  // Max Travel
191  // if (strcmp(MaxPositionNP.name, name) == 0)
192  // {
193  // IUUpdateNumber(&MaxPositionNP, values, names, n);
194 
195  // if (MaxPositionN[0].value > 0)
196  // {
197  // FocusAbsPosN[0].min = SyncN[0].min = 0;
198  // FocusAbsPosN[0].max = SyncN[0].max = MaxPositionN[0].value;
199  // FocusAbsPosN[0].step = SyncN[0].step = MaxPositionN[0].value / 50.0;
200 
201  // FocusRelPosN[0].max = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 2;
202  // FocusRelPosN[0].step = FocusRelPosN[0].max / 100.0;
203  // FocusRelPosN[0].min = 0;
204 
205  // IUUpdateMinMax(&FocusAbsPosNP);
206  // IUUpdateMinMax(&FocusRelPosNP);
207  // IUUpdateMinMax(&SyncNP);
208 
209  // LOGF_INFO("Focuser absolute limits: min (%g) max (%g)", FocusAbsPosN[0].min,
210  // FocusAbsPosN[0].max);
211  // }
212 
213  // MaxPositionNP.s = IPS_OK;
214  // IDSetNumber(&MaxPositionNP, nullptr);
215  // return true;
216  // }
217 
218  // Sync
219  // if (strcmp(SyncNP.name, name) == 0)
220  // {
221  // IUUpdateNumber(&SyncNP, values, names, n);
222  // if (!sync(SyncN[0].value))
223  // SyncNP.s = IPS_ALERT;
224  // else
225  // SyncNP.s = IPS_OK;
226 
227  // IDSetNumber(&SyncNP, nullptr);
228  // return true;
229  // }
230  }
231 
232  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
233 }
234 
236 {
237  bool rc = setPosition(targetTicks);
238 
239  if (!rc)
240  return IPS_ALERT;
241 
242  targetPosition = targetTicks;
243 
244  rc = setStatus(PS_GOTO);
245 
246  if (!rc)
247  return IPS_ALERT;
248 
250 
251  return IPS_BUSY;
252 }
253 
255 {
256  uint32_t finalTicks = FocusAbsPosN[0].value + (ticks * (dir == FOCUS_INWARD ? -1 : 1));
257 
258  return MoveAbsFocuser(finalTicks);
259 }
260 
261 bool PerfectStar::setPosition(uint32_t ticks)
262 {
263  int rc = 0;
264  unsigned char command[3];
265  unsigned char response[3];
266 
267  // 20 bit resolution position. 4 high bits + 16 lower bits
268 
269  // Send 4 high bits first
270  command[0] = 0x28;
271  command[1] = (ticks & 0x40000) >> 16;
272 
273  LOGF_DEBUG("Set Position (%ld)", ticks);
274  LOGF_DEBUG("CMD (%02X %02X)", command[0], command[1]);
275 
276  if (sim)
277  rc = 2;
278  else
279  rc = hid_write(handle, command, 2);
280 
281  if (rc < 0)
282  {
283  LOGF_ERROR("setPosition: Error writing to device (%s)", hid_error(handle));
284  return false;
285  }
286 
287  if (sim)
288  {
289  rc = 2;
290  response[0] = 0x28;
291  response[1] = command[1];
292  }
293  else
294  rc = hid_read_timeout(handle, response, 2, PERFECTSTAR_TIMEOUT);
295 
296  if (rc < 0)
297  {
298  LOGF_ERROR("setPosition: Error reading from device (%s)", hid_error(handle));
299  return false;
300  }
301 
302  LOGF_DEBUG("RES (%02X %02X)", response[0], response[1]);
303 
304  // Send lower 16 bit
305  command[0] = 0x20;
306  // Low Byte
307  command[1] = ticks & 0xFF;
308  // High Byte
309  command[2] = (ticks & 0xFF00) >> 8;
310 
311  LOGF_DEBUG("CMD (%02X %02X %02X)", command[0], command[1], command[2]);
312 
313  if (sim)
314  rc = 3;
315  else
316  rc = hid_write(handle, command, 3);
317 
318  if (rc < 0)
319  {
320  LOGF_ERROR("setPosition: Error writing to device (%s)", hid_error(handle));
321  return false;
322  }
323 
324  if (sim)
325  {
326  rc = 3;
327  response[0] = command[0];
328  response[1] = command[1];
329  response[2] = command[2];
330  }
331  else
332  rc = hid_read_timeout(handle, response, 3, PERFECTSTAR_TIMEOUT);
333 
334  if (rc < 0)
335  {
336  LOGF_ERROR("setPosition: Error reading from device (%s)", hid_error(handle));
337  return false;
338  }
339 
340  LOGF_DEBUG("RES (%02X %02X %02X)", response[0], response[1], response[2]);
341 
342  targetPosition = ticks;
343 
344  // TODO add checking later
345  return true;
346 }
347 
348 bool PerfectStar::getPosition(uint32_t *ticks)
349 {
350  int rc = 0;
351  uint32_t pos = 0;
352  unsigned char command[1];
353  unsigned char response[3];
354 
355  // 20 bit resolution position. 4 high bits + 16 lower bits
356 
357  // Get 4 high bits first
358  command[0] = 0x29;
359 
360  LOG_DEBUG("Get Position (High 4 bits)");
361  LOGF_DEBUG("CMD (%02X)", command[0]);
362 
363  if (sim)
364  rc = 2;
365  else
366  rc = hid_write(handle, command, 1);
367 
368  if (rc < 0)
369  {
370  LOGF_ERROR("getPosition: Error writing to device (%s)", hid_error(handle));
371  return false;
372  }
373 
374  if (sim)
375  {
376  rc = 2;
377  response[0] = command[0];
378  response[1] = simPosition >> 16;
379  }
380  else
381  rc = hid_read_timeout(handle, response, 2, PERFECTSTAR_TIMEOUT);
382 
383  if (rc < 0)
384  {
385  LOGF_ERROR("getPosition: Error reading from device (%s)", hid_error(handle));
386  return false;
387  }
388 
389  LOGF_DEBUG("RES (%02X %02X)", response[0], response[1]);
390 
391  // Store 4 high bits part of a 20 bit number
392  pos = response[1] << 16;
393 
394  // Get 16 lower bits
395  command[0] = 0x21;
396 
397  LOG_DEBUG("Get Position (Lower 16 bits)");
398  LOGF_DEBUG("CMD (%02X)", command[0]);
399 
400  if (sim)
401  rc = 1;
402  else
403  rc = hid_write(handle, command, 1);
404 
405  if (rc < 0)
406  {
407  LOGF_ERROR("getPosition: Error writing to device (%s)", hid_error(handle));
408  return false;
409  }
410 
411  if (sim)
412  {
413  rc = 3;
414  response[0] = command[0];
415  response[1] = simPosition & 0xFF;
416  response[2] = (simPosition & 0xFF00) >> 8;
417  }
418  else
419  rc = hid_read_timeout(handle, response, 3, PERFECTSTAR_TIMEOUT);
420 
421  if (rc < 0)
422  {
423  LOGF_ERROR("getPosition: Error reading from device (%s)", hid_error(handle));
424  return false;
425  }
426 
427  LOGF_DEBUG("RES (%02X %02X %02X)", response[0], response[1], response[2]);
428 
429  // Res[1] is lower byte and Res[2] is high byte. Combine them and add them to ticks.
430  pos |= response[1] | response[2] << 8;
431 
432  *ticks = pos;
433 
434  LOGF_DEBUG("Position: %ld", pos);
435 
436  return true;
437 }
438 
439 bool PerfectStar::setStatus(PS_STATUS targetStatus)
440 {
441  int rc = 0;
442  unsigned char command[2];
443  unsigned char response[3];
444 
445  command[0] = 0x10;
446  command[1] = (targetStatus == PS_HALT) ? 0xFF : targetStatus;
447 
448  LOGF_DEBUG("CMD (%02X %02X)", command[0], command[1]);
449 
450  if (sim)
451  rc = 2;
452  else
453  rc = hid_write(handle, command, 2);
454 
455  if (rc < 0)
456  {
457  LOGF_ERROR("setStatus: Error writing to device (%s)", hid_error(handle));
458  return false;
459  }
460 
461  if (sim)
462  {
463  rc = 3;
464  response[0] = command[0];
465  response[1] = 0;
466  response[2] = command[1];
467  status = targetStatus;
468  // Convert Goto to either "moving in" or "moving out" status
469  if (status == PS_GOTO)
470  {
471  // Moving in state
472  if (targetPosition < FocusAbsPosN[0].value)
473  status = PS_IN;
474  else
475  // Moving out state
476  status = PS_OUT;
477  }
478  }
479  else
480  rc = hid_read_timeout(handle, response, 3, PERFECTSTAR_TIMEOUT);
481 
482  if (rc < 0)
483  {
484  LOGF_ERROR("setStatus: Error reading from device (%s)", hid_error(handle));
485  return false;
486  }
487 
488  LOGF_DEBUG("RES (%02X %02X %02X)", response[0], response[1], response[2]);
489 
490  if (response[1] == 0xFF)
491  {
492  LOG_ERROR("setStatus: Invalid state change.");
493  return false;
494  }
495 
496  return true;
497 }
498 
499 bool PerfectStar::getStatus(PS_STATUS *currentStatus)
500 {
501  int rc = 0;
502  unsigned char command[1];
503  unsigned char response[2];
504 
505  command[0] = 0x11;
506 
507  LOGF_DEBUG("CMD (%02X)", command[0]);
508 
509  if (sim)
510  rc = 1;
511  else
512  rc = hid_write(handle, command, 1);
513 
514  if (rc < 0)
515  {
516  LOGF_ERROR("getStatus: Error writing to device (%s)", hid_error(handle));
517  return false;
518  }
519 
520  if (sim)
521  {
522  rc = 2;
523  response[0] = command[0];
524  response[1] = status;
525  // Halt/SetPos is state = 0 "not moving".
526  if (response[1] == PS_HALT || response[1] == PS_SETPOS)
527  response[1] = 0;
528  }
529  else
530  rc = hid_read_timeout(handle, response, 2, PERFECTSTAR_TIMEOUT);
531 
532  if (rc < 0)
533  {
534  LOGF_ERROR("getStatus: Error reading from device (%s)", hid_error(handle));
535  return false;
536  }
537 
538  LOGF_DEBUG("RES (%02X %02X)", response[0], response[1]);
539 
540  switch (response[1])
541  {
542  case 0:
543  *currentStatus = PS_HALT;
544  LOG_DEBUG("State: Not moving.");
545  break;
546 
547  case 1:
548  *currentStatus = PS_IN;
549  LOG_DEBUG("State: Moving in.");
550  break;
551 
552  case 3:
553  *currentStatus = PS_GOTO;
554  LOG_DEBUG("State: Goto.");
555  break;
556 
557  case 2:
558  *currentStatus = PS_OUT;
559  LOG_DEBUG("State: Moving out.");
560  break;
561 
562  case 5:
563  *currentStatus = PS_LOCKED;
564  LOG_DEBUG("State: Locked.");
565  break;
566 
567  default:
568  LOGF_WARN("Warning: Unknown status (%d)", response[1]);
569  return false;
570  break;
571  }
572 
573  return true;
574 }
575 
577 {
578  return setStatus(PS_HALT);
579 }
580 
581 //bool PerfectStar::sync(uint32_t ticks)
582 bool PerfectStar::SyncFocuser(uint32_t ticks)
583 {
584  bool rc = setPosition(ticks);
585 
586  if (!rc)
587  return false;
588 
589  simPosition = ticks;
590 
591  rc = setStatus(PS_SETPOS);
592 
593  return rc;
594 }
595 
596 //bool PerfectStar::saveConfigItems(FILE *fp)
597 //{
598 // INDI::Focuser::saveConfigItems(fp);
599 
600 // IUSaveConfigNumber(fp, &MaxPositionNP);
601 
602 // return true;
603 //}
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void addSimulationControl()
Add Simulation control to the driver.
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
void setSupportedConnections(const uint8_t &value)
setConnection Set Focuser connection mode. Child class should call this in the constructor before Foc...
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: perfectstar.cpp:79
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: perfectstar.cpp:40
virtual bool Disconnect() override
Disconnect from device.
Definition: perfectstar.cpp:63
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
const char * getDefaultName() override
Definition: perfectstar.cpp:74
hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
Open a HID device using a Vendor ID (VID), Product ID (PID) and optionally a serial number.
Definition: hid_libusb.c:612
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
Definition: hid_libusb.c:997
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
Write an Output report to a HID device.
Definition: hid_libusb.c:929
void HID_API_EXPORT hid_close(hid_device *dev)
Close a HID device.
Definition: hid_libusb.c:1163
HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev)
Get a string describing the last error which occurred.
Definition: hid_libusb.c:1227
int HID_API_EXPORT hid_exit(void)
Finalize the HIDAPI library.
Definition: hid_libusb.c:407
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_OK
Definition: indiapi.h:162
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define PERFECTSTAR_TIMEOUT
Definition: perfectstar.cpp:27
#define currentTicks
Definition: robofocus.cpp:42