Instrument Neutral Distributed Interface INDI  2.0.2
deepskydad_af3.cpp
Go to the documentation of this file.
1 /*
2  Deep Sky Dad AF3 focuser
3 
4  Copyright (C) 2019 Pavle Gartner
5 
6  Based on Moonlite 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_af3.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<DeepSkyDadAF3> deepSkyDadAf3(new DeepSkyDadAF3());
37 
39 {
42 }
43 
45 {
47 
48  // Step Mode
49  IUFillSwitch(&StepModeS[S256], "S256", "1/256 Step", ISS_OFF);
50  IUFillSwitch(&StepModeS[S128], "S128", "1/128 Step", ISS_OFF);
51  IUFillSwitch(&StepModeS[S64], "S64", "1/64 Step", ISS_OFF);
52  IUFillSwitch(&StepModeS[S32], "S32", "1/32 Step", ISS_OFF);
53  IUFillSwitch(&StepModeS[S16], "S16", "1/16 Step", ISS_OFF);
54  IUFillSwitch(&StepModeS[S8], "S8", "1/8 Step", ISS_OFF);
55  IUFillSwitch(&StepModeS[S4], "S4", "1/4 Step", ISS_OFF);
56  IUFillSwitch(&StepModeS[S2], "S2", "1/2 Step", ISS_OFF);
57  IUFillSwitch(&StepModeS[S1], "S1", "Full Step", ISS_OFF);
58  IUFillSwitchVector(&StepModeSP, StepModeS, 9, getDeviceName(), "Step Mode", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
59  IPS_IDLE);
60 
61  // Speed Mode
62  IUFillSwitch(&SpeedModeS[VERY_SLOW], "VERY_SLOW", "Very slow", ISS_OFF);
63  IUFillSwitch(&SpeedModeS[SLOW], "SLOW", "Slow", ISS_OFF);
64  IUFillSwitch(&SpeedModeS[MEDIUM], "MEDIUM", "Medium", ISS_OFF);
65  IUFillSwitch(&SpeedModeS[FAST], "FAST", "Fast", ISS_OFF);
66  IUFillSwitch(&SpeedModeS[VERY_FAST], "VERY_FAST", "Very fast", ISS_OFF);
67  IUFillSwitchVector(&SpeedModeSP, SpeedModeS, 5, getDeviceName(), "Speed Mode", "", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0,
68  IPS_IDLE);
69 
70  /* Relative and absolute movement */
71  FocusRelPosN[0].min = 0.;
72  FocusRelPosN[0].max = 50000.;
73  FocusRelPosN[0].value = 0.;
74  FocusRelPosN[0].step = 10.;
75 
76  FocusAbsPosN[0].min = 0.;
77  FocusAbsPosN[0].max = 1000000.;
78  FocusAbsPosN[0].value = 50000.;
79  FocusAbsPosN[0].step = 5000.;
80 
81  FocusMaxPosN[0].min = 0.;
82  FocusMaxPosN[0].max = 1000000.;
83  FocusMaxPosN[0].value = 1000000.;
84  FocusMaxPosN[0].step = 5000.;
85 
86  FocusSyncN[0].min = 0.;
87  FocusSyncN[0].max = 1000000.;
88  FocusSyncN[0].value = 50000.;
89  FocusSyncN[0].step = 5000.;
90 
91  FocusBacklashN[0].min = -1000;
92  FocusBacklashN[0].max = 1000;
93  FocusBacklashN[0].step = 1;
94  FocusBacklashN[0].value = 0;
95 
96  // Max. movement
97  IUFillNumber(&FocusMaxMoveN[0], "MAX_MOVE", "Steps", "%7.0f", 0, 9999999, 100, 0);
98  IUFillNumberVector(&FocusMaxMoveNP, FocusMaxMoveN, 1, getDeviceName(), "FOCUS_MAX_MOVE", "Max. movement",
100 
101  // Settle buffer
102  IUFillNumber(&SettleBufferN[0], "SETTLE_BUFFER", "Period (ms)", "%5.0f", 0, 99999, 100, 0);
103  IUFillNumberVector(&SettleBufferNP, SettleBufferN, 1, getDeviceName(), "FOCUS_SETTLE_BUFFER", "Settle buffer",
104  OPTIONS_TAB, IP_RW, 0, IPS_IDLE);
105 
106  // Motor move multiplier
107  IUFillNumber(&MoveCurrentMultiplierN[0], "MOTOR_MOVE_MULTIPLIER", "%", "%3.0f", 1, 100, 1, 90);
108  IUFillNumberVector(&MoveCurrentMultiplierNP, MoveCurrentMultiplierN, 1, getDeviceName(), "FOCUS_MMM",
109  "Move current multiplier",
110  OPTIONS_TAB, IP_RW, 0, IPS_IDLE);
111 
112  // Motor hold multiplier
113  IUFillNumber(&HoldCurrentMultiplierN[0], "MOTOR_HOLD_MULTIPLIER", "%", "%3.0f", 1, 100, 1, 40);
114  IUFillNumberVector(&HoldCurrentMultiplierNP, HoldCurrentMultiplierN, 1, getDeviceName(), "FOCUS_MHM",
115  "Hold current multiplier",
116  OPTIONS_TAB, IP_RW, 0, IPS_IDLE);
117 
118  // Focuser temperature
119  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
120  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
122 
124  addDebugControl();
125 
126  return true;
127 }
128 
130 {
132 
133  if (isConnected())
134  {
135  defineProperty(&FocusMaxMoveNP);
136  defineProperty(&StepModeSP);
137  defineProperty(&SpeedModeSP);
138  defineProperty(&SettleBufferNP);
139  defineProperty(&MoveCurrentMultiplierNP);
140  defineProperty(&HoldCurrentMultiplierNP);
141  defineProperty(&TemperatureNP);
142 
143  GetFocusParams();
144 
145  LOG_INFO("deepSkyDadAf3 parameters updated, focuser ready for use.");
146  }
147  else
148  {
149  deleteProperty(FocusMaxMoveNP.name);
150  deleteProperty(StepModeSP.name);
151  deleteProperty(SpeedModeSP.name);
152  deleteProperty(SettleBufferNP.name);
153  deleteProperty(MoveCurrentMultiplierNP.name);
154  deleteProperty(HoldCurrentMultiplierNP.name);
155  deleteProperty(TemperatureNP.name);
156  }
157 
158  return true;
159 }
160 
162 {
163  if (Ack())
164  {
165  LOG_INFO("deepSkyDadAf3 is online. Getting focus parameters...");
166  return true;
167  }
168 
169  LOG_INFO(
170  "Error retrieving data from deepSkyDadAf3, please ensure deepSkyDadAf3 controller is powered and the port is correct.");
171  return false;
172 }
173 
175 {
176  return "Deep Sky Dad AF3";
177 }
178 
179 bool DeepSkyDadAF3::Ack()
180 {
181  sleep(2);
182 
183  char res[DSD_RES] = {0};
184  if (!sendCommand("[GPOS]", res) && !sendCommand("[GPOS]", res)) //try twice
185  {
186  LOG_ERROR("ACK - getPosition failed");
187  return false;
188  }
189 
190  int32_t pos;
191  int rc = sscanf(res, "(%d)", &pos);
192 
193  if (rc <= 0)
194  {
195  LOG_ERROR("ACK - getPosition failed");
196  return false;
197  }
198 
199  return true;
200 }
201 
202 bool DeepSkyDadAF3::readStepMode()
203 {
204  char res[DSD_RES] = {0};
205 
206  if (sendCommand("[GSTP]", res) == false)
207  return false;
208 
209  if (strcmp(res, "(1)") == 0)
210  StepModeS[S1].s = ISS_ON;
211  else if (strcmp(res, "(2)") == 0)
212  StepModeS[S2].s = ISS_ON;
213  else if (strcmp(res, "(4)") == 0)
214  StepModeS[S4].s = ISS_ON;
215  else if (strcmp(res, "(8)") == 0)
216  StepModeS[S8].s = ISS_ON;
217  else if (strcmp(res, "(16)") == 0)
218  StepModeS[S16].s = ISS_ON;
219  else if (strcmp(res, "(32)") == 0)
220  StepModeS[S32].s = ISS_ON;
221  else if (strcmp(res, "(64)") == 0)
222  StepModeS[S64].s = ISS_ON;
223  else if (strcmp(res, "(128)") == 0)
224  StepModeS[S128].s = ISS_ON;
225  else if (strcmp(res, "(256)") == 0)
226  StepModeS[S256].s = ISS_ON;
227  else
228  {
229  LOGF_ERROR("Unknown error: focuser step value (%s)", res);
230  return false;
231  }
232 
233  StepModeSP.s = IPS_OK;
234  return true;
235 }
236 
237 bool DeepSkyDadAF3::readSpeedMode()
238 {
239  char res[DSD_RES] = {0};
240 
241  if (sendCommand("[GSPD]", res) == false)
242  return false;
243 
244  if (strcmp(res, "(1)") == 0)
245  SpeedModeS[VERY_SLOW].s = ISS_ON;
246  else if (strcmp(res, "(2)") == 0)
247  SpeedModeS[SLOW].s = ISS_ON;
248  else if (strcmp(res, "(3)") == 0)
249  SpeedModeS[MEDIUM].s = ISS_ON;
250  else if (strcmp(res, "(4)") == 0)
251  SpeedModeS[FAST].s = ISS_ON;
252  else if (strcmp(res, "(5)") == 0)
253  SpeedModeS[VERY_FAST].s = ISS_ON;
254  else
255  {
256  LOGF_ERROR("Unknown error: focuser speed value (%s)", res);
257  return false;
258  }
259 
260  SpeedModeSP.s = IPS_OK;
261  return true;
262 }
263 
264 bool DeepSkyDadAF3::readPosition()
265 {
266  char res[DSD_RES] = {0};
267 
268  if (sendCommand("[GPOS]", res) == false)
269  return false;
270 
271  int32_t pos;
272  int rc = sscanf(res, "(%d)", &pos);
273 
274  if (rc > 0)
275  FocusAbsPosN[0].value = pos;
276  else
277  {
278  LOGF_ERROR("Unknown error: focuser position value (%s)", res);
279  return false;
280  }
281 
282  return true;
283 }
284 
285 bool DeepSkyDadAF3::readMaxMovement()
286 {
287  char res[DSD_RES] = {0};
288 
289  if (sendCommand("[GMXM]", res) == false)
290  return false;
291 
292  uint32_t steps = 0;
293  int rc = sscanf(res, "(%d)", &steps);
294  if (rc > 0)
295  {
296  FocusMaxMoveN[0].value = steps;
297  FocusMaxMoveNP.s = IPS_OK;
298  }
299  else
300  {
301  LOGF_ERROR("Unknown error: maximum movement value (%s)", res);
302  return false;
303  }
304 
305  return true;
306 }
307 
308 bool DeepSkyDadAF3::readMaxPosition()
309 {
310  char res[DSD_RES] = {0};
311 
312  if (sendCommand("[GMXP]", res) == false)
313  return false;
314 
315  uint32_t steps = 0;
316  int rc = sscanf(res, "(%d)", &steps);
317  if (rc > 0)
318  {
319  FocusMaxPosN[0].value = steps;
321  }
322  else
323  {
324  LOGF_ERROR("Unknown error: maximum position value (%s)", res);
325  return false;
326  }
327 
328  return true;
329 }
330 
331 bool DeepSkyDadAF3::readSettleBuffer()
332 {
333  char res[DSD_RES] = {0};
334 
335  if (sendCommand("[GBUF]", res) == false)
336  return false;
337 
338  uint32_t settleBuffer = 0;
339  int rc = sscanf(res, "(%d)", &settleBuffer);
340  if (rc > 0)
341  {
342  SettleBufferN[0].value = settleBuffer;
343  SettleBufferNP.s = settleBuffer > 0 ? IPS_OK : IPS_IDLE;
344  }
345  else
346  {
347  LOGF_ERROR("Unknown error: settle buffer value (%s)", res);
348  return false;
349  }
350 
351  return true;
352 }
353 
354 bool DeepSkyDadAF3::readMoveCurrentMultiplier()
355 {
356  char res[DSD_RES] = {0};
357 
358  if (sendCommand("[GMMM]", res) == false)
359  return false;
360 
361  uint32_t mcm = 0;
362  int rc = sscanf(res, "(%d)", &mcm);
363  if (rc > 0)
364  {
365  MoveCurrentMultiplierN[0].value = mcm;
366  MoveCurrentMultiplierNP.s = IPS_OK;
367  }
368  else
369  {
370  LOGF_ERROR("Unknown error: move current multiplier value (%s)", res);
371  return false;
372  }
373 
374  return true;
375 }
376 
377 bool DeepSkyDadAF3::readHoldCurrentMultiplier()
378 {
379  char res[DSD_RES] = {0};
380 
381  if (sendCommand("[GMHM]", res) == false)
382  return false;
383 
384  uint32_t hcm = 0;
385  int rc = sscanf(res, "(%d)", &hcm);
386  if (rc > 0)
387  {
388  HoldCurrentMultiplierN[0].value = hcm;
389  HoldCurrentMultiplierNP.s = IPS_OK;
390  }
391  else
392  {
393  LOGF_ERROR("Unknown error: hold current multiplier value (%s)", res);
394  return false;
395  }
396 
397  return true;
398 }
399 
400 bool DeepSkyDadAF3::readTemperature()
401 {
402  char res[DSD_RES] = {0};
403 
404  if (sendCommand("[GTMC]", res) == false)
405  return false;
406 
407  double temp = 0;
408  int rc = sscanf(res, "(%lf)", &temp);
409  if (rc > 0)
410  {
411  TemperatureN[0].value = temp;
412  }
413  else
414  {
415  LOGF_ERROR("Unknown error: focuser temperature value (%s)", res);
416  return false;
417  }
418 
419  return true;
420 }
421 
422 bool DeepSkyDadAF3::isMoving()
423 {
424  char res[DSD_RES] = {0};
425 
426  if (sendCommand("[GMOV]", res) == false)
427  return false;
428 
429  if (strcmp(res, "(1)") == 0)
430  return true;
431  else if (strcmp(res, "(0)") == 0)
432  return false;
433 
434  LOGF_ERROR("Unknown error: isMoving value (%s)", res);
435  return false;
436 }
437 
438 bool DeepSkyDadAF3::SyncFocuser(uint32_t ticks)
439 {
440  char cmd[DSD_RES] = {0};
441  snprintf(cmd, DSD_RES, "[SPOS%07d]", ticks);
442  char res[DSD_RES] = {0};
443  return sendCommand(cmd, res);
444 }
445 
447 {
448  char cmd[DSD_RES] = {0};
449  snprintf(cmd, DSD_RES, "[SREV%01d]", enabled ? 1 : 0);
450  char res[DSD_RES] = {0};
451  return sendCommand(cmd, res);
452 }
453 
454 bool DeepSkyDadAF3::MoveFocuser(uint32_t position)
455 {
456  char cmd[DSD_RES] = {0};
457  char res[DSD_RES] = {0};
458  snprintf(cmd, DSD_RES, "[STRG%06d]", position);
459  // Set Position First
460  if (sendCommand(cmd, res) == false)
461  return false;
462 
463  if(strcmp(res, "!101)") == 0)
464  {
465  LOG_ERROR("MoveFocuserFailed - requested movement too big. You can increase the limit by changing the value of Max. movement.");
466  return false;
467  }
468 
469  // Now start motion toward position
470  if (sendCommand("[SMOV]", res) == false)
471  return false;
472 
473  return true;
474 }
475 
476 bool DeepSkyDadAF3::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
477 {
478  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
479  {
480  // Focus Step Mode
481  if (strcmp(StepModeSP.name, name) == 0)
482  {
483  int current_mode = IUFindOnSwitchIndex(&StepModeSP);
484 
485  IUUpdateSwitch(&StepModeSP, states, names, n);
486 
487  int target_mode = IUFindOnSwitchIndex(&StepModeSP);
488 
489  if (current_mode == target_mode)
490  {
491  StepModeSP.s = IPS_OK;
492  IDSetSwitch(&StepModeSP, nullptr);
493  return true;
494  }
495 
496  char cmd[DSD_RES] = {0};
497 
498  if(target_mode == 0)
499  target_mode = 1;
500  else if(target_mode == 1)
501  target_mode = 2;
502  else if(target_mode == 2)
503  target_mode = 4;
504  else if(target_mode == 3)
505  target_mode = 8;
506  else if(target_mode == 4)
507  target_mode = 16;
508  else if(target_mode == 5)
509  target_mode = 32;
510  else if(target_mode == 6)
511  target_mode = 64;
512  else if(target_mode == 7)
513  target_mode = 128;
514  else if(target_mode == 8)
515  target_mode = 256;
516 
517  snprintf(cmd, DSD_RES, "[SSTP%d]", target_mode);
518  bool rc = sendCommandSet(cmd);
519  if (!rc)
520  {
521  IUResetSwitch(&StepModeSP);
522  StepModeS[current_mode].s = ISS_ON;
523  StepModeSP.s = IPS_ALERT;
524  IDSetSwitch(&StepModeSP, nullptr);
525  return false;
526  }
527 
528  StepModeSP.s = IPS_OK;
529  IDSetSwitch(&StepModeSP, nullptr);
530  return true;
531  }
532 
533  // Focus Speed Mode
534  if (strcmp(SpeedModeSP.name, name) == 0)
535  {
536  int current_mode = IUFindOnSwitchIndex(&SpeedModeSP);
537 
538  IUUpdateSwitch(&SpeedModeSP, states, names, n);
539 
540  int target_mode = IUFindOnSwitchIndex(&SpeedModeSP);
541 
542  if (current_mode == target_mode)
543  {
544  SpeedModeSP.s = IPS_OK;
545  IDSetSwitch(&SpeedModeSP, nullptr);
546  return true;
547  }
548 
549  char cmd[DSD_RES] = {0};
550 
551  if(target_mode == 0)
552  target_mode = 1;
553  else if(target_mode == 1)
554  target_mode = 2;
555  else if(target_mode == 2)
556  target_mode = 3;
557  else if(target_mode == 3)
558  target_mode = 4;
559  else if(target_mode == 4)
560  target_mode = 5;
561 
562  snprintf(cmd, DSD_RES, "[SSPD%d]", target_mode);
563  bool rc = sendCommandSet(cmd);
564  if (!rc)
565  {
566  IUResetSwitch(&SpeedModeSP);
567  SpeedModeS[current_mode].s = ISS_ON;
568  SpeedModeSP.s = IPS_ALERT;
569  IDSetSwitch(&SpeedModeSP, nullptr);
570  return false;
571  }
572 
573  SpeedModeSP.s = IPS_OK;
574  IDSetSwitch(&SpeedModeSP, nullptr);
575  return true;
576  }
577  }
578 
579  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
580 }
581 
582 bool DeepSkyDadAF3::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
583 {
584  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
585  {
586  // Settle buffer Settings
587  if (strcmp(name, SettleBufferNP.name) == 0)
588  {
589  IUUpdateNumber(&SettleBufferNP, values, names, n);
590  char cmd[DSD_RES] = {0};
591  snprintf(cmd, DSD_RES, "[SBUF%06d]", static_cast<int>(SettleBufferN[0].value));
592  bool rc = sendCommandSet(cmd);
593  if (!rc)
594  {
595  SettleBufferNP.s = IPS_ALERT;
596  return false;
597  }
598 
599  SettleBufferNP.s = IPS_OK;
600  IDSetNumber(&SettleBufferNP, nullptr);
601  return true;
602  }
603 
604  // Move current multiplier
605  if (strcmp(name, MoveCurrentMultiplierNP.name) == 0)
606  {
607  IUUpdateNumber(&MoveCurrentMultiplierNP, values, names, n);
608  char cmd[DSD_RES] = {0};
609  snprintf(cmd, DSD_RES, "[SMMM%03d]", static_cast<int>(MoveCurrentMultiplierN[0].value));
610  bool rc = sendCommandSet(cmd);
611  if (!rc)
612  {
613  MoveCurrentMultiplierNP.s = IPS_ALERT;
614  return false;
615  }
616 
617  MoveCurrentMultiplierNP.s = IPS_OK;
618  IDSetNumber(&MoveCurrentMultiplierNP, nullptr);
619  return true;
620  }
621 
622  // Hold current multiplier
623  if (strcmp(name, HoldCurrentMultiplierNP.name) == 0)
624  {
625  IUUpdateNumber(&HoldCurrentMultiplierNP, values, names, n);
626  char cmd[DSD_RES] = {0};
627  snprintf(cmd, DSD_RES, "[SMHM%03d]", static_cast<int>(HoldCurrentMultiplierN[0].value));
628  bool rc = sendCommandSet(cmd);
629  if (!rc)
630  {
631  HoldCurrentMultiplierNP.s = IPS_ALERT;
632  return false;
633  }
634 
635  HoldCurrentMultiplierNP.s = IPS_OK;
636  IDSetNumber(&HoldCurrentMultiplierNP, nullptr);
637  return true;
638  }
639 
640  // Max. position
641  if (strcmp(name, FocusMaxPosNP.name) == 0)
642  {
643  IUUpdateNumber(&FocusMaxPosNP, values, names, n);
644  char cmd[DSD_RES] = {0};
645  snprintf(cmd, DSD_RES, "[SMXP%d]", static_cast<int>(FocusMaxPosN[0].value));
646  bool rc = sendCommandSet(cmd);
647  if (!rc)
648  {
650  return false;
651  }
652 
654  IDSetNumber(&FocusMaxPosNP, nullptr);
655  return true;
656  }
657 
658  // Max. movement
659  // if (strcmp(name, FocusMaxMoveNP.name) == 0)
660  // {
661  // IUUpdateNumber(&FocusMaxMoveNP, values, names, n);
662  // char cmd[DSD_RES] = {0};
663  // snprintf(cmd, DSD_RES, "[SMXM%d]", static_cast<int>(FocusMaxMoveN[0].value));
664  // bool rc = sendCommandSet(cmd);
665  // if (!rc)
666  // {
667  // FocusMaxMoveNP.s = IPS_ALERT;
668  // return false;
669  // }
670 
671  // FocusMaxMoveNP.s = IPS_OK;
672  // IDSetNumber(&FocusMaxMoveNP, nullptr);
673  // return true;
674  // }
675 
676  }
677 
678  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
679 }
680 
681 void DeepSkyDadAF3::GetFocusParams()
682 {
683  IUResetSwitch(&StepModeSP);
684  IUResetSwitch(&SpeedModeSP);
685 
686  if (readPosition())
687  IDSetNumber(&FocusAbsPosNP, nullptr);
688 
689  if (readStepMode())
690  IDSetSwitch(&StepModeSP, nullptr);
691 
692  if (readSpeedMode())
693  IDSetSwitch(&SpeedModeSP, nullptr);
694 
695  if (readSettleBuffer())
696  IDSetNumber(&SettleBufferNP, nullptr);
697 
698  if (readMoveCurrentMultiplier())
699  IDSetNumber(&MoveCurrentMultiplierNP, nullptr);
700 
701  if (readHoldCurrentMultiplier())
702  IDSetNumber(&HoldCurrentMultiplierNP, nullptr);
703 
704  if (readMaxPosition())
705  IDSetNumber(&FocusMaxPosNP, nullptr);
706 
707  if (readMaxMovement())
708  IDSetNumber(&FocusMaxMoveNP, nullptr);
709 
710  if (readTemperature())
711  IDSetNumber(&TemperatureNP, nullptr);
712 }
713 
714 IPState DeepSkyDadAF3::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
715 {
716  INDI_UNUSED(speed);
717  // either go all the way in or all the way out
718  // then use timer to stop
719  if (dir == FOCUS_INWARD)
720  MoveFocuser(0);
721  else
722  MoveFocuser(FocusMaxPosN[0].value);
723 
724  IEAddTimer(duration, &DeepSkyDadAF3::timedMoveHelper, this);
725  return IPS_BUSY;
726 }
727 
728 void DeepSkyDadAF3::timedMoveHelper(void * context)
729 {
730  static_cast<DeepSkyDadAF3*>(context)->timedMoveCallback();
731 }
732 
733 void DeepSkyDadAF3::timedMoveCallback()
734 {
735  AbortFocuser();
739  FocusTimerN[0].value = 0;
740  IDSetNumber(&FocusAbsPosNP, nullptr);
741  IDSetNumber(&FocusRelPosNP, nullptr);
742  IDSetNumber(&FocusTimerNP, nullptr);
743 }
744 
745 
747 {
748  targetPos = targetTicks;
749 
750  double bcValue = FocusBacklashN[0].value;
751  int diff = targetTicks - FocusAbsPosN[0].value;
752  if ((diff > 0 && bcValue < 0) || (diff < 0 && bcValue > 0))
753  {
754  backlashComp = bcValue;
755  targetPos -= bcValue;
756  }
757 
758  if (!MoveFocuser(targetPos))
759  return IPS_ALERT;
760 
761  return IPS_BUSY;
762 }
763 
765 {
766  int32_t newPosition = 0;
767 
768  if (dir == FOCUS_INWARD)
769  newPosition = FocusAbsPosN[0].value - ticks;
770  else
771  newPosition = FocusAbsPosN[0].value + ticks;
772 
773  // Clamp
774  newPosition = std::max(0, std::min(static_cast<int32_t>(FocusAbsPosN[0].max), newPosition));
775  if (!MoveAbsFocuser(newPosition))
776  return IPS_ALERT;
777 
778  // JM 2019-02-10: This is already set by the framework
779  //FocusRelPosN[0].value = ticks;
780  //FocusRelPosNP.s = IPS_BUSY;
781 
782  return IPS_BUSY;
783 }
784 
786 {
787  if (!isConnected())
788  {
790  return;
791  }
792 
793  bool rc = readPosition();
794  if (rc)
795  {
796  if (std::abs(lastPos - FocusAbsPosN[0].value) > 5)
797  {
798  IDSetNumber(&FocusAbsPosNP, nullptr);
799  lastPos = FocusAbsPosN[0].value;
800  }
801  }
802 
804  {
805  if (!isMoving())
806  {
807  if( backlashComp == 0 ) {
810  }
811  IDSetNumber(&FocusAbsPosNP, nullptr);
812  IDSetNumber(&FocusRelPosNP, nullptr);
813  lastPos = FocusAbsPosN[0].value;
814 
815  if(moveAborted)
816  {
817  LOG_INFO("Move aborted.");
818  }
819  else if(backlashComp != 0)
820  {
821  LOGF_INFO("Performing backlash compensation of %i.", (int)backlashComp);
822  targetPos += backlashComp;
823  MoveFocuser(targetPos);
824  }
825  else
826  {
827  LOG_INFO("Focuser reached requested position.");
828  }
829 
830  moveAborted = false;
831  backlashComp = 0;
832  }
833  }
834 
835  rc = readTemperature();
836  if (rc)
837  {
838  //more accurate update
839  if (std::abs(lastTemperature - TemperatureN[0].value) >= 0.1)
840  {
841  IDSetNumber(&TemperatureNP, nullptr);
842  lastTemperature = TemperatureN[0].value;
843  }
844  }
845 
847 }
848 
850 {
851  moveAborted = true;
852  return sendCommand("[STOP]");
853 }
854 
856 {
857  Focuser::saveConfigItems(fp);
858 
859  IUSaveConfigSwitch(fp, &StepModeSP);
860  IUSaveConfigSwitch(fp, &SpeedModeSP);
861  IUSaveConfigNumber(fp, &FocusMaxMoveNP);
862  IUSaveConfigNumber(fp, &SettleBufferNP);
863  IUSaveConfigNumber(fp, &MoveCurrentMultiplierNP);
864  IUSaveConfigNumber(fp, &HoldCurrentMultiplierNP);
865 
866  return true;
867 }
868 
869 bool DeepSkyDadAF3::sendCommand(const char * cmd, char * res)
870 {
871  int nbytes_written = 0, nbytes_read = 0, rc = -1;
872 
873  tcflush(PortFD, TCIOFLUSH);
874 
875  LOGF_DEBUG("CMD <%s>", cmd);
876 
877  if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
878  {
879  char errstr[MAXRBUF] = {0};
880  tty_error_msg(rc, errstr, MAXRBUF);
881  LOGF_ERROR("Serial write error: %s.", errstr);
882  return false;
883  }
884 
885  if (res == nullptr)
886  return true;
887 
888  if ((rc = tty_nread_section(PortFD, res, DSD_RES, DSD_DEL, DSD_TIMEOUT, &nbytes_read)) != TTY_OK)
889  {
890  char errstr[MAXRBUF] = {0};
891  tty_error_msg(rc, errstr, MAXRBUF);
892  LOGF_ERROR("Serial read error: %s.", errstr);
893  return false;
894  }
895 
896  LOGF_DEBUG("RES <%s>", res);
897 
898  tcflush(PortFD, TCIOFLUSH);
899 
900  return true;
901 }
902 
903 bool DeepSkyDadAF3::sendCommandSet(const char * cmd)
904 {
905  char res[DSD_RES] = {0};
906 
907  if (sendCommand(cmd, res) == false)
908  return false;
909 
910  return strcmp(res, "(OK)") == 0;
911 }
912 
914 {
915  INDI_UNUSED(steps);
916  return true;
917 }
918 
920 {
921  char cmd[DSD_RES] = {0};
922 
923  snprintf(cmd, DSD_RES, "[SMXP%d]", ticks);
924 
925  if (sendCommandSet(cmd))
926  {
927  SyncPresets(ticks);
928  return true;
929  }
930 
931  return false;
932 }
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 updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
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.
virtual bool Handshake() override
perform handshake with device to check communication
static void timedMoveHelper(void *context)
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 bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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.
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.
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.
virtual void SyncPresets(uint32_t ticks)
syncPresets Updates the min/max/step range of the preset as per the maximum name of Absolute Focus Tr...
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_INFO(fmt,...)
Definition: indilogger.h:82
#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