Instrument Neutral Distributed Interface INDI  2.0.2
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 
38 std::unique_ptr<AstroTrac> AstroTrac_mount(new AstroTrac());
39 const std::array<uint32_t, AstroTrac::SLEW_MODES> AstroTrac::SLEW_SPEEDS = {{1, 2, 4, 8, 32, 64, 128, 600, 700, 800}};
40 
41 using namespace INDI::AlignmentSubsystem;
42 
44 {
45  setVersion(1, 0);
46 
47  SetTelescopeCapability(TELESCOPE_CAN_PARK | TELESCOPE_CAN_SYNC | TELESCOPE_CAN_GOTO | TELESCOPE_CAN_ABORT |
48  TELESCOPE_HAS_TIME | TELESCOPE_HAS_LOCATION | TELESCOPE_HAS_TRACK_MODE | TELESCOPE_HAS_TRACK_RATE |
49  TELESCOPE_CAN_CONTROL_TRACK | TELESCOPE_HAS_TRACK_RATE | TELESCOPE_HAS_PIER_SIDE,
50  SLEW_MODES);
51  setTelescopeConnection(CONNECTION_TCP);
52 }
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  sprintf(SlewRateSP.sp[i].label, "%dx", SLEW_SPEEDS[i]);
76  SlewRateSP.sp[i].aux = (void *)&SLEW_SPEEDS[i];
77  }
78  SlewRateS[5].s = ISS_ON;
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  TrackState = SCOPE_IDLE;
100 
101  SetParkDataType(PARK_RA_DEC_ENCODER);
102 
103  initGuiderProperties(getDeviceName(), MOTION_TAB);
104 
105  tcpConnection->setDefaultHost("192.168.1.1");
106  tcpConnection->setDefaultPort(23);
107 
108  setDriverInterface(getDriverInterface() | GUIDER_INTERFACE);
109 
110  addAuxControls();
111 
112  InitAlignmentProperties(this);
113  // set mount type to alignment subsystem
114  SetApproximateMountAlignmentFromMountType(EQUATORIAL);
115  // Init Math plugin
116  Initialise(this);
117 
118  // Force the alignment system to always be on
119  getSwitch("ALIGNMENT_SUBSYSTEM_ACTIVE")[0].setState(ISS_ON);
120 
121  return true;
122 }
123 
127 void AstroTrac::ISGetProperties(const char *dev)
128 {
130 
131  defineProperty(MountTypeSP);
132 }
133 
138 {
140 
141  if (isConnected())
142  {
143  getAcceleration(AXIS_RA);
144  getAcceleration(AXIS_DE);
145  getVelocity(AXIS_RA);
146  getVelocity(AXIS_DE);
147 
148  defineProperty(FirmwareTP);
149  defineProperty(AccelerationNP);
150  defineProperty(EncoderNP);
151  defineProperty(&GuideNSNP);
152  defineProperty(&GuideWENP);
153  defineProperty(GuideRateNP);
154 
155  // Initial AZ/AL parking position.
156  if (InitPark())
157  {
158  // If loading parking data is successful, we just set the default parking values.
159  SetAxis1ParkDefault(0);
160  SetAxis2ParkDefault(0);
161  }
162  else
163  {
164  // Otherwise, we set all parking data to default in case no parking data is found.
165  SetAxis1Park(0);
166  SetAxis2Park(0);
167  SetAxis1ParkDefault(0);
168  SetAxis2ParkDefault(0);
169  }
170  }
171  else
172  {
173  deleteProperty(FirmwareTP);
174  deleteProperty(AccelerationNP);
175  deleteProperty(EncoderNP);
176  deleteProperty(GuideNSNP.name);
177  deleteProperty(GuideWENP.name);
178  deleteProperty(GuideRateNP);
179  }
180 
181  return true;
182 }
183 
188 {
189  return getVersion();
190 }
191 
195 bool AstroTrac::getVersion()
196 {
197  char response[DRIVER_LEN] = {0};
198  if (sendCommand("<1zv?>", response))
199  {
200  FirmwareTP[0].setText(response + 4, 4);
201  return true;
202  }
203 
204  return false;
205 }
206 
210 bool AstroTrac::getAcceleration(INDI_EQ_AXIS axis)
211 {
212  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
213  snprintf(command, DRIVER_LEN, "<%da?>", axis + 1);
214  if (sendCommand(command, response))
215  {
216  try
217  {
218  std::string acceleration = std::regex_replace(
219  response,
220  std::regex("<.a(\\d+)>"),
221  std::string("$1"));
222 
223  AccelerationNP[axis].setValue(std::stoi(acceleration));
224  return true;
225  }
226  catch(...)
227  {
228  LOGF_DEBUG("Failed to parse acceleration (%s)", response);
229  }
230  }
231 
232  return false;
233 }
234 
238 bool AstroTrac::setAcceleration(INDI_EQ_AXIS axis, uint32_t a)
239 {
240  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
241 
242  snprintf(command, DRIVER_LEN, "<%da%u>", axis + 1, a);
243  if (sendCommand(command, response))
244  return response[3] == '#';
245 
246  return false;
247 
248 }
249 
253 bool AstroTrac::getVelocity(INDI_EQ_AXIS axis)
254 {
255  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
256  snprintf(command, DRIVER_LEN, "<%dv?>", axis + 1);
257  if (sendCommand(command, response))
258  {
259  try
260  {
261  std::string velocity = std::regex_replace(
262  response,
263  std::regex("<.v([+-]?[0-9]+\\.[0-9]+?)>"),
264  std::string("$1"));
265 
266  TrackRateN[axis].value = std::stod(velocity) * (m_Location.latitude >= 0 ? 1 : -1);
267  return true;
268  }
269  catch(...)
270  {
271  LOGF_DEBUG("Failed to parse velocity (%s)", response);
272  }
273  }
274 
275  return false;
276 }
277 
281 bool AstroTrac::setVelocity(INDI_EQ_AXIS axis, double value)
282 {
283  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
284 
285  // Reverse value depending on hemisphere
286  snprintf(command, DRIVER_LEN, "<%dve%f>", axis + 1, value * (m_Location.latitude >= 0 ? 1 : -1));
287  if (sendCommand(command, response))
288  return response[4] == '#';
289 
290  return false;
291 
292 }
293 
297 bool AstroTrac::stopMotion(INDI_EQ_AXIS axis)
298 {
299  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
300 
301  snprintf(command, DRIVER_LEN, "<%dx>", axis + 1);
302  if (sendCommand(command, response))
303  return response[3] == '#';
304 
305  return false;
306 
307 }
308 
312 bool AstroTrac::isSlewComplete()
313 {
314  char HAResponse[DRIVER_LEN] = {0}, DEResponse[DRIVER_LEN] = {0};
315  if (sendCommand("<1t?>", HAResponse) && sendCommand("<2t?>", DEResponse))
316  {
317  return (HAResponse[3] == '0' && DEResponse[3] == '0');
318  }
319 
320  return false;
321 }
322 
326 bool AstroTrac::syncEncoder(INDI_EQ_AXIS axis, double value)
327 {
328  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
329 
330  snprintf(command, DRIVER_LEN, "<%dy%f>", axis + 1, value);
331  if (sendCommand(command, response))
332  return response[3] == '#';
333 
334  return false;
335 }
336 
340 bool AstroTrac::slewEncoder(INDI_EQ_AXIS axis, double value)
341 {
342  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
343 
344  snprintf(command, DRIVER_LEN, "<%dp%f>", axis + 1, value);
345  if (sendCommand(command, response))
346  return response[3] == '#';
347 
348  return false;
349 }
350 
357 bool AstroTrac::getEncoderPosition(INDI_EQ_AXIS axis)
358 {
359  char command[DRIVER_LEN] = {0}, response[DRIVER_LEN] = {0};
360  snprintf(command, DRIVER_LEN, "<%dp?>", axis + 1);
361  if (sendCommand(command, response))
362  {
363  try
364  {
365  char regex_str[64] = {0};
366  snprintf(regex_str, 64, "<%dp([+-]?[0-9]+\\.[0-9]+?)>", axis + 1);
367  std::string position = std::regex_replace(
368  response,
369  std::regex(regex_str),
370  std::string("$1"));
371 
372  EncoderNP[axis].setValue(std::stod(position));
373  return true;
374  }
375  catch(...)
376  {
377  try
378  {
379  // Check if the response for the other axis
380  INDI_EQ_AXIS other = (axis == AXIS_RA) ? AXIS_DE : AXIS_RA;
381  char regex_str[64] = {0};
382  snprintf(regex_str, 64, "<%dp([+-]?[0-9]+\\.[0-9]+?)>", other + 1);
383  std::string position = std::regex_replace(
384  response,
385  std::regex(regex_str),
386  std::string("$1"));
387 
388  EncoderNP[other].setValue(std::stod(position));
389  return true;
390  }
391  catch(...)
392  {
393  LOGF_DEBUG("Failed to parse position (%s)", response);
394  }
395  }
396  }
397 
398  return false;
399 }
400 
408 void AstroTrac::getRADEFromEncoders(double haEncoder, double deEncoder, double &ra, double &de)
409 {
410  static const double jitter = 0.0005;
411  double ha = 0;
412 
413  // Take care of jitter
414  if (haEncoder > jitter * -1 && haEncoder < jitter)
415  haEncoder = 0;
416  if (deEncoder > jitter * -1 && deEncoder < jitter)
417  deEncoder = 0;
418 
419  // Northern Hemisphere
420  if (LocationN[LOCATION_LATITUDE].value >= 0)
421  {
422  // "Normal" Pointing State (East, looking West)
423  if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || deEncoder >= 0)
424  {
425  de = std::min(90 - deEncoder, 90.0);
426  ha = -6.0 + (haEncoder / 360.0) * 24.0 ;
427  }
428  // "Reversed" Pointing State (West, looking East)
429  else
430  {
431  de = 90 + deEncoder;
432  ha = 6.0 + (haEncoder / 360.0) * 24.0 ;
433  }
434  }
435  else
436  {
437  // East
438  if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || deEncoder <= 0)
439  {
440  de = std::max(-90 - deEncoder, -90.0);
441  ha = -6.0 - (haEncoder / 360.0) * 24.0 ;
442  }
443  // West
444  else
445  {
446  de = -90 + deEncoder;
447  ha = 6.0 - (haEncoder / 360.0) * 24.0 ;
448  }
449  }
450 
451  double lst = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
452  ra = range24(lst - ha);
453 
454  char RAStr[32] = {0}, DecStr[32] = {0};
455  fs_sexa(RAStr, ra, 2, 3600);
456  fs_sexa(DecStr, de, 2, 3600);
457  LOGF_DEBUG("Encoders HA: %.4f DE: %.4f Processed: HA: %.4f DE: %.4f (%s) LST: %.4f RA: %.4f (%s)",
458  haEncoder, deEncoder, ha, de, DecStr, lst, ra, RAStr);
459 }
460 
467 void AstroTrac::getEncodersFromRADE(double ra, double de, double &haEncoder, double &deEncoder)
468 {
469  double lst = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
470  double dHA = rangeHA(lst - ra);
471  // Northern Hemisphere
472  if (LocationN[LOCATION_LATITUDE].value >= 0)
473  {
474  // "Normal" Pointing State (East, looking West)
475  if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || dHA <= 0)
476  {
477  deEncoder = -(de - 90.0);
478  haEncoder = (dHA + 6.0) * 360.0 / 24.0;
479  }
480  // "Reversed" Pointing State (West, looking East)
481  else
482  {
483  deEncoder = de - 90.0;
484  haEncoder = (dHA - 6.0) * 360.0 / 24.0;
485  }
486  }
487  else
488  {
489  // "Normal" Pointing State (East, looking West)
490  if (MountTypeSP.findOnSwitchIndex() == MOUNT_SINGLE_ARM || dHA <= 0)
491  {
492  deEncoder = -(de + 90.0);
493  haEncoder = -(dHA + 6.0) * 360.0 / 24.0;
494  }
495  // "Reversed" Pointing State (West, looking East)
496  else
497  {
498  deEncoder = (de + 90.0);
499  haEncoder = -(dHA - 6.0) * 360 / 24.0;
500  }
501  }
502 }
503 
507 bool AstroTrac::Sync(double ra, double dec)
508 {
509  AlignmentDatabaseEntry NewEntry;
510  NewEntry.ObservationJulianDate = ln_get_julian_from_sys();
511  // Actual Celestial Coordinates
512  NewEntry.RightAscension = ra;
513  NewEntry.Declination = dec;
514  // Apparent Telescope Coordinates
515  NewEntry.TelescopeDirection = TelescopeDirectionVectorFromEquatorialCoordinates(m_MountInternalCoordinates);
516  NewEntry.PrivateDataSize = 0;
517 
518  if (!CheckForDuplicateSyncPoint(NewEntry, 0.001))
519  {
520  GetAlignmentDatabase().push_back(NewEntry);
521 
522  // Tell the client about size change
523  UpdateSize();
524 
525  // Tell the math plugin to reinitiali
526  Initialise(this);
527 
528  char RAStr[32], DecStr[32];
529  fs_sexa(RAStr, ra, 2, 3600);
530  fs_sexa(DecStr, dec, 2, 3600);
531  LOGF_INFO("Syncing to JNOW RA %s - DEC %s", RAStr, DecStr);
532 
533  return true;
534  }
535 
536  LOGF_DEBUG("Sync - duplicate entry RA: %lf(%lf) DEC: %lf", ra * 360.0 / 24.0, ra, dec);
537  return false;
538 }
539 
543 bool AstroTrac::Goto(double ra, double dec)
544 {
545  double haEncoder = 0, deEncoder = 0;
546  INDI::IEquatorialCoordinates telescopeCoordinates;
547  if (getTelescopeFromSkyCoordinates(ra, dec, telescopeCoordinates))
548  {
549  char mountRAString[32] = {0}, mountDEString[32] = {0}, skyRAString[32] = {0}, skyDEString[32] = {0};
550  fs_sexa(mountRAString, telescopeCoordinates.rightascension, 2, 3600);
551  fs_sexa(mountDEString, telescopeCoordinates.declination, 2, 3600);
552  fs_sexa(skyRAString, ra, 2, 3600);
553  fs_sexa(skyDEString, dec, 2, 3600);
554 
555  LOGF_DEBUG("GOTO Sky RA: %s DE: %s ---> Mount RA: %s DE: %s", skyRAString, skyDEString, mountRAString, mountDEString);
556 
557  getEncodersFromRADE(telescopeCoordinates.rightascension, telescopeCoordinates.declination, haEncoder, deEncoder);
558 
559  // Account for acceletaion, max speed, and deceleration by the time we get there.
560  // Get time in seconds
561  double tHA = calculateSlewTime(haEncoder - EncoderNP[AXIS_RA].getValue());
562  // Adjust for hemisphere. Convert time to delta degrees
563  tHA = tHA * (m_Location.latitude >= 0 ? 1 : -1) * TRACKRATE_SIDEREAL / 3600;
564 
565  LOGF_DEBUG("GOTO Encoders HA: %.4f (%.4f + %.4f) DE: %.4f", haEncoder + tHA, haEncoder, tHA, deEncoder);
566 
567  // Now go to each encoder
568  bool rc1 = slewEncoder(AXIS_RA, haEncoder + tHA);
569  bool rc2 = slewEncoder(AXIS_DE, deEncoder);
570 
571  if (rc1 && rc2)
572  {
573  TrackState = SCOPE_SLEWING;
574 
575  char RAStr[32], DecStr[32];
576  fs_sexa(RAStr, ra, 2, 3600);
577  fs_sexa(DecStr, dec, 2, 3600);
578  LOGF_INFO("Slewing to JNOW RA %s - DEC %s", RAStr, DecStr);
579  return true;
580  }
581  }
582 
583  return false;
584 }
585 
591 double AstroTrac::calculateSlewTime(double distance)
592 {
593  // Firstly throw away sign of distance - don't care about direction - and convert to arcsec
594  distance = fabs(distance) * 3600.0;
595 
596  // Now estimate how far mount travels during accelertion and deceleration period
597  double accelerate_decelerate = MAX_SLEW_VELOCITY * MAX_SLEW_VELOCITY / AccelerationNP[AXIS_RA].getValue();
598 
599  // If distance less than this, then calulate using accleration forumlae:
600  if (distance < accelerate_decelerate)
601  {
602  return (2 * sqrt(distance / AccelerationNP[AXIS_RA].getValue()));
603  }
604  else
605  {
606  // Time is equal to twice the time required to accelerate or decelerate, plus the remaining distance at max slew speed
607  return (2.0 * MAX_SLEW_VELOCITY / AccelerationNP[AXIS_RA].getValue() + (distance - accelerate_decelerate) /
608  MAX_SLEW_VELOCITY);
609  }
610 }
611 
616 {
618  double ra = 0, de = 0, skyRA = 0, skyDE = 0;
619 
620  if (isSimulation())
621  simulateMount();
622 
623  double lastHAEncoder = EncoderNP[AXIS_RA].getValue();
624  double lastDEEncoder = EncoderNP[AXIS_DE].getValue();
625  if (getEncoderPosition(AXIS_RA) && getEncoderPosition(AXIS_DE))
626  {
627  getRADEFromEncoders(EncoderNP[AXIS_RA].getValue(), EncoderNP[AXIS_DE].getValue(), ra, de);
628  // Send to client if changed.
629  if (std::fabs(lastHAEncoder - EncoderNP[AXIS_RA].getValue()) > 0
630  || std::fabs(lastDEEncoder - EncoderNP[AXIS_DE].getValue()) > 0)
631  {
632  EncoderNP.apply();
633  }
634  }
635  else
636  return false;
637 
638  if (TrackState == SCOPE_SLEWING || TrackState == SCOPE_PARKING)
639  {
640  if (isSlewComplete())
641  {
642  if (TrackState == SCOPE_SLEWING)
643  {
644  LOG_INFO("Slew complete, tracking...");
645  TrackState = SCOPE_TRACKING;
646  SetTrackEnabled(true);
647  }
648  // Parking
649  else
650  {
651  SetTrackEnabled(false);
652  SetParked(true);
653  }
654  }
655  }
656 
657  m_MountInternalCoordinates.rightascension = ra;
658  m_MountInternalCoordinates.declination = de;
659  TDV = TelescopeDirectionVectorFromEquatorialCoordinates(m_MountInternalCoordinates);
660 
661  if (TransformTelescopeToCelestial(TDV, skyRA, skyDE))
662  {
663  double lst = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
664  double dHA = rangeHA(lst - skyRA);
665  setPierSide(dHA < 0 ? PIER_EAST : PIER_WEST);
666 
667  char mountRAString[32] = {0}, mountDEString[32] = {0}, skyRAString[32] = {0}, skyDEString[32] = {0};
668  fs_sexa(mountRAString, ra, 2, 3600);
669  fs_sexa(mountDEString, de, 2, 3600);
670  fs_sexa(skyRAString, skyRA, 2, 3600);
671  fs_sexa(skyDEString, skyDE, 2, 3600);
672 
673  LOGF_DEBUG("Mount RA: %s DE: %s ---> Sky RA: %s DE: %s", mountRAString, mountDEString, skyRAString, skyDEString);
674 
675  NewRaDec(skyRA, skyDE);
676  return true;
677  }
678 
679  LOG_ERROR("TransformTelescopeToCelestial failed in ReadScopeStatus");
680  return false;
681 }
682 
686 bool AstroTrac::getTelescopeFromSkyCoordinates(double ra, double de, INDI::IEquatorialCoordinates &telescopeCoordinates)
687 {
689  if (TransformCelestialToTelescope(ra, de, 0.0, TDV))
690  {
691  EquatorialCoordinatesFromTelescopeDirectionVector(TDV, telescopeCoordinates);
692  LOGF_DEBUG("TransformCelestialToTelescope: RA=%lf DE=%lf, TDV (x :%lf, y: %lf, z: %lf), local hour RA %lf DEC %lf",
693  ra, de, TDV.x, TDV.y, TDV.z, telescopeCoordinates.rightascension, telescopeCoordinates.declination);
694  return true;
695  }
696 
697  return false;
698 }
699 
704 {
705  if (slewEncoder(AXIS_RA, GetAxis1Park()) && slewEncoder(AXIS_DE, GetAxis2Park()))
706  {
707  TrackState = SCOPE_PARKING;
708  LOG_INFO("Parking is in progress...");
709  return true;
710  }
711 
712  return false;
713 }
714 
719 {
720  SetParked(false);
721  return true;
722 }
723 
727 bool AstroTrac::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
728 {
729  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
730  {
731  // Process alignment properties
732  ProcessAlignmentTextProperties(this, name, texts, names, n);
733  }
734 
735  return INDI::Telescope::ISNewText(dev, name, texts, names, n);
736 }
737 
741 bool AstroTrac::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
742 {
743  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
744  {
745  // Guide Rate
746  if (GuideRateNP.isNameMatch(name))
747  {
748  GuideRateNP.update(values, names, n);
749  GuideRateNP.setState(IPS_OK);
750  GuideRateNP.apply();
751  return true;
752  }
753 
754  // Acceleration
755  if (AccelerationNP.isNameMatch(name))
756  {
757  AccelerationNP.update(values, names, n);
758 
759  if (setAcceleration(AXIS_RA, AccelerationNP[AXIS_RA].getValue())
760  && setAcceleration(AXIS_DE, AccelerationNP[AXIS_DE].getValue()))
761  AccelerationNP.setState(IPS_OK);
762  else
763  AccelerationNP.setState(IPS_ALERT);
764 
765  AccelerationNP.apply();
766  return true;
767  }
768 
769  // Encoders
770  if (EncoderNP.isNameMatch(name))
771  {
772  if (slewEncoder(AXIS_RA, values[0]) && slewEncoder(AXIS_DE, values[1]))
773  {
774  TrackState = SCOPE_SLEWING;
775  EncoderNP.setState(IPS_OK);
776  }
777  else
778  EncoderNP.setState(IPS_ALERT);
779 
780  EncoderNP.apply();
781  return true;
782  }
783 
784  processGuiderProperties(name, values, names, n);
785 
786  // Process alignment properties
787  ProcessAlignmentNumberProperties(this, name, values, names, n);
788 
789  }
790 
791  return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
792 }
793 
797 bool AstroTrac::ISNewSwitch(const char *dev, const char *name, ISState * states, char *names[], int n)
798 {
799  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
800  {
801  // Mount Type
802  if (MountTypeSP.isNameMatch(name))
803  {
804  MountTypeSP.update(states, names, n);
805  MountTypeSP.setState(IPS_OK);
806  MountTypeSP.apply();
807  return true;
808  }
809 
810  // Process alignment properties
811  ProcessAlignmentSwitchProperties(this, name, states, names, n);
812  }
813 
814  return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
815 }
816 
817 
821 bool AstroTrac::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
822  char *formats[], char *names[], int n)
823 {
824  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
825  {
826  // Process alignment properties
827  ProcessAlignmentBLOBProperties(this, name, sizes, blobsizes, blobs, formats, names, n);
828  }
829  // Pass it up the chain
830  return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
831 }
832 
837 {
838  bool rc1 = setVelocity(AXIS_RA, 0) && stopMotion(AXIS_RA);
839  bool rc2 = setVelocity(AXIS_DE, 0) && stopMotion(AXIS_DE);
840 
841  return rc1 && rc2;
842 }
843 
848 {
849  if (TrackState == SCOPE_PARKED)
850  {
851  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
852  return false;
853  }
854 
855  if (command == MOTION_START)
856  {
857  double velocity = SLEW_SPEEDS[IUFindOnSwitchIndex(&SlewRateSP)] * TRACKRATE_SIDEREAL
858  * (dir == DIRECTION_NORTH ? 1 : -1);
859  setVelocity(AXIS_DE, velocity);
860  }
861  else
862  {
863  setVelocity(AXIS_DE, TrackRateN[AXIS_DE].value);
864  stopMotion(AXIS_DE);
865  }
866 
867  return true;
868 }
869 
874 {
875  if (TrackState == SCOPE_PARKED)
876  {
877  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
878  return false;
879  }
880 
881  if (command == MOTION_START)
882  {
883  double velocity = SLEW_SPEEDS[IUFindOnSwitchIndex(&SlewRateSP)] * TRACKRATE_SIDEREAL
884  * (dir == DIRECTION_WEST ? 1 : -1);
885  setVelocity(AXIS_RA, velocity);
886  }
887  else
888  {
889  setVelocity(AXIS_RA, TrackRateN[AXIS_RA].value);
890  stopMotion(AXIS_RA);
891  }
892 
893  return true;
894 }
895 
899 bool AstroTrac::updateLocation(double latitude, double longitude, double elevation)
900 {
902  // Set this according to mount type
903  SetApproximateMountAlignmentFromMountType(EQUATORIAL);
904  return true;
905 }
906 
910 bool AstroTrac::updateTime(ln_date * utc, double utc_offset)
911 {
912  INDI_UNUSED(utc);
913  INDI_UNUSED(utc_offset);
914  return true;
915 }
916 
921 {
922  SetAxis1Park(EncoderNP[AXIS_RA].getValue());
923  SetAxis2Park(EncoderNP[AXIS_DE].getValue());
924 
925  return true;
926 }
927 
932 {
933  SetAxis1Park(0);
934  SetAxis2Park(0);
935 
936  return true;
937 }
938 
943 {
944  // If track rate is zero, assume sidereal for DEC
945  double rate = TrackRateN[AXIS_DE].value > 0 ? TrackRateN[AXIS_DE].value : TRACKRATE_SIDEREAL;
946  // Find delta declination
947  double dDE = GuideRateNP.at(AXIS_DE)->getValue() * rate * ms / 1000.0;
948  // Final velocity guiding north is rate + dDE
949  setVelocity(AXIS_DE, rate + dDE);
950  INDI::Timer::singleShot(ms, [this]()
951  {
952  setVelocity(AXIS_DE, TrackRateN[AXIS_DE].value);
953  GuideNSN[AXIS_RA].value = GuideNSN[AXIS_DE].value = 0;
954  GuideNSNP.s = IPS_OK;
955  IDSetNumber(&GuideNSNP, nullptr);
956  });
957  return IPS_BUSY;
958 }
959 
964 {
965  // If track rate is zero, assume sidereal for DEC
966  double rate = TrackRateN[AXIS_DE].value > 0 ? TrackRateN[AXIS_DE].value : TRACKRATE_SIDEREAL;
967  // Find delta declination
968  double dDE = GuideRateNP.at(AXIS_DE)->getValue() * rate * ms / 1000.0;
969  // Final velocity guiding south is rate - dDE
970  setVelocity(AXIS_DE, rate - dDE);
971  INDI::Timer::singleShot(ms, [this]()
972  {
973  setVelocity(AXIS_DE, TrackRateN[AXIS_DE].value);
974  GuideNSN[AXIS_RA].value = GuideNSN[AXIS_DE].value = 0;
975  GuideNSNP.s = IPS_OK;
976  IDSetNumber(&GuideNSNP, nullptr);
977  });
978  return IPS_BUSY;
979 }
980 
985 {
986  // Movement in arcseconds
987  double dRA = GuideRateNP.at(AXIS_RA)->getValue() * TrackRateN[AXIS_RA].value * ms / 1000.0;
988  // Final velocity guiding east is Sidereal + dRA
989  setVelocity(AXIS_RA, TrackRateN[AXIS_RA].value + dRA);
990  INDI::Timer::singleShot(ms, [this]()
991  {
992  setVelocity(AXIS_RA, TrackRateN[AXIS_RA].value);
993  GuideWEN[AXIS_RA].value = GuideWEN[AXIS_DE].value = 0;
994  GuideWENP.s = IPS_OK;
995  IDSetNumber(&GuideWENP, nullptr);
996  });
997  return IPS_BUSY;
998 }
999 
1004 {
1005  // Movement in arcseconds
1006  double dRA = GuideRateNP.at(AXIS_RA)->getValue() * TrackRateN[AXIS_RA].value * ms / 1000.0;
1007  // Final velocity guiding east is Sidereal + dRA
1008  setVelocity(AXIS_RA, TrackRateN[AXIS_RA].value - dRA);
1009  INDI::Timer::singleShot(ms, [this]()
1010  {
1011  setVelocity(AXIS_RA, TrackRateN[AXIS_RA].value);
1012  GuideWEN[AXIS_RA].value = GuideWEN[AXIS_DE].value = 0;
1013  GuideWENP.s = IPS_OK;
1014  IDSetNumber(&GuideWENP, nullptr);
1015  });
1016  return IPS_BUSY;
1017 }
1018 
1022 bool AstroTrac::SetTrackRate(double raRate, double deRate)
1023 {
1024  return setVelocity(AXIS_RA, raRate) && setVelocity(AXIS_DE, deRate);
1025 }
1026 
1030 bool AstroTrac::SetTrackMode(uint8_t mode)
1031 {
1032  double dRA = 0, dDE = 0;
1033  if (mode == TRACK_SIDEREAL)
1034  dRA = TRACKRATE_SIDEREAL;
1035  else if (mode == TRACK_SOLAR)
1036  dRA = TRACKRATE_SOLAR;
1037  else if (mode == TRACK_LUNAR)
1038  dRA = TRACKRATE_LUNAR;
1039  else if (mode == TRACK_CUSTOM)
1040  {
1041  dRA = TrackRateN[AXIS_RA].value;
1042  dDE = TrackRateN[AXIS_DE].value;
1043  }
1044 
1045  return setVelocity(AXIS_RA, dRA) && setVelocity(AXIS_DE, dDE);
1046 }
1047 
1051 bool AstroTrac::SetTrackEnabled(bool enabled)
1052 {
1053  // On engaging track, we simply set the current track mode and it will take care of the rest including custom track rates.
1054  if (enabled)
1055  return SetTrackMode(IUFindOnSwitchIndex(&TrackModeSP));
1056  // Disable tracking
1057  else
1058  {
1059  setVelocity(AXIS_RA, 0);
1060  setVelocity(AXIS_DE, 0);
1061  }
1062  return true;
1063 }
1064 
1069 {
1071  MountTypeSP.save(fp);
1072  SaveAlignmentConfigProperties(fp);
1073  return true;
1074 }
1075 
1079 void AstroTrac::simulateMount()
1080 {
1081  // milliseconds elapsed
1082  uint32_t elapsed = m_SimulationTimer.elapsed();
1083 
1084  // If too much time elapsed, restart the timer.
1085  if (elapsed > 5000)
1086  {
1087  m_SimulationTimer.restart();
1088  return;
1089  }
1090 
1091  if (MovementWESP.s == IPS_BUSY || MovementNSSP.s == IPS_BUSY)
1092  {
1093  double haVelocity = SLEW_SPEEDS[IUFindOnSwitchIndex(&SlewRateSP)] * TRACKRATE_SIDEREAL
1094  * (IUFindOnSwitchIndex(&MovementWESP) == DIRECTION_NORTH ? 1 : -1) * (m_Location.latitude >= 0 ? 1 : -1);
1095  double deVelocity = SLEW_SPEEDS[IUFindOnSwitchIndex(&SlewRateSP)] * TRACKRATE_SIDEREAL
1096  * (IUFindOnSwitchIndex(&MovementNSSP) == DIRECTION_NORTH ? 1 : -1) * (m_Location.latitude >= 0 ? 1 : -1);
1097 
1098  haVelocity *= MovementWESP.s == IPS_BUSY ? 1 : 0;
1099  deVelocity *= MovementNSSP.s == IPS_BUSY ? 1 : 0;
1100 
1101  // In degrees
1102  double elapsedDistanceHA = (elapsed / 1000.0 * haVelocity) / 3600.0;
1103  double elapsedDistanceDE = (elapsed / 1000.0 * deVelocity) / 3600.0;
1104 
1105  // Hour Angle
1106  SimData.currentMechanicalHA += elapsedDistanceHA;
1107  if (SimData.currentMechanicalHA > 180)
1108  SimData.currentMechanicalHA -= 360;
1109  else if (SimData.currentMechanicalHA < -180)
1110  SimData.currentMechanicalHA += 360;
1111 
1112  SimData.currentMechanicalDE += elapsedDistanceDE;
1113  if (SimData.currentMechanicalDE > 180)
1114  SimData.currentMechanicalDE -= 360;
1115  else if (SimData.currentMechanicalDE < -180)
1116  SimData.currentMechanicalDE += 360;
1117  }
1118  else
1119  {
1120  switch (TrackState)
1121  {
1122  case SCOPE_IDLE:
1123  case SCOPE_PARKED:
1124  break;
1125 
1126  case SCOPE_SLEWING:
1127  case SCOPE_PARKING:
1128  {
1129  // In degrees
1130  double elapsedDistance = (elapsed / 1000.0 * MAX_SLEW_VELOCITY) / 3600.0;
1131 
1132  // Hour Angle
1133  double dHA = SimData.targetMechanicalHA - SimData.currentMechanicalHA;
1134  if (std::abs(dHA) <= elapsedDistance)
1135  SimData.currentMechanicalHA = SimData.targetMechanicalHA;
1136  else if (dHA > 0)
1137  SimData.currentMechanicalHA += elapsedDistance;
1138  else
1139  SimData.currentMechanicalHA -= elapsedDistance;
1140 
1141  // Declination
1142  double dDE = SimData.targetMechanicalDE - SimData.currentMechanicalDE;
1143  if (std::abs(dDE) <= elapsedDistance)
1144  SimData.currentMechanicalDE = SimData.targetMechanicalDE;
1145  else if (dDE > 0)
1146  SimData.currentMechanicalDE += elapsedDistance;
1147  else
1148  SimData.currentMechanicalDE -= elapsedDistance;
1149  }
1150  break;
1151 
1152  case SCOPE_TRACKING:
1153  {
1154  // Increase HA axis at selected tracking rate (arcsec/s).
1155  SimData.currentMechanicalHA += (elapsed / 1000.0 * TrackRateN[AXIS_RA].value) / 3600.0;
1156  if (SimData.currentMechanicalHA > 180)
1157  SimData.currentMechanicalHA = 180;
1158  else if (SimData.currentMechanicalHA < -180)
1159  SimData.currentMechanicalHA = -180;
1160  }
1161  break;
1162  }
1163  }
1164 
1165  m_SimulationTimer.restart();
1166 }
1167 
1172 {
1173  if (enable)
1174  m_SimulationTimer.start();
1175 }
1176 
1180 bool AstroTrac::handleSimulationCommand(const char * cmd, char * res, int cmd_len, int res_len)
1181 {
1182  INDI_UNUSED(cmd_len);
1183 
1184  // Get version
1185  if (strstr(cmd, "zv?"))
1186  {
1187  snprintf(res, res_len, "<%czvSIMU>", cmd[1]);
1188  }
1189  // Get Encoder Position
1190  else if (strstr (cmd, "p?"))
1191  {
1192  snprintf(res, res_len, "<%cp%.6f>", cmd[1], cmd[1] == '1' ? SimData.currentMechanicalHA : SimData.currentMechanicalDE);
1193  }
1194  // Set Encoder Position
1195  else if (strstr (cmd, "p"))
1196  {
1197  double value = 0;
1198  sscanf(cmd, "<%*cp%lf", &value);
1199  if (cmd[1] == '1')
1200  SimData.targetMechanicalHA = value;
1201  else
1202  SimData.targetMechanicalDE = value;
1203  snprintf(res, res_len, "<%cp#>", cmd[1]);
1204  }
1205  // Get Acceleration
1206  else if (strstr (cmd, "a?"))
1207  {
1208  snprintf(res, res_len, "<%ca%u>", cmd[1], SimData.acceleration[cmd[1] - '1']);
1209  }
1210  // Set Acceleration
1211  else if (strstr (cmd, "a"))
1212  {
1213  uint32_t value = 0;
1214  sscanf(cmd, "<%*ca%u", &value);
1215  SimData.acceleration[cmd[1] - '1'] = value;
1216  snprintf(res, res_len, "<%ca#>", cmd[1]);
1217  }
1218  // Get Velocity
1219  else if (strstr (cmd, "v?"))
1220  {
1221  snprintf(res, res_len, "<%cv%.6f>", cmd[1], SimData.velocity[cmd[1] - '1']);
1222  }
1223  // Set Velocity using encoders
1224  else if (strstr (cmd, "ve"))
1225  {
1226  double value = 0;
1227  sscanf(cmd, "<%*cve%lf", &value);
1228  SimData.velocity[cmd[1] - '1'] = value;
1229  snprintf(res, res_len, "<%cve#>", cmd[1]);
1230  }
1231  // Get Slew status
1232  else if (strstr (cmd, "t"))
1233  {
1234  char result = '0';
1235  if (cmd[1] == '1')
1236  result = std::abs(SimData.currentMechanicalHA - SimData.targetMechanicalHA) <= DIFF_THRESHOLD ? '0' : '1';
1237  else
1238  result = std::abs(SimData.currentMechanicalDE - SimData.targetMechanicalDE) <= DIFF_THRESHOLD ? '0' : '1';
1239  snprintf(res, res_len, "<%ct%c#>", cmd[1], result);
1240  }
1241  // Abort
1242  else if (strstr (cmd, "x"))
1243  {
1244  snprintf(res, res_len, "<%cx#>", cmd[1]);
1245  }
1246 
1247  return true;
1248 }
1249 
1253 bool AstroTrac::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
1254 {
1255  int nbytes_written = 0, nbytes_read = 0, rc = -1;
1256 
1257  if (isSimulation())
1258  return handleSimulationCommand(cmd, res, cmd_len, res_len);
1259 
1260  tcflush(PortFD, TCIOFLUSH);
1261 
1262  if (cmd_len > 0)
1263  {
1264  char hex_cmd[DRIVER_LEN * 3] = {0};
1265  hexDump(hex_cmd, cmd, cmd_len);
1266  LOGF_DEBUG("CMD <%s>", hex_cmd);
1267  rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
1268  }
1269  else
1270  {
1271  LOGF_DEBUG("CMD <%s>", cmd);
1272  rc = tty_write_string(PortFD, cmd, &nbytes_written);
1273  }
1274 
1275  if (rc != TTY_OK)
1276  {
1277  char errstr[MAXRBUF] = {0};
1278  tty_error_msg(rc, errstr, MAXRBUF);
1279  LOGF_ERROR("Serial write error: %s.", errstr);
1280  return false;
1281  }
1282 
1283  if (res == nullptr)
1284  {
1285  tcdrain(PortFD);
1286  return true;
1287  }
1288 
1289  if (res_len > 0)
1290  rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
1291  else
1292  rc = tty_nread_section(PortFD, res, DRIVER_LEN, DRIVER_STOP_CHAR, DRIVER_TIMEOUT, &nbytes_read);
1293 
1294  if (rc != TTY_OK)
1295  {
1296  char errstr[MAXRBUF] = {0};
1297  tty_error_msg(rc, errstr, MAXRBUF);
1298  LOGF_ERROR("Serial read error: %s.", errstr);
1299  return false;
1300  }
1301 
1302  if (res_len > 0)
1303  {
1304  char hex_res[DRIVER_LEN * 3] = {0};
1305  hexDump(hex_res, res, res_len);
1306  LOGF_DEBUG("RES <%s>", hex_res);
1307  }
1308  else
1309  {
1310  LOGF_DEBUG("RES <%s>", res);
1311  }
1312 
1313  tcflush(PortFD, TCIOFLUSH);
1314 
1315  return true;
1316 }
1317 
1321 void AstroTrac::hexDump(char * buf, const char * data, int size)
1322 {
1323  for (int i = 0; i < size; i++)
1324  sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
1325 
1326  if (size > 0)
1327  buf[3 * size - 1] = '\0';
1328 }
1329 
1333 std::vector<std::string> AstroTrac::split(const std::string &input, const std::string &regex)
1334 {
1335  // passing -1 as the submatch index parameter performs splitting
1336  std::regex re(regex);
1337  std::sregex_token_iterator
1338  first{input.begin(), input.end(), re, -1},
1339  last;
1340  return {first, last};
1341 }
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.
Definition: astrotrac.cpp:797
virtual const char * getDefaultName() override
Definition: astrotrac.cpp:54
virtual bool Handshake() override
perform handshake with device to check communication
Definition: astrotrac.cpp:187
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
Definition: astrotrac.cpp:1022
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
Definition: astrotrac.cpp:963
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
Definition: astrotrac.cpp:1003
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: astrotrac.cpp:920
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition: astrotrac.cpp:931
virtual void simulationTriggered(bool enable) override
Handle Simulation Trigger.
Definition: astrotrac.cpp:1171
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition: astrotrac.cpp:507
virtual bool saveConfigItems(FILE *fp) override
Config Items.
Definition: astrotrac.cpp:1068
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition: astrotrac.cpp:1030
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.
Definition: astrotrac.cpp:821
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
Definition: astrotrac.cpp:847
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...
Definition: astrotrac.cpp:127
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
Definition: astrotrac.cpp:1051
virtual bool Park() override
Park the telescope to its home position.
Definition: astrotrac.cpp:703
virtual bool UnPark() override
Unpark the telescope if already parked.
Definition: astrotrac.cpp:718
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition: astrotrac.cpp:899
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition: astrotrac.cpp:137
virtual bool ReadScopeStatus() override
Read telescope status.
Definition: astrotrac.cpp:615
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: astrotrac.cpp:741
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
Definition: astrotrac.cpp:984
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.
Definition: astrotrac.cpp:727
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
Definition: astrotrac.cpp:873
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
Definition: astrotrac.cpp:836
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
Definition: astrotrac.cpp:942
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition: astrotrac.cpp:910
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
Definition: astrotrac.cpp:543
void UpdateLocation(double latitude, double longitude, double elevation)
Call this function from within the updateLocation processing path.
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.
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.
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 updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
static void singleShot(int msec, const std::function< void()> &callback)
This static function calls a the given function after a given time interval.
Definition: inditimer.cpp:146
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.
double max(void)
double min(void)
double ra
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
INDI_EQ_AXIS
Definition: indibasetypes.h:34
@ AXIS_DE
Definition: indibasetypes.h:36
@ AXIS_RA
Definition: indibasetypes.h:35
INDI_DIR_WE
Definition: indibasetypes.h:55
@ DIRECTION_WEST
Definition: indibasetypes.h:56
INDI_DIR_NS
Definition: indibasetypes.h:48
@ DIRECTION_NORTH
Definition: indibasetypes.h:49
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition: indicom.c:1225
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition: indicom.c:1235
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
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:141
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:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
#define TRACKRATE_SOLAR
Definition: indicom.h:61
#define TRACKRATE_SIDEREAL
Definition: indicom.h:55
#define TRACKRATE_LUNAR
Definition: indicom.h:64
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
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:711
#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 MAXRBUF
Definition: indiserver.cpp:102
int isSlewComplete(int fd)
Namespace to encapsulate the INDI Alignment Subsystem classes. For more information see "INDI Alignme...
bool getVersion(const int fd, char response[])
const char * getDeviceName()
@ value
the parser finished reading a JSON value
__u8 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 nomalised direction vector (direction cosines)
Definition: Common.h:69