Instrument Neutral Distributed Interface INDI  2.0.2
steeldrive.cpp
Go to the documentation of this file.
1 /*
2  Baader Steeldrive Focuser
3  Copyright (C) 2014 Jasem Mutlaq (mutlaqja@ikarustech.com)
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "steeldrive.h"
22 
23 #include "indicom.h"
24 
25 #include <cmath>
26 #include <memory>
27 #include <cstring>
28 #include <termios.h>
29 #include <unistd.h>
30 
31 #define STEELDRIVE_MAX_RETRIES 1
32 #define STEELDRIVE_TIMEOUT 1
33 #define STEELDRIVE_MAXBUF 16
34 #define STEELDRIVE_CMD 9
35 #define STEELDRIVE_CMD_LONG 11
36 #define STEELDRIVE_TEMPERATURE_FREQ 20 /* Update every 20 POLLMS cycles. For POLLMS 500ms = 10 seconds freq */
37 #define STEELDIVE_POSITION_THRESHOLD 5 /* Only send position updates to client if the diff exceeds 5 steps */
38 
39 #define FOCUS_SETTINGS_TAB "Settings"
40 
41 std::unique_ptr<SteelDrive> steelDrive(new SteelDrive());
42 
44 {
45  // Can move in Absolute & Relative motions, can AbortFocuser motion, and has variable speed.
47 }
48 
50 {
52 
53  FocusSpeedN[0].min = 350;
54  FocusSpeedN[0].max = 1000;
55  FocusSpeedN[0].value = 500;
56  FocusSpeedN[0].step = 50;
57 
58  // Focuser temperature
59  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
60  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
62 
63  // Temperature Settings
64  IUFillNumber(&TemperatureSettingN[FOCUS_T_COEFF], "Coefficient", "", "%.3f", 0, 0.999, 0.1, 0.1);
65  IUFillNumber(&TemperatureSettingN[FOCUS_T_SAMPLES], "# of Samples", "", "%3.0f", 16, 128, 16, 16);
66  IUFillNumberVector(&TemperatureSettingNP, TemperatureSettingN, 2, getDeviceName(), "Temperature Settings", "",
68 
69  // Compensate for temperature
70  IUFillSwitch(&TemperatureCompensateS[0], "Enable", "", ISS_OFF);
71  IUFillSwitch(&TemperatureCompensateS[1], "Disable", "", ISS_ON);
72  IUFillSwitchVector(&TemperatureCompensateSP, TemperatureCompensateS, 2, getDeviceName(), "Temperature Compensate",
74 
75  // Focuser Models
76  IUFillSwitch(&ModelS[0], "NT2", "", ISS_OFF);
77  fSettings[0].maxTrip = 30;
78  fSettings[0].gearRatio = 0.25040;
79  IUFillSwitch(&ModelS[1], "SC2", "", ISS_OFF);
80  fSettings[1].maxTrip = 30;
81  fSettings[1].gearRatio = 0.25040;
82  IUFillSwitch(&ModelS[2], "RT2", "", ISS_OFF);
83  fSettings[2].maxTrip = 80;
84  fSettings[2].gearRatio = 0.25040;
85  IUFillSwitch(&ModelS[3], "RT3", "", ISS_OFF);
86  fSettings[3].maxTrip = 115;
87  fSettings[3].gearRatio = 0.25040;
88  IUFillSwitch(&ModelS[4], "Custom", "", ISS_ON);
89  fSettings[4].maxTrip = 30;
90  fSettings[4].gearRatio = 0.25040;
91  IUFillSwitchVector(&ModelSP, ModelS, 5, getDeviceName(), "Model", "", FOCUS_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0,
92  IPS_IDLE);
93 
94  // Custom Settings
95  IUFillNumber(&CustomSettingN[FOCUS_MAX_TRIP], "Max Trip (mm)", "", "%6.2f", 20, 150, 0, 30);
96  IUFillNumber(&CustomSettingN[FOCUS_GEAR_RATIO], "Gear Ratio", "", "%.5f", 0.1, 1, 0, .25040);
97  IUFillNumberVector(&CustomSettingNP, CustomSettingN, 2, getDeviceName(), "Custom Settings", "", FOCUS_SETTINGS_TAB,
98  IP_RW, 0, IPS_IDLE);
99 
100  // Focuser Accleration
101  IUFillNumber(&AccelerationN[0], "Ramp", "", "%3.0f", 1500., 3000., 100., 2000.);
102  IUFillNumberVector(&AccelerationNP, AccelerationN, 1, getDeviceName(), "FOCUS_ACCELERATION", "Acceleration",
104 
105  // Focus Sync
106  IUFillNumber(&SyncN[0], "Position (steps)", "", "%3.0f", 0., 200000., 100., 0.);
107  IUFillNumberVector(&SyncNP, SyncN, 1, getDeviceName(), "FOCUS_SYNC", "Sync", MAIN_CONTROL_TAB, IP_RW, 0, IPS_IDLE);
108 
109  // Version
110  IUFillText(&VersionT[0], "HW Rev.", "", nullptr);
111  IUFillText(&VersionT[1], "FW Rev.", "", nullptr);
112  IUFillTextVector(&VersionTP, VersionT, 2, getDeviceName(), "FOCUS_VERSION", "Version", MAIN_CONTROL_TAB, IP_RO, 0,
113  IPS_IDLE);
114 
115  FocusRelPosN[0].value = 0;
116  FocusAbsPosN[0].value = 0;
117  simPosition = FocusAbsPosN[0].value;
118 
119  updateFocusMaxRange(fSettings[4].maxTrip, fSettings[4].gearRatio);
120 
121  addAuxControls();
122 
124 
125  return true;
126 }
127 
129 {
131 
132  if (isConnected())
133  {
134  defineProperty(&TemperatureNP);
135  defineProperty(&TemperatureSettingNP);
136  defineProperty(&TemperatureCompensateSP);
137 
138  defineProperty(&ModelSP);
139  defineProperty(&CustomSettingNP);
140  defineProperty(&AccelerationNP);
141  defineProperty(&SyncNP);
142 
143  defineProperty(&VersionTP);
144 
145  GetFocusParams();
146 
147  //loadConfig(true);
148 
149  LOG_INFO("SteelDrive parameters updated, focuser ready for use.");
150  }
151  else
152  {
153  deleteProperty(TemperatureNP.name);
154  deleteProperty(TemperatureSettingNP.name);
155  deleteProperty(TemperatureCompensateSP.name);
156 
157  deleteProperty(ModelSP.name);
158  deleteProperty(CustomSettingNP.name);
159  deleteProperty(AccelerationNP.name);
160  deleteProperty(SyncNP.name);
161 
162  deleteProperty(VersionTP.name);
163  }
164 
165  return true;
166 }
167 
169 {
170  if (isSimulation())
171  return true;
172 
173  if (Ack())
174  {
175  LOG_INFO("SteelDrive is online. Getting focus parameters...");
176  temperatureUpdateCounter = 0;
177  return true;
178  }
179 
180  LOG_INFO("Error retrieving data from SteelDrive, please ensure SteelDrive controller is "
181  "powered and the port is correct.");
182  return false;
183 }
184 
186 {
187  return "Baader SteelDrive";
188 }
189 
190 /************************************************************************************
191  *
192 * ***********************************************************************************/
193 bool SteelDrive::Ack()
194 {
195  int nbytes_written = 0, nbytes_read = 0, rc = -1;
196  char errstr[MAXRBUF] = {0};
197  char resp[STEELDRIVE_MAXBUF] = {0};
198  char hwVer[STEELDRIVE_MAXBUF];
199 
200  tcflush(PortFD, TCIOFLUSH);
201 
202  if (!sim && (rc = tty_write(PortFD, ":FVERSIO#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
203  {
204  tty_error_msg(rc, errstr, MAXRBUF);
205  LOGF_ERROR(":FVERSIO# getHWVersion error: %s.", errstr);
206  return false;
207  }
208 
209  LOG_DEBUG("CMD (:FVERSIO#)");
210 
211  if (sim)
212  {
213  strncpy(resp, ":FV2.00812#", STEELDRIVE_CMD_LONG + 1);
214  nbytes_read = STEELDRIVE_CMD_LONG;
215  }
216  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
217  {
218  tty_error_msg(rc, errstr, MAXRBUF);
219  LOGF_ERROR("getHWVersion error: %s.", errstr);
220  return false;
221  }
222 
223  resp[nbytes_read] = '\0';
224 
225  LOGF_DEBUG("RES (%s)", resp);
226 
227  rc = sscanf(resp, ":FV%s#", hwVer);
228 
229  return rc > 0;
230 }
231 
232 /************************************************************************************
233  *
234 * ***********************************************************************************/
235 bool SteelDrive::updateVersion()
236 {
237  int nbytes_written = 0, nbytes_read = 0, rc = -1;
238  char errstr[MAXRBUF] = {0};
239  char resp[STEELDRIVE_MAXBUF] = {0};
240  char hardware_string[MAXRBUF];
241  char firmware_string[MAXRBUF];
242 
243  char hwdate[STEELDRIVE_MAXBUF / 2];
244  char hwrev[STEELDRIVE_MAXBUF / 2];
245  char fwdate[STEELDRIVE_MAXBUF / 2];
246  char fwrev[STEELDRIVE_MAXBUF / 2];
247 
248  memset(hwdate, 0, sizeof(hwdate));
249  memset(hwrev, 0, sizeof(hwrev));
250  memset(fwdate, 0, sizeof(fwdate));
251  memset(fwrev, 0, sizeof(fwrev));
252 
253  tcflush(PortFD, TCIOFLUSH);
254 
255  if (!sim && (rc = tty_write(PortFD, ":FVERSIO#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
256  {
257  tty_error_msg(rc, errstr, MAXRBUF);
258  LOGF_ERROR(":FVERSIO# getHWVersion write error: %s.", errstr);
259  return false;
260  }
261 
262  LOG_DEBUG("CMD (:FVERSIO#)");
263 
264  if (sim)
265  {
266  strncpy(resp, ":FV2.00812#", STEELDRIVE_CMD_LONG + 1);
267  nbytes_read = STEELDRIVE_CMD_LONG;
268  }
269  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
270  {
271  tty_error_msg(rc, errstr, MAXRBUF);
272  LOGF_ERROR("FVERSIO# getHWVersion read error: %s.", errstr);
273  return false;
274  }
275 
276  resp[nbytes_read] = '\0';
277 
278  LOGF_DEBUG("RES (%s)", resp);
279 
280  rc = sscanf(resp, ":FV%s#", hardware_string);
281 
282  if (rc > 0)
283  {
284  strncpy(hwrev, hardware_string, 3);
285  strncpy(hwdate, hardware_string + 3, 4);
286  char mon[3], year[3];
287  memset(mon, 0, sizeof(mon));
288  memset(year, 0, sizeof(year));
289  strncpy(mon, hwdate, 2);
290  strncpy(year, hwdate + 2, 2);
291  snprintf(hardware_string, MAXRBUF, "Version: %s Date: %s.%s", hwrev, mon, year);
292  IUSaveText(&VersionT[0], hardware_string);
293  }
294  else
295  {
296  LOGF_ERROR("Unknown error: getHWVersion value (%s)", resp);
297  return false;
298  }
299 
300  tcflush(PortFD, TCIOFLUSH);
301 
302  if (!sim && (rc = tty_write(PortFD, ":FNFIRMW#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
303  {
304  tty_error_msg(rc, errstr, MAXRBUF);
305  LOGF_ERROR(":FNFIRMW# getSWVersion write error: %s.", errstr);
306  return false;
307  }
308 
309  LOG_DEBUG("CMD (:FNFIRMW#)");
310 
311  if (sim)
312  {
313  strncpy(resp, ":FN2.21012#", STEELDRIVE_CMD_LONG + 1);
314  nbytes_read = STEELDRIVE_CMD_LONG;
315  }
316  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
317  {
318  tty_error_msg(rc, errstr, MAXRBUF);
319  LOGF_ERROR("FNFIRMW# getSWVersion read error: %s.", errstr);
320  return false;
321  }
322 
323  resp[nbytes_read] = '\0';
324 
325  LOGF_DEBUG("RES (%s)", resp);
326 
327  rc = sscanf(resp, ":FN%s#", firmware_string);
328 
329  if (rc > 0)
330  {
331  strncpy(fwrev, firmware_string, 3);
332  strncpy(fwdate, firmware_string + 3, 4);
333  char mon[3], year[3];
334  memset(mon, 0, sizeof(mon));
335  memset(year, 0, sizeof(year));
336  strncpy(mon, fwdate, 2);
337  strncpy(year, fwdate + 2, 2);
338  snprintf(firmware_string, MAXRBUF, "Version: %s Date: %s.%s", fwrev, mon, year);
339  IUSaveText(&VersionT[1], firmware_string);
340  }
341  else
342  {
343  LOGF_ERROR("Unknown error: getSWVersion value (%s)", resp);
344  return false;
345  }
346 
347  return true;
348 }
349 
350 /************************************************************************************
351  *
352 * ***********************************************************************************/
353 bool SteelDrive::updateTemperature()
354 {
355  int nbytes_written = 0, nbytes_read = 0, rc = -1;
356  char errstr[MAXRBUF] = {0};
357  char resp[STEELDRIVE_MAXBUF] = {0};
358  int temperature;
359 
360  tcflush(PortFD, TCIOFLUSH);
361 
362  if (!sim && (rc = tty_write(PortFD, ":F5ASKT0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
363  {
364  tty_error_msg(rc, errstr, MAXRBUF);
365  LOGF_ERROR(":F5ASKT0# updateTemperature write error: %s.", errstr);
366  return false;
367  }
368 
369  LOG_DEBUG("CMD (:F5ASKT0#)");
370 
371  if (sim)
372  {
373  strncpy(resp, ":F5+1810#", STEELDRIVE_CMD + 1);
374  nbytes_read = STEELDRIVE_CMD;
375  }
376  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
377  {
378  tty_error_msg(rc, errstr, MAXRBUF);
379  LOGF_ERROR(":F5ASKT0# updateTemperature read error: %s.", errstr);
380  return false;
381  }
382 
383  resp[nbytes_read] = '\0';
384 
385  LOGF_DEBUG("RES (%s)", resp);
386 
387  rc = sscanf(resp, ":F5%d#", &temperature);
388 
389  if (rc > 0)
390  {
391  TemperatureN[0].value = ((double)temperature) / 100.0;
392  TemperatureNP.s = IPS_OK;
393  }
394  else
395  {
396  char junk[STEELDRIVE_MAXBUF];
397  rc = sscanf(resp, ":F5%s#", junk);
398  if (rc > 0)
399  {
400  TemperatureN[0].value = 0;
401  LOG_DEBUG("Temperature probe is not connected.");
402  }
403  else
404  LOGF_ERROR("Unknown error: focuser temperature value (%s)", resp);
405 
406  TemperatureNP.s = IPS_ALERT;
407  return false;
408  }
409 
410  return true;
411 }
412 
413 /************************************************************************************
414  *
415 * ***********************************************************************************/
416 bool SteelDrive::updatePosition()
417 {
418  int nbytes_written = 0, nbytes_read = 0, rc = -1;
419  char errstr[MAXRBUF] = {0};
420  char resp[STEELDRIVE_MAXBUF] = {0};
421  unsigned short pos = 0;
422  int retries = 0;
423 
424  for (retries = 0; retries < STEELDRIVE_MAX_RETRIES; retries++)
425  {
426  tcflush(PortFD, TCIOFLUSH);
427 
428  if (!sim && (rc = tty_write(PortFD, ":F8ASKS0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
429  {
430  tty_error_msg(rc, errstr, MAXRBUF);
431  LOGF_ERROR(":F8ASKS0# updatePosition write error: %s.", errstr);
432  return false;
433  }
434 
435  LOG_DEBUG("CMD (:F8ASKS0#)");
436 
437  if (sim)
438  {
439  snprintf(resp, STEELDRIVE_CMD_LONG, ":F8%07u#", (int)simPosition);
440  nbytes_read = STEELDRIVE_CMD_LONG;
441  break;
442  }
443  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT - retries, &nbytes_read)) != TTY_OK)
444  {
445  tty_error_msg(rc, errstr, MAXRBUF);
446  resp[nbytes_read] = '\0';
447  LOGF_DEBUG(":F8ASKS0# updatePosition read error: %s. Retry: %d. Bytes: %d. Buffer (%s)", errstr, retries,
448  nbytes_read, resp);
449  }
450  else
451  break;
452  }
453 
454  if (retries == STEELDRIVE_MAX_RETRIES)
455  {
456  LOG_ERROR("UpdatePosition: failed to read.");
457  return false;
458  }
459 
460  resp[nbytes_read] = '\0';
461 
462  LOGF_DEBUG("RES (%s)", resp);
463 
464  rc = sscanf(resp, ":F8%hu#", &pos);
465 
466  if (rc > 0)
467  {
468  FocusAbsPosN[0].value = pos;
469  }
470  else
471  {
472  LOGF_ERROR("Unknown error: focuser position value (%s)", resp);
473  return false;
474  }
475 
476  return true;
477 }
478 
479 /************************************************************************************
480  *
481 * ***********************************************************************************/
482 bool SteelDrive::updateSpeed()
483 {
484  int nbytes_written = 0, nbytes_read = 0, rc = -1;
485  char errstr[MAXRBUF] = {0};
486  char resp[STEELDRIVE_MAXBUF] = {0};
487  unsigned short speed;
488 
489  tcflush(PortFD, TCIOFLUSH);
490 
491  if (!sim && (rc = tty_write(PortFD, ":FGSPMAX#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
492  {
493  tty_error_msg(rc, errstr, MAXRBUF);
494  LOGF_ERROR(":FGSPMAX# updateSpeed write error: %s.", errstr);
495  return false;
496  }
497 
498  LOG_DEBUG("CMD (:FGSPMAX#)");
499 
500  if (sim)
501  {
502  strncpy(resp, ":FG00350#", STEELDRIVE_CMD + 1);
503  nbytes_read = STEELDRIVE_CMD;
504  }
505  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
506  {
507  tty_error_msg(rc, errstr, MAXRBUF);
508  LOGF_ERROR(":FGSPMAX# updateSpeed read error: %s.", errstr);
509  return false;
510  }
511 
512  resp[nbytes_read] = '\0';
513 
514  LOGF_DEBUG("RES (%s)", resp);
515 
516  rc = sscanf(resp, ":FG%hu#", &speed);
517 
518  if (rc > 0)
519  {
520  FocusSpeedN[0].value = speed;
521  }
522  else
523  {
524  LOGF_ERROR("Unknown error: focuser speed value (%s)", resp);
525  return false;
526  }
527 
528  return true;
529 }
530 
531 /************************************************************************************
532  *
533 * ***********************************************************************************/
534 bool SteelDrive::updateAcceleration()
535 {
536  int nbytes_written = 0, nbytes_read = 0, rc = -1;
537  char errstr[MAXRBUF] = {0};
538  char resp[STEELDRIVE_MAXBUF] = {0};
539  unsigned short accel;
540 
541  tcflush(PortFD, TCIOFLUSH);
542 
543  if (!sim && (rc = tty_write(PortFD, ":FHSPMIN#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
544  {
545  tty_error_msg(rc, errstr, MAXRBUF);
546  LOGF_ERROR(":FHSPMIN# updateAcceleration write error: %s.", errstr);
547  return false;
548  }
549 
550  LOG_DEBUG("CMD (:FHSPMIN#)");
551 
552  if (sim)
553  {
554  strncpy(resp, ":FH01800#", STEELDRIVE_CMD + 1);
555  nbytes_read = STEELDRIVE_CMD;
556  }
557  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
558  {
559  tty_error_msg(rc, errstr, MAXRBUF);
560  LOGF_ERROR(":FHSPMIN# updateAcceleration read error: %s.", errstr);
561  return false;
562  }
563 
564  resp[nbytes_read] = '\0';
565 
566  LOGF_DEBUG("RES (%s)", resp);
567 
568  rc = sscanf(resp, ":FH%hu#", &accel);
569 
570  if (rc > 0)
571  {
572  AccelerationN[0].value = accel;
573  }
574  else
575  {
576  LOGF_ERROR("Unknown error: updateAcceleration value (%s)", resp);
577  return false;
578  }
579  return true;
580 }
581 
582 /************************************************************************************
583  *
584 * ***********************************************************************************/
585 bool SteelDrive::updateTemperatureSettings()
586 {
587  int nbytes_written = 0, nbytes_read = 0, rc = -1;
588  char errstr[MAXRBUF] = {0};
589  char resp[STEELDRIVE_MAXBUF] = {0};
590 
591  char selectedFocuser[1], coeff[3], enabled[1], tResp[STEELDRIVE_MAXBUF];
592 
593  tcflush(PortFD, TCIOFLUSH);
594 
595  if (!sim && (rc = tty_write(PortFD, ":F7ASKC0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
596  {
597  tty_error_msg(rc, errstr, MAXRBUF);
598  LOGF_ERROR(":F7ASKC0# updateTemperatureSettings write error: %s.", errstr);
599  return false;
600  }
601 
602  LOG_DEBUG("CMD (:F7ASKC0#)");
603 
604  if (sim)
605  {
606  strncpy(resp, ":F710004#", STEELDRIVE_CMD + 1);
607  nbytes_read = STEELDRIVE_CMD;
608  }
609  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
610  {
611  tty_error_msg(rc, errstr, MAXRBUF);
612  LOGF_ERROR(":F7ASKC0# updateTemperatureSettings read error: %s.", errstr);
613  return false;
614  }
615 
616  resp[nbytes_read] = '\0';
617 
618  LOGF_DEBUG("RES (%s)", resp);
619 
620  rc = sscanf(resp, ":F7%s#", tResp);
621 
622  if (rc > 0)
623  {
624  strncpy(coeff, tResp, 3);
625  strncpy(enabled, tResp + 3, 1);
626  strncpy(selectedFocuser, tResp + 4, 1);
627 
628  TemperatureSettingN[FOCUS_T_COEFF].value = atof(coeff) / 1000.0;
629 
630  IUResetSwitch(&TemperatureCompensateSP);
631  if (enabled[0] == '0')
632  TemperatureCompensateS[1].s = ISS_ON;
633  else
634  TemperatureCompensateS[0].s = ISS_ON;
635  }
636  else
637  {
638  LOGF_ERROR("Unknown error: updateTemperatureSettings value (%s)", resp);
639  return false;
640  }
641 
642  return true;
643 }
644 
645 /************************************************************************************
646  *
647 * ***********************************************************************************/
648 bool SteelDrive::updateCustomSettings()
649 {
650  int nbytes_written = 0, nbytes_read = 0, rc = -1;
651  char errstr[MAXRBUF] = {0};
652  char resp[STEELDRIVE_MAXBUF] = {0};
653 
654  char selectedFocuser[2], maxTrip[8], tResp[STEELDRIVE_MAXBUF];
655  int gearR;
656  double gearRatio;
657 
658  tcflush(PortFD, TCIOFLUSH);
659 
660  // Get Gear Ratio
661  if (!sim && (rc = tty_write(PortFD, ":FEASKGR#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
662  {
663  tty_error_msg(rc, errstr, MAXRBUF);
664  LOGF_ERROR(":FEASKGR# updateCustomSettings write error: %s.", errstr);
665  return false;
666  }
667 
668  LOG_DEBUG("CMD (:FEASKGR#)");
669 
670  if (sim)
671  {
672  strncpy(resp, ":FE25040#", STEELDRIVE_CMD + 1);
673  nbytes_read = STEELDRIVE_CMD;
674  }
675  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
676  {
677  tty_error_msg(rc, errstr, MAXRBUF);
678  LOGF_ERROR(":FEASKGR# updateCustomSettings read error: %s.", errstr);
679  return false;
680  }
681 
682  resp[nbytes_read] = '\0';
683 
684  LOGF_DEBUG("RES (%s)", resp);
685 
686  rc = sscanf(resp, ":FE%d#", &gearR);
687 
688  if (rc > 0)
689  {
690  gearRatio = ((double)gearR) / 100000.0;
691  }
692  else
693  {
694  LOGF_ERROR("Unknown error: updateCustomSettings value (%s)", resp);
695  return false;
696  }
697 
698  tcflush(PortFD, TCIOFLUSH);
699 
700  // Get Max Trip
701  if (!sim && (rc = tty_write(PortFD, ":F8ASKS1#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
702  {
703  tty_error_msg(rc, errstr, MAXRBUF);
704  LOGF_ERROR(":F8ASKS1# updateCustomSettings write error: %s.", errstr);
705  return false;
706  }
707 
708  LOG_DEBUG("CMD (:F8ASKS1#)");
709 
710  if (sim)
711  {
712  strncpy(resp, ":F40011577#", STEELDRIVE_CMD_LONG + 1);
713  nbytes_read = STEELDRIVE_CMD_LONG;
714  }
715  else if ((rc = tty_read_section(PortFD, resp, '#', STEELDRIVE_TIMEOUT, &nbytes_read)) != TTY_OK)
716  {
717  tty_error_msg(rc, errstr, MAXRBUF);
718  LOGF_ERROR(":F8ASKS1# updateCustomSettings read error: %s.", errstr);
719  return false;
720  }
721 
722  resp[nbytes_read] = '\0';
723 
724  LOGF_DEBUG("RES (%s)", resp);
725 
726  rc = sscanf(resp, ":F%s#", tResp);
727 
728  if (rc > 0)
729  {
730  strncpy(selectedFocuser, tResp, 1);
731  strncpy(maxTrip, tResp + 1, 7);
732 
733  int sFocuser = atoi(selectedFocuser);
734 
735  IUResetSwitch(&ModelSP);
736  ModelS[sFocuser].s = ISS_ON;
737 
738  fSettings[sFocuser].maxTrip = (atof(maxTrip) * gearRatio) / 100.0;
739  fSettings[sFocuser].gearRatio = gearRatio;
740 
741  CustomSettingN[FOCUS_MAX_TRIP].value = fSettings[sFocuser].maxTrip;
742  CustomSettingN[FOCUS_GEAR_RATIO].value = fSettings[sFocuser].gearRatio;
743 
744  LOGF_DEBUG("Updated max trip: %g gear ratio: %g", fSettings[sFocuser].maxTrip,
745  fSettings[sFocuser].gearRatio);
746  }
747  else
748  {
749  LOGF_ERROR("Unknown error: updateCustomSettings value (%s)", resp);
750  return false;
751  }
752 
753  return true;
754 }
755 
756 /************************************************************************************
757  *
758 * ***********************************************************************************/
759 bool SteelDrive::setTemperatureSamples(unsigned int targetSamples, unsigned int *finalSample)
760 {
761  int nbytes_written = 0, rc = -1;
762  char errstr[MAXRBUF] = {0};
763  char cmd[STEELDRIVE_MAXBUF] = {0};
764 
765  int maxSample = TemperatureSettingN[FOCUS_T_SAMPLES].max;
766  int sample = 0;
767 
768  for (int i = maxSample; i > 0;)
769  {
770  if (targetSamples & maxSample)
771  {
772  sample = maxSample;
773  break;
774  }
775 
776  maxSample >>= 1;
777  }
778 
779  int value = 0;
780  if (sample == 16)
781  value = 5000;
782  else if (sample == 32)
783  value = 15000;
784  else if (sample == 64)
785  value = 25000;
786  else
787  value = 35000;
788 
789  snprintf(cmd, STEELDRIVE_CMD + 1, ":FI%05d#", value);
790 
791  tcflush(PortFD, TCIOFLUSH);
792 
793  LOGF_DEBUG("CMD (%s)", cmd);
794 
795  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
796  {
797  tty_error_msg(rc, errstr, MAXRBUF);
798  LOGF_ERROR("%s setTemperatureSamples write error: %s.", cmd, errstr);
799  return false;
800  }
801 
802  TemperatureSettingN[FOCUS_T_SAMPLES].value = sample;
803  *finalSample = sample;
804 
805  return true;
806 }
807 
808 /************************************************************************************
809  *
810 * ***********************************************************************************/
811 bool SteelDrive::setTemperatureCompensation()
812 {
813  int nbytes_written = 0, rc = -1;
814  char errstr[MAXRBUF] = {0};
815  char cmd[STEELDRIVE_MAXBUF] = {0};
816 
817  double coeff = TemperatureSettingN[FOCUS_T_COEFF].value;
818  bool enable = TemperatureCompensateS[0].s == ISS_ON;
819  int selectedFocus = IUFindOnSwitchIndex(&ModelSP);
820 
821  snprintf(cmd, STEELDRIVE_CMD + 1, ":F%02d%03d%d#", selectedFocus, (int)(coeff * 1000), enable ? 2 : 0);
822 
823  tcflush(PortFD, TCIOFLUSH);
824 
825  LOGF_DEBUG("CMD (%s)", cmd);
826 
827  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
828  {
829  tty_error_msg(rc, errstr, MAXRBUF);
830  LOGF_ERROR("setTemperatureCoefficient error: %s.", errstr);
831  return false;
832  }
833 
834  return true;
835 }
836 
837 /************************************************************************************
838  *
839 * ***********************************************************************************/
840 bool SteelDrive::setCustomSettings(double maxTrip, double gearRatio)
841 {
842  int nbytes_written = 0, rc = -1;
843  char errstr[MAXRBUF] = {0};
844  char cmd[STEELDRIVE_MAXBUF] = {0};
845 
846  unsigned short mmTrip = (unsigned short int)(maxTrip / gearRatio * 100.0);
847 
848  snprintf(cmd, STEELDRIVE_CMD_LONG + 1, ":FC%07d#", mmTrip);
849 
850  tcflush(PortFD, TCIOFLUSH);
851 
852  LOGF_DEBUG("CMD (%s)", cmd);
853 
854  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD_LONG, &nbytes_written)) != TTY_OK)
855  {
856  tty_error_msg(rc, errstr, MAXRBUF);
857  LOGF_ERROR("setCustomSettings error: %s.", errstr);
858  return false;
859  }
860 
861  snprintf(cmd, STEELDRIVE_CMD + 1, ":FD%05d#", (int)(gearRatio * 100000));
862 
863  tcflush(PortFD, TCIOFLUSH);
864 
865  LOGF_DEBUG("CMD (%s)", cmd);
866 
867  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
868  {
869  tty_error_msg(rc, errstr, MAXRBUF);
870  LOGF_ERROR("setCustomSettings error: %s.", errstr);
871  return false;
872  }
873 
874  return true;
875 }
876 
877 /************************************************************************************
878  *
879 * ***********************************************************************************/
880 bool SteelDrive::Sync(unsigned int position)
881 {
882  int nbytes_written = 0, rc = -1;
883  char errstr[MAXRBUF] = {0};
884  char cmd[STEELDRIVE_MAXBUF] = {0};
885 
886  snprintf(cmd, STEELDRIVE_CMD_LONG + 1, ":FB%07d#", position);
887 
888  tcflush(PortFD, TCIOFLUSH);
889 
890  LOGF_DEBUG("CMD (%s)", cmd);
891 
892  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD_LONG, &nbytes_written)) != TTY_OK)
893  {
894  tty_error_msg(rc, errstr, MAXRBUF);
895  LOGF_ERROR("Sync error: %s.", errstr);
896  return false;
897  }
898 
899  simPosition = position;
900 
901  return true;
902 }
903 
904 /************************************************************************************
905  *
906 * ***********************************************************************************/
907 bool SteelDrive::moveFocuser(unsigned int position)
908 {
909  int nbytes_written = 0, rc = -1;
910  char errstr[MAXRBUF] = {0};
911  char cmd[STEELDRIVE_MAXBUF] = {0};
912 
913  if (position < FocusAbsPosN[0].min || position > FocusAbsPosN[0].max)
914  {
915  LOGF_ERROR("Requested position value out of bound: %d", position);
916  return false;
917  }
918 
919  if (FocusAbsPosNP.s == IPS_BUSY)
920  AbortFocuser();
921 
922  snprintf(cmd, STEELDRIVE_CMD_LONG + 1, ":F9%07d#", position);
923 
924  tcflush(PortFD, TCIOFLUSH);
925 
926  LOGF_DEBUG("CMD (%s)", cmd);
927 
928  // Goto absolute step
929  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD_LONG, &nbytes_written)) != TTY_OK)
930  {
931  tty_error_msg(rc, errstr, MAXRBUF);
932  LOGF_ERROR("setPosition error: %s.", errstr);
933  return false;
934  }
935 
936  targetPos = position;
937 
938  return true;
939 }
940 
941 /************************************************************************************
942  *
943 * ***********************************************************************************/
944 bool SteelDrive::startMotion(FocusDirection dir)
945 {
946  int nbytes_written = 0, rc = -1;
947  char errstr[MAXRBUF] = {0};
948  char cmd[STEELDRIVE_MAXBUF] = {0};
949 
950  // inward --> decreasing value --> DOWN
951  // outward --> increasing value --> UP
952  strncpy(cmd, (dir == FOCUS_INWARD) ? ":F2MDOW0#" : ":F1MUP00#", STEELDRIVE_CMD + 1);
953 
954  tcflush(PortFD, TCIOFLUSH);
955 
956  LOGF_DEBUG("CMD (%s)", cmd);
957 
958  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
959  {
960  tty_error_msg(rc, errstr, MAXRBUF);
961  LOGF_ERROR("StartMotion error: %s.", errstr);
962  return false;
963  }
964 
965  return true;
966 }
967 
968 /************************************************************************************
969  *
970 * ***********************************************************************************/
971 bool SteelDrive::setSpeed(unsigned short speed)
972 {
973  int nbytes_written = 0, rc = -1;
974  char errstr[MAXRBUF] = {0};
975  char cmd[STEELDRIVE_MAXBUF] = {0};
976 
977  snprintf(cmd, STEELDRIVE_CMD + 1, ":Fg%05d#", speed);
978 
979  tcflush(PortFD, TCIOFLUSH);
980 
981  LOGF_DEBUG("CMD (%s)", cmd);
982 
983  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
984  {
985  tty_error_msg(rc, errstr, MAXRBUF);
986  LOGF_ERROR("setSpeed error: %s.", errstr);
987  return false;
988  }
989 
990  currentSpeed = speed;
991 
992  return true;
993 }
994 
995 /************************************************************************************
996  *
997 * ***********************************************************************************/
998 bool SteelDrive::setAcceleration(unsigned short accel)
999 {
1000  int nbytes_written = 0, rc = -1;
1001  char errstr[MAXRBUF] = {0};
1002  char cmd[STEELDRIVE_MAXBUF] = {0};
1003 
1004  snprintf(cmd, STEELDRIVE_CMD + 1, ":Fh%05d#", accel);
1005 
1006  tcflush(PortFD, TCIOFLUSH);
1007 
1008  LOGF_DEBUG("CMD (%s)", cmd);
1009 
1010  if (!sim && (rc = tty_write(PortFD, cmd, STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
1011  {
1012  tty_error_msg(rc, errstr, MAXRBUF);
1013  LOGF_ERROR("setAcceleration error: %s.", errstr);
1014  return false;
1015  }
1016 
1017  return true;
1018 }
1019 
1020 /************************************************************************************
1021  *
1022 * ***********************************************************************************/
1023 bool SteelDrive::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
1024 {
1025  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1026  {
1027  if (strcmp(TemperatureCompensateSP.name, name) == 0)
1028  {
1029  int last_index = IUFindOnSwitchIndex(&TemperatureCompensateSP);
1030  IUUpdateSwitch(&TemperatureCompensateSP, states, names, n);
1031 
1032  bool rc = setTemperatureCompensation();
1033 
1034  if (!rc)
1035  {
1036  TemperatureCompensateSP.s = IPS_ALERT;
1037  IUResetSwitch(&TemperatureCompensateSP);
1038  TemperatureCompensateS[last_index].s = ISS_ON;
1039  IDSetSwitch(&TemperatureCompensateSP, nullptr);
1040  return false;
1041  }
1042 
1043  TemperatureCompensateSP.s = IPS_OK;
1044  IDSetSwitch(&TemperatureCompensateSP, nullptr);
1045  return true;
1046  }
1047 
1048  if (strcmp(ModelSP.name, name) == 0)
1049  {
1050  IUUpdateSwitch(&ModelSP, states, names, n);
1051 
1052  int i = IUFindOnSwitchIndex(&ModelSP);
1053 
1054  double focusMaxPos = floor(fSettings[i].maxTrip / fSettings[i].gearRatio) * 100;
1055  FocusAbsPosN[0].max = focusMaxPos;
1057 
1058  CustomSettingN[FOCUS_MAX_TRIP].value = fSettings[i].maxTrip;
1059  CustomSettingN[FOCUS_GEAR_RATIO].value = fSettings[i].gearRatio;
1060 
1061  IDSetNumber(&CustomSettingNP, nullptr);
1062 
1063  ModelSP.s = IPS_OK;
1064  IDSetSwitch(&ModelSP, nullptr);
1065 
1066  return true;
1067  }
1068  }
1069 
1070  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
1071 }
1072 
1073 /************************************************************************************
1074  *
1075 * ***********************************************************************************/
1076 bool SteelDrive::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
1077 {
1078  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1079  {
1080  // Set Accelration
1081  if (strcmp(name, AccelerationNP.name) == 0)
1082  {
1083  if (setAcceleration((int)values[0]))
1084  {
1085  IUUpdateNumber(&AccelerationNP, values, names, n);
1086  AccelerationNP.s = IPS_OK;
1087  IDSetNumber(&AccelerationNP, nullptr);
1088  return true;
1089  }
1090  else
1091  {
1092  AccelerationNP.s = IPS_ALERT;
1093  IDSetNumber(&AccelerationNP, nullptr);
1094  return false;
1095  }
1096  }
1097 
1098  // Set Temperature Settings
1099  if (strcmp(name, TemperatureSettingNP.name) == 0)
1100  {
1101  // Coeff is only needed when we enable or disable the temperature compensation. Here we only set the # of samples
1102  unsigned int targetSamples;
1103 
1104  if (strcmp(names[0], TemperatureSettingN[FOCUS_T_SAMPLES].name) == 0)
1105  targetSamples = (int)values[0];
1106  else
1107  targetSamples = (int)values[1];
1108 
1109  unsigned int finalSample = targetSamples;
1110 
1111  if (setTemperatureSamples(targetSamples, &finalSample))
1112  {
1113  IUUpdateNumber(&TemperatureSettingNP, values, names, n);
1114  TemperatureSettingN[FOCUS_T_SAMPLES].value = finalSample;
1115 
1116  if (TemperatureSettingN[FOCUS_T_COEFF].value > TemperatureSettingN[FOCUS_T_COEFF].max)
1117  TemperatureSettingN[FOCUS_T_COEFF].value = TemperatureSettingN[FOCUS_T_COEFF].max;
1118 
1119  TemperatureSettingNP.s = IPS_OK;
1120  IDSetNumber(&TemperatureSettingNP, nullptr);
1121  return true;
1122  }
1123  else
1124  {
1125  TemperatureSettingNP.s = IPS_ALERT;
1126  IDSetNumber(&TemperatureSettingNP, nullptr);
1127  return true;
1128  }
1129  }
1130 
1131  // Set Custom Settings
1132  if (strcmp(name, CustomSettingNP.name) == 0)
1133  {
1134  int i = IUFindOnSwitchIndex(&ModelSP);
1135 
1136  // If the model is NOT set to custom, then the values cannot be updated
1137  if (i != 4)
1138  {
1139  CustomSettingNP.s = IPS_IDLE;
1140  LOG_WARN("You can not set custom values for a non-custom focuser.");
1141  IDSetNumber(&CustomSettingNP, nullptr);
1142  return false;
1143  }
1144 
1145  double maxTrip, gearRatio;
1146 
1147  if (strcmp(names[0], CustomSettingN[FOCUS_MAX_TRIP].name) == 0)
1148  {
1149  maxTrip = values[0];
1150  gearRatio = values[1];
1151  }
1152  else
1153  {
1154  maxTrip = values[1];
1155  gearRatio = values[0];
1156  }
1157 
1158  if (setCustomSettings(maxTrip, gearRatio))
1159  {
1160  IUUpdateNumber(&CustomSettingNP, values, names, n);
1161  CustomSettingNP.s = IPS_OK;
1162  IDSetNumber(&CustomSettingNP, nullptr);
1163 
1164  updateFocusMaxRange(maxTrip, gearRatio);
1167  }
1168  else
1169  {
1170  CustomSettingNP.s = IPS_ALERT;
1171  IDSetNumber(&CustomSettingNP, nullptr);
1172  }
1173  }
1174 
1175  // Set Sync Position
1176  if (strcmp(name, SyncNP.name) == 0)
1177  {
1178  if (Sync((unsigned int)values[0]))
1179  {
1180  IUUpdateNumber(&SyncNP, values, names, n);
1181  SyncNP.s = IPS_OK;
1182  IDSetNumber(&SyncNP, nullptr);
1183 
1184  if (updatePosition())
1185  IDSetNumber(&FocusAbsPosNP, nullptr);
1186 
1187  return true;
1188  }
1189  else
1190  {
1191  SyncNP.s = IPS_ALERT;
1192  IDSetNumber(&SyncNP, nullptr);
1193 
1194  return false;
1195  }
1196  }
1197  }
1198 
1199  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
1200 }
1201 
1202 /************************************************************************************
1203  *
1204 * ***********************************************************************************/
1205 void SteelDrive::GetFocusParams()
1206 {
1207  if (updateVersion())
1208  IDSetText(&VersionTP, nullptr);
1209 
1210  if (updateTemperature())
1211  IDSetNumber(&TemperatureNP, nullptr);
1212 
1213  if (updateTemperatureSettings())
1214  IDSetNumber(&TemperatureSettingNP, nullptr);
1215 
1216  if (updatePosition())
1217  IDSetNumber(&FocusAbsPosNP, nullptr);
1218 
1219  if (updateSpeed())
1220  IDSetNumber(&FocusSpeedNP, nullptr);
1221 
1222  if (updateAcceleration())
1223  IDSetNumber(&AccelerationNP, nullptr);
1224 
1225  if (updateCustomSettings())
1226  {
1227  IDSetNumber(&CustomSettingNP, nullptr);
1228  IDSetSwitch(&ModelSP, nullptr);
1229  }
1230 }
1231 
1233 {
1234  bool rc = false;
1235 
1236  rc = setSpeed(speed);
1237 
1238  if (!rc)
1239  return false;
1240 
1241  currentSpeed = speed;
1242 
1243  FocusSpeedNP.s = IPS_OK;
1244  IDSetNumber(&FocusSpeedNP, nullptr);
1245 
1246  return true;
1247 }
1248 
1249 IPState SteelDrive::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
1250 {
1251  if (speed != (int)currentSpeed)
1252  {
1253  bool rc = setSpeed(speed);
1254 
1255  if (!rc)
1256  return IPS_ALERT;
1257  }
1258 
1259  gettimeofday(&focusMoveStart, nullptr);
1260  focusMoveRequest = duration / 1000.0;
1261 
1262  startMotion(dir);
1263 
1264  if (duration <= getCurrentPollingPeriod())
1265  {
1266  usleep(getCurrentPollingPeriod() * 1000);
1267  AbortFocuser();
1268  return IPS_OK;
1269  }
1270 
1271  return IPS_BUSY;
1272 }
1273 
1275 {
1276  targetPos = targetTicks;
1277 
1278  bool rc = false;
1279 
1280  rc = moveFocuser(targetPos);
1281 
1282  if (!rc)
1283  return IPS_ALERT;
1284 
1286 
1287  return IPS_BUSY;
1288 }
1289 
1291 {
1292  double newPosition = 0;
1293  bool rc = false;
1294 
1295  if (dir == FOCUS_INWARD)
1296  newPosition = FocusAbsPosN[0].value - ticks;
1297  else
1298  newPosition = FocusAbsPosN[0].value + ticks;
1299 
1300  rc = moveFocuser(newPosition);
1301 
1302  if (!rc)
1303  return IPS_ALERT;
1304 
1305  FocusRelPosN[0].value = ticks;
1308 
1309  return IPS_BUSY;
1310 }
1311 
1313 {
1314  if (!isConnected())
1315  return;
1316 
1317  bool rc = updatePosition();
1318  if (rc)
1319  {
1320  if (fabs(lastPos - FocusAbsPosN[0].value) > STEELDIVE_POSITION_THRESHOLD)
1321  {
1322  IDSetNumber(&FocusAbsPosNP, nullptr);
1323  lastPos = FocusAbsPosN[0].value;
1324  }
1325  }
1326 
1327  if (temperatureUpdateCounter++ > STEELDRIVE_TEMPERATURE_FREQ)
1328  {
1329  temperatureUpdateCounter = 0;
1330  rc = updateTemperature();
1331  if (rc)
1332  {
1333  if (fabs(lastTemperature - TemperatureN[0].value) >= 0.5)
1334  lastTemperature = TemperatureN[0].value;
1335  }
1336 
1337  IDSetNumber(&TemperatureNP, nullptr);
1338  }
1339 
1340  if (FocusTimerNP.s == IPS_BUSY)
1341  {
1342  float remaining = CalcTimeLeft(focusMoveStart, focusMoveRequest);
1343 
1344  if (sim)
1345  {
1346  if (FocusMotionS[FOCUS_INWARD].s == ISS_ON)
1347  {
1348  FocusAbsPosN[0].value -= FocusSpeedN[0].value;
1349  if (FocusAbsPosN[0].value <= 0)
1350  FocusAbsPosN[0].value = 0;
1351  simPosition = FocusAbsPosN[0].value;
1352  }
1353  else
1354  {
1355  FocusAbsPosN[0].value += FocusSpeedN[0].value;
1356  if (FocusAbsPosN[0].value >= FocusAbsPosN[0].max)
1357  FocusAbsPosN[0].value = FocusAbsPosN[0].max;
1358  simPosition = FocusAbsPosN[0].value;
1359  }
1360  }
1361 
1362  // If we exceed focus abs values, stop timer and motion
1363  if (FocusAbsPosN[0].value <= 0 || FocusAbsPosN[0].value >= FocusAbsPosN[0].max)
1364  {
1365  AbortFocuser();
1366 
1367  if (FocusAbsPosN[0].value <= 0)
1368  FocusAbsPosN[0].value = 0;
1369  else
1370  FocusAbsPosN[0].value = FocusAbsPosN[0].max;
1371 
1372  FocusTimerN[0].value = 0;
1374  }
1375  else if (remaining <= 0)
1376  {
1377  FocusTimerNP.s = IPS_OK;
1378  FocusTimerN[0].value = 0;
1379  AbortFocuser();
1380  }
1381  else
1382  FocusTimerN[0].value = remaining * 1000.0;
1383 
1384  IDSetNumber(&FocusTimerNP, nullptr);
1385  }
1386 
1388  {
1389  if (sim)
1390  {
1391  if (FocusAbsPosN[0].value < targetPos)
1392  simPosition += 100;
1393  else
1394  simPosition -= 100;
1395 
1396  if (fabs(simPosition - targetPos) < 100)
1397  {
1398  FocusAbsPosN[0].value = targetPos;
1399  simPosition = FocusAbsPosN[0].value;
1400  }
1401  }
1402 
1403  // Set it OK to within 5 steps
1404  if (fabs(targetPos - FocusAbsPosN[0].value) < 5)
1405  {
1408  IDSetNumber(&FocusAbsPosNP, nullptr);
1409  IDSetNumber(&FocusRelPosNP, nullptr);
1410  lastPos = FocusAbsPosN[0].value;
1411  LOG_INFO("Focuser reached requested position.");
1412  }
1413  }
1414 
1416 }
1417 
1418 /************************************************************************************
1419  *
1420 * ***********************************************************************************/
1422 {
1423  int nbytes_written = 0, rc = -1;
1424  char errstr[MAXRBUF] = {0};
1425 
1426  tcflush(PortFD, TCIOFLUSH);
1427 
1428  LOG_DEBUG("CMD :F3STOP0#");
1429 
1430  if (!sim && (rc = tty_write(PortFD, ":F3STOP0#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
1431  {
1432  tty_error_msg(rc, errstr, MAXRBUF);
1433  LOGF_ERROR(":F3STOP0# Stop error: %s.", errstr);
1434  return false;
1435  }
1436 
1437  if (FocusRelPosNP.s == IPS_BUSY)
1438  {
1440  IDSetNumber(&FocusRelPosNP, nullptr);
1441  }
1442 
1445  IDSetNumber(&FocusTimerNP, nullptr);
1446  IDSetNumber(&FocusAbsPosNP, nullptr);
1447 
1448  return true;
1449 }
1450 
1451 /************************************************************************************
1452  *
1453 * ***********************************************************************************/
1454 float SteelDrive::CalcTimeLeft(timeval start, float req)
1455 {
1456  double timesince;
1457  double timeleft;
1458  struct timeval now
1459  {
1460  0, 0
1461  };
1462  gettimeofday(&now, nullptr);
1463 
1464  timesince =
1465  (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
1466  timesince = timesince / 1000;
1467  timeleft = req - timesince;
1468  return timeleft;
1469 }
1470 
1471 /************************************************************************************
1472  *
1473 * ***********************************************************************************/
1475 {
1477 
1478  IUSaveConfigNumber(fp, &TemperatureSettingNP);
1479  IUSaveConfigSwitch(fp, &TemperatureCompensateSP);
1481  IUSaveConfigNumber(fp, &AccelerationNP);
1482  IUSaveConfigNumber(fp, &CustomSettingNP);
1483  IUSaveConfigSwitch(fp, &ModelSP);
1484 
1485  return saveFocuserConfig();
1486 }
1487 
1488 /************************************************************************************
1489  *
1490 * ***********************************************************************************/
1491 bool SteelDrive::saveFocuserConfig()
1492 {
1493  int nbytes_written = 0, rc = -1;
1494  char errstr[MAXRBUF] = {0};
1495 
1496  tcflush(PortFD, TCIOFLUSH);
1497 
1498  LOG_DEBUG("CMD (:FFPOWER#)");
1499 
1500  if (!sim && (rc = tty_write(PortFD, ":FFPOWER#", STEELDRIVE_CMD, &nbytes_written)) != TTY_OK)
1501  {
1502  tty_error_msg(rc, errstr, MAXRBUF);
1503  LOGF_ERROR(":FFPOWER# saveFocuserConfig error: %s.", errstr);
1504  return false;
1505  }
1506 
1507  return true;
1508 }
1509 
1510 /************************************************************************************
1511  *
1512 * ***********************************************************************************/
1514 {
1515  tty_set_debug(enable ? 1 : 0);
1516 }
1517 
1518 /************************************************************************************
1519  *
1520 * ***********************************************************************************/
1521 void SteelDrive::updateFocusMaxRange(double maxTrip, double gearRatio)
1522 {
1523  double maxSteps = floor(maxTrip / gearRatio * 100.0);
1524 
1525  /* Relative and absolute movement */
1526  FocusRelPosN[0].min = 0;
1527  FocusRelPosN[0].max = floor(maxSteps / 2.0);
1528  FocusRelPosN[0].step = 100;
1529 
1530  FocusAbsPosN[0].min = 0;
1531  FocusAbsPosN[0].max = maxSteps;
1532  FocusAbsPosN[0].step = 1000;
1533 }
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.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INumberVectorProperty FocusSpeedNP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
INumberVectorProperty FocusTimerNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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 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.
void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
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....
Definition: steeldrive.cpp:128
@ FOCUS_T_COEFF
Definition: steeldrive.h:45
@ FOCUS_T_SAMPLES
Definition: steeldrive.h:46
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
const char * getDefaultName() override
Definition: steeldrive.cpp:185
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: steeldrive.cpp:49
@ FOCUS_MAX_TRIP
Definition: steeldrive.h:40
@ FOCUS_GEAR_RATIO
Definition: steeldrive.h:41
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual IPState MoveRelFocuser(FocusDirection dir, unsigned int ticks) override
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
virtual bool Handshake() override
perform handshake with device to check communication
Definition: steeldrive.cpp:168
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
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_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:566
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
Definition: indicom.c:360
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
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 IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
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 IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
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
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
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1296
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOG_WARN(txt)
Definition: indilogger.h:73
#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
@ value
the parser finished reading a JSON value
__u8 cmd[4]
Definition: pwc-ioctl.h:2
#define STEELDRIVE_TIMEOUT
Definition: steeldrive.cpp:32
std::unique_ptr< SteelDrive > steelDrive(new SteelDrive())
#define STEELDRIVE_MAX_RETRIES
Definition: steeldrive.cpp:31
#define STEELDRIVE_MAXBUF
Definition: steeldrive.cpp:33
#define FOCUS_SETTINGS_TAB
Definition: steeldrive.cpp:39
#define STEELDRIVE_CMD
Definition: steeldrive.cpp:34
#define STEELDIVE_POSITION_THRESHOLD
Definition: steeldrive.cpp:37
#define STEELDRIVE_TEMPERATURE_FREQ
Definition: steeldrive.cpp:36
#define STEELDRIVE_CMD_LONG
Definition: steeldrive.cpp:35
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250