#if 0 LX200 Basic Driver Copyright (C) 2015 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 "lxd650.h" #include "indicom.h" #include "lx200driver.h" #include #include #include #include #include #include using namespace INDI::AlignmentSubsystem; /* Simulation Parameters */ #define SLEWRATE 1 /* slew rate, degrees/s */ #define SIDRATE 0.004178 /* sidereal rate, degrees/s */ /* Our telescope auto pointer */ static std::unique_ptr telescope(new LXD650()); /************************************************************************************** ** LX200 Basic constructor ***************************************************************************************/ LXD650::LXD650() { setVersion(2, 1); DBG_SCOPE = INDI::Logger::getInstance().addDebugLevel("Scope Verbose", "SCOPE"); SetTelescopeCapability(TELESCOPE_CAN_SYNC | TELESCOPE_CAN_GOTO | TELESCOPE_CAN_ABORT | TELESCOPE_HAS_LOCATION | TELESCOPE_HAS_TIME, 4); LOG_DEBUG("Initializing from LX200 Basic device..."); } /************************************************************************************** ** ***************************************************************************************/ void LXD650::debugTriggered(bool enable) { INDI_UNUSED(enable); setLX200Debug(getDeviceName(), DBG_SCOPE); } /************************************************************************************** ** ***************************************************************************************/ const char *LXD650::getDefaultName() { return "LXD-650 1697"; } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::initProperties() { /* Make sure to init parent properties first */ INDI::Telescope::initProperties(); setDriverInterface(getDriverInterface() | GUIDER_INTERFACE); // Slew threshold IUFillNumber(&SlewAccuracyN[0], "SlewRA", "RA (arcmin)", "%10.6m", 0., 60., 1., 3.0); IUFillNumber(&SlewAccuracyN[1], "SlewDEC", "Dec (arcmin)", "%10.6m", 0., 60., 1., 3.0); IUFillNumberVector(&SlewAccuracyNP, SlewAccuracyN, NARRAY(SlewAccuracyN), getDeviceName(), "Slew Accuracy", "", OPTIONS_TAB, IP_RW, 0, IPS_IDLE); LXDTrackModeSP[LXD_TRACK_SIDEREAL].fill("TRACK_SIDEREAL", "Sidereal", ISS_ON); LXDTrackModeSP[LXD_TRACK_SOLAR].fill("TRACK_SOLAR", "Solar", ISS_OFF); LXDTrackModeSP[LXD_TRACK_LUNAR].fill("TRACK_LUNAR", "Lunar", ISS_OFF); LXDTrackModeSP[LXD_TRACK_CUSTOM].fill("TRACK_CUSTOM", "Custom", ISS_OFF); LXDTrackModeSP.fill(getDeviceName(), "LXD_TRACK_MODE", "Track Mode", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE); IUFillNumber(&LXDTrackRateN, "LXD_TRACK_RATE_RA", "RA (arcsecs/s)", "%.6f", -16384.0, 16384.0, 0.000001, TRACKRATE_SIDEREAL); IUFillNumberVector(&LXDTrackRateNP, &LXDTrackRateN, 1, getDeviceName(), "LXD_TRACK_RATE", "Track Rates", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE); addAuxControls(); currentRA = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value); currentDEC = LocationN[LOCATION_LATITUDE].value > 0 ? 90 : -90; InitAlignmentProperties(this); getSwitch("ALIGNMENT_SUBSYSTEM_ACTIVE")->sp[0].s = ISS_ON; initGuiderProperties(getDeviceName(), GUIDE_TAB); // Rate rate GuideRateNP[AXIS_AZ].fill("GUIDE_RATE_WE", "W/E Rate", "%.f", 0, 1, .01, 0.5); GuideRateNP[AXIS_ALT].fill("GUIDE_RATE_NS", "N/S Rate", "%.f", 0, 1, .01, 0.5); GuideRateNP.fill(getDeviceName(), "GUIDE_RATE", "Guiding Rate", GUIDE_TAB, IP_RW, 0, IPS_IDLE); setDriverInterface(getDriverInterface() | GUIDER_INTERFACE); // SetTelescopeCapability(GetTelescopeCapability() | TELESCOPE_HAS_PIER_SIDE, 4); return true; } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::updateProperties() { INDI::Telescope::updateProperties(); if (isConnected()) { defineProperty(&SlewAccuracyNP); defineProperty(&LXDTrackModeSP); defineProperty(&LXDTrackRateNP); defineProperty(&GuideNSNP); defineProperty(&GuideWENP); defineProperty(&GuideRateNP); getBasicData(); } else { deleteProperty(GuideRateNP.getName()); deleteProperty(GuideWENP.name); deleteProperty(GuideNSNP.name); deleteProperty(LXDTrackRateNP.name); deleteProperty(LXDTrackModeSP.getName()); deleteProperty(SlewAccuracyNP.name); } return true; } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::Handshake() { if (getLX200RA(PortFD, ¤tRA) != 0) { LOG_ERROR("Error communication with telescope."); return false; } return true; } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::isSlewComplete() { const double dx = targetRA - currentRA; const double dy = targetDEC - currentDEC; return fabs(dx) <= (SlewAccuracyN[0].value / (900.0)) && fabs(dy) <= (SlewAccuracyN[1].value / 60.0); } /************************************************************************************** ** ***************************************************************************************/ void LXD650::show_alignment (const char *wot, double ra1, double dec1, double ra2, double dec2) { char ra1_str[32] = {0}; char dec1_str[32] = {0}; char ra2_str[32] = {0}; char dec2_str[32] = {0}; fs_sexa (ra1_str, ra1, 2, 3600); fs_sexa (dec1_str, dec1, 2, 3600); fs_sexa (ra2_str, ra2, 2, 3600); fs_sexa (dec2_str, dec2, 2, 3600); LOGF_INFO ("Mapping: %s RA %s DE %s => RA %s DE %s", wot, ra1_str, dec1_str, ra2_str, dec2_str); } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::ReadScopeStatus() { if (!isConnected()) return false; if (isSimulation()) { mountSim(); return true; } if (getLX200RA(PortFD, ¤tRA) < 0 || getLX200DEC(PortFD, ¤tDEC) < 0) { EqNP.s = IPS_ALERT; IDSetNumber(&EqNP, "Error reading RA/DEC."); return false; } int mode = IUFindOnSwitchIndex(&LXDTrackModeSP); if (mode != LXD_TRACK_CUSTOM) { double rate; if (!getTrackFreq(PortFD, &rate)) { LXDTrackRateN.value = rate * (TRACKRATE_SOLAR / 60.); IDSetNumber(&LXDTrackRateNP, nullptr); } } if (TrackState == SCOPE_SLEWING) { // Check if LX200 is done slewing if (isSlewComplete()) { TrackState = SCOPE_TRACKING; LOG_INFO("Slew is complete. Tracking..."); } } #if 0 NewRaDec(currentRA, currentDEC); #else double sky_RA, sky_DEC; if (!TelescopeEquatorialToSky(currentRA, currentDEC, sky_RA, sky_DEC)) { sky_RA = currentRA; sky_DEC = currentDEC; } NewRaDec(sky_RA, sky_DEC); show_alignment("Mount->Sky", currentRA, currentDEC, sky_RA, sky_DEC); #endif // setPierSide(PIER_UNKNOWN); // setPierSide(PIER_WEST); return true; } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::Goto(double r, double d) { #if 1 double mount_r, mount_d; if (!SkyToTelescopeEquatorial(r, d, mount_r, mount_d)) { mount_r = r; mount_d = d; } show_alignment("Sky->Mount", r, d, mount_r, mount_d); r = mount_r; d = mount_d; #endif targetRA = r; targetDEC = d; char RAStr[64] = {0}, DecStr[64] = {0}; fs_sexa(RAStr, targetRA, 2, 3600); fs_sexa(DecStr, targetDEC, 2, 3600); // If moving, let's stop it first. if (EqNP.s == IPS_BUSY) { if (!isSimulation() && abortSlew(PortFD) < 0) { AbortSP.s = IPS_ALERT; IDSetSwitch(&AbortSP, "Abort slew failed."); return false; } AbortSP.s = IPS_OK; EqNP.s = IPS_IDLE; IDSetSwitch(&AbortSP, "Slew aborted."); IDSetNumber(&EqNP, nullptr); // sleep for 100 mseconds usleep(100000); } if (!isSimulation()) { if (setObjectRA(PortFD, targetRA) < 0 || (setObjectDEC(PortFD, targetDEC)) < 0) { EqNP.s = IPS_ALERT; IDSetNumber(&EqNP, "Error setting RA/DEC."); return false; } int err = 0; /* Slew reads the '0', that is not the end of the slew */ if ((err = Slew(PortFD))) { EqNP.s = IPS_ALERT; IDSetNumber(&EqNP, "Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr); slewError(err); return false; } } TrackState = SCOPE_SLEWING; //EqNP.s = IPS_BUSY; LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr); return true; } /************************************************************************************** ** ***************************************************************************************/ #if 0 bool LXD650::Sync(double ra, double dec) { char syncString[256] = {0}; if (!isSimulation() && (setObjectRA(PortFD, ra) < 0 || (setObjectDEC(PortFD, dec)) < 0)) { EqNP.s = IPS_ALERT; IDSetNumber(&EqNP, "Error setting RA/DEC. Unable to Sync."); return false; } if (!isSimulation() && ::Sync(PortFD, syncString) < 0) { EqNP.s = IPS_ALERT; IDSetNumber(&EqNP, "Synchronization failed."); return false; } currentRA = ra; currentDEC = dec; LOG_INFO("Synchronization successful."); EqNP.s = IPS_OK; NewRaDec(currentRA, currentDEC); return true; } #else bool LXD650::Sync (double ra, double dec) { getBasicData(); AlignmentDatabaseEntry NewEntry; NewEntry.ObservationJulianDate = ln_get_julian_from_sys(); NewEntry.RightAscension = ra; NewEntry.Declination = dec; INDI::IEquatorialCoordinates MountRADE {currentRA, currentDEC}; NewEntry.TelescopeDirection = TelescopeDirectionVectorFromEquatorialCoordinates (MountRADE); NewEntry.PrivateDataSize = 0; DEBUGF (INDI::AlignmentSubsystem::DBG_ALIGNMENT, "New sync point Date %lf RA %lf DEC %lf TDV(x %lf y %lf z %lf)", NewEntry.ObservationJulianDate, NewEntry.RightAscension, NewEntry.Declination, NewEntry.TelescopeDirection.x, NewEntry.TelescopeDirection.y, NewEntry.TelescopeDirection.z); show_alignment ("Sync Sky:Mount",ra,dec,currentRA, currentDEC); LOGF_INFO ("New sync point Date %lf RA %lf DEC %lf TDV(x %lf y %lf z %lf)", NewEntry.ObservationJulianDate, NewEntry.RightAscension, NewEntry.Declination, NewEntry.TelescopeDirection.x, NewEntry.TelescopeDirection.y, NewEntry.TelescopeDirection.z); if (!CheckForDuplicateSyncPoint (NewEntry)) { GetAlignmentDatabase().push_back (NewEntry); // bool AddAlignmentEntryEquatorial(double actualRA, double actualDec, double mountRA, double mountDec); // Tell the client about size change UpdateSize(); // Tell the math plugin to reinitialise Initialise (this); // Force read before restarting ReadScopeStatus(); #if 0 // Sync cord wrap syncCoordWrapPosition(); #endif #if 0 // The tracking seconds should be reset to restart the drift compensation resetTracking(); #endif return true; } return false; } #endif ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::setLocalDate(uint8_t days, uint8_t months, uint16_t years) { return (setCalenderDate(PortFD, days, months, years) == 0); } bool LXD650::setLocalTime24(uint8_t hour, uint8_t minute, uint8_t second) { return (setLocalTime(PortFD, hour, minute, second) == 0); } bool LXD650::setUTCOffset(double offset) { return (::setUTCOffset(PortFD, (offset * -1.0)) == 0); } bool LXD650::updateTime(ln_date *utc, double utc_offset) { struct ln_zonedate ltm; double JD; if (isSimulation()) return true; ln_date_to_zonedate(utc, <m, utc_offset * 3600.0); JD = ln_get_julian_day(utc); LOGF_DEBUG("New JD is %.2f", JD); // Meade defines UTC Offset as the offset ADDED to local time to yield UTC, which // is the opposite of the standard definition of UTC offset! if (setUTCOffset(utc_offset) == false) { LOG_ERROR("Error setting UTC Offset."); return false; } // Set Local Time if (setLocalTime24(ltm.hours, ltm.minutes, ltm.seconds) == false) { LOG_ERROR("Error setting local time."); return false; } if (setLocalDate(ltm.days, ltm.months, ltm.years) == false) { LOG_ERROR("Error setting local date."); return false; } LOG_INFO("Time updated, updating planetary data..."); return true; } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::updateLocation(double latitude, double longitude, double elevation) { // Update INDI Alignment Subsystem Location UpdateLocation(latitude, longitude, elevation); LOGF_INFO("Location %.1f %.1f %.1f",latitude,longitude,elevation); SetApproximateMountAlignment(latitude >= 0 ? NORTH_CELESTIAL_POLE : SOUTH_CELESTIAL_POLE); Initialise(this); #if 0 // update cordwrap position at each init of the alignment subsystem syncCoordWrapPosition(); #endif // JM 2021-04-10: MUST convert from INDI longitude to standard longitude. // DO NOT REMOVE if (longitude > 180) longitude = longitude - 360; if (!isSimulation()) { if (setSiteLongitude(PortFD, longitude) < 0) { LOG_ERROR("Error setting site longitude coordinates"); return false; } if (setSiteLatitude(PortFD, latitude) < 0) { LOG_ERROR("Error setting site latitude coordinates"); return false; } } return true; } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::ISSnoopDevice(XMLEle *root) { #if 0 const char *propName = findXMLAttValu(root, "name"); // update cordwrap position at each init of the alignment subsystem if (!strcmp(propName, "ALIGNMENT_SUBSYSTEM_MATH_PLUGIN_INITIALISE") && telescope_caux->isConnected()) telescope_caux->syncCoordWrapPosition(); #endif return Telescope::ISSnoopDevice(root); } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) { if (strcmp(dev, getDeviceName()) == 0) { // Process alignment properties ProcessAlignmentBLOBProperties(this, name, sizes, blobsizes, blobs, formats, names, n); } // Pass it up the chain return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n); } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) { if (dev != nullptr && strcmp(dev, getDeviceName()) == 0) { if (!strcmp(name, SlewAccuracyNP.name)) { if (IUUpdateNumber(&SlewAccuracyNP, values, names, n) < 0) return false; SlewAccuracyNP.s = IPS_OK; if (SlewAccuracyN[0].value < 3 || SlewAccuracyN[1].value < 3) IDSetNumber(&SlewAccuracyNP, "Warning: Setting the slew accuracy too low may result in a dead lock"); IDSetNumber(&SlewAccuracyNP, nullptr); return true; } // Guide Rate if (!strcmp(name, LXDTrackRateNP.name)) { if (IUUpdateNumber(&LXDTrackRateNP, values, names, n) < 0) return false; LXDTrackRateNP.s = IPS_OK; IDSetNumber(&LXDTrackRateNP, nullptr); return LXDSetTrackRate(LXDTrackRateN.value); } // Guide Rate if (GuideRateNP.isNameMatch(name)) { GuideRateNP.update(values, names, n); GuideRateNP.setState(IPS_OK); GuideRateNP.apply(); return true; } // Process Guide Properties processGuiderProperties(name, values, names, n); // Process Alignment Properties ProcessAlignmentNumberProperties(this, name, values, names, n); } return INDI::Telescope::ISNewNumber(dev, name, values, names, n); } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) { if (strcmp(dev, getDeviceName()) == 0) { // Process alignment properties ProcessAlignmentSwitchProperties(this, name, states, names, n); if (LXDTrackModeSP.isNameMatch(name)) { LXDTrackModeSP.update(states, names, n); LXDTrackModeSP.setState(IPS_OK); LXDTrackModeSP.apply(); return LXDSetTrackMode(IUFindOnSwitchIndex(&LXDTrackModeSP)); } } return INDI::Telescope::ISNewSwitch(dev, name, states, names, n); } /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) { if (!strcmp(dev, getDeviceName())) ProcessAlignmentTextProperties(this, name, texts, names, n); return INDI::Telescope::ISNewText(dev, name, texts, names, n); } bool LXD650::LXDSetTrackRate(double rate) { rate *= 60. / TRACKRATE_SOLAR; selectTrackingMode(PortFD, LX200_TRACK_MANUAL); if (setTrackFreq(PortFD, rate)) return false; return true; } bool LXD650::LXDSetTrackMode(uint8_t mode) { double rate = TRACKRATE_SIDEREAL; if (mode == LXD_TRACK_SIDEREAL) { if (selectTrackingMode(PortFD, LX200_TRACK_SIDEREAL)) return false; return true; } if (mode == LXD_TRACK_SOLAR) rate = TRACKRATE_SOLAR; else if (mode == LXD_TRACK_LUNAR) rate = TRACKRATE_LUNAR; else if (mode == LXD_TRACK_CUSTOM) rate = TrackRateN[AXIS_RA].value; return LXDSetTrackRate(rate); } ///////////////////////////////////////////////////////////////////////////////////// /// // // // // ///////////////////////////////////////////////////////////////////////////////////// void LXD650::show_guide (const char *dir, int ms) { LOGF_INFO ("Guiding %s for %d ms", dir, ms); } void LXD650::GuideNorthCB(void) { GuideNorth_TID = 0; HaltMovement(PortFD, LX200_NORTH); GuideComplete(AXIS_DE); } void LXD650::GuideNorthProxy(void *context) { static_cast(context)->GuideNorthCB(); } IPState LXD650::GuideNorth(uint32_t ms) { double rate = GuideRateNP[AXIS_ALT].getValue(); ms = (uint32_t) (rate * (double) ms); setSlewMode(PortFD, LX200_SLEW_GUIDE); if (GuideNorth_TID) { IERmTimer(GuideNorth_TID); GuideNorth_TID = 0; } show_guide("N", ms); MoveTo(PortFD, LX200_NORTH); GuideNorth_TID = IEAddTimer(ms, GuideNorthProxy, this); return IPS_BUSY; } void LXD650::GuideSouthCB(void) { GuideSouth_TID = 0; HaltMovement(PortFD, LX200_SOUTH); GuideComplete(AXIS_DE); } void LXD650::GuideSouthProxy(void *context) { static_cast(context)->GuideSouthCB(); } IPState LXD650::GuideSouth(uint32_t ms) { double rate = GuideRateNP[AXIS_ALT].getValue(); ms = (uint32_t) (rate * (double) ms); setSlewMode(PortFD, LX200_SLEW_GUIDE); if (GuideSouth_TID) { IERmTimer(GuideSouth_TID); GuideSouth_TID = 0; } show_guide("S", ms); MoveTo(PortFD, LX200_SOUTH); GuideSouth_TID = IEAddTimer(ms, GuideSouthProxy, this); return IPS_BUSY; } void LXD650::GuideWestCB(void) { GuideWest_TID = 0; HaltMovement(PortFD, LX200_WEST); GuideComplete(AXIS_RA); } void LXD650::GuideWestProxy(void *context) { static_cast(context)->GuideWestCB(); } IPState LXD650::GuideWest(uint32_t ms) { double rate = GuideRateNP[AXIS_AZ].getValue(); ms = (uint32_t) (rate * (double) ms); setSlewMode(PortFD, LX200_SLEW_GUIDE); if (GuideWest_TID) { IERmTimer(GuideWest_TID); GuideWest_TID = 0; } show_guide("W", ms); MoveTo(PortFD, LX200_WEST); GuideWest_TID = IEAddTimer(ms, GuideWestProxy, this); return IPS_BUSY; } void LXD650::GuideEastCB(void) { GuideEast_TID = 0; HaltMovement(PortFD, LX200_EAST); GuideComplete(AXIS_RA); } void LXD650::GuideEastProxy(void *context) { static_cast(context)->GuideEastCB(); } IPState LXD650::GuideEast(uint32_t ms) { double rate = GuideRateNP[AXIS_AZ].getValue(); ms = (uint32_t) (rate * (double) ms); setSlewMode(PortFD, LX200_SLEW_GUIDE); if (GuideEast_TID) { IERmTimer(GuideEast_TID); GuideEast_TID = 0; } show_guide("E", ms); MoveTo(PortFD, LX200_EAST); GuideEast_TID = IEAddTimer(ms, GuideEastProxy, this); return IPS_BUSY; } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// void LXD650::set_slew_rate_from_property(void) { unsigned rate = IUFindOnSwitchIndex(&SlewRateSP); enum TSlew rates[]={LX200_SLEW_GUIDE,LX200_SLEW_FIND,LX200_SLEW_CENTER,LX200_SLEW_GUIDE}; if (rate>=(sizeof(rates)/sizeof(rates[0]))) return; setSlewMode(PortFD, rates[rate]); } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) { if (command == MOTION_START) { set_slew_rate_from_property(); if (dir == DIRECTION_NORTH) { MoveTo(PortFD, LX200_NORTH); } else { MoveTo(PortFD, LX200_SOUTH); } } else { if (dir == DIRECTION_NORTH) { HaltMovement(PortFD, LX200_NORTH); } else { HaltMovement(PortFD, LX200_SOUTH); } } return true; } ///////////////////////////////////////////////////////////////////////////////////// /// ///////////////////////////////////////////////////////////////////////////////////// bool LXD650::MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) { if (command == MOTION_START) { set_slew_rate_from_property(); if (dir == DIRECTION_WEST) { MoveTo(PortFD, LX200_WEST); } else { MoveTo(PortFD, LX200_EAST); } } else { if (dir == DIRECTION_WEST) { HaltMovement(PortFD, LX200_WEST); } else { HaltMovement(PortFD, LX200_EAST); } } return true; } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::Abort() { if (!isSimulation() && abortSlew(PortFD) < 0) { LOG_ERROR("Failed to abort slew."); return false; } EqNP.s = IPS_IDLE; TrackState = SCOPE_IDLE; IDSetNumber(&EqNP, nullptr); LOG_INFO("Slew aborted."); return true; } /************************************************************************************** ** ***************************************************************************************/ void LXD650::getBasicData() { // Make sure short checkLX200EquatorialFormat(PortFD); // Get current RA/DEC getLX200RA(PortFD, ¤tRA); getLX200DEC(PortFD, ¤tDEC); IDSetNumber(&EqNP, nullptr); } /************************************************************************************** ** ***************************************************************************************/ void LXD650::mountSim() { static struct timeval ltv; struct timeval tv; double dt, da, dx; int nlocked; /* update elapsed time since last poll, don't presume exactly POLLMS */ gettimeofday(&tv, nullptr); if (ltv.tv_sec == 0 && ltv.tv_usec == 0) ltv = tv; dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6; ltv = tv; da = SLEWRATE * dt; /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */ switch (TrackState) { case SCOPE_TRACKING: /* RA moves at sidereal, Dec stands still */ currentRA += (SIDRATE * dt / 15.); break; case SCOPE_SLEWING: /* slewing - nail it when both within one pulse @ SLEWRATE */ nlocked = 0; dx = targetRA - currentRA; if (fabs(dx) <= da) { currentRA = targetRA; nlocked++; } else if (dx > 0) currentRA += da / 15.; else currentRA -= da / 15.; dx = targetDEC - currentDEC; if (fabs(dx) <= da) { currentDEC = targetDEC; nlocked++; } else if (dx > 0) currentDEC += da; else currentDEC -= da; if (nlocked == 2) { TrackState = SCOPE_TRACKING; } break; default: break; } NewRaDec(currentRA, currentDEC); } /************************************************************************************** ** ***************************************************************************************/ void LXD650::slewError(int slewCode) { EqNP.s = IPS_ALERT; if (slewCode == 1) IDSetNumber(&EqNP, "Object below horizon."); else if (slewCode == 2) IDSetNumber(&EqNP, "Object below the minimum elevation limit."); else IDSetNumber(&EqNP, "Slew failed."); } /************************************************************************************** ** ***************************************************************************************/ bool LXD650::saveConfigItems(FILE *fp) { INDI::Telescope::saveConfigItems(fp); SaveAlignmentConfigProperties(fp); IUSaveConfigNumber(fp, &SlewAccuracyNP); IUSaveConfigNumber(fp, &GuideRateNP); return true; }