Instrument Neutral Distributed Interface INDI 2.2.5
usbfocusv3.cpp
Go to the documentation of this file.
1/*
2 USB Focus V3
3 Copyright (C) 2016 G. Schmidt
4 Copyright (C) 2018-2023 Jarno Paananen
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#include "usbfocusv3.h"
23
24#include "indicom.h"
25
26#include <cmath>
27#include <cstring>
28#include <memory>
29
30#include <termios.h>
31#include <unistd.h>
32
33#define USBFOCUSV3_TIMEOUT 5
34
35/***************************** Class USBFocusV3 *******************************/
36
37static std::unique_ptr<USBFocusV3> usbFocusV3(new USBFocusV3());
38
40{
41 setVersion(1, 1);
42 // Can move in Absolute & Relative motions, can AbortFocuser motion, and has variable speed.
48}
49
51{
53
54 /*** init controller parameters ***/
55
56 direction = 0;
57 stepmode = 1;
58 speed = 3;
59 stepsdeg = 20;
60 tcomp_thr = 5;
61 firmware = 0;
62 maxpos = 65535;
63
64 /*** init driver parameters ***/
65
66 FocusSpeedNP[0].setMin(1);
67 FocusSpeedNP[0].setMax(3);
68 FocusSpeedNP[0].setValue(2);
69
70 // Step Mode
71 StepModeSP[UFOPHSTEPS].fill("HALF", "Half Step", ISS_ON);
72 StepModeSP[UFOPFSTEPS].fill("FULL", "Full Step", ISS_OFF);
73 StepModeSP.fill(getDeviceName(), "STEP_MODE", "Step Mode", OPTIONS_TAB, IP_RW,
75
76 // Direction
77 RotDirSP[UFOPSDIR].fill("STANDARD", "Standard rotation", ISS_ON);
78 RotDirSP[UFOPRDIR].fill("REVERSE", "Reverse rotation", ISS_OFF);
79 RotDirSP.fill(getDeviceName(), "ROTATION_MODE", "Rotation Mode", OPTIONS_TAB, IP_RW,
81
82 // Focuser temperature
83 TemperatureNP[0].fill("TEMPERATURE", "Celsius", "%6.2f", -50., 70., 0., 0.);
84 TemperatureNP.fill(getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
86
87 // Maximum Position
88 MaxPositionNP[0].fill("MAXPOSITION", "Maximum position", "%5.0f", 1., 65535., 0., 65535.);
89 MaxPositionNP.fill(getDeviceName(), "FOCUS_MAXPOSITION", "Max. Position",
91
92 // Temperature Settings
93 TemperatureSettingNP[0].fill("COEFFICIENT", "Coefficient", "%3.0f", 0., 999., 1., 15.);
94 TemperatureSettingNP[1].fill("THRESHOLD", "Threshold", "%3.0f", 0., 999., 1., 10.);
95 TemperatureSettingNP.fill(getDeviceName(), "TEMPERATURE_SETTINGS",
96 "Temp. Settings", OPTIONS_TAB, IP_RW, 0, IPS_IDLE);
97
98 // Temperature Compensation Sign
99 TempCompSignSP[UFOPNSIGN].fill("NEGATIVE", "Negative", ISS_OFF);
100 TempCompSignSP[UFOPPSIGN].fill("POSITIVE", "Positive", ISS_ON);
101 TempCompSignSP.fill(getDeviceName(), "TCOMP_SIGN", "TComp. Sign", OPTIONS_TAB,
103
104 // Compensate for temperature
105 TemperatureCompensateSP[0].fill("ENABLE", "Enable", ISS_OFF);
106 TemperatureCompensateSP[1].fill("DISABLE", "Disable", ISS_ON);
107 TemperatureCompensateSP.fill(getDeviceName(), "TEMP_COMPENSATION",
108 "Temp. Comp.", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
109
110 FocusBacklashNP[0].setMin(-65535.);
111 FocusBacklashNP[0].setMax(65535.);
112 FocusBacklashNP[0].setStep(1000.);
113 FocusBacklashNP[0].setValue(0.);
114
115 // Reset
116 ResetSP[0].fill("RESET", "Reset", ISS_OFF);
117 ResetSP.fill(getDeviceName(), "RESET", "Reset", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 0,
118 IPS_IDLE);
119
120 // Firmware version
121 FWversionNP[0].fill("FIRMWARE", "Firmware Version", "%5.0f", 0., 65535., 1., 0.);
122 FWversionNP.fill(getDeviceName(), "FW_VERSION", "Firmware", OPTIONS_TAB, IP_RO, 0,
123 IPS_IDLE);
124
125 /* Relative and absolute movement */
126 FocusRelPosNP[0].setMin(0.);
127 FocusRelPosNP[0].setMax(float(maxpos));
128 FocusRelPosNP[0].setValue(0);
129 FocusRelPosNP[0].setStep(1);
130
131 FocusAbsPosNP[0].setMin(0.);
132 FocusAbsPosNP[0].setMax(float(maxpos));
133 FocusAbsPosNP[0].setValue(0);
134 FocusAbsPosNP[0].setStep(1);
135
137
139
140 return true;
141}
142
144{
146
147 if (isConnected())
148 {
149 defineProperty(TemperatureNP);
150 defineProperty(MaxPositionNP);
151 defineProperty(StepModeSP);
152 defineProperty(RotDirSP);
153 defineProperty(MaxPositionNP);
154 defineProperty(TemperatureSettingNP);
155 defineProperty(TempCompSignSP);
156 defineProperty(TemperatureCompensateSP);
157 defineProperty(ResetSP);
158 defineProperty(FWversionNP);
159
160 GetFocusParams();
161
162 loadConfig(true);
163
164 LOG_INFO("USBFocusV3 parameters updated, focuser ready for use.");
165 }
166 else
167 {
168 deleteProperty(TemperatureNP);
169 deleteProperty(MaxPositionNP);
170 deleteProperty(StepModeSP);
171 deleteProperty(RotDirSP);
172 deleteProperty(TemperatureSettingNP);
173 deleteProperty(TempCompSignSP);
174 deleteProperty(TemperatureCompensateSP);
175 deleteProperty(ResetSP);
176 deleteProperty(FWversionNP);
177 }
178
179 return true;
180}
181
183{
184 int tries = 2;
185
186 do
187 {
188 if (Ack())
189 {
190 LOG_INFO("USBFocusV3 is online. Getting focus parameters...");
191 return true;
192 }
193 LOG_INFO("Error retrieving data from USBFocusV3, trying resync...");
194 }
195 while (--tries > 0 && Resync());
196
197 LOG_INFO("Error retrieving data from USBFocusV3, please ensure controller "
198 "is powered and the port is correct.");
199 return false;
200}
201
203{
204 return "USBFocusV3";
205}
206
207bool USBFocusV3::Resync()
208{
209 char cmd[] = " "; // Illegal command to trigger error response
210 int nbytes_written = 0, nbytes_read = 0, rc = -1;
211 char errstr[MAXRBUF];
212 char resp[UFORESLEN] = {};
213
214 // Send up to 5 space characters and wait for error
215 // response ("ER=1") after which the communication
216 // is back in sync
217 tcflush(PortFD, TCIOFLUSH);
218
219 for (int resync = 0; resync < UFOCMDLEN; resync++)
220 {
221 LOGF_INFO("Retry %d...", resync + 1);
222
223 if ((rc = tty_write(PortFD, cmd, 1, &nbytes_written)) != TTY_OK)
224 {
225 tty_error_msg(rc, errstr, MAXRBUF);
226 LOGF_ERROR("Error writing resync: %s.", errstr);
227 return false;
228 }
229
230 rc = tty_nread_section(PortFD, resp, UFORESLEN, '\r', 3, &nbytes_read);
231 if (rc == TTY_OK && nbytes_read > 0)
232 {
233 // We got a response
234 return true;
235 }
236 // We didn't get response yet, retry
237 }
238 LOG_ERROR("No valid resync response.");
239 return false;
240}
241
242bool USBFocusV3::sendCommand(const char *cmd, char *resp)
243{
244 pthread_mutex_lock(&cmdlock);
245
246 int nbytes_written = 0, nbytes_read = 0, rc = -1;
247 char errstr[MAXRBUF];
248 LOGF_DEBUG("CMD: %s.", cmd);
249
250 tcflush(PortFD, TCIOFLUSH);
251 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
252 {
253 tty_error_msg(rc, errstr, MAXRBUF);
254 LOGF_WARN("Error writing command %s: %s.", cmd, errstr);
255 pthread_mutex_unlock(&cmdlock);
256 return false;
257 }
258
259 if (resp)
260 {
261 if ((rc = tty_nread_section(PortFD, resp, UFORESLEN, '\r', USBFOCUSV3_TIMEOUT, &nbytes_read)) != TTY_OK)
262 {
263 tty_error_msg(rc, errstr, MAXRBUF);
264 LOGF_WARN("Error reading response for command %s: %s.", cmd, errstr);
265 pthread_mutex_unlock(&cmdlock);
266 return false;
267 }
268
269 // If we are moving, check for ACK * response first
270 if (moving && nbytes_read > 0 && resp[0] == UFORSACK)
271 {
272 moving = false;
273 if (nbytes_read > 1)
274 {
275 // Move rest of the response down one byte
276 nbytes_read--;
277 memmove(&resp[0], &resp[1], nbytes_read);
278 }
279 }
280
281 if (nbytes_read < 2)
282 {
283 LOGF_WARN("Invalid response for command %s: %s.", cmd, resp);
284 pthread_mutex_unlock(&cmdlock);
285 return false;
286 }
287 // Be sure we receive the \n\r to not truncate the response
288 int rc = sscanf(resp, "%s\n\r", resp);
289 if (rc <= 0)
290 {
291 LOGF_WARN("Invalid response for command %s: missing cr+lf", cmd);
292 pthread_mutex_unlock(&cmdlock);
293 return false;
294 }
295 LOGF_DEBUG("RES: %s.", resp);
296 }
297 pthread_mutex_unlock(&cmdlock);
298 return true;
299}
300
301// Special version to work around command FTAXXA, which replies without \n\r
302bool USBFocusV3::sendCommandSpecial(const char *cmd, char *resp)
303{
304 pthread_mutex_lock(&cmdlock);
305
306 int nbytes_written = 0, nbytes_read = 0, rc = -1;
307 char errstr[MAXRBUF];
308 LOGF_DEBUG("CMD: %s.", cmd);
309
310 tcflush(PortFD, TCIOFLUSH);
311 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
312 {
313 tty_error_msg(rc, errstr, MAXRBUF);
314 LOGF_WARN("Error writing command %s: %s.", cmd, errstr);
315 pthread_mutex_unlock(&cmdlock);
316 return false;
317 }
318
319 if (resp)
320 {
321 // We assume answer A=x where x is 0 or 1
322 if ((rc = tty_read(PortFD, resp, 3, USBFOCUSV3_TIMEOUT, &nbytes_read)) != TTY_OK)
323 {
324 tty_error_msg(rc, errstr, MAXRBUF);
325 LOGF_WARN("Error reading response for command %s: %s.", cmd, errstr);
326 pthread_mutex_unlock(&cmdlock);
327 return false;
328 }
329 resp[nbytes_read] = '\0';
330 LOGF_DEBUG("RES: %s.", resp);
331 }
332 pthread_mutex_unlock(&cmdlock);
333 return true;
334}
335
336bool USBFocusV3::Ack()
337{
338 char resp[UFORESLEN] = {};
339 tcflush(PortFD, TCIOFLUSH);
340
341 if (!sendCommand(UFOCDEVID, resp))
342 return false;
343
344 if (strncmp(resp, UFOID, UFORESLEN) != 0)
345 {
346 LOGF_ERROR("USBFocusV3 not properly identified! Answer was: %s.", resp);
347 return false;
348 }
349 return true;
350}
351
353{
354 char resp[UFORESLEN] = {};
355 if (!sendCommand(UFOCREADPARAM, resp))
356 return false;
357
358 sscanf(resp, "C=%u-%u-%u-%u-%u-%u-%u", &direction, &stepmode, &speed, &stepsdeg, &tcomp_thr, &firmware, &maxpos);
359 return true;
360}
361
362bool USBFocusV3::updateStepMode()
363{
364 StepModeSP.reset();
365
366 if (stepmode == UFOPHSTEPS)
367 StepModeSP[UFOPHSTEPS].setState(ISS_ON);
368 else if (stepmode == UFOPFSTEPS)
369 StepModeSP[UFOPFSTEPS].setState(ISS_ON);
370 else
371 {
372 LOGF_ERROR("Unknown error: focuser step value (%d)", stepmode);
373 return false;
374 }
375 return true;
376}
377
378bool USBFocusV3::updateRotDir()
379{
380 RotDirSP.reset();
381
382 if (direction == UFOPSDIR)
383 RotDirSP[UFOPSDIR].setState(ISS_ON);
384 else if (direction == UFOPRDIR)
385 RotDirSP[UFOPRDIR].setState(ISS_ON);
386 else
387 {
388 LOGF_ERROR("Unknown error: rotation direction (%d)", direction);
389 return false;
390 }
391
392 return true;
393}
394
395bool USBFocusV3::updateTemperature()
396{
397 char resp[UFORESLEN] = {};
398 int j = 0;
399
400 // retry up to 5 time to fix data desyncronization.
401 // see: https://bitbucket.org/usb-focus/ascom-driver/src/f0ec7d0faee605f9a3d9e2c4d599f9d537211201/USB_Focus/Driver.cs?at=master&fileviewer=file-view-default#Driver.cs-898
402
403 while (j <= 5)
404 {
405 if (sendCommand(UFOCREADTEMP, resp))
406 {
407
408 float temp;
409 int rc = sscanf(resp, "T=%f", &temp);
410
411 if (rc > 0)
412 {
413 TemperatureNP[0].setValue(temp);
414 break;
415 }
416 else
417 {
418 LOGF_DEBUG("Unknown error: focuser temperature value (%s)", resp);
419 }
420 }
421 j++;
422 }
423 if (j > 5)
424 {
425 LOGF_ERROR("Unknown error: focuser temperature value (%s)", resp);
426 return false;
427 }
428
429 return true;
430}
431
432bool USBFocusV3::updateFWversion()
433{
434 FWversionNP[0].setValue(firmware);
435 return true;
436}
437
438bool USBFocusV3::updatePosition()
439{
440 char resp[UFORESLEN] = {};
441 int j = 0;
442
443 // retry up to 5 time to fix data desyncronization.
444 // see: https://bitbucket.org/usb-focus/ascom-driver/src/f0ec7d0faee605f9a3d9e2c4d599f9d537211201/USB_Focus/Driver.cs?at=master&fileviewer=file-view-default#Driver.cs-746
445
446 while (j <= 5)
447 {
448 if (sendCommand(UFOCREADPOS, resp))
449 {
450
451 int pos = -1;
452 int rc = sscanf(resp, "P=%u", &pos);
453
454 if (rc > 0)
455 {
456 FocusAbsPosNP[0].setValue(pos);
457 break;
458 }
459 else
460 {
461 LOGF_DEBUG("Unknown error: focuser position value (%s)", resp);
462 }
463 }
464 j++;
465 }
466 if (j > 5)
467 {
468 LOGF_ERROR("Unknown error: focuser position value (%s)", resp);
469 return false;
470 }
471
472 return true;
473}
474
475bool USBFocusV3::updateMaxPos()
476{
477 MaxPositionNP[0].setValue(maxpos);
478 FocusAbsPosNP[0].setMax(maxpos);
479 return true;
480}
481
482bool USBFocusV3::updateTempCompSettings()
483{
484 TemperatureSettingNP[0].setValue(stepsdeg);
485 TemperatureSettingNP[1].setValue(tcomp_thr);
486 return true;
487}
488
489bool USBFocusV3::updateTempCompSign()
490{
491 char resp[UFORESLEN] = {};
492 // This command seems to have a bug in firmware 1505 that it
493 // doesn't send \n\r in reply like all others except movement
494 // commands so use a special version for it
495 if (!sendCommandSpecial(UFOCGETSIGN, resp))
496 return false;
497
498 unsigned int sign;
499 int rc = sscanf(resp, "A=%u", &sign);
500
501 if (rc > 0)
502 {
503 TempCompSignSP.reset();
504
505 if (sign == UFOPNSIGN)
506 TempCompSignSP[UFOPNSIGN].setState(ISS_ON);
507 else if (sign == UFOPPSIGN)
508 TempCompSignSP[UFOPPSIGN].setState(ISS_ON);
509 else
510 {
511 LOGF_ERROR("Unknown error: temp. comp. sign (%d)", sign);
512 return false;
513 }
514 }
515 else
516 {
517 LOGF_ERROR("Unknown error: temp. comp. sign value (%s)", resp);
518 return false;
519 }
520
521 return true;
522}
523
524bool USBFocusV3::updateSpeed()
525{
526 int drvspeed;
527
528 switch (speed)
529 {
530 case UFOPSPDAV:
531 drvspeed = 3;
532 break;
533 case UFOPSPDSL:
534 drvspeed = 2;
535 break;
536 case UFOPSPDUS:
537 drvspeed = 1;
538 break;
539 default:
540 drvspeed = 0;
541 break;
542 }
543
544 if (drvspeed != 0)
545 {
546 currentSpeed = drvspeed;
547 FocusSpeedNP[0].setValue(drvspeed);
548 }
549 else
550 {
551 LOGF_ERROR("Unknown error: focuser speed value (%d)", speed);
552 return false;
553 }
554
555 return true;
556}
557
558bool USBFocusV3::setAutoTempCompThreshold(unsigned int thr)
559{
560 char cmd[UFOCMDLEN + 1];
561 snprintf(cmd, UFOCMDLEN + 1, UFOCSETTCTHR, thr);
562
563 char resp[UFORESLEN] = {};
564 if (!sendCommand(cmd, resp))
565 return false;
566
567 if (strncmp(resp, UFORSDONE, strlen(UFORSDONE)) == 0)
568 {
569 tcomp_thr = thr;
570 return true;
571 }
572
573 LOG_ERROR("setAutoTempCompThreshold error: did not receive DONE.");
574
575 return false;
576}
577
578bool USBFocusV3::setTemperatureCoefficient(unsigned int coefficient)
579{
580 char cmd[UFOCMDLEN + 1];
581 snprintf(cmd, UFOCMDLEN + 1, UFOCSETSTDEG, coefficient);
582
583 char resp[UFORESLEN] = {};
584 if (!sendCommand(cmd, resp))
585 return false;
586
587 if (strncmp(resp, UFORSDONE, strlen(UFORSDONE)) == 0)
588 {
589 stepsdeg = coefficient;
590 return true;
591 }
592
593 LOG_ERROR("setTemperatureCoefficient error: did not receive DONE.");
594
595 return false;
596}
597
598bool USBFocusV3::reset()
599{
600 char resp[UFORESLEN] = {};
601 if (!sendCommand(UFOCRESET, resp))
602 return false;
603
604 GetFocusParams();
605
606 return true;
607}
608
609bool USBFocusV3::MoveFocuserUF(FocusDirection dir, unsigned int rticks)
610{
611 char cmd[UFOCMDLEN + 1];
612
613 unsigned int ticks;
614
615 if ((dir == FOCUS_INWARD) && (rticks > FocusAbsPosNP[0].getValue()))
616 {
617 ticks = FocusAbsPosNP[0].getValue();
618 LOGF_WARN("Requested %u ticks but inward movement has been limited to %u ticks", rticks, ticks);
619 }
620 else if ((dir == FOCUS_OUTWARD) && ((FocusAbsPosNP[0].getValue() + rticks) > MaxPositionNP[0].getValue()))
621 {
622 ticks = MaxPositionNP[0].getValue() - FocusAbsPosNP[0].getValue();
623 LOGF_WARN("Requested %u ticks but outward movement has been limited to %u ticks", rticks, ticks);
624 }
625 else
626 {
627 ticks = rticks;
628 }
629
630 if (dir == FOCUS_INWARD)
631 {
632 if (backlashMove == false && backlashIn == true && backlashSteps != 0)
633 {
634 ticks += backlashSteps;
635 backlashTargetPos = targetPos - backlashSteps;
636 backlashMove = true;
637 }
638 snprintf(cmd, UFOCMDLEN + 1, UFOCMOVEIN, ticks);
639 }
640 else
641 {
642 if (backlashMove == false && backlashIn == false && backlashSteps != 0)
643 {
644 ticks += backlashSteps;
645 backlashTargetPos = targetPos + backlashSteps;
646 backlashMove = true;
647 }
648 snprintf(cmd, UFOCMDLEN + 1, UFOCMOVEOUT, ticks);
649 }
650 moving = true;
651 return sendCommand(cmd, nullptr);
652}
653
654bool USBFocusV3::setStepMode(FocusStepMode mode)
655{
656 char resp[UFORESLEN] = {};
657 char cmd[UFOCMDLEN + 1];
658
659 if (mode == FOCUS_HALF_STEP)
660 snprintf(cmd, UFOCMDLEN + 1, "%s", UFOCSETHSTEPS);
661 else
662 snprintf(cmd, UFOCMDLEN + 1, "%s", UFOCSETFSTEPS);
663
664 if (!sendCommand(cmd, resp))
665 return false;
666
667 stepmode = mode;
668 return true;
669}
670
671bool USBFocusV3::setRotDir(unsigned int dir)
672{
673 char resp[UFORESLEN] = {};
674 char cmd[UFOCMDLEN + 1];
675
676 if (dir == UFOPSDIR)
677 snprintf(cmd, UFOCMDLEN + 1, "%s", UFOCSETSDIR);
678 else
679 snprintf(cmd, UFOCMDLEN + 1, "%s", UFOCSETRDIR);
680
681 if (!sendCommand(cmd, resp))
682 return false;
683
684 direction = dir;
685 return true;
686}
687
688bool USBFocusV3::setMaxPos(unsigned int maxp)
689{
690 char cmd[UFOCMDLEN + 1];
691 char resp[UFORESLEN] = {};
692
693 if (maxp >= 1 && maxp <= 65535)
694 {
695 snprintf(cmd, UFOCMDLEN + 1, UFOCSETMAX, maxp);
696 }
697 else
698 {
699 LOGF_ERROR("Focuser max. pos. value %d out of bounds", maxp);
700 return false;
701 }
702
703 if (!sendCommand(cmd, resp))
704 return false;
705
706 if (strncmp(resp, UFORSDONE, strlen(UFORSDONE)) == 0)
707 {
708 maxpos = maxp;
709 FocusAbsPosNP[0].setMax(maxpos);
710 return true;
711 }
712
713 LOG_ERROR("setMaxPos error: did not receive DONE.");
714
715 return false;
716}
717
718bool USBFocusV3::setSpeed(unsigned short drvspeed)
719{
720 char cmd[UFOCMDLEN + 1];
721 char resp[UFORESLEN] = {};
722
723 unsigned int spd;
724
725 switch (drvspeed)
726 {
727 case 3:
728 spd = UFOPSPDAV;
729 break;
730 case 2:
731 spd = UFOPSPDSL;
732 break;
733 case 1:
734 spd = UFOPSPDUS;
735 break;
736 default:
737 spd = UFOPSPDERR;
738 break;
739 }
740
741 if (spd != UFOPSPDERR)
742 {
743 snprintf(cmd, UFOCMDLEN + 1, UFOCSETSPEED, spd);
744 }
745 else
746 {
747 LOGF_ERROR("Focuser speed value %d out of bounds", drvspeed);
748 return false;
749 }
750
751 if (!sendCommand(cmd, resp))
752 return false;
753
754 if (strncmp(resp, UFORSDONE, strlen(UFORSDONE)) == 0)
755 {
756 speed = spd;
757 return true;
758 }
759
760 LOG_ERROR("setSpeed error: did not receive DONE.");
761
762 return false;
763}
764
765bool USBFocusV3::setTemperatureCompensation(bool enable)
766{
767 char cmd[UFOCMDLEN + 1];
768 char resp[UFORESLEN] = {};
769
770 if (enable)
771 snprintf(cmd, UFOCMDLEN + 1, "%s", UFOCSETAUTO);
772 else
773 snprintf(cmd, UFOCMDLEN + 1, "%s", UFOCSETMANU);
774
775 if (!sendCommand(cmd, resp))
776 return false;
777
778 return true;
779}
780
781bool USBFocusV3::setTempCompSign(unsigned int sign)
782{
783 char cmd[UFOCMDLEN + 1];
784 char resp[UFORESLEN] = {};
785
786 snprintf(cmd, UFOCMDLEN + 1, UFOCSETSIGN, sign);
787
788 if (!sendCommand(cmd, resp))
789 return false;
790
791 if (strncmp(resp, UFORSDONE, strlen(UFORSDONE)) == 0)
792 {
793 return true;
794 }
795
796 LOG_ERROR("setTempCompSign error: did not receive DONE.");
797
798 return false;
799}
800
801bool USBFocusV3::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
802{
803 if (!dev || strcmp(dev, getDeviceName()))
804 return false;
805
806 if (StepModeSP.isNameMatch(name))
807 {
808 int current_mode = StepModeSP.findOnSwitchIndex();
809 StepModeSP.update(states, names, n);
810 int target_mode = StepModeSP.findOnSwitchIndex();
811 if (current_mode != target_mode)
812 {
813 bool rc;
814 if (target_mode == UFOPHSTEPS)
815 rc = setStepMode(FOCUS_HALF_STEP);
816 else
817 rc = setStepMode(FOCUS_FULL_STEP);
818
819 if (!rc)
820 {
821 StepModeSP.reset();
822 StepModeSP[current_mode].setState(ISS_ON);
823 StepModeSP.setState(IPS_ALERT);
824 StepModeSP.apply();
825 return false;
826 }
827 }
828 StepModeSP.setState(IPS_OK);
829 StepModeSP.apply();
830 return true;
831 }
832
833 if (RotDirSP.isNameMatch(name))
834 {
835 int current_mode = RotDirSP.findOnSwitchIndex();
836 RotDirSP.update(states, names, n);
837 int target_mode = RotDirSP.findOnSwitchIndex();
838 if (current_mode != target_mode)
839 {
840 if (!setRotDir(target_mode))
841 {
842 RotDirSP.reset();
843 RotDirSP[current_mode].setState(ISS_ON);
844 RotDirSP.setState(IPS_ALERT);
845 RotDirSP.apply();
846 return false;
847 }
848 }
849
850 RotDirSP.setState(IPS_OK);
851 RotDirSP.apply();
852 return true;
853 }
854
855 if (TemperatureCompensateSP.isNameMatch(name))
856 {
857 int last_index = TemperatureCompensateSP.findOnSwitchIndex();
858 TemperatureCompensateSP.update(states, names, n);
859 int target_index = TemperatureCompensateSP.findOnSwitchIndex();
860
861 if (last_index != target_index)
862 {
863 if (!setTemperatureCompensation((TemperatureCompensateSP[0].getState() == ISS_ON)))
864 {
865 TemperatureCompensateSP.setState(IPS_ALERT);
866 TemperatureCompensateSP.reset();
867 TemperatureCompensateSP[last_index].setState(ISS_ON);
868 TemperatureCompensateSP.apply();
869 return false;
870 }
871 }
872 TemperatureCompensateSP.setState(IPS_OK);
873 TemperatureCompensateSP.apply();
874 return true;
875 }
876
877 if (TempCompSignSP.isNameMatch(name))
878 {
879 int current_mode = TempCompSignSP.findOnSwitchIndex();
880 TempCompSignSP.update(states, names, n);
881 int target_mode = TempCompSignSP.findOnSwitchIndex();
882 if (current_mode != target_mode)
883 {
884 if (!setTempCompSign(target_mode))
885 {
886 TempCompSignSP.reset();
887 TempCompSignSP[current_mode].setState(ISS_ON);
888 TempCompSignSP.setState(IPS_ALERT);
889 TempCompSignSP.apply();
890 return false;
891 }
892 }
893 TempCompSignSP.setState(IPS_OK);
894 TempCompSignSP.apply();
895 return true;
896 }
897
898 if (ResetSP.isNameMatch(name))
899 {
900 ResetSP.reset();
901
902 if (reset())
903 ResetSP.setState(IPS_OK);
904 else
905 ResetSP.setState(IPS_ALERT);
906
907 ResetSP.apply();
908 return true;
909 }
910 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
911}
912
913bool USBFocusV3::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
914{
915 if (!dev || strcmp(dev, getDeviceName()))
916 return false;
917
918 if (MaxPositionNP.isNameMatch(name))
919 {
920 MaxPositionNP.update(values, names, n);
921 if (!setMaxPos(MaxPositionNP[0].getValue()))
922 {
923 MaxPositionNP.setState(IPS_ALERT);
924 MaxPositionNP.apply();
925 return false;
926 }
927 MaxPositionNP.setState(IPS_OK);
928 MaxPositionNP.apply();
929 return true;
930 }
931
932 if (TemperatureSettingNP.isNameMatch(name))
933 {
934 TemperatureSettingNP.update(values, names, n);
935 if (!setAutoTempCompThreshold(TemperatureSettingNP[1].getValue()) ||
936 !setTemperatureCoefficient(TemperatureSettingNP[UFOPNSIGN].getValue()))
937 {
938 TemperatureSettingNP.setState(IPS_ALERT);
939 TemperatureSettingNP.apply();
940 return false;
941 }
942
943 TemperatureSettingNP.setState(IPS_OK);
944 TemperatureSettingNP.apply();
945 return true;
946 }
947 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
948}
949
950void USBFocusV3::GetFocusParams()
951{
953
954 if (updatePosition())
956
957 if (updateMaxPos())
958 {
959 MaxPositionNP.apply();
961 }
962
963 if (updateTemperature())
964 TemperatureNP.apply();
965
966 if (updateTempCompSettings())
967 TemperatureSettingNP.apply();
968
969 if (updateTempCompSign())
970 TempCompSignSP.apply();
971
972 if (updateSpeed())
974
975 if (updateStepMode())
976 StepModeSP.apply();
977
978 if (updateRotDir())
979 RotDirSP.apply();
980
981 if (updateFWversion())
982 FWversionNP.apply();
983}
984
986{
987 if (!setSpeed(speed))
988 return false;
989
990 currentSpeed = speed;
991
994
995 return true;
996}
997
999{
1000 long ticks;
1001
1002 targetPos = targetTicks;
1003
1004 ticks = targetPos - FocusAbsPosNP[0].getValue();
1005
1006 bool rc = false;
1007
1008 if (ticks < 0)
1009 rc = MoveFocuserUF(FOCUS_INWARD, -ticks);
1010 else if (ticks > 0)
1011 rc = MoveFocuserUF(FOCUS_OUTWARD, ticks);
1012
1013 if (!rc)
1014 return IPS_ALERT;
1015
1017
1018 return IPS_BUSY;
1019}
1020
1022{
1023 uint32_t aticks;
1024
1025 if ((dir == FOCUS_INWARD) && (ticks > FocusAbsPosNP[0].getValue()))
1026 {
1027 aticks = FocusAbsPosNP[0].getValue();
1029 "Requested %u ticks but relative inward movement has been limited to %u ticks", ticks, aticks);
1030 ticks = aticks;
1031 }
1032 else if ((dir == FOCUS_OUTWARD) && ((FocusAbsPosNP[0].getValue() + ticks) > MaxPositionNP[0].getValue()))
1033 {
1034 aticks = MaxPositionNP[0].getValue() - FocusAbsPosNP[0].getValue();
1036 "Requested %u ticks but relative outward movement has been limited to %u ticks", ticks, aticks);
1037 ticks = aticks;
1038 }
1039
1040 if (!MoveFocuserUF(dir, (unsigned int)ticks))
1041 return IPS_ALERT;
1042
1043 FocusRelPosNP[0].setValue(ticks);
1045
1046 return IPS_BUSY;
1047}
1048
1050{
1051 if (!isConnected())
1052 {
1053 return;
1054 }
1055
1056 if (updatePosition())
1057 {
1058 if (fabs(lastPos - FocusAbsPosNP[0].getValue()) > 5)
1059 {
1061 lastPos = FocusAbsPosNP[0].getValue();
1062 }
1063 }
1064
1065 if (updateTemperature())
1066 {
1067 if (fabs(lastTemperature - TemperatureNP[0].getValue()) >= 0.5)
1068 {
1069 TemperatureNP.apply();
1070 lastTemperature = TemperatureNP[0].getValue();
1071 }
1072 }
1073
1075 {
1076 float remaining = CalcTimeLeft(focusMoveStart, focusMoveRequest);
1077
1078 if (remaining <= 0)
1079 {
1081 FocusTimerNP[0].setValue(0);
1082 AbortFocuser();
1083 }
1084 else
1085 FocusTimerNP[0].setValue(remaining * 1000.0);
1086
1088 }
1089
1091 {
1092 if (backlashMove && (fabs(backlashTargetPos - FocusAbsPosNP[0].getValue()) < 1))
1093 {
1094 // Backlash target reached, now go to real target
1095 MoveAbsFocuser(targetPos);
1096 backlashMove = false;
1097 }
1098 else
1099 {
1100 if (fabs(targetPos - FocusAbsPosNP[0].getValue()) < 1)
1101 {
1106 lastPos = FocusAbsPosNP[0].getValue();
1107 LOG_INFO("Focuser reached requested position.");
1108 }
1109 }
1110 }
1111
1113}
1114
1116{
1117 char resp[UFORESLEN] = {};
1118 if (!sendCommand(UFOCABORT, resp))
1119 return false;
1120
1125 backlashMove = false;
1126 moving = false;
1127 return true;
1128}
1129
1130float USBFocusV3::CalcTimeLeft(timeval start, float req)
1131{
1132 double timesince;
1133 double timeleft;
1134 struct timeval now
1135 {
1136 0, 0
1137 };
1138 gettimeofday(&now, nullptr);
1139
1140 timesince =
1141 (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
1142 timesince = timesince / 1000;
1143 timeleft = req - timesince;
1144 return timeleft;
1145}
1146
1148{
1149 backlashIn = steps < 0;
1150 backlashSteps = std::abs(steps);
1151
1152 return true;
1153}
bool isConnected() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusTimerNP
INDI::PropertyNumber FocusBacklashNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
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 setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
bool getControllerStatus()
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
@ FOCUS_FULL_STEP
Definition usbfocusv3.h:85
@ FOCUS_HALF_STEP
Definition usbfocusv3.h:84
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
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 updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual const char * getDefaultName() override
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
#define MAXRBUF
Definition indidriver.c:52
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#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 DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
#define USBFOCUSV3_TIMEOUT
#define UFOCMOVEOUT
Definition usbfocusv3.h:32
#define UFOCRESET
Definition usbfocusv3.h:47
#define UFOCSETMANU
Definition usbfocusv3.h:46
#define UFOID
Definition usbfocusv3.h:51
#define UFOCSETSPEED
Definition usbfocusv3.h:36
#define UFOCSETFSTEPS
Definition usbfocusv3.h:40
#define UFOCSETAUTO
Definition usbfocusv3.h:45
#define UFORSACK
Definition usbfocusv3.h:53
#define UFOPNSIGN
Definition usbfocusv3.h:64
#define UFOCDEVID
Definition usbfocusv3.h:29
#define UFOCMDLEN
Definition usbfocusv3.h:72
#define UFOCREADTEMP
Definition usbfocusv3.h:31
#define UFOCMOVEIN
Definition usbfocusv3.h:33
#define UFOPRDIR
Definition usbfocusv3.h:60
#define UFOCSETRDIR
Definition usbfocusv3.h:39
#define UFOCSETSIGN
Definition usbfocusv3.h:44
#define UFOCGETSIGN
Definition usbfocusv3.h:43
#define UFOCSETTCTHR
Definition usbfocusv3.h:37
#define UFORSDONE
Definition usbfocusv3.h:55
#define UFOPSPDUS
Definition usbfocusv3.h:69
#define UFORESLEN
Definition usbfocusv3.h:71
#define UFOCSETMAX
Definition usbfocusv3.h:35
#define UFOPFSTEPS
Definition usbfocusv3.h:61
#define UFOPSDIR
Definition usbfocusv3.h:59
#define UFOCSETSDIR
Definition usbfocusv3.h:38
#define UFOPSPDAV
Definition usbfocusv3.h:67
#define UFOPPSIGN
Definition usbfocusv3.h:63
#define UFOPSPDERR
Definition usbfocusv3.h:66
#define UFOCREADPOS
Definition usbfocusv3.h:30
#define UFOCABORT
Definition usbfocusv3.h:34
#define UFOCSETHSTEPS
Definition usbfocusv3.h:41
#define UFOPHSTEPS
Definition usbfocusv3.h:62
#define UFOCSETSTDEG
Definition usbfocusv3.h:42
#define UFOCREADPARAM
Definition usbfocusv3.h:28
#define UFOPSPDSL
Definition usbfocusv3.h:68