Instrument Neutral Distributed Interface INDI 2.2.5
pegasus_prodigyMF.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2021 Chrysikos Efstathios. All rights reserved.
3
4 Pegasus ProdigyMF
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#include "pegasus_prodigyMF.h"
25
26#include "indicom.h"
28
29#include <cmath>
30#include <cstring>
31#include <memory>
32
33#include <termios.h>
34#include <unistd.h>
35
36#define DMFC_TIMEOUT 3
37#define FOCUS_SETTINGS_TAB "Settings"
38#define TEMPERATURE_THRESHOLD 0.1
39
40static std::unique_ptr<PegasusProdigyMF> focusCube(new PegasusProdigyMF());
41
42
44{
45 // Can move in Absolute & Relative motions, can AbortFocuser motion.
51}
52
54{
56
57 // Focuser temperature
58 IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
59 IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
61
62 // Max Speed
63 IUFillNumber(&MaxSpeedN[0], "Value", "", "%6.2f", 100, 1000., 100., 400.);
64 IUFillNumberVector(&MaxSpeedNP, MaxSpeedN, 1, getDeviceName(), "MaxSpeed", "", FOCUS_SETTINGS_TAB, IP_RW, 0, IPS_IDLE);
65
66
67
68 // Firmware Version
69 IUFillText(&FirmwareVersionT[0], "Version", "Version", "");
70 IUFillTextVector(&FirmwareVersionTP, FirmwareVersionT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO,
71 0, IPS_IDLE);
72
73 // Relative and absolute movement
74 FocusRelPosNP[0].setMin(0.);
75 FocusRelPosNP[0].setMax(20000.);
76 FocusRelPosNP[0].setValue(0);
77 FocusRelPosNP[0].setStep(1000);
78
79 FocusAbsPosNP[0].setMax(20000.);
80 FocusAbsPosNP[0].setMin(0.);
81 FocusAbsPosNP[0].setValue(0);
82 FocusAbsPosNP[0].setStep(1000);
83
84 FocusMaxPosNP[0].setMax(20000);
85 FocusMaxPosNP[0].setValue(20000);
86 FocusAbsPosNP[0].setMin(0);
87
88
92 return true;
93}
94
96{
98
99 if (isConnected())
100 {
101 defineProperty(&MaxSpeedNP);
102 defineProperty(&TemperatureNP);
103 defineProperty(&FirmwareVersionTP);
104 }
105 else
106 {
107 deleteProperty(MaxSpeedNP.name);
108 deleteProperty(TemperatureNP.name);
109 deleteProperty(FirmwareVersionTP.name);
110 }
111
112 return true;
113}
114
116{
117 if (ack())
118 {
119 LOGF_INFO("%s is online. Getting focus parameters...", this->getDeviceName());
120 return true;
121 }
122
123 LOGF_INFO(
124 "Error retrieving data from %s, please ensure device is powered and the port is correct.", this->getDeviceName());
125 return false;
126}
127
129{
130 return "Pegasus ProdigyMF";
131}
132
133bool PegasusProdigyMF::ack()
134{
135 int nbytes_written = 0, nbytes_read = 0, rc = -1;
136 char errstr[MAXRBUF];
137 char cmd[2] = {0};
138 char res[16] = {0};
139
140 cmd[0] = '#';
141 cmd[1] = 0xA;
142
143 LOGF_DEBUG("CMD <%#02X>", cmd[0]);
144
145 tcflush(PortFD, TCIOFLUSH);
146
147 if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
148 {
149 tty_error_msg(rc, errstr, MAXRBUF);
150 LOGF_ERROR("Ack error: %s.", errstr);
151 return false;
152 }
153
154 if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
155 {
156 tty_error_msg(rc, errstr, MAXRBUF);
157 LOGF_ERROR("Ack error: %s.", errstr);
158 return false;
159 }
160
161 // Get rid of 0xA
162 res[nbytes_read - 1] = 0;
163
164
165 if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
166
167 LOGF_DEBUG("RES <%s>", res);
168
169 tcflush(PortFD, TCIOFLUSH);
170
171 if(strstr(res, "OK_PRDG") != nullptr)
172 return true;
173
174 return false;
175}
176
177
179{
180 int nbytes_written = 0, rc = -1;
181 char cmd[16] = {0};
182
183 snprintf(cmd, 16, "W:%ud", ticks);
184 cmd[strlen(cmd)] = 0xA;
185
186 LOGF_DEBUG("CMD <%s>", cmd);
187
188 // Set Position
189 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
190 {
191 char errstr[MAXRBUF];
192 tty_error_msg(rc, errstr, MAXRBUF);
193 LOGF_ERROR("sync error: %s.", errstr);
194 return false;
195 }
196
197 this->ignoreResponse();
198
199 return true;
200}
201
202bool PegasusProdigyMF::move(uint32_t newPosition)
203{
204 int nbytes_written = 0, rc = -1;
205 char cmd[16] = {0};
206
207 snprintf(cmd, 16, "M:%ud", newPosition);
208 cmd[strlen(cmd)] = 0xA;
209
210 LOGF_DEBUG("CMD <%s>", cmd);
211
212 // Set Position
213 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
214 {
215 char errstr[MAXRBUF];
216 tty_error_msg(rc, errstr, MAXRBUF);
217 LOGF_ERROR("move error: %s.", errstr);
218 return false;
219 }
220
221 this->ignoreResponse();
222
223 return true;
224}
225
226bool PegasusProdigyMF::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
227{
228 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
229 {
230 // MaxSpeed
231 if (strcmp(name, MaxSpeedNP.name) == 0)
232 {
233 IUUpdateNumber(&MaxSpeedNP, values, names, n);
234 bool rc = setMaxSpeed(MaxSpeedN[0].value);
235 MaxSpeedNP.s = rc ? IPS_OK : IPS_ALERT;
236 IDSetNumber(&MaxSpeedNP, nullptr);
237 return true;
238 }
239
240 }
241
242 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
243}
244
245void PegasusProdigyMF::ignoreResponse()
246{
247 int nbytes_read = 0;
248 char res[64];
249 tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read);
250}
251
252bool PegasusProdigyMF::updateFocusParams()
253{
254 int nbytes_written = 0, nbytes_read = 0, rc = -1;
255 char errstr[MAXRBUF];
256 char cmd[3] = {0};
257 char res[64];
258 cmd[0] = 'A';
259 cmd[1] = 0xA;
260
261 LOGF_DEBUG("CMD <%#02X>", cmd[0]);
262
263 tcflush(PortFD, TCIOFLUSH);
264
265 if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
266 {
267 tty_error_msg(rc, errstr, MAXRBUF);
268 LOGF_ERROR("GetFocusParams error: %s.", errstr);
269 return false;
270 }
271
272
273 if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
274 {
275 tty_error_msg(rc, errstr, MAXRBUF);
276 LOGF_ERROR("GetFocusParams error: %s.", errstr);
277 return false;
278 }
279
280 res[nbytes_read - 1] = 0;
281
282 // Check for '\r' at end of string and replace with nullptr (DMFC firmware version 2.8)
283 if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
284
285 LOGF_DEBUG("RES <%s>", res);
286
287 tcflush(PortFD, TCIOFLUSH);
288
289 char *token = std::strtok(res, ":");
290
291 // #1 Status
292 if (token == nullptr || (strstr(token, "OK_PRDG") == nullptr))
293 {
294 LOGF_ERROR("Invalid status response. %s", res);
295 return false;
296 }
297
298 // #2 Version
299 token = std::strtok(nullptr, ":");
300
301 if (token == nullptr)
302 {
303 LOG_ERROR("Invalid version response.");
304 return false;
305 }
306
307 if (FirmwareVersionT[0].text == nullptr || strcmp(FirmwareVersionT[0].text, token))
308 {
309 IUSaveText(&FirmwareVersionT[0], token);
310 FirmwareVersionTP.s = IPS_OK;
311 IDSetText(&FirmwareVersionTP, nullptr);
312 }
313
314 // #3 Motor Type
315 token = std::strtok(nullptr, ":");
316
317 // #4 Temperature
318 token = std::strtok(nullptr, ":");
319
320 if (token == nullptr)
321 {
322 LOG_ERROR("Invalid temperature response.");
323 return false;
324 }
325
326 double temperature = atof(token);
327 if (temperature == -127)
328 {
329 TemperatureNP.s = IPS_ALERT;
330 IDSetNumber(&TemperatureNP, nullptr);
331 }
332 else
333 {
334 if (fabs(temperature - TemperatureN[0].value) > TEMPERATURE_THRESHOLD)
335 {
336 TemperatureN[0].value = temperature;
337 TemperatureNP.s = IPS_OK;
338 IDSetNumber(&TemperatureNP, nullptr);
339 }
340 }
341
342 // #5 Position
343 token = std::strtok(nullptr, ":");
344
345 if (token == nullptr)
346 {
347 LOG_ERROR("Invalid position response.");
348 return false;
349 }
350
351 currentPosition = atoi(token);
352 if (currentPosition != FocusAbsPosNP[0].getValue())
353 {
354 FocusAbsPosNP[0].setValue(currentPosition);
356 }
357
358 // #6 Moving Status
359 token = std::strtok(nullptr, ":");
360
361 if (token == nullptr)
362 {
363 LOG_ERROR("Invalid moving status response.");
364 return false;
365 }
366
367 isMoving = (token[0] == '1');
368
369 // #7 LED Status fake read
370 token = std::strtok(nullptr, ":");
371
372
373
374 // #8 Reverse Status
375 token = std::strtok(nullptr, ":");
376
377 if (token == nullptr)
378 {
379 LOG_ERROR("Invalid reverse response.");
380 return false;
381 }
382
383 int reverseStatus = atoi(token);
384 if (reverseStatus >= 0 && reverseStatus <= 1)
385 {
387 FocusReverseSP[INDI_ENABLED].setState((reverseStatus == 1) ? ISS_ON : ISS_OFF);
388 FocusReverseSP[INDI_DISABLED].setState((reverseStatus == 0) ? ISS_ON : ISS_OFF);
391 }
392
393 // #9 Encoder status fake read
394 token = std::strtok(nullptr, ":");
395
396
397 // #10 Backlash fake read
398 token = std::strtok(nullptr, ":");
399
400
401 return true;
402}
403
404bool PegasusProdigyMF::setMaxSpeed(uint16_t speed)
405{
406 int nbytes_written = 0, rc = -1;
407 char cmd[16] = {0};
408
409 snprintf(cmd, 16, "S:%d", speed);
410 cmd[strlen(cmd)] = 0xA;
411
412 LOGF_DEBUG("CMD <%s>", cmd);
413
414 tcflush(PortFD, TCIOFLUSH);
415
416 // Set Speed
417 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
418 {
419 char errstr[MAXRBUF];
420 tty_error_msg(rc, errstr, MAXRBUF);
421 LOGF_ERROR("setMaxSpeed error: %s.", errstr);
422 return false;
423 }
424
425 this->ignoreResponse();
426 return true;
427}
428
430{
431 int nbytes_written = 0, rc = -1;
432 char cmd[16] = {0};
433
434 snprintf(cmd, 16, "N:%d", enabled ? 1 : 0);
435 cmd[strlen(cmd)] = 0xA;
436
437 LOGF_DEBUG("CMD <%s>", cmd);
438
439 tcflush(PortFD, TCIOFLUSH);
440
441 // Reverse
442 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
443 {
444 char errstr[MAXRBUF];
445 tty_error_msg(rc, errstr, MAXRBUF);
446 LOGF_ERROR("Reverse error: %s.", errstr);
447 return false;
448 }
449
450 this->ignoreResponse();
451
452 return true;
453}
454
455
457{
458 targetPosition = targetTicks;
459
460 bool rc = move(targetPosition);
461
462 if (!rc)
463 return IPS_ALERT;
464
466
467 return IPS_BUSY;
468}
469
471{
472 double newPosition = 0;
473 bool rc = false;
474
475 if (dir == FOCUS_INWARD)
476 newPosition = FocusAbsPosNP[0].getValue() - ticks;
477 else
478 newPosition = FocusAbsPosNP[0].getValue() + ticks;
479
480 rc = move(newPosition);
481
482 if (!rc)
483 return IPS_ALERT;
484
485 FocusRelPosNP[0].setValue(ticks);
487
488 return IPS_BUSY;
489}
490
492{
493 if (!isConnected())
494 {
496 return;
497 }
498
499 bool rc = updateFocusParams();
500
501 if (rc)
502 {
504 {
505 if (isMoving == false)
506 {
511 LOG_INFO("Focuser reached requested position.");
512 }
513 }
514 }
515
517}
518
520{
521 int nbytes_written;
522 char cmd[2] = { 'H', 0xA };
523
524 if (tty_write(PortFD, cmd, 2, &nbytes_written) == TTY_OK)
525 {
530 this->ignoreResponse();
531 return true;
532 }
533 else
534 return false;
535}
536
538{
540 IUSaveConfigNumber(fp, &MaxSpeedNP);
541
542 return true;
543}
double temperature
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::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusAbsPosNP
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,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
Connection::Serial * serialConnection
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * getDefaultName() override
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool saveConfigItems(FILE *fp) override
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
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 TEMPERATURE_THRESHOLD
Definition dmfc.cpp:35
#define FOCUS_SETTINGS_TAB
Definition dmfc.cpp:34
@ 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
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 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:490
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 IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
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 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:398
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#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:321
char name[MAXINDINAME]
Definition indiapi.h:248