Instrument Neutral Distributed Interface INDI 2.2.5
lx200_pegasus_nyx101.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2021 Chrysikos Efstathios. All rights reserved.
3
4 Pegasus NYX
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
26#include "lx200driver.h"
27#include "indicom.h"
29#include <libnova/transform.h>
30
31#include <cmath>
32#include <cstring>
33#include <stdio.h>
34#include <termios.h>
35#include <unistd.h>
36#include <regex>
37
38const char *SETTINGS_TAB = "Settings";
39const char *STATUS_TAB = "Status";
40
60
62{
64
68
69 // Mount Type
70 int mountType = Equatorial;
71 IUGetConfigOnSwitchIndex(getDeviceName(), "MOUNT_TYPE", &mountType);
72 MountTypeSP[AltAz].fill("AltAz", "AltAz", mountType == AltAz ? ISS_ON : ISS_OFF);
73 MountTypeSP[Equatorial].fill("Equatorial", "Equatorial", mountType == Equatorial ? ISS_ON : ISS_OFF);
74 MountTypeSP.fill(getDeviceName(), "MOUNT_TYPE", "Mount Type", SETTINGS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
75
76 if (mountType == Equatorial)
78
79 // Overwrite TRACK_CUSTOM, with TRACK_KING
80 TrackModeSP[TRACK_KING].fill("TRACK_KING", "King", ISS_OFF);
81
82 // Elevation Limits
83 ElevationLimitNP[OVERHEAD].fill("ELEVATION_OVERHEAD", "Overhead", "%g", 60, 90, 1, 90);
84 ElevationLimitNP[HORIZON].fill("ELEVATION_HORIZON", "Horizon", "%g", -30, 0, 1, 0);
85 ElevationLimitNP.fill(getDeviceName(), "ELEVATION_LIMIT", "Elevation Limit", MAIN_CONTROL_TAB, IP_RW, 0,
86 IPS_IDLE);
87
88 // Meridian
89 MeridianLimitNP[0].fill("VALUE", "Degrees (+/- 120)", "%.f", -120, 120, 1, 0);
90 MeridianLimitNP.fill(getDeviceName(), "MERIDIAN_LIMIT", "Limit", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
91
92 // Flip
93 FlipSP[0].fill("Flip", "Flip", ISS_OFF);
94 FlipSP.fill(getDeviceName(), "FLIP", "Pier Side", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
95
96 // Refraction
97 RefractSP[REFRACT_ON].fill("REFRACTION_ON", "On", ISS_OFF);
98 RefractSP[REFRACT_OFF].fill("REFRACTION_OFF", "Off", ISS_OFF);
99 RefractSP.fill(getDeviceName(), "REFRACTION",
100 "Refraction", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
101
102 // Safety Limits
103 SafetyLimitSP[SET_SAFETY_LIMIT].fill("SET_SAFETY_LIMIT", "Set", ISS_OFF);
104 SafetyLimitSP[CLEAR_SAFETY_LIMIT].fill("CLEAR_SAFETY_LIMIT", "Clear", ISS_OFF);
105 SafetyLimitSP.fill(getDeviceName(), "SAFETY_LIMIT",
106 "Custom Limits", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
107 // Guide Rate
108 int guideRate = 1;
109 IUGetConfigOnSwitchIndex(getDeviceName(), "GUIDE_RATE", &guideRate);
110 GuideRateSP[0].fill("0.25", "0.25", guideRate == 0 ? ISS_ON : ISS_OFF);
111 GuideRateSP[1].fill("0.50", "0.50", guideRate == 1 ? ISS_ON : ISS_OFF);
112 GuideRateSP[2].fill("1.00", "1.00", guideRate == 2 ? ISS_ON : ISS_OFF);
113 GuideRateSP.fill(getDeviceName(), "GUIDE_RATE", "Guide Rate", SETTINGS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
114
115 //Go Home
116 // HomeSP[0].fill("GO", "Go", ISS_OFF);
117 // HomeSP.fill(getDeviceName(), "TELESCOPE_HOME", "Home go", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
118
119 // //Reset Home
120 // ResetHomeSP[0].fill("Home", "Reset", ISS_OFF);
121 // ResetHomeSP.fill(getDeviceName(), "HOME_RESET", "Home Reset", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
122
123 verboseReport = false;
124 VerboseReportSP[0].fill("On", "On", ISS_OFF);
125 VerboseReportSP[1].fill("Off", "Off", ISS_ON);
126 VerboseReportSP.fill(getDeviceName(), "REPORT_VERBOSE", "Verbose", STATUS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
127
128 Report[0].fill("Report", "GU", "-");
129 Report.fill(getDeviceName(), "Report", "Report", STATUS_TAB, IP_RO, 60, IPS_IDLE);
130
131#ifdef DEBUG_NYX
132 DebugCommandTP[0].fill("Command", "", "");
133 DebugCommandTP.fill(getDeviceName(), "DebugCommand", "", MAIN_CONTROL_TAB, IP_RW, 0,
134 IPS_IDLE);
135#endif
136
137 IsTracking[0].fill("IsTracking", "n", "-");
138 IsTracking.fill(getDeviceName(), "IsTracking", "IsTracking", STATUS_TAB, IP_RO, 60, IPS_IDLE);
139
140 IsSlewCompleted[0].fill("IsSlewCompleted", "N", "-");
141 IsSlewCompleted.fill(getDeviceName(), "IsSlewCompleted", "IsSlewCompleted", STATUS_TAB, IP_RO, 60, IPS_IDLE);
142
143 IsParked[0].fill("IsParked", "p/P", "-");
144 IsParked.fill(getDeviceName(), "IsParked", "IsParked", STATUS_TAB, IP_RO, 60, IPS_IDLE);
145
146 IsParkginInProgress[0].fill("IsParkginInProgress", "I", "-");
147 IsParkginInProgress.fill(getDeviceName(), "IsParkginInProgress", "IsParkginInProgress", STATUS_TAB, IP_RO, 60, IPS_IDLE);
148
149 IsAtHomePosition[0].fill("IsAtHomePosition", "H", "-");
150 IsAtHomePosition.fill(getDeviceName(), "IsAtHomePosition", "IsAtHomePosition", STATUS_TAB, IP_RO, 60, IPS_IDLE);
151
152 MountAltAz[0].fill("MountAltAz", "A", "-");
153 MountAltAz.fill(getDeviceName(), "MountAltAz", "MountAltAz", STATUS_TAB, IP_RO, 60, IPS_IDLE);
154
155 MountEquatorial[0].fill("MountEquatorial", "E", "-");
156 MountEquatorial.fill(getDeviceName(), "MountEquatorial", "MountEquatorial", STATUS_TAB, IP_RO, 60, IPS_IDLE);
157
158 PierNone[0].fill("PierNone", "", "-");
159 PierNone.fill(getDeviceName(), "PierNone", "PierNone", STATUS_TAB, IP_RO, 60, IPS_IDLE);
160
161 PierEast[0].fill("PierEast", "T", "-");
162 PierEast.fill(getDeviceName(), "PierEast", "PierEast", STATUS_TAB, IP_RO, 60, IPS_IDLE);
163
164 PierWest[0].fill("PierWest", "W", "-");
165 PierWest.fill(getDeviceName(), "PierWest", "PierWest", STATUS_TAB, IP_RO, 60, IPS_IDLE);
166
167 DoesRefractionComp[0].fill("DoesRefractionComp", "r", "-");
168 DoesRefractionComp.fill(getDeviceName(), "DoesRefractionComp", "DoesRefractionComp", STATUS_TAB, IP_RO, 60, IPS_IDLE);
169
170 WaitingAtHome[0].fill("WaitingAtHome", "w", "-");
171 WaitingAtHome.fill(getDeviceName(), "WaitingAtHome", "WaitingAtHome", STATUS_TAB, IP_RO, 60, IPS_IDLE);
172
173 IsHomePaused[0].fill("IsHomePaused", "u", "-");
174 IsHomePaused.fill(getDeviceName(), "IsHomePaused", "IsHomePaused", STATUS_TAB, IP_RO, 60, IPS_IDLE);
175
176 ParkFailed[0].fill("ParkFailed", "F", "-");
177 ParkFailed.fill(getDeviceName(), "ParkFailed", "ParkFailed", STATUS_TAB, IP_RO, 60, IPS_IDLE);
178
179 SlewingHome[0].fill("SlewingHome", "h", "-");
180 SlewingHome.fill(getDeviceName(), "SlewingHome", "SlewingHome", STATUS_TAB, IP_RO, 60, IPS_IDLE);
181
182 // Reboot
183 RebootSP[0].fill("Reboot", "Reboot", ISS_OFF);
184 RebootSP.fill(getDeviceName(), "REBOOT", "Reboot", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
185
186 // Slew Rates
187 SlewRateSP[0].setLabel("2x");
188 SlewRateSP[1].setLabel("8x");
189 SlewRateSP[2].setLabel("16x");
190 SlewRateSP[3].setLabel("64x");
191 SlewRateSP[4].setLabel("128x");
192 SlewRateSP[5].setLabel("200x");
193 SlewRateSP[6].setLabel("300x");
194 SlewRateSP[7].setLabel("600x");
195 SlewRateSP[8].setLabel("900x");
196 SlewRateSP[9].setLabel("1200x");
198
200
201
202 return true;
203}
204
206{
208
209 if (isConnected())
210 {
211 char status[DRIVER_LEN] = {0};
212 if (sendCommand(":GU#", status))
213 {
214 if(strchr(status, 'P'))
215 SetParked(true);
216 else
217 SetParked(false);
218
219 MountTypeSP.reset();
220 MountTypeSP[AltAz].setState(strchr(status, 'A') ? ISS_ON : ISS_OFF);
221 MountTypeSP[Equatorial].setState(strchr(status, 'E') ? ISS_ON : ISS_OFF);
222 MountTypeSP.setState(IPS_OK);
223 MountTypeSP.apply();
224 }
225
226 if(sendCommand(":GX90#", status))
227 {
228 std::string c = status;
229
230 GuideRateSP.reset();
231 GuideRateSP[0].setState((c.find("0.25") != std::string::npos) ? ISS_ON : ISS_OFF);
232 GuideRateSP[1].setState((c.find("0.50") != std::string::npos) ? ISS_ON : ISS_OFF);
233 GuideRateSP[2].setState((c.find("1.00") != std::string::npos) ? ISS_ON : ISS_OFF);
234 GuideRateSP.setState((IPS_OK));
235 GuideRateSP.apply();
236 }
237
238 if(sendCommand(":Go#", status))
239 {
240 std::string c = status;
241 ElevationLimitNP[OVERHEAD].value = std::stoi(c);
242 }
243
244 if(sendCommand(":Gh#", status))
245 {
246 std::string c = status;
247 ElevationLimitNP[HORIZON].value = std::stoi(c);
248 }
249
250 if(sendCommand(":GXE9#", status))
251 {
252 std::string c = status;
253 MeridianLimitNP[0].setValue(std::stoi(c));
254 }
255
256 defineProperty(MountTypeSP);
257 defineProperty(GuideRateSP);
258 // defineProperty(HomeSP);
259 // defineProperty(ResetHomeSP);
260 defineProperty(Report);
261 defineProperty(FlipSP);
262 defineProperty(MeridianLimitNP);
263 defineProperty(ElevationLimitNP);
264 defineProperty(RefractSP);
265 defineProperty(SafetyLimitSP);
266#ifdef DEBUG_NYX
267 defineProperty(DebugCommandTP);
268#endif
269 defineProperty(RebootSP);
270 defineProperty(VerboseReportSP);
271 defineProperty(IsTracking);
272 defineProperty(IsSlewCompleted);
273 defineProperty(IsParked);
274 defineProperty(IsParkginInProgress);
275 defineProperty(IsAtHomePosition);
276 defineProperty(MountAltAz);
277 defineProperty(MountEquatorial);
278 defineProperty(PierNone);
279 defineProperty(PierEast);
280 defineProperty(PierWest);
281 defineProperty(DoesRefractionComp);
282 defineProperty(WaitingAtHome);
283 defineProperty(IsHomePaused);
284 defineProperty(ParkFailed);
285 defineProperty(SlewingHome);
286 }
287 else
288 {
289 deleteProperty(MountTypeSP);
290 deleteProperty(GuideRateSP);
291 //deleteProperty(HomeSP);
292 deleteProperty(MeridianLimitNP);
293 deleteProperty(FlipSP);
294 deleteProperty(ElevationLimitNP);
295 deleteProperty(SafetyLimitSP);
296 //deleteProperty(ResetHomeSP);
297 deleteProperty(Report);
298#ifdef DEBUG_NYX
299 deleteProperty(DebugCommandTP);
300#endif
301 deleteProperty(RebootSP);
302 deleteProperty(VerboseReportSP);
303 deleteProperty(RefractSP);
304 deleteProperty(IsTracking);
305 deleteProperty(IsSlewCompleted);
306 deleteProperty(IsParked);
307 deleteProperty(IsParkginInProgress);
308 deleteProperty(IsAtHomePosition);
309 deleteProperty(MountAltAz);
310 deleteProperty(MountEquatorial);
311 deleteProperty(PierNone);
312 deleteProperty(PierEast);
313 deleteProperty(PierWest);
314 deleteProperty(DoesRefractionComp);
315 deleteProperty(WaitingAtHome);
316 deleteProperty(IsHomePaused);
317 deleteProperty(ParkFailed);
318 deleteProperty(SlewingHome);
319
320 }
321
322 return true;
323}
324
325
327{
328 return "Pegasus NYX-101";
329}
330
331const char *ON = "ON";
332const char *OFF = "OFF";
333
334void LX200NYX101::SetPropertyText(INDI::PropertyText propertyTxt, IPState state)
335{
336 if(!verboseReport)
337 return;
338
339 if(state == IPS_OK)
340 {
341 propertyTxt[0].setText(ON);
342 }
343 else if(state == IPS_BUSY)
344 {
345 propertyTxt[0].setText(OFF);
346 }
347 else if(state == IPS_IDLE)
348 {
349 propertyTxt[0].setText("-");
350 }
351 propertyTxt.setState(state);
352 propertyTxt.apply();
353}
354
356{
357 if (!isConnected())
358 return false;
359
360 bool _IsTracking = true;
361 SetPropertyText(IsTracking, IPS_OK);
362
363 bool _IsSlewCompleted = false;
364 SetPropertyText(IsSlewCompleted, IPS_BUSY);
365
366 bool _IsParked = false;
367 SetPropertyText(IsParked, IPS_BUSY);
368
369 //bool _IsParkginInProgress = false;
370 SetPropertyText(IsParkginInProgress, IPS_BUSY);
371
372 //bool _IsAtHomePosition = false;
373 SetPropertyText(IsAtHomePosition, IPS_BUSY);
374
375 NYXTelescopeTrackMode _TrackingMode = TRACK_SIDEREAL;
376
377 //MountType _MountType = Equatorial;
378
380
381 bool _DoesRefractionComp = false;
382 SetPropertyText(DoesRefractionComp, IPS_BUSY);
383
384 //bool _WaitingAtHome = false;
385 SetPropertyText(WaitingAtHome, IPS_BUSY);
386
387 //bool _IsHomePaused = false;
388 SetPropertyText(IsHomePaused, IPS_BUSY);
389
390 //bool _ParkFailed = false;
391 SetPropertyText(ParkFailed, IPS_BUSY);
392
393 //bool _SlewingHome = false;
394 SetPropertyText(SlewingHome, IPS_BUSY);
395
396 char status[DRIVER_LEN] = {0};
397 if(sendCommand(":GU#", status))
398 {
399 Report[0].text = status;
400 Report.apply();
401 int index = 0;
402 while(true)
403 {
404 switch (status[index++])
405 {
406 case 'n':
407 _IsTracking = false;
408 SetPropertyText(IsTracking, IPS_BUSY);
409 continue;
410 case 'N':
411 _IsSlewCompleted = true;
412 SetPropertyText(IsSlewCompleted, IPS_OK);
413 continue;
414 case 'p':
415 _IsParked = false;
416 SetPropertyText(IsParked, IPS_BUSY);
417 continue;
418 case 'P':
419 _IsParked = true;
420 SetPropertyText(IsParked, IPS_OK);
421 continue;
422 case 'I':
423 //_IsParkginInProgress = true;
424 SetPropertyText(IsParkginInProgress, IPS_OK);
425 continue;
426 case 'H':
427 //_IsAtHomePosition = true;
428 SetPropertyText(IsAtHomePosition, IPS_OK);
429 continue;
430 case '(':
431 _TrackingMode = TRACK_LUNAR;
432 continue;
433 case 'O':
434 _TrackingMode = TRACK_SOLAR;
435 continue;
436 case 'k':
437 _TrackingMode = TRACK_KING;
438 continue;
439 case 'A':
440 //_MountType = AltAz;
441 SetPropertyText(MountAltAz, IPS_OK);
442 SetPropertyText(MountEquatorial, IPS_BUSY);
443 continue;
444 case 'E':
445 //_MountType = Equatorial;
446 SetPropertyText(MountEquatorial, IPS_OK);
447 SetPropertyText(MountAltAz, IPS_BUSY);
448 continue;
449 case 'T':
450 _PierSide = PIER_EAST;
451 continue;
452 case 'W':
453 _PierSide = PIER_WEST;
454 continue;
455 case 'r':
456 _DoesRefractionComp = true;
457 SetPropertyText(DoesRefractionComp, IPS_OK);
458 continue;
459 case 'w':
460 //_WaitingAtHome = true;
461 SetPropertyText(WaitingAtHome, IPS_OK);
462 continue;
463 case 'u':
464 //_IsHomePaused = true;
465 SetPropertyText(IsHomePaused, IPS_OK);
466 continue;
467 case 'F':
468 //_ParkFailed = true;
469 SetPropertyText(ParkFailed, IPS_OK);
470 continue;
471 case 'h':
472 //_SlewingHome = true;
473 SetPropertyText(SlewingHome, IPS_OK);
474 continue;
475 case '#':
476 break;
477 default:
478 continue;
479 }
480 break;
481 }
482 }
483 if(_DoesRefractionComp )
484 {
485 RefractSP[REFRACT_ON].setState(ISS_ON);
486 RefractSP[REFRACT_OFF].setState(ISS_OFF);
487 RefractSP.setState(IPS_OK);
488 RefractSP.apply();
489 }
490 else
491 {
492 RefractSP[REFRACT_ON].setState(ISS_OFF);
493 RefractSP[REFRACT_OFF].setState(ISS_ON);
494 RefractSP.setState(IPS_OK);
495 RefractSP.apply();
496 }
497 TrackModeSP[TRACK_SIDEREAL].setState(ISS_OFF);
498 TrackModeSP[TRACK_LUNAR].setState(ISS_OFF);
499 TrackModeSP[TRACK_SOLAR].setState(ISS_OFF);
500 TrackModeSP[TRACK_KING].setState(ISS_OFF);
501 TrackModeSP[_TrackingMode].setState(ISS_ON);
504
505 switch(_PierSide)
506 {
508 SetPropertyText(PierNone, IPS_OK);
509 SetPropertyText(PierEast, IPS_BUSY);
510 SetPropertyText(PierWest, IPS_BUSY);
511 break;
513 SetPropertyText(PierEast, IPS_OK);
514 SetPropertyText(PierNone, IPS_BUSY);
515 SetPropertyText(PierWest, IPS_BUSY);
516 break;
518 SetPropertyText(PierWest, IPS_OK);
519 SetPropertyText(PierEast, IPS_BUSY);
520 SetPropertyText(PierNone, IPS_BUSY);
521 break;
522 }
523
524
526 {
527 if (_IsSlewCompleted)
528 {
530 LOG_INFO("Slew is complete. Tracking...");
531 }
532 }
533 else if (TrackState != SCOPE_PARKED && _IsParked)
534 {
535 SetParked(true);
536 }
537 else
538 {
539 auto wasTracking = TrackStateSP[INDI_ENABLED].getState() == ISS_ON;
540 if (wasTracking != _IsTracking)
541 TrackState = _IsTracking ? SCOPE_TRACKING : SCOPE_IDLE;
542 }
543
544
546 {
548 LOG_ERROR("Error reading Ra - Dec");
549 EqNP.apply();
550 return false;
551 }
552
553 if (HasPierSide())
554 {
555 char response[DRIVER_LEN] = {0};
556 if (sendCommand(":Gm#", response))
557 {
558 if (response[0] == 'W')
560 else if (response[0] == 'E')
562 else
564 }
565 }
566
568
569 return true;
570}
571
572bool LX200NYX101::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
573{
574 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
575 {
576 if (MeridianLimitNP.isNameMatch(name))
577 {
578 MeridianLimitNP.update(values, names, n);
579 if (!isSimulation())
580 {
581 std::string command = ":SXE9," + std::to_string(MeridianLimitNP[0].getValue()) + "#";;
582 sendCommand(command.c_str());
583 command = ":SXEA," + std::to_string(MeridianLimitNP[0].getValue()) + "#";;
584 sendCommand(command.c_str());
585 if (MeridianLimitNP.getState() == IPS_OK)
586 {
587 LOGF_INFO("Meridian!: ", MeridianLimitNP[0].getValue());
588
589 }
590 }
591 else
592 {
593 MeridianLimitNP.setState(IPS_OK);
594 }
595
596 MeridianLimitNP.apply();
597 return true;
598 }
599
600 if (ElevationLimitNP.isNameMatch(name))
601 {
602 if(ElevationLimitNP.update(values, names, n))
603 {
604 for(int i = 0; i < n; ++i)
605 {
606 if(ElevationLimitNP[OVERHEAD].isNameMatch(names[i]))
607 {
608 std::string command = ":So" + std::to_string(static_cast<int>(ElevationLimitNP[OVERHEAD].getValue())) + "#";;
609 sendCommand(command.c_str());
610 }
611 else if(ElevationLimitNP[HORIZON].isNameMatch(names[i]))
612 {
613 std::string command = ":Sh" + std::to_string(static_cast<int>(ElevationLimitNP[HORIZON].getValue())) + "#";;
614 sendCommand(command.c_str());
615 }
616 }
617 ElevationLimitNP.apply();
618 return true;
619 }
620 }
621 }
622
623 return LX200Generic::ISNewNumber(dev, name, values, names, n);
624}
625
626bool LX200NYX101::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
627{
628 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
629 {
630 // Mount Type
631 if (MountTypeSP.isNameMatch(name))
632 {
633 int previousType = MountTypeSP.findOnSwitchIndex();
634 MountTypeSP.update(states, names, n);
635 IPState state = IPS_OK;
636 if (isConnected())
637 {
638 auto targetType = MountTypeSP.findOnSwitchIndex();
639 state = setMountType(targetType) ? IPS_OK : IPS_ALERT;
640 if (state == IPS_OK && previousType != targetType)
641 LOG_WARN("Restart mount in order to apply changes to Mount Type.");
642 }
643 MountTypeSP.setState(state);
644 MountTypeSP.apply();
645 return true;
646 }
647 else if(GuideRateSP.isNameMatch(name))
648 {
649 int previousType = GuideRateSP.findOnSwitchIndex();
650 GuideRateSP.update(states, names, n);
651 IPState state = IPS_OK;
652 if (isConnected())
653 {
654 auto targetType = GuideRateSP.findOnSwitchIndex();
655 state = setGuideRate(targetType) ? IPS_OK : IPS_ALERT;
656 if (state == IPS_OK && previousType != targetType)
657 LOG_WARN("RA and DEC guide rate changed.");
658 }
659 GuideRateSP.setState(state);
660 GuideRateSP.apply();
661 return true;
662 }
663 else if(FlipSP.isNameMatch(name))
664 {
665 FlipSP.update(states, names, n);
666 IPState state = IPS_OK;
667 if (isConnected())
668 {
669 FlipSP[0].setState(ISS_OFF);
670 sendCommand(":MN#");
671 }
672 FlipSP.setState(state);
673 FlipSP.apply();
674 return true;
675 }
676 else if(RebootSP.isNameMatch(name))
677 {
678 RebootSP.update(states, names, n);
679 IPState state = IPS_OK;
680 if (isConnected())
681 {
682 RebootSP[0].setState(ISS_OFF);
683 sendCommand(":ERESET#");
684 }
685 RebootSP.setState(state);
686 RebootSP.apply();
687 return true;
688 }
689 else if(SafetyLimitSP.isNameMatch(name))
690 {
691 SafetyLimitSP.update(states, names, n);
692 auto index = SafetyLimitSP.findOnSwitchIndex();
693 switch(index)
694 {
695 case SET_SAFETY_LIMIT:
696 sendCommand(":Sc1#");
697 sendCommand(":Sc#");
698 break;
699 case CLEAR_SAFETY_LIMIT:
700 sendCommand(":Sc0#");
701 sendCommand(":Sc#");
702 break;
703 }
704 SafetyLimitSP.apply();
705 return true;
706 }
707 else if(RefractSP.isNameMatch(name))
708 {
709 RefractSP.update(states, names, n);
710 auto index = RefractSP.findOnSwitchIndex();
711
712 switch(index)
713 {
714 case REFRACT_ON:
715 sendCommand(":Tr#");
716 break;
717 case REFRACT_OFF:
718 sendCommand(":Tn#");
719 break;
720 }
721 RefractSP.apply();
722 return true;
723 }
724 else if(VerboseReportSP.isNameMatch(name))
725 {
726 VerboseReportSP.update(states, names, n);
727 int index = VerboseReportSP.findOnSwitchIndex();
728 VerboseReportSP[index].setState(ISS_OFF);
729
730 if(index == 0)
731 verboseReport = true;
732 else
733 {
734
735 SetPropertyText(IsTracking, IPS_IDLE);
736 SetPropertyText(IsSlewCompleted, IPS_IDLE);
737 SetPropertyText(IsParked, IPS_IDLE);
738 SetPropertyText(IsParkginInProgress, IPS_IDLE);
739 SetPropertyText(IsAtHomePosition, IPS_IDLE);
740 SetPropertyText(MountAltAz, IPS_IDLE);
741 SetPropertyText(MountEquatorial, IPS_IDLE);
742 SetPropertyText(PierNone, IPS_IDLE);
743 SetPropertyText(PierEast, IPS_IDLE);
744 SetPropertyText(PierWest, IPS_IDLE);
745 SetPropertyText(DoesRefractionComp, IPS_IDLE);
746 SetPropertyText(WaitingAtHome, IPS_IDLE);
747 SetPropertyText(IsHomePaused, IPS_IDLE);
748 SetPropertyText(ParkFailed, IPS_IDLE);
749 SetPropertyText(SlewingHome, IPS_IDLE);
750 verboseReport = false;
751 }
752
753
754 VerboseReportSP.setState(index == 0 ? IPS_OK : IPS_IDLE);
755 VerboseReportSP.apply();
756 return true;
757 }
758 }
759 return LX200Generic::ISNewSwitch(dev, name, states, names, n);
760}
761
762#ifdef DEBUG_NYX
763bool LX200NYX101::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
764{
765 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
766 {
767 if (DebugCommandTP.isNameMatch(name))
768 {
769 DebugCommandTP.update(texts, names, n);
770 for(int i = 0; i < n; i++)
771 {
772 if(DebugCommandTP[0].isNameMatch(names[i]))
773 {
774 char status[DRIVER_LEN] = {0};
775 sendCommand(texts[i]);
776 i = n;
777 }
778 }
779 return true;
780 }
781 }
782 return LX200Generic::ISNewText(dev, name, texts, names, n);
783
784}
785#endif
786
788{
789 double value = 0.0;
790 switch(index)
791 {
792 case 0:
793 value = 0.01;
794 break;
795 case 1:
796 value = 0.03;
797 break;
798 case 2:
799 value = 0.07;
800 break;
801 case 3:
802 value = 0.27;
803 break;
804 case 4:
805 value = 0.50;
806 break;
807 case 5:
808 value = 0.65;
809 break;
810 case 6:
811 value = 0.80;
812 break;
813 case 7:
814 value = 1;
815 break;
816 case 8:
817 value = 2.5;
818 break;
819 case 9:
820 value = 5;
821 break;
822 };
823
824 char decCommand[DRIVER_LEN] = {0};
825 snprintf(decCommand, DRIVER_LEN, ":RE%f#", value);
826
827 char raCommand[DRIVER_LEN] = {0};
828 snprintf(raCommand, DRIVER_LEN, ":RA%f#", value);
829
830 return sendCommand(decCommand) && sendCommand(raCommand);
831}
832
833bool LX200NYX101::setGuideRate(int rate)
834{
835 char command[DRIVER_LEN] = {0};
836 snprintf(command, DRIVER_LEN, ":R%d#", rate);
837 return sendCommand(command);
838}
839
841{
842 switch(mode)
843 {
844 case TRACK_SIDEREAL:
845 return sendCommand(":TQ#");
846 break;
847 case TRACK_SOLAR:
848 return sendCommand(":TS#");
849 break;
850 case TRACK_LUNAR:
851 return sendCommand(":TL#");
852 break;
853 case TRACK_KING:
854 return sendCommand(":TK#");
855 break;
856 }
857 return false;
858}
859
860bool LX200NYX101::setMountType(int type)
861{
862 return sendCommand((type == Equatorial) ? ":SXEM,1#" : "::SXEM,3#");
863}
864
865bool LX200NYX101::goToPark()
866{
867 LOG_INFO("Park requested.");
868 return sendCommand(":hP#");
869}
870
871bool LX200NYX101::goToUnPark()
872{
873 return sendCommand(":hR#");
874}
875
877{
878 bool rc = goToPark();
879 if (rc)
881
882 return rc;
883}
884
886{
887 bool rc = goToUnPark();
888 if(rc)
889 SetParked(false);
890
891 return rc;
892}
893
895{
896 char response[DRIVER_LEN] = {0};
897 bool rc = sendCommand(enabled ? ":Te#" : ":Td#", response, 4, 1);
898 return rc && response[0] == '1';
899}
900
901bool LX200NYX101::setUTCOffset(double offset)
902{
903 int h {0}, m {0}, s {0};
904 char command[DRIVER_LEN] = {0};
905 offset *= -1;
906 getSexComponents(offset, &h, &m, &s);
907
908 snprintf(command, DRIVER_LEN, ":SG%c%02d:%02d#", offset >= 0 ? '+' : '-', std::abs(h), m);
909 return setStandardProcedure(PortFD, command) == 0;
910}
911
912bool LX200NYX101::setLocalDate(uint8_t days, uint8_t months, uint16_t years)
913{
914 char command[DRIVER_LEN] = {0};
915 snprintf(command, DRIVER_LEN, ":SC%02d/%02d/%02d#", months, days, years % 100);
916 return setStandardProcedure(PortFD, command) == 0;
917}
918
919bool LX200NYX101::updateLocation(double latitude, double longitude, double elevation)
920{
921 INDI_UNUSED(elevation);
922 int d{0}, m{0}, s{0};
923
924 // JM 2021-04-10: MUST convert from INDI longitude to standard longitude.
925 // DO NOT REMOVE
926 if (longitude > 180)
927 longitude = longitude - 360;
928
929 char command[DRIVER_LEN] = {0};
930 // Reverse as per Meade way
931 longitude *= -1;
932 getSexComponents(longitude, &d, &m, &s);
933 snprintf(command, DRIVER_LEN, ":Sg%c%03d*%02d:%02d#", longitude >= 0 ? '+' : '-', std::abs(d), m, s);
934 if (setStandardProcedure(PortFD, command) < 0)
935 {
936 LOG_ERROR("Error setting site longitude coordinates");
937 return false;
938 }
939
940 getSexComponents(latitude, &d, &m, &s);
941 snprintf(command, DRIVER_LEN, ":St%c%02d*%02d:%02d#", latitude >= 0 ? '+' : '-', std::abs(d), m, s);
942 if (setStandardProcedure(PortFD, command) < 0)
943 {
944 LOG_ERROR("Error setting site latitude coordinates");
945 return false;
946 }
947
948 return true;
949}
950
951
952bool LX200NYX101::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
953{
954 int nbytes_written = 0, nbytes_read = 0, rc = -1;
955
956 tcflush(PortFD, TCIOFLUSH);
957
958 if (cmd_len > 0)
959 {
960 char hex_cmd[DRIVER_LEN * 3] = {0};
961 hexDump(hex_cmd, cmd, cmd_len);
962 LOGF_DEBUG("CMD <%s>", hex_cmd);
963 rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
964 }
965 else
966 {
967 LOGF_DEBUG("CMD <%s>", cmd);
968 rc = tty_write_string(PortFD, cmd, &nbytes_written);
969 }
970
971 if (rc != TTY_OK)
972 {
973 char errstr[MAXRBUF] = {0};
974 tty_error_msg(rc, errstr, MAXRBUF);
975 LOGF_ERROR("Serial write error: %s.", errstr);
976 return false;
977 }
978
979 if (res == nullptr)
980 {
981 tcdrain(PortFD);
982 return true;
983 }
984
985 if (res_len > 0)
986 rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
987 else
988 rc = tty_nread_section(PortFD, res, DRIVER_LEN, DRIVER_STOP_CHAR, DRIVER_TIMEOUT, &nbytes_read);
989
990 if (rc != TTY_OK)
991 {
992 char errstr[MAXRBUF] = {0};
993 tty_error_msg(rc, errstr, MAXRBUF);
994 LOGF_ERROR("Serial read error: %s.", errstr);
995 return false;
996 }
997
998 if (res_len > 0)
999 {
1000 char hex_res[DRIVER_LEN * 3] = {0};
1001 hexDump(hex_res, res, res_len);
1002 LOGF_DEBUG("RES <%s>", hex_res);
1003 }
1004 else
1005 {
1006 LOGF_DEBUG("RES <%s>", res);
1007 }
1008
1009 tcflush(PortFD, TCIOFLUSH);
1010
1011 return true;
1012}
1013
1014void LX200NYX101::hexDump(char * buf, const char * data, int size)
1015{
1016 for (int i = 0; i < size; i++)
1017 sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
1018
1019 if (size > 0)
1020 buf[3 * size - 1] = '\0';
1021}
1022
1023// Homing
1025{
1026 switch (action)
1027 {
1028 case HOME_GO:
1029 sendCommand(":hC#");
1030 return IPS_BUSY;
1031
1032 case HOME_SET:
1033 sendCommand(":hF#");
1034 return IPS_OK;
1035
1036 default:
1037 return IPS_ALERT;
1038 }
1039
1040 return IPS_ALERT;
1041}
1042
1043std::vector<std::string> LX200NYX101::split(const std::string &input, const std::string &regex)
1044{
1045 // passing -1 as the submatch index parameter performs splitting
1046 std::regex re(regex);
1047 std::sregex_token_iterator
1048 first{input.begin(), input.end(), re, -1},
1049 last;
1050 return {first, last};
1051}
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
bool isConnected() const
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)
bool isSimulation() const
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
TelescopeStatus TrackState
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
Connection::Serial * serialConnection
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool setUTCOffset(double offset) override
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool setLocalDate(uint8_t days, uint8_t months, uint16_t years) override
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool Park() override
Park the telescope to its home position.
virtual const char * getDefaultName() override
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual IPState ExecuteHomeAction(TelescopeHomeAction action) override
Execute a homing action.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void setLX200Capability(uint32_t cap)
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
#define STATUS_TAB
Definition gemini.cpp:38
INDI::PropertyNumber EqNP
INDI::PropertySwitch TrackStateSP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
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_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
void getSexComponents(double value, int *d, int *m, int *s)
Definition indicom.c:243
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
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 INDI_UNUSED(x)
Definition indidevapi.h:132
#define MAXRBUF
Definition indidriver.c:52
int IUGetConfigOnSwitchIndex(const char *dev, const char *property, int *index)
IUGetConfigOnSwitchIndex Opens configuration file and reads single switch property to find ON switch ...
Definition indidriver.c:719
#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
const char * SETTINGS_TAB
const char * STATUS_TAB
const char * OFF
const char * ON
int setStandardProcedure(int fd, const char *data)
#define getLX200DEC(fd, x)
@ LX200_24
Definition lx200driver.h:64
#define getLX200RA(fd, x)
uint16_t type
Definition pwc-ioctl.h:0
uint8_t cmd[4]
Definition pwc-ioctl.h:2
#define SETTINGS_TAB
Definition robofocus.cpp:44
#define OFF
Definition stvdriver.h:26
#define ON
Definition stvdriver.h:27