Instrument Neutral Distributed Interface INDI  2.0.2
ddw_dome.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  DDW Dome INDI Driver
3 
4  Copyright(c) 2020-2023 Jarno Paananen. All rights reserved.
5 
6  based on:
7 
8  ScopeDome Dome INDI Driver
9 
10  Copyright(c) 2017-2023 Jarno Paananen. All rights reserved.
11 
12  and
13 
14  Baader Planetarium Dome INDI Driver
15 
16  Copyright(c) 2014 Jasem Mutlaq. All rights reserved.
17 
18  Baader Dome INDI Driver
19 
20  This library is free software; you can redistribute it and/or
21  modify it under the terms of the GNU Library General Public
22  License version 2 as published by the Free Software Foundation.
23  .
24  This library is distributed in the hope that it will be useful,
25  but WITHOUT ANY WARRANTY; without even the implied warranty of
26  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27  Library General Public License for more details.
28  .
29  You should have received a copy of the GNU Library General Public License
30  along with this library; see the file COPYING.LIB. If not, write to
31  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
32  Boston, MA 02110-1301, USA.
33 *******************************************************************************/
34 
35 #include "ddw_dome.h"
36 
38 #include "indicom.h"
39 
40 #define DDW_TIMEOUT 2
41 
42 // We declare an auto pointer to DDW.
43 std::unique_ptr<DDW> ddw(new DDW());
44 
46 {
47  setVersion(1, 0);
50 }
51 
53 {
55 
56  FirmwareVersionNP[0].fill("VERSION", "Version", "%2.0f", 0.0, 99.0, 1.0, 0.0);
57  FirmwareVersionNP.fill(getDeviceName(), "FIRMWARE", "Firmware", INFO_TAB, IP_RO, 60, IPS_IDLE);
58 
60 
62 
63  // Set serial parameters
65 
66  setPollingPeriodRange(1000, 3000); // Device doesn't like too long interval
68  return true;
69 }
70 
71 /************************************************************************************
72  *
73 * ***********************************************************************************/
75 {
76  if (InitPark())
77  {
78  // If loading parking data is successful, we just set the default parking
79  // values.
81  }
82  else
83  {
84  // Otherwise, we set all parking data to default in case no parking data is
85  // found.
88  }
89 
90  FirmwareVersionNP[0].setValue(fwVersion);
93  return true;
94 }
95 
96 /************************************************************************************
97  *
98 * ***********************************************************************************/
100 {
101  // Send GINF command and check if the controller responds
102  int rc = writeCmd("GINF");
103  if (rc != TTY_OK)
104  return false;
105 
106  std::string response;
107  rc = readStatus(response);
108  if (rc != TTY_OK)
109  return false;
110 
111  LOGF_DEBUG("Initial response: %s", response.c_str());
112 
113  // Response should start with V
114  if (response[0] != 'V')
115  return false;
116 
117  parseGINF(response.c_str());
118  cmdState = IDLE;
120  IDSetNumber(&DomeAbsPosNP, nullptr);
121  return true;
122 }
123 
124 /************************************************************************************
125  *
126 * ***********************************************************************************/
127 const char *DDW::getDefaultName()
128 {
129  return "DDW Dome";
130 }
131 
132 /************************************************************************************
133  *
134 * ***********************************************************************************/
136 {
138 
139  if (isConnected())
140  {
142  SetupParms();
143  }
144  else
145  {
147  }
148 
149  return true;
150 }
151 
152 /************************************************************************************
153  *
154 * ***********************************************************************************/
155 int DDW::writeCmd(const char *cmd)
156 {
157  int nbytes_written = 0, rc = -1;
158  char errstr[MAXRBUF];
159 
160  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
161  {
162  tty_error_msg(rc, errstr, MAXRBUF);
163  LOGF_ERROR("Error writing command: %s. Cmd: %s", errstr, cmd);
164  }
165  return rc;
166 }
167 
168 int DDW::readStatus(std::string &status)
169 {
170  int nbytes_read = 0, rc = -1;
171  char errstr[MAXRBUF];
172 
173  // Read response and parse it
174  char response[256] = { 0 };
175 
176  // Read buffer
177  if ((rc = tty_nread_section(PortFD, response, sizeof(response) - 1, '\r', DDW_TIMEOUT, &nbytes_read)) != TTY_OK)
178  {
179  tty_error_msg(rc, errstr, MAXRBUF);
180  LOGF_ERROR("Error reading: %s.", errstr);
181  }
182  status = response;
183  return rc;
184 }
185 
186 void DDW::parseGINF(const char *response)
187 {
188  /* GINF packet structure:
189 
190  Field Content Note (each datum is separated by comma)
191  1 V# Denotes Version Data. E.g., V1
192  2 Dticks DTICKS is dome circumference in ticks 0-32767. Value is sent as a string of characters, e.g., 457. Leading zeros not transmitted.
193  3 Home1 Azimuth location of the HOME position in ticks 0-32767
194  4 Coast Coast value in ticks (0-255)
195  5 ADAZ Current dome azimuth in Ticks 0-32767
196  6 Slave Slave Status 0=slave off 1=slave on
197  7 Shutter Shutter status 0=indeterminate, 1=closed, 2=open
198  8 DSR status DSR Status 0=indet, 1=closed, 2=open
199  9 Home Home sensor 0=home, 1=not home
200  10 HTICK_CCLK Azimuth ticks of counterclockwise edge of Home position
201  11 HTICK_CLK Azimuth ticks of clockwise edge of Home position
202  12 UPINS Status of all user digital output pins
203  13 WEAAGE Age of weather info in minutes 0 to 255 (255 means expired)
204  14 WINDDIR 0-255 wind direction (use (n/255)*359 to compute actual direction), subtract dome azimuth if weather module is mounted on dome top.
205  15 WINDSPD Windspeed 0-255 miles per hour
206  16 TEMP Temperature 0-255, representing -100 to 155 degrees F
207  17 HUMID Humidity 0-100% relative
208  18 WETNESS Wetness 0 (dry) to 100 (soaking wet)
209  19 SNOW Snow 0 (none) to 100 (sensor covered)
210  20 WIND PEAK Windspeed Peak level over session 0-255 miles per hour
211  21 SCOPEAZ Scope azimuth from LX-200 (999 if not available)
212  22 INTDZ Internal “deadzone”- angular displacement around the dome opening centerline within which desired dome azimuth can change without causing dome movement.
213  23 INTOFF Internal offset- angular distance DDW will add to the desired azimuth, causing the dome to preceed the telescope’s position when a slaved goto occurs.
214  24 car ret
215  25 car ret
216  */
217 
218  int version;
219  int dticks;
220  int homepos;
221  int coast;
222  int azimuth;
223  int slave;
224  int shutter;
225  int dsr;
226  int home;
227  int htick_cclk;
228  int htick_clk;
229  int upins;
230  int weatherAge;
231  int windDir;
232  int windSpd;
233  int temperature;
234  int humidity;
235  int wetness;
236  int snow;
237  int windPeak;
238  int scopeAzimuth;
239  int deadzone;
240  int offset;
241 
242  int num = sscanf(response, "V%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", &version,
243  &dticks, &homepos, &coast, &azimuth, &slave, &shutter, &dsr, &home, &htick_cclk, &htick_clk,
244  &upins, &weatherAge, &windDir, &windSpd, &temperature, &humidity, &wetness, &snow, &windPeak,
245  &scopeAzimuth, &deadzone, &offset);
246 
247  if (num == 23)
248  {
249  fwVersion = version;
250  ticksPerRev = dticks;
251  homeAz = 360.0 * homepos / ticksPerRev;
252 
253  DomeAbsPosN[0].value = 360.0 * azimuth / ticksPerRev;
254 
255  ShutterState newState;
256  switch (shutter)
257  {
258  case 1:
259  newState = SHUTTER_CLOSED;
260  break;
261  case 2:
262  newState = SHUTTER_OPENED;
263  break;
264  default:
265  newState = SHUTTER_UNKNOWN;
266  break;
267  }
268 
269  // Only send if changed to avoid spam
270  if (getShutterState() != newState)
271  {
272  setShutterState(newState);
273  }
274  }
275 }
276 
277 /************************************************************************************
278  *
279 * ***********************************************************************************/
281 {
282  if (!isConnected())
283  return; // No need to reset timer if we are not connected anymore
284 
285  // The controller may write responses at any time due to hand controller usage
286  // and command responses, so need to poll for them here
287  while(true)
288  {
289  char errstr[MAXRBUF];
290  char response[256] = { 0 };
291  int nr;
292  int rc = tty_read(PortFD, response, 1, 0, &nr);
293  if (rc != TTY_OK || nr != 1)
294  {
295  // Nothing more to read
296  if (++watchdog * getCurrentPollingPeriod() > 60 * 1000)
297  {
298  // If we haven't heard from the controller for 60s, send GINF command to keep DDW watchdog from triggering
299  writeCmd("GINF");
300  watchdog = 0; // prevent from triggering immediately again
301  }
302  break; // exit loop
303  }
304 
305  watchdog = 0;
306  switch (response[0])
307  {
308  case 'L':
309  LOG_DEBUG("Moving left");
310  break;
311  case 'R':
312  LOG_DEBUG("Moving right");
313  break;
314  case 'T':
315  LOG_DEBUG("Move tick");
316  break;
317  case 'P':
318  {
319  // Read tick count
320  if ((rc = tty_nread_section(PortFD, response + 1, sizeof(response) - 2, '\r', DDW_TIMEOUT, &nr)) !=
321  TTY_OK)
322  {
323  tty_error_msg(rc, errstr, MAXRBUF);
324  LOGF_ERROR("Error reading: %s.", errstr);
325  }
326  int tick = -1;
327  sscanf(response, "P%d", &tick);
328  LOGF_DEBUG("Tick counter %d", tick);
329 
330  // Update current position
331  DomeAbsPosN[0].value = 359.0 * tick / ticksPerRev;
332  IDSetNumber(&DomeAbsPosNP, nullptr);
333  break;
334  }
335  case 'O':
336  LOG_DEBUG("Opening shutter");
337  break;
338  case 'C':
339  LOG_DEBUG("Closing shutter");
340  break;
341  case 'S':
342  LOG_DEBUG("Shutter tick");
343  break;
344  case 'Z':
345  {
346  if ((rc = tty_nread_section(PortFD, response + 1, sizeof(response) - 2, '\r', DDW_TIMEOUT, &nr)) !=
347  TTY_OK)
348  {
349  tty_error_msg(rc, errstr, MAXRBUF);
350  LOGF_ERROR("Error reading: %s.", errstr);
351  }
352  int tick = -1;
353  sscanf(response, "Z%d", &tick);
354  LOGF_DEBUG("Shutter encoder %d", tick);
355  break;
356  }
357  case 'V':
358  {
359  // Move finished, read rest of GINF packet and parse it
360  // Read buffer
361  if ((rc = tty_nread_section(PortFD, response + 1, sizeof(response) - 2, '\r', DDW_TIMEOUT, &nr)) !=
362  TTY_OK)
363  {
364  tty_error_msg(rc, errstr, MAXRBUF);
365  LOGF_ERROR("Error reading: %s.", errstr);
366  }
367 
368  LOGF_DEBUG("GINF packet: %s", response);
369  parseGINF(response);
370 
371  auto prevState = cmdState;
372  cmdState = IDLE;
373 
374  // Check which operation was completed
375  switch (getDomeState())
376  {
377  case DOME_PARKING:
378  if(prevState == SHUTTER_OPERATION)
379  {
380  // First phase of parking done, now move to park position
382  }
383  else
384  {
385  SetParked(true);
386  }
387  break;
388  case DOME_UNPARKING:
389  SetParked(false);
390  break;
391  default:
392  if(prevState == MOVING)
393  {
394  if (gotoPending)
395  {
396  LOGF_DEBUG("Performing pending goto to %f", gotoTarget);
397  DomeAbsPosNP.s = MoveAbs(gotoTarget);
398  gotoPending = false;
399  }
400  else
401  {
402  LOG_DEBUG("Move complete");
404  }
405  }
406  break;
407  }
408  IDSetNumber(&DomeAbsPosNP, nullptr);
409  break;
410  }
411  default:
412  LOGF_ERROR("Unknown response character %c", response[0]);
413  break;
414  }
415  }
417 }
418 
419 /************************************************************************************
420  *
421 * ***********************************************************************************/
423 {
424  LOGF_DEBUG("MoveAbs (%f)", az);
425 
426  if (cmdState == MOVING)
427  {
428  LOG_DEBUG("Dome already moving, latching the new move command");
429  gotoTarget = az;
430  gotoPending = true;
431  return IPS_BUSY;
432  }
433  else if (cmdState != IDLE)
434  {
435  LOG_ERROR("Dome needs to be idle to issue moves");
436  return IPS_ALERT;
437  }
438 
439  // Construct move command
440  int rounded = round(az); // Controller only supports degrees
441  char cmd[5];
442  snprintf(cmd, sizeof(cmd), "G%03d", rounded);
443  cmdState = MOVING;
444  writeCmd(cmd);
445  return IPS_BUSY;
446 }
447 
448 /************************************************************************************
449  *
450 * ***********************************************************************************/
452 {
453  if (cmdState != IDLE)
454  {
455  LOG_ERROR("Dome needs to be idle to issue park");
456  return IPS_ALERT;
457  }
458 
459  // First close the shutter if wanted (moves to home position), then move to park position
461  {
463  }
464  else
465  {
466  return MoveAbs(GetAxis1Park());
467  }
468 }
469 
470 /************************************************************************************
471  *
472 * ***********************************************************************************/
474 {
475  if (cmdState != IDLE)
476  {
477  LOG_ERROR("Dome needs to be idle to issue unpark");
478  return IPS_ALERT;
479  }
480 
482  {
484  }
485  return IPS_OK;
486 }
487 
488 /************************************************************************************
489  *
490 * ***********************************************************************************/
492 {
493  if (cmdState != IDLE)
494  {
495  LOG_ERROR("Dome needs to be idle for shutter operations");
496  return IPS_ALERT;
497  }
498  if (operation == SHUTTER_OPEN)
499  {
500  LOG_INFO("Opening shutter");
501  cmdState = SHUTTER_OPERATION;
502  if (writeCmd("GOPN") != TTY_OK)
503  return IPS_ALERT;
504  }
505  else
506  {
507  LOG_INFO("Closing shutter");
508  cmdState = SHUTTER_OPERATION;
509  if (writeCmd("GCLS") != TTY_OK)
510  return IPS_ALERT;
511  }
512  return IPS_BUSY;
513 }
514 
515 /************************************************************************************
516  *
517 * ***********************************************************************************/
519 {
520  writeCmd("!!"); // Any two characters not used for command will do
521  cmdState = IDLE;
522  return true;
523 }
524 
525 /************************************************************************************
526  *
527 * ***********************************************************************************/
528 bool DDW::saveConfigItems(FILE *fp)
529 {
530  return INDI::Dome::saveConfigItems(fp);
531 }
532 
533 /************************************************************************************
534  *
535 * ***********************************************************************************/
537 {
538  SetAxis1Park(DomeAbsPosN[0].value);
539  return true;
540 }
541 /************************************************************************************
542  *
543 * ***********************************************************************************/
544 
546 {
547  // By default set park position to home position
548  SetAxis1Park(homeAz);
549  return true;
550 }
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Definition: ddw_dome.h:39
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: ddw_dome.cpp:52
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
Definition: ddw_dome.cpp:528
virtual IPState Park() override
Goto Park Position. The park position is an absolute azimuth value.
Definition: ddw_dome.cpp:451
virtual bool Handshake() override
perform handshake with device to check communication
Definition: ddw_dome.cpp:99
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: ddw_dome.cpp:135
DDW()
Definition: ddw_dome.cpp:45
INDI::PropertyNumber FirmwareVersionNP
Definition: ddw_dome.h:66
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: ddw_dome.cpp:536
virtual IPState ControlShutter(ShutterOperation operation) override
Open or Close shutter.
Definition: ddw_dome.cpp:491
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: ddw_dome.cpp:280
virtual IPState UnPark() override
UnPark dome. The action of the Unpark command is dome specific, but it may include opening the shutte...
Definition: ddw_dome.cpp:473
virtual IPState MoveAbs(double az) override
Move the Dome to an absolute azimuth.
Definition: ddw_dome.cpp:422
virtual bool Abort() override
Abort all dome motion.
Definition: ddw_dome.cpp:518
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition: ddw_dome.cpp:545
bool SetupParms()
Definition: ddw_dome.cpp:74
virtual const char * getDefaultName() override
Definition: ddw_dome.cpp:127
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
@ DOME_CAN_PARK
Definition: indidome.h:159
@ DOME_CAN_ABS_MOVE
Definition: indidome.h:157
@ DOME_HAS_SHUTTER
Definition: indidome.h:161
@ DOME_CAN_ABORT
Definition: indidome.h:156
@ CONNECTION_SERIAL
Definition: indidome.h:172
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
@ SHUTTER_OPEN_ON_UNPARK
Definition: indidome.h:579
@ SHUTTER_CLOSE_ON_PARK
Definition: indidome.h:578
@ DOME_UNPARKING
Definition: indidome.h:135
@ DOME_PARKING
Definition: indidome.h:134
@ DOME_SYNCED
Definition: indidome.h:133
void SetAxis1Park(double value)
SetRAPark Set current AZ parking position. The data park file (stored in ~/.indi/ParkData....
Definition: indidome.cpp:1861
INumber DomeAbsPosN[1]
Definition: indidome.h:534
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
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indidome.cpp:93
INumberVectorProperty DomeAbsPosNP
Definition: indidome.h:533
double GetAxis1Park()
Definition: indidome.cpp:1851
@ 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:279
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
Definition: indidome.cpp:1043
void setShutterState(const ShutterState &value)
Definition: indidome.cpp:1119
void setDomeState(const DomeState &value)
Definition: indidome.cpp:1156
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
ShutterState getShutterState() const
Definition: indidome.h:291
DomeState getDomeState() const
Definition: indidome.h:285
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
std::unique_ptr< DDW > ddw(new DDW())
#define DDW_TIMEOUT
Definition: ddw_dome.cpp:40
const char * INFO_TAB
INFO_TAB Where all the properties for general information are located.
@ ISS_ON
Definition: indiapi.h:152
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#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 MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
double round(double value, int decimal_places)