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