Instrument Neutral Distributed Interface INDI 2.2.5
deepskydad_af1.cpp
Go to the documentation of this file.
1/*
2 Deep Sky Dad AF1 focuser
3
4 Copyright (C) 2019 Pavle Gartner
5
6 Based on Moonline driver.
7 Copyright (C) 2013-2019 Jasem Mutlaq ([email protected])
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23*/
24
25#include "deepskydad_af1.h"
26
27#include "indicom.h"
28
29#include <cmath>
30#include <cstring>
31#include <memory>
32
33#include <termios.h>
34#include <unistd.h>
35
36static std::unique_ptr<DeepSkyDadAF1> deepSkyDadAf1(new DeepSkyDadAF1());
37
42
44{
46
47 // Step Mode
48 StepModeSP[EIGHT].fill("EIGHT", "Eight Step", ISS_OFF);
49 StepModeSP[QUARTER].fill("QUARTER", "Quarter Step", ISS_OFF);
50 StepModeSP[HALF].fill("HALF", "Half Step", ISS_OFF);
51 StepModeSP[FULL].fill("FULL", "Full Step", ISS_OFF);
52 StepModeSP.fill(getDeviceName(), "Step Mode", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
53 IPS_IDLE);
54
55 /* Relative and absolute movement */
56 FocusRelPosNP[0].setMin(0.);
57 FocusRelPosNP[0].setMax(5000.);
58 FocusRelPosNP[0].setValue(0.);
59 FocusRelPosNP[0].setStep(10.);
60
61 FocusAbsPosNP[0].setMin(0.);
62 FocusAbsPosNP[0].setMax(100000.);
63 FocusAbsPosNP[0].setValue(50000.);
64 FocusAbsPosNP[0].setStep(500.);
65
66 FocusMaxPosNP[0].setMin(0.);
67 FocusMaxPosNP[0].setMax(9999999);
68 FocusMaxPosNP[0].setValue(9999999);
69 FocusMaxPosNP[0].setStep(500);
70
71 // Max. movement
72 FocusMaxMoveNP[0].fill("MAX_MOVE", "Steps", "%7.0f", 0, 9999999, 100, 0);
73 FocusMaxMoveNP.fill(getDeviceName(), "FOCUS_MAX_MOVE", "Max. movement",
75
76 // Settle buffer
77 SettleBufferNP[0].fill("SETTLE_BUFFER", "Period (ms)", "%5.0f", 0, 99999, 100, 0);
78 SettleBufferNP.fill(getDeviceName(), "FOCUS_SETTLE_BUFFER", "Settle buffer",
80
81 // Idle coils timeout (ms)
82 IUFillNumber(&IdleCoilsTimeoutN[0], "IDLE_COILS_TIMEOUT", "Period (ms)", "%6.0f", 0, 999999, 1000, 60000);
83 IUFillNumberVector(&IdleCoilsTimeoutNP, IdleCoilsTimeoutN, 1, getDeviceName(), "FOCUS_IDLE_COILS_TIMEOUT",
84 "Idle - coils timeout",
86
87 // Coils mode
88 CoilsModeSP[ALWAYS_ON].fill("ALWAYS_ON", "Always on", ISS_OFF);
89 CoilsModeSP[IDLE_OFF].fill("IDLE_OFF", "Idle - off", ISS_OFF);
90 CoilsModeSP[IDLE_COILS_TIMEOUT].fill("IDLE_COILS_TIMEOUT", "Idle - coils timeout (ms)", ISS_OFF);
91 CoilsModeSP.fill(getDeviceName(), "Coils mode", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
92 IPS_IDLE);
93
94 // Current move
95 CurrentMoveSP[CURRENT_25].fill("CMV_25", "25%", ISS_OFF);
96 CurrentMoveSP[CURRENT_50].fill("CMV_50", "50%", ISS_OFF);
97 CurrentMoveSP[CURRENT_75].fill("CMV_75", "75%", ISS_OFF);
98 CurrentMoveSP[CURRENT_100].fill("CMV_100", "100%", ISS_OFF);
99 CurrentMoveSP.fill(getDeviceName(), "Current - move", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY,
100 0, IPS_IDLE);
101
102 // Current hold
103 CurrentHoldSP[CURRENT_25].fill("CHD_25", "25%", ISS_OFF);
104 CurrentHoldSP[CURRENT_50].fill("CHD_50", "50%", ISS_OFF);
105 CurrentHoldSP[CURRENT_75].fill("CHD_75", "75%", ISS_OFF);
106 CurrentHoldSP[CURRENT_100].fill("CHD_100", "100%", ISS_OFF);
107 CurrentHoldSP.fill(getDeviceName(), "Current - hold", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY,
108 0, IPS_IDLE);
109
112
113 return true;
114}
115
117{
119
120 if (isConnected())
121 {
122 defineProperty(FocusMaxMoveNP);
123 defineProperty(StepModeSP);
124 defineProperty(SettleBufferNP);
125 defineProperty(CoilsModeSP);
126 defineProperty(&IdleCoilsTimeoutNP);
127 defineProperty(CurrentMoveSP);
128 defineProperty(CurrentHoldSP);
129
130 GetFocusParams();
131
132 LOG_INFO("deepSkyDadAf1 parameters updated, focuser ready for use.");
133 }
134 else
135 {
136 deleteProperty(FocusMaxMoveNP);
137 deleteProperty(StepModeSP);
138 deleteProperty(SettleBufferNP);
139 deleteProperty(CoilsModeSP);
140 deleteProperty(IdleCoilsTimeoutNP.name);
141 deleteProperty(CurrentMoveSP);
142 deleteProperty(CurrentHoldSP);
143 }
144
145 return true;
146}
147
149{
150 if (Ack())
151 {
152 LOG_INFO("deepSkyDadAf1 is online. Getting focus parameters...");
153 return true;
154 }
155
156 LOG_INFO(
157 "Error retrieving data from deepSkyDadAf1, please ensure deepSkyDadAf1 controller is powered and the port is correct.");
158 return false;
159}
160
162{
163 return "Deep Sky Dad AF1";
164}
165
166bool DeepSkyDadAF1::Ack()
167{
168 sleep(2);
169
170 char res[DSD_RES] = {0};
171 if (!sendCommand("[GPOS]", res))
172 {
173 LOG_ERROR("ACK - getPosition failed");
174 return false;
175 }
176
177 int32_t pos;
178 int rc = sscanf(res, "(%d)", &pos);
179
180 if (rc <= 0)
181 {
182 LOG_ERROR("ACK - getPosition failed");
183 return false;
184 }
185
186 return true;
187}
188
189bool DeepSkyDadAF1::readStepMode()
190{
191 char res[DSD_RES] = {0};
192
193 if (sendCommand("[GSTP]", res) == false)
194 return false;
195
196 if (strcmp(res, "(1)") == 0)
197 StepModeSP[FULL].setState(ISS_ON);
198 else if (strcmp(res, "(2)") == 0)
199 StepModeSP[HALF].setState(ISS_ON);
200 else if (strcmp(res, "(4)") == 0)
201 StepModeSP[QUARTER].setState(ISS_ON);
202 else if (strcmp(res, "(8)") == 0)
203 StepModeSP[EIGHT].setState(ISS_ON);
204 else
205 {
206 LOGF_ERROR("Unknown error: focuser step value (%s)", res);
207 return false;
208 }
209
210 return true;
211}
212
213bool DeepSkyDadAF1::readPosition()
214{
215 char res[DSD_RES] = {0};
216
217 if (sendCommand("[GPOS]", res) == false)
218 return false;
219
220 int32_t pos;
221 int rc = sscanf(res, "(%d)", &pos);
222
223 if (rc > 0)
224 FocusAbsPosNP[0].setValue(pos);
225 else
226 {
227 LOGF_ERROR("Unknown error: focuser position value (%s)", res);
228 return false;
229 }
230
231 return true;
232}
233
234bool DeepSkyDadAF1::readMaxMovement()
235{
236 char res[DSD_RES] = {0};
237
238 if (sendCommand("[GMXM]", res) == false)
239 return false;
240
241 uint32_t steps = 0;
242 int rc = sscanf(res, "(%d)", &steps);
243 if (rc > 0)
244 {
245 FocusMaxMoveNP[0].setValue(steps);
246 FocusMaxMoveNP.setState(IPS_OK);
247 }
248 else
249 {
250 LOGF_ERROR("Unknown error: maximum movement value (%s)", res);
251 return false;
252 }
253
254 return true;
255}
256
257bool DeepSkyDadAF1::readMaxPosition()
258{
259 char res[DSD_RES] = {0};
260
261 if (sendCommand("[GMXP]", res) == false)
262 return false;
263
264 uint32_t steps = 0;
265 int rc = sscanf(res, "(%d)", &steps);
266 if (rc > 0)
267 {
268 FocusMaxPosNP[0].setValue(steps);
270 }
271 else
272 {
273 LOGF_ERROR("Unknown error: maximum position value (%s)", res);
274 return false;
275 }
276
277 return true;
278}
279
280bool DeepSkyDadAF1::readSettleBuffer()
281{
282 char res[DSD_RES] = {0};
283
284 if (sendCommand("[GBUF]", res) == false)
285 return false;
286
287 uint32_t settleBuffer = 0;
288 int rc = sscanf(res, "(%d)", &settleBuffer);
289 if (rc > 0)
290 {
291 SettleBufferNP[0].setValue(settleBuffer);
292 SettleBufferNP.setState(settleBuffer > 0 ? IPS_OK : IPS_IDLE);
293 }
294 else
295 {
296 LOGF_ERROR("Unknown error: settle buffer value (%s)", res);
297 return false;
298 }
299
300 return true;
301}
302
303bool DeepSkyDadAF1::readIdleCoilsTimeout()
304{
305 char res[DSD_RES] = {0};
306
307 if (sendCommand("[GIDC]", res) == false)
308 return false;
309
310 uint32_t ms = 0;
311 int rc = sscanf(res, "(%d)", &ms);
312 if (rc > 0)
313 {
314 IdleCoilsTimeoutN[0].value = ms;
315 IdleCoilsTimeoutNP.s = ms > 0 ? IPS_OK : IPS_IDLE;
316 }
317 else
318 {
319 LOGF_ERROR("Unknown error: idle coils timeout value (%s)", res);
320 return false;
321 }
322
323 return true;
324}
325
326bool DeepSkyDadAF1::readCoilsMode()
327{
328 char res[DSD_RES] = {0};
329
330 if (sendCommand("[GCLM]", res) == false)
331 return false;
332
333 if (strcmp(res, "(0)") == 0)
334 {
335 CoilsModeSP.setState(IPS_IDLE);
336 CoilsModeSP[IDLE_OFF].setState(ISS_ON);
337 }
338 else if (strcmp(res, "(1)") == 0)
339 {
340 CoilsModeSP.setState(IPS_OK);
341 CoilsModeSP[ALWAYS_ON].setState(ISS_ON);
342 }
343 else if (strcmp(res, "(2)") == 0)
344 {
345 CoilsModeSP.setState(IPS_IDLE);
346 CoilsModeSP[IDLE_COILS_TIMEOUT].setState(ISS_ON);
347 }
348 else
349 {
350 LOGF_ERROR("Unknown error: readCoilsMode value (%s)", res);
351 return false;
352 }
353
354 return true;
355}
356
357bool DeepSkyDadAF1::readCurrentMove()
358{
359 char res[DSD_RES] = {0};
360
361 if (sendCommand("[GCMV%]", res) == false)
362 return false;
363
364 if (strcmp(res, "(25%)") == 0)
365 {
366 CurrentMoveSP.setState(IPS_OK);
367 CurrentMoveSP[CURRENT_25].setState(ISS_ON);
368 }
369 else if (strcmp(res, "(50%)") == 0)
370 {
371 CurrentMoveSP.setState(IPS_OK);
372 CurrentMoveSP[CURRENT_50].setState(ISS_ON);
373 }
374 else if (strcmp(res, "(75%)") == 0)
375 {
376 CurrentMoveSP.setState(IPS_OK);
377 CurrentMoveSP[CURRENT_75].setState(ISS_ON);
378 }
379 else if (strcmp(res, "(100%)") == 0)
380 {
381 CurrentMoveSP.setState(IPS_OK);
382 CurrentMoveSP[CURRENT_100].setState(ISS_ON);
383 }
384
385 else
386 {
387 LOGF_ERROR("Unknown error: currentMove value (%s)", res);
388 return false;
389 }
390
391 return true;
392}
393
394bool DeepSkyDadAF1::readCurrentHold()
395{
396 char res[DSD_RES] = {0};
397
398 if (sendCommand("[GCHD%]", res) == false)
399 return false;
400
401 if (strcmp(res, "(25%)") == 0)
402 {
403 CurrentHoldSP.setState(IPS_OK);
404 CurrentHoldSP[CURRENT_25].setState(ISS_ON);
405 }
406 else if (strcmp(res, "(50%)") == 0)
407 {
408 CurrentHoldSP.setState(IPS_OK);
409 CurrentHoldSP[CURRENT_50].setState(ISS_ON);
410 }
411 else if (strcmp(res, "(75%)") == 0)
412 {
413 CurrentHoldSP.setState(IPS_OK);
414 CurrentHoldSP[CURRENT_75].setState(ISS_ON);
415 }
416 else if (strcmp(res, "(100%)") == 0)
417 {
418 CurrentHoldSP.setState(IPS_OK);
419 CurrentHoldSP[CURRENT_100].setState(ISS_ON);
420 }
421
422 else
423 {
424 LOGF_ERROR("Unknown error: currentMove value (%s)", res);
425 return false;
426 }
427
428 return true;
429}
430
431bool DeepSkyDadAF1::isMoving()
432{
433 char res[DSD_RES] = {0};
434
435 if (sendCommand("[GMOV]", res) == false)
436 return false;
437
438 if (strcmp(res, "(1)") == 0)
439 return true;
440 else if (strcmp(res, "(0)") == 0)
441 return false;
442
443 LOGF_ERROR("Unknown error: isMoving value (%s)", res);
444 return false;
445}
446
447bool DeepSkyDadAF1::SyncFocuser(uint32_t ticks)
448{
449 char cmd[DSD_RES] = {0};
450 snprintf(cmd, DSD_RES, "[SPOS%06d]", ticks);
451 return sendCommand(cmd);
452}
453
455{
456 char cmd[DSD_RES] = {0};
457 snprintf(cmd, DSD_RES, "[SREV%01d]", enabled ? 1 : 0);
458 return sendCommand(cmd);
459}
460
461bool DeepSkyDadAF1::MoveFocuser(uint32_t position)
462{
463 char cmd[DSD_RES] = {0};
464 char res[DSD_RES] = {0};
465 snprintf(cmd, DSD_RES, "[STRG%06d]", position);
466 // Set Position First
467 if (sendCommand(cmd, res) == false)
468 return false;
469
470 if(strcmp(res, "!101)") == 0)
471 {
472 LOG_ERROR("MoveFocuserFailed - requested movement too big. You can increase the limit by changing the value of Max. movement.");
473 return false;
474 }
475
476 // Now start motion toward position
477 if (sendCommand("[SMOV]") == false)
478 return false;
479
480 return true;
481}
482
483bool DeepSkyDadAF1::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
484{
485 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
486 {
487 // Focus Step Mode
488 if (StepModeSP.isNameMatch(name))
489 {
490 int current_mode = StepModeSP.findOnSwitchIndex();
491
492 StepModeSP.update(states, names, n);
493
494 int target_mode =StepModeSP.findOnSwitchIndex();
495
496 if (current_mode == target_mode)
497 {
498 StepModeSP.setState(IPS_OK);
499 StepModeSP.apply();
500 return true;
501 }
502
503 char cmd[DSD_RES] = {0};
504
505 if(target_mode == 0)
506 target_mode = 1;
507 else if(target_mode == 1)
508 target_mode = 2;
509 else if(target_mode == 2)
510 target_mode = 4;
511 else if(target_mode == 3)
512 target_mode = 8;
513
514 snprintf(cmd, DSD_RES, "[SSTP%d]", target_mode);
515 bool rc = sendCommandSet(cmd);
516 if (!rc)
517 {
518 StepModeSP.reset();
519 StepModeSP[current_mode].setState(ISS_ON);
520 StepModeSP.setState(IPS_ALERT);
521 StepModeSP.apply();
522 return false;
523 }
524
525 StepModeSP.setState(IPS_OK);
526 StepModeSP.apply();
527 return true;
528 }
529
530 // Coils mode
531 if (CoilsModeSP.isNameMatch(name))
532 {
533 int coilsModeCurrent = CoilsModeSP.findOnSwitchIndex();
534
535 CoilsModeSP.update(states, names, n);
536
537 int coilsModeTarget = CoilsModeSP.findOnSwitchIndex();
538
539 if (coilsModeCurrent == coilsModeTarget)
540 {
541 CoilsModeSP.apply();
542 return true;
543 }
544
545 if(coilsModeTarget == 0)
546 coilsModeTarget = 1;
547 else if(coilsModeTarget == 1)
548 coilsModeTarget = 0;
549 else if(coilsModeTarget == 2)
550 coilsModeTarget = 2;
551
552 char cmd[DSD_RES] = {0};
553 snprintf(cmd, DSD_RES, "[SCLM%d]", coilsModeTarget);
554
555 bool rc = sendCommandSet(cmd);
556 if (!rc)
557 {
558 CoilsModeSP.reset();
559 CoilsModeSP[coilsModeCurrent].setState(ISS_ON);
560 CoilsModeSP.setState(IPS_ALERT);
561 CoilsModeSP.apply();
562 return false;
563 }
564
565 CoilsModeSP.setState(coilsModeTarget == 1 ? IPS_OK : IPS_IDLE);
566 CoilsModeSP.apply();
567 return true;
568 }
569
570 // Current - move
571 if (CurrentMoveSP.isNameMatch(name))
572 {
573 int current = CurrentMoveSP.findOnSwitchIndex();
574
575 CurrentMoveSP.update(states, names, n);
576
577 int targetCurrent = CurrentMoveSP.findOnSwitchIndex();
578
579 if (current == targetCurrent)
580 {
581 CurrentMoveSP.apply();
582 return true;
583 }
584
585 int targetCurrentValue = 75;
586 switch(targetCurrent)
587 {
588 case 0:
589 targetCurrentValue = 25;
590 break;
591 case 1:
592 targetCurrentValue = 50;
593 break;
594 case 2:
595 targetCurrentValue = 75;
596 break;
597 case 3:
598 targetCurrentValue = 100;
599 break;
600 }
601
602 char cmd[DSD_RES] = {0};
603 snprintf(cmd, DSD_RES, "[SCMV%d%%]", targetCurrentValue);
604
605 bool rc = sendCommandSet(cmd);
606 if (!rc)
607 {
608 CurrentMoveSP.reset();
609 CurrentMoveSP[current].setState(ISS_ON);
610 CurrentMoveSP.setState(IPS_ALERT);
611 CurrentMoveSP.apply();
612 return false;
613 }
614
615 CurrentMoveSP.setState(IPS_OK);
616 CurrentMoveSP.apply();
617 return true;
618 }
619
620 // Current - hold
621 if (CurrentHoldSP.isNameMatch(name))
622 {
623 int current = CurrentHoldSP.findOnSwitchIndex();
624
625 CurrentHoldSP.update(states, names, n);
626
627 int targetCurrent = CurrentHoldSP.findOnSwitchIndex();
628
629 if (current == targetCurrent)
630 {
631 CurrentHoldSP.apply();
632 return true;
633 }
634
635 int targetCurrentValue = 75;
636 switch(targetCurrent)
637 {
638 case 0:
639 targetCurrentValue = 25;
640 break;
641 case 1:
642 targetCurrentValue = 50;
643 break;
644 case 2:
645 targetCurrentValue = 75;
646 break;
647 case 3:
648 targetCurrentValue = 100;
649 break;
650 }
651
652 char cmd[DSD_RES] = {0};
653 snprintf(cmd, DSD_RES, "[SCHD%d%%]", targetCurrentValue);
654
655 bool rc = sendCommandSet(cmd);
656 if (!rc)
657 {
658 CurrentHoldSP.reset();
659 CurrentHoldSP[current].setState(ISS_ON);
660 CurrentHoldSP.setState(IPS_ALERT);
661 CurrentHoldSP.apply();
662 return false;
663 }
664
665 CurrentHoldSP.setState(IPS_OK);
666 CurrentHoldSP.apply();
667 return true;
668 }
669 }
670
671 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
672}
673
674bool DeepSkyDadAF1::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
675{
676 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
677 {
678 // Settle buffer Settings
679 if (SettleBufferNP.isNameMatch(name))
680 {
681 SettleBufferNP.update(values, names, n);
682 char cmd[DSD_RES] = {0};
683 snprintf(cmd, DSD_RES, "[SBUF%06d]", static_cast<int>(SettleBufferNP[0].getValue()));
684 bool rc = sendCommandSet(cmd);
685 if (!rc)
686 {
687 SettleBufferNP.setState(IPS_ALERT);
688 return false;
689 }
690
691 SettleBufferNP.setState(IPS_OK);
692 SettleBufferNP.apply();
693 return true;
694 }
695
696 // Idle coils timeout
697 if (strcmp(name, IdleCoilsTimeoutNP.name) == 0)
698 {
699 IUUpdateNumber(&IdleCoilsTimeoutNP, values, names, n);
700 char cmd[DSD_RES] = {0};
701 snprintf(cmd, DSD_RES, "[SIDC%06d]", static_cast<int>(IdleCoilsTimeoutN[0].value));
702 bool rc = sendCommandSet(cmd);
703 if (!rc)
704 {
705 IdleCoilsTimeoutNP.s = IPS_ALERT;
706 return false;
707 }
708
709 IdleCoilsTimeoutNP.s = IPS_OK;
710 IDSetNumber(&IdleCoilsTimeoutNP, nullptr);
711 return true;
712 }
713
714
715 // Max. movement
716 if (FocusMaxMoveNP.isNameMatch(name))
717 {
718 FocusMaxMoveNP.update(values, names, n);
719 char cmd[DSD_RES] = {0};
720 snprintf(cmd, DSD_RES, "[SMXM%d]", static_cast<int>(FocusMaxMoveNP[0].getValue()));
721 bool rc = sendCommandSet(cmd);
722 if (!rc)
723 {
724 FocusMaxMoveNP.setState(IPS_ALERT);
725 return false;
726 }
727
728 FocusMaxMoveNP.setState(IPS_OK);
729 FocusMaxMoveNP.apply();
730 return true;
731 }
732 }
733
734 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
735}
736
737void DeepSkyDadAF1::GetFocusParams()
738{
739 StepModeSP.reset();
740 CoilsModeSP.reset();
741 CurrentMoveSP.reset();
742 CurrentHoldSP.reset();
743
744 if (readPosition())
746
747 if (readStepMode())
748 StepModeSP.apply();
749
750 if (readSettleBuffer())
751 SettleBufferNP.apply();
752
753 if (readMaxPosition())
755
756 if (readMaxMovement())
757 FocusMaxMoveNP.apply();
758
759 if (readIdleCoilsTimeout())
760 IDSetNumber(&IdleCoilsTimeoutNP, nullptr);
761
762 if (readCoilsMode())
763 CoilsModeSP.apply();
764
765 if (readCurrentMove())
766 CurrentMoveSP.apply();
767
768 if (readCurrentHold())
769 CurrentHoldSP.apply();
770}
771
772IPState DeepSkyDadAF1::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
773{
774 INDI_UNUSED(speed);
775 // either go all the way in or all the way out
776 // then use timer to stop
777 if (dir == FOCUS_INWARD)
778 MoveFocuser(0);
779 else
780 MoveFocuser(FocusMaxPosNP[0].getValue());
781
783 return IPS_BUSY;
784}
785
787{
788 static_cast<DeepSkyDadAF1*>(context)->timedMoveCallback();
789}
790
791void DeepSkyDadAF1::timedMoveCallback()
792{
793 AbortFocuser();
797 FocusTimerNP[0].setValue(0);
801}
802
803
805{
806 targetPos = targetTicks;
807
808 if (!MoveFocuser(targetPos))
809 return IPS_ALERT;
810
811 return IPS_BUSY;
812}
813
815{
816 int32_t newPosition = 0;
817
818 if (dir == FOCUS_INWARD)
819 newPosition = FocusAbsPosNP[0].getValue() - ticks;
820 else
821 newPosition = FocusAbsPosNP[0].getValue() + ticks;
822
823 // Clamp
824 newPosition = std::max(0, std::min(static_cast<int32_t>(FocusAbsPosNP[0].getMax()), newPosition));
825 if (!MoveFocuser(newPosition))
826 return IPS_ALERT;
827
828 // JM 2019-02-10: This is already set by the framework
829 //FocusRelPosNP[0].setValue(ticks);
830 //FocusRelPosNP.setState(IPS_BUSY);
831
832 return IPS_BUSY;
833}
834
836{
837 if (!isConnected())
838 {
840 return;
841 }
842
843 bool rc = readPosition();
844 if (rc)
845 {
846 if (std::abs(lastPos - FocusAbsPosNP[0].getValue()) > 5)
847 {
849 lastPos = FocusAbsPosNP[0].getValue();
850 }
851 }
852
854 {
855 if (!isMoving())
856 {
861 lastPos = FocusAbsPosNP[0].getValue();
862 LOG_INFO("Focuser reached requested position.");
863 }
864 }
865
867}
868
870{
871 return sendCommand("[STOP]");
872}
873
875{
876 Focuser::saveConfigItems(fp);
877
878 StepModeSP.save(fp);
879 FocusMaxMoveNP.save(fp);
880 SettleBufferNP.save(fp);
881 CoilsModeSP.save(fp);
882 IUSaveConfigNumber(fp, &IdleCoilsTimeoutNP);
883 CurrentMoveSP.save(fp);
884 CurrentHoldSP.save(fp);
885
886 return true;
887}
888
889bool DeepSkyDadAF1::sendCommand(const char * cmd, char * res)
890{
891 int nbytes_written = 0, nbytes_read = 0, rc = -1;
892
893 tcflush(PortFD, TCIOFLUSH);
894
895 LOGF_DEBUG("CMD <%s>", cmd);
896
897 if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
898 {
899 char errstr[MAXRBUF] = {0};
900 tty_error_msg(rc, errstr, MAXRBUF);
901 LOGF_ERROR("Serial write error: %s.", errstr);
902 return false;
903 }
904
905 if (res == nullptr)
906 return true;
907
908 if ((rc = tty_nread_section(PortFD, res, DSD_RES, DSD_DEL, DSD_TIMEOUT, &nbytes_read)) != TTY_OK)
909 {
910 char errstr[MAXRBUF] = {0};
911 tty_error_msg(rc, errstr, MAXRBUF);
912 LOGF_ERROR("Serial read error: %s.", errstr);
913 return false;
914 }
915
916 LOGF_DEBUG("RES <%s>", res);
917
918 tcflush(PortFD, TCIOFLUSH);
919
920 return true;
921}
922
923bool DeepSkyDadAF1::sendCommandSet(const char * cmd)
924{
925 char res[DSD_RES] = {0};
926
927 if (sendCommand(cmd, res) == false)
928 return false;
929
930 return strcmp(res, "(OK)") == 0;
931}
932
934 {
935 char cmd[DSD_RES] = {0};
936 snprintf(cmd, DSD_RES, "[SMXP%d]", static_cast<int>(ticks));
937 return sendCommandSet(cmd);
938 }
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....
static void timedMoveHelper(void *context)
const char * getDefaultName() override
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 IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveAbsFocuser Move to an absolute target position.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser Move focuser in a specific direction and speed for period of time.
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveRelFocuser Move focuser for a relative amount of ticks in a specific direction.
virtual bool saveConfigItems(FILE *fp) override
virtual bool SetFocuserMaxPosition(uint32_t ticks) override
SetFocuserMaxPosition Update focuser maximum position. It only updates the PresetNP property limits.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
bool isConnected() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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 FocusTimerNP
INDI::PropertyNumber FocusMaxPosNP
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
void save(FILE *f) const
save Save property to configuration file.
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)
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,...
int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
Register a new single-shot timer function, fp, to be called with ud as argument after ms.
Definition eventloop.c:586
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
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_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
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
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:490
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition indidevapi.c:398
#define INDI_UNUSED(x)
Definition indidevapi.h:132
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#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
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:321