Instrument Neutral Distributed Interface INDI 2.2.5
deepskydad_af2.cpp
Go to the documentation of this file.
1/*
2 Deep Sky Dad AF2 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_af2.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<DeepSkyDadAF2> deepSkyDadAf2(new DeepSkyDadAF2());
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 IdleCoilsTimeoutNP[0].fill("IDLE_COILS_TIMEOUT", "Period (ms)", "%6.0f", 0, 999999, 1000, 60000);
83 IdleCoilsTimeoutNP.fill(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
110 // Focuser temperature
111 TemperatureNP[0].fill("TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
112 TemperatureNP.fill(getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
114
117
118 return true;
119}
120
122{
124
125 if (isConnected())
126 {
127 defineProperty(FocusMaxMoveNP);
128 defineProperty(StepModeSP);
129 defineProperty(SettleBufferNP);
130 defineProperty(CoilsModeSP);
131 defineProperty(IdleCoilsTimeoutNP);
132 defineProperty(CurrentMoveSP);
133 defineProperty(CurrentHoldSP);
134
135 defineProperty(TemperatureNP);
136
137 GetFocusParams();
138
139 LOG_INFO("deepSkyDadAf2 parameters updated, focuser ready for use.");
140 }
141 else
142 {
143 deleteProperty(FocusMaxMoveNP);
144 deleteProperty(StepModeSP);
145 deleteProperty(SettleBufferNP);
146 deleteProperty(CoilsModeSP);
147 deleteProperty(IdleCoilsTimeoutNP);
148 deleteProperty(CurrentMoveSP);
149 deleteProperty(CurrentHoldSP);
150 deleteProperty(TemperatureNP);
151 }
152
153 return true;
154}
155
157{
158 if (Ack())
159 {
160 LOG_INFO("deepSkyDadAf2 is online. Getting focus parameters...");
161 return true;
162 }
163
164 LOG_INFO(
165 "Error retrieving data from deepSkyDadAf2, please ensure deepSkyDadAf2 controller is powered and the port is correct.");
166 return false;
167}
168
170{
171 return "Deep Sky Dad AF2";
172}
173
174bool DeepSkyDadAF2::Ack()
175{
176 sleep(2);
177
178 char res[DSD_RES] = {0};
179 if (!sendCommand("[GPOS]", res))
180 {
181 LOG_ERROR("ACK - getPosition failed");
182 return false;
183 }
184
185 int32_t pos;
186 int rc = sscanf(res, "(%d)", &pos);
187
188 if (rc <= 0)
189 {
190 LOG_ERROR("ACK - getPosition failed");
191 return false;
192 }
193
194 return true;
195}
196
197bool DeepSkyDadAF2::readStepMode()
198{
199 char res[DSD_RES] = {0};
200
201 if (sendCommand("[GSTP]", res) == false)
202 return false;
203
204 if (strcmp(res, "(1)") == 0)
205 StepModeSP[FULL].setState(ISS_ON);
206 else if (strcmp(res, "(2)") == 0)
207 StepModeSP[HALF].setState(ISS_ON);
208 else if (strcmp(res, "(4)") == 0)
209 StepModeSP[QUARTER].setState(ISS_ON);
210 else if (strcmp(res, "(8)") == 0)
211 StepModeSP[EIGHT].setState(ISS_ON);
212 else
213 {
214 LOGF_ERROR("Unknown error: focuser step value (%s)", res);
215 return false;
216 }
217
218 return true;
219}
220
221bool DeepSkyDadAF2::readPosition()
222{
223 char res[DSD_RES] = {0};
224
225 if (sendCommand("[GPOS]", res) == false)
226 return false;
227
228 int32_t pos;
229 int rc = sscanf(res, "(%d)", &pos);
230
231 if (rc > 0)
232 FocusAbsPosNP[0].setValue(pos);
233 else
234 {
235 LOGF_ERROR("Unknown error: focuser position value (%s)", res);
236 return false;
237 }
238
239 return true;
240}
241
242bool DeepSkyDadAF2::readMaxMovement()
243{
244 char res[DSD_RES] = {0};
245
246 if (sendCommand("[GMXM]", res) == false)
247 return false;
248
249 uint32_t steps = 0;
250 int rc = sscanf(res, "(%d)", &steps);
251 if (rc > 0)
252 {
253 FocusMaxMoveNP[0].setValue(steps);
254 FocusMaxMoveNP.setState(IPS_OK);
255 }
256 else
257 {
258 LOGF_ERROR("Unknown error: maximum movement value (%s)", res);
259 return false;
260 }
261
262 return true;
263}
264
265bool DeepSkyDadAF2::readMaxPosition()
266{
267 char res[DSD_RES] = {0};
268
269 if (sendCommand("[GMXP]", res) == false)
270 return false;
271
272 uint32_t steps = 0;
273 int rc = sscanf(res, "(%d)", &steps);
274 if (rc > 0)
275 {
276 FocusMaxPosNP[0].setValue(steps);
278 }
279 else
280 {
281 LOGF_ERROR("Unknown error: maximum position value (%s)", res);
282 return false;
283 }
284
285 return true;
286}
287
288bool DeepSkyDadAF2::readSettleBuffer()
289{
290 char res[DSD_RES] = {0};
291
292 if (sendCommand("[GBUF]", res) == false)
293 return false;
294
295 uint32_t settleBuffer = 0;
296 int rc = sscanf(res, "(%d)", &settleBuffer);
297 if (rc > 0)
298 {
299 SettleBufferNP[0].setValue(settleBuffer);
300 SettleBufferNP.setState(settleBuffer > 0 ? IPS_OK : IPS_IDLE);
301 }
302 else
303 {
304 LOGF_ERROR("Unknown error: settle buffer value (%s)", res);
305 return false;
306 }
307
308 return true;
309}
310
311bool DeepSkyDadAF2::readIdleCoilsTimeout()
312{
313 char res[DSD_RES] = {0};
314
315 if (sendCommand("[GIDC]", res) == false)
316 return false;
317
318 uint32_t ms = 0;
319 int rc = sscanf(res, "(%d)", &ms);
320 if (rc > 0)
321 {
322 IdleCoilsTimeoutNP[0].setValue(ms);
323 IdleCoilsTimeoutNP.setState(ms > 0 ? IPS_OK : IPS_IDLE);
324 }
325 else
326 {
327 LOGF_ERROR("Unknown error: idle coils timeout value (%s)", res);
328 return false;
329 }
330
331 return true;
332}
333
334bool DeepSkyDadAF2::readCoilsMode()
335{
336 char res[DSD_RES] = {0};
337
338 if (sendCommand("[GCLM]", res) == false)
339 return false;
340
341 if (strcmp(res, "(0)") == 0)
342 {
343 CoilsModeSP.setState(IPS_IDLE);
344 CoilsModeSP[IDLE_OFF].setState(ISS_ON);
345 }
346 else if (strcmp(res, "(1)") == 0)
347 {
348 CoilsModeSP.setState(IPS_OK);
349 CoilsModeSP[ALWAYS_ON].setState(ISS_ON);
350 }
351 else if (strcmp(res, "(2)") == 0)
352 {
353 CoilsModeSP.setState(IPS_IDLE);
354 CoilsModeSP[IDLE_COILS_TIMEOUT].setState(ISS_ON);
355 }
356 else
357 {
358 LOGF_ERROR("Unknown error: readCoilsMode value (%s)", res);
359 return false;
360 }
361
362 return true;
363}
364
365bool DeepSkyDadAF2::readCurrentMove()
366{
367 char res[DSD_RES] = {0};
368
369 if (sendCommand("[GCMV%]", res) == false)
370 return false;
371
372 if (strcmp(res, "(25%)") == 0)
373 {
374 CurrentMoveSP.setState(IPS_OK);
375 CurrentMoveSP[CURRENT_25].setState(ISS_ON);
376 }
377 else if (strcmp(res, "(50%)") == 0)
378 {
379 CurrentMoveSP.setState(IPS_OK);
380 CurrentMoveSP[CURRENT_50].setState(ISS_ON);
381 }
382 else if (strcmp(res, "(75%)") == 0)
383 {
384 CurrentMoveSP.setState(IPS_OK);
385 CurrentMoveSP[CURRENT_75].setState(ISS_ON);
386 }
387 else if (strcmp(res, "(100%)") == 0)
388 {
389 CurrentMoveSP.setState(IPS_OK);
390 CurrentMoveSP[CURRENT_100].setState(ISS_ON);
391 }
392
393 else
394 {
395 LOGF_ERROR("Unknown error: currentMove value (%s)", res);
396 return false;
397 }
398
399 return true;
400}
401
402bool DeepSkyDadAF2::readCurrentHold()
403{
404 char res[DSD_RES] = {0};
405
406 if (sendCommand("[GCHD%]", res) == false)
407 return false;
408
409 if (strcmp(res, "(25%)") == 0)
410 {
411 CurrentHoldSP.setState(IPS_OK);
412 CurrentHoldSP[CURRENT_25].setState(ISS_ON);
413 }
414 else if (strcmp(res, "(50%)") == 0)
415 {
416 CurrentHoldSP.setState(IPS_OK);
417 CurrentHoldSP[CURRENT_50].setState(ISS_ON);
418 }
419 else if (strcmp(res, "(75%)") == 0)
420 {
421 CurrentHoldSP.setState(IPS_OK);
422 CurrentHoldSP[CURRENT_75].setState(ISS_ON);
423 }
424 else if (strcmp(res, "(100%)") == 0)
425 {
426 CurrentHoldSP.setState(IPS_OK);
427 CurrentHoldSP[CURRENT_100].setState(ISS_ON);
428 }
429
430 else
431 {
432 LOGF_ERROR("Unknown error: currentMove value (%s)", res);
433 return false;
434 }
435
436 return true;
437}
438
439bool DeepSkyDadAF2::readTemperature()
440{
441 char res[DSD_RES] = {0};
442
443 if (sendCommand("[GTMC]", res) == false)
444 return false;
445
446 double temp = 0;
447 int rc = sscanf(res, "(%lf)", &temp);
448 if (rc > 0)
449 {
450 TemperatureNP[0].setValue(temp);
451 }
452 else
453 {
454 LOGF_ERROR("Unknown error: focuser temperature value (%s)", res);
455 return false;
456 }
457
458 return true;
459}
460
461bool DeepSkyDadAF2::isMoving()
462{
463 char res[DSD_RES] = {0};
464
465 if (sendCommand("[GMOV]", res) == false)
466 return false;
467
468 if (strcmp(res, "(1)") == 0)
469 return true;
470 else if (strcmp(res, "(0)") == 0)
471 return false;
472
473 LOGF_ERROR("Unknown error: isMoving value (%s)", res);
474 return false;
475}
476
477bool DeepSkyDadAF2::SyncFocuser(uint32_t ticks)
478{
479 char cmd[DSD_RES] = {0};
480 snprintf(cmd, DSD_RES, "[SPOS%06d]", ticks);
481 return sendCommand(cmd);
482}
483
485{
486 char cmd[DSD_RES] = {0};
487 snprintf(cmd, DSD_RES, "[SREV%01d]", enabled ? 1 : 0);
488 return sendCommand(cmd);
489}
490
491bool DeepSkyDadAF2::MoveFocuser(uint32_t position)
492{
493 char cmd[DSD_RES] = {0};
494 char res[DSD_RES] = {0};
495 snprintf(cmd, DSD_RES, "[STRG%06d]", position);
496 // Set Position First
497 if (sendCommand(cmd, res) == false)
498 return false;
499
500 if(strcmp(res, "!101)") == 0)
501 {
502 LOG_ERROR("MoveFocuserFailed - requested movement too big. You can increase the limit by changing the value of Max. movement.");
503 return false;
504 }
505
506 // Now start motion toward position
507 if (sendCommand("[SMOV]") == false)
508 return false;
509
510 return true;
511}
512
513bool DeepSkyDadAF2::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
514{
515 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
516 {
517 // Focus Step Mode
518 if (StepModeSP.isNameMatch(name))
519 {
520 int current_mode = StepModeSP.findOnSwitchIndex();
521
522 StepModeSP.update(states, names, n);
523
524 int target_mode = StepModeSP.findOnSwitchIndex();
525
526 if (current_mode == target_mode)
527 {
528 StepModeSP.setState(IPS_OK);
529 StepModeSP.apply();
530 return true;
531 }
532
533 char cmd[DSD_RES] = {0};
534
535 if(target_mode == 0)
536 target_mode = 1;
537 else if(target_mode == 1)
538 target_mode = 2;
539 else if(target_mode == 2)
540 target_mode = 4;
541 else if(target_mode == 3)
542 target_mode = 8;
543
544 snprintf(cmd, DSD_RES, "[SSTP%d]", target_mode);
545 bool rc = sendCommandSet(cmd);
546 if (!rc)
547 {
548 StepModeSP.reset();
549 StepModeSP[current_mode].setState(ISS_ON);
550 StepModeSP.setState(IPS_ALERT);
551 StepModeSP.apply();
552 return false;
553 }
554
555 StepModeSP.setState(IPS_OK);
556 StepModeSP.apply();
557 return true;
558 }
559
560 // Coils mode
561 if (CoilsModeSP.isNameMatch(name))
562 {
563 int coilsModeCurrent = CoilsModeSP.findOnSwitchIndex();
564
565 CoilsModeSP.update(states, names, n);
566
567 int coilsModeTarget = CoilsModeSP.findOnSwitchIndex();
568
569 if (coilsModeCurrent == coilsModeTarget)
570 {
571 CoilsModeSP.apply();
572 return true;
573 }
574
575 if(coilsModeTarget == 0)
576 coilsModeTarget = 1;
577 else if(coilsModeTarget == 1)
578 coilsModeTarget = 0;
579 else if(coilsModeTarget == 2)
580 coilsModeTarget = 2;
581
582 char cmd[DSD_RES] = {0};
583 snprintf(cmd, DSD_RES, "[SCLM%d]", coilsModeTarget);
584
585 bool rc = sendCommandSet(cmd);
586 if (!rc)
587 {
588 CoilsModeSP.reset();
589 CoilsModeSP[coilsModeCurrent].setState(ISS_ON);
590 CoilsModeSP.setState(IPS_ALERT);
591 CoilsModeSP.apply();
592 return false;
593 }
594
595 CoilsModeSP.setState(coilsModeTarget == 1 ? IPS_OK : IPS_IDLE);
596 CoilsModeSP.apply();
597 return true;
598 }
599
600 // Current - move
601 if (CurrentMoveSP.isNameMatch(name))
602 {
603 int current = CurrentMoveSP.findOnSwitchIndex();
604
605 CurrentMoveSP.update(states, names, n);
606
607 int targetCurrent = CurrentMoveSP.findOnSwitchIndex();
608
609 if (current == targetCurrent)
610 {
611 CurrentMoveSP.apply();
612 return true;
613 }
614
615 int targetCurrentValue = 75;
616 switch(targetCurrent)
617 {
618 case 0:
619 targetCurrentValue = 25;
620 break;
621 case 1:
622 targetCurrentValue = 50;
623 break;
624 case 2:
625 targetCurrentValue = 75;
626 break;
627 case 3:
628 targetCurrentValue = 100;
629 break;
630 }
631
632 char cmd[DSD_RES] = {0};
633 snprintf(cmd, DSD_RES, "[SCMV%d%%]", targetCurrentValue);
634
635 bool rc = sendCommandSet(cmd);
636 if (!rc)
637 {
638 CurrentMoveSP.reset();
639 CurrentMoveSP[current].setState(ISS_ON);
640 CurrentMoveSP.setState(IPS_ALERT);
641 CurrentMoveSP.apply();
642 return false;
643 }
644
645 CurrentMoveSP.setState(IPS_OK);
646 CurrentMoveSP.apply();
647 return true;
648 }
649
650 // Current - hold
651 if (CurrentHoldSP.isNameMatch(name))
652 {
653 int current = CurrentHoldSP.findOnSwitchIndex();
654
655 CurrentHoldSP.update(states, names, n);
656
657 int targetCurrent = CurrentHoldSP.findOnSwitchIndex();
658
659 if (current == targetCurrent)
660 {
661 CurrentHoldSP.apply();
662 return true;
663 }
664
665 int targetCurrentValue = 75;
666 switch(targetCurrent)
667 {
668 case 0:
669 targetCurrentValue = 25;
670 break;
671 case 1:
672 targetCurrentValue = 50;
673 break;
674 case 2:
675 targetCurrentValue = 75;
676 break;
677 case 3:
678 targetCurrentValue = 100;
679 break;
680 }
681
682 char cmd[DSD_RES] = {0};
683 snprintf(cmd, DSD_RES, "[SCHD%d%%]", targetCurrentValue);
684
685 bool rc = sendCommandSet(cmd);
686 if (!rc)
687 {
688 CurrentHoldSP.reset();
689 CurrentHoldSP[current].setState(ISS_ON);
690 CurrentHoldSP.setState(IPS_ALERT);
691 CurrentHoldSP.apply();
692 return false;
693 }
694
695 CurrentHoldSP.setState(IPS_OK);
696 CurrentHoldSP.apply();
697 return true;
698 }
699 }
700
701 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
702}
703
704bool DeepSkyDadAF2::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
705{
706 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
707 {
708 // Settle buffer Settings
709 if (SettleBufferNP.isNameMatch(name))
710 {
711 SettleBufferNP.update(values, names, n);
712 char cmd[DSD_RES] = {0};
713 snprintf(cmd, DSD_RES, "[SBUF%06d]", static_cast<int>(SettleBufferNP[0].getValue()));
714 bool rc = sendCommandSet(cmd);
715 if (!rc)
716 {
717 SettleBufferNP.setState(IPS_ALERT);
718 return false;
719 }
720
721 SettleBufferNP.setState(IPS_OK);
722 SettleBufferNP.apply();
723 return true;
724 }
725
726 // Idle coils timeout
727 if (IdleCoilsTimeoutNP.isNameMatch(name))
728 {
729 IdleCoilsTimeoutNP.update(values, names, n);
730 char cmd[DSD_RES] = {0};
731 snprintf(cmd, DSD_RES, "[SIDC%06d]", static_cast<int>(IdleCoilsTimeoutNP[0].getValue()));
732 bool rc = sendCommandSet(cmd);
733 if (!rc)
734 {
735 IdleCoilsTimeoutNP.setState(IPS_ALERT);
736 return false;
737 }
738
739 IdleCoilsTimeoutNP.setState(IPS_OK);
740 IdleCoilsTimeoutNP.apply();
741 return true;
742 }
743
744 // Max. movement
745 if (FocusMaxMoveNP.isNameMatch(name))
746 {
747 FocusMaxMoveNP.update(values, names, n);
748 char cmd[DSD_RES] = {0};
749 snprintf(cmd, DSD_RES, "[SMXM%d]", static_cast<int>(FocusMaxMoveNP[0].getValue()));
750 bool rc = sendCommandSet(cmd);
751 if (!rc)
752 {
753 FocusMaxMoveNP.setState(IPS_ALERT);
754 return false;
755 }
756
757 FocusMaxMoveNP.setState(IPS_OK);
758 FocusMaxMoveNP.apply();
759 return true;
760 }
761
762 }
763
764 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
765}
766
767void DeepSkyDadAF2::GetFocusParams()
768{
769 StepModeSP.reset();
770 CoilsModeSP.reset();
771 CurrentMoveSP.reset();
772 CurrentHoldSP.reset();
773
774 if (readPosition())
776
777 if (readStepMode())
778 StepModeSP.apply();
779
780 if (readSettleBuffer())
781 SettleBufferNP.apply();
782
783 if (readMaxPosition())
785
786 if (readMaxMovement())
787 FocusMaxMoveNP.apply();
788
789 if (readIdleCoilsTimeout())
790 IdleCoilsTimeoutNP.apply();
791
792 if (readCoilsMode())
793 CoilsModeSP.apply();
794
795 if (readCurrentMove())
796 CurrentMoveSP.apply();
797
798 if (readCurrentHold())
799 CurrentHoldSP.apply();
800
801 if (readTemperature())
802 TemperatureNP.apply();
803}
804
805IPState DeepSkyDadAF2::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
806{
807 INDI_UNUSED(speed);
808 // either go all the way in or all the way out
809 // then use timer to stop
810 if (dir == FOCUS_INWARD)
811 MoveFocuser(0);
812 else
813 MoveFocuser(FocusMaxPosNP[0].getValue());
814
816 return IPS_BUSY;
817}
818
820{
821 static_cast<DeepSkyDadAF2*>(context)->timedMoveCallback();
822}
823
824void DeepSkyDadAF2::timedMoveCallback()
825{
826 AbortFocuser();
830 FocusTimerNP[0].setValue(0);
834}
835
836
838{
839 targetPos = targetTicks;
840
841 if (!MoveFocuser(targetPos))
842 return IPS_ALERT;
843
844 return IPS_BUSY;
845}
846
848{
849 int32_t newPosition = 0;
850
851 if (dir == FOCUS_INWARD)
852 newPosition = FocusAbsPosNP[0].getValue() - ticks;
853 else
854 newPosition = FocusAbsPosNP[0].getValue() + ticks;
855
856 // Clamp
857 newPosition = std::max(0, std::min(static_cast<int32_t>(FocusAbsPosNP[0].getMax()), newPosition));
858 if (!MoveFocuser(newPosition))
859 return IPS_ALERT;
860
861 // JM 2019-02-10: This is already set by the framework
862 //FocusRelPosNP[0].setValue(ticks);
863 //FocusRelPosNP.setState(IPS_BUSY);
864
865 return IPS_BUSY;
866}
867
869{
870 if (!isConnected())
871 {
873 return;
874 }
875
876 bool rc = readPosition();
877 if (rc)
878 {
879 if (std::abs(lastPos - FocusAbsPosNP[0].getValue()) > 5)
880 {
882 lastPos = FocusAbsPosNP[0].getValue();
883 }
884 }
885
887 {
888 if (!isMoving())
889 {
894 lastPos = FocusAbsPosNP[0].getValue();
895 LOG_INFO("Focuser reached requested position.");
896 }
897 }
898
899 rc = readTemperature();
900 if (rc)
901 {
902 if (std::abs(lastTemperature - TemperatureNP[0].getValue()) >= 0.5)
903 {
904 TemperatureNP.apply();
905 lastTemperature = TemperatureNP[0].getValue();
906 }
907 }
908
910}
911
913{
914 return sendCommand("[STOP]");
915}
916
918{
919 Focuser::saveConfigItems(fp);
920
921 StepModeSP.save(fp);
922 FocusMaxMoveNP.save(fp);
923 SettleBufferNP.apply();
924 CoilsModeSP.save(fp);
925 IdleCoilsTimeoutNP.save(fp);
926 CurrentMoveSP.save(fp);
927 CurrentHoldSP.save(fp);
928
929 return true;
930}
931
932bool DeepSkyDadAF2::sendCommand(const char * cmd, char * res)
933{
934 int nbytes_written = 0, nbytes_read = 0, rc = -1;
935
936 tcflush(PortFD, TCIOFLUSH);
937
938 LOGF_DEBUG("CMD <%s>", cmd);
939
940 if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
941 {
942 char errstr[MAXRBUF] = {0};
943 tty_error_msg(rc, errstr, MAXRBUF);
944 LOGF_ERROR("Serial write error: %s.", errstr);
945 return false;
946 }
947
948 if (res == nullptr)
949 return true;
950
951 if ((rc = tty_nread_section(PortFD, res, DSD_RES, DSD_DEL, DSD_TIMEOUT, &nbytes_read)) != TTY_OK)
952 {
953 char errstr[MAXRBUF] = {0};
954 tty_error_msg(rc, errstr, MAXRBUF);
955 LOGF_ERROR("Serial read error: %s.", errstr);
956 return false;
957 }
958
959 LOGF_DEBUG("RES <%s>", res);
960
961 tcflush(PortFD, TCIOFLUSH);
962
963 return true;
964}
965
966bool DeepSkyDadAF2::sendCommandSet(const char * cmd)
967{
968 char res[DSD_RES] = {0};
969
970 if (sendCommand(cmd, res) == false)
971 return false;
972
973 return strcmp(res, "(OK)") == 0;
974}
975
977 {
978 char cmd[DSD_RES] = {0};
979 snprintf(cmd, DSD_RES, "[SMXP%d]", static_cast<int>(ticks));
980 return sendCommandSet(cmd);
981 }
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
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 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 void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool saveConfigItems(FILE *fp) override
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveAbsFocuser Move to an absolute target position.
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
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.
const char * getDefaultName() override
virtual bool SetFocuserMaxPosition(uint32_t ticks) override
SetFocuserMaxPosition Update focuser maximum position. It only updates the PresetNP property limits.
static void timedMoveHelper(void *context)
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
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
@ 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_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
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define MAXRBUF
Definition indidriver.c:52
#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