Instrument Neutral Distributed Interface INDI 2.2.5
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
29#ifdef _USE_SYSTEM_JSONLIB
30#include <nlohmann/json.hpp>
31#else
32#include <indijson.hpp>
33#endif
34
35#include <cmath>
36#include <cstring>
37#include <memory>
38#include <algorithm>
39
40#include <assert.h>
41#include <termios.h>
42#include <unistd.h>
44#include <sys/ioctl.h>
45
46static std::unique_ptr<EsattoArco> esattoarco(new EsattoArco());
47
48static const char *ENVIRONMENT_TAB = "Environment";
49static const char *ROTATOR_TAB = "Rotator";
50
60
61/************************************************************************************************************
62 *
63*************************************************************************************************************/
65{
67
68 setConnectionParams();
69
70 // Firmware information
71 FirmwareTP[ESATTO_FIRMWARE_SN].fill("ESATTO_FIRMWARE_SN", "Esatto SN", "");
72 FirmwareTP[ESATTO_FIRMWARE_VERSION].fill("ESATTO_FIRMWARE_VERSION", "Esatto Firmware", "");
73 FirmwareTP[ARCO_FIRMWARE_SN].fill("ARCO_FIRMWARE_SN", "Arco SN", "");
74 FirmwareTP[ARCO_FIRMWARE_VERSION].fill("VERARCO_FIRMWARE_VERSIONSION", "Arco Firmware", "");
75 FirmwareTP.fill(getDeviceName(), "FOCUS_FIRMWARE", "Firmware", CONNECTION_TAB, IP_RO, 60, IPS_IDLE);
76
80
81 // Voltage Information
82 VoltageNP[VOLTAGE_12V].fill("VOLTAGE_12V", "12v", "%.2f", 0, 100, 0., 0.);
83 VoltageNP[VOLTAGE_USB].fill("VOLTAGE_USB", "USB", "%.2f", 0, 100, 0., 0.);
84 VoltageNP.fill(getDeviceName(), "VOLTAGE_IN", "Voltage in", ENVIRONMENT_TAB, IP_RO, 0, IPS_IDLE);
85
86 // Focuser temperature
87 TemperatureNP[TEMPERATURE_MOTOR].fill("TEMPERATURE", "Motor (c)", "%.2f", -50, 70., 0., 0.);
88 TemperatureNP[TEMPERATURE_EXTERNAL].fill("TEMPERATURE_ETX", "External (c)", "%.2f", -50, 70., 0., 0.);
89 TemperatureNP.fill(getDeviceName(), "FOCUS_TEMPERATURE", "Temperature", ENVIRONMENT_TAB,
90 IP_RO, 0, IPS_IDLE);
91
92 // Current Speed
93 SpeedNP[0].fill("SPEED", "steps/s", "%.f", 0, 7000., 1, 0);
94 SpeedNP.fill(getDeviceName(), "FOCUS_SPEED", "Motor Speed", MAIN_CONTROL_TAB, IP_RO, 0,
95 IPS_IDLE);
96
97 // Backlash measurement
98 BacklashMessageTP[0].fill("BACKLASH", "Backlash stage", "Press START to measure backlash.");
99 BacklashMessageTP.fill(getDeviceName(), "BACKLASH_MESSAGE", "Backlash",
101
102 // Backlash measurement stages
103 BacklashMeasurementSP[BACKLASH_START].fill("BACKLASH_START", "Start", ISS_OFF);
104 BacklashMeasurementSP[BACKLASH_NEXT].fill("BACKLASH_NEXT", "Next", ISS_OFF);
105 BacklashMeasurementSP.fill(getDeviceName(), "FOCUS_BACKLASH", "Backlash",
108
109 // Speed Moves
110 FastMoveSP[FASTMOVE_IN].fill("FASTMOVE_IN", "Move In", ISS_OFF);
111 FastMoveSP[FASTMOVE_OUT].fill("FASTMOVE_OUT", "Move out", ISS_OFF);
112 FastMoveSP[FASTMOVE_STOP].fill("FASTMOVE_STOP", "Stop", ISS_OFF);
113 FastMoveSP.fill(getDeviceName(), "FAST_MOVE", "Calibration Move", MAIN_CONTROL_TAB, IP_RW,
115
116 // Override the default Max. Position to make it Read-Only
117 FocusMaxPosNP.fill(getDeviceName(), "FOCUS_MAX", "Max. Position", MAIN_CONTROL_TAB, IP_RO,
118 0, IPS_IDLE);
119
124
126
127 // Rotator Ticks
128 RotatorAbsPosNP[0].fill("ROTATOR_ABSOLUTE_POSITION", "Ticks", "%.f", 0., 100000., 1000., 0.);
129 RotatorAbsPosNP.fill(getDeviceName(), "ABS_ROTATOR_POSITION", "Goto", ROTATOR_TAB, IP_RW,
130 0, IPS_IDLE );
131 // Rotator Calibration
132 RotatorCalibrationSP[ARCO_CALIBRATION_START].fill("ARCO_CALIBRATION_START", "Start", ISS_OFF);
133 RotatorCalibrationSP.fill(getDeviceName(), "ARCO_CALIBRATION", "Calibrate", ROTATOR_TAB,
135
136 // Read reverse rotator config
138
140 // Defaults
142
143 // Relative and absolute movement
144 FocusRelPosNP[0].setMin(0.);
145 FocusRelPosNP[0].setMax(50000.);
146 FocusRelPosNP[0].setValue(0);
147 FocusRelPosNP[0].setStep(1000);
148
149 FocusAbsPosNP[0].setMin(0.);
150 FocusAbsPosNP[0].setMax(200000.);
151 FocusAbsPosNP[0].setValue(0);
152 FocusAbsPosNP[0].setStep(1000);
153
154 FocusMaxPosNP[0].setValue(2097152);
155 PresetNP[0].setMax(FocusMaxPosNP[0].getValue());
156 PresetNP[1].setMax(FocusMaxPosNP[0].getValue());
157 PresetNP[2].setMax(FocusMaxPosNP[0].getValue());
158
159 FocusBacklashNP[0].setMin(0);
160 FocusBacklashNP[0].setMax(10000);
161 FocusBacklashNP[0].setStep(1);
162 FocusBacklashNP[0].setValue(0);
163
165
167
168 return true;
169}
170
171/************************************************************************************************************
172 *
173*************************************************************************************************************/
175{
176 if (isConnected() && updateMaxLimit() == false)
177 LOGF_WARN("Check you have the latest %s firmware. Focuser requires calibration.", getDeviceName());
178
179 if (isConnected())
180 {
181 if (getStartupValues())
182 LOGF_INFO("Parameters updated, %s ready for use.", getDeviceName());
183 else
184 LOG_WARN("Failed to inquire parameters. Check logs.");
185
186 //Focuser
188
189 defineProperty(SpeedNP);
190 defineProperty(BacklashMessageTP);
191 defineProperty(BacklashMeasurementSP);
192 defineProperty(FirmwareTP);
193
194 if (updateTemperature())
195 defineProperty(TemperatureNP);
196
197 if (updateVoltageIn())
198 defineProperty(VoltageNP);
199
200 // Rotator
202 defineProperty(RotatorAbsPosNP);
203 defineProperty(RotatorCalibrationSP);
204 defineProperty(&RotCalibrationMessageTP);
205 }
206 else
207 {
208 //Focuser
210
211 if (TemperatureNP.getState() == IPS_OK)
212 deleteProperty(TemperatureNP);
213
214 deleteProperty(FirmwareTP.getName());
215 deleteProperty(VoltageNP.getName());
216 deleteProperty(BacklashMessageTP);
217 deleteProperty(BacklashMeasurementSP);
218 deleteProperty(SpeedNP);
219
220 // Rotator
222 deleteProperty(RotatorAbsPosNP);
223 deleteProperty(RotatorCalibrationSP);
224 deleteProperty(RotCalibrationMessageTP.name);
225 }
226
227 return true;
228}
229
230/************************************************************************************************************
231 *
232*************************************************************************************************************/
234{
235 if (Ack())
236 {
237 LOGF_INFO("%s is online. Getting parameters...", getDeviceName());
238 return true;
239 }
240
241 LOG_INFO("Error retrieving data from device, please ensure focuser is powered and the port is correct.");
242 return false;
243}
244
245/************************************************************************************************************
246 *
247*************************************************************************************************************/
249{
250 return "Esatto Arco";
251}
252
253/************************************************************************************************************
254 *
255*************************************************************************************************************/
256bool EsattoArco::updateTemperature()
257{
258 double temperature = 0;
259 if (m_Esatto->getMotorTemp(temperature))
260 {
261 TemperatureNP[TEMPERATURE_MOTOR].setValue(temperature);
262 TemperatureNP.setState(IPS_OK);
263 }
264 else
265 TemperatureNP.setState(IPS_ALERT);
266
267 TemperatureNP[TEMPERATURE_EXTERNAL].setValue(-273.15);
268 if (m_Esatto->getExternalTemp(temperature) && temperature > -127)
269 {
270 TemperatureNP[TEMPERATURE_EXTERNAL].setValue(temperature);
271 }
272
273 return true;
274}
275
276/************************************************************************************************************
277 *
278*************************************************************************************************************/
279bool EsattoArco::updateMaxLimit()
280{
281 uint32_t maxLimit = 0;
282
283 if (m_Esatto->getMaxPosition(maxLimit) && maxLimit > 0)
284 {
285 FocusMaxPosNP[0].setMax(maxLimit);
286 if (FocusMaxPosNP[0].getValue() > maxLimit)
287 FocusMaxPosNP[0].setValue(maxLimit);
288
289 FocusAbsPosNP[0].setMin(0);
290 FocusAbsPosNP[0].setMax(maxLimit);
291 FocusAbsPosNP[0].setValue(0);
292 FocusAbsPosNP[0].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
293
294 FocusRelPosNP[0].setMin(0.);
295 FocusRelPosNP[0].setMax(FocusAbsPosNP[0].getStep());
296 FocusRelPosNP[0].setValue(0);
297 FocusRelPosNP[0].setStep(FocusAbsPosNP[0].getStep());
298
299 PresetNP[0].setMax(maxLimit);
300 PresetNP[0].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
301 PresetNP[1].setMax(maxLimit);
302 PresetNP[1].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
303 PresetNP[2].setMax(maxLimit);
304 PresetNP[2].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
305
306
308 return true;
309 }
310
312 return false;
313}
314
315/************************************************************************************************************
316 *
317*************************************************************************************************************/
319{
320 return m_Esatto->setBacklash(steps);
321}
322
323/************************************************************************************************************
324 *
325*************************************************************************************************************/
326bool EsattoArco::updatePosition()
327{
328 uint32_t steps;
329 // Update focuser position
330 if (m_Esatto->getAbsolutePosition(steps))
331 FocusAbsPosNP[0].setValue(steps);
332
333 double arcoPosition;
334 // Update Arco steps position
335 if (m_Arco->getAbsolutePosition(PrimalucaLabs::UNIT_STEPS, arcoPosition))
336 {
337 //Update Rotator Position
338 RotatorAbsPosNP[0].setValue(arcoPosition);
339 }
340
341 // Update Arco degrees position
342 if (m_Arco->getAbsolutePosition(PrimalucaLabs::UNIT_DEGREES, arcoPosition))
343 {
344 //Update Rotator Position
345 const bool isReversed = ReverseRotatorSP[INDI_ENABLED].getState() == ISS_ON;
346 if (isReversed)
347 GotoRotatorNP[0].setValue(range360(360 - arcoPosition));
348 else
349 GotoRotatorNP[0].setValue(range360(arcoPosition));
350
351 }
352
353 return true;
354}
355
356/************************************************************************************************************
357 *
358*************************************************************************************************************/
359bool EsattoArco::updateVoltageIn()
360{
361 double voltage;
362 if (m_Esatto->getVoltage12v(voltage))
363 VoltageNP[VOLTAGE_12V].setValue(voltage);
364
365 VoltageNP.setState((voltage >= 11.0) ? IPS_OK : IPS_ALERT);
366 if (m_Esatto->getVoltageUSB(voltage))
367 VoltageNP[VOLTAGE_USB].setValue(voltage);
368 return true;
369}
370
371/************************************************************************************************************
372 *
373*************************************************************************************************************/
374bool EsattoArco::isMotionComplete()
375{
376 uint32_t speed;
377 if (m_Esatto->getCurrentSpeed(speed))
378 {
379 return speed == 0;
380 }
381 return false;
382}
383
384/************************************************************************************************************
385 *
386*************************************************************************************************************/
387bool EsattoArco::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
388{
389 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
390 {
391 // Set backlash
392 if (BacklashMeasurementSP.isNameMatch(name))
393 {
394 BacklashMeasurementSP.setState(IPS_BUSY);
395 BacklashMeasurementSP.update(states, names, n);
396
397 auto current_switch = BacklashMeasurementSP.findOnSwitchIndex();
398 BacklashMeasurementSP[current_switch].setState(ISS_ON);
399 BacklashMeasurementSP.apply();
400
401 if (current_switch == BACKLASH_START)
402 {
403 if (bStage == BacklashIdle || bStage == BacklashComplete )
404 {
405 // Start the backlash measurement process
406 LOG_INFO("Start Backlash measurement.");
407 BacklashMeasurementSP.setState(IPS_BUSY);
408 BacklashMeasurementSP.apply();
409
410 BacklashMessageTP[0].setText("Drive the focuser in any direction until focus changes.");
411 BacklashMessageTP.apply();
412
413 // Set next step
414 bStage = BacklashMinimum;
415 }
416 else
417 {
418 LOG_INFO("Already started backlash measure. Proceed to next step.");
419 BacklashMessageTP[0].setText("Already started. Proceed to NEXT.");
420 BacklashMessageTP.apply();
421 }
422 }
423 else if (current_switch == BACKLASH_NEXT)
424 {
425 if (bStage == BacklashMinimum)
426 {
427 FocusBacklashNP[0].setValue(static_cast<int32_t>(FocusAbsPosNP[0].getValue()));
428
429 BacklashMessageTP[0].setText( "Drive the focuser in the opposite direction, then press NEXT to finish.");
430 BacklashMessageTP.apply();
431 bStage = BacklashMaximum;
432 }
433 else if (bStage == BacklashMaximum)
434 {
435 FocusBacklashNP[0].setValue(FocusBacklashNP[0].getValue() - FocusAbsPosNP[0].getValue());
436
437 // Set Esatto backlash
438 SetFocuserBacklash(FocusBacklashNP[0].getValue());
440
442
443 BacklashMessageTP[0].setText("Backlash Measure Completed.");
444 BacklashMessageTP.apply();
445
446 bStage = BacklashComplete;
447
448 LOG_INFO("Backlash measurement completed");
449 BacklashMeasurementSP.setState(IPS_OK);
450 BacklashMeasurementSP.apply();
451 BacklashMeasurementSP[current_switch].setState(ISS_OFF);
452 BacklashMeasurementSP.apply();
453 }
454 else
455 {
456 BacklashMessageTP[0].setText("Backlash not in progress.");
457 BacklashMessageTP.apply();
458 }
459
460 }
461 return true;
462 }
463 // Fast motion
464 else if (FastMoveSP.isNameMatch(name))
465 {
466 FastMoveSP.update(states, names, n);
467 auto current_switch = FastMoveSP.findOnSwitchIndex();
468
469 switch (current_switch)
470 {
471 case FASTMOVE_IN:
472 if (!m_Esatto->fastMoveIn())
473 return false;
474 FastMoveSP.setState(IPS_BUSY);
475 break;
476 case FASTMOVE_OUT:
477 if (!m_Esatto->fastMoveOut())
478 return false;
479 FastMoveSP.setState(IPS_BUSY);
480 break;
481 case FASTMOVE_STOP:
482 if (!m_Esatto->stop())
483 return false;
484 FastMoveSP.setState(IPS_IDLE);
485 break;
486 default:
487 break;
488 }
489
490 FastMoveSP.apply();
491 return true;
492 }
493 // Rotator Calibration
494 else if (RotatorCalibrationSP.isNameMatch(name))
495 {
496 if(m_Arco->calibrate())
497 {
498 LOG_INFO("Calibrating Arco. Please wait.");
499 RotatorAbsPosNP.setState(IPS_BUSY);
501 RotatorCalibrationSP.setState(IPS_BUSY);
502 RotatorCalibrationSP.apply();
504 RotatorAbsPosNP.apply();
505 }
506 else
507 {
508 RotatorCalibrationSP.reset();
509 RotatorCalibrationSP.setState(IPS_ALERT);
510 RotatorCalibrationSP.apply();
511 }
512 return true;
513 }
514 else if (strstr(name, "ROTATOR"))
515 {
516 if (INDI::RotatorInterface::processSwitch(dev, name, states, names, n))
517 return true;
518 }
519 }
520 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
521}
522
523/************************************************************************************************************
524 *
525*************************************************************************************************************/
526bool EsattoArco::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
527{
528 if (dev == nullptr || strcmp(dev, getDeviceName()) != 0)
529 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
530
531 else if (RotatorAbsPosNP.isNameMatch(name))
532 {
533 if (m_Arco->moveAbsolutePoition(PrimalucaLabs::UNIT_STEPS, values[0]))
534 RotatorAbsPosNP.setState(IPS_BUSY);
535 else
536 RotatorAbsPosNP.setState(IPS_ALERT);
537 GotoRotatorNP.setState(RotatorAbsPosNP.getState());
538 RotatorAbsPosNP.apply();
540 if (RotatorAbsPosNP.getState() == IPS_BUSY)
541 LOGF_INFO("Rotator moving to %.f steps...", values[0]);
542 return true;
543 }
544 else if (strstr(name, "ROTATOR"))
545 {
546 if (INDI::RotatorInterface::processNumber(dev, name, values, names, n))
547 return true;
548 }
549
550 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
551}
552
553/************************************************************************************************************
554 *
555*************************************************************************************************************/
557{
558 if (m_Esatto->goAbsolutePosition(targetTicks))
559 {
560 RotatorAbsPosNP.setState(IPS_BUSY);
561 RotatorAbsPosNP.apply();
562 return IPS_BUSY;
563 }
564 return IPS_ALERT;
565}
566
567/************************************************************************************************************
568 *
569*************************************************************************************************************/
571{
572 int reversed = (FocusReverseSP.findOnSwitchIndex() == INDI_ENABLED) ? -1 : 1;
573 int relativeTicks = ((dir == FOCUS_INWARD) ? -ticks : ticks) * reversed;
574 double newPosition = FocusAbsPosNP[0].getValue() + relativeTicks;
575
576 bool rc = MoveAbsFocuser(newPosition);
577
578 return (rc ? IPS_BUSY : IPS_ALERT);
579}
580
581/************************************************************************************************************
582 *
583*************************************************************************************************************/
585{
586 return m_Esatto->stop();
587}
588
589/************************************************************************************************************
590 *
591*************************************************************************************************************/
593{
594 if (!isConnected())
595 {
597 return;
598 }
599
600 auto currentFocusPosition = FocusAbsPosNP[0].getValue();
601 auto currentRotatorPosition = RotatorAbsPosNP[0].getValue();
602 if (updatePosition())
603 {
604 if (std::abs(currentFocusPosition - FocusAbsPosNP[0].getValue()) > 0)
605 {
606 // Focuser State Machine
607 if (FocusAbsPosNP.getState() == IPS_BUSY && m_Esatto->isBusy() == false)
608 {
613 }
614 else
616 }
617
618 // Rotator State Machine
619
620 // Only check status if position changed.
621 if (std::abs(currentRotatorPosition - RotatorAbsPosNP[0].getValue()) > 0)
622 {
623 // Rotator was busy and now stopped?
624 if (GotoRotatorNP.getState() == IPS_BUSY && m_Arco->isBusy() == false)
625 {
626 // Check if we were calibrating
627 if(RotatorCalibrationSP.getState() == IPS_BUSY)
628 {
629 RotatorCalibrationSP.setState(IPS_IDLE);
630 RotatorCalibrationSP.apply();
631 LOG_INFO("Arco calibration complete.");
632 if(m_Arco->sync(PrimalucaLabs::UNIT_STEPS, 0))
633 LOG_INFO("Arco position synced to zero.");
634 }
636 RotatorAbsPosNP.setState(IPS_OK);
638 RotatorAbsPosNP.apply();
639 }
640 else
641 {
643 RotatorAbsPosNP.apply();
644 }
645 }
646 }
647
648 if (m_TemperatureCounter++ == TEMPERATURE_FREQUENCY)
649 {
650 auto currentTemperature = TemperatureNP[0].getValue();
651 if (updateTemperature())
652 {
653 if (std::abs(currentTemperature - TemperatureNP[0].getValue()) >= 0.1)
654 TemperatureNP.apply();
655 }
656
657 auto current12V = VoltageNP[VOLTAGE_12V].getValue();
658 auto currentUSB = VoltageNP[VOLTAGE_USB].getValue();
659 if (updateVoltageIn())
660 {
661 if (std::abs(current12V - VoltageNP[VOLTAGE_12V].getValue()) >= 0.1 ||
662 std::abs(currentUSB - VoltageNP[VOLTAGE_USB].getValue()) >= 0.1)
663 {
664 VoltageNP.apply();
665 if (VoltageNP[VOLTAGE_12V].getValue() < 11.0)
666 LOG_WARN("Please check 12v DC power supply is connected.");
667 }
668 }
669
670 m_TemperatureCounter = 0; // Reset the counter
671 }
672
674}
675
676/************************************************************************************************************
677 *
678*************************************************************************************************************/
679bool EsattoArco::getStartupValues()
680{
681 updatePosition();
682 json info;
683 if (m_Arco->getMotorInfo(info))
684 {
685 int calMax, calMin;
686 try
687 {
688 info["get"]["MOT2"]["CAL_MAXPOS"].get_to(calMax);
689 info["get"]["MOT2"]["CAL_MINPOS"].get_to(calMin);
690 }
691 catch (json::exception &e)
692 {
693 // output exception information
694 LOGF_ERROR("Failed to parse info: %s Exception: %s id: %d", info.dump().c_str(),
695 e.what(), e.id);
696 return false;
697 }
698 RotatorAbsPosNP[0].setMin(calMin);
699 RotatorAbsPosNP[0].setMax(calMax);
700 RotatorAbsPosNP[0].setStep(std::abs(calMax - calMin) / 50.0);
701 }
702 return true;
703}
704
705/************************************************************************************************************
706 *
707*************************************************************************************************************/
708bool EsattoArco::Ack()
709{
710 std::string serial, firmware;
711
712 if(initCommandSet() == false)
713 {
714 LOG_ERROR("Failed setting attributes on serial port and init command sets");
715 return false;
716 }
717
718 if (m_Arco->setEnabled(true) && !m_Arco->isEnabled())
719 {
720 LOG_ERROR("Failed to enable ARCO rotator. Please check it is powered and connected.");
721 return false;
722 }
723
724 bool rc1 = m_Esatto->getSerialNumber(serial);
725 bool rc2 = m_Esatto->getFirmwareVersion(firmware);
726
727 if (rc1 && rc2)
728 {
729 FirmwareTP[ESATTO_FIRMWARE_SN].setText(serial);
730 FirmwareTP[ESATTO_FIRMWARE_VERSION].setText(firmware);
731 LOGF_INFO("Esatto SN: %s Firmware version: %s", FirmwareTP[ESATTO_FIRMWARE_SN].getText(),
732 FirmwareTP[ESATTO_FIRMWARE_VERSION].getText());
733 }
734 else
735 return false;
736
737 rc1 = m_Arco->getSerialNumber(serial);
738 rc2 = m_Arco->getFirmwareVersion(firmware);
739
740 if (rc1 && rc2)
741 {
742 FirmwareTP[ARCO_FIRMWARE_SN].setText(serial);
743 FirmwareTP[ARCO_FIRMWARE_VERSION].setText(firmware);
744 LOGF_INFO("Arco SN: %s Firmware version: %s", FirmwareTP[ARCO_FIRMWARE_SN].getText(),
745 FirmwareTP[ARCO_FIRMWARE_VERSION].getText());
746 }
747 else
748 return false;
749
750 return true;
751}
752
753/************************************************************************************************************
754 *
755*************************************************************************************************************/
756void EsattoArco::setConnectionParams()
757{
760}
761
762/************************************************************************************************************
763 *
764*************************************************************************************************************/
765bool EsattoArco::initCommandSet()
766{
767 struct termios tty_setting;
768 if (tcgetattr(PortFD, &tty_setting) == -1)
769 {
770 LOG_ERROR("setTTYFlags: failed getting tty attributes.");
771 return false;
772 }
773 tty_setting.c_lflag |= ICANON;
774 if (tcsetattr(PortFD, TCSANOW, &tty_setting))
775 {
776 LOG_ERROR("setTTYFlags: failed setting attributes on serial port.");
777 return false;
778 }
779
780 m_Esatto.reset(new PrimalucaLabs::Esatto(getDeviceName(), PortFD));
781 m_Arco.reset(new PrimalucaLabs::Arco(getDeviceName(), PortFD));
782 return true;
783}
784
785/************************************************************************************************************
786 *
787*************************************************************************************************************/
789{
790 Focuser::saveConfigItems(fp);
792 return true;
793}
794
795/************************************************************************************************************
796 *
797*************************************************************************************************************/
799{
800 // Rotator move 0 to +180 degrees CCW
801 // Rotator move 0 to -180 degrees CW
802 // This is from looking at rotator from behind.
803 const bool isReversed = ReverseRotatorSP[INDI_ENABLED].getState() == ISS_ON;
804 auto newAngle = 0;
805 if (isReversed)
806 newAngle = ( angle > 180 ? 360 - angle : angle * -1);
807 else
808 newAngle = ( angle > 180 ? angle - 360 : angle);
809
810 if (m_Arco->moveAbsolutePoition(PrimalucaLabs::UNIT_DEGREES, newAngle))
811 return IPS_BUSY;
812 return IPS_ALERT;
813}
814
815/************************************************************************************************************
816 *
817*************************************************************************************************************/
819{
820 auto rc = m_Arco->stop();
821 if (rc && RotatorAbsPosNP.getState() != IPS_IDLE)
822 {
823 RotatorAbsPosNP.setState(IPS_IDLE);
825 RotatorAbsPosNP.apply();
827 }
828
829 return rc;
830}
831
832/************************************************************************************************************
833 *
834*************************************************************************************************************/
836{
837 // Do not use Primaluca native reverse since it has some bugs
838 //return m_Arco->reverse(enabled);
839 INDI_UNUSED(enabled);
840 GotoRotatorNP[0].setValue(range360(360 - GotoRotatorNP[0].getValue()));
841 return true;
842}
843
844/************************************************************************************************************
845 *
846*************************************************************************************************************/
847bool EsattoArco::SyncRotator(double angle)
848{
849 const bool isReversed = ReverseRotatorSP[INDI_ENABLED].getState() == ISS_ON;
850 auto newAngle = 0;
851 if (isReversed)
852 newAngle = ( angle > 180 ? 360 - angle : angle * -1);
853 else
854 newAngle = ( angle > 180 ? angle - 360 : angle);
855 return m_Arco->sync(PrimalucaLabs::UNIT_DEGREES, newAngle);
856}
double temperature
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
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
const char * getDefaultName() override
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool saveConfigItems(FILE *fp) override
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
virtual bool AbortRotator() override
AbortRotator Abort all motion.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
bool isConnected() const
INDI::PropertyText getText(const char *name) const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
uint32_t getDriverInterface() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INDI::PropertyNumber FocusRelPosNP
INDI::PropertySwitch FocusReverseSP
virtual bool SetFocuserBacklashEnabled(bool enabled)
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusBacklashNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
Connection::Serial * serialConnection
INDI::PropertyNumber PresetNP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, 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
void initProperties(const char *groupName)
Initialize Rotator properties. It is recommended to call this function within initProperties() of you...
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.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
INDI::PropertySwitch ReverseRotatorSP
INDI::PropertyNumber GotoRotatorNP
Provides interface to implement Rotator functionality.
a class to store JSON values
ValueType & get_to(ValueType &v) const noexcept(noexcept(JSONSerializer< ValueType >::from_json(std::declval< const basic_json_t & >(), v)))
get a value (explicit)
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
general exception of the basic_json class
const int id
the id of the exception
const char * what() const noexcept override
returns the explanatory string
const char * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
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:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_ATMOST1
Definition indiapi.h:172
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition indicom.c:1321
Implementations for common driver routines.
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#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 getDeviceName
#define ENVIRONMENT_TAB
char name[MAXINDINAME]
Definition indiapi.h:248
struct termios tty_setting
Definition stvdriver.c:51