Instrument Neutral Distributed Interface INDI  2.0.2
indidome.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  INDI Dome Base Class
3  Copyright(c) 2014 Jasem Mutlaq. All rights reserved.
4 
5  The code used calculate dome target AZ and ZD is written by Ferran Casarramona, and adapted from code from Markus Wildi.
6  The transformations are based on the paper Matrix Method for Coodinates Transformation written by Toshimi Taki (http://www.asahi-net.or.jp/~zs3t-tk).
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Library General Public
10  License version 2 as published by the Free Software Foundation.
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  Library General Public License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to
19  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  Boston, MA 02110-1301, USA.
21 *******************************************************************************/
22 
23 #include "indidome.h"
24 
25 #include "indicom.h"
26 #include "indicontroller.h"
27 #include "inditimer.h"
30 
31 #include <libnova/julian_day.h>
32 #include <libnova/sidereal_time.h>
33 #include <libnova/transform.h>
34 
35 #include <cerrno>
36 #include <cmath>
37 #include <cstring>
38 #include <wordexp.h>
39 #include <pwd.h>
40 #include <unistd.h>
41 #include <limits>
42 
43 #define DOME_SLAVING_TAB "Slaving"
44 #define DOME_COORD_THRESHOLD \
45  0.1 /* Only send debug messages if the differences between old and new values of Az/Alt excceds this value */
46 
47 namespace INDI
48 {
49 
50 Dome::Dome() : ParkDataFileName(GetHomeDirectory() + "/.indi/ParkData.xml")
51 {
52  controller = new Controller(this);
53 
54  controller->setButtonCallback(buttonHelper);
55 
56  prev_az = prev_alt = prev_ra = prev_dec = 0;
59 
60  capability = 0;
61 
64 
66  ParkdataXmlRoot = nullptr;
67 
68  m_MountUpdateTimer.callOnTimeout(std::bind(&Dome::UpdateMountCoords, this));
69 }
70 
72 {
73  delXMLEle(ParkdataXmlRoot);
74 
75  delete controller;
76  delete serialConnection;
77  delete tcpConnection;
78 }
79 
80 std::string Dome::GetHomeDirectory() const
81 {
82  // Check first the HOME environmental variable
83  const char * HomeDir = getenv("HOME");
84 
85  // ...otherwise get the home directory of the current user.
86  if (!HomeDir)
87  {
88  HomeDir = getpwuid(getuid())->pw_dir;
89  }
90  return (HomeDir ? std::string(HomeDir) : "");
91 }
92 
94 {
95  DefaultDevice::initProperties(); // let the base class flesh in what it wants
96 
97  // Presets
98  IUFillNumber(&PresetN[0], "Preset 1", "", "%6.2f", 0, 360.0, 1.0, 0);
99  IUFillNumber(&PresetN[1], "Preset 2", "", "%6.2f", 0, 360.0, 1.0, 0);
100  IUFillNumber(&PresetN[2], "Preset 3", "", "%6.2f", 0, 360.0, 1.0, 0);
101  IUFillNumberVector(&PresetNP, PresetN, 3, getDeviceName(), "Presets", "", "Presets", IP_RW, 0, IPS_IDLE);
102 
103  //Preset GOTO
104  IUFillSwitch(&PresetGotoS[0], "Preset 1", "", ISS_OFF);
105  IUFillSwitch(&PresetGotoS[1], "Preset 2", "", ISS_OFF);
106  IUFillSwitch(&PresetGotoS[2], "Preset 3", "", ISS_OFF);
107  IUFillSwitchVector(&PresetGotoSP, PresetGotoS, 3, getDeviceName(), "Goto", "", "Presets", IP_RW, ISR_1OFMANY, 0,
108  IPS_IDLE);
109 
110  // IUFillSwitch(&AutoParkS[0], "INDI_ENABLED", "Enable", ISS_OFF);
111  // IUFillSwitch(&AutoParkS[1], "INDI_DISABLED", "Disable", ISS_ON);
112  // IUFillSwitchVector(&AutoParkSP, AutoParkS, 2, getDeviceName(), "DOME_AUTOPARK", "Auto Park", OPTIONS_TAB, IP_RW,
113  // ISR_1OFMANY, 0, IPS_IDLE);
114 
115  // Active Devices
116  IUFillText(&ActiveDeviceT[0], "ACTIVE_TELESCOPE", "Telescope", "Telescope Simulator");
117  //IUFillText(&ActiveDeviceT[1], "ACTIVE_WEATHER", "Weather", "WunderGround");
118  IUFillTextVector(&ActiveDeviceTP, ActiveDeviceT, 1, getDeviceName(), "ACTIVE_DEVICES", "Snoop devices", OPTIONS_TAB,
119  IP_RW, 60, IPS_IDLE);
120 
121  // Use locking if telescope is unparked
122  IUFillSwitch(&MountPolicyS[MOUNT_IGNORED], "MOUNT_IGNORED", "Mount ignored", ISS_ON);
123  IUFillSwitch(&MountPolicyS[MOUNT_LOCKS], "MOUNT_LOCKS", "Mount locks", ISS_OFF);
124  IUFillSwitchVector(&MountPolicySP, MountPolicyS, 2, getDeviceName(), "MOUNT_POLICY", "Mount Policy", OPTIONS_TAB, IP_RW,
125  ISR_1OFMANY, 60, IPS_IDLE);
126 
127  // Shutter Policy
128  IUFillSwitch(&ShutterParkPolicyS[SHUTTER_CLOSE_ON_PARK], "SHUTTER_CLOSE_ON_PARK", "Close On Park", ISS_OFF);
129  IUFillSwitch(&ShutterParkPolicyS[SHUTTER_OPEN_ON_UNPARK], "SHUTTER_OPEN_ON_UNPARK", "Open On UnPark", ISS_OFF);
130  IUFillSwitchVector(&ShutterParkPolicySP, ShutterParkPolicyS, 2, getDeviceName(), "DOME_SHUTTER_PARK_POLICY", "Shutter",
132 
133  // Measurements
134  IUFillNumber(&DomeMeasurementsN[DM_DOME_RADIUS], "DM_DOME_RADIUS", "Radius (m)", "%6.2f", 0.0, 50.0, 1.0, 0.0);
135  IUFillNumber(&DomeMeasurementsN[DM_SHUTTER_WIDTH], "DM_SHUTTER_WIDTH", "Shutter width (m)", "%6.2f", 0.0, 10.0, 1.0,
136  0.0);
137  IUFillNumber(&DomeMeasurementsN[DM_NORTH_DISPLACEMENT], "DM_NORTH_DISPLACEMENT", "N displacement (m)", "%6.2f",
138  -10.0, 10.0, 1.0, 0.0);
139  IUFillNumber(&DomeMeasurementsN[DM_EAST_DISPLACEMENT], "DM_EAST_DISPLACEMENT", "E displacement (m)", "%6.2f", -10.0,
140  10.0, 1.0, 0.0);
141  IUFillNumber(&DomeMeasurementsN[DM_UP_DISPLACEMENT], "DM_UP_DISPLACEMENT", "Up displacement (m)", "%6.2f", -10,
142  10.0, 1.0, 0.0);
143  IUFillNumber(&DomeMeasurementsN[DM_OTA_OFFSET], "DM_OTA_OFFSET", "OTA offset (m)", "%6.2f", -10.0, 10.0, 1.0, 0.0);
144  IUFillNumberVector(&DomeMeasurementsNP, DomeMeasurementsN, 6, getDeviceName(), "DOME_MEASUREMENTS", "Measurements",
146 
147  IUFillSwitch(&OTASideS[DM_OTA_SIDE_EAST], "DM_OTA_SIDE_EAST", "East", ISS_OFF);
148  IUFillSwitch(&OTASideS[DM_OTA_SIDE_WEST], "DM_OTA_SIDE_WEST", "West", ISS_OFF);
149  IUFillSwitch(&OTASideS[DM_OTA_SIDE_MOUNT], "DM_OTA_SIDE_MOUNT", "Mount", ISS_ON);
150  IUFillSwitch(&OTASideS[DM_OTA_SIDE_HA], "DM_OTA_SIDE_HA", "Hour Angle", ISS_OFF);
151  IUFillSwitch(&OTASideS[DM_OTA_SIDE_IGNORE], "DM_OTA_SIDE_IGNORE", "Ignore", ISS_OFF);
152  IUFillSwitchVector(&OTASideSP, OTASideS, 5, getDeviceName(), "DM_OTA_SIDE", "Meridian side", DOME_SLAVING_TAB,
153  IP_RW, ISR_ATMOST1, 60, IPS_OK);
154 
155  IUFillSwitch(&DomeAutoSyncS[0], "DOME_AUTOSYNC_ENABLE", "Enable", ISS_OFF);
156  IUFillSwitch(&DomeAutoSyncS[1], "DOME_AUTOSYNC_DISABLE", "Disable", ISS_ON);
158  IP_RW, ISR_1OFMANY, 60, IPS_OK);
159 
160  IUFillNumber(&DomeSpeedN[0], "DOME_SPEED_VALUE", "RPM", "%6.2f", 0.0, 10, 0.1, 1.0);
161  IUFillNumberVector(&DomeSpeedNP, DomeSpeedN, 1, getDeviceName(), "DOME_SPEED", "Speed", MAIN_CONTROL_TAB, IP_RW, 60,
162  IPS_OK);
163 
164  IUFillNumber(&DomeSyncN[0], "DOME_SYNC_VALUE", "Az", "%.2f", 0.0, 360, 10, 0.0);
165  IUFillNumberVector(&DomeSyncNP, DomeSyncN, 1, getDeviceName(), "DOME_SYNC", "Sync", MAIN_CONTROL_TAB, IP_RW, 60,
166  IPS_OK);
167 
168  IUFillSwitch(&DomeMotionS[0], "DOME_CW", "Dome CW", ISS_OFF);
169  IUFillSwitch(&DomeMotionS[1], "DOME_CCW", "Dome CCW", ISS_OFF);
171  ISR_ATMOST1, 60, IPS_OK);
172 
173  // Driver can define those to clients if there is support
174  IUFillNumber(&DomeAbsPosN[0], "DOME_ABSOLUTE_POSITION", "Degrees", "%6.2f", 0.0, 360.0, 1.0, 0.0);
175  IUFillNumberVector(&DomeAbsPosNP, DomeAbsPosN, 1, getDeviceName(), "ABS_DOME_POSITION", "Absolute Position",
177 
178  IUFillNumber(&DomeRelPosN[0], "DOME_RELATIVE_POSITION", "Degrees", "%6.2f", -180, 180.0, 10.0, 0.0);
179  IUFillNumberVector(&DomeRelPosNP, DomeRelPosN, 1, getDeviceName(), "REL_DOME_POSITION", "Relative Position",
181 
182  IUFillSwitch(&AbortS[0], "ABORT", "Abort", ISS_OFF);
183  IUFillSwitchVector(&AbortSP, AbortS, 1, getDeviceName(), "DOME_ABORT_MOTION", "Abort Motion", MAIN_CONTROL_TAB,
184  IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
185 
186  IUFillNumber(&DomeParamN[0], "AUTOSYNC_THRESHOLD", "Autosync threshold (deg)", "%6.2f", 0.0, 360.0, 1.0, 0.5);
188  60, IPS_OK);
189 
190  IUFillSwitch(&ParkS[0], "PARK", "Park(ed)", ISS_OFF);
191  IUFillSwitch(&ParkS[1], "UNPARK", "UnPark(ed)", ISS_OFF);
193  60, IPS_OK);
194 
195  // Backlash Compensation
196  IUFillSwitch(&DomeBacklashS[INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_OFF);
197  IUFillSwitch(&DomeBacklashS[INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_ON);
198  IUFillSwitchVector(&DomeBacklashSP, DomeBacklashS, 2, getDeviceName(), "DOME_BACKLASH_TOGGLE", "Backlash", OPTIONS_TAB,
199  IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
200 
201 
202  // Backlash Compensation Value
203  IUFillNumber(&DomeBacklashN[0], "DOME_BACKLASH_VALUE", "Steps", "%.f", 0, 1e6, 100, 0);
204  IUFillNumberVector(&DomeBacklashNP, DomeBacklashN, 1, getDeviceName(), "DOME_BACKLASH_STEPS", "Backlash",
205  OPTIONS_TAB, IP_RW, 60, IPS_OK);
206 
207 
208  IUFillSwitch(&DomeShutterS[0], "SHUTTER_OPEN", "Open", ISS_OFF);
209  IUFillSwitch(&DomeShutterS[1], "SHUTTER_CLOSE", "Close", ISS_ON);
210  IUFillSwitchVector(&DomeShutterSP, DomeShutterS, 2, getDeviceName(), "DOME_SHUTTER", "Shutter", MAIN_CONTROL_TAB,
211  IP_RW, ISR_ATMOST1, 60, IPS_OK);
212 
213  IUFillSwitch(&ParkOptionS[0], "PARK_CURRENT", "Current", ISS_OFF);
214  IUFillSwitch(&ParkOptionS[1], "PARK_DEFAULT", "Default", ISS_OFF);
215  IUFillSwitch(&ParkOptionS[2], "PARK_WRITE_DATA", "Write Data", ISS_OFF);
216  IUFillSwitchVector(&ParkOptionSP, ParkOptionS, 3, getDeviceName(), "DOME_PARK_OPTION", "Park Options", SITE_TAB,
217  IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
218 
219  addDebugControl();
220 
221  controller->mapController("Dome CW", "CW/Open", Controller::CONTROLLER_BUTTON, "BUTTON_1");
222  controller->mapController("Dome CCW", "CCW/Close", Controller::CONTROLLER_BUTTON, "BUTTON_2");
223 
224  controller->initProperties();
225 
226  IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_EOD_COORD");
227  IDSnoopDevice(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
228  IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_PARK");
229  if (CanAbsMove())
230  IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_PIER_SIDE");
231 
232  //IDSnoopDevice(ActiveDeviceT[1].text, "WEATHER_STATUS");
233 
235 
236  if (domeConnection & CONNECTION_SERIAL)
237  {
240  {
241  return callHandshake();
242  });
244  }
245 
246  if (domeConnection & CONNECTION_TCP)
247  {
248  tcpConnection = new Connection::TCP(this);
250  {
251  return callHandshake();
252  });
254  }
255 
256  return true;
257 }
258 
259 void Dome::ISGetProperties(const char * dev)
260 {
261  // First we let our parent populate
263 
265  loadConfig(true, "ACTIVE_DEVICES");
266 
267  ISState isMountIgnored = ISS_OFF;
268  if (IUGetConfigSwitch(getDeviceName(), MountPolicySP.name, MountPolicyS[MOUNT_IGNORED].name, &isMountIgnored) == 0)
269  {
270  MountPolicyS[MOUNT_IGNORED].s = isMountIgnored;
271  MountPolicyS[MOUNT_LOCKS].s = (isMountIgnored == ISS_ON) ? ISS_OFF : ISS_ON;
272  }
274 
275  controller->ISGetProperties(dev);
276  return;
277 }
278 
280 {
281  if (isConnected())
282  {
283  if (HasShutter())
284  {
287  }
288 
289  // Now we add our Dome specific stuff
291 
292  if (HasVariableSpeed())
293  {
295  }
296  if (CanRelMove())
298  if (CanAbsMove())
300  if (CanAbort())
302  if (CanAbsMove())
303  {
306 
311  }
312  if (CanSync())
314 
315  if (CanPark())
316  {
318  if (parkDataType != PARK_NONE)
319  {
322  }
323  }
324 
325  if (HasBacklash())
326  {
329  }
330 
331  //defineProperty(&AutoParkSP);
332  }
333  else
334  {
335  if (HasShutter())
336  {
339  }
340 
342 
343  if (HasVariableSpeed())
344  {
346  }
347  if (CanRelMove())
349  if (CanAbsMove())
351  if (CanAbort())
353  if (CanAbsMove())
354  {
357 
362  }
363 
364  if (CanSync())
366 
367  if (CanPark())
368  {
370  if (parkDataType != PARK_NONE)
371  {
374  }
375  }
376 
377  if (HasBacklash())
378  {
381  }
382 
383  //deleteProperty(AutoParkSP.name);
384  }
385 
386  controller->updateProperties();
387  return true;
388 }
389 
390 bool Dome::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
391 {
392  // first check if it's for our device
393  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
394  {
395  if (!strcmp(name, PresetNP.name))
396  {
397  IUUpdateNumber(&PresetNP, values, names, n);
398  PresetNP.s = IPS_OK;
399  IDSetNumber(&PresetNP, nullptr);
400 
401  return true;
402  }
403  // Dome Sync
404  else if (!strcmp(name, DomeSyncNP.name))
405  {
406  if (Sync(values[0]))
407  {
408  IUUpdateNumber(&DomeSyncNP, values, names, n);
409  DomeSyncNP.s = IPS_OK;
410  DomeAbsPosN[0].value = values[0];
411  IDSetNumber(&DomeAbsPosNP, nullptr);
412  }
413  else
414  {
416  }
417 
418  IDSetNumber(&DomeSyncNP, nullptr);
419  return true;
420  }
421  else if (!strcmp(name, DomeParamNP.name))
422  {
423  IUUpdateNumber(&DomeParamNP, values, names, n);
424  DomeParamNP.s = IPS_OK;
425  IDSetNumber(&DomeParamNP, nullptr);
426  return true;
427  }
428  else if (!strcmp(name, DomeSpeedNP.name))
429  {
430  double newSpeed = values[0];
431  Dome::SetSpeed(newSpeed);
432  return true;
433  }
434  else if (!strcmp(name, DomeAbsPosNP.name))
435  {
436  double newPos = values[0];
437  Dome::MoveAbs(newPos);
438  return true;
439  }
440  else if (!strcmp(name, DomeRelPosNP.name))
441  {
442  double newPos = values[0];
443  Dome::MoveRel(newPos);
444  return true;
445  }
446  else if (!strcmp(name, DomeMeasurementsNP.name))
447  {
448  IUUpdateNumber(&DomeMeasurementsNP, values, names, n);
450  IDSetNumber(&DomeMeasurementsNP, nullptr);
451 
452  return true;
453  }
454  else if (strcmp(name, ParkPositionNP.name) == 0)
455  {
456  IUUpdateNumber(&ParkPositionNP, values, names, n);
458 
459  Axis1ParkPosition = ParkPositionN[AXIS_RA].value;
460  IDSetNumber(&ParkPositionNP, nullptr);
461  return true;
462  }
464  // Backlash value
466  else if (!strcmp(name, DomeBacklashNP.name))
467  {
469  {
471  DEBUGDEVICE(dev, Logger::DBG_WARNING, "Dome backlash must be enabled first.");
472  }
473  else
474  {
475  uint32_t steps = static_cast<uint32_t>(values[0]);
476  if (SetBacklash(steps))
477  {
478  DomeBacklashN[0].value = values[0];
480  }
481  else
483  }
484  IDSetNumber(&DomeBacklashNP, nullptr);
485  return true;
486  }
487  }
488 
489  return DefaultDevice::ISNewNumber(dev, name, values, names, n);
490 }
491 
492 bool Dome::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
493 {
494  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
495  {
497  // GOTO Presets
499  if (!strcmp(PresetGotoSP.name, name))
500  {
501  if (m_DomeState == DOME_PARKED)
502  {
504  "Please unpark before issuing any motion commands.");
506  IDSetSwitch(&PresetGotoSP, nullptr);
507  return false;
508  }
509 
510  IUUpdateSwitch(&PresetGotoSP, states, names, n);
511  int index = IUFindOnSwitchIndex(&PresetGotoSP);
512  IPState rc = Dome::MoveAbs(PresetN[index].value);
513  if (rc == IPS_OK || rc == IPS_BUSY)
514  {
516  LOGF_INFO("Moving to Preset %d (%.2f degrees).", index + 1, PresetN[index].value);
517  IDSetSwitch(&PresetGotoSP, nullptr);
518  return true;
519  }
520 
522  IDSetSwitch(&PresetGotoSP, nullptr);
523  return false;
524  }
526  // Dome Auto Sync
528  else if (!strcmp(name, DomeAutoSyncSP.name))
529  {
530  IUUpdateSwitch(&DomeAutoSyncSP, states, names, n);
532 
533  if (DomeAutoSyncS[0].s == ISS_ON)
534  {
535  IDSetSwitch(&DomeAutoSyncSP, "Dome will now be synced to mount azimuth position.");
536  LOG_WARN("Dome will now be synced to mount azimuth position.");
537 
539  m_MountUpdateTimer.start(HORZ_UPDATE_TIMER);
540  }
541  else
542  {
543  IDSetSwitch(&DomeAutoSyncSP, "Dome is no longer synced to mount azimuth position.");
544  LOG_WARN("Dome is no longer synced to mount azimuth position.");
545  m_MountUpdateTimer.stop();
546 
547  if (DomeAbsPosNP.s == IPS_BUSY || DomeRelPosNP.s == IPS_BUSY /* || DomeTimerNP.s == IPS_BUSY*/)
548  Dome::Abort();
549  }
550 
551  return true;
552  }
554  // OTA Side
556  else if (!strcmp(name, OTASideSP.name))
557  {
558  IUUpdateSwitch(&OTASideSP, states, names, n);
559  OTASideSP.s = IPS_OK;
560 
561  if (OTASideS[DM_OTA_SIDE_EAST].s == ISS_ON)
562  {
563  LOG_WARN("Dome shall be synced assuming OTA situated east of the pier");
564  }
565  else if (OTASideS[DM_OTA_SIDE_WEST].s == ISS_ON)
566  {
567  LOG_WARN("Dome shall be synced assuming OTA situated west of the pier");
568  }
569  else if (OTASideS[DM_OTA_SIDE_MOUNT].s == ISS_ON)
570  {
571  LOG_WARN("Dome shall be synced from pier side as reported by the mount.");
572  }
573  else if (OTASideS[DM_OTA_SIDE_HA].s == ISS_ON)
574  {
575  LOG_WARN("Dome shall be synced for OTA pier side derived from Hour Angle.");
576  UpdateAutoSync();
577  }
578  else if (OTASideS[DM_OTA_SIDE_IGNORE].s == ISS_ON)
579  {
580  LOG_WARN("Dome shall be synced by ignoring pier side as in a fork mount.");
581  }
582 
583  UpdateAutoSync();
584  IDSetSwitch(&OTASideSP, nullptr);
585  return true;
586  }
588  // Dome Motion
590  else if (!strcmp(name, DomeMotionSP.name))
591  {
592  // Check if any switch is ON
593  for (int i = 0; i < n; i++)
594  {
595  if (states[i] == ISS_ON)
596  {
597  if (!strcmp(DomeMotionS[DOME_CW].name, names[i]))
599  else
601 
602  return true;
603  }
604  }
605 
606  // All switches are off, so let's turn off last motion
607  int current_direction = IUFindOnSwitchIndex(&DomeMotionSP);
608  // Shouldn't happen
609  if (current_direction < 0)
610  {
612  IDSetSwitch(&DomeMotionSP, nullptr);
613  return false;
614  }
615 
616  Dome::Move(static_cast<DomeDirection>(current_direction), MOTION_STOP);
617 
618  return true;
619  }
621  // Abort Motion
623  else if (!strcmp(name, AbortSP.name))
624  {
625  Dome::Abort();
626  return true;
627  }
629  // Shutter
631  else if (!strcmp(name, DomeShutterSP.name))
632  {
633  // Check if any switch is ON
634  for (int i = 0; i < n; i++)
635  {
636  if (states[i] == ISS_ON)
637  {
638  // Open
639  if (!strcmp(DomeShutterS[0].name, names[i]))
640  {
642  }
643  else
644  {
646  }
647  }
648  }
649  }
651  // Parking Switch
653  else if (!strcmp(name, ParkSP.name))
654  {
655  // Check if any switch is ON
656  for (int i = 0; i < n; i++)
657  {
658  if (states[i] == ISS_ON)
659  {
660  if (!strcmp(ParkS[0].name, names[i]))
661  {
662  if (m_DomeState == DOME_PARKING)
663  return false;
664 
665  return (Dome::Park() != IPS_ALERT);
666  }
667  else
668  {
670  return false;
671 
672  return (Dome::UnPark() != IPS_ALERT);
673  }
674  }
675  }
676  }
678  // Parking Option
680  else if (!strcmp(name, ParkOptionSP.name))
681  {
682  IUUpdateSwitch(&ParkOptionSP, states, names, n);
684  if (!sp)
685  return false;
686 
687  bool rc = false;
688 
690 
691  if (!strcmp(sp->name, "PARK_CURRENT"))
692  {
693  rc = SetCurrentPark();
694  }
695  else if (!strcmp(sp->name, "PARK_DEFAULT"))
696  {
697  rc = SetDefaultPark();
698  }
699  else if (!strcmp(sp->name, "PARK_WRITE_DATA"))
700  {
701  rc = WriteParkData();
702  if (rc)
703  DEBUG(Logger::DBG_SESSION, "Saved Park Status/Position.");
704  else
705  LOG_WARN( "Can not save Park Status/Position.");
706  }
707 
708  ParkOptionSP.s = rc ? IPS_OK : IPS_ALERT;
709  IDSetSwitch(&ParkOptionSP, nullptr);
710 
711  return true;
712  }
714  // Auto Park
716 #if 0
717  else if (!strcmp(name, AutoParkSP.name))
718  {
719  IUUpdateSwitch(&AutoParkSP, states, names, n);
720 
721  AutoParkSP.s = IPS_OK;
722 
723  if (AutoParkS[0].s == ISS_ON)
724  LOG_WARN( "Warning: Auto park is enabled. If weather conditions are in the "
725  "danger zone, the dome will be automatically parked. Only enable this "
726  "option is parking the dome at any time will not cause damage to any "
727  "equipment.");
728  else
729  DEBUG(Logger::DBG_SESSION, "Auto park is disabled.");
730 
731  IDSetSwitch(&AutoParkSP, nullptr);
732 
733  return true;
734  }
735 #endif
737  // Telescope Parking Policy
739  else if (!strcmp(name, MountPolicySP.name))
740  {
741  IUUpdateSwitch(&MountPolicySP, states, names, n);
744  LOG_INFO("Mount Policy set to: Mount ignored. Dome can park regardless of mount parking state.");
745  else
746  LOG_WARN("Mount Policy set to: Mount locks. This prevents the dome from parking when mount is unparked.");
747  IDSetSwitch(&MountPolicySP, nullptr);
748  triggerSnoop(ActiveDeviceT[0].text, "TELESCOPE_PARK");
749  return true;
750  }
752  // Shutter Parking Policy
754  else if (!strcmp(name, ShutterParkPolicySP.name))
755  {
756  IUUpdateSwitch(&ShutterParkPolicySP, states, names, n);
758  IDSetSwitch(&ShutterParkPolicySP, nullptr);
759  return true;
760  }
762  // Backlash enable/disable
764  else if (strcmp(name, DomeBacklashSP.name) == 0)
765  {
766  int prevIndex = IUFindOnSwitchIndex(&DomeBacklashSP);
767  IUUpdateSwitch(&DomeBacklashSP, states, names, n);
768  const bool enabled = IUFindOnSwitchIndex(&DomeBacklashSP) == INDI_ENABLED;
769 
770  if (SetBacklashEnabled(enabled))
771  {
772  IUUpdateSwitch(&DomeBacklashSP, states, names, n);
774  LOGF_INFO("Dome backlash is %s.", (enabled ? "enabled" : "disabled"));
775  }
776  else
777  {
779  DomeBacklashS[prevIndex].s = ISS_ON;
781  LOG_ERROR("Failed to set trigger Dome backlash.");
782  }
783 
784  IDSetSwitch(&DomeBacklashSP, nullptr);
785  return true;
786  }
787  }
788 
789  controller->ISNewSwitch(dev, name, states, names, n);
790 
791  // Nobody has claimed this, so, ignore it
792  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
793 }
794 
795 bool Dome::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
796 {
797  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
798  {
799  if (strcmp(name, ActiveDeviceTP.name) == 0)
800  {
802  IUUpdateText(&ActiveDeviceTP, texts, names, n);
803  IDSetText(&ActiveDeviceTP, nullptr);
804 
805  IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_EOD_COORD");
806  IDSnoopDevice(ActiveDeviceT[0].text, "TARGET_EOD_COORD");
807  IDSnoopDevice(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
808  IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_PARK");
809  if (CanAbsMove())
810  IDSnoopDevice(ActiveDeviceT[0].text, "TELESCOPE_PIER_SIDE");
811  //IDSnoopDevice(ActiveDeviceT[1].text, "WEATHER_STATUS");
812 
813  return true;
814  }
815  }
816 
817  controller->ISNewText(dev, name, texts, names, n);
818 
819  return DefaultDevice::ISNewText(dev, name, texts, names, n);
820 }
821 
823 {
824  XMLEle * ep = nullptr;
825  const char * propName = findXMLAttValu(root, "name");
826 
827  // Check TARGET
828  if (!strcmp("TARGET_EOD_COORD", propName))
829  {
830  int rc_ra = -1, rc_de = -1;
831  double ra = 0, de = 0;
832 
833  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
834  {
835  const char * elemName = findXMLAttValu(ep, "name");
836 
837  LOGF_DEBUG("Snooped Target RA-DEC: %s", pcdataXMLEle(ep));
838  if (!strcmp(elemName, "RA"))
839  rc_ra = f_scansexa(pcdataXMLEle(ep), &ra);
840  else if (!strcmp(elemName, "DEC"))
841  rc_de = f_scansexa(pcdataXMLEle(ep), &de);
842  }
843  // Dont start moving the dome till the mount has initialized all the variables
844  if (HaveRaDec && CanAbsMove())
845  {
846  if (rc_ra == 0 && rc_de == 0)
847  {
848  // everything parsed ok, so lets start the dome to moving
849  // If this slew involves a meridian flip, then the slaving calcs will end up using
850  // the wrong OTA side. Lets set things up so our slaving code will calculate the side
851  // for the target slew instead of using mount pier side info
852  // and see if we can get there at the same time as the mount
853  // TODO: see what happens in a meridian flip with OTASide
856  LOGF_DEBUG("Calling Update mount to anticipate goto target: %g - DEC: %g",
858  UseHourAngle = true;
860  UseHourAngle = false;
861  }
862  }
863 
864  return true;
865  }
866  // Check EOD
867  if (!strcmp("EQUATORIAL_EOD_COORD", propName))
868  {
869  int rc_ra = -1, rc_de = -1;
870  double ra = 0, de = 0;
871 
872  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
873  {
874  const char * elemName = findXMLAttValu(ep, "name");
875 
876  if (!strcmp(elemName, "RA"))
877  rc_ra = f_scansexa(pcdataXMLEle(ep), &ra);
878  else if (!strcmp(elemName, "DEC"))
879  rc_de = f_scansexa(pcdataXMLEle(ep), &de);
880  }
881 
882  if (rc_ra == 0 && rc_de == 0)
883  {
884  // Do not spam log
885  if (std::fabs(mountEquatorialCoords.rightascension - ra) > 0.01
886  || std::fabs(mountEquatorialCoords.declination - de) > 0.01)
887  {
888  char RAStr[64] = {0}, DEStr[64] = {0};
889  fs_sexa(RAStr, ra, 2, 3600);
890  fs_sexa(DEStr, de, 2, 3600);
891 
892  LOGF_DEBUG("Snooped RA %s DEC %s", RAStr, DEStr);
893  }
894 
897  }
898 
900  crackIPState(findXMLAttValu(root, "state"), &m_MountState);
901 
902  // If the diff > 0.1 then the mount is in motion, so let's wait until it settles before moving the doom
905  {
908  //LOGF_DEBUG("Snooped RA: %g - DEC: %g", mountEquatorialCoords.rightascension, mountEquatorialCoords.declination);
909  // a mount still initializing will emit 0 and 0 on the first go
910  // we dont want to process 0/0
912  HaveRaDec = true;
913  }
914  // else mount stable, i.e. tracking, so let's update mount coords and check if we need to move
915  else if (m_MountState == IPS_OK || m_MountState == IPS_IDLE)
917 
918  return true;
919  }
920 
921  // Check Geographic coords
922  if (!strcmp("GEOGRAPHIC_COORD", propName))
923  {
924  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
925  {
926  const char * elemName = findXMLAttValu(ep, "name");
927  if (!strcmp(elemName, "LONG"))
928  {
929  double indiLong;
930  f_scansexa(pcdataXMLEle(ep), &indiLong);
931  if (indiLong > 180)
932  indiLong -= 360;
933  observer.longitude = indiLong;
934  HaveLatLong = true;
935  }
936  else if (!strcmp(elemName, "LAT"))
938  }
939 
940  LOGF_DEBUG("Snooped LONG: %g - LAT: %g", observer.longitude, observer.latitude);
941 
943 
944  return true;
945  }
946 
947  // Check Telescope Park status
948  if (!strcmp("TELESCOPE_PARK", propName))
949  {
950  if (!strcmp(findXMLAttValu(root, "state"), "Ok"))
951  {
952  bool prevState = IsLocked;
953  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
954  {
955  const char * elemName = findXMLAttValu(ep, "name");
956 
957  if ((!strcmp(elemName, "PARK") && !strcmp(pcdataXMLEle(ep), "On")))
958  IsMountParked = true;
959  else if ((!strcmp(elemName, "UNPARK") && !strcmp(pcdataXMLEle(ep), "On")))
960  IsMountParked = false;
961 
962  if (IsLocked && !strcmp(elemName, "PARK") && !strcmp(pcdataXMLEle(ep), "On"))
963  IsLocked = false;
964  else if (!IsLocked && !strcmp(elemName, "UNPARK") && !strcmp(pcdataXMLEle(ep), "On"))
965  IsLocked = true;
966  }
967  if (prevState != IsLocked && MountPolicyS[1].s == ISS_ON)
968  LOGF_INFO("Telescope status changed. Lock is set to: %s",
969  IsLocked ? "locked" : "unlocked");
970  }
971  return true;
972  }
973 
974  // Weather Status
975  // JM 2020-07-16: Weather handling moved to Watchdog driver
976 #if 0
977  if (!strcmp("WEATHER_STATUS", propName))
978  {
979  weatherState = IPS_ALERT;
980  crackIPState(findXMLAttValu(root, "state"), &weatherState);
981 
982  if (weatherState == IPS_ALERT)
983  {
984  if (CanPark())
985  {
986  if (!isParked())
987  {
988  if (AutoParkS[0].s == ISS_ON)
989  {
990  LOG_WARN("Weather conditions in the danger zone! Parking dome...");
991  Dome::Park();
992  }
993  else
994  {
995  LOG_WARN("Weather conditions in the danger zone! AutoPark is disabled. Please park the dome!");
996  }
997  }
998  }
999  else
1000  LOG_WARN("Weather conditions in the danger zone! Close the dome immediately!");
1001 
1002  return true;
1003  }
1004  }
1005 #endif
1006  if (!strcmp("TELESCOPE_PIER_SIDE", propName))
1007  {
1008  // crack the message
1009  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
1010  {
1011  const char * elemName = findXMLAttValu(ep, "name");
1012 
1013  if (!strcmp(elemName, "PIER_EAST") && !strcmp(pcdataXMLEle(ep), "On"))
1014  mountOTASide = -1;
1015  else if (!strcmp(elemName, "PIER_WEST") && !strcmp(pcdataXMLEle(ep), "On"))
1016  mountOTASide = 1;
1017  }
1018 
1019  return true;
1020  }
1021 
1022  controller->ISSnoopDevice(root);
1023 
1024  return DefaultDevice::ISSnoopDevice(root);
1025 }
1026 
1027 bool Dome::SetBacklash(int32_t steps)
1028 {
1029  INDI_UNUSED(steps);
1030  LOG_ERROR("Dome does not support backlash compensation.");
1031  return false;
1032 }
1033 
1034 bool Dome::SetBacklashEnabled(bool enabled)
1035 {
1036  // If disabled, set the Domeer backlash to zero.
1037  if (enabled)
1038  return SetBacklash(static_cast<int32_t>(DomeBacklashN[0].value));
1039  else
1040  return SetBacklash(0);
1041 }
1042 
1043 bool Dome::saveConfigItems(FILE * fp)
1044 {
1046 
1054 
1055  if (HasBacklash())
1056  {
1059  }
1060 
1061  if (HasShutter())
1062  {
1064  }
1065 
1066  controller->saveConfigItems(fp);
1067 
1068  return true;
1069 }
1070 
1071 void Dome::triggerSnoop(const char * driverName, const char * snoopedProp)
1072 {
1073  LOGF_DEBUG("Active Snoop, driver: %s, property: %s", driverName, snoopedProp);
1074  IDSnoopDevice(driverName, snoopedProp);
1075 }
1076 
1078 {
1079  return MountPolicyS[1].s == ISS_ON && IsLocked;
1080 }
1081 
1082 void Dome::buttonHelper(const char * button_n, ISState state, void * context)
1083 {
1084  static_cast<Dome *>(context)->processButton(button_n, state);
1085 }
1086 
1087 void Dome::processButton(const char * button_n, ISState state)
1088 {
1089  //ignore OFF
1090  if (state == ISS_OFF)
1091  return;
1092 
1093  // Dome In
1094  if (!strcmp(button_n, "Dome CW"))
1095  {
1096  if (DomeMotionSP.s != IPS_BUSY)
1098  else
1100  }
1101  else if (!strcmp(button_n, "Dome CCW"))
1102  {
1103  if (DomeMotionSP.s != IPS_BUSY)
1105  else
1107  }
1108  else if (!strcmp(button_n, "Dome Abort"))
1109  {
1110  Dome::Abort();
1111  }
1112 }
1113 
1115 {
1116  return m_MountState;
1117 }
1118 
1120 {
1121  switch (value)
1122  {
1123  case SHUTTER_OPENED:
1127  break;
1128 
1129  case SHUTTER_CLOSED:
1133  break;
1134 
1135 
1136  case SHUTTER_MOVING:
1138  break;
1139 
1140  case SHUTTER_ERROR:
1142  LOG_WARN("Shutter failure.");
1143  break;
1144 
1145  case SHUTTER_UNKNOWN:
1148  LOG_WARN("Unknown shutter status.");
1149  break;
1150  }
1151 
1152  IDSetSwitch(&DomeShutterSP, nullptr);
1153  m_ShutterState = value;
1154 }
1155 
1157 {
1158  switch (value)
1159  {
1160  case DOME_IDLE:
1161  if (DomeMotionSP.s == IPS_BUSY)
1162  {
1165  IDSetSwitch(&DomeMotionSP, nullptr);
1166  }
1167  if (DomeAbsPosNP.s == IPS_BUSY)
1168  {
1170  IDSetNumber(&DomeAbsPosNP, nullptr);
1171  }
1172  if (DomeRelPosNP.s == IPS_BUSY)
1173  {
1175  IDSetNumber(&DomeRelPosNP, nullptr);
1176  }
1177  break;
1178 
1179  case DOME_SYNCED:
1180  if (DomeMotionSP.s == IPS_BUSY)
1181  {
1183  DomeMotionSP.s = IPS_OK;
1184  IDSetSwitch(&DomeMotionSP, nullptr);
1185  }
1186  if (DomeAbsPosNP.s == IPS_BUSY)
1187  {
1188  DomeAbsPosNP.s = IPS_OK;
1189  IDSetNumber(&DomeAbsPosNP, nullptr);
1190  }
1191  if (DomeRelPosNP.s == IPS_BUSY)
1192  {
1193  DomeRelPosNP.s = IPS_OK;
1194  IDSetNumber(&DomeRelPosNP, nullptr);
1195  }
1196  break;
1197 
1198  case DOME_PARKED:
1199  if (DomeMotionSP.s == IPS_BUSY)
1200  {
1203  IDSetSwitch(&DomeMotionSP, nullptr);
1204  }
1205  if (DomeAbsPosNP.s == IPS_BUSY)
1206  {
1208  IDSetNumber(&DomeAbsPosNP, nullptr);
1209  }
1210  if (DomeRelPosNP.s == IPS_BUSY)
1211  {
1213  IDSetNumber(&DomeRelPosNP, nullptr);
1214  }
1216  ParkSP.s = IPS_OK;
1217  ParkS[0].s = ISS_ON;
1218  IDSetSwitch(&ParkSP, nullptr);
1219  IsParked = true;
1220  break;
1221 
1222  case DOME_PARKING:
1224  ParkSP.s = IPS_BUSY;
1225  ParkS[0].s = ISS_ON;
1226  IDSetSwitch(&ParkSP, nullptr);
1227  break;
1228 
1229  case DOME_UNPARKING:
1231  ParkSP.s = IPS_BUSY;
1232  ParkS[1].s = ISS_ON;
1233  IDSetSwitch(&ParkSP, nullptr);
1234  break;
1235 
1236  case DOME_UNPARKED:
1238  ParkSP.s = IPS_OK;
1239  ParkS[1].s = ISS_ON;
1240  IDSetSwitch(&ParkSP, nullptr);
1241  IsParked = false;
1242  break;
1243 
1244  case DOME_UNKNOWN:
1246  ParkSP.s = IPS_IDLE;
1247  IsParked = false;
1248  IDSetSwitch(&ParkSP, nullptr);
1249  break;
1250 
1251  case DOME_ERROR:
1252  ParkSP.s = IPS_ALERT;
1253  IDSetSwitch(&ParkSP, nullptr);
1254  break;
1255 
1256  case DOME_MOVING:
1257  break;
1258  }
1259 
1260  m_DomeState = value;
1261 }
1262 
1263 /*
1264 The problem to get a dome azimuth given a telescope azimuth, altitude and geometry (telescope placement, mount geometry) can be seen as solve the intersection between the optical axis with the dome, that is, the intersection between a line and a sphere.
1265 To do that we need to calculate the optical axis line taking the centre of the dome as origin of coordinates.
1266 */
1267 
1268 // Returns false if it can't solve it due bad geometry of the observatory
1269 // Returns:
1270 // Az : Azimuth required to the dome in order to center the shutter aperture with telescope
1271 // minAz: Minimum azimuth in order to avoid any dome interference to the full aperture of the telescope
1272 // maxAz: Maximum azimuth in order to avoid any dome interference to the full aperture of the telescope
1273 bool Dome::GetTargetAz(double &Az, double &Alt, double &minAz, double &maxAz)
1274 {
1275  point3D MountCenter, OptCenter, OptVector, DomeIntersect;
1276  double hourAngle;
1277  double mu1, mu2;
1278 
1279  if (HaveLatLong == false)
1280  {
1281  triggerSnoop(ActiveDeviceT[0].text, "GEOGRAPHIC_COORD");
1282  LOG_WARN( "Geographic coordinates are not yet defined, triggering snoop...");
1283  return false;
1284  }
1285 
1286  double JD = ln_get_julian_from_sys();
1287  double MSD = ln_get_mean_sidereal_time(JD);
1288 
1289  LOGF_DEBUG("JD: %g - MSD: %g", JD, MSD);
1290 
1291  MountCenter.x = DomeMeasurementsN[DM_EAST_DISPLACEMENT].value; // Positive to East
1292  MountCenter.y = DomeMeasurementsN[DM_NORTH_DISPLACEMENT].value; // Positive to North
1293  MountCenter.z = DomeMeasurementsN[DM_UP_DISPLACEMENT].value; // Positive Up
1294 
1295  LOGF_DEBUG("MC.x: %g - MC.y: %g MC.z: %g", MountCenter.x, MountCenter.y, MountCenter.z);
1296 
1297  // Get hour angle in hours
1298  hourAngle = rangeHA( MSD + observer.longitude / 15.0 - mountEquatorialCoords.rightascension);
1299 
1300  LOGF_DEBUG("HA: %g Lng: %g RA: %g", hourAngle, observer.longitude, mountEquatorialCoords.rightascension);
1301 
1302  int OTASide = 0; // Side of the telescope with respect of the mount, 1: west, -1: east, 0: use the mid point
1303 
1304  if (OTASideSP.s == IPS_OK)
1305  {
1306  if(OTASideS[DM_OTA_SIDE_HA].s == ISS_ON || (UseHourAngle && OTASideS[DM_OTA_SIDE_MOUNT].s == ISS_ON))
1307  {
1308  // Note if the telescope points West, OTA is at east of the pier, and viceversa.
1309  if(hourAngle > 0)
1310  OTASide = -1;
1311  else
1312  OTASide = 1;
1313  }
1314  else if(OTASideS[DM_OTA_SIDE_EAST].s == ISS_ON)
1315  OTASide = -1;
1316  else if(OTASideS[DM_OTA_SIDE_WEST].s == ISS_ON)
1317  OTASide = 1;
1318  else if(OTASideS[DM_OTA_SIDE_MOUNT].s == ISS_ON)
1319  OTASide = mountOTASide;
1320 
1321  LOGF_DEBUG("OTA_SIDE selection: %d", IUFindOnSwitchIndex(&OTASideSP));
1322  }
1323 
1324  OpticalCenter(MountCenter, OTASide * DomeMeasurementsN[DM_OTA_OFFSET].value, observer.latitude, hourAngle, OptCenter);
1325 
1326  LOGF_DEBUG("OTA_SIDE: %d", OTASide);
1327  LOGF_DEBUG("Mount OTA_SIDE: %d", mountOTASide);
1328  LOGF_DEBUG("OTA_OFFSET: %g Lat: %g", DomeMeasurementsN[DM_OTA_OFFSET].value, observer.latitude);
1329  LOGF_DEBUG("OC.x: %g - OC.y: %g OC.z: %g", OptCenter.x, OptCenter.y, OptCenter.z);
1330 
1331  // To be sure mountHoriztonalCoords is up to date.
1333 
1334  // Get optical axis point. This and the previous form the optical axis line
1337  LOGF_DEBUG("OV.x: %g - OV.y: %g OV.z: %g", OptVector.x, OptVector.y, OptVector.z);
1338 
1339  if (Intersection(OptCenter, OptVector, DomeMeasurementsN[DM_DOME_RADIUS].value, mu1, mu2))
1340  {
1341  // If telescope is pointing over the horizon, the solution is mu1, else is mu2
1342  if (mu1 < 0)
1343  mu1 = mu2;
1344 
1345  double yx;
1346  double HalfApertureChordAngle;
1347  double RadiusAtAlt;
1348 
1349  DomeIntersect.x = OptCenter.x + mu1 * (OptVector.x );
1350  DomeIntersect.y = OptCenter.y + mu1 * (OptVector.y );
1351  DomeIntersect.z = OptCenter.z + mu1 * (OptVector.z );
1352 
1353  if (fabs(DomeIntersect.x) > 0.00001)
1354  {
1355  yx = DomeIntersect.y / DomeIntersect.x;
1356  Az = 90 - 180 * atan(yx) / M_PI;
1357  if (DomeIntersect.x < 0)
1358  {
1359  Az = Az + 180;
1360  }
1361  if (Az >= 360)
1362  Az -= 360;
1363  else if (Az < 0)
1364  Az += 360;
1365  }
1366  else
1367  {
1368  // Dome East-West line or zenith
1369  if (DomeIntersect.y > 0)
1370  Az = 90;
1371  else
1372  Az = 270;
1373  }
1374 
1375  if ((fabs(DomeIntersect.x) > 0.00001) || (fabs(DomeIntersect.y) > 0.00001))
1376  Alt = 180 *
1377  atan(DomeIntersect.z /
1378  sqrt((DomeIntersect.x * DomeIntersect.x) + (DomeIntersect.y * DomeIntersect.y))) /
1379  M_PI;
1380  else
1381  Alt = 90; // Dome Zenith
1382 
1383  // Calculate the Azimuth range in the given Altitude of the dome
1384  RadiusAtAlt = DomeMeasurementsN[DM_DOME_RADIUS].value * cos(M_PI * Alt / 180); // Radius alt the given altitude
1385 
1386  if (DomeMeasurementsN[DM_SHUTTER_WIDTH].value < (2 * RadiusAtAlt))
1387  {
1388  HalfApertureChordAngle = 180 * asin(DomeMeasurementsN[DM_SHUTTER_WIDTH].value / (2 * RadiusAtAlt)) /
1389  M_PI; // Angle of a chord of half aperture length
1390  minAz = Az - HalfApertureChordAngle;
1391  if (minAz < 0)
1392  minAz = minAz + 360;
1393  maxAz = Az + HalfApertureChordAngle;
1394  if (maxAz >= 360)
1395  maxAz = maxAz - 360;
1396  }
1397  else
1398  {
1399  minAz = 0;
1400  maxAz = 360;
1401  }
1402  return true;
1403  }
1404 
1405  return false;
1406 }
1407 
1408 bool Dome::Intersection(point3D p1, point3D dp, double r, double &mu1, double &mu2)
1409 {
1410  double a, b, c;
1411  double bb4ac;
1412 
1413  a = dp.x * dp.x + dp.y * dp.y + dp.z * dp.z;
1414  b = 2 * (dp.x * p1.x + dp.y * p1.y + dp.z * p1.z);
1415  c = 0.0;
1416  c = c + p1.x * p1.x + p1.y * p1.y + p1.z * p1.z;
1417  c = c - r * r;
1418  bb4ac = b * b - 4 * a * c;
1419  if ((fabs(a) < 0.0000001) || (bb4ac < 0))
1420  {
1421  mu1 = 0;
1422  mu2 = 0;
1423  return false;
1424  }
1425 
1426  mu1 = (-b + sqrt(bb4ac)) / (2 * a);
1427  mu2 = (-b - sqrt(bb4ac)) / (2 * a);
1428 
1429  return true;
1430 }
1431 
1432 bool Dome::OpticalCenter(point3D MountCenter, double dOpticalAxis, double Lat, double Ah, point3D &OP)
1433 {
1434  double q, f;
1435  double cosf, sinf, cosq, sinq;
1436 
1437  // Note: this transformation is a circle rotated around X axis -(90 - Lat) degrees
1438  q = M_PI * (90 - Lat) / 180;
1439  f = -M_PI * (180 + Ah * 15) / 180;
1440 
1441  cosf = cos(f);
1442  sinf = sin(f);
1443  cosq = cos(q);
1444  sinq = sin(q);
1445 
1446  OP.x = (dOpticalAxis * cosf + MountCenter.x); // The sign of dOpticalAxis determines de side of the tube
1447  OP.y = (dOpticalAxis * sinf * cosq + MountCenter.y);
1448  OP.z = (dOpticalAxis * sinf * sinq + MountCenter.z);
1449 
1450  return true;
1451 }
1452 
1453 bool Dome::OpticalVector(double Az, double Alt, point3D &OV)
1454 {
1455  double q, f;
1456 
1457  q = M_PI * Alt / 180;
1458  f = M_PI * Az / 180;
1459  OV.x = cos(q) * sin(f);
1460  OV.y = cos(q) * cos(f);
1461  OV.z = sin(q);
1462 
1463  return true;
1464 }
1465 
1466 double Dome::Csc(double x)
1467 {
1468  return 1.0 / sin(x);
1469 }
1470 
1471 double Dome::Sec(double x)
1472 {
1473  return 1.0 / cos(x);
1474 }
1475 
1476 bool Dome::CheckHorizon(double HA, double dec, double lat)
1477 {
1478  double sinh_value;
1479 
1480  sinh_value = cos(lat) * cos(HA) * cos(dec) + sin(lat) * sin(dec);
1481 
1482  if (sinh_value >= 0.0)
1483  return true;
1484 
1485  return false;
1486 }
1487 
1489 {
1490  // If not initialized yet, return.
1492  return;
1493 
1494  // Dont do this if we haven't had co-ordinates from the mount yet
1495  if (!HaveLatLong)
1496  return;
1497  if (!HaveRaDec)
1498  return;
1499 
1501 
1502  // Control debug flooding
1505  {
1508  LOGF_DEBUG("Updated telescope Az: %g - Alt: %g", prev_az, prev_alt);
1509  }
1510 
1511  // Check if we need to move if mount is unparked.
1512  if (IsMountParked == false)
1513  UpdateAutoSync();
1514 }
1515 
1517 {
1519  {
1520  if (CanPark())
1521  {
1522  if (isParked() == true)
1523  {
1524  if (AutoSyncWarning == false)
1525  {
1526  LOG_WARN("Cannot perform autosync with dome parked. Please unpark to enable autosync operation.");
1527  AutoSyncWarning = true;
1528  }
1529  return;
1530  }
1531  }
1532 
1533  AutoSyncWarning = false;
1534  double targetAz = 0, targetAlt = 0, minAz = 0, maxAz = 0;
1535  bool res;
1536  res = GetTargetAz(targetAz, targetAlt, minAz, maxAz);
1537  if (!res)
1538  {
1539  LOGF_DEBUG("GetTargetAz failed %g", targetAz);
1540  return;
1541  }
1542  LOGF_DEBUG("Calculated target azimuth is %.2f. MinAz: %.2f, MaxAz: %.2f", targetAz, minAz,
1543  maxAz);
1544 
1545  if (fabs(targetAz - DomeAbsPosN[0].value) > DomeParamN[0].value)
1546  {
1547  IPState ret = Dome::MoveAbs(targetAz);
1548  if (ret == IPS_OK)
1549  LOGF_DEBUG("Dome synced to position %.2f degrees.", targetAz);
1550  else if (ret == IPS_BUSY)
1551  LOGF_DEBUG("Dome is syncing to position %.2f degrees...", targetAz);
1552  else
1553  LOG_ERROR("Dome failed to sync to new requested position.");
1554 
1555  DomeAbsPosNP.s = ret;
1556  IDSetNumber(&DomeAbsPosNP, nullptr);
1557  }
1558  }
1559 }
1560 
1561 void Dome::SetDomeCapability(uint32_t cap)
1562 {
1563  capability = cap;
1564 
1565  if (CanAbort())
1566  controller->mapController("Dome Abort", "Dome Abort", Controller::CONTROLLER_BUTTON, "BUTTON_3");
1567 }
1568 
1570 {
1571  switch (status)
1572  {
1573  case SHUTTER_OPENED:
1574  return "Shutter is open.";
1575  case SHUTTER_CLOSED:
1576  return "Shutter is closed.";
1577  case SHUTTER_MOVING:
1578  return "Shutter is moving.";
1579  case SHUTTER_ERROR:
1580  return "Shutter has errors.";
1581  case SHUTTER_UNKNOWN:
1582  default:
1583  return "Shutter status is unknown.";
1584  }
1585 }
1586 
1588 {
1589  parkDataType = type;
1590 
1591  switch (parkDataType)
1592  {
1593  case PARK_NONE:
1594  strncpy(DomeMotionS[DOME_CW].label, "Open", MAXINDILABEL);
1595  strncpy(DomeMotionS[DOME_CCW].label, "Close", MAXINDILABEL);
1596  break;
1597 
1598  case PARK_AZ:
1599  IUFillNumber(&ParkPositionN[AXIS_AZ], "PARK_AZ", "AZ D:M:S", "%10.6m", 0.0, 360.0, 0.0, 0);
1600  IUFillNumberVector(&ParkPositionNP, ParkPositionN, 1, getDeviceName(), "DOME_PARK_POSITION", "Park Position",
1601  SITE_TAB, IP_RW, 60, IPS_IDLE);
1602  break;
1603 
1604  case PARK_AZ_ENCODER:
1605  IUFillNumber(&ParkPositionN[AXIS_AZ], "PARK_AZ", "AZ Encoder", "%.0f", 0, 16777215, 1, 0);
1606  IUFillNumberVector(&ParkPositionNP, ParkPositionN, 1, getDeviceName(), "DOME_PARK_POSITION", "Park Position",
1607  SITE_TAB, IP_RW, 60, IPS_IDLE);
1608  break;
1609 
1610  default:
1611  break;
1612  }
1613 }
1614 
1615 void Dome::SyncParkStatus(bool isparked)
1616 {
1617  IsParked = isparked;
1618 
1620 
1621  if (IsParked)
1622  {
1624  DEBUG(Logger::DBG_SESSION, "Dome is parked.");
1625  }
1626  else
1627  {
1629  DEBUG(Logger::DBG_SESSION, "Dome is unparked.");
1630  }
1631 }
1632 
1633 void Dome::SetParked(bool isparked)
1634 {
1635  SyncParkStatus(isparked);
1636  WriteParkData();
1637 }
1638 
1640 {
1641  return IsParked;
1642 }
1643 
1645 {
1646  const char * loadres = LoadParkData();
1647  if (loadres)
1648  {
1649  LOGF_INFO("InitPark: No Park data in file %s: %s", ParkDataFileName.c_str(), loadres);
1650  SyncParkStatus(false);
1651  return false;
1652  }
1653 
1654  SyncParkStatus(isParked());
1655 
1656  if (parkDataType != PARK_NONE)
1657  {
1658  LOGF_DEBUG("InitPark Axis1 %.2f", Axis1ParkPosition);
1659  ParkPositionN[AXIS_AZ].value = Axis1ParkPosition;
1660  IDSetNumber(&ParkPositionNP, nullptr);
1661 
1662  // If parked, store the position as current azimuth angle or encoder ticks
1663  if (isParked() && CanAbsMove())
1664  {
1665  DomeAbsPosN[0].value = ParkPositionN[AXIS_AZ].value;
1666  IDSetNumber(&DomeAbsPosNP, nullptr);
1667  }
1668  }
1669 
1670  return true;
1671 }
1672 
1673 const char * Dome::LoadParkXML()
1674 {
1675  wordexp_t wexp;
1676  FILE * fp;
1677  LilXML * lp;
1678  static char errmsg[512];
1679 
1680  XMLEle * parkxml;
1681  XMLAtt * ap;
1682  bool devicefound = false;
1683 
1684  ParkDeviceName = getDeviceName();
1685  ParkstatusXml = nullptr;
1686  ParkdeviceXml = nullptr;
1687  ParkpositionXml = nullptr;
1688  ParkpositionAxis1Xml = nullptr;
1689 
1690  if (wordexp(ParkDataFileName.c_str(), &wexp, 0))
1691  {
1692  wordfree(&wexp);
1693  return "Badly formed filename.";
1694  }
1695 
1696  if (!(fp = fopen(wexp.we_wordv[0], "r")))
1697  {
1698  wordfree(&wexp);
1699  return strerror(errno);
1700  }
1701  wordfree(&wexp);
1702 
1703  lp = newLilXML();
1704 
1705  if (ParkdataXmlRoot)
1706  delXMLEle(ParkdataXmlRoot);
1707 
1708  ParkdataXmlRoot = readXMLFile(fp, lp, errmsg);
1709  fclose(fp);
1710 
1711  delLilXML(lp);
1712  if (!ParkdataXmlRoot)
1713  return errmsg;
1714 
1715  if (!strcmp(tagXMLEle(nextXMLEle(ParkdataXmlRoot, 1)), "parkdata"))
1716  return "Not a park data file";
1717 
1718  parkxml = nextXMLEle(ParkdataXmlRoot, 1);
1719 
1720  while (parkxml)
1721  {
1722  if (strcmp(tagXMLEle(parkxml), "device"))
1723  {
1724  parkxml = nextXMLEle(ParkdataXmlRoot, 0);
1725  continue;
1726  }
1727  ap = findXMLAtt(parkxml, "name");
1728  if (ap && (!strcmp(valuXMLAtt(ap), ParkDeviceName)))
1729  {
1730  devicefound = true;
1731  break;
1732  }
1733  parkxml = nextXMLEle(ParkdataXmlRoot, 0);
1734  }
1735 
1736  if (!devicefound)
1737  return "No park data found for this device";
1738 
1739  ParkdeviceXml = parkxml;
1740  ParkstatusXml = findXMLEle(parkxml, "parkstatus");
1741 
1742  if (parkDataType != PARK_NONE)
1743  {
1744  ParkpositionXml = findXMLEle(parkxml, "parkposition");
1745  ParkpositionAxis1Xml = findXMLEle(ParkpositionXml, "axis1position");
1746 
1747  if (ParkpositionAxis1Xml == nullptr)
1748  {
1749  return "Park position invalid or missing.";
1750  }
1751  }
1752  else if (ParkstatusXml == nullptr)
1753  return "Park status invalid or missing.";
1754 
1755  return nullptr;
1756 }
1757 
1758 const char * Dome::LoadParkData()
1759 {
1760  IsParked = false;
1761 
1762  const char * result = LoadParkXML();
1763  if (result != nullptr)
1764  return result;
1765 
1766  if (!strcmp(pcdataXMLEle(ParkstatusXml), "true"))
1767  IsParked = true;
1768 
1769  if (parkDataType == PARK_NONE)
1770  return nullptr;
1771 
1772  double axis1Pos = std::numeric_limits<double>::quiet_NaN();
1773 
1774  int rc = sscanf(pcdataXMLEle(ParkpositionAxis1Xml), "%lf", &axis1Pos);
1775  if (rc != 1)
1776  {
1777  return "Unable to parse Park Position Axis 1.";
1778  }
1779 
1780  if (std::isnan(axis1Pos) == false)
1781  {
1782  Axis1ParkPosition = axis1Pos;
1783  return nullptr;
1784  }
1785 
1786  return "Failed to parse Park Position.";
1787 }
1788 
1790 {
1791  // We need to refresh parking data in case other devices parking states were updated since we
1792  // read the data the first time.
1793  if (LoadParkXML() != nullptr)
1794  LOG_DEBUG("Failed to refresh parking data.");
1795 
1796  wordexp_t wexp;
1797  FILE * fp;
1798  char pcdata[30];
1799  ParkDeviceName = getDeviceName();
1800 
1801  if (wordexp(ParkDataFileName.c_str(), &wexp, 0))
1802  {
1803  wordfree(&wexp);
1804  LOGF_INFO("WriteParkData: can not write file %s: Badly formed filename.",
1805  ParkDataFileName.c_str());
1806  return false;
1807  }
1808 
1809  if (!(fp = fopen(wexp.we_wordv[0], "w")))
1810  {
1811  wordfree(&wexp);
1812  LOGF_INFO("WriteParkData: can not write file %s: %s", ParkDataFileName.c_str(),
1813  strerror(errno));
1814  return false;
1815  }
1816 
1817  if (!ParkdataXmlRoot)
1818  ParkdataXmlRoot = addXMLEle(nullptr, "parkdata");
1819 
1820  if (!ParkdeviceXml)
1821  {
1822  ParkdeviceXml = addXMLEle(ParkdataXmlRoot, "device");
1823  addXMLAtt(ParkdeviceXml, "name", ParkDeviceName);
1824  }
1825 
1826  if (!ParkstatusXml)
1827  ParkstatusXml = addXMLEle(ParkdeviceXml, "parkstatus");
1828  if (parkDataType != PARK_NONE)
1829  {
1830  if (!ParkpositionXml)
1831  ParkpositionXml = addXMLEle(ParkdeviceXml, "parkposition");
1832  if (!ParkpositionAxis1Xml)
1833  ParkpositionAxis1Xml = addXMLEle(ParkpositionXml, "axis1position");
1834  }
1835 
1836  editXMLEle(ParkstatusXml, (IsParked ? "true" : "false"));
1837 
1838  if (parkDataType != PARK_NONE)
1839  {
1840  snprintf(pcdata, sizeof(pcdata), "%lf", Axis1ParkPosition);
1841  editXMLEle(ParkpositionAxis1Xml, pcdata);
1842  }
1843 
1844  prXMLEle(fp, ParkdataXmlRoot, 0);
1845  fclose(fp);
1846  wordfree(&wexp);
1847 
1848  return true;
1849 }
1850 
1852 {
1853  return Axis1ParkPosition;
1854 }
1855 
1857 {
1858  return Axis1DefaultParkPosition;
1859 }
1860 
1861 void Dome::SetAxis1Park(double value)
1862 {
1863  Axis1ParkPosition = value;
1864  ParkPositionN[AXIS_RA].value = value;
1865  IDSetNumber(&ParkPositionNP, nullptr);
1866 }
1867 
1868 void Dome::SetAxis1ParkDefault(double value)
1869 {
1870  Axis1DefaultParkPosition = value;
1871 }
1872 
1874 {
1875  // Check if it is already parked.
1876  if (CanPark())
1877  {
1878  if (parkDataType != PARK_NONE && isParked())
1879  {
1880  LOG_WARN( "Please unpark the dome before issuing any motion commands.");
1881  return IPS_ALERT;
1882  }
1883  }
1884 
1885  if ((DomeMotionSP.s != IPS_BUSY && (DomeAbsPosNP.s == IPS_BUSY || DomeRelPosNP.s == IPS_BUSY)) ||
1887  {
1888  LOG_WARN( "Please stop dome before issuing any further motion commands.");
1889  return IPS_ALERT;
1890  }
1891 
1892  int current_direction = IUFindOnSwitchIndex(&DomeMotionSP);
1893 
1894  // if same move requested, return
1895  if (DomeMotionSP.s == IPS_BUSY && current_direction == dir && operation == MOTION_START)
1896  return IPS_BUSY;
1897 
1898  DomeMotionSP.s = Move(dir, operation);
1899 
1900  if (DomeMotionSP.s == IPS_BUSY || DomeMotionSP.s == IPS_OK)
1901  {
1902  m_DomeState = (operation == MOTION_START) ? DOME_MOVING : DOME_IDLE;
1904  if (operation == MOTION_START)
1905  DomeMotionS[dir].s = ISS_ON;
1906  }
1907 
1908  IDSetSwitch(&DomeMotionSP, nullptr);
1909 
1910  return DomeMotionSP.s;
1911 }
1912 
1913 IPState Dome::MoveRel(double azDiff)
1914 {
1915  if (CanRelMove() == false)
1916  {
1917  LOG_ERROR( "Dome does not support relative motion.");
1918  return IPS_ALERT;
1919  }
1920 
1921  if (m_DomeState == DOME_PARKED)
1922  {
1923  LOG_ERROR( "Please unpark before issuing any motion commands.");
1925  IDSetNumber(&DomeRelPosNP, nullptr);
1926  return IPS_ALERT;
1927  }
1928 
1930  {
1931  LOG_WARN( "Please stop dome before issuing any further motion commands.");
1933  IDSetNumber(&DomeRelPosNP, nullptr);
1934  return IPS_ALERT;
1935  }
1936 
1937  IPState rc;
1938 
1939  if ((rc = MoveRel(azDiff)) == IPS_OK)
1940  {
1942  DomeRelPosNP.s = IPS_OK;
1943  DomeRelPosN[0].value = azDiff;
1944  IDSetNumber(&DomeRelPosNP, "Dome moved %.2f degrees %s.", azDiff,
1945  (azDiff > 0) ? "clockwise" : "counter clockwise");
1946  if (CanAbsMove())
1947  {
1948  DomeAbsPosNP.s = IPS_OK;
1949  IDSetNumber(&DomeAbsPosNP, nullptr);
1950  }
1951  return IPS_OK;
1952  }
1953  else if (rc == IPS_BUSY)
1954  {
1956  DomeRelPosN[0].value = azDiff;
1958  IDSetNumber(&DomeRelPosNP, "Dome is moving %.2f degrees %s...", azDiff,
1959  (azDiff > 0) ? "clockwise" : "counter clockwise");
1960  if (CanAbsMove())
1961  {
1963  IDSetNumber(&DomeAbsPosNP, nullptr);
1964  }
1965 
1968  DomeMotionS[DOME_CW].s = (azDiff > 0) ? ISS_ON : ISS_OFF;
1969  DomeMotionS[DOME_CCW].s = (azDiff < 0) ? ISS_ON : ISS_OFF;
1970  IDSetSwitch(&DomeMotionSP, nullptr);
1971  return IPS_BUSY;
1972  }
1973 
1976  LOG_WARN("Dome failed to move to new requested position.");
1977  IDSetNumber(&DomeRelPosNP, nullptr);
1978  return IPS_ALERT;
1979 }
1980 
1982 {
1983  if (CanAbsMove() == false)
1984  {
1985  LOG_ERROR("Dome does not support MoveAbs(). MoveAbs() must be implemented in the child class.");
1986  return IPS_ALERT;
1987  }
1988 
1989  if (m_DomeState == DOME_PARKED)
1990  {
1991  LOG_ERROR( "Please unpark before issuing any motion commands.");
1993  IDSetNumber(&DomeAbsPosNP, nullptr);
1994  return IPS_ALERT;
1995  }
1996 
1998  {
1999  LOG_WARN( "Please stop dome before issuing any further motion commands.");
2000  return IPS_ALERT;
2001  }
2002 
2003  IPState rc;
2004 
2005  if (az < DomeAbsPosN[0].min || az > DomeAbsPosN[0].max)
2006  {
2007  LOGF_ERROR( "Error: requested azimuth angle %.2f is out of range.", az);
2009  IDSetNumber(&DomeAbsPosNP, nullptr);
2010  return IPS_ALERT;
2011  }
2012 
2013  if ((rc = MoveAbs(az)) == IPS_OK)
2014  {
2016  DomeAbsPosNP.s = IPS_OK;
2017  DomeAbsPosN[0].value = az;
2018  LOGF_INFO("Dome moved to position %.2f degrees azimuth.", az);
2019  IDSetNumber(&DomeAbsPosNP, nullptr);
2020 
2021  return IPS_OK;
2022  }
2023  else if (rc == IPS_BUSY)
2024  {
2027  LOGF_INFO("Dome is moving to position %.2f degrees azimuth...", az);
2028  IDSetNumber(&DomeAbsPosNP, nullptr);
2029 
2032  DomeMotionS[DOME_CW].s = (az > DomeAbsPosN[0].value) ? ISS_ON : ISS_OFF;
2033  DomeMotionS[DOME_CCW].s = (az < DomeAbsPosN[0].value) ? ISS_ON : ISS_OFF;
2034  IDSetSwitch(&DomeMotionSP, nullptr);
2035 
2036  return IPS_BUSY;
2037  }
2038 
2041  IDSetNumber(&DomeAbsPosNP, "Dome failed to move to new requested position.");
2042  return IPS_ALERT;
2043 }
2044 
2045 bool Dome::Sync(double az)
2046 {
2047  INDI_UNUSED(az);
2048  LOG_WARN("Syncing is not supported.");
2049  return false;
2050 }
2051 
2053 {
2054  if (CanAbort() == false)
2055  {
2056  LOG_ERROR( "Dome does not support abort.");
2057  return false;
2058  }
2059 
2061 
2062  if (Abort())
2063  {
2064  AbortSP.s = IPS_OK;
2065 
2067  {
2069  if (m_DomeState == DOME_PARKING)
2070  {
2071  DEBUG(Logger::DBG_SESSION, "Parking aborted.");
2072  // If parking was aborted then it was UNPARKED before
2073  ParkS[1].s = ISS_ON;
2074  }
2075  else
2076  {
2077  DEBUG(Logger::DBG_SESSION, "UnParking aborted.");
2078  // If unparking aborted then it was PARKED before
2079  ParkS[0].s = ISS_ON;
2080  }
2081 
2082  ParkSP.s = IPS_ALERT;
2083  IDSetSwitch(&ParkSP, nullptr);
2084  }
2085 
2087  }
2088  else
2089  {
2090  AbortSP.s = IPS_ALERT;
2091 
2092  // If alert was aborted during parking or unparking, the parking state is unknown
2094  {
2096  ParkSP.s = IPS_IDLE;
2097  IDSetSwitch(&ParkSP, nullptr);
2098  }
2099  }
2100 
2101  IDSetSwitch(&AbortSP, nullptr);
2102 
2103  return (AbortSP.s == IPS_OK);
2104 }
2105 
2106 bool Dome::SetSpeed(double speed)
2107 {
2108  if (HasVariableSpeed() == false)
2109  {
2110  LOG_ERROR( "Dome does not support variable speed.");
2111  return false;
2112  }
2113 
2114  if (SetSpeed(speed))
2115  {
2116  DomeSpeedNP.s = IPS_OK;
2117  DomeSpeedN[0].value = speed;
2118  }
2119  else
2121 
2122  IDSetNumber(&DomeSpeedNP, nullptr);
2123 
2124  return (DomeSpeedNP.s == IPS_OK);
2125 }
2126 
2128 {
2129  if (HasShutter() == false)
2130  {
2131  LOG_ERROR( "Dome does not have shutter control.");
2132  return IPS_ALERT;
2133  }
2134 
2135  // if (weatherState == IPS_ALERT && operation == SHUTTER_OPEN)
2136  // {
2137  // LOG_WARN( "Weather is in the danger zone! Cannot open shutter.");
2138  // return IPS_ALERT;
2139  // }
2140 
2141  int currentStatus = IUFindOnSwitchIndex(&DomeShutterSP);
2142 
2143  // No change of status, let's return
2144  if (DomeShutterSP.s == IPS_BUSY && currentStatus == operation)
2145  {
2146  IDSetSwitch(&DomeShutterSP, nullptr);
2147  return DomeShutterSP.s;
2148  }
2149 
2150  DomeShutterSP.s = ControlShutter(operation);
2151 
2152  if (DomeShutterSP.s == IPS_OK)
2153  {
2154  IDSetSwitch(&DomeShutterSP, "Shutter is %s.", (operation == SHUTTER_OPEN ? "open" : "closed"));
2156  return DomeShutterSP.s;
2157  }
2158  else if (DomeShutterSP.s == IPS_BUSY)
2159  {
2161  DomeShutterS[operation].s = ISS_ON;
2162  IDSetSwitch(&DomeShutterSP, "Shutter is %s...", (operation == 0 ? "opening" : "closing"));
2164  return DomeShutterSP.s;
2165  }
2166 
2167  IDSetSwitch(&DomeShutterSP, "Shutter failed to %s.", (operation == 0 ? "open" : "close"));
2168  return IPS_ALERT;
2169 }
2170 
2172 {
2173  // Not really supposed to get this at all, but just in case.
2174  if (CanPark() == false)
2175  {
2176  LOG_ERROR( "Dome does not support parking.");
2177  return IPS_ALERT;
2178  }
2179 
2180  // No need to park if parked already.
2181  if (m_DomeState == DOME_PARKED)
2182  {
2184  ParkS[0].s = ISS_ON;
2185  LOG_INFO("Dome already parked.");
2186  IDSetSwitch(&ParkSP, nullptr);
2187  return IPS_OK;
2188  }
2189 
2190  // Check if dome is locked due to Mount Policy
2191  if (isLocked())
2192  {
2194  ParkS[1].s = ISS_ON;
2195  ParkSP.s = IPS_ALERT;
2196  IDSetSwitch(&ParkSP, nullptr);
2197  LOG_INFO("Cannot Park Dome when mount is locking. See: Mount Policy in options tab.");
2198  return IPS_ALERT;
2199  }
2200 
2201  // Now ask child driver to start the actual parking process.
2202  ParkSP.s = Park();
2203 
2204  // IPS_OK is when it is immediately parked so realisticly this does not happen
2205  // unless dome is physically parked but just needed a state change.
2206  if (ParkSP.s == IPS_OK)
2207  SetParked(true);
2208  // Dome is moving to parking position
2209  else if (ParkSP.s == IPS_BUSY)
2210  {
2212 
2213  if (CanAbsMove())
2215 
2217  ParkS[0].s = ISS_ON;
2218  }
2219  else
2220  IDSetSwitch(&ParkSP, nullptr);
2221 
2222  return ParkSP.s;
2223 }
2224 
2226 {
2227  if (CanPark() == false)
2228  {
2229  LOG_ERROR( "Dome does not support parking.");
2230  return IPS_ALERT;
2231  }
2232 
2233  if (m_DomeState != DOME_PARKED)
2234  {
2236  ParkS[1].s = ISS_ON;
2237  DEBUG(Logger::DBG_SESSION, "Dome already unparked.");
2238  IDSetSwitch(&ParkSP, nullptr);
2239  return IPS_OK;
2240  }
2241 
2242  // if (weatherState == IPS_ALERT)
2243  // {
2244  // LOG_WARN( "Weather is in the danger zone! Cannot unpark dome.");
2245  // ParkSP.s = IPS_OK;
2246  // IDSetSwitch(&ParkSP, nullptr);
2247  // return IPS_ALERT;
2248  // }
2249 
2250  ParkSP.s = UnPark();
2251 
2252  if (ParkSP.s == IPS_OK)
2253  SetParked(false);
2254  else if (ParkSP.s == IPS_BUSY)
2256  else
2257  IDSetSwitch(&ParkSP, nullptr);
2258 
2259  return ParkSP.s;
2260 }
2261 
2263 {
2264  LOG_WARN( "Parking is not supported.");
2265  return false;
2266 }
2267 
2269 {
2270  LOG_WARN( "Parking is not supported.");
2271  return false;
2272 }
2273 
2275 {
2276  return false;
2277 }
2278 
2279 bool Dome::callHandshake()
2280 {
2281  if (domeConnection > 0)
2282  {
2285  else if (getActiveConnection() == tcpConnection)
2287  }
2288 
2289  return Handshake();
2290 }
2291 
2293 {
2294  return domeConnection;
2295 }
2296 
2297 void Dome::setDomeConnection(const uint8_t &value)
2298 {
2300 
2301  if (value == 0 || (mask & value) == 0)
2302  {
2303  LOGF_ERROR( "Invalid connection mode %d", value);
2304  return;
2305  }
2306 
2307  domeConnection = value;
2308 }
2309 
2310 }
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
The TCP class manages connection with devices over the network via TCP/IP. Upon successfull connectio...
Definition: connectiontcp.h:38
int getPortFD() const
Definition: connectiontcp.h:84
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
The Controller class provides functionality to access a controller (e.g. joystick) input and send it ...
void setButtonCallback(buttonFunc buttonCallback)
setButtonCallback Sets the callback function when a new button input is detected.
virtual bool ISSnoopDevice(XMLEle *root)
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
void mapController(const char *propertyName, const char *propertyLabel, ControllerType type, const char *initialValue)
mapController adds a new property to the joystick's settings.
virtual bool initProperties()
virtual bool saveConfigItems(FILE *fp)
virtual void ISGetProperties(const char *dev)
virtual bool updateProperties()
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties()
Initilize properties initial state and value. The child class must implement this function.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
Connection::Interface * getActiveConnection()
void addDebugControl()
Add Debug control to the driver.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
Class to provide general functionality of a Dome device.
Definition: indidome.h:75
bool CanPark()
Definition: indidome.h:242
ISwitch DomeShutterS[2]
Definition: indidome.h:549
INumberVectorProperty DomeMeasurementsNP
Definition: indidome.h:587
double prev_alt
Definition: indidome.h:614
INDI::IHorizontalCoordinates mountHoriztonalCoords
Definition: indidome.h:633
INumberVectorProperty PresetNP
Definition: indidome.h:583
Connection::TCP * tcpConnection
Definition: indidome.h:620
DomeState m_DomeState
Definition: indidome.h:623
@ CONNECTION_SERIAL
Definition: indidome.h:172
@ CONNECTION_TCP
Definition: indidome.h:173
@ CONNECTION_NONE
Definition: indidome.h:171
INumber DomeMeasurementsN[6]
Definition: indidome.h:586
ISwitch DomeMotionS[2]
Definition: indidome.h:531
virtual IPState UnPark()
UnPark dome. The action of the Unpark command is dome specific, but it may include opening the shutte...
Definition: indidome.cpp:2225
uint32_t capability
Definition: indidome.h:563
void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Definition: indidome.cpp:1633
void SetDomeCapability(uint32_t cap)
SetDomeCapability set the dome capabilities. All capabilities must be initialized.
Definition: indidome.cpp:1561
void setDomeConnection(const uint8_t &value)
setDomeConnection Set Dome connection mode. Child class should call this in the constructor before Do...
Definition: indidome.cpp:2297
ISwitchVectorProperty PresetGotoSP
Definition: indidome.h:585
const char * GetShutterStatusString(ShutterState status)
getShutterStatusString
Definition: indidome.cpp:1569
INDI::IEquatorialCoordinates mountEquatorialCoords
Definition: indidome.h:634
bool CheckHorizon(double HA, double dec, double lat)
CheckHorizon Returns true if telescope points above horizon.
Definition: indidome.cpp:1476
ISwitch DomeBacklashS[2]
Definition: indidome.h:608
ITextVectorProperty ActiveDeviceTP
Definition: indidome.h:566
ISwitchVectorProperty ShutterParkPolicySP
Definition: indidome.h:574
@ SHUTTER_OPEN_ON_UNPARK
Definition: indidome.h:579
@ SHUTTER_CLOSE_ON_PARK
Definition: indidome.h:578
INumber ParkPositionN[1]
Definition: indidome.h:554
bool isParked()
isParked is dome currently parked?
Definition: indidome.cpp:1639
bool HaveRaDec
Definition: indidome.h:636
bool OpticalCenter(point3D MountCenter, double dOpticalAxis, double Lat, double Ah, point3D &OP)
OpticalCenter This function calculates the distance from the optical axis to the Dome center.
Definition: indidome.cpp:1432
INumber DomeSpeedN[1]
Definition: indidome.h:528
DomeState
Dome status.
Definition: indidome.h:130
@ DOME_UNPARKING
Definition: indidome.h:135
@ DOME_PARKED
Definition: indidome.h:136
@ DOME_ERROR
Definition: indidome.h:139
@ DOME_UNPARKED
Definition: indidome.h:137
@ DOME_PARKING
Definition: indidome.h:134
@ DOME_MOVING
Definition: indidome.h:132
@ DOME_SYNCED
Definition: indidome.h:133
@ DOME_UNKNOWN
Definition: indidome.h:138
virtual bool SetSpeed(double rpm)
SetSpeed Set Dome speed. This does not initiate motion, it sets the speed for the next motion command...
Definition: indidome.cpp:2106
virtual bool Sync(double az)
Sync sets the dome current azimuth as the supplied azimuth position.
Definition: indidome.cpp:2045
ISwitch AbortS[1]
Definition: indidome.h:540
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition: indidome.cpp:259
ShutterState m_ShutterState
Definition: indidome.h:624
double prev_dec
Definition: indidome.h:614
bool CanRelMove()
Definition: indidome.h:234
ISwitchVectorProperty OTASideSP
Definition: indidome.h:589
void SetAxis1Park(double value)
SetRAPark Set current AZ parking position. The data park file (stored in ~/.indi/ParkData....
Definition: indidome.cpp:1861
double prev_ra
Definition: indidome.h:614
void UpdateMountCoords()
updateCoords updates the horizontal coordinates (Az & Alt) of the mount from the snooped RA,...
Definition: indidome.cpp:1488
INumber DomeAbsPosN[1]
Definition: indidome.h:534
INumberVectorProperty DomeParamNP
Definition: indidome.h:542
IPState getMountState() const
Definition: indidome.cpp:1114
ISwitch ShutterParkPolicyS[2]
Definition: indidome.h:575
void SetAxis1ParkDefault(double steps)
SetAxis1Park Set default AZ parking position.
Definition: indidome.cpp:1868
ShutterOperation
Shutter operation command.
Definition: indidome.h:114
@ SHUTTER_CLOSE
Definition: indidome.h:116
@ SHUTTER_OPEN
Definition: indidome.h:115
int PortFD
Definition: indidome.h:617
DomeMotionCommand
Definition: indidome.h:97
@ MOTION_STOP
Definition: indidome.h:99
@ MOTION_START
Definition: indidome.h:98
double Csc(double x)
Definition: indidome.cpp:1466
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indidome.cpp:93
double prev_az
Definition: indidome.h:614
ISwitch ParkOptionS[3]
Definition: indidome.h:557
virtual void UpdateAutoSync()
UpdateAutoSync This function calculates the target dome azimuth from the mount's target coordinates g...
Definition: indidome.cpp:1516
@ DOME_CCW
Definition: indidome.h:94
virtual IPState MoveRel(double azDiff)
Move the Dome to an relative position.
Definition: indidome.cpp:1913
INumberVectorProperty DomeAbsPosNP
Definition: indidome.h:533
@ DM_OTA_SIDE_EAST
Definition: indidome.h:595
@ DM_OTA_SIDE_MOUNT
Definition: indidome.h:597
@ DM_OTA_SIDE_IGNORE
Definition: indidome.h:599
@ DM_OTA_SIDE_WEST
Definition: indidome.h:596
@ DM_OTA_SIDE_HA
Definition: indidome.h:598
double GetAxis1Park()
Definition: indidome.cpp:1851
INumberVectorProperty ParkPositionNP
Definition: indidome.h:555
ISwitchVectorProperty AbortSP
Definition: indidome.h:539
ISwitch OTASideS[5]
Definition: indidome.h:590
ISwitchVectorProperty DomeShutterSP
Definition: indidome.h:548
bool CanSync()
Definition: indidome.h:250
bool HasShutter()
Definition: indidome.h:258
ISwitchVectorProperty ParkOptionSP
Definition: indidome.h:558
@ SHUTTER_ERROR
Definition: indidome.h:151
@ SHUTTER_MOVING
Definition: indidome.h:149
@ SHUTTER_UNKNOWN
Definition: indidome.h:150
@ SHUTTER_OPENED
Definition: indidome.h:147
@ SHUTTER_CLOSED
Definition: indidome.h:148
bool isLocked()
isLocked, is the dome currently locked?
Definition: indidome.cpp:1077
INumber DomeSyncN[1]
Definition: indidome.h:546
INumberVectorProperty DomeSyncNP
Definition: indidome.h:545
int mountOTASide
Definition: indidome.h:602
bool HasVariableSpeed()
Definition: indidome.h:266
virtual bool SetBacklash(int32_t steps)
SetBacklash Set the dome backlash compensation value.
Definition: indidome.cpp:1027
ISwitchVectorProperty ParkSP
Definition: indidome.h:551
INumber PresetN[3]
Definition: indidome.h:582
double GetAxis1ParkDefault()
Definition: indidome.cpp:1856
IText ActiveDeviceT[1]
Definition: indidome.h:567
virtual IPState Park()
Goto Park Position. The park position is an absolute azimuth value.
Definition: indidome.cpp:2171
const char * LoadParkData()
Definition: indidome.cpp:1758
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indidome.cpp:279
ISwitchVectorProperty DomeBacklashSP
Definition: indidome.h:607
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indidome.cpp:492
IPState m_MountState
Definition: indidome.h:625
virtual bool SetDefaultPark()
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition: indidome.cpp:2268
bool Intersection(point3D p1, point3D p2, double r, double &mu1, double &mu2)
Intersection Calculate the intersection of a ray and a sphere. The line segment is defined from p1 to...
Definition: indidome.cpp:1408
virtual bool SetBacklashEnabled(bool enabled)
SetBacklashEnabled Enables or disables the dome backlash compensation.
Definition: indidome.cpp:1034
double Sec(double x)
Definition: indidome.cpp:1471
virtual IPState MoveAbs(double az)
Move the Dome to an absolute azimuth.
Definition: indidome.cpp:1981
bool CanAbort()
Definition: indidome.h:218
INumberVectorProperty DomeRelPosNP
Definition: indidome.h:536
IGeographicCoordinates observer
Definition: indidome.h:628
ISwitch PresetGotoS[3]
Definition: indidome.h:584
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
Definition: indidome.cpp:822
ISwitchVectorProperty DomeMotionSP
Definition: indidome.h:530
static void buttonHelper(const char *button_n, ISState state, void *context)
Definition: indidome.cpp:1082
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
Definition: indidome.cpp:1043
INumberVectorProperty DomeBacklashNP
Definition: indidome.h:611
ISwitch ParkS[2]
Definition: indidome.h:552
virtual bool SetCurrentPark()
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: indidome.cpp:2262
INumber DomeParamN[1]
Definition: indidome.h:543
bool CanAbsMove()
Definition: indidome.h:226
@ PARK_AZ_ENCODER
Definition: indidome.h:107
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: indidome.cpp:795
void setShutterState(const ShutterState &value)
Definition: indidome.cpp:1119
bool OpticalVector(double Az, double Alt, point3D &OV)
OpticalVector This function calculates a second point for determining the optical axis.
Definition: indidome.cpp:1453
ISwitch DomeAutoSyncS[2]
Definition: indidome.h:604
virtual ~Dome()
Definition: indidome.cpp:71
bool HasBacklash()
Definition: indidome.h:274
void setDomeState(const DomeState &value)
Definition: indidome.cpp:1156
@ MOUNT_IGNORED
Definition: indidome.h:122
@ MOUNT_LOCKS
Definition: indidome.h:123
INumberVectorProperty DomeSpeedNP
Definition: indidome.h:527
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
Definition: indidome.cpp:1644
Connection::Serial * serialConnection
Definition: indidome.h:619
void SetParkDataType(DomeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
Definition: indidome.cpp:1587
ISwitchVectorProperty MountPolicySP
Definition: indidome.h:570
bool WriteParkData()
Definition: indidome.cpp:1789
DomeParkData parkDataType
Definition: indidome.h:564
virtual bool Handshake()
perform handshake with device to check communication
Definition: indidome.cpp:2274
@ DM_NORTH_DISPLACEMENT
Definition: indidome.h:85
@ DM_EAST_DISPLACEMENT
Definition: indidome.h:86
@ DM_SHUTTER_WIDTH
Definition: indidome.h:84
@ DM_DOME_RADIUS
Definition: indidome.h:83
@ DM_OTA_OFFSET
Definition: indidome.h:88
@ DM_UP_DISPLACEMENT
Definition: indidome.h:87
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: indidome.cpp:390
INumber DomeRelPosN[1]
Definition: indidome.h:537
bool HaveLatLong
Definition: indidome.h:630
INumber DomeBacklashN[1]
Definition: indidome.h:612
virtual IPState ControlShutter(ShutterOperation operation)
Open or Close shutter.
Definition: indidome.cpp:2127
ISwitchVectorProperty DomeAutoSyncSP
Definition: indidome.h:603
ISwitch MountPolicyS[2]
Definition: indidome.h:571
bool GetTargetAz(double &Az, double &Alt, double &minAz, double &maxAz)
GetTargetAz.
Definition: indidome.cpp:1273
uint8_t getDomeConnection() const
Definition: indidome.cpp:2292
virtual IPState Move(DomeDirection dir, DomeMotionCommand operation)
Move the Dome in a particular direction.
Definition: indidome.cpp:1873
virtual bool Abort()
Abort all dome motion.
Definition: indidome.cpp:2052
void callOnTimeout(const std::function< void()> &callback)
Definition: inditimer.cpp:76
void start()
Starts or restarts the timer with the timeout specified in interval.
Definition: inditimer.cpp:82
void stop()
Stops the timer.
Definition: inditimer.cpp:97
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * SITE_TAB
SITE_TAB Where all site information setting are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
double max(void)
int errno
double min(void)
double ra
double dec
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
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
#define MAXINDILABEL
Definition: indiapi.h:192
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_NOFMANY
Definition: indiapi.h:175
@ ISR_ATMOST1
Definition: indiapi.h:174
@ AXIS_RA
Definition: indibasetypes.h:35
@ AXIS_AZ
Definition: indibasetypes.h:42
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition: indicom.c:205
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition: indicom.c:1225
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition: indicom.c:141
Implementations for common driver routines.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition: indidevapi.c:108
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidevapi.c:20
int crackIPState(const char *str, IPState *ip)
Extract property state (Idle, OK, Busy, Alert) from the supplied string.
Definition: indidevapi.c:576
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
#define DOME_COORD_THRESHOLD
Definition: indidome.cpp:44
#define DOME_SLAVING_TAB
Definition: indidome.cpp:43
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 IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
Function a Driver calls to snoop on another Device. Snooped messages will then arrive via ISSnoopDevi...
Definition: indidriver.c:143
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
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:653
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition: indilogger.h:56
#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
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition: lilxml.cpp:524
LilXML * newLilXML()
Create a new lilxml parser.
Definition: lilxml.cpp:150
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition: lilxml.cpp:644
XMLAtt * addXMLAtt(XMLEle *ep, const char *name, const char *valu)
Add an XML attribute to an existing XML element.
Definition: lilxml.cpp:706
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition: lilxml.cpp:606
void editXMLEle(XMLEle *ep, const char *pcdata)
set the pcdata of the given element
Definition: lilxml.cpp:698
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition: lilxml.cpp:600
void prXMLEle(FILE *fp, XMLEle *ep, int level)
Print an XML element.
Definition: lilxml.cpp:844
XMLEle * readXMLFile(FILE *fp, LilXML *lp, char ynot[])
Handy wrapper to read one xml file.
Definition: lilxml.cpp:653
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition: lilxml.cpp:555
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition: lilxml.cpp:167
void delLilXML(LilXML *lp)
Delete a lilxml parser.
Definition: lilxml.cpp:159
XMLEle * addXMLEle(XMLEle *parent, const char *tag)
add an element with the given tag to the given element. parent can be NULL to make a new root.
Definition: lilxml.cpp:670
XMLEle * findXMLEle(XMLEle *ep, const char *tag)
Find an XML element within an XML element.
Definition: lilxml.cpp:537
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.cpp:624
Namespace to encapsulate INDI client, drivers, and mediator classes.
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition: libastro.cpp:140
__le16 type
Definition: pwc-ioctl.h:0
One switch descriptor.
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250
double y
Definition: indidome.h:34
double x
Definition: indidome.h:34
double z
Definition: indidome.h:34