Instrument Neutral Distributed Interface INDI  2.0.2
lx200ss2000pc.cpp
Go to the documentation of this file.
1 /*
2  SkySensor2000PC
3  Copyright (C) 2015 Camiel Severijns
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "lx200ss2000pc.h"
22 
23 #include "indicom.h"
24 #include "lx200driver.h"
25 
26 #include <cmath>
27 #include <string.h>
28 
29 #include <libnova/transform.h>
30 #include <termios.h>
31 #include <unistd.h>
32 
33 #define RB_MAX_LEN 64
34 
35 const int LX200SS2000PC::ShortTimeOut = 2; // In seconds.
36 const int LX200SS2000PC::LongTimeOut = 10; // In seconds.
37 
39 {
40  setVersion(1, 1);
41 
43 
50 }
51 
53 {
55 
56  IUFillNumber(&SlewAccuracyN[0], "SlewRA", "RA (arcmin)", "%10.6m", 0., 60., 1., 3.0);
57  IUFillNumber(&SlewAccuracyN[1], "SlewDEC", "Dec (arcmin)", "%10.6m", 0., 60., 1., 3.0);
58  IUFillNumberVector(&SlewAccuracyNP, SlewAccuracyN, NARRAY(SlewAccuracyN), getDeviceName(), "Slew Accuracy", "",
60 
62 
63  return true;
64 }
65 
67 {
69 
70  if (isConnected())
71  {
72  defineProperty(&SlewAccuracyNP);
73  }
74  else
75  {
76  deleteProperty(SlewAccuracyNP.name);
77  }
78 
79  return true;
80 }
81 
82 bool LX200SS2000PC::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
83 {
84  if (strcmp(dev, getDeviceName()) == 0)
85  {
86  if (!strcmp(name, SlewAccuracyNP.name))
87  {
88  if (IUUpdateNumber(&SlewAccuracyNP, values, names, n) < 0)
89  return false;
90 
91  SlewAccuracyNP.s = IPS_OK;
92 
93  if (SlewAccuracyN[0].value < 3 || SlewAccuracyN[1].value < 3)
94  IDSetNumber(&SlewAccuracyNP, "Warning: Setting the slew accuracy too low may result in a dead lock");
95 
96  IDSetNumber(&SlewAccuracyNP, nullptr);
97  return true;
98  }
99  }
100 
101  return LX200Generic::ISNewNumber(dev, name, values, names, n);
102 }
103 
105 {
107 
108  IUSaveConfigNumber(fp, &SlewAccuracyNP);
109 
110  return true;
111 }
112 
114 {
115  return const_cast<const char *>("SkySensor2000PC");
116 }
117 
118 bool LX200SS2000PC::updateTime(ln_date *utc, double utc_offset)
119 {
120  bool result = true;
121  // This method is largely identical to the one in the LX200Generic class.
122  // The difference is that it ensures that updates that require planetary
123  // data to be recomputed by the SkySensor2000PC are only done when really
124  // necessary because this takes quite some time.
125  if (!isSimulation())
126  {
127  result = false;
128  struct ln_zonedate ltm;
129  ln_date_to_zonedate(utc, &ltm, static_cast<long>(utc_offset * 3600.0 + 0.5));
130  LOGF_DEBUG("New zonetime is %04d-%02d-%02d %02d:%02d:%06.3f (offset=%ld)", ltm.years,
131  ltm.months, ltm.days, ltm.hours, ltm.minutes, ltm.seconds, ltm.gmtoff);
132  JD = ln_get_julian_day(utc);
133  LOGF_DEBUG("New JD is %f", JD);
134  if (setLocalTime(PortFD, ltm.hours, ltm.minutes, static_cast<int>(ltm.seconds + 0.5)) < 0)
135  {
136  LOG_ERROR("Error setting local time.");
137  }
138  else if (!setCalenderDate(ltm.years, ltm.months, ltm.days))
139  {
140  LOG_ERROR("Error setting local date.");
141  }
142  // Meade defines UTC Offset as the offset ADDED to local time to yield UTC, which
143  // is the opposite of the standard definition of UTC offset!
144  else if (!setUTCOffset(-(utc_offset)))
145  {
146  LOG_ERROR("Error setting UTC Offset.");
147  }
148  else
149  {
150  LOG_INFO("Time updated.");
151  result = true;
152  }
153  }
154  return result;
155 }
156 
158 {
159  if (!isSimulation())
162  sendScopeTime();
163 }
164 
166 {
167  const double dx = targetRA - currentRA;
168  const double dy = targetDEC - currentDEC;
169  return fabs(dx) <= (SlewAccuracyN[0].value / (900.0)) && fabs(dy) <= (SlewAccuracyN[1].value / 60.0);
170 }
171 
172 bool LX200SS2000PC::getCalendarDate(int &year, int &month, int &day)
173 {
174  char date[RB_MAX_LEN];
175  bool result = (getCommandString(PortFD, date, ":GC#") == 0);
176  LOGF_DEBUG("LX200SS2000PC::getCalendarDate():: Date string from telescope: %s", date);
177  if (result)
178  {
179  result = (sscanf(date, "%d%*c%d%*c%d", &month, &day, &year) == 3); // Meade format is MM/DD/YY
180  LOGF_DEBUG("setCalenderDate: Date retrieved from telescope: %02d/%02d/%02d.", month, day,
181  year);
182  if (result)
183  year +=
184  (year > 50 ? 1900 :
185  2000); // Year 50 or later is in the 20th century, anything less is in the 21st century.
186  }
187  return result;
188 }
189 
190 bool LX200SS2000PC::setCalenderDate(int year, int month, int day)
191 {
192  // This method differs from the setCalenderDate function in lx200driver.cpp
193  // in that it reads and checks the complete response from the SkySensor2000PC.
194  // In addition, this method only sends the date when it differs from the date
195  // of the SkySensor2000PC because the resulting update of the planetary data
196  // takes quite some time.
197  bool result = true;
198  int ss_year = 0, ss_month = 0, ss_day = 0;
199  const bool send_to_skysensor =
200  (!getCalendarDate(ss_year, ss_month, ss_day) || year != ss_year || month != ss_month || day != ss_day);
201  LOGF_DEBUG("LX200SS2000PC::setCalenderDate(): Driver date %02d/%02d/%02d, SS2000PC date %02d/%02d/%02d.", month, day,
202  year, ss_month, ss_day, ss_year);
203  if (send_to_skysensor)
204  {
205  char buffer[RB_MAX_LEN];
206  int nbytes_written = 0;
207 
208  snprintf(buffer, sizeof(buffer), ":SC %02d/%02d/%02d#", month, day, (year % 100));
209  result = (tty_write_string(PortFD, buffer, &nbytes_written) == TTY_OK && nbytes_written == (int)strlen(buffer));
210  if (result)
211  {
212  int nbytes_read = 0;
213  result = (tty_nread_section(PortFD, buffer, RB_MAX_LEN, '#', ShortTimeOut, &nbytes_read) == TTY_OK
214  && nbytes_read == 1 &&
215  buffer[0] == '1');
216  if (result)
217  {
218  if (tty_nread_section(PortFD, buffer, RB_MAX_LEN, '#', ShortTimeOut, &nbytes_read) != TTY_OK ||
219  strncmp(buffer, "Updating planetary data#", 24) != 0)
220  {
221  LOGF_ERROR(
222  "LX200SS2000PC::setCalenderDate(): Received unexpected first line '%s'.", buffer);
223  result = false;
224  }
225  else if (tty_nread_section(PortFD, buffer, RB_MAX_LEN, '#', LongTimeOut, &nbytes_read) != TTY_OK &&
226  strncmp(buffer, " #", 24) != 0)
227  {
228  LOGF_ERROR(
229  "LX200SS2000PC::setCalenderDate(): Received unexpected second line '%s'.", buffer);
230  result = false;
231  }
232  }
233  }
234  }
235  return result;
236 }
237 
238 bool LX200SS2000PC::setUTCOffset(double offset)
239 {
240  bool result = true;
241  int ss_timezone;
242  const bool send_to_skysensor = (getUTCOffset(PortFD, &ss_timezone) != 0 || offset != ss_timezone);
243  if (send_to_skysensor)
244  {
245  char temp_string[RB_MAX_LEN];
246  snprintf(temp_string, sizeof(temp_string), ":SG %+03d#", static_cast<int>(offset));
247  result = (setStandardProcedure(PortFD, temp_string) == 0);
248  }
249  return result;
250 }
251 
252 bool LX200SS2000PC::updateLocation(double latitude, double longitude, double elevation)
253 {
254  INDI_UNUSED(elevation);
255 
256  if (isSimulation())
257  return true;
258 
259  if (latitude == 0.0 && longitude == 0.0)
260  return true;
261 
262  if (setSiteLatitude(PortFD, latitude) < 0)
263  {
264  LOG_ERROR("Error setting site latitude coordinates");
265  }
266 
267  if (setSiteLongitude(PortFD, 360.0 - longitude) < 0)
268  {
269  LOG_ERROR("Error setting site longitude coordinates");
270  return false;
271  }
272 
273  char slat[RB_MAX_LEN], slong[RB_MAX_LEN];
274  fs_sexa(slat, latitude, 3, 3600);
275  fs_sexa(slong, longitude, 4, 3600);
276 
277  LOGF_INFO("Site location updated to Latitude: %.32s - Longitude: %.32s", slat, slong);
278 
279  return true;
280 }
281 
282 // This override is needed, because the Sky Sensor 2000 PC requires a space
283 // between the command its argument, unlike the 'standard' LX200 mounts, which
284 // does not work on this mount.
285 int LX200SS2000PC::setSiteLatitude(int fd, double Lat)
286 {
287  int d, m, s;
288  char sign;
289  char temp_string[RB_MAX_LEN];
290 
291  if (Lat > 0)
292  sign = '+';
293  else
294  sign = '-';
295 
296  getSexComponents(Lat, &d, &m, &s);
297 
298  snprintf(temp_string, sizeof(temp_string), ":St %c%03d*%02d#", sign, d, m);
299 
300  return setStandardProcedure(fd, temp_string);
301 }
302 
303 // This override is needed, because the Sky Sensor 2000 PC requires a space
304 // between the command its argument, unlike the 'standard' LX200 mounts, which
305 // does not work on this mount.
306 int LX200SS2000PC::setSiteLongitude(int fd, double Long)
307 {
308  int d, m, s;
309  char temp_string[RB_MAX_LEN];
310 
311  getSexComponents(Long, &d, &m, &s);
312 
313  snprintf(temp_string, sizeof(temp_string), ":Sg %03d*%02d#", d, m);
314 
315  return setStandardProcedure(fd, temp_string);
316 }
317 
319 {
320  double parkAz = GetAxis1Park();
321  double parkAlt = GetAxis2Park();
322 
323  char AzStr[16], AltStr[16];
324  fs_sexa(AzStr, parkAz, 2, 3600);
325  fs_sexa(AltStr, parkAlt, 2, 3600);
326  LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
327 
328  if (isSimulation())
329  {
330 
331  INDI::IEquatorialCoordinates equatorialCoords {0, 0};
332  INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
333  INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
334  Goto(equatorialCoords.rightascension, equatorialCoords.declination);
335  }
336  else
337  {
338  if (setObjAz(PortFD, parkAz) < 0 || setObjAlt(PortFD, parkAlt) < 0)
339  {
340  LOG_ERROR("Error setting Az/Alt.");
341  return false;
342  }
343 
344  int err = 0;
345 
346  /* Slew reads the '0', that is not the end of the slew */
347  if ((err = Slew(PortFD)))
348  {
349  LOGF_ERROR("Error Slewing to Az %s - Alt %s", AzStr, AltStr);
350  slewError(err);
351  return false;
352  }
353  }
354 
355  EqNP.s = IPS_BUSY;
357  LOG_INFO("Parking is in progress...");
358 
359  return true;
360 }
361 
363 {
364  // First we unpark astrophysics
365  if (isSimulation() == false)
366  {
368  {
369  LOG_ERROR("UnParking Failed.");
370  return false;
371  }
372  }
373 
374  // Then we sync with to our last stored position
375  double parkAz = GetAxis1Park();
376  double parkAlt = GetAxis2Park();
377 
378  char AzStr[16], AltStr[16];
379  fs_sexa(AzStr, parkAz, 2, 3600);
380  fs_sexa(AltStr, parkAlt, 2, 3600);
381  LOGF_DEBUG("Syncing to parked coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
382 
383  if (isSimulation())
384  {
385  INDI::IEquatorialCoordinates equatorialCoords {0, 0};
386  INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
387  INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
388  currentRA = equatorialCoords.rightascension;
389  currentDEC = equatorialCoords.declination;
390  }
391  else
392  {
393  if (setObjAz(PortFD, parkAz) < 0 || (setObjAlt(PortFD, parkAlt)) < 0)
394  {
395  LOG_ERROR("Error setting Az/Alt.");
396  return false;
397  }
398 
399  char syncString[256];
400  if (::Sync(PortFD, syncString) < 0)
401  {
402  LOG_WARN("Sync failed.");
403  return false;
404  }
405  }
406 
407  SetParked(false);
408  return true;
409 }
410 
412 {
414  INDI::IHorizontalCoordinates horizontalCoords {0, 0};
415  INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
416  double parkAZ = horizontalCoords.azimuth;
417  double parkAlt = horizontalCoords.altitude;
418 
419  char AzStr[16], AltStr[16];
420  fs_sexa(AzStr, parkAZ, 2, 3600);
421  fs_sexa(AltStr, parkAlt, 2, 3600);
422 
423  LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
424 
425  SetAxis1Park(parkAZ);
426  SetAxis2Park(parkAlt);
427 
428  return true;
429 }
430 
432 {
433  // Az = 0 for North hemisphere
434  SetAxis1Park(LocationN[LOCATION_LATITUDE].value > 0 ? 0 : 180);
435 
436  // Alt = Latitude
438 
439  return true;
440 }
441 
443 {
444  if (!isConnected())
445  return false;
446 
447  if (isSimulation())
448  {
449  mountSim();
450  return true;
451  }
452 
453  //if (check_lx200_connection(PortFD))
454  //return false;
455 
456  if (TrackState == SCOPE_SLEWING)
457  {
458  // Check if LX200 is done slewing
459  if (isSlewComplete())
460  {
461  // Set slew mode to "Centering"
464  IDSetSwitch(&SlewRateSP, nullptr);
465 
467  LOG_INFO("Slew is complete. Tracking...");
468  }
469  }
470  else if (TrackState == SCOPE_PARKING)
471  {
472  if (isSlewComplete())
473  {
474  SetParked(true);
475 
477  }
478  }
479 
481  {
482  EqNP.s = IPS_ALERT;
483  IDSetNumber(&EqNP, "Error reading RA/DEC.");
484  return false;
485  }
486 
488 
489  return true;
490 }
491 
492 
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
double GetAxis1Park() const
double GetAxis2Park() const
ISwitchVectorProperty SlewRateSP
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
INumber LocationN[3]
ISwitch * SlewRateS
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual void getBasicData(void) override
virtual const char * getDefaultName(void) override
virtual bool setUTCOffset(double offset) override
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool Park() override
Park the telescope to its home position.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool isSlewComplete(void) override
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual void slewError(int slewCode)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool sendScopeLocation()
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool sendScopeTime()
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
void setLX200Capability(uint32_t cap)
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
#define setLocalTime(fd, x, y, z)
Definition: ieq45driver.h:141
@ ISS_ON
Definition: indiapi.h:152
#define NARRAY(a)
Handy macro to find the number of elements in array a[]. Must be used with actual array,...
Definition: indiapi.h:500
@ IP_RW
Definition: indiapi.h:186
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
void getSexComponents(double value, int *d, int *m, int *s)
Definition: indicom.c:254
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
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
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
int fd
Definition: intelliscope.c:43
int setAlignmentMode(int fd, unsigned int alignMode)
int setObjAlt(int fd, double alt)
int setObjAz(int fd, double az)
int setStandardProcedure(int fd, const char *data)
int setSiteLongitude(int fd, double CartographicLongitude, bool addSpace)
int checkLX200EquatorialFormat(int fd)
int setSiteLatitude(int fd, double Lat, bool addSpace)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
int getCalendarDate(int fd, char *date)
int Slew(int fd)
int getCommandString(int fd, char *data, const char *cmd)
#define getUTCOffset(fd, x)
Definition: lx200driver.h:137
@ LX200_ALIGN_POLAR
Definition: lx200driver.h:34
@ LX200_ALIGN_LAND
Definition: lx200driver.h:36
#define getLX200DEC(fd, x)
Definition: lx200driver.h:118
#define getLX200RA(fd, x)
Definition: lx200driver.h:117
#define RB_MAX_LEN
std::vector< uint8_t > buffer
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition: libastro.cpp:140
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition: libastro.cpp:156
char name[MAXINDINAME]
Definition: indiapi.h:323