Instrument Neutral Distributed Interface INDI  2.0.2
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 (mutlaqja@ikarustech.com)
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 
36 static std::unique_ptr<DeepSkyDadAF1> deepSkyDadAf1(new DeepSkyDadAF1());
37 
39 {
41 }
42 
44 {
46 
47  // Step Mode
48  IUFillSwitch(&StepModeS[EIGHT], "EIGHT", "Eight Step", ISS_OFF);
49  IUFillSwitch(&StepModeS[QUARTER], "QUARTER", "Quarter Step", ISS_OFF);
50  IUFillSwitch(&StepModeS[HALF], "HALF", "Half Step", ISS_OFF);
51  IUFillSwitch(&StepModeS[FULL], "FULL", "Full Step", ISS_OFF);
52  IUFillSwitchVector(&StepModeSP, StepModeS, 4, getDeviceName(), "Step Mode", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
53  IPS_IDLE);
54 
55  /* Relative and absolute movement */
56  FocusRelPosN[0].min = 0.;
57  FocusRelPosN[0].max = 5000.;
58  FocusRelPosN[0].value = 0.;
59  FocusRelPosN[0].step = 10.;
60 
61  FocusAbsPosN[0].min = 0.;
62  FocusAbsPosN[0].max = 100000.;
63  FocusAbsPosN[0].value = 50000.;
64  FocusAbsPosN[0].step = 500.;
65 
66  // Max. movement
67  IUFillNumber(&FocusMaxMoveN[0], "MAX_MOVE", "Steps", "%7.0f", 0, 9999999, 100, 0);
68  IUFillNumberVector(&FocusMaxMoveNP, FocusMaxMoveN, 1, getDeviceName(), "FOCUS_MAX_MOVE", "Max. movement",
70 
71  // Settle buffer
72  IUFillNumber(&SettleBufferN[0], "SETTLE_BUFFER", "Period (ms)", "%5.0f", 0, 99999, 100, 0);
73  IUFillNumberVector(&SettleBufferNP, SettleBufferN, 1, getDeviceName(), "FOCUS_SETTLE_BUFFER", "Settle buffer",
75 
76  // Idle coils timeout (ms)
77  IUFillNumber(&IdleCoilsTimeoutN[0], "IDLE_COILS_TIMEOUT", "Period (ms)", "%6.0f", 0, 999999, 1000, 60000);
78  IUFillNumberVector(&IdleCoilsTimeoutNP, IdleCoilsTimeoutN, 1, getDeviceName(), "FOCUS_IDLE_COILS_TIMEOUT",
79  "Idle - coils timeout",
81 
82  // Coils mode
83  IUFillSwitch(&CoilsModeS[ALWAYS_ON], "ALWAYS_ON", "Always on", ISS_OFF);
84  IUFillSwitch(&CoilsModeS[IDLE_OFF], "IDLE_OFF", "Idle - off", ISS_OFF);
85  IUFillSwitch(&CoilsModeS[IDLE_COILS_TIMEOUT], "IDLE_COILS_TIMEOUT", "Idle - coils timeout (ms)", ISS_OFF);
86  IUFillSwitchVector(&CoilsModeSP, CoilsModeS, 3, getDeviceName(), "Coils mode", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
87  IPS_IDLE);
88 
89  // Current move
90  IUFillSwitch(&CurrentMoveS[CURRENT_25], "CMV_25", "25%", ISS_OFF);
91  IUFillSwitch(&CurrentMoveS[CURRENT_50], "CMV_50", "50%", ISS_OFF);
92  IUFillSwitch(&CurrentMoveS[CURRENT_75], "CMV_75", "75%", ISS_OFF);
93  IUFillSwitch(&CurrentMoveS[CURRENT_100], "CMV_100", "100%", ISS_OFF);
94  IUFillSwitchVector(&CurrentMoveSP, CurrentMoveS, 4, getDeviceName(), "Current - move", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY,
95  0, IPS_IDLE);
96 
97  // Current hold
98  IUFillSwitch(&CurrentHoldS[CURRENT_25], "CHD_25", "25%", ISS_OFF);
99  IUFillSwitch(&CurrentHoldS[CURRENT_50], "CHD_50", "50%", ISS_OFF);
100  IUFillSwitch(&CurrentHoldS[CURRENT_75], "CHD_75", "75%", ISS_OFF);
101  IUFillSwitch(&CurrentHoldS[CURRENT_100], "CHD_100", "100%", ISS_OFF);
102  IUFillSwitchVector(&CurrentHoldSP, CurrentHoldS, 4, getDeviceName(), "Current - hold", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY,
103  0, IPS_IDLE);
104 
106  addDebugControl();
107 
108  return true;
109 }
110 
112 {
114 
115  if (isConnected())
116  {
117  defineProperty(&FocusMaxMoveNP);
118  defineProperty(&StepModeSP);
119  defineProperty(&SettleBufferNP);
120  defineProperty(&CoilsModeSP);
121  defineProperty(&IdleCoilsTimeoutNP);
122  defineProperty(&CurrentMoveSP);
123  defineProperty(&CurrentHoldSP);
124 
125  GetFocusParams();
126 
127  LOG_INFO("deepSkyDadAf1 parameters updated, focuser ready for use.");
128  }
129  else
130  {
131  deleteProperty(FocusMaxMoveNP.name);
132  deleteProperty(StepModeSP.name);
133  deleteProperty(SettleBufferNP.name);
134  deleteProperty(CoilsModeSP.name);
135  deleteProperty(IdleCoilsTimeoutNP.name);
136  deleteProperty(CurrentMoveSP.name);
137  deleteProperty(CurrentHoldSP.name);
138  }
139 
140  return true;
141 }
142 
144 {
145  if (Ack())
146  {
147  LOG_INFO("deepSkyDadAf1 is online. Getting focus parameters...");
148  return true;
149  }
150 
151  LOG_INFO(
152  "Error retrieving data from deepSkyDadAf1, please ensure deepSkyDadAf1 controller is powered and the port is correct.");
153  return false;
154 }
155 
157 {
158  return "Deep Sky Dad AF1";
159 }
160 
161 bool DeepSkyDadAF1::Ack()
162 {
163  sleep(2);
164 
165  char res[DSD_RES] = {0};
166  if (!sendCommand("[GPOS]", res))
167  {
168  LOG_ERROR("ACK - getPosition failed");
169  return false;
170  }
171 
172  int32_t pos;
173  int rc = sscanf(res, "(%d)", &pos);
174 
175  if (rc <= 0)
176  {
177  LOG_ERROR("ACK - getPosition failed");
178  return false;
179  }
180 
181  return true;
182 }
183 
184 bool DeepSkyDadAF1::readStepMode()
185 {
186  char res[DSD_RES] = {0};
187 
188  if (sendCommand("[GSTP]", res) == false)
189  return false;
190 
191  if (strcmp(res, "(1)") == 0)
192  StepModeS[FULL].s = ISS_ON;
193  else if (strcmp(res, "(2)") == 0)
194  StepModeS[HALF].s = ISS_ON;
195  else if (strcmp(res, "(4)") == 0)
196  StepModeS[QUARTER].s = ISS_ON;
197  else if (strcmp(res, "(8)") == 0)
198  StepModeS[EIGHT].s = ISS_ON;
199  else
200  {
201  LOGF_ERROR("Unknown error: focuser step value (%s)", res);
202  return false;
203  }
204 
205  return true;
206 }
207 
208 bool DeepSkyDadAF1::readPosition()
209 {
210  char res[DSD_RES] = {0};
211 
212  if (sendCommand("[GPOS]", res) == false)
213  return false;
214 
215  int32_t pos;
216  int rc = sscanf(res, "(%d)", &pos);
217 
218  if (rc > 0)
219  FocusAbsPosN[0].value = pos;
220  else
221  {
222  LOGF_ERROR("Unknown error: focuser position value (%s)", res);
223  return false;
224  }
225 
226  return true;
227 }
228 
229 bool DeepSkyDadAF1::readMaxMovement()
230 {
231  char res[DSD_RES] = {0};
232 
233  if (sendCommand("[GMXM]", res) == false)
234  return false;
235 
236  uint32_t steps = 0;
237  int rc = sscanf(res, "(%d)", &steps);
238  if (rc > 0)
239  {
240  FocusMaxMoveN[0].value = steps;
241  FocusMaxMoveNP.s = IPS_OK;
242  }
243  else
244  {
245  LOGF_ERROR("Unknown error: maximum movement value (%s)", res);
246  return false;
247  }
248 
249  return true;
250 }
251 
252 bool DeepSkyDadAF1::readMaxPosition()
253 {
254  char res[DSD_RES] = {0};
255 
256  if (sendCommand("[GMXP]", res) == false)
257  return false;
258 
259  uint32_t steps = 0;
260  int rc = sscanf(res, "(%d)", &steps);
261  if (rc > 0)
262  {
263  FocusMaxPosN[0].value = steps;
265  }
266  else
267  {
268  LOGF_ERROR("Unknown error: maximum position value (%s)", res);
269  return false;
270  }
271 
272  return true;
273 }
274 
275 bool DeepSkyDadAF1::readSettleBuffer()
276 {
277  char res[DSD_RES] = {0};
278 
279  if (sendCommand("[GBUF]", res) == false)
280  return false;
281 
282  uint32_t settleBuffer = 0;
283  int rc = sscanf(res, "(%d)", &settleBuffer);
284  if (rc > 0)
285  {
286  SettleBufferN[0].value = settleBuffer;
287  SettleBufferNP.s = settleBuffer > 0 ? IPS_OK : IPS_IDLE;
288  }
289  else
290  {
291  LOGF_ERROR("Unknown error: settle buffer value (%s)", res);
292  return false;
293  }
294 
295  return true;
296 }
297 
298 bool DeepSkyDadAF1::readIdleCoilsTimeout()
299 {
300  char res[DSD_RES] = {0};
301 
302  if (sendCommand("[GIDC]", res) == false)
303  return false;
304 
305  uint32_t ms = 0;
306  int rc = sscanf(res, "(%d)", &ms);
307  if (rc > 0)
308  {
309  IdleCoilsTimeoutN[0].value = ms;
310  IdleCoilsTimeoutNP.s = ms > 0 ? IPS_OK : IPS_IDLE;
311  }
312  else
313  {
314  LOGF_ERROR("Unknown error: idle coils timeout value (%s)", res);
315  return false;
316  }
317 
318  return true;
319 }
320 
321 bool DeepSkyDadAF1::readCoilsMode()
322 {
323  char res[DSD_RES] = {0};
324 
325  if (sendCommand("[GCLM]", res) == false)
326  return false;
327 
328  if (strcmp(res, "(0)") == 0)
329  {
330  CoilsModeSP.s = IPS_IDLE;
331  CoilsModeS[IDLE_OFF].s = ISS_ON;
332  }
333  else if (strcmp(res, "(1)") == 0)
334  {
335  CoilsModeSP.s = IPS_OK;
336  CoilsModeS[ALWAYS_ON].s = ISS_ON;
337  }
338  else if (strcmp(res, "(2)") == 0)
339  {
340  CoilsModeSP.s = IPS_IDLE;
341  CoilsModeS[IDLE_COILS_TIMEOUT].s = ISS_ON;
342  }
343  else
344  {
345  LOGF_ERROR("Unknown error: readCoilsMode value (%s)", res);
346  return false;
347  }
348 
349  return true;
350 }
351 
352 bool DeepSkyDadAF1::readCurrentMove()
353 {
354  char res[DSD_RES] = {0};
355 
356  if (sendCommand("[GCMV%]", res) == false)
357  return false;
358 
359  if (strcmp(res, "(25%)") == 0)
360  {
361  CurrentMoveSP.s = IPS_OK;
362  CurrentMoveS[CURRENT_25].s = ISS_ON;
363  }
364  else if (strcmp(res, "(50%)") == 0)
365  {
366  CurrentMoveSP.s = IPS_OK;
367  CurrentMoveS[CURRENT_50].s = ISS_ON;
368  }
369  else if (strcmp(res, "(75%)") == 0)
370  {
371  CurrentMoveSP.s = IPS_OK;
372  CurrentMoveS[CURRENT_75].s = ISS_ON;
373  }
374  else if (strcmp(res, "(100%)") == 0)
375  {
376  CurrentMoveSP.s = IPS_OK;
377  CurrentMoveS[CURRENT_100].s = ISS_ON;
378  }
379 
380  else
381  {
382  LOGF_ERROR("Unknown error: currentMove value (%s)", res);
383  return false;
384  }
385 
386  return true;
387 }
388 
389 bool DeepSkyDadAF1::readCurrentHold()
390 {
391  char res[DSD_RES] = {0};
392 
393  if (sendCommand("[GCHD%]", res) == false)
394  return false;
395 
396  if (strcmp(res, "(25%)") == 0)
397  {
398  CurrentHoldSP.s = IPS_OK;
399  CurrentHoldS[CURRENT_25].s = ISS_ON;
400  }
401  else if (strcmp(res, "(50%)") == 0)
402  {
403  CurrentHoldSP.s = IPS_OK;
404  CurrentHoldS[CURRENT_50].s = ISS_ON;
405  }
406  else if (strcmp(res, "(75%)") == 0)
407  {
408  CurrentHoldSP.s = IPS_OK;
409  CurrentHoldS[CURRENT_75].s = ISS_ON;
410  }
411  else if (strcmp(res, "(100%)") == 0)
412  {
413  CurrentHoldSP.s = IPS_OK;
414  CurrentHoldS[CURRENT_100].s = ISS_ON;
415  }
416 
417  else
418  {
419  LOGF_ERROR("Unknown error: currentMove value (%s)", res);
420  return false;
421  }
422 
423  return true;
424 }
425 
426 bool DeepSkyDadAF1::isMoving()
427 {
428  char res[DSD_RES] = {0};
429 
430  if (sendCommand("[GMOV]", res) == false)
431  return false;
432 
433  if (strcmp(res, "(1)") == 0)
434  return true;
435  else if (strcmp(res, "(0)") == 0)
436  return false;
437 
438  LOGF_ERROR("Unknown error: isMoving value (%s)", res);
439  return false;
440 }
441 
442 bool DeepSkyDadAF1::SyncFocuser(uint32_t ticks)
443 {
444  char cmd[DSD_RES] = {0};
445  snprintf(cmd, DSD_RES, "[SPOS%06d]", ticks);
446  return sendCommand(cmd);
447 }
448 
450 {
451  char cmd[DSD_RES] = {0};
452  snprintf(cmd, DSD_RES, "[SREV%01d]", enabled ? 1 : 0);
453  return sendCommand(cmd);
454 }
455 
456 bool DeepSkyDadAF1::MoveFocuser(uint32_t position)
457 {
458  char cmd[DSD_RES] = {0};
459  char res[DSD_RES] = {0};
460  snprintf(cmd, DSD_RES, "[STRG%06d]", position);
461  // Set Position First
462  if (sendCommand(cmd, res) == false)
463  return false;
464 
465  if(strcmp(res, "!101)") == 0)
466  {
467  LOG_ERROR("MoveFocuserFailed - requested movement too big. You can increase the limit by changing the value of Max. movement.");
468  return false;
469  }
470 
471  // Now start motion toward position
472  if (sendCommand("[SMOV]") == false)
473  return false;
474 
475  return true;
476 }
477 
478 bool DeepSkyDadAF1::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
479 {
480  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
481  {
482  // Focus Step Mode
483  if (strcmp(StepModeSP.name, name) == 0)
484  {
485  int current_mode = IUFindOnSwitchIndex(&StepModeSP);
486 
487  IUUpdateSwitch(&StepModeSP, states, names, n);
488 
489  int target_mode = IUFindOnSwitchIndex(&StepModeSP);
490 
491  if (current_mode == target_mode)
492  {
493  StepModeSP.s = IPS_OK;
494  IDSetSwitch(&StepModeSP, nullptr);
495  return true;
496  }
497 
498  char cmd[DSD_RES] = {0};
499 
500  if(target_mode == 0)
501  target_mode = 1;
502  else if(target_mode == 1)
503  target_mode = 2;
504  else if(target_mode == 2)
505  target_mode = 4;
506  else if(target_mode == 3)
507  target_mode = 8;
508 
509  snprintf(cmd, DSD_RES, "[SSTP%d]", target_mode);
510  bool rc = sendCommandSet(cmd);
511  if (!rc)
512  {
513  IUResetSwitch(&StepModeSP);
514  StepModeS[current_mode].s = ISS_ON;
515  StepModeSP.s = IPS_ALERT;
516  IDSetSwitch(&StepModeSP, nullptr);
517  return false;
518  }
519 
520  StepModeSP.s = IPS_OK;
521  IDSetSwitch(&StepModeSP, nullptr);
522  return true;
523  }
524 
525  // Coils mode
526  if (strcmp(CoilsModeSP.name, name) == 0)
527  {
528  int coilsModeCurrent = IUFindOnSwitchIndex(&CoilsModeSP);
529 
530  IUUpdateSwitch(&CoilsModeSP, states, names, n);
531 
532  int coilsModeTarget = IUFindOnSwitchIndex(&CoilsModeSP);
533 
534  if (coilsModeCurrent == coilsModeTarget)
535  {
536  IDSetSwitch(&CoilsModeSP, nullptr);
537  return true;
538  }
539 
540  if(coilsModeTarget == 0)
541  coilsModeTarget = 1;
542  else if(coilsModeTarget == 1)
543  coilsModeTarget = 0;
544  else if(coilsModeTarget == 2)
545  coilsModeTarget = 2;
546 
547  char cmd[DSD_RES] = {0};
548  snprintf(cmd, DSD_RES, "[SCLM%d]", coilsModeTarget);
549 
550  bool rc = sendCommandSet(cmd);
551  if (!rc)
552  {
553  IUResetSwitch(&CoilsModeSP);
554  CoilsModeS[coilsModeCurrent].s = ISS_ON;
555  CoilsModeSP.s = IPS_ALERT;
556  IDSetSwitch(&CoilsModeSP, nullptr);
557  return false;
558  }
559 
560  CoilsModeSP.s = coilsModeTarget == 1 ? IPS_OK : IPS_IDLE;
561  IDSetSwitch(&CoilsModeSP, nullptr);
562  return true;
563  }
564 
565  // Current - move
566  if (strcmp(CurrentMoveSP.name, name) == 0)
567  {
568  int current = IUFindOnSwitchIndex(&CurrentMoveSP);
569 
570  IUUpdateSwitch(&CurrentMoveSP, states, names, n);
571 
572  int targetCurrent = IUFindOnSwitchIndex(&CurrentMoveSP);
573 
574  if (current == targetCurrent)
575  {
576  IDSetSwitch(&CurrentMoveSP, nullptr);
577  return true;
578  }
579 
580  int targetCurrentValue = 75;
581  switch(targetCurrent)
582  {
583  case 0:
584  targetCurrentValue = 25;
585  break;
586  case 1:
587  targetCurrentValue = 50;
588  break;
589  case 2:
590  targetCurrentValue = 75;
591  break;
592  case 3:
593  targetCurrentValue = 100;
594  break;
595  }
596 
597  char cmd[DSD_RES] = {0};
598  snprintf(cmd, DSD_RES, "[SCMV%d%%]", targetCurrentValue);
599 
600  bool rc = sendCommandSet(cmd);
601  if (!rc)
602  {
603  IUResetSwitch(&CurrentMoveSP);
604  CurrentMoveS[current].s = ISS_ON;
605  CurrentMoveSP.s = IPS_ALERT;
606  IDSetSwitch(&CurrentMoveSP, nullptr);
607  return false;
608  }
609 
610  CurrentMoveSP.s = IPS_OK;
611  IDSetSwitch(&CurrentMoveSP, nullptr);
612  return true;
613  }
614 
615  // Current - hold
616  if (strcmp(CurrentHoldSP.name, name) == 0)
617  {
618  int current = IUFindOnSwitchIndex(&CurrentHoldSP);
619 
620  IUUpdateSwitch(&CurrentHoldSP, states, names, n);
621 
622  int targetCurrent = IUFindOnSwitchIndex(&CurrentHoldSP);
623 
624  if (current == targetCurrent)
625  {
626  IDSetSwitch(&CurrentHoldSP, nullptr);
627  return true;
628  }
629 
630  int targetCurrentValue = 75;
631  switch(targetCurrent)
632  {
633  case 0:
634  targetCurrentValue = 25;
635  break;
636  case 1:
637  targetCurrentValue = 50;
638  break;
639  case 2:
640  targetCurrentValue = 75;
641  break;
642  case 3:
643  targetCurrentValue = 100;
644  break;
645  }
646 
647  char cmd[DSD_RES] = {0};
648  snprintf(cmd, DSD_RES, "[SCHD%d%%]", targetCurrentValue);
649 
650  bool rc = sendCommandSet(cmd);
651  if (!rc)
652  {
653  IUResetSwitch(&CurrentHoldSP);
654  CurrentHoldS[current].s = ISS_ON;
655  CurrentHoldSP.s = IPS_ALERT;
656  IDSetSwitch(&CurrentHoldSP, nullptr);
657  return false;
658  }
659 
660  CurrentHoldSP.s = IPS_OK;
661  IDSetSwitch(&CurrentHoldSP, nullptr);
662  return true;
663  }
664  }
665 
666  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
667 }
668 
669 bool DeepSkyDadAF1::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
670 {
671  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
672  {
673  // Settle buffer Settings
674  if (strcmp(name, SettleBufferNP.name) == 0)
675  {
676  IUUpdateNumber(&SettleBufferNP, values, names, n);
677  char cmd[DSD_RES] = {0};
678  snprintf(cmd, DSD_RES, "[SBUF%06d]", static_cast<int>(SettleBufferN[0].value));
679  bool rc = sendCommandSet(cmd);
680  if (!rc)
681  {
682  SettleBufferNP.s = IPS_ALERT;
683  return false;
684  }
685 
686  SettleBufferNP.s = IPS_OK;
687  IDSetNumber(&SettleBufferNP, nullptr);
688  return true;
689  }
690 
691  // Idle coils timeout
692  if (strcmp(name, IdleCoilsTimeoutNP.name) == 0)
693  {
694  IUUpdateNumber(&IdleCoilsTimeoutNP, values, names, n);
695  char cmd[DSD_RES] = {0};
696  snprintf(cmd, DSD_RES, "[SIDC%06d]", static_cast<int>(IdleCoilsTimeoutN[0].value));
697  bool rc = sendCommandSet(cmd);
698  if (!rc)
699  {
700  IdleCoilsTimeoutNP.s = IPS_ALERT;
701  return false;
702  }
703 
704  IdleCoilsTimeoutNP.s = IPS_OK;
705  IDSetNumber(&IdleCoilsTimeoutNP, nullptr);
706  return true;
707  }
708 
709  // Max. position
710  if (strcmp(name, FocusMaxPosNP.name) == 0)
711  {
712  IUUpdateNumber(&FocusMaxPosNP, values, names, n);
713  char cmd[DSD_RES] = {0};
714  snprintf(cmd, DSD_RES, "[SMXP%d]", static_cast<int>(FocusMaxPosN[0].value));
715  bool rc = sendCommandSet(cmd);
716  if (!rc)
717  {
719  return false;
720  }
721 
723  IDSetNumber(&FocusMaxPosNP, nullptr);
724  return true;
725  }
726 
727  // Max. movement
728  if (strcmp(name, FocusMaxMoveNP.name) == 0)
729  {
730  IUUpdateNumber(&FocusMaxMoveNP, values, names, n);
731  char cmd[DSD_RES] = {0};
732  snprintf(cmd, DSD_RES, "[SMXM%d]", static_cast<int>(FocusMaxMoveN[0].value));
733  bool rc = sendCommandSet(cmd);
734  if (!rc)
735  {
736  FocusMaxMoveNP.s = IPS_ALERT;
737  return false;
738  }
739 
740  FocusMaxMoveNP.s = IPS_OK;
741  IDSetNumber(&FocusMaxMoveNP, nullptr);
742  return true;
743  }
744  }
745 
746  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
747 }
748 
749 void DeepSkyDadAF1::GetFocusParams()
750 {
751  IUResetSwitch(&StepModeSP);
752  IUResetSwitch(&CoilsModeSP);
753  IUResetSwitch(&CurrentMoveSP);
754  IUResetSwitch(&CurrentHoldSP);
755 
756  if (readPosition())
757  IDSetNumber(&FocusAbsPosNP, nullptr);
758 
759  if (readStepMode())
760  IDSetSwitch(&StepModeSP, nullptr);
761 
762  if (readSettleBuffer())
763  IDSetNumber(&SettleBufferNP, nullptr);
764 
765  if (readMaxPosition())
766  IDSetNumber(&FocusMaxPosNP, nullptr);
767 
768  if (readMaxMovement())
769  IDSetNumber(&FocusMaxMoveNP, nullptr);
770 
771  if (readIdleCoilsTimeout())
772  IDSetNumber(&IdleCoilsTimeoutNP, nullptr);
773 
774  if (readCoilsMode())
775  IDSetSwitch(&CoilsModeSP, nullptr);
776 
777  if (readCurrentMove())
778  IDSetSwitch(&CurrentMoveSP, nullptr);
779 
780  if (readCurrentHold())
781  IDSetSwitch(&CurrentHoldSP, nullptr);
782 }
783 
784 IPState DeepSkyDadAF1::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
785 {
786  INDI_UNUSED(speed);
787  // either go all the way in or all the way out
788  // then use timer to stop
789  if (dir == FOCUS_INWARD)
790  MoveFocuser(0);
791  else
792  MoveFocuser(FocusMaxPosN[0].value);
793 
794  IEAddTimer(duration, &DeepSkyDadAF1::timedMoveHelper, this);
795  return IPS_BUSY;
796 }
797 
798 void DeepSkyDadAF1::timedMoveHelper(void * context)
799 {
800  static_cast<DeepSkyDadAF1*>(context)->timedMoveCallback();
801 }
802 
803 void DeepSkyDadAF1::timedMoveCallback()
804 {
805  AbortFocuser();
809  FocusTimerN[0].value = 0;
810  IDSetNumber(&FocusAbsPosNP, nullptr);
811  IDSetNumber(&FocusRelPosNP, nullptr);
812  IDSetNumber(&FocusTimerNP, nullptr);
813 }
814 
815 
817 {
818  targetPos = targetTicks;
819 
820  if (!MoveFocuser(targetPos))
821  return IPS_ALERT;
822 
823  return IPS_BUSY;
824 }
825 
827 {
828  int32_t newPosition = 0;
829 
830  if (dir == FOCUS_INWARD)
831  newPosition = FocusAbsPosN[0].value - ticks;
832  else
833  newPosition = FocusAbsPosN[0].value + ticks;
834 
835  // Clamp
836  newPosition = std::max(0, std::min(static_cast<int32_t>(FocusAbsPosN[0].max), newPosition));
837  if (!MoveFocuser(newPosition))
838  return IPS_ALERT;
839 
840  // JM 2019-02-10: This is already set by the framework
841  //FocusRelPosN[0].value = ticks;
842  //FocusRelPosNP.s = IPS_BUSY;
843 
844  return IPS_BUSY;
845 }
846 
848 {
849  if (!isConnected())
850  {
852  return;
853  }
854 
855  bool rc = readPosition();
856  if (rc)
857  {
858  if (std::abs(lastPos - FocusAbsPosN[0].value) > 5)
859  {
860  IDSetNumber(&FocusAbsPosNP, nullptr);
861  lastPos = FocusAbsPosN[0].value;
862  }
863  }
864 
866  {
867  if (!isMoving())
868  {
871  IDSetNumber(&FocusAbsPosNP, nullptr);
872  IDSetNumber(&FocusRelPosNP, nullptr);
873  lastPos = FocusAbsPosN[0].value;
874  LOG_INFO("Focuser reached requested position.");
875  }
876  }
877 
879 }
880 
882 {
883  return sendCommand("[STOP]");
884 }
885 
887 {
888  Focuser::saveConfigItems(fp);
889 
890  IUSaveConfigSwitch(fp, &StepModeSP);
891  IUSaveConfigNumber(fp, &FocusMaxMoveNP);
892  IUSaveConfigNumber(fp, &SettleBufferNP);
893  IUSaveConfigSwitch(fp, &CoilsModeSP);
894  IUSaveConfigNumber(fp, &IdleCoilsTimeoutNP);
895  IUSaveConfigSwitch(fp, &CurrentMoveSP);
896  IUSaveConfigSwitch(fp, &CurrentHoldSP);
897 
898  return true;
899 }
900 
901 bool DeepSkyDadAF1::sendCommand(const char * cmd, char * res)
902 {
903  int nbytes_written = 0, nbytes_read = 0, rc = -1;
904 
905  tcflush(PortFD, TCIOFLUSH);
906 
907  LOGF_DEBUG("CMD <%s>", cmd);
908 
909  if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
910  {
911  char errstr[MAXRBUF] = {0};
912  tty_error_msg(rc, errstr, MAXRBUF);
913  LOGF_ERROR("Serial write error: %s.", errstr);
914  return false;
915  }
916 
917  if (res == nullptr)
918  return true;
919 
920  if ((rc = tty_nread_section(PortFD, res, DSD_RES, DSD_DEL, DSD_TIMEOUT, &nbytes_read)) != TTY_OK)
921  {
922  char errstr[MAXRBUF] = {0};
923  tty_error_msg(rc, errstr, MAXRBUF);
924  LOGF_ERROR("Serial read error: %s.", errstr);
925  return false;
926  }
927 
928  LOGF_DEBUG("RES <%s>", res);
929 
930  tcflush(PortFD, TCIOFLUSH);
931 
932  return true;
933 }
934 
935 bool DeepSkyDadAF1::sendCommandSet(const char * cmd)
936 {
937  char res[DSD_RES] = {0};
938 
939  if (sendCommand(cmd, res) == false)
940  return false;
941 
942  return strcmp(res, "(OK)") == 0;
943 }
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
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
Process the client newNumber command.
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
Initilize properties initial state and value. The child class must implement this function.
virtual bool Handshake() override
perform handshake with device to check communication
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
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
INumberVectorProperty FocusTimerNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INumberVectorProperty FocusMaxPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
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:582
double max(void)
double min(void)
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
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:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
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:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
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:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#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 MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371