Instrument Neutral Distributed Interface INDI  1.9.5
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 }
STEELDRIVE_TEMPERATURE_FREQ
#define STEELDRIVE_TEMPERATURE_FREQ
Definition: steeldrive.cpp:36
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
IP_RO
@ IP_RO
Definition: indiapi.h:183
SteelDrive::getDefaultName
const char * getDefaultName() override
Definition: steeldrive.cpp:185
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
SteelDrive::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: steeldrive.cpp:128
INDI::DefaultDevice::addAuxControls
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
Definition: defaultdevice.cpp:665
SteelDrive::MoveFocuser
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.
Definition: steeldrive.cpp:1249
IUUpdateMinMax
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1850
IPState
IPState
Property state.
Definition: indiapi.h:158
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
min
double min(void)
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
indicom.h
Implementations for common driver routines.
IDSetText
void IDSetText(const ITextVectorProperty *t, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing text vector property.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
SteelDrive
Definition: steeldrive.h:25
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
IUFillNumber
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: indidriver.c:348
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
MAIN_CONTROL_TAB
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
Definition: defaultdevice.cpp:34
INDI::FocuserInterface::FocusTimerN
INumber FocusTimerN[1]
Definition: indifocuserinterface.h:279
IUFillTextVector
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: indidriver.c:477
INDI::DefaultDevice::setDefaultPollingPeriod
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
Definition: defaultdevice.cpp:1157
SteelDrive::SetFocuserSpeed
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
Definition: steeldrive.cpp:1232
INDI::FocuserInterface::FOCUSER_HAS_VARIABLE_SPEED
@ FOCUSER_HAS_VARIABLE_SPEED
Definition: indifocuserinterface.h:79
SteelDrive::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: steeldrive.cpp:1474
STEELDRIVE_CMD
#define STEELDRIVE_CMD
Definition: steeldrive.cpp:34
INDI::Focuser::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: indifocuser.cpp:241
SteelDrive::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: steeldrive.cpp:1274
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
INDI::FocuserInterface::FocusMotionS
ISwitch FocusMotionS[2]
Definition: indifocuserinterface.h:275
tty_read_section
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:557
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
IUSaveConfigNumber
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indicom.c:1455
IUFillText
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: indidriver.c:369
SteelDrive::FocusCustomSetting::maxTrip
double maxTrip
Definition: steeldrive.h:33
currentSpeed
#define currentSpeed
Definition: robofocus.cpp:36
STEELDIVE_POSITION_THRESHOLD
#define STEELDIVE_POSITION_THRESHOLD
Definition: steeldrive.cpp:37
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
max
double max(void)
IUResetSwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1442
tty_error_msg
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1156
STEELDRIVE_CMD_LONG
#define STEELDRIVE_CMD_LONG
Definition: steeldrive.cpp:35
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
SteelDrive::FOCUS_MAX_TRIP
@ FOCUS_MAX_TRIP
Definition: steeldrive.h:40
SteelDrive::SteelDrive
SteelDrive()
Definition: steeldrive.cpp:43
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
FOCUS_SETTINGS_TAB
#define FOCUS_SETTINGS_TAB
Definition: steeldrive.cpp:39
STEELDRIVE_MAXBUF
#define STEELDRIVE_MAXBUF
Definition: steeldrive.cpp:33
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
INDI::Focuser::PortFD
int PortFD
Definition: indifocuser.h:116
SteelDrive::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: steeldrive.cpp:49
STEELDRIVE_TIMEOUT
#define STEELDRIVE_TIMEOUT
Definition: steeldrive.cpp:32
INDI::FocuserInterface::FocusTimerNP
INumberVectorProperty FocusTimerNP
Definition: indifocuserinterface.h:278
tty_write
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:415
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
IUFillSwitchVector
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: indidriver.c:412
IUFillNumberVector
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: indidriver.c:455
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
ISR_1OFMANY
@ ISR_1OFMANY
Definition: indiapi.h:172
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::FocuserInterface::FocusRelPosN
INumber FocusRelPosN[1]
Definition: indifocuserinterface.h:287
tty_set_debug
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
Definition: indicom.c:352
INDI::Focuser::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indifocuser.cpp:120
_INumberVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:322
IUUpdateSwitch
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:171
INDI::FocuserInterface::FocusRelPosNP
INumberVectorProperty FocusRelPosNP
Definition: indifocuserinterface.h:286
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
SteelDrive::debugTriggered
void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
Definition: steeldrive.cpp:1513
SteelDrive::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: steeldrive.cpp:1023
LOG_DEBUG
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
SteelDrive::FOCUS_T_SAMPLES
@ FOCUS_T_SAMPLES
Definition: steeldrive.h:46
SteelDrive::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: steeldrive.cpp:1076
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
Sync
int Sync(int fd, char *matchedObject)
Definition: lx200driver.cpp:1561
IUSaveText
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1449
SteelDrive::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: steeldrive.cpp:1312
STEELDRIVE_MAX_RETRIES
#define STEELDRIVE_MAX_RETRIES
Definition: steeldrive.cpp:31
steelDrive
std::unique_ptr< SteelDrive > steelDrive(new SteelDrive())
name
const char * name
Definition: indiserver.c:116
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
SteelDrive::FOCUS_T_COEFF
@ FOCUS_T_COEFF
Definition: steeldrive.h:45
steeldrive.h
IUUpdateNumber
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:225
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
IP_RW
@ IP_RW
Definition: indiapi.h:185
LOG_WARN
#define LOG_WARN(txt)
Definition: indilogger.h:73
INDI::FocuserInterface::FocusSpeedNP
INumberVectorProperty FocusSpeedNP
Definition: indifocuserinterface.h:268
ISState
ISState
Switch state.
Definition: indiapi.h:148
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1424
SteelDrive::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: steeldrive.cpp:1421
IUSaveConfigSwitch
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indicom.c:1465
INDI::Focuser::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:58
TTY_OK
@ TTY_OK
Definition: indicom.h:94
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
SteelDrive::FOCUS_GEAR_RATIO
@ FOCUS_GEAR_RATIO
Definition: steeldrive.h:41
INDI::Focuser::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indifocuser.cpp:168
INDI::FocuserInterface::FocusAbsPosN
INumber FocusAbsPosN[1]
Definition: indifocuserinterface.h:283
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
SteelDrive::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, unsigned int ticks) override
Definition: steeldrive.cpp:1290
SteelDrive::Handshake
virtual bool Handshake() override
perform handshake with device to check communication
Definition: steeldrive.cpp:168
IUFillSwitch
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: indidriver.c:320
SteelDrive::FocusCustomSetting::gearRatio
double gearRatio
Definition: steeldrive.h:34
INDI::Focuser::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: indifocuser.cpp:145
INDI::FocuserInterface::FocusSpeedN
INumber FocusSpeedN[1]
Definition: indifocuserinterface.h:269
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151