Instrument Neutral Distributed Interface INDI 2.2.5
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.
32static std::unique_ptr<PerfectStar> perfectStar(new PerfectStar());
33
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 // FocusAbsPosNP[0].setMin(SyncNP[0].setMin(0));
93 // FocusAbsPosNP[0].setMax(SyncNP[0].setMax(MaxPositionN[0].value));
94 // FocusAbsPosNP[0].setStep(SyncNP[0].setStep(MaxPositionN[0].value / 50.0));
95 // FocusAbsPosNP[0].getValue() = 0;
96
97 // FocusRelPosNP[0].setMax((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 2);
98 // FocusRelPosNP[0].setStep(FocusRelPosNP[0].getMax() / 100.0);
99 // FocusRelPosNP[0].setValue(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 FocusAbsPosNP[0].setValue(currentTicks);
135
136 getStatus(&status);
137
139 {
140 if (sim)
141 {
142 if (FocusAbsPosNP[0].getValue() < targetPosition)
143 simPosition += 500;
144 else
145 simPosition -= 500;
146
147 if (std::abs((int64_t)simPosition - (int64_t)targetPosition) < 500)
148 {
149 FocusAbsPosNP[0].setValue(targetPosition);
150 simPosition = FocusAbsPosNP[0].getValue();
151 status = PS_NOOP;
152 }
153
154 FocusAbsPosNP[0].setValue(simPosition);
155 }
156
157 if (status == PS_HALT && targetPosition == FocusAbsPosNP[0].getValue())
158 {
160 {
163 }
164
166 LOG_DEBUG("Focuser reached target position.");
167 }
168 else if (status == PS_NOOP)
169 {
171 {
174 }
175
177 LOG_INFO("Focuser reached home position.");
178 }
179 }
180
182
184}
185
186bool 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 // FocusAbsPosNP[0].setMin(SyncNP[0].setMin(0));
198 // FocusAbsPosNP[0].setMax(SyncNP[0].setMax(MaxPositionN[0].value));
199 // FocusAbsPosNP[0].setStep(SyncNP[0].setStep(MaxPositionN[0].value / 50.0));
200
201 // FocusRelPosNP[0].setMax((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 2);
202 // FocusRelPosNP[0].setStep(FocusRelPosNP[0].getMax() / 100.0);
203 // FocusRelPosNP[0].setMin(0);
204
205 // FocusAbsPosNP.updateMinMax();
206 // FocusRelPosNP.updateMinMax();
207 // IUUpdateMinMax(&SyncNP);
208
209 // LOGF_INFO("Focuser absolute limits: min (%g) max (%g)", FocusAbsPosNP[0].getMin(),
210 // FocusAbsPosNP[0].getMax());
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 = FocusAbsPosNP[0].getValue() + (ticks * (dir == FOCUS_INWARD ? -1 : 1));
257
258 return MoveAbsFocuser(finalTicks);
259}
260
261bool 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
348bool 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
439bool 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 < FocusAbsPosNP[0].getValue())
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
499bool 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)
582bool 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
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.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusAbsPosNP
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
void setSupportedConnections(const uint8_t &value)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
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
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool Disconnect() override
Disconnect from device.
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
const char * getDefaultName() override
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.
HID_API_EXPORT const wchar_t *HID_API_CALL hid_error(hid_device *dev)
Get a string describing the last error which occurred.
int HID_API_EXPORT hid_exit(void)
Finalize the HIDAPI library.
Definition hid_libusb.c:407
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_OK
Definition indiapi.h:160
#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 getDeviceName
#define PERFECTSTAR_TIMEOUT
#define currentTicks
Definition robofocus.cpp:38