You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2035 lines
49 KiB
2035 lines
49 KiB
#if 0
|
|
LX200 Generic
|
|
Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
#endif
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include "indicom.h"
|
|
#include "lx200driver.h"
|
|
#include "lx200gps.h"
|
|
#include "lx200classic.h"
|
|
|
|
/*
|
|
** Return the timezone offset in hours (as a double, so fractional
|
|
** hours are possible, for instance in Newfoundland). Also sets
|
|
** daylight on non-Linux systems to record whether DST is in effect.
|
|
*/
|
|
|
|
|
|
#if !(TIMEZONE_IS_INT)
|
|
//static int daylight = 0;
|
|
int daylight = 0;
|
|
#endif
|
|
|
|
static inline double timezoneOffset()
|
|
{
|
|
/*
|
|
** In Linux, there's a timezone variable that holds the timezone offset;
|
|
** Otherwise, we need to make a little detour. The directions of the offset
|
|
** are different: CET is -3600 in Linux and +3600 elsewhere.
|
|
*/
|
|
#if TIMEZONE_IS_INT
|
|
return timezone / (60 * 60);
|
|
#else
|
|
time_t now;
|
|
struct tm *tm;
|
|
now = time(NULL);
|
|
tm = localtime(&now);
|
|
daylight = tm->tm_isdst;
|
|
return -(tm->tm_gmtoff) / (60 * 60);
|
|
#endif
|
|
}
|
|
|
|
LX200Generic *telescope = NULL;
|
|
int MaxReticleFlashRate = 3;
|
|
|
|
/* There is _one_ binary for all LX200 drivers, but each binary is renamed
|
|
** to its device name (i.e. lx200gps, lx200_16..etc). The main function will
|
|
** fetch from std args the binary name and ISInit will create the apporpiate
|
|
** device afterwards. If the binary name does not match any known devices,
|
|
** we simply create a generic device
|
|
*/
|
|
extern char* me;
|
|
|
|
#define COMM_GROUP "Communication"
|
|
#define BASIC_GROUP "Main Control"
|
|
#define MOVE_GROUP "Movement Control"
|
|
#define DATETIME_GROUP "Date/Time"
|
|
#define SITE_GROUP "Site Management"
|
|
#define FOCUS_GROUP "Focus Control"
|
|
|
|
#define RA_THRESHOLD 0.01
|
|
#define DEC_THRESHOLD 0.05
|
|
#define LX200_SLEW 0
|
|
#define LX200_TRACK 1
|
|
#define LX200_SYNC 2
|
|
#define LX200_PARK 3
|
|
|
|
static void ISPoll(void *);
|
|
static void retryConnection(void *);
|
|
|
|
/*INDI controls */
|
|
static ISwitch PowerS[] = {{"CONNECT" , "Connect" , ISS_OFF, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON, 0, 0}};
|
|
static ISwitch AlignmentS [] = {{"Polar", "", ISS_ON, 0, 0}, {"AltAz", "", ISS_OFF, 0, 0}, {"Land", "", ISS_OFF, 0, 0}};
|
|
static ISwitch SitesS[] = {{"Site 1", "", ISS_ON, 0, 0}, {"Site 2", "", ISS_OFF, 0, 0}, {"Site 3", "", ISS_OFF, 0, 0}, {"Site 4", "", ISS_OFF, 0 ,0}};
|
|
static ISwitch SlewModeS[] = {{"Max", "", ISS_ON, 0, 0}, {"Find", "", ISS_OFF, 0, 0}, {"Centering", "", ISS_OFF, 0, 0}, {"Guide", "", ISS_OFF, 0 , 0}};
|
|
static ISwitch OnCoordSetS[] = {{"SLEW", "Slew", ISS_ON, 0, 0 }, {"TRACK", "Track", ISS_OFF, 0, 0}, {"SYNC", "Sync", ISS_OFF, 0 , 0}};
|
|
static ISwitch TrackModeS[] = {{ "Default", "", ISS_ON, 0, 0} , { "Lunar", "", ISS_OFF, 0, 0}, {"Manual", "", ISS_OFF, 0, 0}};
|
|
static ISwitch abortSlewS[] = {{"ABORT", "Abort", ISS_OFF, 0, 0 }};
|
|
static ISwitch ParkS[] = { {"PARK", "Park", ISS_OFF, 0, 0} };
|
|
|
|
static ISwitch MovementS[] = {{"N", "North", ISS_OFF, 0, 0}, {"W", "West", ISS_OFF, 0, 0}, {"E", "East", ISS_OFF, 0, 0}, {"S", "South", ISS_OFF, 0, 0}};
|
|
|
|
static INumber FocusSpeedN[] = {{"SPEED", "Speed", "%0.f", 0., 3., 1., 0.}};
|
|
static ISwitch FocusMotionS[] = { {"IN", "Focus in", ISS_OFF, 0, 0}, {"OUT", "Focus out", ISS_OFF, 0, 0}};
|
|
static INumber FocusTimerN[] = { {"TIMER", "Timer (s)", "%10.6m", 0., 120., 1., 0., 0, 0, 0 }};
|
|
|
|
static INumberVectorProperty FocusTimerNP = { mydev, "FOCUS_TIMER", "Focus Timer", FOCUS_GROUP, IP_RW, 0, IPS_IDLE, FocusTimerN, NARRAY(FocusTimerN), "", 0};
|
|
|
|
/* equatorial position */
|
|
INumber eq[] = {
|
|
{"RA", "RA H:M:S", "%10.6m", 0., 24., 0., 0., 0, 0, 0},
|
|
{"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0},
|
|
};
|
|
//TODO decide appropiate TIME_OUT
|
|
// N.B. No Static identifier as it is needed for external linkage
|
|
INumberVectorProperty eqNum = {
|
|
mydev, "EQUATORIAL_EOD_COORD", "Equatorial JNow", BASIC_GROUP, IP_RW, 0, IPS_IDLE,
|
|
eq, NARRAY(eq), "", 0};
|
|
|
|
/* Fundamental group */
|
|
ISwitchVectorProperty PowerSP = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, PowerS, NARRAY(PowerS), "", 0};
|
|
static IText PortT[] = {{"PORT", "Port", 0, 0, 0, 0}};
|
|
static ITextVectorProperty Port = { mydev, "DEVICE_PORT", "Ports", COMM_GROUP, IP_RW, 0, IPS_IDLE, PortT, NARRAY(PortT), "", 0};
|
|
|
|
/* Basic data group */
|
|
static ISwitchVectorProperty AlignmentSw = { mydev, "Alignment", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, AlignmentS, NARRAY(AlignmentS), "", 0};
|
|
|
|
/* Movement group */
|
|
static ISwitchVectorProperty OnCoordSetSw = { mydev, "ON_COORD_SET", "On Set", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, OnCoordSetS, NARRAY(OnCoordSetS), "", 0};
|
|
|
|
static ISwitchVectorProperty abortSlewSw = { mydev, "ABORT_MOTION", "Abort Slew/Track", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, abortSlewS, NARRAY(abortSlewS), "", 0};
|
|
|
|
ISwitchVectorProperty ParkSP = {mydev, "PARK", "Park Scope", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, ParkS, NARRAY(ParkS), "", 0 };
|
|
|
|
static ISwitchVectorProperty SlewModeSw = { mydev, "Slew rate", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SlewModeS, NARRAY(SlewModeS), "", 0};
|
|
|
|
static ISwitchVectorProperty TrackModeSw = { mydev, "Tracking Mode", "", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, TrackModeS, NARRAY(TrackModeS), "", 0};
|
|
|
|
static INumber TrackFreq[] = {{ "trackFreq", "Freq", "%g", 56.4, 60.1, 0.1, 60.1, 0, 0, 0}};
|
|
|
|
static INumberVectorProperty TrackingFreq= { mydev, "Tracking Frequency", "", MOVE_GROUP, IP_RW, 0, IPS_IDLE, TrackFreq, NARRAY(TrackFreq), "", 0};
|
|
|
|
static ISwitchVectorProperty MovementSw = { mydev, "MOVEMENT", "Move toward", MOVE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, MovementS, NARRAY(MovementS), "", 0};
|
|
|
|
// Focus Control
|
|
static INumberVectorProperty FocusSpeedNP = {mydev, "FOCUS_SPEED", "Speed", FOCUS_GROUP, IP_RW, 0, IPS_IDLE, FocusSpeedN, NARRAY(FocusSpeedN), "", 0};
|
|
|
|
static ISwitchVectorProperty FocusMotionSw = {mydev, "FOCUS_MOTION", "Motion", FOCUS_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FocusMotionS, NARRAY(FocusMotionS), "", 0};
|
|
|
|
/* Data & Time */
|
|
static IText UTC[] = {{"UTC", "UTC", 0, 0, 0, 0}};
|
|
ITextVectorProperty Time = { mydev, "TIME", "UTC Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, UTC, NARRAY(UTC), "", 0};
|
|
static INumber STime[] = {{"LST", "Sidereal time", "%10.6m" , 0.,24.,0.,0., 0, 0, 0}};
|
|
INumberVectorProperty SDTime = { mydev, "SDTIME", "Sidereal Time", DATETIME_GROUP, IP_RW, 0, IPS_IDLE, STime, NARRAY(STime), "", 0};
|
|
|
|
|
|
/* Site managment */
|
|
static ISwitchVectorProperty SitesSw = { mydev, "Sites", "", SITE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, SitesS, NARRAY(SitesS), "", 0};
|
|
/* geographic location */
|
|
static INumber geo[] = {
|
|
{"LAT", "Lat. D:M:S +N", "%10.6m", -90., 90., 0., 0., 0, 0, 0},
|
|
{"LONG", "Long. D:M:S +E", "%10.6m", 0., 360., 0., 0., 0, 0, 0},
|
|
};
|
|
static INumberVectorProperty geoNum = {
|
|
mydev, "GEOGRAPHIC_COORD", "Geographic Location", SITE_GROUP, IP_RW, 0., IPS_IDLE,
|
|
geo, NARRAY(geo), "", 0};
|
|
static IText SiteNameT[] = {{"SiteName", "", 0, 0, 0, 0}};
|
|
static ITextVectorProperty SiteName = { mydev, "Site Name", "", SITE_GROUP, IP_RW, 0 , IPS_IDLE, SiteNameT, NARRAY(SiteNameT), "", 0};
|
|
|
|
void changeLX200GenericDeviceName(const char * newName)
|
|
{
|
|
strcpy(PowerSP.device , newName);
|
|
strcpy(Port.device , newName);
|
|
strcpy(AlignmentSw.device, newName);
|
|
|
|
// BASIC_GROUP
|
|
strcpy(eqNum.device, newName);
|
|
strcpy(OnCoordSetSw.device , newName );
|
|
strcpy(abortSlewSw.device , newName );
|
|
strcpy(ParkSP.device, newName);
|
|
|
|
// MOVE_GROUP
|
|
strcpy(SlewModeSw.device , newName );
|
|
strcpy(TrackModeSw.device , newName );
|
|
strcpy(TrackingFreq.device , newName );
|
|
strcpy(MovementSw.device , newName );
|
|
|
|
// FOCUS_GROUP
|
|
strcpy(FocusSpeedNP.device , newName );
|
|
strcpy(FocusMotionSw.device , newName );
|
|
strcpy(FocusTimerNP.device, newName);
|
|
|
|
// DATETIME_GROUP
|
|
strcpy(Time.device , newName );
|
|
strcpy(SDTime.device , newName );
|
|
|
|
// SITE_GROUP
|
|
strcpy(SitesSw.device , newName );
|
|
strcpy(SiteName.device , newName );
|
|
strcpy(geoNum.device , newName );
|
|
|
|
}
|
|
|
|
void changeAllDeviceNames(const char *newName)
|
|
{
|
|
changeLX200GenericDeviceName(newName);
|
|
changeLX200AutostarDeviceName(newName);
|
|
changeLX200_16DeviceName(newName);
|
|
changeLX200ClassicDeviceName(newName);
|
|
changeLX200GPSDeviceName(newName);
|
|
}
|
|
|
|
|
|
/* send client definitions of all properties */
|
|
void ISInit()
|
|
{
|
|
static int isInit=0;
|
|
|
|
if (isInit)
|
|
return;
|
|
|
|
isInit = 1;
|
|
|
|
PortT[0].text = strcpy(new char[32], "/dev/ttyS0");
|
|
UTC[0].text = strcpy(new char[32], "YYYY-MM-DDTHH:MM:SS");
|
|
|
|
if (strstr(me, "lx200classic"))
|
|
{
|
|
fprintf(stderr , "initilizaing from LX200 classic device...\n");
|
|
// 1. mydev = device_name
|
|
changeAllDeviceNames("LX200 Classic");
|
|
// 2. device = sub_class
|
|
telescope = new LX200Classic();
|
|
telescope->setCurrentDeviceName("LX200 Classic");
|
|
|
|
MaxReticleFlashRate = 3;
|
|
}
|
|
|
|
else if (strstr(me, "lx200gps"))
|
|
{
|
|
fprintf(stderr , "initilizaing from LX200 GPS device...\n");
|
|
// 1. mydev = device_name
|
|
changeAllDeviceNames("LX200 GPS");
|
|
// 2. device = sub_class
|
|
telescope = new LX200GPS();
|
|
telescope->setCurrentDeviceName("LX200 GPS");
|
|
|
|
MaxReticleFlashRate = 9;
|
|
}
|
|
else if (strstr(me, "lx200_16"))
|
|
{
|
|
|
|
IDLog("Initilizaing from LX200 16 device...\n");
|
|
// 1. mydev = device_name
|
|
changeAllDeviceNames("LX200 16");
|
|
// 2. device = sub_class
|
|
telescope = new LX200_16();
|
|
telescope->setCurrentDeviceName("LX200 16");
|
|
|
|
MaxReticleFlashRate = 3;
|
|
}
|
|
else if (strstr(me, "lx200autostar"))
|
|
{
|
|
fprintf(stderr , "initilizaing from autostar device...\n");
|
|
|
|
// 1. change device name
|
|
changeAllDeviceNames("LX200 Autostar");
|
|
// 2. device = sub_class
|
|
telescope = new LX200Autostar();
|
|
telescope->setCurrentDeviceName("LX200 Autostar");
|
|
|
|
MaxReticleFlashRate = 9;
|
|
}
|
|
// be nice and give them a generic device
|
|
else
|
|
{
|
|
telescope = new LX200Generic();
|
|
telescope->setCurrentDeviceName("LX200 Generic");
|
|
}
|
|
|
|
}
|
|
|
|
void ISGetProperties (const char *dev)
|
|
{ ISInit(); telescope->ISGetProperties(dev); IEAddTimer (POLLMS, ISPoll, NULL);}
|
|
void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
|
|
{ ISInit(); telescope->ISNewSwitch(dev, name, states, names, n);}
|
|
void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
|
|
{ ISInit(); telescope->ISNewText(dev, name, texts, names, n);}
|
|
void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
|
|
{ ISInit(); telescope->ISNewNumber(dev, name, values, names, n);}
|
|
void ISPoll (void *p) { telescope->ISPoll(); IEAddTimer (POLLMS, ISPoll, NULL); p=p;}
|
|
void ISNewBLOB (const char */*dev*/, const char */*name*/, int */*sizes[]*/, char **/*blobs[]*/, char **/*formats[]*/, char **/*names[]*/, int /*n*/)
|
|
{}
|
|
|
|
/**************************************************
|
|
*** LX200 Generic Implementation
|
|
***************************************************/
|
|
|
|
LX200Generic::LX200Generic()
|
|
{
|
|
struct tm *utp;
|
|
time_t t;
|
|
time (&t);
|
|
utp = gmtime (&t);
|
|
|
|
currentSiteNum = 1;
|
|
trackingMode = LX200_TRACK_DEFAULT;
|
|
lastSet = -1;
|
|
fault = false;
|
|
simulation = false;
|
|
targetRA = 0;
|
|
targetDEC = 0;
|
|
currentRA = 0;
|
|
currentDEC = 0;
|
|
currentSet = 0;
|
|
UTCOffset = 0;
|
|
lastMove[0] = lastMove[1] = lastMove[2] = lastMove[3] = 0;
|
|
|
|
localTM = new tm;
|
|
|
|
utp->tm_mon += 1;
|
|
utp->tm_year += 1900;
|
|
JD = UTtoJD(utp);
|
|
|
|
IDLog("Julian Day is %g\n", JD);
|
|
|
|
// Children call parent routines, this is the default
|
|
IDLog("initilizaing from generic LX200 device...\n");
|
|
IDLog("INDI Version: 2004-02-17\n");
|
|
|
|
//enableSimulation(true);
|
|
}
|
|
|
|
void LX200Generic::setCurrentDeviceName(const char * devName)
|
|
{
|
|
strcpy(thisDevice, devName);
|
|
|
|
}
|
|
|
|
void LX200Generic::ISGetProperties(const char *dev)
|
|
{
|
|
|
|
if (dev && strcmp (thisDevice, dev))
|
|
return;
|
|
|
|
// COMM_GROUP
|
|
IDDefSwitch (&PowerSP, NULL);
|
|
IDDefText (&Port, NULL);
|
|
IDDefSwitch (&AlignmentSw, NULL);
|
|
|
|
// BASIC_GROUP
|
|
IDDefNumber (&eqNum, NULL);
|
|
IDDefSwitch (&OnCoordSetSw, NULL);
|
|
IDDefSwitch (&abortSlewSw, NULL);
|
|
IDDefSwitch (&ParkSP, NULL);
|
|
|
|
// MOVE_GROUP
|
|
IDDefNumber (&TrackingFreq, NULL);
|
|
IDDefSwitch (&SlewModeSw, NULL);
|
|
IDDefSwitch (&TrackModeSw, NULL);
|
|
IDDefSwitch (&MovementSw, NULL);
|
|
|
|
// FOCUS_GROUP
|
|
IDDefNumber(&FocusSpeedNP, NULL);
|
|
IDDefSwitch(&FocusMotionSw, NULL);
|
|
IDDefNumber(&FocusTimerNP, NULL);
|
|
|
|
// DATETIME_GROUP
|
|
IDDefText (&Time, NULL);
|
|
IDDefNumber (&SDTime, NULL);
|
|
|
|
// SITE_GROUP
|
|
IDDefSwitch (&SitesSw, NULL);
|
|
IDDefText (&SiteName, NULL);
|
|
IDDefNumber (&geoNum, NULL);
|
|
|
|
/* Send the basic data to the new client if the previous client(s) are already connected. */
|
|
if (PowerSP.s == IPS_OK)
|
|
getBasicData();
|
|
|
|
}
|
|
|
|
void LX200Generic::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
|
|
{
|
|
int err;
|
|
struct tm *ltp = new tm;
|
|
struct tm utm;
|
|
time_t ltime;
|
|
time (<ime);
|
|
localtime_r (<ime, ltp);
|
|
IText *tp;
|
|
|
|
// ignore if not ours //
|
|
if (strcmp (dev, thisDevice))
|
|
return;
|
|
|
|
// suppress warning
|
|
n=n;
|
|
|
|
if (!strcmp(name, Port.name) )
|
|
{
|
|
Port.s = IPS_OK;
|
|
tp = IUFindText( &Port, names[0] );
|
|
if (!tp)
|
|
return;
|
|
|
|
tp->text = new char[strlen(texts[0])+1];
|
|
strcpy(tp->text, texts[0]);
|
|
IDSetText (&Port, NULL);
|
|
return;
|
|
}
|
|
|
|
if (!strcmp (name, SiteName.name) )
|
|
{
|
|
if (checkPower(&SiteName))
|
|
return;
|
|
|
|
if ( ( err = setSiteName(texts[0], currentSiteNum) < 0) )
|
|
{
|
|
handleError(&SiteName, err, "Setting site name");
|
|
return;
|
|
}
|
|
SiteName.s = IPS_OK;
|
|
tp = IUFindText(&SiteName, names[0]);
|
|
tp->text = new char[strlen(texts[0])+1];
|
|
strcpy(tp->text, texts[0]);
|
|
IDSetText(&SiteName , "Site name updated");
|
|
return;
|
|
}
|
|
|
|
if (!strcmp (name, Time.name))
|
|
{
|
|
if (checkPower(&Time))
|
|
return;
|
|
|
|
if (extractISOTime(texts[0], &utm) < 0)
|
|
{
|
|
Time.s = IPS_IDLE;
|
|
IDSetText(&Time , "Time invalid");
|
|
return;
|
|
}
|
|
ltp->tm_mon += 1;
|
|
ltp->tm_year += 1900;
|
|
|
|
|
|
/*dayDiff = utm.tm_mday - ltp->tm_mday;
|
|
if (dayDiff == 0)
|
|
UTCOffset = (ltp->tm_hour - utm.tm_hour);
|
|
else if (dayDiff > 0)
|
|
UTCOffset = ltp->tm_hour - utm.tm_hour - 24;
|
|
else UTCOffset = ltp->tm_hour - utm.tm_hour + 24;*/
|
|
tzset();
|
|
|
|
UTCOffset = timezoneOffset();
|
|
|
|
IDLog("local time is %02d:%02d:%02d\nUTCOffset: %g\n", ltp->tm_hour, ltp->tm_min, ltp->tm_sec, UTCOffset);
|
|
|
|
getSDTime(&STime[0].value);
|
|
IDSetNumber(&SDTime, NULL);
|
|
|
|
if ( ( err = setUTCOffset(UTCOffset) < 0) )
|
|
{
|
|
Time.s = IPS_IDLE;
|
|
IDSetText( &Time , "Setting UTC Offset failed.");
|
|
return;
|
|
}
|
|
|
|
if ( ( err = setLocalTime(ltp->tm_hour, ltp->tm_min, ltp->tm_sec) < 0) )
|
|
{
|
|
handleError(&Time, err, "Setting local time");
|
|
return;
|
|
}
|
|
|
|
tp = IUFindText(&Time, names[0]);
|
|
if (!tp)
|
|
return;
|
|
tp->text = new char[strlen(texts[0])+1];
|
|
strcpy(tp->text, texts[0]);
|
|
Time.s = IPS_OK;
|
|
|
|
// update JD
|
|
JD = UTtoJD(&utm);
|
|
|
|
utm.tm_mon += 1;
|
|
utm.tm_year += 1900;
|
|
|
|
IDLog("New JD is %f\n", (float) JD);
|
|
|
|
if ((localTM->tm_mday == ltp->tm_mday ) && (localTM->tm_mon == ltp->tm_mon) &&
|
|
(localTM->tm_year == ltp->tm_year))
|
|
{
|
|
IDSetText(&Time , "Time updated to %s", texts[0]);
|
|
return;
|
|
}
|
|
|
|
localTM = ltp;
|
|
|
|
if (!strcmp(dev, "LX200 GPS"))
|
|
{
|
|
if ( ( err = setCalenderDate(utm.tm_mday, utm.tm_mon, utm.tm_year) < 0) )
|
|
{
|
|
handleError(&Time, err, "Setting UTC date.");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ( err = setCalenderDate(ltp->tm_mday, ltp->tm_mon, ltp->tm_year) < 0) )
|
|
{
|
|
handleError(&Time, err, "Setting local date.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
IDSetText(&Time , "Date changed, updating planetary data...");
|
|
}
|
|
}
|
|
|
|
|
|
void LX200Generic::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
|
|
{
|
|
int h =0, m =0, s=0, err;
|
|
double newRA =0, newDEC =0;
|
|
|
|
// ignore if not ours //
|
|
if (strcmp (dev, thisDevice))
|
|
return;
|
|
|
|
if (!strcmp (name, eqNum.name))
|
|
{
|
|
int i=0, nset=0;
|
|
|
|
if (checkPower(&eqNum))
|
|
return;
|
|
|
|
for (nset = i = 0; i < n; i++)
|
|
{
|
|
INumber *eqp = IUFindNumber (&eqNum, names[i]);
|
|
if (eqp == &eq[0])
|
|
{
|
|
newRA = values[i];
|
|
nset += newRA >= 0 && newRA <= 24.0;
|
|
} else if (eqp == &eq[1])
|
|
{
|
|
newDEC = values[i];
|
|
nset += newDEC >= -90.0 && newDEC <= 90.0;
|
|
}
|
|
}
|
|
|
|
if (nset == 2)
|
|
{
|
|
/*eqNum.s = IPS_BUSY;*/
|
|
char RAStr[32], DecStr[32];
|
|
|
|
fs_sexa(RAStr, newRA, 2, 3600);
|
|
fs_sexa(DecStr, newDEC, 2, 3600);
|
|
|
|
IDLog("We received J2000 RA %g - DEC %g\n", newRA, newDEC);
|
|
IDLog("We received J2000 RA %s - DEC %s\n", RAStr, DecStr);
|
|
|
|
/*apparentCoord( (double) J2000, JD, &newRA, &newDEC);
|
|
|
|
fs_sexa(RAStr, newRA, 2, 3600);
|
|
fs_sexa(DecStr, newDEC, 2, 3600);
|
|
|
|
IDLog("Processed to JNow RA %f - DEC %f\n", newRA, newDEC);
|
|
IDLog("Processed to JNow RA %s - DEC %s\n", RAStr, DecStr);*/
|
|
|
|
if ( (err = setObjectRA(newRA)) < 0 || ( err = setObjectDEC(newDEC)) < 0)
|
|
{
|
|
handleError(&eqNum, err, "Setting RA/DEC");
|
|
return;
|
|
}
|
|
|
|
/*eqNum.s = IPS_BUSY;*/
|
|
targetRA = newRA;
|
|
targetDEC = newDEC;
|
|
|
|
if (MovementSw.s == IPS_BUSY)
|
|
{
|
|
for (int i=0; i < 4; i++)
|
|
{
|
|
lastMove[i] = 0;
|
|
MovementS[i].s = ISS_OFF;
|
|
}
|
|
|
|
MovementSw.s = IPS_IDLE;
|
|
IDSetSwitch(&MovementSw, NULL);
|
|
}
|
|
|
|
if (handleCoordSet())
|
|
{
|
|
eqNum.s = IPS_IDLE;
|
|
IDSetNumber(&eqNum, NULL);
|
|
|
|
}
|
|
} // end nset
|
|
else
|
|
{
|
|
eqNum.s = IPS_IDLE;
|
|
IDSetNumber(&eqNum, "RA or Dec missing or invalid");
|
|
}
|
|
|
|
return;
|
|
} /* end eqNum */
|
|
|
|
if ( !strcmp (name, SDTime.name) )
|
|
{
|
|
if (checkPower(&SDTime))
|
|
return;
|
|
|
|
|
|
if (values[0] < 0.0 || values[0] > 24.0)
|
|
{
|
|
SDTime.s = IPS_IDLE;
|
|
IDSetNumber(&SDTime , "Time invalid");
|
|
return;
|
|
}
|
|
|
|
getSexComponents(values[0], &h, &m, &s);
|
|
IDLog("Time is %02d:%02d:%02d\n", h, m, s);
|
|
|
|
if ( ( err = setSDTime(h, m, s) < 0) )
|
|
{
|
|
handleError(&SDTime, err, "Setting siderial time");
|
|
return;
|
|
}
|
|
|
|
SDTime.np[0].value = values[0];
|
|
SDTime.s = IPS_OK;
|
|
|
|
IDSetNumber(&SDTime , "Sidereal time updated to %02d:%02d:%02d", h, m, s);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!strcmp (name, geoNum.name))
|
|
{
|
|
// new geographic coords
|
|
double newLong = 0, newLat = 0;
|
|
int i, nset;
|
|
char msg[128];
|
|
|
|
if (checkPower(&geoNum))
|
|
return;
|
|
|
|
|
|
for (nset = i = 0; i < n; i++)
|
|
{
|
|
INumber *geop = IUFindNumber (&geoNum, names[i]);
|
|
if (geop == &geo[0])
|
|
{
|
|
newLat = values[i];
|
|
nset += newLat >= -90.0 && newLat <= 90.0;
|
|
} else if (geop == &geo[1])
|
|
{
|
|
newLong = values[i];
|
|
nset += newLong >= 0.0 && newLong < 360.0;
|
|
}
|
|
}
|
|
|
|
if (nset == 2)
|
|
{
|
|
char l[32], L[32];
|
|
geoNum.s = IPS_OK;
|
|
fs_sexa (l, newLat, 3, 3600);
|
|
fs_sexa (L, newLong, 4, 3600);
|
|
|
|
if ( ( err = setSiteLongitude(360.0 - newLong) < 0) )
|
|
{
|
|
handleError(&geoNum, err, "Setting site coordinates");
|
|
return;
|
|
}
|
|
|
|
setSiteLatitude(newLat);
|
|
geoNum.np[0].value = newLat;
|
|
geoNum.np[1].value = newLong;
|
|
snprintf (msg, sizeof(msg), "Site location updated to Lat %.32s - Long %.32s", l, L);
|
|
} else
|
|
{
|
|
geoNum.s = IPS_IDLE;
|
|
strcpy(msg, "Lat or Long missing or invalid");
|
|
}
|
|
IDSetNumber (&geoNum, "%s", msg);
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp (name, TrackingFreq.name) )
|
|
{
|
|
|
|
if (checkPower(&TrackingFreq))
|
|
return;
|
|
|
|
IDLog("Trying to set track freq of: %f\n", values[0]);
|
|
|
|
if ( ( err = setTrackFreq(values[0])) < 0)
|
|
{
|
|
handleError(&TrackingFreq, err, "Setting tracking frequency");
|
|
return;
|
|
}
|
|
|
|
TrackingFreq.s = IPS_OK;
|
|
TrackingFreq.np[0].value = values[0];
|
|
IDSetNumber(&TrackingFreq, "Tracking frequency set to %04.1f", values[0]);
|
|
if (trackingMode != LX200_TRACK_MANUAL)
|
|
{
|
|
trackingMode = LX200_TRACK_MANUAL;
|
|
TrackModeS[0].s = ISS_OFF;
|
|
TrackModeS[1].s = ISS_OFF;
|
|
TrackModeS[2].s = ISS_ON;
|
|
TrackModeSw.s = IPS_OK;
|
|
selectTrackingMode(trackingMode);
|
|
IDSetSwitch(&TrackModeSw, NULL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(name, FocusTimerNP.name))
|
|
{
|
|
if (checkPower(&FocusTimerNP))
|
|
return;
|
|
|
|
// Don't update if busy
|
|
if (FocusTimerNP.s == IPS_BUSY)
|
|
return;
|
|
|
|
IUUpdateNumbers(&FocusTimerNP, values, names, n);
|
|
|
|
FocusTimerNP.s = IPS_OK;
|
|
|
|
IDSetNumber(&FocusTimerNP, NULL);
|
|
IDLog("Setting focus timer to %g\n", FocusTimerN[0].value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Focus speed
|
|
if (!strcmp (name, FocusSpeedNP.name))
|
|
{
|
|
if (checkPower(&FocusSpeedNP))
|
|
return;
|
|
|
|
if (IUUpdateNumbers(&FocusSpeedNP, values, names, n) < 0)
|
|
return;
|
|
|
|
/* disable timer and motion */
|
|
if (FocusSpeedN[0].value == 0)
|
|
{
|
|
IUResetSwitches(&FocusMotionSw);
|
|
FocusMotionSw.s = IPS_IDLE;
|
|
FocusTimerNP.s = IPS_IDLE;
|
|
IDSetSwitch(&FocusMotionSw, NULL);
|
|
IDSetNumber(&FocusTimerNP, NULL);
|
|
}
|
|
|
|
setFocuserSpeedMode( ( (int) FocusSpeedN[0].value));
|
|
FocusSpeedNP.s = IPS_OK;
|
|
IDSetNumber(&FocusSpeedNP, NULL);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
void LX200Generic::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
|
|
{
|
|
int index;
|
|
int dd, mm, err;
|
|
char combinedDir[64];
|
|
ISwitch *swp;
|
|
|
|
// suppress warning
|
|
names = names;
|
|
|
|
//IDLog("in new Switch with Device= %s and Property= %s and #%d items\n", dev, name,n);
|
|
//IDLog("SolarSw name is %s\n", SolarSw.name);
|
|
|
|
//IDLog("The device name is %s\n", dev);
|
|
// ignore if not ours //
|
|
if (strcmp (thisDevice, dev))
|
|
return;
|
|
|
|
// FIRST Switch ALWAYS for power
|
|
if (!strcmp (name, PowerSP.name))
|
|
{
|
|
IUResetSwitches(&PowerSP);
|
|
IUUpdateSwitches(&PowerSP, states, names, n);
|
|
powerTelescope();
|
|
return;
|
|
}
|
|
|
|
// Coord set
|
|
if (!strcmp(name, OnCoordSetSw.name))
|
|
{
|
|
if (checkPower(&OnCoordSetSw))
|
|
return;
|
|
|
|
IUResetSwitches(&OnCoordSetSw);
|
|
IUUpdateSwitches(&OnCoordSetSw, states, names, n);
|
|
currentSet = getOnSwitch(&OnCoordSetSw);
|
|
OnCoordSetSw.s = IPS_OK;
|
|
IDSetSwitch(&OnCoordSetSw, NULL);
|
|
}
|
|
|
|
// Parking
|
|
if (!strcmp(name, ParkSP.name))
|
|
{
|
|
if (checkPower(&ParkSP))
|
|
return;
|
|
|
|
ParkSP.s = IPS_IDLE;
|
|
|
|
|
|
if ( (err = getSDTime(&STime[0].value)) < 0)
|
|
{
|
|
handleError(&ParkSP, err, "Get siderial time");
|
|
return;
|
|
}
|
|
|
|
if (AlignmentS[0].s == ISS_ON)
|
|
{
|
|
targetRA = STime[0].value;
|
|
targetDEC = 0;
|
|
setObjectRA(targetRA);
|
|
setObjectDEC(targetDEC);
|
|
}
|
|
|
|
else if (AlignmentS[1].s == ISS_ON)
|
|
{
|
|
targetRA = calculateRA(STime[0].value);
|
|
targetDEC = calculateDec(geo[0].value, STime[0].value);
|
|
setObjectRA(targetRA);
|
|
setObjectDEC(targetDEC);
|
|
IDLog("Parking the scope in AltAz (0,0) which corresponds to (RA,DEC) of (%g,%g)\n", targetRA, targetDEC);
|
|
IDLog("Current Sidereal time is: %g\n", STime[0].value);
|
|
IDSetNumber(&SDTime, NULL);
|
|
}
|
|
else
|
|
{
|
|
IDSetSwitch(&ParkSP, "You can only park the telescope in Polar or AltAz modes.");
|
|
return;
|
|
}
|
|
|
|
IDSetNumber(&SDTime, NULL);
|
|
|
|
currentSet = LX200_PARK;
|
|
handleCoordSet();
|
|
}
|
|
|
|
// Abort Slew
|
|
if (!strcmp (name, abortSlewSw.name))
|
|
{
|
|
if (checkPower(&abortSlewSw))
|
|
{
|
|
abortSlewSw.s = IPS_IDLE;
|
|
IDSetSwitch(&abortSlewSw, NULL);
|
|
return;
|
|
}
|
|
|
|
IUResetSwitches(&abortSlewSw);
|
|
abortSlew();
|
|
|
|
if (eqNum.s == IPS_BUSY)
|
|
{
|
|
abortSlewSw.s = IPS_OK;
|
|
eqNum.s = IPS_IDLE;
|
|
IDSetSwitch(&abortSlewSw, "Slew aborted.");
|
|
IDSetNumber(&eqNum, NULL);
|
|
}
|
|
else if (MovementSw.s == IPS_BUSY)
|
|
{
|
|
|
|
for (int i=0; i < 4; i++)
|
|
lastMove[i] = 0;
|
|
|
|
MovementSw.s = IPS_IDLE;
|
|
abortSlewSw.s = IPS_OK;
|
|
eqNum.s = IPS_IDLE;
|
|
IUResetSwitches(&MovementSw);
|
|
IUResetSwitches(&abortSlewSw);
|
|
IDSetSwitch(&abortSlewSw, "Slew aborted.");
|
|
IDSetSwitch(&MovementSw, NULL);
|
|
IDSetNumber(&eqNum, NULL);
|
|
}
|
|
else
|
|
{
|
|
IUResetSwitches(&MovementSw);
|
|
abortSlewSw.s = IPS_OK;
|
|
IDSetSwitch(&abortSlewSw, NULL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Alignment
|
|
if (!strcmp (name, AlignmentSw.name))
|
|
{
|
|
if (checkPower(&AlignmentSw))
|
|
return;
|
|
|
|
IUResetSwitches(&AlignmentSw);
|
|
IUUpdateSwitches(&AlignmentSw, states, names, n);
|
|
index = getOnSwitch(&AlignmentSw);
|
|
|
|
if ( ( err = setAlignmentMode(index) < 0) )
|
|
{
|
|
handleError(&AlignmentSw, err, "Setting alignment");
|
|
return;
|
|
}
|
|
|
|
AlignmentSw.s = IPS_OK;
|
|
IDSetSwitch (&AlignmentSw, NULL);
|
|
return;
|
|
|
|
}
|
|
|
|
// Sites
|
|
if (!strcmp (name, SitesSw.name))
|
|
{
|
|
if (checkPower(&SitesSw))
|
|
return;
|
|
|
|
IUResetSwitches(&SitesSw);
|
|
IUUpdateSwitches(&SitesSw, states, names, n);
|
|
currentSiteNum = getOnSwitch(&SitesSw) + 1;
|
|
|
|
if ( ( err = selectSite(currentSiteNum) < 0) )
|
|
{
|
|
handleError(&SitesSw, err, "Selecting sites");
|
|
return;
|
|
}
|
|
|
|
if ( ( err = getSiteLatitude(&dd, &mm) < 0))
|
|
{
|
|
handleError(&SitesSw, err, "Selecting sites");
|
|
return;
|
|
}
|
|
|
|
if (dd > 0) geoNum.np[0].value = dd + mm / 60.0;
|
|
else geoNum.np[0].value = dd - mm / 60.0;
|
|
|
|
if ( ( err = getSiteLongitude(&dd, &mm) < 0))
|
|
{
|
|
handleError(&SitesSw, err, "Selecting sites");
|
|
return;
|
|
}
|
|
|
|
if (dd > 0) geoNum.np[1].value = 360.0 - (dd + mm / 60.0);
|
|
else geoNum.np[1].value = (dd - mm / 60.0) * -1.0;
|
|
|
|
getSiteName( SiteName.tp[0].text, currentSiteNum);
|
|
|
|
IDLog("Selecting site %d\n", currentSiteNum);
|
|
|
|
geoNum.s = SiteName.s = SitesSw.s = IPS_OK;
|
|
|
|
IDSetNumber (&geoNum, NULL);
|
|
IDSetText (&SiteName, NULL);
|
|
IDSetSwitch (&SitesSw, NULL);
|
|
return;
|
|
}
|
|
|
|
// Focus Motion
|
|
if (!strcmp (name, FocusMotionSw.name))
|
|
{
|
|
if (checkPower(&FocusMotionSw))
|
|
return;
|
|
|
|
IUResetSwitches(&FocusMotionSw);
|
|
|
|
// If speed is "halt"
|
|
if (FocusSpeedN[0].value == 0)
|
|
{
|
|
FocusMotionSw.s = IPS_IDLE;
|
|
IDSetSwitch(&FocusMotionSw, NULL);
|
|
return;
|
|
}
|
|
|
|
IUUpdateSwitches(&FocusMotionSw, states, names, n);
|
|
index = getOnSwitch(&FocusMotionSw);
|
|
|
|
|
|
if ( ( err = setFocuserMotion(index) < 0) )
|
|
{
|
|
handleError(&FocusMotionSw, err, "Setting focuser speed");
|
|
return;
|
|
}
|
|
|
|
|
|
FocusMotionSw.s = IPS_BUSY;
|
|
|
|
// with a timer
|
|
if (FocusTimerN[0].value > 0)
|
|
FocusTimerNP.s = IPS_BUSY;
|
|
|
|
IDSetSwitch(&FocusMotionSw, NULL);
|
|
return;
|
|
}
|
|
|
|
// Slew mode
|
|
if (!strcmp (name, SlewModeSw.name))
|
|
{
|
|
if (checkPower(&SlewModeSw))
|
|
return;
|
|
|
|
IUResetSwitches(&SlewModeSw);
|
|
IUUpdateSwitches(&SlewModeSw, states, names, n);
|
|
index = getOnSwitch(&SlewModeSw);
|
|
|
|
if ( ( err = setSlewMode(index) < 0) )
|
|
{
|
|
handleError(&SlewModeSw, err, "Setting slew mode");
|
|
return;
|
|
}
|
|
|
|
SlewModeSw.s = IPS_OK;
|
|
IDSetSwitch(&SlewModeSw, NULL);
|
|
return;
|
|
}
|
|
|
|
// Movement
|
|
if (!strcmp (name, MovementSw.name))
|
|
{
|
|
if (checkPower(&MovementSw))
|
|
return;
|
|
|
|
index = -1;
|
|
IUUpdateSwitches(&MovementSw, states, names, n);
|
|
swp = IUFindSwitch(&MovementSw, names[0]);
|
|
|
|
if (!swp)
|
|
{
|
|
abortSlew();
|
|
IUResetSwitches(&MovementSw);
|
|
MovementSw.s = IPS_IDLE;
|
|
IDSetSwitch(&MovementSw, NULL);
|
|
}
|
|
|
|
if (swp == &MovementS[0]) index = 0;
|
|
else if (swp == &MovementS[1]) index = 1;
|
|
else if (swp == &MovementS[2]) index = 2;
|
|
else index = 3;
|
|
|
|
lastMove[index] = lastMove[index] == 0 ? 1 : 0;
|
|
if (lastMove[index] == 0)
|
|
MovementS[index].s = ISS_OFF;
|
|
|
|
// North/South movement is illegal
|
|
if (lastMove[LX200_NORTH] && lastMove[LX200_SOUTH])
|
|
{
|
|
abortSlew();
|
|
for (int i=0; i < 4; i++)
|
|
lastMove[i] = 0;
|
|
|
|
IUResetSwitches(&MovementSw);
|
|
MovementSw.s = IPS_IDLE;
|
|
IDSetSwitch(&MovementSw, "Slew aborted.");
|
|
return;
|
|
}
|
|
|
|
// East/West movement is illegal
|
|
if (lastMove[LX200_EAST] && lastMove[LX200_WEST])
|
|
{
|
|
abortSlew();
|
|
for (int i=0; i < 4; i++)
|
|
lastMove[i] = 0;
|
|
|
|
IUResetSwitches(&MovementSw);
|
|
MovementSw.s = IPS_IDLE;
|
|
IDSetSwitch(&MovementSw, "Slew aborted.");
|
|
return;
|
|
}
|
|
|
|
IDLog("We have switch %d \n ", index);
|
|
IDLog("NORTH: %d -- WEST: %d -- EAST: %d -- SOUTH %d\n", lastMove[0], lastMove[1], lastMove[2], lastMove[3]);
|
|
|
|
if (lastMove[index] == 1)
|
|
{
|
|
IDLog("issuing a move command\n");
|
|
if ( ( err = MoveTo(index) < 0) )
|
|
{
|
|
handleError(&MovementSw, err, "Setting motion direction");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
HaltMovement(index);
|
|
|
|
if (!lastMove[0] && !lastMove[1] && !lastMove[2] && !lastMove[3])
|
|
MovementSw.s = IPS_IDLE;
|
|
|
|
if (lastMove[index] == 0)
|
|
IDSetSwitch(&MovementSw, "Moving toward %s aborted.", Direction[index]);
|
|
else
|
|
{
|
|
MovementSw.s = IPS_BUSY;
|
|
if (lastMove[LX200_NORTH] && lastMove[LX200_WEST])
|
|
strcpy(combinedDir, "North West");
|
|
else if (lastMove[LX200_NORTH] && lastMove[LX200_EAST])
|
|
strcpy(combinedDir, "North East");
|
|
else if (lastMove[LX200_SOUTH] && lastMove[LX200_WEST])
|
|
strcpy(combinedDir, "South West");
|
|
else if (lastMove[LX200_SOUTH] && lastMove[LX200_EAST])
|
|
strcpy(combinedDir, "South East");
|
|
else
|
|
strcpy(combinedDir, Direction[index]);
|
|
|
|
IDSetSwitch(&MovementSw, "Moving %s...", combinedDir);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Tracking mode
|
|
if (!strcmp (name, TrackModeSw.name))
|
|
{
|
|
if (checkPower(&TrackModeSw))
|
|
return;
|
|
|
|
IUResetSwitches(&TrackModeSw);
|
|
IUUpdateSwitches(&TrackModeSw, states, names, n);
|
|
trackingMode = getOnSwitch(&TrackModeSw);
|
|
|
|
if ( ( err = selectTrackingMode(trackingMode) < 0) )
|
|
{
|
|
handleError(&TrackModeSw, err, "Setting tracking mode.");
|
|
return;
|
|
}
|
|
|
|
getTrackFreq(&TrackFreq[0].value);
|
|
TrackModeSw.s = IPS_OK;
|
|
IDSetNumber(&TrackingFreq, NULL);
|
|
IDSetSwitch(&TrackModeSw, NULL);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
void LX200Generic::handleError(ISwitchVectorProperty *svp, int err, const char *msg)
|
|
{
|
|
|
|
svp->s = IPS_ALERT;
|
|
|
|
/* First check to see if the telescope is connected */
|
|
if (testTelescope())
|
|
{
|
|
/* The telescope is off locally */
|
|
PowerS[0].s = ISS_OFF;
|
|
PowerS[1].s = ISS_ON;
|
|
PowerSP.s = IPS_BUSY;
|
|
IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
|
|
|
|
IDSetSwitch(svp, NULL);
|
|
IEAddTimer(10000, retryConnection, NULL);
|
|
return;
|
|
}
|
|
|
|
/* If the error is a time out, then the device doesn't support this property or busy*/
|
|
if (err == -2)
|
|
{
|
|
svp->s = IPS_ALERT;
|
|
IDSetSwitch(svp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
|
|
}
|
|
else
|
|
/* Changing property failed, user should retry. */
|
|
IDSetSwitch( svp , "%s failed.", msg);
|
|
|
|
fault = true;
|
|
}
|
|
|
|
void LX200Generic::handleError(INumberVectorProperty *nvp, int err, const char *msg)
|
|
{
|
|
|
|
nvp->s = IPS_ALERT;
|
|
|
|
/* First check to see if the telescope is connected */
|
|
if (testTelescope())
|
|
{
|
|
/* The telescope is off locally */
|
|
PowerS[0].s = ISS_OFF;
|
|
PowerS[1].s = ISS_ON;
|
|
PowerSP.s = IPS_BUSY;
|
|
IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
|
|
|
|
IDSetNumber(nvp, NULL);
|
|
IEAddTimer(10000, retryConnection, NULL);
|
|
return;
|
|
}
|
|
|
|
/* If the error is a time out, then the device doesn't support this property */
|
|
if (err == -2)
|
|
{
|
|
nvp->s = IPS_ALERT;
|
|
IDSetNumber(nvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
|
|
}
|
|
else
|
|
/* Changing property failed, user should retry. */
|
|
IDSetNumber( nvp , "%s failed.", msg);
|
|
|
|
fault = true;
|
|
}
|
|
|
|
void LX200Generic::handleError(ITextVectorProperty *tvp, int err, const char *msg)
|
|
{
|
|
|
|
tvp->s = IPS_ALERT;
|
|
|
|
/* First check to see if the telescope is connected */
|
|
if (testTelescope())
|
|
{
|
|
/* The telescope is off locally */
|
|
PowerS[0].s = ISS_OFF;
|
|
PowerS[1].s = ISS_ON;
|
|
PowerSP.s = IPS_BUSY;
|
|
IDSetSwitch(&PowerSP, "Telescope is not responding to commands, will retry in 10 seconds.");
|
|
|
|
IDSetText(tvp, NULL);
|
|
IEAddTimer(10000, retryConnection, NULL);
|
|
return;
|
|
}
|
|
|
|
/* If the error is a time out, then the device doesn't support this property */
|
|
if (err == -2)
|
|
{
|
|
tvp->s = IPS_ALERT;
|
|
IDSetText(tvp, "Device timed out. Current device may be busy or does not support %s. Will retry again.", msg);
|
|
}
|
|
|
|
else
|
|
/* Changing property failed, user should retry. */
|
|
IDSetText( tvp , "%s failed.", msg);
|
|
|
|
fault = true;
|
|
}
|
|
|
|
void LX200Generic::correctFault()
|
|
{
|
|
|
|
fault = false;
|
|
IDMessage(thisDevice, "Telescope is online.");
|
|
|
|
}
|
|
|
|
bool LX200Generic::isTelescopeOn(void)
|
|
{
|
|
if (simulation) return true;
|
|
|
|
return (PowerSP.sp[0].s == ISS_ON);
|
|
}
|
|
|
|
static void retryConnection(void * p)
|
|
{
|
|
p=p;
|
|
|
|
if (testTelescope())
|
|
{
|
|
PowerSP.s = IPS_IDLE;
|
|
IDSetSwitch(&PowerSP, "The connection to the telescope is lost.");
|
|
return;
|
|
}
|
|
|
|
PowerS[0].s = ISS_ON;
|
|
PowerS[1].s = ISS_OFF;
|
|
PowerSP.s = IPS_OK;
|
|
|
|
IDSetSwitch(&PowerSP, "The connection to the telescope has been resumed.");
|
|
|
|
}
|
|
|
|
void LX200Generic::ISPoll()
|
|
{
|
|
double dx, dy;
|
|
/*static int okCounter = 3;*/
|
|
int err=0;
|
|
|
|
if (!isTelescopeOn())
|
|
return;
|
|
|
|
switch (eqNum.s)
|
|
{
|
|
case IPS_IDLE:
|
|
getLX200RA(¤tRA);
|
|
getLX200DEC(¤tDEC);
|
|
|
|
if ( fabs (currentRA - lastRA) > 0.01 || fabs (currentDEC - lastDEC) > 0.01)
|
|
{
|
|
eqNum.np[0].value = lastRA = currentRA;
|
|
eqNum.np[1].value = lastDEC = currentDEC;
|
|
IDSetNumber (&eqNum, NULL);
|
|
}
|
|
break;
|
|
|
|
case IPS_BUSY:
|
|
getLX200RA(¤tRA);
|
|
getLX200DEC(¤tDEC);
|
|
dx = targetRA - currentRA;
|
|
dy = targetDEC - currentDEC;
|
|
|
|
IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA);
|
|
IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC);
|
|
|
|
eqNum.np[0].value = currentRA;
|
|
eqNum.np[1].value = currentDEC;
|
|
|
|
// Wait until acknowledged or within threshold
|
|
if (fabs(dx) <= RA_THRESHOLD && fabs(dy) <= DEC_THRESHOLD)
|
|
{
|
|
/* Don't set current to target. This might leave residual cumulative error
|
|
currentRA = targetRA;
|
|
currentDEC = targetDEC;
|
|
*/
|
|
|
|
eqNum.np[0].value = lastRA = currentRA;
|
|
eqNum.np[1].value = lastDEC = currentDEC;
|
|
IUResetSwitches(&OnCoordSetSw);
|
|
OnCoordSetSw.s = IPS_OK;
|
|
eqNum.s = IPS_OK;
|
|
IDSetNumber (&eqNum, NULL);
|
|
|
|
switch (currentSet)
|
|
{
|
|
case LX200_SLEW:
|
|
OnCoordSetSw.sp[0].s = ISS_ON;
|
|
IDSetSwitch (&OnCoordSetSw, "Slew is complete.");
|
|
break;
|
|
|
|
case LX200_TRACK:
|
|
OnCoordSetSw.sp[1].s = ISS_ON;
|
|
IDSetSwitch (&OnCoordSetSw, "Slew is complete. Tracking...");
|
|
break;
|
|
|
|
case LX200_SYNC:
|
|
break;
|
|
|
|
case LX200_PARK:
|
|
if (setSlewMode(LX200_SLEW_GUIDE) < 0)
|
|
{
|
|
handleError(&eqNum, err, "Setting slew mode");
|
|
return;
|
|
}
|
|
|
|
IUResetSwitches(&SlewModeSw);
|
|
SlewModeS[LX200_SLEW_GUIDE].s = ISS_ON;
|
|
IDSetSwitch(&SlewModeSw, NULL);
|
|
|
|
MoveTo(LX200_EAST);
|
|
IUResetSwitches(&MovementSw);
|
|
MovementS[LX200_EAST].s = ISS_ON;
|
|
MovementSw.s = IPS_BUSY;
|
|
for (int i=0; i < 4; i++)
|
|
lastMove[i] = 0;
|
|
lastMove[LX200_EAST] = 1;
|
|
IDSetSwitch(&MovementSw, NULL);
|
|
|
|
ParkSP.s = IPS_OK;
|
|
IDSetSwitch (&ParkSP, "Park is complete. Turn off the telescope now.");
|
|
break;
|
|
}
|
|
|
|
} else
|
|
{
|
|
eqNum.np[0].value = currentRA;
|
|
eqNum.np[1].value = currentDEC;
|
|
IDSetNumber (&eqNum, NULL);
|
|
}
|
|
break;
|
|
|
|
case IPS_OK:
|
|
|
|
/*if (--okCounter >= 0)
|
|
break;
|
|
|
|
// Activate again in 3 seconds
|
|
okCounter = 3;*/
|
|
|
|
if ( (err = getLX200RA(¤tRA)) < 0 || (err = getLX200DEC(¤tDEC)) < 0)
|
|
{
|
|
handleError(&eqNum, err, "Getting RA/DEC");
|
|
return;
|
|
}
|
|
|
|
if (fault)
|
|
correctFault();
|
|
|
|
if ( (currentRA != lastRA) || (currentDEC != lastDEC))
|
|
{
|
|
eqNum.np[0].value = lastRA = currentRA;
|
|
eqNum.np[1].value = lastDEC = currentDEC;
|
|
IDSetNumber (&eqNum, NULL);
|
|
}
|
|
break;
|
|
|
|
|
|
case IPS_ALERT:
|
|
break;
|
|
}
|
|
|
|
switch (MovementSw.s)
|
|
{
|
|
case IPS_IDLE:
|
|
break;
|
|
case IPS_BUSY:
|
|
getLX200RA(¤tRA);
|
|
getLX200DEC(¤tDEC);
|
|
/*apparentCoord( JD, (double) J2000, ¤tRA, ¤tDEC);*/
|
|
eqNum.np[0].value = currentRA;
|
|
eqNum.np[1].value = currentDEC;
|
|
|
|
IDSetNumber (&eqNum, NULL);
|
|
break;
|
|
case IPS_OK:
|
|
break;
|
|
case IPS_ALERT:
|
|
break;
|
|
}
|
|
|
|
switch (FocusTimerNP.s)
|
|
{
|
|
case IPS_IDLE:
|
|
break;
|
|
|
|
case IPS_BUSY:
|
|
FocusTimerN[0].value--;
|
|
|
|
if (FocusTimerN[0].value == 0)
|
|
{
|
|
|
|
if ( ( err = setFocuserSpeedMode(0) < 0) )
|
|
{
|
|
handleError(&FocusSpeedNP, err, "setting focuser speed mode");
|
|
IDLog("Error setting focuser speed mode\n");
|
|
return;
|
|
}
|
|
|
|
|
|
FocusMotionSw.s = IPS_IDLE;
|
|
FocusTimerNP.s = IPS_OK;
|
|
FocusSpeedNP.s = IPS_OK;
|
|
|
|
IUResetSwitches(&FocusMotionSw);
|
|
FocusSpeedN[0].value = 0;
|
|
|
|
IDSetNumber(&FocusSpeedNP, NULL);
|
|
IDSetSwitch(&FocusMotionSw, NULL);
|
|
}
|
|
|
|
IDSetNumber(&FocusTimerNP, NULL);
|
|
break;
|
|
|
|
case IPS_OK:
|
|
break;
|
|
|
|
case IPS_ALERT:
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void LX200Generic::getBasicData()
|
|
{
|
|
|
|
int err;
|
|
struct tm *timep;
|
|
time_t ut;
|
|
time (&ut);
|
|
timep = gmtime (&ut);
|
|
strftime (Time.tp[0].text, sizeof(Time.tp[0].text), "%Y-%m-%dT%H:%M:%S", timep);
|
|
|
|
IDLog("PC UTC time is %s\n", Time.tp[0].text);
|
|
|
|
getAlignment();
|
|
|
|
checkLX200Format();
|
|
|
|
if ( (err = getTimeFormat(&timeFormat)) < 0)
|
|
IDMessage(thisDevice, "Failed to retrieve time format from device.");
|
|
else
|
|
{
|
|
timeFormat = (timeFormat == 24) ? LX200_24 : LX200_AM;
|
|
// We always do 24 hours
|
|
if (timeFormat != LX200_24)
|
|
toggleTimeFormat();
|
|
}
|
|
|
|
getLX200RA(&targetRA);
|
|
getLX200DEC(&targetDEC);
|
|
|
|
eqNum.np[0].value = targetRA;
|
|
eqNum.np[1].value = targetDEC;
|
|
|
|
IDSetNumber (&eqNum, NULL);
|
|
|
|
SiteNameT[0].text = new char[64];
|
|
|
|
if ( (err = getSiteName(SiteNameT[0].text, currentSiteNum)) < 0)
|
|
IDMessage(thisDevice, "Failed to get site name from device");
|
|
else
|
|
IDSetText (&SiteName, NULL);
|
|
|
|
if ( (err = getTrackFreq(&TrackFreq[0].value)) < 0)
|
|
IDMessage(thisDevice, "Failed to get tracking frequency from device.");
|
|
else
|
|
IDSetNumber (&TrackingFreq, NULL);
|
|
|
|
|
|
updateLocation();
|
|
updateTime();
|
|
|
|
}
|
|
|
|
int LX200Generic::handleCoordSet()
|
|
{
|
|
|
|
int err;
|
|
char syncString[256];
|
|
char RAStr[32], DecStr[32];
|
|
double dx, dy;
|
|
|
|
IDLog("In Handle Coord Set()\n");
|
|
|
|
switch (currentSet)
|
|
{
|
|
|
|
// Slew
|
|
case LX200_SLEW:
|
|
lastSet = LX200_SLEW;
|
|
if (eqNum.s == IPS_BUSY)
|
|
{
|
|
IDLog("Aboring Slew\n");
|
|
abortSlew();
|
|
|
|
// sleep for 100 mseconds
|
|
usleep(100000);
|
|
}
|
|
|
|
if ((err = Slew()))
|
|
{
|
|
slewError(err);
|
|
return (-1);
|
|
}
|
|
|
|
eqNum.s = IPS_BUSY;
|
|
fs_sexa(RAStr, targetRA, 2, 3600);
|
|
fs_sexa(DecStr, targetDEC, 2, 3600);
|
|
IDSetNumber(&eqNum, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
|
|
IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
|
|
break;
|
|
|
|
// Track
|
|
case LX200_TRACK:
|
|
IDLog("We're in LX200_TRACK\n");
|
|
if (eqNum.s == IPS_BUSY)
|
|
{
|
|
IDLog("Aboring Slew\n");
|
|
abortSlew();
|
|
|
|
// sleep for 200 mseconds
|
|
usleep(200000);
|
|
}
|
|
|
|
dx = fabs ( targetRA - currentRA );
|
|
dy = fabs (targetDEC - currentDEC);
|
|
|
|
|
|
if (dx >= TRACKING_THRESHOLD || dy >= TRACKING_THRESHOLD)
|
|
{
|
|
IDLog("Exceeded Tracking threshold, will attempt to slew to the new target.\n");
|
|
IDLog("targetRA is %g, currentRA is %g\n", targetRA, currentRA);
|
|
IDLog("targetDEC is %g, currentDEC is %g\n*************************\n", targetDEC, currentDEC);
|
|
|
|
if ((err = Slew()))
|
|
{
|
|
slewError(err);
|
|
return (-1);
|
|
}
|
|
|
|
fs_sexa(RAStr, targetRA, 2, 3600);
|
|
fs_sexa(DecStr, targetDEC, 2, 3600);
|
|
eqNum.s = IPS_BUSY;
|
|
IDSetNumber(&eqNum, "Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
|
|
IDLog("Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
|
|
}
|
|
else
|
|
{
|
|
IDLog("Tracking called, but tracking threshold not reached yet.\n");
|
|
eqNum.s = IPS_OK;
|
|
eqNum.np[0].value = currentRA;
|
|
eqNum.np[1].value = currentDEC;
|
|
|
|
if (lastSet != LX200_TRACK)
|
|
IDSetNumber(&eqNum, "Tracking...");
|
|
else
|
|
IDSetNumber(&eqNum, NULL);
|
|
}
|
|
lastSet = LX200_TRACK;
|
|
break;
|
|
|
|
// Sync
|
|
case LX200_SYNC:
|
|
lastSet = LX200_SYNC;
|
|
eqNum.s = IPS_IDLE;
|
|
|
|
if ( ( err = Sync(syncString) < 0) )
|
|
{
|
|
IDSetNumber( &eqNum , "Synchronization failed.");
|
|
return (-1);
|
|
}
|
|
|
|
eqNum.s = IPS_OK;
|
|
IDLog("Synchronization successful %s\n", syncString);
|
|
IDSetNumber(&eqNum, "Synchronization successful.");
|
|
break;
|
|
|
|
// PARK
|
|
// Set RA to LST and DEC to 0 degrees, slew, then change to 'guide' slew after slew is complete.
|
|
case LX200_PARK:
|
|
if (eqNum.s == IPS_BUSY)
|
|
{
|
|
abortSlew();
|
|
|
|
// sleep for 200 mseconds
|
|
usleep(200000);
|
|
}
|
|
|
|
if ((err = Slew()))
|
|
{
|
|
slewError(err);
|
|
return (-1);
|
|
}
|
|
|
|
ParkSP.s = IPS_BUSY;
|
|
eqNum.s = IPS_BUSY;
|
|
IDSetNumber(&eqNum, NULL);
|
|
IDSetSwitch(&ParkSP, "The telescope is slewing to park position. Turn off the telescope after park is complete.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
int LX200Generic::getOnSwitch(ISwitchVectorProperty *sp)
|
|
{
|
|
for (int i=0; i < sp->nsp ; i++)
|
|
if (sp->sp[i].s == ISS_ON)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int LX200Generic::checkPower(ISwitchVectorProperty *sp)
|
|
{
|
|
if (simulation) return 0;
|
|
|
|
if (PowerSP.s != IPS_OK)
|
|
{
|
|
if (!strcmp(sp->label, ""))
|
|
IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->name);
|
|
else
|
|
IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", sp->label);
|
|
|
|
sp->s = IPS_IDLE;
|
|
IDSetSwitch(sp, NULL);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int LX200Generic::checkPower(INumberVectorProperty *np)
|
|
{
|
|
if (simulation) return 0;
|
|
|
|
if (PowerSP.s != IPS_OK)
|
|
{
|
|
|
|
if (!strcmp(np->label, ""))
|
|
IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->name);
|
|
else
|
|
IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", np->label);
|
|
|
|
np->s = IPS_IDLE;
|
|
IDSetNumber(np, NULL);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int LX200Generic::checkPower(ITextVectorProperty *tp)
|
|
{
|
|
|
|
if (simulation) return 0;
|
|
|
|
if (PowerSP.s != IPS_OK)
|
|
{
|
|
if (!strcmp(tp->label, ""))
|
|
IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->name);
|
|
else
|
|
IDMessage (thisDevice, "Cannot change property %s while the telescope is offline.", tp->label);
|
|
|
|
tp->s = IPS_IDLE;
|
|
IDSetText(tp, NULL);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
void LX200Generic::powerTelescope()
|
|
{
|
|
switch (PowerSP.sp[0].s)
|
|
{
|
|
case ISS_ON:
|
|
|
|
if (simulation)
|
|
{
|
|
PowerSP.s = IPS_OK;
|
|
IDSetSwitch (&PowerSP, "Simulated telescope is online.");
|
|
updateTime();
|
|
return;
|
|
}
|
|
|
|
if (Connect(Port.tp[0].text))
|
|
{
|
|
PowerS[0].s = ISS_OFF;
|
|
PowerS[1].s = ISS_ON;
|
|
IDSetSwitch (&PowerSP, "Error connecting to port %s\n", Port.tp[0].text);
|
|
return;
|
|
}
|
|
if (testTelescope())
|
|
{
|
|
PowerS[0].s = ISS_OFF;
|
|
PowerS[1].s = ISS_ON;
|
|
IDSetSwitch (&PowerSP, "Error connecting to Telescope. Telescope is offline.");
|
|
return;
|
|
}
|
|
|
|
IDLog("telescope test successfful\n");
|
|
PowerSP.s = IPS_OK;
|
|
IDSetSwitch (&PowerSP, "Telescope is online. Retrieving basic data...");
|
|
getBasicData();
|
|
break;
|
|
|
|
case ISS_OFF:
|
|
PowerS[0].s = ISS_OFF;
|
|
PowerS[1].s = ISS_ON;
|
|
PowerSP.s = IPS_IDLE;
|
|
IDSetSwitch (&PowerSP, "Telescope is offline.");
|
|
IDLog("Telescope is offline.");
|
|
Disconnect();
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void LX200Generic::slewError(int slewCode)
|
|
{
|
|
OnCoordSetSw.s = IPS_IDLE;
|
|
ParkSP.s = IPS_IDLE;
|
|
IDSetSwitch(&ParkSP, NULL);
|
|
|
|
if (slewCode == 1)
|
|
IDSetSwitch (&OnCoordSetSw, "Object below horizon.");
|
|
else if (slewCode == 2)
|
|
IDSetSwitch (&OnCoordSetSw, "Object below the minimum elevation limit.");
|
|
else
|
|
IDSetSwitch (&OnCoordSetSw, "Slew failed.");
|
|
|
|
|
|
}
|
|
|
|
void LX200Generic::getAlignment()
|
|
{
|
|
|
|
if (PowerSP.s != IPS_OK)
|
|
return;
|
|
|
|
signed char align = ACK();
|
|
if (align < 0)
|
|
{
|
|
IDSetSwitch (&AlignmentSw, "Failed to get telescope alignment.");
|
|
return;
|
|
}
|
|
|
|
AlignmentS[0].s = ISS_OFF;
|
|
AlignmentS[1].s = ISS_OFF;
|
|
AlignmentS[2].s = ISS_OFF;
|
|
|
|
switch (align)
|
|
{
|
|
case 'P': AlignmentS[0].s = ISS_ON;
|
|
break;
|
|
case 'A': AlignmentS[1].s = ISS_ON;
|
|
break;
|
|
case 'L': AlignmentS[2].s = ISS_ON;
|
|
break;
|
|
}
|
|
|
|
AlignmentSw.s = IPS_OK;
|
|
IDSetSwitch (&AlignmentSw, NULL);
|
|
IDLog("ACK success %c\n", align);
|
|
}
|
|
|
|
void LX200Generic::enableSimulation(bool enable)
|
|
{
|
|
simulation = enable;
|
|
|
|
if (simulation)
|
|
IDLog("Warning: Simulation is activated.\n");
|
|
else
|
|
IDLog("Simulation is disabled.\n");
|
|
}
|
|
|
|
void LX200Generic::updateTime()
|
|
{
|
|
|
|
char cdate[32];
|
|
double ctime;
|
|
int h, m, s;
|
|
int day, month, year, result;
|
|
int UTC_h, UTC_month, UTC_year, UTC_day, daysInFeb;
|
|
bool leapYear;
|
|
|
|
tzset();
|
|
|
|
UTCOffset = timezoneOffset();
|
|
IDLog("Daylight: %s - TimeZone: %g\n", daylight ? "Yes" : "No", UTCOffset);
|
|
|
|
|
|
if (simulation)
|
|
{
|
|
sprintf(UTC[0].text, "%d-%02d-%02dT%02d:%02d:%02d", 1979, 6, 25, 3, 30, 30);
|
|
IDLog("Telescope ISO date and time: %s\n", UTC[0].text);
|
|
IDSetText(&Time, NULL);
|
|
return;
|
|
}
|
|
|
|
getLocalTime24(&ctime);
|
|
getSexComponents(ctime, &h, &m, &s);
|
|
|
|
UTC_h = h;
|
|
|
|
if ( (result = getSDTime(&STime[0].value)) < 0)
|
|
IDMessage(thisDevice, "Failed to retrieve siderial time from device.");
|
|
|
|
getCalenderDate(cdate);
|
|
|
|
result = sscanf(cdate, "%d/%d/%d", &year, &month, &day);
|
|
if (result != 3) return;
|
|
|
|
if (year % 4 == 0)
|
|
{
|
|
if (year % 100 == 0)
|
|
{
|
|
if (year % 400 == 0)
|
|
leapYear = true;
|
|
else
|
|
leapYear = false;
|
|
}
|
|
else
|
|
leapYear = true;
|
|
}
|
|
else
|
|
leapYear = false;
|
|
|
|
daysInFeb = leapYear ? 29 : 28;
|
|
|
|
UTC_year = year;
|
|
UTC_month = month;
|
|
UTC_day = day;
|
|
|
|
IDLog("day: %d - month %d - year: %d\n", day, month, year);
|
|
|
|
// we'll have to convert telescope time to UTC manually starting from hour up
|
|
// seems like a stupid way to do it.. oh well
|
|
UTC_h = (int) UTCOffset + h;
|
|
if (UTC_h < 0)
|
|
{
|
|
UTC_h += 24;
|
|
UTC_day--;
|
|
}
|
|
else if (UTC_h > 24)
|
|
{
|
|
UTC_h -= 24;
|
|
UTC_day++;
|
|
}
|
|
|
|
switch (UTC_month)
|
|
{
|
|
case 1:
|
|
case 8:
|
|
if (UTC_day < 1)
|
|
{
|
|
UTC_day = 31;
|
|
UTC_month--;
|
|
}
|
|
else if (UTC_day > 31)
|
|
{
|
|
UTC_day = 1;
|
|
UTC_month++;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (UTC_day < 1)
|
|
{
|
|
UTC_day = 31;
|
|
UTC_month--;
|
|
}
|
|
else if (UTC_day > daysInFeb)
|
|
{
|
|
UTC_day = 1;
|
|
UTC_month++;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if (UTC_day < 1)
|
|
{
|
|
UTC_day = daysInFeb;
|
|
UTC_month--;
|
|
}
|
|
else if (UTC_day > 31)
|
|
{
|
|
UTC_day = 1;
|
|
UTC_month++;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
case 6:
|
|
case 9:
|
|
case 11:
|
|
if (UTC_day < 1)
|
|
{
|
|
UTC_day = 31;
|
|
UTC_month--;
|
|
}
|
|
else if (UTC_day > 30)
|
|
{
|
|
UTC_day = 1;
|
|
UTC_month++;
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
case 7:
|
|
case 10:
|
|
case 12:
|
|
if (UTC_day < 1)
|
|
{
|
|
UTC_day = 30;
|
|
UTC_month--;
|
|
}
|
|
else if (UTC_day > 31)
|
|
{
|
|
UTC_day = 1;
|
|
UTC_month++;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
if (UTC_month < 1)
|
|
{
|
|
UTC_month = 12;
|
|
UTC_year--;
|
|
}
|
|
else if (UTC_month > 12)
|
|
{
|
|
UTC_month = 1;
|
|
UTC_year++;
|
|
}
|
|
|
|
/* Format it into ISO 8601 */
|
|
sprintf(UTC[0].text, "%d-%02d-%02dT%02d:%02d:%02d", UTC_year, UTC_month, UTC_day, UTC_h, m, s);
|
|
|
|
IDLog("Local telescope time: %02d:%02d:%02d\n", h, m , s);
|
|
IDLog("Telescope SD Time is: %g\n", STime[0].value);
|
|
IDLog("UTC date and time: %s\n", UTC[0].text);
|
|
|
|
|
|
// Let's send everything to the client
|
|
IDSetText(&Time, NULL);
|
|
IDSetNumber(&SDTime, NULL);
|
|
|
|
}
|
|
|
|
void LX200Generic::updateLocation()
|
|
{
|
|
|
|
int dd = 0, mm = 0, err = 0;
|
|
|
|
if ( (err = getSiteLatitude(&dd, &mm)) < 0)
|
|
IDMessage(thisDevice, "Failed to get site latitude from device.");
|
|
else
|
|
{
|
|
if (dd > 0)
|
|
geoNum.np[0].value = dd + mm/60.0;
|
|
else
|
|
geoNum.np[0].value = dd - mm/60.0;
|
|
|
|
IDLog("Autostar Latitude: %d:%d\n", dd, mm);
|
|
IDLog("INDI Latitude: %g\n", geoNum.np[0].value);
|
|
}
|
|
|
|
if ( (err = getSiteLongitude(&dd, &mm)) < 0)
|
|
IDMessage(thisDevice, "Failed to get site longitude from device.");
|
|
else
|
|
{
|
|
if (dd > 0) geoNum.np[1].value = 360.0 - (dd + mm/60.0);
|
|
else geoNum.np[1].value = (dd - mm/60.0) * -1.0;
|
|
|
|
IDLog("Autostar Longitude: %d:%d\n", dd, mm);
|
|
IDLog("INDI Longitude: %g\n", geoNum.np[1].value);
|
|
}
|
|
|
|
IDSetNumber (&geoNum, NULL);
|
|
|
|
}
|
|
|
|
|