Instrument Neutral Distributed Interface INDI 2.2.5
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.
43std::unique_ptr<DDW> ddw(new DDW());
44
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;
121 return true;
122}
123
124/************************************************************************************
125 *
126* ***********************************************************************************/
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* ***********************************************************************************/
155int 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
168int 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
186void 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 DomeAbsPosNP[0].setValue(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 DomeAbsPosNP[0].setValue(359.0 * tick / ticksPerRev);
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.setState(MoveAbs(gotoTarget));
398 gotoPending = false;
399 }
400 else
401 {
402 LOG_DEBUG("Move complete");
404 }
405 }
406 break;
407 }
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
462
463 return MoveAbs(GetAxis1Park());
464}
465
466/************************************************************************************
467 *
468* ***********************************************************************************/
470{
471 if (cmdState != IDLE)
472 {
473 LOG_ERROR("Dome needs to be idle to issue unpark");
474 return IPS_ALERT;
475 }
476
478 {
480 }
481 return IPS_OK;
482}
483
484/************************************************************************************
485 *
486* ***********************************************************************************/
488{
489 if (cmdState != IDLE)
490 {
491 LOG_ERROR("Dome needs to be idle for shutter operations");
492 return IPS_ALERT;
493 }
494 if (operation == SHUTTER_OPEN)
495 {
496 LOG_INFO("Opening shutter");
497 cmdState = SHUTTER_OPERATION;
498 if (writeCmd("GOPN") != TTY_OK)
499 return IPS_ALERT;
500 }
501 else
502 {
503 LOG_INFO("Closing shutter");
504 cmdState = SHUTTER_OPERATION;
505 if (writeCmd("GCLS") != TTY_OK)
506 return IPS_ALERT;
507 }
508 return IPS_BUSY;
509}
510
511/************************************************************************************
512 *
513* ***********************************************************************************/
515{
516 writeCmd("!!"); // Any two characters not used for command will do
517 cmdState = IDLE;
518 return true;
519}
520
521/************************************************************************************
522 *
523* ***********************************************************************************/
525{
527}
528
529/************************************************************************************
530 *
531* ***********************************************************************************/
533{
534 SetAxis1Park(DomeAbsPosNP[0].getValue());
535 return true;
536}
537/************************************************************************************
538 *
539* ***********************************************************************************/
540
542{
543 // By default set park position to home position
544 SetAxis1Park(homeAz);
545 return true;
546}
double humidity
double temperature
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
Initialize 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:524
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:532
virtual IPState ControlShutter(ShutterOperation operation) override
Open or Close shutter.
Definition ddw_dome.cpp:487
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:469
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:514
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition ddw_dome.cpp:541
bool SetupParms()
Definition ddw_dome.cpp:74
virtual const char * getDefaultName() override
Definition ddw_dome.cpp:127
bool isConnected() const
void setPollingPeriodRange(uint32_t minimum, uint32_t maximum)
setPollingPeriodRange Set the range permitted by the polling range in milliseconds
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
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.
void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
void SetDomeCapability(uint32_t cap)
SetDomeCapability set the dome capabilities. All capabilities must be initialized.
void setDomeConnection(const uint8_t &value)
setDomeConnection Set Dome connection mode. Child class should call this in the constructor before Do...
INDI::PropertySwitch ShutterParkPolicySP
Definition indidome.h:566
@ SHUTTER_OPEN_ON_UNPARK
Definition indidome.h:570
@ SHUTTER_CLOSE_ON_PARK
Definition indidome.h:569
@ DOME_UNPARKING
Definition indidome.h:135
void SetAxis1Park(double value)
SetRAPark Set current AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetAxis1Park Set default AZ parking position.
ShutterOperation
Shutter operation command.
Definition indidome.h:114
@ SHUTTER_CLOSE
Definition indidome.h:116
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indidome.cpp:105
@ DOME_CAN_PARK
Definition indidome.h:159
@ DOME_CAN_ABS_MOVE
Definition indidome.h:157
@ DOME_HAS_SHUTTER
Definition indidome.h:161
@ DOME_CAN_ABORT
Definition indidome.h:156
double GetAxis1Park()
@ SHUTTER_UNKNOWN
Definition indidome.h:150
@ SHUTTER_OPENED
Definition indidome.h:147
@ SHUTTER_CLOSED
Definition indidome.h:148
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition indidome.cpp:284
INDI::PropertyNumber DomeAbsPosNP
Definition indidome.h:536
@ CONNECTION_SERIAL
Definition indidome.h:172
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
void setShutterState(const ShutterState &value)
void setDomeState(const DomeState &value)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
Connection::Serial * serialConnection
Definition indidome.h:607
void SetParkDataType(DomeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
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)
#define DDW_TIMEOUT
Definition ddw_dome.cpp:40
std::unique_ptr< DDW > ddw(new DDW())
const char * INFO_TAB
INFO_TAB Where all the properties for general information are located.
@ ISS_ON
Definition indiapi.h:150
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
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:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
#define MAXRBUF
Definition indidriver.c:52
#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 getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
double round(double value, int decimal_places)