Instrument Neutral Distributed Interface INDI  2.0.2
esattoarco.cpp
Go to the documentation of this file.
1 /*
2  Primaluca Labs Essato-Arco Focuser+Rotator Driver
3 
4  Copyright (C) 2020 Piotr Zyziuk
5  Copyright (C) 2020-2022 Jasem Mutlaq
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public
9  License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21  JM 2022.07.16: Major refactor to using json.h and update to Essato Arco
22  Document protocol revision 3.3 (8th July 2022).
23 */
24 
25 #include "esattoarco.h"
26 
27 #include "indicom.h"
28 #include "json.h"
29 
30 #include <cmath>
31 #include <cstring>
32 #include <memory>
33 #include <algorithm>
34 
35 #include <assert.h>
36 #include <termios.h>
37 #include <unistd.h>
39 #include <sys/ioctl.h>
40 
41 static std::unique_ptr<EsattoArco> esattoarco(new EsattoArco());
42 
43 static const char *ENVIRONMENT_TAB = "Environment";
44 static const char *ROTATOR_TAB = "Rotator";
45 
47 {
48  setVersion(1, 0);
49 
50  // Focuser capabilities
52  // Rotator capabilities
54 }
55 
56 /************************************************************************************************************
57  *
58 *************************************************************************************************************/
60 {
62 
63  setConnectionParams();
64 
65  // Firmware information
66  FirmwareTP[ESATTO_FIRMWARE_SN].fill("ESATTO_FIRMWARE_SN", "Esatto SN", "");
67  FirmwareTP[ESATTO_FIRMWARE_VERSION].fill("ESATTO_FIRMWARE_VERSION", "Esatto Firmware", "");
68  FirmwareTP[ARCO_FIRMWARE_SN].fill("ARCO_FIRMWARE_SN", "Arco SN", "");
69  FirmwareTP[ARCO_FIRMWARE_VERSION].fill("VERARCO_FIRMWARE_VERSIONSION", "Arco Firmware", "");
70  FirmwareTP.fill(getDeviceName(), "FOCUS_FIRMWARE", "Firmware", CONNECTION_TAB, IP_RO, 60, IPS_IDLE);
71 
75 
76  // Voltage Information
77  VoltageNP[VOLTAGE_12V].fill("VOLTAGE_12V", "12v", "%.2f", 0, 100, 0., 0.);
78  VoltageNP[VOLTAGE_USB].fill("VOLTAGE_USB", "USB", "%.2f", 0, 100, 0., 0.);
79  VoltageNP.fill(getDeviceName(), "VOLTAGE_IN", "Voltage in", ENVIRONMENT_TAB, IP_RO, 0, IPS_IDLE);
80 
81  // Focuser temperature
82  IUFillNumber(&TemperatureN[TEMPERATURE_MOTOR], "TEMPERATURE", "Motor (c)", "%.2f", -50, 70., 0., 0.);
83  IUFillNumber(&TemperatureN[TEMPERATURE_EXTERNAL], "TEMPERATURE_ETX", "External (c)", "%.2f", -50, 70., 0., 0.);
84  IUFillNumberVector(&TemperatureNP, TemperatureN, 2, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature", ENVIRONMENT_TAB,
85  IP_RO, 0, IPS_IDLE);
86 
87  // Current Speed
88  IUFillNumber(&SpeedN[0], "SPEED", "steps/s", "%.f", 0, 7000., 1, 0);
89  IUFillNumberVector(&SpeedNP, SpeedN, 1, getDeviceName(), "FOCUS_SPEED", "Motor Speed", MAIN_CONTROL_TAB, IP_RO, 0,
90  IPS_IDLE);
91 
92  // Backlash measurement
93  IUFillText(&BacklashMessageT[0], "BACKLASH", "Backlash stage", "Press START to measure backlash.");
94  IUFillTextVector(&BacklashMessageTP, BacklashMessageT, 1, getDeviceName(), "BACKLASH_MESSAGE", "Backlash",
96 
97  // Backlash measurement stages
98  IUFillSwitch(&BacklashMeasurementS[BACKLASH_START], "BACKLASH_START", "Start", ISS_OFF);
99  IUFillSwitch(&BacklashMeasurementS[BACKLASH_NEXT], "BACKLASH_NEXT", "Next", ISS_OFF);
100  IUFillSwitchVector(&BacklashMeasurementSP, BacklashMeasurementS, 2, getDeviceName(), "FOCUS_BACKLASH", "Backlash",
102  IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
103 
104  // Speed Moves
105  IUFillSwitch(&FastMoveS[FASTMOVE_IN], "FASTMOVE_IN", "Move In", ISS_OFF);
106  IUFillSwitch(&FastMoveS[FASTMOVE_OUT], "FASTMOVE_OUT", "Move out", ISS_OFF);
107  IUFillSwitch(&FastMoveS[FASTMOVE_STOP], "FASTMOVE_STOP", "Stop", ISS_OFF);
108  IUFillSwitchVector(&FastMoveSP, FastMoveS, 3, getDeviceName(), "FAST_MOVE", "Calibration Move", MAIN_CONTROL_TAB, IP_RW,
109  ISR_ATMOST1, 0, IPS_IDLE);
110 
111  // Override the default Max. Position to make it Read-Only
112  IUFillNumberVector(&FocusMaxPosNP, FocusMaxPosN, 1, getDeviceName(), "FOCUS_MAX", "Max. Position", MAIN_CONTROL_TAB, IP_RO,
113  0, IPS_IDLE);
114 
119 
121 
122  // Rotator Ticks
123  IUFillNumber(&RotatorAbsPosN[0], "ROTATOR_ABSOLUTE_POSITION", "Ticks", "%.f", 0., 100000., 1000., 0.);
124  IUFillNumberVector(&RotatorAbsPosNP, RotatorAbsPosN, 1, getDeviceName(), "ABS_ROTATOR_POSITION", "Goto", ROTATOR_TAB, IP_RW,
125  0, IPS_IDLE );
126  // Rotator Calibration
127  IUFillSwitch(&RotCalibrationS[ARCO_CALIBRATION_START], "ARCO_CALIBRATION_START", "Start", ISS_OFF);
128  IUFillSwitchVector(&RotatorCalibrationSP, RotCalibrationS, 1, getDeviceName(), "ARCO_CALIBRATION", "Calibrate", ROTATOR_TAB,
129  IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
130 
131  // Read reverse rotator config
132  int index = -1;
134  if (index >= 0)
135  {
137  ReverseRotatorS[index].s = ISS_ON;
138  }
139 
141  // Defaults
143 
144  // Relative and absolute movement
145  FocusRelPosN[0].min = 0.;
146  FocusRelPosN[0].max = 50000.;
147  FocusRelPosN[0].value = 0;
148  FocusRelPosN[0].step = 1000;
149 
150  FocusAbsPosN[0].min = 0.;
151  FocusAbsPosN[0].max = 200000.;
152  FocusAbsPosN[0].value = 0;
153  FocusAbsPosN[0].step = 1000;
154 
155  FocusMaxPosN[0].value = 2097152;
156  PresetN[0].max = FocusMaxPosN[0].value;
157  PresetN[1].max = FocusMaxPosN[0].value;
158  PresetN[2].max = FocusMaxPosN[0].value;
159 
160  FocusBacklashN[0].min = 0;
161  FocusBacklashN[0].max = 10000;
162  FocusBacklashN[0].step = 1;
163  FocusBacklashN[0].value = 0;
164 
165  addAuxControls();
166 
168 
169  return true;
170 }
171 
172 /************************************************************************************************************
173  *
174 *************************************************************************************************************/
176 {
177  if (isConnected() && updateMaxLimit() == false)
178  LOGF_WARN("Check you have the latest %s firmware. Focuser requires calibration.", getDeviceName());
179 
180  if (isConnected())
181  {
182  if (getStartupValues())
183  LOGF_INFO("Parameters updated, %s ready for use.", getDeviceName());
184  else
185  LOG_WARN("Failed to inquire parameters. Check logs.");
186 
187  //Focuser
189 
190  defineProperty(&SpeedNP);
191  defineProperty(&BacklashMessageTP);
192  defineProperty(&BacklashMeasurementSP);
193  defineProperty(FirmwareTP);
194 
195  if (updateTemperature())
196  defineProperty(&TemperatureNP);
197 
198  if (updateVoltageIn())
199  defineProperty(VoltageNP);
200 
201  // Rotator
203  defineProperty(&RotatorAbsPosNP);
204  defineProperty(&RotatorCalibrationSP);
205  defineProperty(&RotCalibrationMessageTP);
206  }
207  else
208  {
209  //Focuser
211 
212  if (TemperatureNP.s == IPS_OK)
213  deleteProperty(TemperatureNP.name);
214 
215  deleteProperty(FirmwareTP.getName());
216  deleteProperty(VoltageNP.getName());
217  deleteProperty(BacklashMessageTP.name);
218  deleteProperty(BacklashMeasurementSP.name);
219  deleteProperty(SpeedNP.name);
220 
221  // Rotator
223  deleteProperty(RotatorAbsPosNP.name);
224  deleteProperty(RotatorCalibrationSP.name);
225  deleteProperty(RotCalibrationMessageTP.name);
226  }
227 
228  return true;
229 }
230 
231 /************************************************************************************************************
232  *
233 *************************************************************************************************************/
235 {
236  if (Ack())
237  {
238  LOGF_INFO("%s is online. Getting parameters...", getDeviceName());
239  return true;
240  }
241 
242  LOG_INFO("Error retrieving data from device, please ensure focuser is powered and the port is correct.");
243  return false;
244 }
245 
246 /************************************************************************************************************
247  *
248 *************************************************************************************************************/
250 {
251  return "Esatto Arco";
252 }
253 
254 /************************************************************************************************************
255  *
256 *************************************************************************************************************/
257 bool EsattoArco::updateTemperature()
258 {
259  double temperature = 0;
260  if (m_Esatto->getMotorTemp(temperature))
261  {
262  TemperatureN[TEMPERATURE_MOTOR].value = temperature;
263  TemperatureNP.s = IPS_OK;
264  }
265  else
266  TemperatureNP.s = IPS_ALERT;
267 
268  TemperatureN[TEMPERATURE_EXTERNAL].value = -273.15;
269  if (m_Esatto->getExternalTemp(temperature) && temperature > -127)
270  {
271  TemperatureN[TEMPERATURE_EXTERNAL].value = temperature;
272  }
273 
274  return true;
275 }
276 
277 /************************************************************************************************************
278  *
279 *************************************************************************************************************/
280 bool EsattoArco::updateMaxLimit()
281 {
282  uint32_t maxLimit = 0;
283 
284  if (m_Esatto->getMaxPosition(maxLimit) && maxLimit > 0)
285  {
286  FocusMaxPosN[0].max = maxLimit;
287  if (FocusMaxPosN[0].value > maxLimit)
288  FocusMaxPosN[0].value = maxLimit;
289 
290  FocusAbsPosN[0].min = 0;
291  FocusAbsPosN[0].max = maxLimit;
292  FocusAbsPosN[0].value = 0;
293  FocusAbsPosN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
294 
295  FocusRelPosN[0].min = 0.;
296  FocusRelPosN[0].max = FocusAbsPosN[0].step * 10;
297  FocusRelPosN[0].value = 0;
298  FocusRelPosN[0].step = FocusAbsPosN[0].step;
299 
300  PresetN[0].max = maxLimit;
301  PresetN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
302  PresetN[1].max = maxLimit;
303  PresetN[1].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
304  PresetN[2].max = maxLimit;
305  PresetN[2].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
306 
307 
309  return true;
310  }
311 
313  return false;
314 }
315 
316 /************************************************************************************************************
317  *
318 *************************************************************************************************************/
320 {
321  return m_Esatto->setBacklash(steps);
322 }
323 
324 /************************************************************************************************************
325  *
326 *************************************************************************************************************/
327 bool EsattoArco::updatePosition()
328 {
329  uint32_t steps;
330  // Update focuser position
331  if (m_Esatto->getAbsolutePosition(steps))
332  FocusAbsPosN[0].value = steps;
333 
334  double arcoPosition;
335  // Update Arco steps position
336  if (m_Arco->getAbsolutePosition(PrimalucaLabs::UNIT_STEPS, arcoPosition))
337  {
338  //Update Rotator Position
339  RotatorAbsPosN[0].value = arcoPosition;
340  }
341 
342  // Update Arco degrees position
343  if (m_Arco->getAbsolutePosition(PrimalucaLabs::UNIT_DEGREES, arcoPosition))
344  {
345  //Update Rotator Position
346  const bool isReversed = ReverseRotatorS[INDI_ENABLED].s == ISS_ON;
347  if (isReversed)
348  GotoRotatorN[0].value = range360(360 - arcoPosition);
349  else
350  GotoRotatorN[0].value = range360(arcoPosition);
351 
352  }
353 
354  return true;
355 }
356 
357 /************************************************************************************************************
358  *
359 *************************************************************************************************************/
360 bool EsattoArco::updateVoltageIn()
361 {
362  double voltage;
363  if (m_Esatto->getVoltage12v(voltage))
364  VoltageNP[VOLTAGE_12V].setValue(voltage);
365 
366  VoltageNP.setState((voltage >= 11.0) ? IPS_OK : IPS_ALERT);
367  if (m_Esatto->getVoltageUSB(voltage))
368  VoltageNP[VOLTAGE_USB].setValue(voltage);
369  return true;
370 }
371 
372 /************************************************************************************************************
373  *
374 *************************************************************************************************************/
375 bool EsattoArco::isMotionComplete()
376 {
377  uint32_t speed;
378  if (m_Esatto->getCurrentSpeed(speed))
379  {
380  return speed == 0;
381  }
382  return false;
383 }
384 
385 /************************************************************************************************************
386  *
387 *************************************************************************************************************/
388 bool EsattoArco::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
389 {
390  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
391  {
392  // Set backlash
393  if (!strcmp(name, BacklashMeasurementSP.name))
394  {
395  BacklashMeasurementSP.s = IPS_BUSY;
396  IUUpdateSwitch(&BacklashMeasurementSP, states, names, n);
397 
398  auto current_switch = IUFindOnSwitchIndex(&BacklashMeasurementSP);
399  BacklashMeasurementS[current_switch].s = ISS_ON;
400  IDSetSwitch(&BacklashMeasurementSP, nullptr);
401 
402  if (current_switch == BACKLASH_START)
403  {
404  if (bStage == BacklashIdle || bStage == BacklashComplete )
405  {
406  // Start the backlash measurement process
407  LOG_INFO("Start Backlash measurement.");
408  BacklashMeasurementSP.s = IPS_BUSY;
409  IDSetSwitch(&BacklashMeasurementSP, nullptr);
410 
411  IUSaveText(&BacklashMessageT[0], "Drive the focuser in any direction until focus changes.");
412  IDSetText(&BacklashMessageTP, nullptr);
413 
414  // Set next step
415  bStage = BacklashMinimum;
416  }
417  else
418  {
419  LOG_INFO("Already started backlash measure. Proceed to next step.");
420  IUSaveText(&BacklashMessageT[0], "Already started. Proceed to NEXT.");
421  IDSetText(&BacklashMessageTP, nullptr);
422  }
423  }
424  else if (current_switch == BACKLASH_NEXT)
425  {
426  if (bStage == BacklashMinimum)
427  {
428  FocusBacklashN[0].value = static_cast<int32_t>(FocusAbsPosN[0].value);
429 
430  IUSaveText(&BacklashMessageT[0], "Drive the focuser in the opposite direction, then press NEXT to finish.");
431  IDSetText(&BacklashMessageTP, nullptr);
432  bStage = BacklashMaximum;
433  }
434  else if (bStage == BacklashMaximum)
435  {
436  FocusBacklashN[0].value -= FocusAbsPosN[0].value;
437 
438  // Set Esatto backlash
440  IDSetNumber(&FocusBacklashNP, nullptr);
441 
443 
444  IUSaveText(&BacklashMessageT[0], "Backlash Measure Completed.");
445  IDSetText(&BacklashMessageTP, nullptr);
446 
447  bStage = BacklashComplete;
448 
449  LOG_INFO("Backlash measurement completed");
450  BacklashMeasurementSP.s = IPS_OK;
451  IDSetSwitch(&BacklashMeasurementSP, nullptr);
452  BacklashMeasurementS[current_switch].s = ISS_OFF;
453  IDSetSwitch(&BacklashMeasurementSP, nullptr);
454  }
455  else
456  {
457  IUSaveText(&BacklashMessageT[0], "Backlash not in progress.");
458  IDSetText(&BacklashMessageTP, nullptr);
459  }
460 
461  }
462  return true;
463  }
464  // Fast motion
465  else if (!strcmp(name, FastMoveSP.name))
466  {
467  IUUpdateSwitch(&FastMoveSP, states, names, n);
468  auto current_switch = IUFindOnSwitchIndex(&FastMoveSP);
469 
470  switch (current_switch)
471  {
472  case FASTMOVE_IN:
473  if (!m_Esatto->fastMoveIn())
474  return false;
475  FastMoveSP.s = IPS_BUSY;
476  break;
477  case FASTMOVE_OUT:
478  if (!m_Esatto->fastMoveOut())
479  return false;
480  FastMoveSP.s = IPS_BUSY;
481  break;
482  case FASTMOVE_STOP:
483  if (!m_Esatto->stop())
484  return false;
485  FastMoveSP.s = IPS_IDLE;
486  break;
487  default:
488  break;
489  }
490 
491  IDSetSwitch(&FastMoveSP, nullptr);
492  return true;
493  }
494  // Rotator Calibration
495  else if (!strcmp(name, RotatorCalibrationSP.name))
496  {
497  if(m_Arco->calibrate())
498  {
499  LOG_INFO("Calibrating Arco. Please wait.");
500  RotatorAbsPosNP.s = IPS_BUSY;
502  RotatorCalibrationSP.s = IPS_BUSY;
503  IDSetSwitch(&RotatorCalibrationSP, nullptr);
504  IDSetNumber(&GotoRotatorNP, nullptr);
505  IDSetNumber(&RotatorAbsPosNP, nullptr);
506  }
507  else
508  {
509  IUResetSwitch(&RotatorCalibrationSP);
510  RotatorCalibrationSP.s = IPS_ALERT;
511  IDSetSwitch(&RotatorCalibrationSP, nullptr);
512  }
513  return true;
514  }
515  else if (strstr(name, "ROTATOR"))
516  {
517  if (INDI::RotatorInterface::processSwitch(dev, name, states, names, n))
518  return true;
519  }
520  }
521  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
522 }
523 
524 /************************************************************************************************************
525  *
526 *************************************************************************************************************/
527 bool EsattoArco::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
528 {
529  if (dev == nullptr || strcmp(dev, getDeviceName()) != 0)
530  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
531 
532  else if (strcmp(name, RotatorAbsPosNP.name) == 0)
533  {
534  if (m_Arco->moveAbsolutePoition(PrimalucaLabs::UNIT_STEPS, values[0]))
535  RotatorAbsPosNP.s = IPS_BUSY;
536  else
537  RotatorAbsPosNP.s = IPS_ALERT;
538  GotoRotatorNP.s = RotatorAbsPosNP.s;
539  IDSetNumber(&RotatorAbsPosNP, nullptr);
540  IDSetNumber(&GotoRotatorNP, nullptr);
541  if (RotatorAbsPosNP.s == IPS_BUSY)
542  LOGF_INFO("Rotator moving to %.f steps...", values[0]);
543  return true;
544  }
545  else if (strstr(name, "ROTATOR"))
546  {
547  if (INDI::RotatorInterface::processNumber(dev, name, values, names, n))
548  return true;
549  }
550 
551  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
552 }
553 
554 /************************************************************************************************************
555  *
556 *************************************************************************************************************/
557 IPState EsattoArco::MoveAbsFocuser(uint32_t targetTicks)
558 {
559  if (m_Esatto->goAbsolutePosition(targetTicks))
560  {
561  RotatorAbsPosNP.s = IPS_BUSY;
562  IDSetNumber(&RotatorAbsPosNP, nullptr);
563  return IPS_BUSY;
564  }
565  return IPS_ALERT;
566 }
567 
568 /************************************************************************************************************
569  *
570 *************************************************************************************************************/
572 {
573  int reversed = (IUFindOnSwitchIndex(&FocusReverseSP) == INDI_ENABLED) ? -1 : 1;
574  int relativeTicks = ((dir == FOCUS_INWARD) ? -ticks : ticks) * reversed;
575  double newPosition = FocusAbsPosN[0].value + relativeTicks;
576 
577  bool rc = MoveAbsFocuser(newPosition);
578 
579  return (rc ? IPS_BUSY : IPS_ALERT);
580 }
581 
582 /************************************************************************************************************
583  *
584 *************************************************************************************************************/
586 {
587  return m_Esatto->stop();
588 }
589 
590 /************************************************************************************************************
591  *
592 *************************************************************************************************************/
594 {
595  if (!isConnected())
596  {
598  return;
599  }
600 
601  auto currentFocusPosition = FocusAbsPosN[0].value;
602  auto currentRotatorPosition = RotatorAbsPosN[0].value;
603  if (updatePosition())
604  {
605  if (std::abs(currentFocusPosition - FocusAbsPosN[0].value) > 0)
606  {
607  // Focuser State Machine
608  if (FocusAbsPosNP.s == IPS_BUSY && m_Esatto->isBusy() == false)
609  {
612  IDSetNumber(&FocusAbsPosNP, nullptr);
613  IDSetNumber(&FocusRelPosNP, nullptr);
614  }
615  else
616  IDSetNumber(&FocusAbsPosNP, nullptr);
617  }
618 
619  // Rotator State Machine
620 
621  // Only check status if position changed.
622  if (std::abs(currentRotatorPosition - RotatorAbsPosN[0].value) > 0)
623  {
624  // Rotator was busy and now stopped?
625  if (GotoRotatorNP.s == IPS_BUSY && m_Arco->isBusy() == false)
626  {
627  // Check if we were calibrating
628  if(RotatorCalibrationSP.s == IPS_BUSY)
629  {
630  RotatorCalibrationSP.s = IPS_IDLE;
631  IDSetSwitch(&RotatorCalibrationSP, nullptr);
632  LOG_INFO("Arco calibration complete.");
633  if(m_Arco->sync(PrimalucaLabs::UNIT_STEPS, 0))
634  LOG_INFO("Arco position synced to zero.");
635  }
637  RotatorAbsPosNP.s = IPS_OK;
638  IDSetNumber(&GotoRotatorNP, nullptr);
639  IDSetNumber(&RotatorAbsPosNP, nullptr);
640  }
641  else
642  {
643  IDSetNumber(&GotoRotatorNP, nullptr);
644  IDSetNumber(&RotatorAbsPosNP, nullptr);
645  }
646  }
647  }
648 
649  if (m_TemperatureCounter++ == TEMPERATURE_FREQUENCY)
650  {
651  auto currentTemperature = TemperatureN[0].value;
652  if (updateTemperature())
653  {
654  if (std::abs(currentTemperature - TemperatureN[0].value) >= 0.1)
655  IDSetNumber(&TemperatureNP, nullptr);
656  }
657 
658  auto current12V = VoltageNP[VOLTAGE_12V].getValue();
659  auto currentUSB = VoltageNP[VOLTAGE_USB].getValue();
660  if (updateVoltageIn())
661  {
662  if (std::abs(current12V - VoltageNP[VOLTAGE_12V].getValue()) >= 0.1 ||
663  std::abs(currentUSB - VoltageNP[VOLTAGE_USB].getValue()) >= 0.1)
664  {
665  VoltageNP.apply();
666  if (VoltageNP[VOLTAGE_12V].getValue() < 11.0)
667  LOG_WARN("Please check 12v DC power supply is connected.");
668  }
669  }
670 
671  m_TemperatureCounter = 0; // Reset the counter
672  }
673 
675 }
676 
677 /************************************************************************************************************
678  *
679 *************************************************************************************************************/
680 bool EsattoArco::getStartupValues()
681 {
682  updatePosition();
683  json info;
684  if (m_Arco->getMotorInfo(info))
685  {
686  int calMax, calMin;
687  try
688  {
689  info["get"]["MOT2"]["CAL_MAXPOS"].get_to(calMax);
690  info["get"]["MOT2"]["CAL_MINPOS"].get_to(calMin);
691  }
692  catch (json::exception &e)
693  {
694  // output exception information
695  LOGF_ERROR("Failed to parse info: %s Exception: %s id: %d", info.dump().c_str(),
696  e.what(), e.id);
697  return false;
698  }
699  RotatorAbsPosN[0].min = calMin;
700  RotatorAbsPosN[0].max = calMax;
701  RotatorAbsPosN[0].step = std::abs(calMax - calMin) / 50.0;
702  }
703  return true;
704 }
705 
706 /************************************************************************************************************
707  *
708 *************************************************************************************************************/
709 bool EsattoArco::Ack()
710 {
711  std::string serial, firmware;
712 
713  if(initCommandSet() == false)
714  {
715  LOG_ERROR("Failed setting attributes on serial port and init command sets");
716  return false;
717  }
718 
719  if (m_Arco->setEnabled(true) && !m_Arco->isEnabled())
720  {
721  LOG_ERROR("Failed to enable ARCO rotator. Please check it is powered and connected.");
722  return false;
723  }
724 
725  bool rc1 = m_Esatto->getSerialNumber(serial);
726  bool rc2 = m_Esatto->getFirmwareVersion(firmware);
727 
728  if (rc1 && rc2)
729  {
730  FirmwareTP[ESATTO_FIRMWARE_SN].setText(serial);
731  FirmwareTP[ESATTO_FIRMWARE_VERSION].setText(firmware);
732  LOGF_INFO("Esatto SN: %s Firmware version: %s", FirmwareTP[ESATTO_FIRMWARE_SN].getText(),
733  FirmwareTP[ESATTO_FIRMWARE_VERSION].getText());
734  }
735  else
736  return false;
737 
738  rc1 = m_Arco->getSerialNumber(serial);
739  rc2 = m_Arco->getFirmwareVersion(firmware);
740 
741  if (rc1 && rc2)
742  {
743  FirmwareTP[ARCO_FIRMWARE_SN].setText(serial);
744  FirmwareTP[ARCO_FIRMWARE_VERSION].setText(firmware);
745  LOGF_INFO("Arco SN: %s Firmware version: %s", FirmwareTP[ARCO_FIRMWARE_SN].getText(),
746  FirmwareTP[ARCO_FIRMWARE_VERSION].getText());
747  }
748  else
749  return false;
750 
751  return true;
752 }
753 
754 /************************************************************************************************************
755  *
756 *************************************************************************************************************/
757 void EsattoArco::setConnectionParams()
758 {
761 }
762 
763 /************************************************************************************************************
764  *
765 *************************************************************************************************************/
766 bool EsattoArco::initCommandSet()
767 {
768  struct termios tty_setting;
769  if (tcgetattr(PortFD, &tty_setting) == -1)
770  {
771  LOG_ERROR("setTTYFlags: failed getting tty attributes.");
772  return false;
773  }
774  tty_setting.c_lflag |= ICANON;
775  if (tcsetattr(PortFD, TCSANOW, &tty_setting))
776  {
777  LOG_ERROR("setTTYFlags: failed setting attributes on serial port.");
778  return false;
779  }
780 
781  m_Esatto.reset(new PrimalucaLabs::Esatto(getDeviceName(), PortFD));
782  m_Arco.reset(new PrimalucaLabs::Arco(getDeviceName(), PortFD));
783  return true;
784 }
785 
786 /************************************************************************************************************
787  *
788 *************************************************************************************************************/
790 {
791  Focuser::saveConfigItems(fp);
793  return true;
794 }
795 
796 /************************************************************************************************************
797  *
798 *************************************************************************************************************/
800 {
801  // Rotator move 0 to +180 degrees CCW
802  // Rotator move 0 to -180 degrees CW
803  // This is from looking at rotator from behind.
804  const bool isReversed = ReverseRotatorS[INDI_ENABLED].s == ISS_ON;
805  auto newAngle = 0;
806  if (isReversed)
807  newAngle = ( angle > 180 ? 360 - angle : angle * -1);
808  else
809  newAngle = ( angle > 180 ? angle - 360 : angle);
810 
811  if (m_Arco->moveAbsolutePoition(PrimalucaLabs::UNIT_DEGREES, newAngle))
812  return IPS_BUSY;
813  return IPS_ALERT;
814 }
815 
816 /************************************************************************************************************
817  *
818 *************************************************************************************************************/
820 {
821  auto rc = m_Arco->stop();
822  if (rc && RotatorAbsPosNP.s != IPS_IDLE)
823  {
824  RotatorAbsPosNP.s = IPS_IDLE;
826  IDSetNumber(&RotatorAbsPosNP, nullptr);
827  IDSetNumber(&GotoRotatorNP, nullptr);
828  }
829 
830  return rc;
831 }
832 
833 /************************************************************************************************************
834  *
835 *************************************************************************************************************/
836 bool EsattoArco::ReverseRotator(bool enabled)
837 {
838  // Do not use Primaluca native reverse since it has some bugs
839  //return m_Arco->reverse(enabled);
840  INDI_UNUSED(enabled);
841  GotoRotatorN[0].value = range360(360 - GotoRotatorN[0].value);
842  return true;
843 }
844 
845 /************************************************************************************************************
846  *
847 *************************************************************************************************************/
848 bool EsattoArco::SyncRotator(double angle)
849 {
850  const bool isReversed = ReverseRotatorS[INDI_ENABLED].s == ISS_ON;
851  auto newAngle = 0;
852  if (isReversed)
853  newAngle = ( angle > 180 ? 360 - angle : angle * -1);
854  else
855  newAngle = ( angle > 180 ? angle - 360 : angle);
856  return m_Arco->sync(PrimalucaLabs::UNIT_DEGREES, newAngle);
857 }
void setWordSize(const uint8_t &value)
setWordSize Set word size to be used in the serial connection. Default 8
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: esattoarco.cpp:388
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: esattoarco.cpp:593
const char * getDefaultName() override
Definition: esattoarco.cpp:249
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: esattoarco.cpp:557
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: esattoarco.cpp:175
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: esattoarco.cpp:571
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: esattoarco.cpp:59
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: esattoarco.cpp:585
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
Definition: esattoarco.cpp:799
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: esattoarco.cpp:527
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: esattoarco.cpp:789
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
Definition: esattoarco.cpp:836
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
Definition: esattoarco.cpp:319
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
Definition: esattoarco.cpp:848
virtual bool AbortRotator() override
AbortRotator Abort all motion.
Definition: esattoarco.cpp:819
virtual bool Handshake() override
perform handshake with device to check communication
Definition: esattoarco.cpp:234
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
INDI::PropertyText getText(const char *name) const
Definition: basedevice.cpp:94
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
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.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
uint16_t getDriverInterface() const
INumberVectorProperty FocusBacklashNP
virtual bool SetFocuserBacklashEnabled(bool enabled)
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
ISwitchVectorProperty FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INumberVectorProperty FocusMaxPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
INumber PresetN[3]
Definition: indifocuser.h:107
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.
Connection::Serial * serialConnection
Definition: indifocuser.h:116
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getName() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
INumberVectorProperty GotoRotatorNP
void initProperties(const char *groupName)
Initilize Rotator properties. It is recommended to call this function within initProperties() of your...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
ISwitchVectorProperty ReverseRotatorSP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
Provides interface to implement Rotator functionality.
a class to store JSON values
Definition: json.h:18647
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
Definition: json.h:19810
ValueType & get_to(ValueType &v) const noexcept(noexcept(JSONSerializer< ValueType >::from_json(std::declval< const basic_json_t & >(), v)))
get a value (explicit)
Definition: json.h:20336
general exception of the basic_json class
Definition: json.h:4032
const char * what() const noexcept override
returns the explanatory string
Definition: json.h:4035
const int id
the id of the exception
Definition: json.h:4041
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
#define ROTATOR_TAB
Definition: gemini.cpp:39
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_ATMOST1
Definition: indiapi.h:174
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition: indicom.c:1245
Implementations for common driver routines.
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 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
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUGetConfigOnSwitchIndex(const char *dev, const char *property, int *index)
IUGetConfigOnSwitchIndex Opens configuration file and reads single switch property to find ON switch ...
Definition: indidriver.c:711
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#define LOG_WARN(txt)
Definition: indilogger.h:73
#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 ENVIRONMENT_TAB
const char * CONNECTION_TAB
#define currentTemperature
Definition: robofocus.cpp:38
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250
struct termios tty_setting
Definition: stvdriver.c:51