Instrument Neutral Distributed Interface INDI 2.2.5
scopedome_dome.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 ScopeDome Dome INDI Driver
3
4 Copyright(c) 2017-2024 Jarno Paananen. All rights reserved.
5
6 based on:
7
8 ScopeDome Windows ASCOM driver version 5.1.30
9
10 and
11
12 Baader Planetarium Dome INDI Driver
13
14 Copyright(c) 2014 Jasem Mutlaq. All rights reserved.
15
16 Baader Dome INDI Driver
17
18 This library is free software; you can redistribute it and/or
19 modify it under the terms of the GNU Library General Public
20 License version 2 as published by the Free Software Foundation.
21 .
22 This library is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 Library General Public License for more details.
26 .
27 You should have received a copy of the GNU Library General Public License
28 along with this library; see the file COPYING.LIB. If not, write to
29 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 Boston, MA 02110-1301, USA.
31*******************************************************************************/
32
33#include "scopedome_dome.h"
34#include "scopedome_arduino.h"
35#include "scopedome_sim.h"
36#include "scopedome_usb21.h"
37
39#include "indicom.h"
40
41#include <cmath>
42#include <cstdio>
43#include <cstring>
44#include <memory>
45#include <termios.h>
46#include <unistd.h>
47#include <wordexp.h>
48
49// We declare an auto pointer to ScopeDome.
50std::unique_ptr<ScopeDome> scopeDome(new ScopeDome());
51
53{
54 setVersion(2, 1);
55 targetAz = 0;
60
62
63 stepsPerRevolution = ~0;
64
65 // Load dome inertia table if present
66 wordexp_t wexp;
67 if (wordexp("~/.indi/ScopeDome_DomeInertia_Table.txt", &wexp, 0) == 0)
68 {
69 FILE *inertia = fopen(wexp.we_wordv[0], "r");
70 if (inertia)
71 {
72 // skip UTF-8 marker bytes
73 fseek(inertia, 3, SEEK_SET);
74 char line[100];
75 int lineNum = 0;
76
77 while (fgets(line, sizeof(line), inertia))
78 {
79 int step, result;
80 // File format is "steps ; actual steps ;" but with varying number of spaces
81 char* splitter = strchr(line, ';');
82 if(splitter)
83 {
84 *splitter++ = 0;
85
86 step = atoi(line);
87 result = atoi(splitter);
88 if (step == lineNum)
89 {
90 inertiaTable.push_back(result);
91 }
92 }
93 lineNum++;
94 }
95 fclose(inertia);
96 LOGF_INFO("Read inertia file %s", wexp.we_wordv[0]);
97 }
98 else
99 {
100 LOG_WARN("Could not read inertia file, please generate one with Windows "
101 "driver setup and copy to "
102 "~/.indi/ScopeDome_DomeInertia_Table.txt");
103 }
104 }
105 wordfree(&wexp);
106}
107
109{
111
112 DomeHomePositionNP[0].fill("DH_POSITION", "AZ (deg)", "%6.2f", 0.0, 360.0, 1.0, 0.0);
113 DomeHomePositionNP.fill(getDeviceName(), "DOME_HOME_POSITION",
114 "Home sensor position", SITE_TAB, IP_RW, 60, IPS_OK);
115
116 HomeSensorPolaritySP[ScopeDomeCard::ACTIVE_HIGH].fill("ACTIVE_HIGH", "Active high", ISS_ON);
117 HomeSensorPolaritySP[ScopeDomeCard::ACTIVE_LOW].fill("ACTIVE_LOW", "Active low", ISS_OFF);
118 HomeSensorPolaritySP.fill(getDeviceName(), "HOME_SENSOR_POLARITY",
119 "Home sensor polarity", SITE_TAB, IP_RW, ISR_1OFMANY, 0, IPS_OK);
120
121 FindHomeSP[0].fill("START", "Start", ISS_OFF);
122 FindHomeSP.fill(getDeviceName(), "FIND_HOME", "Find home", MAIN_CONTROL_TAB, IP_RW,
123 ISR_ATMOST1, 0, IPS_OK);
124
125 DerotateSP[0].fill("START", "Start", ISS_OFF);
126 DerotateSP.fill(getDeviceName(), "DEROTATE", "Derotate", MAIN_CONTROL_TAB, IP_RW,
127 ISR_ATMOST1, 0, IPS_OK);
128
129 FirmwareVersionsNP[0].fill("MAIN", "Main part", "%2.2f", 0.0, 99.0, 1.0, 0.0);
130 FirmwareVersionsNP[1].fill("ROTARY", "Rotary part", "%2.1f", 0.0, 99.0, 1.0, 0.0);
131 FirmwareVersionsNP.fill(getDeviceName(), "FIRMWARE_VERSION",
132 "Firmware versions", INFO_TAB, IP_RO, 60, IPS_IDLE);
133
134 StepsPerRevolutionNP[0].fill("STEPS", "Steps per revolution", "%5.0f", 0.0, 99999.0, 1.0, 0.0);
135 StepsPerRevolutionNP.fill(getDeviceName(), "CALIBRATION_VALUES",
136 "Calibration values", SITE_TAB, IP_RO, 60, IPS_IDLE);
137
138 CalibrationNeededSP[0].fill("CALIBRATION_NEEDED", "Calibration needed", ISS_OFF);
139 CalibrationNeededSP.fill(getDeviceName(), "CALIBRATION_STATUS",
140 "Calibration status", SITE_TAB, IP_RO, ISR_ATMOST1, 0, IPS_IDLE);
141
142 StartCalibrationSP[0].fill("START", "Start", ISS_OFF);
143 StartCalibrationSP.fill(getDeviceName(), "RUN_CALIBRATION", "Run calibration",
145
146 char defaultUsername[MAXINDINAME] = "scopedome";
147 char defaultPassword[MAXINDINAME] = "default";
148 IUGetConfigText(getDeviceName(), "CREDENTIALS", "USERNAME", defaultUsername, MAXINDINAME);
149 IUGetConfigText(getDeviceName(), "CREDENTIALS", "PASSWORD", defaultPassword, MAXINDINAME);
150 CredentialsTP[0].fill("USERNAME", "User name", defaultUsername);
151 CredentialsTP[1].fill("PASSWORD", "Password", defaultPassword);
152 CredentialsTP.fill(getDeviceName(), "CREDENTIALS", "Credentials", CONNECTION_TAB, IP_RW, 0, IPS_OK);
153
154 ISState usbcard21 = ISS_ON;
155 ISState arduino = ISS_OFF;
156 IUGetConfigSwitch(getDeviceName(), "CARD_TYPE", "USB_CARD_21", &usbcard21);
157 IUGetConfigSwitch(getDeviceName(), "CARD_TYPE", "ARDUINO", &arduino);
158 CardTypeSP[SCOPEDOME_USB21].fill("USB_CARD_21", "USB Card 2.1", usbcard21);
159 CardTypeSP[SCOPEDOME_ARDUINO].fill("ARDUINO", "Arduino Card", arduino);
160 CardTypeSP.fill(getDeviceName(), "CARD_TYPE", "Card type",
162
164 {
165 case SCOPEDOME_USB21:
166 default:
168 break;
171 break;
172 }
173
176
179
180 setPollingPeriodRange(1000, 3000); // Device doesn't like too long interval
182 return true;
183}
184
185/************************************************************************************
186 *
187 * ***********************************************************************************/
189{
190 targetAz = 0;
191
192 stepsPerRevolution = interface->getStepsPerRevolution();
193 LOGF_INFO("Steps per revolution read as %d", stepsPerRevolution);
194 StepsPerRevolutionNP[0].setValue(stepsPerRevolution);
197
198 if (UpdatePosition())
200
203
206
207 if (InitPark())
208 {
209 // If loading parking data is successful, we just set the default parking
210 // values.
212 }
213 else
214 {
215 // Otherwise, we set all parking data to default in case no parking data is
216 // found.
217 SetAxis1Park(0);
219 }
220
221 bool calibrationNeeded = interface->isCalibrationNeeded();
222 CalibrationNeededSP[0].setState(calibrationNeeded ? ISS_ON : ISS_OFF);
225
226 double fwMain, fwRotary;
227 interface->getFirmwareVersions(fwMain, fwRotary);
228 FirmwareVersionsNP[0].setValue(fwMain);
229 FirmwareVersionsNP[1].setValue(fwRotary);
232
233 rotaryLinkEstablished = interface->getInputState(ScopeDomeCard::ROTARY_LINK);
234 if(rotaryLinkEstablished == ISS_OFF)
235 {
236 LOG_WARN("Rotary link not established, shutter control not possible");
237 }
238 return true;
239}
240
241/************************************************************************************
242 *
243 ************************************************************************************/
245{
246 if(reconnecting)
247 return true;
248
249 sim = isSimulation();
250
251 if (sim)
252 {
253 interface.reset(static_cast<ScopeDomeCard *>(new ScopeDomeSim()));
254 }
255 else
256 {
258 {
259 case SCOPEDOME_USB21:
260 default:
261 interface.reset(static_cast<ScopeDomeCard *>(new ScopeDomeUSB21(this, PortFD)));
262 break;
264 interface.reset(static_cast<ScopeDomeCard *>(new ScopeDomeArduino(this, getActiveConnection())));
265 break;
266 }
267 }
268 if (interface->detect())
269 {
270 return true;
271 }
272 LOG_ERROR("Can't identify the card, check card type and baud rate (115200 for USB Card 2.1, 9600 for Arduino Card)");
273 return false;
274}
275
276/************************************************************************************
277 *
278 ************************************************************************************/
280{
281 return (const char *)"ScopeDome Dome";
282}
283
284/************************************************************************************
285 *
286 ************************************************************************************/
288{
290
291 if (isConnected())
292 {
293 // Initialize dynamic properties
294 {
295 size_t numSensors = interface->getNumberOfSensors();
296 SensorsNP.resize(numSensors);
297 for(size_t i = 0; i < numSensors; i++)
298 {
299 auto info = interface->getSensorInfo(i);
300 SensorsNP[i].fill(info.propName.c_str(), info.label.c_str(),
301 info.format.c_str(), info.minValue, info.maxValue, 1.0, 0.0);
302 }
303 SensorsNP.fill(getDeviceName(), "SCOPEDOME_SENSORS",
304 "Sensors", INFO_TAB, IP_RO, 60, IPS_IDLE);
305 }
306
307 {
308 size_t numRelays = interface->getNumberOfRelays();
309 RelaysSP.resize(numRelays);
310 for(size_t i = 0; i < numRelays; i++)
311 {
312 auto info = interface->getRelayInfo(i);
313 RelaysSP[i].fill(info.propName.c_str(), info.label.c_str(), ISS_OFF);
314 }
315 RelaysSP.fill(getDeviceName(), "RELAYS", "Relays", MAIN_CONTROL_TAB, IP_RW,
317 0, IPS_IDLE);
318 }
319
320 {
321 size_t numInputs = interface->getNumberOfInputs();
322 InputsSP.resize(numInputs);
323 for(size_t i = 0; i < numInputs; i++)
324 {
325 auto info = interface->getInputInfo(i);
326 InputsSP[i].fill(info.propName.c_str(), info.label.c_str(), ISS_OFF);
327 }
328 InputsSP.fill(getDeviceName(), "INPUTS", "Inputs", INFO_TAB, IP_RO,
330 }
331
343 SetupParms();
344 }
345 else
346 {
358 }
359
360 return true;
361}
362
363/************************************************************************************
364 *
365 * ***********************************************************************************/
366bool ScopeDome::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
367{
368 // ignore if not ours
369 if (dev == nullptr || strcmp(dev, getDeviceName()))
370 return false;
371
372 if (FindHomeSP.isNameMatch(name))
373 {
374 if (status != DOME_HOMING)
375 {
376 LOG_INFO("Finding home sensor");
382 interface->findHome();
383 }
384 return true;
385 }
386
387 if (DerotateSP.isNameMatch(name))
388 {
389 if (status != DOME_DEROTATING)
390 {
391 LOG_INFO("De-rotating started");
397 }
398 return true;
399 }
400
402 {
404 {
405 LOG_INFO("Calibration started");
411 interface->calibrate();
412 }
413 return true;
414 }
415
416 if (RelaysSP.isNameMatch(name))
417 {
418 RelaysSP.update(states, names, n);
419 for(int i = 0; i < n; i++)
420 {
421 interface->setRelayState(i, RelaysSP[i].getState());
422 }
423 RelaysSP.apply();
424 return true;
425 }
426
427 if (CardTypeSP.isNameMatch(name))
428 {
429 CardTypeSP.update(states, names, n);
431
432 if(CardTypeSP[0].getState() == ISS_ON)
433 {
434 // USB Card 2.1 uses 115200 bauds
437 }
438 else
439 {
440 // Arduino Card uses 9600 bauds
443 }
444 return true;
445 }
447 {
448 HomeSensorPolaritySP.update(states, names, n);
450
452 {
453 interface->setHomeSensorPolarity(ScopeDomeCard::ACTIVE_LOW);
454 LOG_DEBUG("Home sensor polarity set to ACTIVE_LOW");
455 }
456 else
457 {
458 interface->setHomeSensorPolarity(ScopeDomeCard::ACTIVE_HIGH);
459 LOG_DEBUG("Home sensor polarity set to ACTIVE_HIGH");
460 }
461 return true;
462 }
463
464 // Pass to base class
465 return INDI::Dome::ISNewSwitch(dev, name, states, names, n);
466}
467
468bool ScopeDome::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
469{
470 // ignore if not ours
471 if (dev == nullptr || strcmp(dev, getDeviceName()))
472 return false;
473
475 {
476 DomeHomePositionNP.update(values, names, n);
479 LOGF_DEBUG("Dome home position set to %f", DomeHomePositionNP[0].getValue());
480 return true;
481 }
482 return INDI::Dome::ISNewNumber(dev, name, values, names, n);
483}
484
485bool ScopeDome::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
486{
487 // ignore if not ours
488 if (dev == nullptr || strcmp(dev, getDeviceName()))
489 return false;
490
491 if (CredentialsTP.isNameMatch(name))
492 {
493 CredentialsTP.update(texts, names, n);
495 return true;
496 }
497 return INDI::Dome::ISNewText(dev, name, texts, names, n);
498}
499
500/************************************************************************************
501 *
502 * ***********************************************************************************/
504{
505 int rc = 0;
506 if (rc != 0)
507 {
508 LOGF_ERROR("Error reading input state: %d", rc);
509 return false;
510 }
511
512 for(size_t i = 0; i < InputsSP.size(); i++)
513 {
514 InputsSP[i].setState(interface->getInputValue(i) ? ISS_ON : ISS_OFF);
515 }
517 InputsSP.apply();
518
520 {
521 if (interface->getInputState(ScopeDomeCard::OPEN1) == ISS_ON) // shutter open switch triggered
522 {
524 {
526 interface->controlShutter(ScopeDomeCard::STOP_SHUTTER);
528 {
529 SetParked(false);
530 }
532 }
533 }
534 else if (interface->getInputState(ScopeDomeCard::CLOSED1) == ISS_ON) // shutter closed switch triggered
535 {
537 {
539 interface->controlShutter(ScopeDomeCard::STOP_SHUTTER);
540
542 {
543 SetParked(true);
544 }
546 }
547 }
548 }
549 else
550 {
551 if (interface->getInputState(ScopeDomeCard::OPEN1) == ISS_ON) // shutter open switch triggered
552 {
554 }
555 else if (interface->getInputState(ScopeDomeCard::CLOSED1) == ISS_ON) // shutter closed switch triggered
556 {
558 }
559 }
560
561 ISState link = interface->getInputState(ScopeDomeCard::ROTARY_LINK);
562 if (link != rotaryLinkEstablished)
563 {
564 rotaryLinkEstablished = link;
565 if(rotaryLinkEstablished == ISS_OFF)
566 {
567 LOG_WARN("Rotary link not established, shutter control not possible");
568 }
569 else
570 {
571 LOG_WARN("Rotary link established, shutter control now possible");
572
573 if (reconnected && m_ShutterState == SHUTTER_MOVING)
574 {
575 // We have just reconnected the serial port, possibly due to controller reset.
576 // If we were shutting or opening the shutter, retry that operation now that we have shutter link.
578 {
579 LOG_WARN("Retrying closing shutter");
580 interface->controlShutter(ScopeDomeCard::CLOSE_SHUTTER);
581 }
582 else if (targetShutter == SHUTTER_OPEN)
583 {
584 LOG_WARN("Retrying opening shutter");
585 interface->controlShutter(ScopeDomeCard::OPEN_SHUTTER);
586 }
587 reconnected = false;
588 }
589 }
590 }
591 return true;
592}
593
594/************************************************************************************
595 *
596 * ***********************************************************************************/
598{
599 rotationCounter = interface->getRotationCounter();
600
601 // We assume counter value 0 is at home sensor position and grows counter clockwise as this is
602 // how USB Card 2.1 works.
603 double az = ((double)rotationCounter * -360.0 / stepsPerRevolution) + DomeHomePositionNP[0].getValue();
604 az = fmod(az, 360.0);
605 if (az < 0.0)
606 {
607 az += 360.0;
608 }
609 DomeAbsPosNP[0].setValue(az);
610 LOGF_DEBUG("Dome position %f, step count %d", az, rotationCounter);
611 return true;
612}
613
614/************************************************************************************
615 *
616 * ***********************************************************************************/
618{
619 for (size_t i = 0; i < SensorsNP.size(); ++i)
620 {
621 SensorsNP[i].setValue(interface->getSensorValue(i));
622 }
623 // SensorsN[10].value = getDewPoint(SensorsN[8].value, SensorsN[7].value);
626 return true;
627}
628
629/************************************************************************************
630 *
631 * ***********************************************************************************/
633{
634 for(size_t i = 0; i < RelaysSP.size(); i++)
635 {
636 RelaysSP[i].setState(interface->getRelayState(i));
637 }
639 RelaysSP.apply();
640 return true;
641}
642
643/************************************************************************************
644 *
645 * ***********************************************************************************/
647{
648 if (!isConnected())
649 return; // No need to reset timer if we are not connected anymore
650
651 interface->updateState();
652
653 currentStatus = interface->getStatus();
654
658
660
661 if (status == DOME_HOMING)
662 {
663 if ((currentStatus & (ScopeDomeCard::STATUS_HOMING | ScopeDomeCard::STATUS_MOVING)) == 0)
664 {
665 double azDiff = DomeHomePositionNP[0].getValue() - DomeAbsPosNP[0].getValue();
666
667 if (azDiff > 180)
668 {
669 azDiff -= 360;
670 }
671 if (azDiff < -180)
672 {
673 azDiff += 360;
674 }
675
676 if (interface->getInputState(ScopeDomeCard::HOME) || fabs(azDiff) <= DomeParamNP[0].getValue())
677 {
678 // Found home (or close enough)
679 LOG_INFO("Home sensor found");
681 targetAz = DomeHomePositionNP[0].getValue();
682
683 // Reset counters
684 interface->resetCounter();
685
689 }
690 else
691 {
692 // We overshoot, go closer
693 MoveAbs(DomeHomePositionNP[0].getValue());
694 }
695 }
697 }
698 else if (status == DOME_DEROTATING)
699 {
700 if ((currentStatus & ScopeDomeCard::STATUS_MOVING) == 0)
701 {
702 currentRotation = interface->getRotationCounterExt();
703 LOGF_INFO("Current rotation is %d", currentRotation);
704 if (abs(currentRotation) < 100)
705 {
706 // Close enough
707 LOG_INFO("De-rotation complete");
712 }
713 else
714 {
715 interface->move(currentRotation);
716 }
717 }
719 }
720 else if (status == DOME_CALIBRATING)
721 {
723 {
724 stepsPerRevolution = interface->getStepsPerRevolution();
725 LOGF_INFO("Calibration complete, steps per revolution read as %d", stepsPerRevolution);
726 StepsPerRevolutionNP[0].setValue(stepsPerRevolution);
733 }
734 }
735 else if (DomeAbsPosNP.getState() == IPS_BUSY)
736 {
737 if ((currentStatus & ScopeDomeCard::STATUS_MOVING) == 0)
738 {
739 // Rotation idle, are we close enough?
740 double azDiff = targetAz - DomeAbsPosNP[0].getValue();
741
742 if (azDiff > 180)
743 {
744 azDiff -= 360;
745 }
746 if (azDiff < -180)
747 {
748 azDiff += 360;
749 }
750 if (!refineMove || fabs(azDiff) <= DomeParamNP[0].getValue())
751 {
752 if (refineMove)
753 {
754 DomeAbsPosNP[0].setValue(targetAz);
755 }
757 LOG_INFO("Dome reached requested azimuth angle.");
758
759 if (getDomeState() == DOME_PARKING)
760 {
762 && interface->getInputState(ScopeDomeCard::CLOSED1) == ISS_OFF)
763 {
765 }
766 else
767 {
768 SetParked(true);
769 }
770 }
771 else if (getDomeState() == DOME_UNPARKING)
772 {
773 SetParked(false);
774 }
775 else
776 {
778 }
779 }
780 else
781 {
782 // Refine azimuth
784 }
785 }
787 }
788 else
789 {
791 }
792
793 // Read temperatures only every 10th time
794 static int tmpCounter = 0;
795 if (--tmpCounter <= 0)
796 {
798 tmpCounter = 10;
799 }
800
802}
803
804/************************************************************************************
805 *
806 * ***********************************************************************************/
808{
809 LOGF_DEBUG("MoveAbs (%f)", az);
810 targetAz = az;
811 double azDiff = az - DomeAbsPosNP[0].getValue();
812 LOGF_DEBUG("azDiff = %f", azDiff);
813
814 // Make relative (-180 - 180) regardless if it passes az 0
815 if (azDiff > 180)
816 {
817 azDiff -= 360;
818 }
819 if (azDiff < -180)
820 {
821 azDiff += 360;
822 }
823
824 LOGF_DEBUG("azDiff rel = %f", azDiff);
825
826 refineMove = true;
827 return sendMove(azDiff);
828}
829
830/************************************************************************************
831 *
832 * ***********************************************************************************/
834{
835 refineMove = false;
836 return sendMove(azDiff);
837}
838
839/************************************************************************************
840 *
841 * ***********************************************************************************/
842IPState ScopeDome::sendMove(double azDiff)
843{
844 if (azDiff < 0)
845 {
846 int steps = static_cast<int>(-azDiff * stepsPerRevolution / 360.0);
847 LOGF_DEBUG("CCW (%d)", steps);
848 if (steps <= 0)
849 {
850 return IPS_OK;
851 }
852 steps = compensateInertia(steps);
853 LOGF_DEBUG("CCW inertia (%d)", steps);
854 if (steps <= 0)
855 {
856 return IPS_OK;
857 }
858 interface->move(-steps);
859 }
860 else
861 {
862 int steps = static_cast<int>(azDiff * stepsPerRevolution / 360.0);
863 LOGF_DEBUG("CW (%d)", steps);
864 if (steps <= 0)
865 {
866 return IPS_OK;
867 }
868 steps = compensateInertia(steps);
869 LOGF_DEBUG("CW inertia (%d)", steps);
870 if (steps <= 0)
871 {
872 return IPS_OK;
873 }
874 interface->move(steps);
875 }
876 return IPS_BUSY;
877}
878
879/************************************************************************************
880 *
881 * ***********************************************************************************/
883{
884 // Map to button outputs
885 if (operation == MOTION_START)
886 {
887 refineMove = false;
888 if (dir == DOME_CW)
889 {
890 interface->setOutputState(ScopeDomeCard::CCW, ISS_OFF);
891 interface->setOutputState(ScopeDomeCard::CW, ISS_ON);
892 }
893 else
894 {
895 interface->setOutputState(ScopeDomeCard::CW, ISS_OFF);
896 interface->setOutputState(ScopeDomeCard::CCW, ISS_ON);
897 }
898 return IPS_BUSY;
899 }
900 interface->setOutputState(ScopeDomeCard::CW, ISS_OFF);
901 interface->setOutputState(ScopeDomeCard::CCW, ISS_OFF);
902 return IPS_OK;
903}
904
905/************************************************************************************
906 *
907 * ***********************************************************************************/
909{
910 // First move to park position and then optionally close shutter
913 if (s == IPS_OK && ShutterParkPolicySP[SHUTTER_CLOSE_ON_PARK].getState() == ISS_ON)
914 {
915 // Already at park position, just close if needed
917 }
918 return s;
919}
920
921/************************************************************************************
922 *
923 * ***********************************************************************************/
925{
927 {
929 }
930 return IPS_OK;
931}
932
933/************************************************************************************
934 *
935 * ***********************************************************************************/
937{
938 LOGF_INFO("Control shutter %d", (int)operation);
939 targetShutter = operation;
940 if (operation == SHUTTER_OPEN)
941 {
942 LOG_INFO("Opening shutter");
943 if (interface->getInputState(ScopeDomeCard::OPEN1))
944 {
945 LOG_INFO("Shutter already open");
946 return IPS_OK;
947 }
948 interface->controlShutter(ScopeDomeCard::OPEN_SHUTTER);
949 }
950 else
951 {
952 LOG_INFO("Closing shutter");
953 if (interface->getInputState(ScopeDomeCard::CLOSED1))
954 {
955 LOG_INFO("Shutter already closed");
956 return IPS_OK;
957 }
958 interface->controlShutter(ScopeDomeCard::CLOSE_SHUTTER);
959 }
960
962 return IPS_BUSY;
963}
964
965/************************************************************************************
966 *
967 * ***********************************************************************************/
969{
970 interface->abort();
973 reconnected = false; // just in case.
974 return true;
975}
976
977/************************************************************************************
978 *
979 * ***********************************************************************************/
981{
983
986 CardTypeSP.save(fp);
988 return true;
989}
990
991/************************************************************************************
992 *
993 * ***********************************************************************************/
995{
996 SetAxis1Park(DomeAbsPosNP[0].getValue());
997 return true;
998}
999/************************************************************************************
1000 *
1001 * ***********************************************************************************/
1002
1004{
1005 // By default set position to 90
1006 SetAxis1Park(90);
1007 return true;
1008}
1009
1010/************************************************************************************
1011 *
1012 * ***********************************************************************************/
1013void ScopeDome::reconnect()
1014{
1015 // Reconnect serial port after write error
1016 LOG_INFO("Reconnecting serial port");
1017 reconnecting = true;
1019 usleep(1000000); // 1s
1022 interface->setPortFD(PortFD);
1023 LOG_INFO("Reconnected");
1024 reconnecting = false;
1025 reconnected = true; // Retry shutter operation when link returns
1026}
1027
1028/*
1029 * Saturation Vapor Pressure formula for range -100..0 Deg. C.
1030 * This is taken from
1031 * ITS-90 Formulations for Vapor Pressure, Frostpoint Temperature,
1032 * Dewpoint Temperature, and Enhancement Factors in the Range 100 to +100 C
1033 * by Bob Hardy
1034 * as published in "The Proceedings of the Third International Symposium on
1035 * Humidity & Moisture",
1036 * Teddington, London, England, April 1998
1037 */
1038static const float k0 = -5.8666426e3;
1039static const float k1 = 2.232870244e1;
1040static const float k2 = 1.39387003e-2;
1041static const float k3 = -3.4262402e-5;
1042static const float k4 = 2.7040955e-8;
1043static const float k5 = 6.7063522e-1;
1044
1045static float pvsIce(float T)
1046{
1047 float lnP = k0 / T + k1 + (k2 + (k3 + (k4 * T)) * T) * T + k5 * log(T);
1048 return exp(lnP);
1049}
1050
1063static const float n1 = 0.11670521452767e4;
1064static const float n6 = 0.14915108613530e2;
1065static const float n2 = -0.72421316703206e6;
1066static const float n7 = -0.48232657361591e4;
1067static const float n3 = -0.17073846940092e2;
1068static const float n8 = 0.40511340542057e6;
1069static const float n4 = 0.12020824702470e5;
1070static const float n9 = -0.23855557567849;
1071static const float n5 = -0.32325550322333e7;
1072static const float n10 = 0.65017534844798e3;
1073
1074static float pvsWater(float T)
1075{
1076 float th = T + n9 / (T - n10);
1077 float A = (th + n1) * th + n2;
1078 ;
1079 float B = (n3 * th + n4) * th + n5;
1080 float C = (n6 * th + n7) * th + n8;
1081 ;
1082
1083 float p = 2.0f * C / (-B + sqrt(B * B - 4.0f * A * C));
1084 p *= p;
1085 p *= p;
1086 return p * 1e6;
1087}
1088
1089static const float C_OFFSET = 273.15f;
1090static const float minT = 173; // -100 Deg. C.
1091static const float maxT = 678;
1092
1093static float PVS(float T)
1094{
1095 if (T < minT || T > maxT)
1096 return 0;
1097 else if (T < C_OFFSET)
1098 return pvsIce(T);
1099 else
1100 return pvsWater(T);
1101}
1102
1103static float solve(float (*f)(float), float y, float x0)
1104{
1105 float x = x0;
1106 int maxCount = 10;
1107 int count = 0;
1108 while (++count < maxCount)
1109 {
1110 float xNew;
1111 float dx = x / 1000;
1112 float z = f(x);
1113 xNew = x + dx * (y - z) / (f(x + dx) - z);
1114 if (fabs((xNew - x) / xNew) < 0.0001f)
1115 return xNew;
1116 x = xNew;
1117 }
1118 return 0;
1119}
1120
1121float ScopeDome::getDewPoint(float RH, float T)
1122{
1123 T = T + C_OFFSET;
1124 return solve(PVS, RH / 100 * PVS(T), T) - C_OFFSET;
1125}
1126
1127int ScopeDome::compensateInertia(int steps)
1128{
1129 if (inertiaTable.size() == 0)
1130 {
1131 LOGF_DEBUG("inertia passthrough %d", steps);
1132 return steps; // pass value as such if we don't have enough data
1133 }
1134
1135 for (uint16_t out = 0; out < inertiaTable.size(); out++)
1136 {
1137 if (inertiaTable[out] > steps)
1138 {
1139 LOGF_DEBUG("inertia %d -> %d", steps, out - 1);
1140 return out - 1;
1141 }
1142 }
1143 // Check difference from largest table entry and assume we have
1144 // similar inertia also after that
1145 int lastEntry = inertiaTable.size() - 1;
1146 int inertia = inertiaTable[lastEntry] - lastEntry;
1147 int movement = steps - inertia;
1148 LOGF_DEBUG("inertia %d -> %d", steps, movement);
1149 if (movement <= 0)
1150 return 0;
1151
1152 return movement;
1153}
void log(const std::string &log)
Definition Utils.cpp:161
std::thread th
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual bool Disconnect() override
Disconnect Disconnect from device.
virtual bool Connect() override
Connect Connect to device via the implemented communication medium. Do not perform any handshakes.
bool isConnected() const
void setPollingPeriodRange(uint32_t minimum, uint32_t maximum)
setPollingPeriodRange Set the range permitted by the polling range in milliseconds
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.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
Connection::Interface * getActiveConnection()
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
void SetDomeCapability(uint32_t cap)
SetDomeCapability set the dome capabilities. All capabilities must be initialized.
void setDomeConnection(const uint8_t &value)
setDomeConnection Set Dome connection mode. Child class should call this in the constructor before Do...
INDI::PropertySwitch ShutterParkPolicySP
Definition indidome.h:566
@ SHUTTER_OPEN_ON_UNPARK
Definition indidome.h:570
@ SHUTTER_CLOSE_ON_PARK
Definition indidome.h:569
const char * GetShutterStatusString(ShutterState status)
getShutterStatusString
INDI::PropertyNumber DomeParamNP
Definition indidome.h:542
@ DOME_UNPARKING
Definition indidome.h:135
ShutterState m_ShutterState
Definition indidome.h:612
void SetAxis1Park(double value)
SetRAPark Set current AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetAxis1Park Set default AZ parking position.
ShutterOperation
Shutter operation command.
Definition indidome.h:114
@ SHUTTER_CLOSE
Definition indidome.h:116
DomeMotionCommand
Definition indidome.h:97
@ MOTION_START
Definition indidome.h:98
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indidome.cpp:105
@ DOME_CAN_PARK
Definition indidome.h:159
@ DOME_CAN_ABS_MOVE
Definition indidome.h:157
@ DOME_HAS_SHUTTER
Definition indidome.h:161
@ DOME_CAN_REL_MOVE
Definition indidome.h:158
@ DOME_CAN_ABORT
Definition indidome.h:156
double GetAxis1Park()
@ SHUTTER_MOVING
Definition indidome.h:149
@ SHUTTER_UNKNOWN
Definition indidome.h:150
@ SHUTTER_OPENED
Definition indidome.h:147
@ SHUTTER_CLOSED
Definition indidome.h:148
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition indidome.cpp:284
INDI::PropertyNumber DomeAbsPosNP
Definition indidome.h:536
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition indidome.cpp:503
@ CONNECTION_SERIAL
Definition indidome.h:172
@ CONNECTION_TCP
Definition indidome.h:173
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition indidome.cpp:809
void setShutterState(const ShutterState &value)
void setDomeState(const DomeState &value)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
Connection::Serial * serialConnection
Definition indidome.h:607
void SetParkDataType(DomeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
INDI::PropertySwitch DomeShutterSP
Definition indidome.h:546
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition indidome.cpp:394
DomeState getDomeState() const
Definition indidome.h:285
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
void resize(size_t size)
const char * getName() const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
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)
bool update(const char *const texts[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
friend class ScopeDomeUSB21
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
INDI::PropertySwitch DerotateSP
virtual const char * getDefaultName() override
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
INDI::PropertyText CredentialsTP
ShutterState simShutterStatus
friend class ScopeDomeArduino
INDI::PropertySwitch FindHomeSP
ShutterOperation targetShutter
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
double targetAz
INDI::PropertyNumber DomeHomePositionNP
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
DomeStatus status
bool UpdateSensorStatus()
INDI::PropertyNumber FirmwareVersionsNP
virtual IPState MoveRel(double azDiff) override
Move the Dome to an relative position.
virtual IPState ControlShutter(ShutterOperation operation) override
Open or Close shutter.
virtual IPState UnPark() override
UnPark dome. The action of the Unpark command is dome specific, but it may include opening the shutte...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool Abort() override
Abort all dome motion.
INDI::PropertySwitch CardTypeSP
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
bool UpdateShutterStatus()
virtual bool Handshake() override
perform handshake with device to check communication
INDI::PropertySwitch StartCalibrationSP
INDI::PropertySwitch HomeSensorPolaritySP
INDI::PropertySwitch InputsSP
INDI::PropertySwitch RelaysSP
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
INDI::PropertyNumber SensorsNP
virtual IPState MoveAbs(double az) override
Move the Dome to an absolute azimuth.
INDI::PropertyNumber StepsPerRevolutionNP
INDI::PropertySwitch CalibrationNeededSP
bool UpdateRelayStatus()
bool UpdatePosition()
virtual IPState Move(DomeDirection dir, DomeMotionCommand operation) override
Move the Dome in a particular direction.
virtual IPState Park() override
Goto Park Position. The park position is an absolute azimuth value.
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.
const char * INFO_TAB
INFO_TAB Where all the properties for general information are located.
const char * SITE_TAB
SITE_TAB Where all site information setting are located.
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_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
@ ISR_ATMOST1
Definition indiapi.h:172
#define MAXINDINAME
Definition indiapi.h:189
Implementations for common driver routines.
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition indidriver.c:908
int IUGetConfigSwitch(const char *dev, const char *property, const char *member, ISState *value)
IUGetConfigSwitch Opens configuration file and reads single switch property.
Definition indidriver.c:657
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
std::unique_ptr< ScopeDome > scopeDome(new ScopeDome())