Instrument Neutral Distributed Interface INDI 2.2.5
pegasus_scopsoag.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2021 Chrysikos Efstathios. All rights reserved.
3
4 Pegasus ScopsOAG
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 The full GNU General Public License is included in this distribution in the
22 file called LICENSE.
23*******************************************************************************/
24
25#include "pegasus_scopsoag.h"
26
27#include "indicom.h"
29
30#include <cmath>
31#include <cstring>
32#include <memory>
33
34#include <termios.h>
35#include <unistd.h>
36
37#define DMFC_TIMEOUT 3
38#define FOCUS_SETTINGS_TAB "Settings"
39#define TEMPERATURE_THRESHOLD 0.1
40
41static std::unique_ptr<PegasusScopsOAG> scopsOAG(new PegasusScopsOAG());
42
44{
45 // Can move in Absolute & Relative motions, can AbortFocuser motion.
51}
52
54{
56
57 // LED
58 IUFillSwitch(&LEDS[LED_OFF], "Off", "", ISS_ON);
59 IUFillSwitch(&LEDS[LED_ON], "On", "", ISS_OFF);
61
62 // Firmware Version
63 IUFillText(&FirmwareVersionT[0], "Version", "Version", "");
64 IUFillTextVector(&FirmwareVersionTP, FirmwareVersionT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO,
65 0, IPS_IDLE);
66
67 // Relative and absolute movement
68 FocusRelPosNP[0].setMin(0.);
69 FocusRelPosNP[0].setMax(50000.);
70 FocusRelPosNP[0].setValue(0);
71 FocusRelPosNP[0].setStep(1000);
72
73 FocusAbsPosNP[0].setMin(0.);
74 FocusAbsPosNP[0].setMax(100000);
75 FocusAbsPosNP[0].setValue(0);
76 FocusAbsPosNP[0].setStep(1000);
77
78 // Backlash compensation
79 FocusBacklashNP[0].setMin(1); // 0 is off.
80 FocusBacklashNP[0].setMax(1000);
81 FocusBacklashNP[0].setValue(1);
82 FocusBacklashNP[0].setStep(1);
83
84 //LED Default ON
85 LEDS[LED_ON].s = ISS_ON;
86 LEDS[LED_OFF].s = ISS_OFF;
87
91
92 return true;
93}
94
96{
98
99 if (isConnected())
100 {
101
105 defineProperty(&LEDSP);
106 defineProperty(&FirmwareVersionTP);
107 }
108 else
109 {
110 deleteProperty(LEDSP.name);
111 deleteProperty(FirmwareVersionTP.name);
112 }
113
114 return true;
115}
116
118{
119 if (ack())
120 {
121 LOGF_INFO("%s is online. Getting focus parameters...", this->getDeviceName());
122 return true;
123 }
124
125 LOGF_INFO(
126 "Error retrieving data from %s, please ensure device is powered and the port is correct.", this->getDeviceName());
127 return false;
128}
129
130
132{
133 return "Pegasus ScopsOAG";
134}
135
136bool PegasusScopsOAG::ack()
137{
138 int nbytes_written = 0, nbytes_read = 0, rc = -1;
139 char errstr[MAXRBUF];
140 char cmd[2] = {0};
141 char res[16] = {0};
142
143 cmd[0] = '#';
144 cmd[1] = 0xA;
145
146 LOGF_DEBUG("CMD <%#02X>", cmd[0]);
147
148 tcflush(PortFD, TCIOFLUSH);
149
150 if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
151 {
152 tty_error_msg(rc, errstr, MAXRBUF);
153 LOGF_ERROR("Ack error: %s.", errstr);
154 return false;
155 }
156
157 if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
158 {
159 tty_error_msg(rc, errstr, MAXRBUF);
160 LOGF_ERROR("Ack error: %s.", errstr);
161 return false;
162 }
163
164 // Get rid of 0xA
165 res[nbytes_read - 1] = 0;
166
167
168 if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
169
170 LOGF_DEBUG("RES <%s>", res);
171
172 tcflush(PortFD, TCIOFLUSH);
173
174
175
176 if((strstr(res, "OK_SCOPS") != nullptr))
177 return true;
178
179 return false;
180}
181
182
184{
185 int nbytes_written = 0, rc = -1;
186 char cmd[16] = {0};
187
188 snprintf(cmd, 16, "W:%ud", ticks);
189 cmd[strlen(cmd)] = 0xA;
190
191 LOGF_DEBUG("CMD <%s>", cmd);
192
193 // Set Position
194 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
195 {
196 char errstr[MAXRBUF];
197 tty_error_msg(rc, errstr, MAXRBUF);
198 LOGF_ERROR("sync error: %s.", errstr);
199 return false;
200 }
201
202 this->ignoreResponse();
203
204 return true;
205}
206
207bool PegasusScopsOAG::move(uint32_t newPosition)
208{
209 int nbytes_written = 0, rc = -1;
210 char cmd[16] = {0};
211
212 snprintf(cmd, 16, "M:%ud", newPosition);
213 cmd[strlen(cmd)] = 0xA;
214
215 LOGF_DEBUG("CMD <%s>", cmd);
216
217 // Set Position
218 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
219 {
220 char errstr[MAXRBUF];
221 tty_error_msg(rc, errstr, MAXRBUF);
222 LOGF_ERROR("move error: %s.", errstr);
223 return false;
224 }
225
226 this->ignoreResponse();
227
228 return true;
229}
230
231bool PegasusScopsOAG::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
232{
233 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
234 {
235 // LED
236 if (!strcmp(name, LEDSP.name))
237 {
238 IUUpdateSwitch(&LEDSP, states, names, n);
239 bool rc = setLedEnabled(LEDS[LED_ON].s == ISS_ON);
240 LEDSP.s = rc ? IPS_OK : IPS_ALERT;
241 IDSetSwitch(&LEDSP, nullptr);
242 return true;
243 }
244 }
245 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
246}
247
248void PegasusScopsOAG::ignoreResponse()
249{
250 int nbytes_read = 0;
251 char res[64];
252 tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read);
253}
254
255bool PegasusScopsOAG::updateFocusParams()
256{
257 int nbytes_written = 0, nbytes_read = 0, rc = -1;
258 char errstr[MAXRBUF];
259 char cmd[3] = {0};
260 char res[64];
261 cmd[0] = 'A';
262 cmd[1] = 0xA;
263
264 LOGF_DEBUG("CMD <%#02X>", cmd[0]);
265
266 tcflush(PortFD, TCIOFLUSH);
267
268 if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
269 {
270 tty_error_msg(rc, errstr, MAXRBUF);
271 LOGF_ERROR("GetFocusParams error: %s.", errstr);
272 return false;
273 }
274
275
276 if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
277 {
278 tty_error_msg(rc, errstr, MAXRBUF);
279 LOGF_ERROR("GetFocusParams error: %s.", errstr);
280 return false;
281 }
282
283 res[nbytes_read - 1] = 0;
284
285 // Check for '\r' at end of string and replace with nullptr (DMFC firmware version 2.8)
286 if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
287
288 LOGF_DEBUG("RES <%s>", res);
289
290 tcflush(PortFD, TCIOFLUSH);
291
292 char *token = std::strtok(res, ":");
293
294
295 // #1 Status
296 if (token == nullptr || ((strstr(token, "OK_SCOPS") == nullptr)))
297 {
298 LOGF_ERROR("Invalid status response. %s", res);
299 return false;
300 }
301
302 // #2 Version
303 token = std::strtok(nullptr, ":");
304
305 if (token == nullptr)
306 {
307 LOG_ERROR("Invalid version response.");
308 return false;
309 }
310
311 if (FirmwareVersionT[0].text == nullptr || strcmp(FirmwareVersionT[0].text, token))
312 {
313 IUSaveText(&FirmwareVersionT[0], token);
314 FirmwareVersionTP.s = IPS_OK;
315 IDSetText(&FirmwareVersionTP, nullptr);
316 }
317
318 // #3 Motor Type
319 token = std::strtok(nullptr, ":");
320
321 // #4 Temperature
322 token = std::strtok(nullptr, ":");
323
324 // #5 Position
325 token = std::strtok(nullptr, ":");
326
327 if (token == nullptr)
328 {
329 LOG_ERROR("Invalid position response.");
330 return false;
331 }
332
333 currentPosition = atoi(token);
334 if (currentPosition != FocusAbsPosNP[0].getValue())
335 {
336 FocusAbsPosNP[0].setValue(currentPosition);
338 }
339
340 // #6 Moving Status
341 token = std::strtok(nullptr, ":");
342
343 if (token == nullptr)
344 {
345 LOG_ERROR("Invalid moving status response.");
346 return false;
347 }
348
349 isMoving = (token[0] == '1');
350
351 // #7 LED Status
352 token = std::strtok(nullptr, ":");
353
354 if (token == nullptr)
355 {
356 LOG_ERROR("Invalid LED response.");
357 return false;
358 }
359
360 int ledStatus = atoi(token);
361 if (ledStatus >= 0 && ledStatus <= 1)
362 {
363 IUResetSwitch(&LEDSP);
364 LEDS[ledStatus].s = ISS_ON;
365 LEDSP.s = IPS_OK;
366 IDSetSwitch(&LEDSP, nullptr);
367 }
368
369 // #8 Reverse Status
370 token = std::strtok(nullptr, ":");
371
372 // #9 Encoder status
373 token = std::strtok(nullptr, ":");
374
375 // #10 Backlash
376 token = std::strtok(nullptr, ":");
377
378 if (token == nullptr)
379 {
380 LOG_ERROR("Invalid encoder response.");
381 return false;
382 }
383
384 int backlash = atoi(token);
385 // If backlash is zero then compensation is disabled
386 if (backlash == 0 && FocusBacklashSP[INDI_ENABLED].getState() == ISS_ON)
387 {
388 LOG_WARN("Backlash value is zero, disabling backlash switch...");
389
394 }
395 else if (backlash > 0 && (FocusBacklashSP[INDI_DISABLED].getState() == ISS_ON || backlash != FocusBacklashNP[0].getValue()))
396 {
397 if (backlash != FocusBacklashNP[0].getValue())
398 {
399 FocusBacklashNP[0].setValue(backlash);
402 }
403
404 if (FocusBacklashSP[INDI_DISABLED].getState() == ISS_ON)
405 {
410 }
411 }
412
413 return true;
414}
415
416
417bool PegasusScopsOAG::setLedEnabled(bool enable)
418{
419 int nbytes_written = 0, rc = -1;
420 char cmd[16] = {0};
421
422 snprintf(cmd, 16, "L:%d", enable ? 2 : 1);
423 cmd[strlen(cmd)] = 0xA;
424
425 LOGF_DEBUG("CMD <%s>", cmd);
426
427 tcflush(PortFD, TCIOFLUSH);
428
429 // Led
430 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
431 {
432 char errstr[MAXRBUF];
433 tty_error_msg(rc, errstr, MAXRBUF);
434 LOGF_ERROR("Led error: %s.", errstr);
435 return false;
436 }
437
438 this->ignoreResponse();
439 return true;
440}
441
443{
444 targetPosition = targetTicks;
445
446 bool rc = move(targetPosition);
447
448 if (!rc)
449 return IPS_ALERT;
450
452
453 return IPS_BUSY;
454}
455
457{
458 double newPosition = 0;
459 bool rc = false;
460
461 if (dir == FOCUS_INWARD)
462 newPosition = FocusAbsPosNP[0].getValue() - ticks;
463 else
464 newPosition = FocusAbsPosNP[0].getValue() + ticks;
465
466 rc = move(newPosition);
467
468 if (!rc)
469 return IPS_ALERT;
470
471 FocusRelPosNP[0].setValue(ticks);
473
474 return IPS_BUSY;
475}
476
478{
479 if (!isConnected())
480 {
482 return;
483 }
484
485 bool rc = updateFocusParams();
486
487 if (rc)
488 {
490 {
491 if (isMoving == false)
492 {
497 LOG_INFO("Focuser reached requested position.");
498 }
499 }
500 }
501
503}
504
506{
507 int nbytes_written;
508 char cmd[2] = { 'H', 0xA };
509
510 if (tty_write(PortFD, cmd, 2, &nbytes_written) == TTY_OK)
511 {
516 this->ignoreResponse();
517 return true;
518 }
519 else
520 return false;
521}
522
524{
526 IUSaveConfigSwitch(fp, &LEDSP);
527
528 return true;
529}
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
bool isConnected() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FocusRelPosNP
INDI::PropertySwitch FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertySwitch FocusBacklashSP
INDI::PropertyNumber FocusBacklashNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool saveConfigItems(FILE *fp) override
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Connection::Serial * serialConnection
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * getDefaultName() override
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual bool saveConfigItems(FILE *fp) override
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
#define DMFC_TIMEOUT
Definition dmfc.cpp:33
#define FOCUS_SETTINGS_TAB
Definition dmfc.cpp:34
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:653
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
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:376
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:416
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:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248