Instrument Neutral Distributed Interface INDI 2.2.5
astrotrac.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3
4 Driver for using TheSky6 Pro Scripted operations for mounts via the TCP server.
5 While this technically can operate any mount connected to the TheSky6 Pro, it is
6 intended for AstroTrac mounts control.
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License version 2 as published by the Free Software Foundation.
11 .
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16 .
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*******************************************************************************/
22
23#include "astrotrac.h"
24
25#include "indicom.h"
26#include "inditimer.h"
28#include <libnova/sidereal_time.h>
29#include <libnova/transform.h>
30
31#include <cmath>
32#include <cstring>
33#include <memory>
34#include <regex>
35#include <array>
36#include <termios.h>
37
38std::unique_ptr<AstroTrac> AstroTrac_mount(new AstroTrac());
39const std::array<uint32_t, AstroTrac::SLEW_MODES> AstroTrac::SLEW_SPEEDS = {{1, 2, 4, 8, 32, 64, 128, 600, 700, 800}};
40
41using namespace INDI::AlignmentSubsystem;
42
53
55{
56 return "AstroTrac";
57}
58
63{
65
66 // Track Modes
67 AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
68 AddTrackMode("TRACK_SOLAR", "Solar");
69 AddTrackMode("TRACK_LUNAR", "Lunar");
70 AddTrackMode("TRACK_CUSTOM", "Custom");
71
72 // Slew Speeds
73 for (uint8_t i = 0; i < SLEW_MODES; i++)
74 {
75 SlewRateSP[i].setLabel(std::to_string(SLEW_SPEEDS[i]) + "x");
76 SlewRateSP[i].setAux((void *)&SLEW_SPEEDS[i]);
77 }
79
80 // Mount Type
81 int configMountType = MOUNT_GEM;
82 IUGetConfigOnSwitchIndex(getDeviceName(), "MOUNT_TYPE", &configMountType);
83 MountTypeSP[MOUNT_GEM].fill("MOUNT_GEM", "GEM",
84 configMountType == MOUNT_GEM ? ISS_ON : ISS_OFF);
85 MountTypeSP[MOUNT_SINGLE_ARM].fill("MOUNT_SINGLE_ARM", "Single ARM",
86 configMountType == MOUNT_GEM ? ISS_OFF : ISS_ON);
87 MountTypeSP.fill(getDeviceName(), "MOUNT_TYPE", "Mount Type", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
88
89 // Acceleration
90 AccelerationNP[AXIS_RA].fill("AXIS_RA", "RA arcsec/sec^2", "%.2f", 0, 3600, 100, 0);
91 AccelerationNP[AXIS_DE].fill("AXIS_DE", "DE arcsec/sec^2", "%.2f", 0, 3600, 100, 0);
92 AccelerationNP.fill(getDeviceName(), "MOUNT_ACCELERATION", "Acceleration", MOTION_TAB, IP_RW, 60, IPS_IDLE);
93
94 // Encoders
95 EncoderNP[AXIS_RA].fill("AXIS_RA", "Hour Angle", "%.2f", -3600, 3600, 100, 0);
96 EncoderNP[AXIS_DE].fill("AXIS_DE", "Declination", "%.2f", -3600, 3600, 100, 0);
97 EncoderNP.fill(getDeviceName(), "MOUNT_ENCODERS", "Encoders", MOTION_TAB, IP_RW, 60, IPS_IDLE);
98
99 // How fast to guide compared to sidereal rate.
100 // The default rate is 0.1x sidereal, which is apparently the best for the mount accoring to the author of X2 plugin.
101 // Users may need to calibrate guiding for longer to see movement
102 GuideRateNP[AXIS_RA].fill("GUIDE_RATE_WE", "W/E Rate", "%g", 0, 1, 0.1, 0.1);
103 GuideRateNP[AXIS_DE].fill("GUIDE_RATE_NS", "N/S Rate", "%g", 0, 1, 0.1, 0.1);
104 GuideRateNP.fill(getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RW, 0, IPS_IDLE);
105
107
109
111
112 tcpConnection->setDefaultHost("192.168.1.1");
114
116
118
120 // set mount type to alignment subsystem
122 // Init Math plugin
123 Initialise(this);
124
125 // Force the alignment system to always be on
126 getSwitch("ALIGNMENT_SUBSYSTEM_ACTIVE")[0].setState(ISS_ON);
127
128 return true;
129}
130
134void AstroTrac::ISGetProperties(const char *dev)
135{
137
138 defineProperty(MountTypeSP);
139}
140
145{
147
148 if (isConnected())
149 {
150 getAcceleration(AXIS_RA);
151 getAcceleration(AXIS_DE);
152 getVelocity(AXIS_RA);
153 getVelocity(AXIS_DE);
154
155 defineProperty(FirmwareTP);
156 defineProperty(AccelerationNP);
157 defineProperty(EncoderNP);
158 defineProperty(GuideRateNP);
159
160 // Initial AZ/AL parking position.
161 if (InitPark())
162 {
163 // If loading parking data is successful, we just set the default parking values.
166 }
167 else
168 {
169 // Otherwise, we set all parking data to default in case no parking data is found.
170 SetAxis1Park(0);
171 SetAxis2Park(0);
174 }
175 }
176 else
177 {
178 deleteProperty(FirmwareTP);
179 deleteProperty(AccelerationNP);
180 deleteProperty(EncoderNP);
181 deleteProperty(GuideRateNP);
182 }
183
185
186
187 return true;
188}
189
194{
195 return getVersion();
196}
197
201bool AstroTrac::getVersion()
202{
203 char response[DRIVER_LEN] = {0};
204 if (sendCommand("<1zv?>", response))
205 {
206 FirmwareTP[0].setText(response + 4, 4);
207 return true;
208 }
209
210 return false;
211}
212
216bool AstroTrac::getAcceleration(INDI_EQ_AXIS axis)
217{
218 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
219 snprintf(command, DRIVER_LEN, "<%da?>", axis + 1);
220 if (sendCommand(command, response))
221 {
222 try
223 {
224 std::string acceleration = std::regex_replace(
225 response,
226 std::regex("<.a(\\d+)>"),
227 std::string("$1"));
228
229 AccelerationNP[axis].setValue(std::stoi(acceleration));
230 return true;
231 }
232 catch (...)
233 {
234 LOGF_DEBUG("Failed to parse acceleration (%s)", response);
235 }
236 }
237
238 return false;
239}
240
244bool AstroTrac::setAcceleration(INDI_EQ_AXIS axis, uint32_t a)
245{
246 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
247
248 snprintf(command, DRIVER_LEN, "<%da%u>", axis + 1, a);
249 if (sendCommand(command, response))
250 return response[3] == '#';
251
252 return false;
253}
254
258bool AstroTrac::getVelocity(INDI_EQ_AXIS axis, double &value)
259{
260 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
261 snprintf(command, DRIVER_LEN, "<%dv?>", axis + 1);
262 if (sendCommand(command, response))
263 {
264 try
265 {
266 std::string velocity = std::regex_replace(
267 response,
268 std::regex("<.v([+-]?[0-9]+\\.[0-9]+?)>"),
269 std::string("$1"));
270
271 value = std::stod(velocity) * (m_Location.latitude >= 0 ? 1 : -1);
272 return true;
273 }
274 catch (...)
275 {
276 LOGF_DEBUG("Failed to parse velocity (%s)", response);
277 }
278 }
279
280 value = 0.0;
281 return false;
282}
283
287bool AstroTrac::getVelocity(INDI_EQ_AXIS axis)
288{
289 double velocity(0);
290 if (getVelocity(axis, velocity))
291 {
292 TrackRateNP[axis].setValue(velocity);
293 return true;
294 }
295 return false;
296}
297
301bool AstroTrac::setVelocity(INDI_EQ_AXIS axis, double value)
302{
303 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
304
305 // Reverse value depending on hemisphere
306 snprintf(command, DRIVER_LEN, "<%dve%f>", axis + 1, value * (m_Location.latitude >= 0 ? 1 : -1));
307 if (sendCommand(command, response))
308 return response[4] == '#';
309
310 return false;
311}
312
316bool AstroTrac::stopMotion(INDI_EQ_AXIS axis)
317{
318 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
319
320 snprintf(command, DRIVER_LEN, "<%dx>", axis + 1);
321 if (sendCommand(command, response))
322 return response[3] == '#';
323
324 return false;
325}
326
330bool AstroTrac::isSlewComplete()
331{
332 char HAResponse[DRIVER_LEN] = {0}, DEResponse[DRIVER_LEN] = {0};
333 if (sendCommand("<1t?>", HAResponse) && sendCommand("<2t?>", DEResponse))
334 {
335 return (HAResponse[3] == '0' && DEResponse[3] == '0');
336 }
337
338 return false;
339}
340
344bool AstroTrac::syncEncoder(INDI_EQ_AXIS axis, double value)
345{
346 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
347
348 snprintf(command, DRIVER_LEN, "<%dy%f>", axis + 1, value);
349 if (sendCommand(command, response))
350 return response[3] == '#';
351
352 return false;
353}
354
358bool AstroTrac::slewEncoder(INDI_EQ_AXIS axis, double value)
359{
360 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
361
362 snprintf(command, DRIVER_LEN, "<%dp%f>", axis + 1, value);
363 if (sendCommand(command, response))
364 return response[3] == '#';
365
366 return false;
367}
368
375bool AstroTrac::getEncoderPosition(INDI_EQ_AXIS axis)
376{
377 char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
378 snprintf(command, DRIVER_LEN, "<%dp?>", axis + 1);
379 if (sendCommand(command, response))
380 {
381 try
382 {
383 char regex_str[64] = {0};
384 snprintf(regex_str, 64, "<%dp([+-]?[0-9]+\\.[0-9]+?)>", axis + 1);
385 std::string position = std::regex_replace(
386 response,
387 std::regex(regex_str),
388 std::string("$1"));
389
390 EncoderNP[axis].setValue(std::stod(position));
391 return true;
392 }
393 catch (...)
394 {
395 try
396 {
397 // Check if the response for the other axis
398 INDI_EQ_AXIS other = (axis == AXIS_RA) ? AXIS_DE : AXIS_RA;
399 char regex_str[64] = {0};
400 snprintf(regex_str, 64, "<%dp([+-]?[0-9]+\\.[0-9]+?)>", other + 1);
401 std::string position = std::regex_replace(
402 response,
403 std::regex(regex_str),
404 std::string("$1"));
405
406 EncoderNP[other].setValue(std::stod(position));
407 return true;
408 }
409 catch (...)
410 {
411 LOGF_DEBUG("Failed to parse position (%s)", response);
412 }
413 }
414 }
415
416 return false;
417}
418
426void AstroTrac::getRADEFromEncoders(double haEncoder, double deEncoder, double &ra, double &de, TelescopePierSide &pierSide)
427{
428 static const double jitter = 0.0005;
429 double ha = 0;
430
431 // Take care of jitter
432 if (haEncoder > jitter * -1 && haEncoder < jitter)
433 haEncoder = 0;
434 if (deEncoder > jitter * -1 && deEncoder < jitter)
435 deEncoder = 0;
436
437 // Northern Hemisphere
438 if (LocationNP[LOCATION_LATITUDE].getValue() >= 0)
439 {
440 // "Normal" Pointing State (West, looking East)
441 if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || deEncoder >= 0)
442 {
443 de = std::min(90 - deEncoder, 90.0);
444 ha = -6.0 + (haEncoder / 360.0) * 24.0;
445 pierSide = PIER_WEST;
446 }
447 // "Reversed" Pointing State (East, looking West)
448 else
449 {
450 de = 90 + deEncoder;
451 ha = 6.0 + (haEncoder / 360.0) * 24.0;
452 pierSide = PIER_EAST;
453 }
454 }
455 // Southern Hemisphere
456 else
457 {
458 // "Normal" Pointing State (West, looking East)
459 if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || deEncoder <= 0)
460 {
461 de = std::max(-90 - deEncoder, -90.0);
462 ha = -6.0 - (haEncoder / 360.0) * 24.0;
463 pierSide = PIER_WEST;
464 }
465 // "Reversed" Pointing State (East, looking West)
466 else
467 {
468 de = -90 + deEncoder;
469 ha = 6.0 - (haEncoder / 360.0) * 24.0;
470 pierSide = PIER_EAST;
471 }
472 }
473
474 double lst = get_local_sidereal_time(LocationNP[LOCATION_LONGITUDE].getValue());
475 ra = range24(lst - ha);
476
477 char RAStr[32] = {0}, DecStr[32] = {0};
478 fs_sexa(RAStr, ra, 2, 3600);
479 fs_sexa(DecStr, de, 2, 3600);
480 LOGF_DEBUG("Encoders HA: %.4f DE: %.4f Processed: HA: %.4f DE: %.4f (%s) LST: %.4f RA: %.4f (%s)",
481 haEncoder, deEncoder, ha, de, DecStr, lst, ra, RAStr);
482}
483
490void AstroTrac::getEncodersFromRADE(double ra, double de, double &haEncoder, double &deEncoder)
491{
492 double lst = get_local_sidereal_time(LocationNP[LOCATION_LONGITUDE].getValue());
493 double dHA = rangeHA(lst - ra);
494 // Northern Hemisphere
495 if (LocationNP[LOCATION_LATITUDE].getValue() >= 0)
496 {
497 // "Normal" Pointing State (East, looking West)
498 if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || dHA <= 0)
499 {
500 deEncoder = -(de - 90.0);
501 haEncoder = (dHA + 6.0) * 360.0 / 24.0;
502 }
503 // "Reversed" Pointing State (West, looking East)
504 else
505 {
506 deEncoder = de - 90.0;
507 haEncoder = (dHA - 6.0) * 360.0 / 24.0;
508 }
509 }
510 else
511 {
512 // "Normal" Pointing State (East, looking West)
513 if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || dHA <= 0)
514 {
515 deEncoder = -(de + 90.0);
516 haEncoder = -(dHA + 6.0) * 360.0 / 24.0;
517 }
518 // "Reversed" Pointing State (West, looking East)
519 else
520 {
521 deEncoder = (de + 90.0);
522 haEncoder = -(dHA - 6.0) * 360 / 24.0;
523 }
524 }
525}
526
530bool AstroTrac::Sync(double ra, double dec)
531{
532 AlignmentDatabaseEntry NewEntry;
533 NewEntry.ObservationJulianDate = ln_get_julian_from_sys();
534 // Actual Celestial Coordinates
535 NewEntry.RightAscension = ra;
536 NewEntry.Declination = dec;
537 // Apparent Telescope Coordinates
538 NewEntry.TelescopeDirection = TelescopeDirectionVectorFromEquatorialCoordinates(m_MountInternalCoordinates);
539 NewEntry.PrivateDataSize = 0;
540
541 if (!CheckForDuplicateSyncPoint(NewEntry, 0.001))
542 {
543 GetAlignmentDatabase().push_back(NewEntry);
544
545 // Tell the client about size change
546 UpdateSize();
547
548 // Tell the math plugin to reinitiali
549 Initialise(this);
550
551 char RAStr[32], DecStr[32];
552 fs_sexa(RAStr, ra, 2, 3600);
553 fs_sexa(DecStr, dec, 2, 3600);
554 LOGF_INFO("Syncing to JNOW RA %s - DEC %s", RAStr, DecStr);
555
556 return true;
557 }
558
559 LOGF_DEBUG("Sync - duplicate entry RA: %lf(%lf) DEC: %lf", ra * 360.0 / 24.0, ra, dec);
560 return false;
561}
562
566bool AstroTrac::Goto(double ra, double dec)
567{
568 double haEncoder = 0, deEncoder = 0;
569 INDI::IEquatorialCoordinates telescopeCoordinates;
570 if (getTelescopeFromSkyCoordinates(ra, dec, telescopeCoordinates))
571 {
572 char mountRAString[32] = {0}, mountDEString[32] = {0}, skyRAString[32] = {0}, skyDEString[32] = {0};
573 fs_sexa(mountRAString, telescopeCoordinates.rightascension, 2, 3600);
574 fs_sexa(mountDEString, telescopeCoordinates.declination, 2, 3600);
575 fs_sexa(skyRAString, ra, 2, 3600);
576 fs_sexa(skyDEString, dec, 2, 3600);
577
578 LOGF_DEBUG("GOTO Sky RA: %s DE: %s ---> Mount RA: %s DE: %s", skyRAString, skyDEString, mountRAString, mountDEString);
579
580 getEncodersFromRADE(telescopeCoordinates.rightascension, telescopeCoordinates.declination, haEncoder, deEncoder);
581
582 // Account for acceletaion, max speed, and deceleration by the time we get there.
583 // Get time in seconds
584 double tHA = calculateSlewTime(haEncoder - EncoderNP[AXIS_RA].getValue());
585 // Adjust for hemisphere. Convert time to delta degrees
586 tHA = tHA * (m_Location.latitude >= 0 ? 1 : -1) * TRACKRATE_SIDEREAL / 3600;
587
588 LOGF_DEBUG("GOTO Encoders HA: %.4f (%.4f + %.4f) DE: %.4f", haEncoder + tHA, haEncoder, tHA, deEncoder);
589
590 // Now go to each encoder
591 bool rc1 = slewEncoder(AXIS_RA, haEncoder + tHA);
592 bool rc2 = slewEncoder(AXIS_DE, deEncoder);
593
594 if (rc1 && rc2)
595 {
597
598 char RAStr[32], DecStr[32];
599 fs_sexa(RAStr, ra, 2, 3600);
600 fs_sexa(DecStr, dec, 2, 3600);
601 LOGF_INFO("Slewing to JNOW RA %s - DEC %s", RAStr, DecStr);
602 return true;
603 }
604 }
605
606 return false;
607}
608
614double AstroTrac::calculateSlewTime(double distance)
615{
616 // Firstly throw away sign of distance - don't care about direction - and convert to arcsec
617 distance = fabs(distance) * 3600.0;
618
619 // Now estimate how far mount travels during acceleration and deceleration period
620 double accelerate_decelerate = MAX_SLEW_VELOCITY * MAX_SLEW_VELOCITY / AccelerationNP[AXIS_RA].getValue();
621
622 // If distance less than this, then calculate using acceleration forumlae:
623 if (distance < accelerate_decelerate)
624 {
625 return (2 * sqrt(distance / AccelerationNP[AXIS_RA].getValue()));
626 }
627 else
628 {
629 // Time is equal to twice the time required to accelerate or decelerate, plus the remaining distance at max slew speed
630 return (2.0 * MAX_SLEW_VELOCITY / AccelerationNP[AXIS_RA].getValue() + (distance - accelerate_decelerate) /
631 MAX_SLEW_VELOCITY);
632 }
633}
634
639{
641 double ra = 0, de = 0, skyRA = 0, skyDE = 0;
643
644 if (isSimulation())
645 simulateMount();
646
647 double lastHAEncoder = EncoderNP[AXIS_RA].getValue();
648 double lastDEEncoder = EncoderNP[AXIS_DE].getValue();
649 if (getEncoderPosition(AXIS_RA) && getEncoderPosition(AXIS_DE))
650 {
651 getRADEFromEncoders(EncoderNP[AXIS_RA].getValue(), EncoderNP[AXIS_DE].getValue(), ra, de, side);
652 // Send to client if changed.
653 if (std::fabs(lastHAEncoder - EncoderNP[AXIS_RA].getValue()) > 0
654 || std::fabs(lastDEEncoder - EncoderNP[AXIS_DE].getValue()) > 0)
655 {
656 EncoderNP.apply();
657 }
658 }
659 else
660 return false;
661
663 {
664 if (isSlewComplete())
665 {
667 {
668 LOG_INFO("Slew complete, tracking...");
670 SetTrackEnabled(true);
671 }
672 // Parking
673 else
674 {
675 SetTrackEnabled(false);
676 SetParked(true);
677 }
678 }
679 }
680
681 m_MountInternalCoordinates.rightascension = ra;
682 m_MountInternalCoordinates.declination = de;
683 TDV = TelescopeDirectionVectorFromEquatorialCoordinates(m_MountInternalCoordinates);
684
685 if (TransformTelescopeToCelestialJD(TDV, skyRA, skyDE, ln_get_julian_from_sys()))
686 {
687 // double lst = get_local_sidereal_time(LocationNP[LOCATION_LONGITUDE].getValue());
688 // double dHA = rangeHA(lst - skyRA);
689 setPierSide(side);
690
691 char mountRAString[32] = {0}, mountDEString[32] = {0}, skyRAString[32] = {0}, skyDEString[32] = {0};
692 fs_sexa(mountRAString, ra, 2, 3600);
693 fs_sexa(mountDEString, de, 2, 3600);
694 fs_sexa(skyRAString, skyRA, 2, 3600);
695 fs_sexa(skyDEString, skyDE, 2, 3600);
696
697 LOGF_DEBUG("Mount RA: %s DE: %s ---> Sky RA: %s DE: %s", mountRAString, mountDEString, skyRAString, skyDEString);
698
699 NewRaDec(skyRA, skyDE);
700 return true;
701 }
702
703 LOG_ERROR("TransformTelescopeToCelestial failed in ReadScopeStatus");
704 return false;
705}
706
710bool AstroTrac::getTelescopeFromSkyCoordinates(double ra, double de, INDI::IEquatorialCoordinates &telescopeCoordinates)
711{
713 if (TransformCelestialToTelescopeJD(ra, de, ln_get_julian_from_sys(), TDV))
714 {
715 EquatorialCoordinatesFromTelescopeDirectionVector(TDV, telescopeCoordinates);
716 LOGF_DEBUG("TransformCelestialToTelescope: RA=%lf DE=%lf, TDV (x :%lf, y: %lf, z: %lf), local hour RA %lf DEC %lf",
717 ra, de, TDV.x, TDV.y, TDV.z, telescopeCoordinates.rightascension, telescopeCoordinates.declination);
718 return true;
719 }
720
721 return false;
722}
723
728{
729 if (slewEncoder(AXIS_RA, GetAxis1Park()) && slewEncoder(AXIS_DE, GetAxis2Park()))
730 {
732 LOG_INFO("Parking is in progress...");
733 return true;
734 }
735
736 return false;
737}
738
743{
744 SetParked(false);
745 return true;
746}
747
751bool AstroTrac::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
752{
753 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
754 {
755 // Process alignment properties
756 ProcessAlignmentTextProperties(this, name, texts, names, n);
757 }
758
759 return INDI::Telescope::ISNewText(dev, name, texts, names, n);
760}
761
765bool AstroTrac::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
766{
767 // Check guider interface
768 if (GI::processNumber(dev, name, values, names, n))
769 return true;
770
771 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
772 {
773 // Guide Rate
774 if (GuideRateNP.isNameMatch(name))
775 {
776 GuideRateNP.update(values, names, n);
777 GuideRateNP.setState(IPS_OK);
778 GuideRateNP.apply();
779 return true;
780 }
781
782 // Acceleration
783 if (AccelerationNP.isNameMatch(name))
784 {
785 AccelerationNP.update(values, names, n);
786
787 if (setAcceleration(AXIS_RA, AccelerationNP[AXIS_RA].getValue())
788 && setAcceleration(AXIS_DE, AccelerationNP[AXIS_DE].getValue()))
789 AccelerationNP.setState(IPS_OK);
790 else
791 AccelerationNP.setState(IPS_ALERT);
792
793 AccelerationNP.apply();
794 return true;
795 }
796
797 // Encoders
798 if (EncoderNP.isNameMatch(name))
799 {
800 if (slewEncoder(AXIS_RA, values[0]) && slewEncoder(AXIS_DE, values[1]))
801 {
803 EncoderNP.setState(IPS_OK);
804 }
805 else
806 EncoderNP.setState(IPS_ALERT);
807
808 EncoderNP.apply();
809 return true;
810 }
811
812 // Process alignment properties
813 ProcessAlignmentNumberProperties(this, name, values, names, n);
814 }
815
816 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
817}
818
822bool AstroTrac::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
823{
824 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
825 {
826 // Mount Type
827 if (MountTypeSP.isNameMatch(name))
828 {
829 MountTypeSP.update(states, names, n);
830 MountTypeSP.setState(IPS_OK);
831 MountTypeSP.apply();
832 return true;
833 }
834
835 // Process alignment properties
836 ProcessAlignmentSwitchProperties(this, name, states, names, n);
837 }
838
839 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
840}
841
845bool AstroTrac::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
846 char *formats[], char *names[], int n)
847{
848 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
849 {
850 // Process alignment properties
851 ProcessAlignmentBLOBProperties(this, name, sizes, blobsizes, blobs, formats, names, n);
852 }
853 // Pass it up the chain
854 return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
855}
856
861{
862 bool rc1 = setVelocity(AXIS_RA, 0) && stopMotion(AXIS_RA);
863 bool rc2 = setVelocity(AXIS_DE, 0) && stopMotion(AXIS_DE);
864
865 return rc1 && rc2;
866}
867
872{
874 {
875 LOG_ERROR("Please unpark the mount before issuing any motion commands.");
876 return false;
877 }
878
879 if (command == MOTION_START)
880 {
882 * (dir == DIRECTION_NORTH ? 1 : -1);
883 setVelocity(AXIS_DE, velocity);
884 }
885 else
886 {
887 // reset tracking
889 stopMotion(AXIS_DE);
890 }
891
892 return true;
893}
894
899{
901 {
902 LOG_ERROR("Please unpark the mount before issuing any motion commands.");
903 return false;
904 }
905
906 if (command == MOTION_START)
907 {
909 * (dir == DIRECTION_WEST ? 1 : -1);
910 setVelocity(AXIS_RA, velocity);
911 }
912 else
913 {
914 // reset tracking
916 stopMotion(AXIS_RA);
917 }
918
919 return true;
920}
921
925bool AstroTrac::updateLocation(double latitude, double longitude, double elevation)
926{
928 // Set this according to mount type
930 return true;
931}
932
936bool AstroTrac::updateTime(ln_date *utc, double utc_offset)
937{
938 INDI_UNUSED(utc);
939 INDI_UNUSED(utc_offset);
940 return true;
941}
942
947{
948 SetAxis1Park(EncoderNP[AXIS_RA].getValue());
949 SetAxis2Park(EncoderNP[AXIS_DE].getValue());
950
951 return true;
952}
953
958{
959 SetAxis1Park(0);
960 SetAxis2Park(0);
961
962 return true;
963}
964
969{
970 // do nothing if idle
972 {
973 GuideNSNP[AXIS_RA].setValue(0);
974 GuideNSNP[AXIS_DE].setValue(0);
977
978 LOG_INFO("Please start tracking to set guide pulse");
979
980 return IPS_IDLE;
981 }
982
983 // get the current velocity
984 double lastRARate(0), lastDERate(0);
985 getVelocity(AXIS_RA, lastRARate);
986 getVelocity(AXIS_DE, lastDERate);
987
988 // if current DEC velocity is zero, try RA otherwise assume sidereal
989 lastRARate = std::fabs(lastRARate);
990 double rate = std::fabs(lastDERate) > 0 ? lastDERate : (lastRARate > 0 ? lastRARate : TRACKRATE_SIDEREAL);
991
992 // absolute movement in arcseconds
993 double dDE = std::fabs(GuideRateNP.at(AXIS_DE)->getValue() * rate * ms / 1000.0);
994
995 // final velocity guiding north is rate + dDE
996 setVelocity(AXIS_DE, rate + dDE);
997 INDI::Timer::singleShot(ms, [this]()
998 {
999 // reset tracking
1001 GuideNSNP[AXIS_RA].setValue(0);
1002 GuideNSNP[AXIS_DE].setValue(0);
1004 GuideNSNP.apply();
1005 });
1006 return IPS_BUSY;
1007}
1008
1013{
1014 // do nothing if idle
1016 {
1017 GuideNSNP[AXIS_RA].setValue(0);
1018 GuideNSNP[AXIS_DE].setValue(0);
1020 GuideNSNP.apply();
1021
1022 LOG_INFO("Please start tracking to set guide pulse");
1023
1024 return IPS_IDLE;
1025 }
1026
1027 // get the current velocity
1028 double lastRARate(0), lastDERate(0);
1029 getVelocity(AXIS_RA, lastRARate);
1030 getVelocity(AXIS_DE, lastDERate);
1031
1032 // if current DEC velocity is zero, try RA otherwise assume sidereal
1033 lastRARate = std::fabs(lastRARate);
1034 double rate = std::fabs(lastDERate) > 0 ? lastDERate : (lastRARate > 0 ? -lastRARate : -TRACKRATE_SIDEREAL);
1035
1036 // absolute movement in arcseconds
1037 double dDE = std::fabs(GuideRateNP.at(AXIS_DE)->getValue() * rate * ms / 1000.0);
1038
1039 // final velocity guiding south is rate - dDE
1040 setVelocity(AXIS_DE, rate - dDE);
1041 INDI::Timer::singleShot(ms, [this]()
1042 {
1043 // reset tracking
1045 GuideNSNP[AXIS_RA].setValue(0);
1046 GuideNSNP[AXIS_DE].setValue(0);
1048 GuideNSNP.apply();
1049 });
1050 return IPS_BUSY;
1051}
1052
1057{
1058 // do nothing if idle
1060 {
1061 GuideWENP[AXIS_RA].setValue(0);
1062 GuideWENP[AXIS_DE].setValue(0);
1064 GuideWENP.apply();
1065
1066 LOG_INFO("Please start tracking to set guide pulse");
1067
1068 return IPS_IDLE;
1069 }
1070
1071 // get the current velocity
1072 double lastRARate(0);
1073 getVelocity(AXIS_RA, lastRARate);
1074
1075 // if the current RA rate is zero, which is a valid custom tracking rate, just assume sidereal
1076 double rate = std::fabs(lastRARate) > 0 ? lastRARate : TRACKRATE_SIDEREAL;
1077
1078 // absolute movement in arcseconds
1079 double dRA = std::fabs(GuideRateNP.at(AXIS_RA)->getValue() * rate * ms / 1000.0);
1080
1081 // not sure if this logic belongs here or the calling function
1082 // // compensate RA velocity for declination
1083 // TelescopeDirectionVector TDV;
1084 // TDV = TelescopeDirectionVectorFromEquatorialCoordinates(m_MountInternalCoordinates);
1085 // double skyRA = 0, skyDE = 0;
1086 // if (TransformTelescopeToCelestial(TDV, skyRA, skyDE))
1087 // {
1088 // dRA *= std::cos(M_PI / 180 * skyDE);
1089 // }
1090
1091 // final velocity guiding east is rate + dRA, i.e the guide star will appear to move east
1092 setVelocity(AXIS_RA, rate + dRA);
1093 INDI::Timer::singleShot(ms, [this]()
1094 {
1095 // reset the tracking mode
1097 GuideWENP[AXIS_RA].setValue(0);
1098 GuideWENP[AXIS_DE].setValue(0);
1100 GuideWENP.apply();
1101 });
1102
1103 return IPS_BUSY;
1104}
1105
1110{
1111 // do nothing if idle
1113 {
1114 GuideWENP[AXIS_RA].setValue(0);
1115 GuideWENP[AXIS_DE].setValue(0);
1117 GuideWENP.apply();
1118
1119 LOG_INFO("Please start tracking to set guide pulse");
1120
1121 return IPS_IDLE;
1122 }
1123
1124 // get the current velocity
1125 double lastRARate(0);
1126 getVelocity(AXIS_RA, lastRARate);
1127
1128 // if the current RA rate is zero, which is a valid custom tracking rate, just assume sidereal
1129 double rate = std::fabs(lastRARate) > 0 ? lastRARate : TRACKRATE_SIDEREAL;
1130
1131 // absolute movement in arcseconds
1132 double dRA = std::fabs(GuideRateNP.at(AXIS_RA)->getValue() * rate * ms / 1000.0);
1133
1134 // not sure if this logic belongs here or the calling function
1135 // // compensate RA velocity for declination
1136 // TelescopeDirectionVector TDV;
1137 // TDV = TelescopeDirectionVectorFromEquatorialCoordinates(m_MountInternalCoordinates);
1138 // double skyRA = 0, skyDE = 0;
1139 // if (TransformTelescopeToCelestial(TDV, skyRA, skyDE))
1140 // {
1141 // dRA *= std::cos(M_PI / 180 * skyDE);
1142 // }
1143
1144 // final velocity guiding west is rate - dRA, i.e the guide star will appear to move west
1145 setVelocity(AXIS_RA, rate - dRA);
1146 INDI::Timer::singleShot(ms, [this]()
1147 {
1148 // reset the tracking mode
1150 GuideWENP[AXIS_RA].setValue(0);
1151 GuideWENP[AXIS_DE].setValue(0);
1153 GuideWENP.apply();
1154 });
1155 return IPS_BUSY;
1156}
1157
1161bool AstroTrac::SetTrackRate(double raRate, double deRate)
1162{
1163 return setVelocity(AXIS_RA, raRate) && setVelocity(AXIS_DE, deRate);
1164}
1165
1169bool AstroTrac::SetTrackMode(uint8_t mode)
1170{
1171 double dRA = 0, dDE = 0;
1172 if (mode == TRACK_SIDEREAL)
1173 dRA = TRACKRATE_SIDEREAL;
1174 else if (mode == TRACK_SOLAR)
1175 dRA = TRACKRATE_SOLAR;
1176 else if (mode == TRACK_LUNAR)
1177 dRA = TRACKRATE_LUNAR;
1178 else if (mode == TRACK_CUSTOM)
1179 {
1180 dRA = TrackRateNP[AXIS_RA].getValue();
1181 dDE = TrackRateNP[AXIS_DE].getValue();
1182 }
1183
1184 return setVelocity(AXIS_RA, dRA) && setVelocity(AXIS_DE, dDE);
1185}
1186
1191{
1192 // On engaging track, we simply set the current track mode and it will take care of the rest including custom track rates.
1193 if (enabled)
1195 // Disable tracking
1196 else
1197 {
1198 setVelocity(AXIS_RA, 0);
1199 setVelocity(AXIS_DE, 0);
1200 }
1201 return true;
1202}
1203
1208{
1210 MountTypeSP.save(fp);
1212 return true;
1213}
1214
1218void AstroTrac::simulateMount()
1219{
1220 // milliseconds elapsed
1221 uint32_t elapsed = m_SimulationTimer.elapsed();
1222
1223 // If too much time elapsed, restart the timer.
1224 if (elapsed > 5000)
1225 {
1226 m_SimulationTimer.restart();
1227 return;
1228 }
1229
1231 {
1232 double haVelocity = SLEW_SPEEDS[SlewRateSP.findOnSwitchIndex()] * TRACKRATE_SIDEREAL *
1233 (MovementWESP.findOnSwitchIndex() == DIRECTION_NORTH ? 1 : -1) * (m_Location.latitude >= 0 ? 1 : -1);
1234 double deVelocity = SLEW_SPEEDS[SlewRateSP.findOnSwitchIndex()] * TRACKRATE_SIDEREAL *
1235 (MovementNSSP.findOnSwitchIndex() == DIRECTION_NORTH ? 1 : -1) * (m_Location.latitude >= 0 ? 1 : -1);
1236
1237 haVelocity *= MovementWESP.getState() == IPS_BUSY ? 1 : 0;
1238 deVelocity *= MovementNSSP.getState() == IPS_BUSY ? 1 : 0;
1239
1240 // In degrees
1241 double elapsedDistanceHA = (elapsed / 1000.0 * haVelocity) / 3600.0;
1242 double elapsedDistanceDE = (elapsed / 1000.0 * deVelocity) / 3600.0;
1243
1244 // Hour Angle
1245 SimData.currentMechanicalHA += elapsedDistanceHA;
1246 if (SimData.currentMechanicalHA > 180)
1247 SimData.currentMechanicalHA -= 360;
1248 else if (SimData.currentMechanicalHA < -180)
1249 SimData.currentMechanicalHA += 360;
1250
1251 SimData.currentMechanicalDE += elapsedDistanceDE;
1252 if (SimData.currentMechanicalDE > 180)
1253 SimData.currentMechanicalDE -= 360;
1254 else if (SimData.currentMechanicalDE < -180)
1255 SimData.currentMechanicalDE += 360;
1256 }
1257 else
1258 {
1259 switch (TrackState)
1260 {
1261 case SCOPE_IDLE:
1262 case SCOPE_PARKED:
1263 break;
1264
1265 case SCOPE_SLEWING:
1266 case SCOPE_PARKING:
1267 {
1268 // In degrees
1269 double elapsedDistance = (elapsed / 1000.0 * MAX_SLEW_VELOCITY) / 3600.0;
1270
1271 // Hour Angle
1272 double dHA = SimData.targetMechanicalHA - SimData.currentMechanicalHA;
1273 if (std::abs(dHA) <= elapsedDistance)
1274 SimData.currentMechanicalHA = SimData.targetMechanicalHA;
1275 else if (dHA > 0)
1276 SimData.currentMechanicalHA += elapsedDistance;
1277 else
1278 SimData.currentMechanicalHA -= elapsedDistance;
1279
1280 // Declination
1281 double dDE = SimData.targetMechanicalDE - SimData.currentMechanicalDE;
1282 if (std::abs(dDE) <= elapsedDistance)
1283 SimData.currentMechanicalDE = SimData.targetMechanicalDE;
1284 else if (dDE > 0)
1285 SimData.currentMechanicalDE += elapsedDistance;
1286 else
1287 SimData.currentMechanicalDE -= elapsedDistance;
1288 }
1289 break;
1290
1291 case SCOPE_TRACKING:
1292 {
1293 // Increase HA axis at selected tracking rate (arcsec/s).
1294 SimData.currentMechanicalHA += (elapsed / 1000.0 * TrackRateNP[AXIS_RA].getValue()) / 3600.0;
1295 if (SimData.currentMechanicalHA > 180)
1296 SimData.currentMechanicalHA = 180;
1297 else if (SimData.currentMechanicalHA < -180)
1298 SimData.currentMechanicalHA = -180;
1299 }
1300 break;
1301 }
1302 }
1303
1304 m_SimulationTimer.restart();
1305}
1306
1311{
1312 if (enable)
1313 m_SimulationTimer.start();
1314}
1315
1319bool AstroTrac::handleSimulationCommand(const char *cmd, char *res, int cmd_len, int res_len)
1320{
1321 INDI_UNUSED(cmd_len);
1322
1323 // Get version
1324 if (strstr(cmd, "zv?"))
1325 {
1326 snprintf(res, res_len, "<%czvSIMU>", cmd[1]);
1327 }
1328 // Get Encoder Position
1329 else if (strstr(cmd, "p?"))
1330 {
1331 snprintf(res, res_len, "<%cp%.6f>", cmd[1], cmd[1] == '1' ? SimData.currentMechanicalHA : SimData.currentMechanicalDE);
1332 }
1333 // Set Encoder Position
1334 else if (strstr(cmd, "p"))
1335 {
1336 double value = 0;
1337 sscanf(cmd, "<%*cp%lf", &value);
1338 if (cmd[1] == '1')
1339 SimData.targetMechanicalHA = value;
1340 else
1341 SimData.targetMechanicalDE = value;
1342 snprintf(res, res_len, "<%cp#>", cmd[1]);
1343 }
1344 // Get Acceleration
1345 else if (strstr(cmd, "a?"))
1346 {
1347 snprintf(res, res_len, "<%ca%u>", cmd[1], SimData.acceleration[cmd[1] - '1']);
1348 }
1349 // Set Acceleration
1350 else if (strstr(cmd, "a"))
1351 {
1352 uint32_t value = 0;
1353 sscanf(cmd, "<%*ca%u", &value);
1354 SimData.acceleration[cmd[1] - '1'] = value;
1355 snprintf(res, res_len, "<%ca#>", cmd[1]);
1356 }
1357 // Get Velocity
1358 else if (strstr(cmd, "v?"))
1359 {
1360 snprintf(res, res_len, "<%cv%.6f>", cmd[1], SimData.velocity[cmd[1] - '1']);
1361 }
1362 // Set Velocity using encoders
1363 else if (strstr(cmd, "ve"))
1364 {
1365 double value = 0;
1366 sscanf(cmd, "<%*cve%lf", &value);
1367 SimData.velocity[cmd[1] - '1'] = value;
1368 snprintf(res, res_len, "<%cve#>", cmd[1]);
1369 }
1370 // Get Slew status
1371 else if (strstr(cmd, "t"))
1372 {
1373 char result = '0';
1374 if (cmd[1] == '1')
1375 result = std::abs(SimData.currentMechanicalHA - SimData.targetMechanicalHA) <= DIFF_THRESHOLD ? '0' : '1';
1376 else
1377 result = std::abs(SimData.currentMechanicalDE - SimData.targetMechanicalDE) <= DIFF_THRESHOLD ? '0' : '1';
1378 snprintf(res, res_len, "<%ct%c#>", cmd[1], result);
1379 }
1380 // Abort
1381 else if (strstr(cmd, "x"))
1382 {
1383 snprintf(res, res_len, "<%cx#>", cmd[1]);
1384 }
1385
1386 return true;
1387}
1388
1392bool AstroTrac::sendCommand(const char *cmd, char *res, int cmd_len, int res_len)
1393{
1394 int nbytes_written = 0, nbytes_read = 0, rc = -1;
1395
1396 if (isSimulation())
1397 return handleSimulationCommand(cmd, res, cmd_len, res_len);
1398
1399 tcflush(PortFD, TCIOFLUSH);
1400
1401 if (cmd_len > 0)
1402 {
1403 char hex_cmd[DRIVER_LEN * 3] = {0};
1404 hexDump(hex_cmd, cmd, cmd_len);
1405 LOGF_DEBUG("CMD <%s>", hex_cmd);
1406 rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
1407 }
1408 else
1409 {
1410 LOGF_DEBUG("CMD <%s>", cmd);
1411 rc = tty_write_string(PortFD, cmd, &nbytes_written);
1412 }
1413
1414 if (rc != TTY_OK)
1415 {
1416 char errstr[MAXRBUF] = {0};
1417 tty_error_msg(rc, errstr, MAXRBUF);
1418 LOGF_ERROR("Serial write error: %s.", errstr);
1419 return false;
1420 }
1421
1422 if (res == nullptr)
1423 {
1424 tcdrain(PortFD);
1425 return true;
1426 }
1427
1428 if (res_len > 0)
1429 rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
1430 else
1431 rc = tty_nread_section(PortFD, res, DRIVER_LEN, DRIVER_STOP_CHAR, DRIVER_TIMEOUT, &nbytes_read);
1432
1433 if (rc != TTY_OK)
1434 {
1435 char errstr[MAXRBUF] = {0};
1436 tty_error_msg(rc, errstr, MAXRBUF);
1437 LOGF_ERROR("Serial read error: %s.", errstr);
1438 return false;
1439 }
1440
1441 if (res_len > 0)
1442 {
1443 char hex_res[DRIVER_LEN * 3] = {0};
1444 hexDump(hex_res, res, res_len);
1445 LOGF_DEBUG("RES <%s>", hex_res);
1446 }
1447 else
1448 {
1449 LOGF_DEBUG("RES <%s>", res);
1450 }
1451
1452 tcflush(PortFD, TCIOFLUSH);
1453
1454 return true;
1455}
1456
1460void AstroTrac::hexDump(char *buf, const char *data, int size)
1461{
1462 for (int i = 0; i < size; i++)
1463 sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
1464
1465 if (size > 0)
1466 buf[3 * size - 1] = '\0';
1467}
1468
1472std::vector<std::string> AstroTrac::split(const std::string &input, const std::string &regex)
1473{
1474 // passing -1 as the submatch index parameter performs splitting
1475 std::regex re(regex);
1476 std::sregex_token_iterator
1477 first{input.begin(), input.end(), re, -1},
1478 last;
1479 return {first, last};
1480}
std::unique_ptr< AstroTrac > AstroTrac_mount(new AstroTrac())
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual const char * getDefaultName() override
Definition astrotrac.cpp:54
double velocity[2]
Definition astrotrac.h:182
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual void simulationTriggered(bool enable) override
Handle Simulation Trigger.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
double currentMechanicalDE
Definition astrotrac.h:180
virtual bool saveConfigItems(FILE *fp) override
Config Items.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool Park() override
Park the telescope to its home position.
uint32_t acceleration[2]
Definition astrotrac.h:184
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition astrotrac.cpp:62
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
void ProcessAlignmentNumberProperties(Telescope *pTelescope, const char *name, double values[], char *names[], int n)
Call this function from within the ISNewNumber processing path. The function will handle any alignmen...
void ProcessAlignmentSwitchProperties(Telescope *pTelescope, const char *name, ISState *states, char *names[], int n)
Call this function from within the ISNewSwitch processing path. The function will handle any alignmen...
void InitAlignmentProperties(Telescope *pTelescope)
Initialize alignment subsystem properties. It is recommended to call this function within initPropert...
void ProcessAlignmentBLOBProperties(Telescope *pTelescope, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Call this function from within the ISNewBlob processing path. The function will handle any alignment ...
void SaveAlignmentConfigProperties(FILE *fp)
Call this function to save persistent alignment related properties. This function should be called fr...
void ProcessAlignmentTextProperties(Telescope *pTelescope, const char *name, char *texts[], char *names[], int n)
Call this function from within the ISNewText processing path. The function will handle any alignment ...
bool CheckForDuplicateSyncPoint(const AlignmentDatabaseEntry &CandidateEntry, double Tolerance=0.1) const
Check if a entry already exists in the database.
AlignmentDatabaseType & GetAlignmentDatabase()
Get a reference to the in memory database.
void UpdateLocation(double latitude, double longitude, double elevation)
Call this function from within the updateLocation processing path.
void UpdateSize()
Call this function when the number of entries in the database changes.
bool TransformTelescopeToCelestialJD(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianDate)
TransformTelescopeToCelestialJD Transforms Mount Coords to Celestial (Sky) Coordinates.
bool TransformCelestialToTelescopeJD(double RightAscension, double Declination, double JulianDate, TelescopeDirectionVector &ApparentTelescopeDirectionVector)
TransformCelestialToTelescopeJD Transforms Celestial (Sky) Coords to Mount Coordinates.
bool Initialise(InMemoryDatabase *pInMemoryDatabase)
Initialise or re-initialise the math plugin. Re-reading the in memory database as necessary.
void SetApproximateMountAlignmentFromMountType(MountType_t Type)
Call this function to set the ApproximateMountAlignment property of the current Math Plugin....
void EquatorialCoordinatesFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IEquatorialCoordinates &EquatorialCoordinates)
Calculates equatorial coordinates from the supplied telescope direction vector and declination.
const TelescopeDirectionVector TelescopeDirectionVectorFromEquatorialCoordinates(INDI::IEquatorialCoordinates EquatorialCoordinates)
Calculates a telescope direction vector from the supplied equatorial coordinates.
bool isConnected() const
INDI::PropertySwitch getSwitch(const char *name) const
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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 addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
int64_t elapsed() const
Returns the number of milliseconds since this ElapsedTimer was last started.
int64_t restart()
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
void start()
Starts this timer. Once started, a timer value can be checked with elapsed().
INDI::PropertyNumber GuideNSNP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyNumber GuideWENP
void initProperties(const char *groupName)
Initialize guider properties. It is recommended to call this function within initProperties() of your...
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const
const WidgetView< T > * at(size_t index) 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)
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
double GetAxis1Park() const
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
void setTelescopeConnection(const uint8_t &value)
setTelescopeConnection Set telescope connection mode. Child class should call this in the constructor...
double GetAxis2Park() const
virtual int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Connection::TCP * tcpConnection
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
static void singleShot(int msec, const std::function< void()> &callback)
This static function calls a the given function after a given time interval.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
INDI::PropertyNumber TrackRateNP
INDI::PropertySwitch MovementNSSP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertySwitch MovementWESP
INDI::PropertyNumber LocationNP
double ra
double dec
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
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
INDI_EQ_AXIS
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_NORTH
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition indicom.c:1301
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition indicom.c:1311
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 fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition indicom.c:130
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
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
#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 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 TRACKRATE_SOLAR
Definition indimacros.h:45
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define TRACKRATE_LUNAR
Definition indimacros.h:48
#define getDeviceName
Namespace to encapsulate the INDI Alignment Subsystem classes. For more information see "INDI Alignme...
uint8_t cmd[4]
Definition pwc-ioctl.h:2
Entry in the in memory alignment database.
Definition Common.h:152
double RightAscension
Right ascension in decimal hours. N.B. libnova works in decimal degrees so conversion is always neede...
Definition Common.h:190
TelescopeDirectionVector TelescopeDirection
Normalised vector giving telescope pointing direction. This is referred to elsewhere as the "apparent...
Definition Common.h:197
double Declination
Declination in decimal degrees.
Definition Common.h:193
int PrivateDataSize
This size in bytes of any private data.
Definition Common.h:203
Holds a normalised direction vector (direction cosines)
Definition Common.h:69