Instrument Neutral Distributed Interface INDI 2.2.5
skywatcherAltAzSimple.cpp
Go to the documentation of this file.
1
17
18#include "indicom.h"
20
21#include <libnova/transform.h>
22// libnova specifies round() on old systems and it collides with the new gcc 5.x/6.x headers
23#define HAVE_ROUND
24#include <libnova/utility.h>
25
26#include <chrono>
27#include <cstring>
28#include <fstream>
29#include <iostream>
30#include <thread>
31
32#include <sys/stat.h>
33
34// We declare an auto pointer to SkywatcherAltAzSimple.
35std::unique_ptr<SkywatcherAltAzSimple> SkywatcherAltAzSimplePtr(new SkywatcherAltAzSimple());
36
37/* Preset Slew Speeds */
38#define SLEWMODES 9
39double SlewSpeeds[SLEWMODES] = { 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 600.0 };
40
41void ISPoll(void *p);
42
43namespace
44{
45bool FileExists(const std::string &name)
46{
47 struct stat buffer;
48
49 return (stat (name.c_str(), &buffer) == 0);
50}
51
52std::string GetLogTimestamp()
53{
54 time_t Now = time(nullptr);
55 struct tm TimeStruct;
56 char Buffer[60];
57 std::string FinalStr;
58
59 TimeStruct = *localtime(&Now);
60 strftime(Buffer, sizeof(Buffer), "%Y%m%d %H:%M:%S", &TimeStruct);
61 FinalStr = Buffer;
62 // Add the millisecond part
63 std::chrono::system_clock::time_point NowClock = std::chrono::system_clock::now();
64 std::chrono::system_clock::duration TimePassed = NowClock.time_since_epoch();
65
66 TimePassed -= std::chrono::duration_cast<std::chrono::seconds>(TimePassed);
67 FinalStr += "." + std::to_string(static_cast<unsigned>(TimePassed / std::chrono::milliseconds(1)));
68 return FinalStr;
69}
70} // namespace
71
72SkywatcherAltAzSimple::SkywatcherAltAzSimple() : TrackLogFileName(GetHomeDirectory() + "/.indi/sw_mount_track_log.txt")
73{
74 // Set up the logging pointer in SkyWatcherAPI
75 pChildTelescope = this;
78 SLEWMODES);
79 std::remove(TrackLogFileName.c_str());
80}
81
83{
84 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::Abort");
85 LogMessage("MOVE ABORT");
89
91 {
94 GuideNSNP[0].setValue(0);
95 GuideNSNP[1].setValue(0);
96 GuideWENP[0].setValue(0);
97 GuideWENP[1].setValue(0);
98
99 IDMessage(getDeviceName(), "Guide aborted.");
102
103 return true;
104 }
105
106 return true;
107}
108
110{
111 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::Handshake");
113
114 Connection::Interface *activeConnection = getActiveConnection();
115 if (!activeConnection->name().compare("CONNECTION_TCP"))
116 {
118 }
119
120 bool Result = InitMount();
121
123 {
124 SerialPortName = serialConnection->port();
125 }
126 else
127 {
128 SerialPortName = "";
129 }
130
131 RecoverAfterReconnection = false;
132 DEBUGF(DBG_SCOPE, "SkywatcherAltAzSimple::Handshake - Result: %d", Result);
133 return Result;
134}
135
137{
138 //DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::getDefaultName\n");
139 return "Skywatcher Alt-Az Wedge";
140}
141
143{
144 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::Goto");
145
146 if (TrackState != SCOPE_IDLE)
147 Abort();
148
149 DEBUGF(DBG_SCOPE, "RA %lf DEC %lf", ra, dec);
150
151 if (IUFindSwitch(&CoordSP, "TRACK")->s == ISS_ON || IUFindSwitch(&CoordSP, "SLEW")->s == ISS_ON)
152 {
153 char RAStr[32], DecStr[32];
154 fs_sexa(RAStr, ra, 2, 3600);
155 fs_sexa(DecStr, dec, 2, 3600);
156 CurrentTrackingTarget.rightascension = ra;
157 CurrentTrackingTarget.declination = dec;
158 DEBUGF(INDI::Logger::DBG_SESSION, "New Tracking target RA %s DEC %s", RAStr, DecStr);
159 }
160
161 INDI::IHorizontalCoordinates AltAz { 0, 0 };
162
163 AltAz = GetAltAzPosition(ra, dec);
164 DEBUGF(DBG_SCOPE, "New Altitude %lf degrees %ld microsteps Azimuth %lf degrees %ld microsteps", AltAz.altitude,
165 DegreesToMicrosteps(AXIS2, AltAz.altitude), AltAz.azimuth, DegreesToMicrosteps(AXIS1, AltAz.azimuth));
166 LogMessage("NEW GOTO TARGET: Ra %lf Dec %lf - Alt %lf Az %lf - microsteps %ld %ld", ra, dec, AltAz.altitude,
167 AltAz.azimuth,
168 DegreesToMicrosteps(AXIS2, AltAz.altitude), DegreesToMicrosteps(AXIS1, AltAz.azimuth));
169
170 // Update the current encoder positions
173
174 long AltitudeOffsetMicrosteps =
176 long AzimuthOffsetMicrosteps =
178
179 DEBUGF(DBG_SCOPE, "Initial deltas Altitude %ld microsteps Azimuth %ld microsteps", AltitudeOffsetMicrosteps,
180 AzimuthOffsetMicrosteps);
181 if (AltitudeOffsetMicrosteps > MicrostepsPerRevolution[AXIS2] / 2)
182 {
183 // Going the long way round - send it the other way
184 AltitudeOffsetMicrosteps -= MicrostepsPerRevolution[AXIS2];
185 }
186 if (AzimuthOffsetMicrosteps > MicrostepsPerRevolution[AXIS1] / 2)
187 {
188 // Going the long way round - send it the other way
189 AzimuthOffsetMicrosteps -= MicrostepsPerRevolution[AXIS1];
190 }
191 if (AltitudeOffsetMicrosteps < -MicrostepsPerRevolution[AXIS2] / 2)
192 {
193 // Going the long way round - send it the other way
194 AltitudeOffsetMicrosteps += MicrostepsPerRevolution[AXIS2];
195 }
196 if (AzimuthOffsetMicrosteps < -MicrostepsPerRevolution[AXIS1] / 2)
197 {
198 // Going the long way round - send it the other way
199 AzimuthOffsetMicrosteps += MicrostepsPerRevolution[AXIS1];
200 }
201 DEBUGF(DBG_SCOPE, "Initial Axis2 %ld microsteps Axis1 %ld microsteps",
203 DEBUGF(DBG_SCOPE, "Current Axis2 %ld microsteps Axis1 %ld microsteps",
205 DEBUGF(DBG_SCOPE, "Altitude offset %ld microsteps Azimuth offset %ld microsteps",
206 AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
207
208 if (IUFindSwitch(&SlewModesSP, "SLEW_NORMAL")->s == ISS_ON)
209 {
210 SilentSlewMode = false;
211 }
212 else
213 {
214 SilentSlewMode = true;
215 }
216 SlewTo(AXIS1, AzimuthOffsetMicrosteps);
217 SlewTo(AXIS2, AltitudeOffsetMicrosteps);
218
220
221 return true;
222}
223
225{
226 IDLog("SkywatcherAltAzSimple::initProperties\n");
227
228 // Allow the base class to initialise its visible before connection properties
230
231 for (int i = 0; i < SlewRateSP.nsp; ++i)
232 {
233 sprintf(SlewRateSP.sp[i].label, "%.fx", SlewSpeeds[i]);
234 SlewRateSP.sp[i].aux = (void *)&SlewSpeeds[i];
235 }
236 strncpy(SlewRateSP.sp[SlewRateSP.nsp - 1].name, "SLEW_MAX", MAXINDINAME);
237
238 // Add default properties
241
242 // Set up property variables
243 IUFillText(&BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION], "MOTOR_CONTROL_FIRMWARE_VERSION",
244 "Motor control firmware version", "-");
245 IUFillText(&BasicMountInfoT[MOUNT_CODE], "MOUNT_CODE", "Mount code", "-");
246 IUFillText(&BasicMountInfoT[MOUNT_NAME], "MOUNT_NAME", "Mount name", "-");
247 IUFillText(&BasicMountInfoT[IS_DC_MOTOR], "IS_DC_MOTOR", "Is DC motor", "-");
248 IUFillTextVector(&BasicMountInfoTP, BasicMountInfoT, 4, getDeviceName(), "BASIC_MOUNT_INFO",
249 "Basic mount information", DetailedMountInfoPage, IP_RO, 60, IPS_IDLE);
250
251 IUFillNumber(&AxisOneInfoN[MICROSTEPS_PER_REVOLUTION], "MICROSTEPS_PER_REVOLUTION", "Microsteps per revolution",
252 "%.0f", 0, 0xFFFFFF, 1, 0);
253 IUFillNumber(&AxisOneInfoN[STEPPER_CLOCK_FREQUENCY], "STEPPER_CLOCK_FREQUENCY", "Stepper clock frequency", "%.0f", 0,
254 0xFFFFFF, 1, 0);
255 IUFillNumber(&AxisOneInfoN[HIGH_SPEED_RATIO], "HIGH_SPEED_RATIO", "High speed ratio", "%.0f", 0, 0xFFFFFF, 1, 0);
256 IUFillNumber(&AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION], "MICROSTEPS_PER_WORM_REVOLUTION",
257 "Microsteps per worm revolution", "%.0f", 0, 0xFFFFFF, 1, 0);
258
259 IUFillNumberVector(&AxisOneInfoNP, AxisOneInfoN, 4, getDeviceName(), "AXIS_ONE_INFO", "Axis one information",
260 DetailedMountInfoPage, IP_RO, 60, IPS_IDLE);
261
262 IUFillSwitch(&AxisOneStateS[FULL_STOP], "FULL_STOP", "FULL_STOP", ISS_OFF);
263 IUFillSwitch(&AxisOneStateS[SLEWING], "SLEWING", "SLEWING", ISS_OFF);
264 IUFillSwitch(&AxisOneStateS[SLEWING_TO], "SLEWING_TO", "SLEWING_TO", ISS_OFF);
265 IUFillSwitch(&AxisOneStateS[SLEWING_FORWARD], "SLEWING_FORWARD", "SLEWING_FORWARD", ISS_OFF);
266 IUFillSwitch(&AxisOneStateS[HIGH_SPEED], "HIGH_SPEED", "HIGH_SPEED", ISS_OFF);
267 IUFillSwitch(&AxisOneStateS[NOT_INITIALISED], "NOT_INITIALISED", "NOT_INITIALISED", ISS_ON);
268 IUFillSwitchVector(&AxisOneStateSP, AxisOneStateS, 6, getDeviceName(), "AXIS_ONE_STATE", "Axis one state",
269 DetailedMountInfoPage, IP_RO, ISR_NOFMANY, 60, IPS_IDLE);
270
271 IUFillNumber(&AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION], "MICROSTEPS_PER_REVOLUTION", "Microsteps per revolution",
272 "%.0f", 0, 0xFFFFFF, 1, 0);
273 IUFillNumber(&AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY], "STEPPER_CLOCK_FREQUENCY", "Step timer frequency", "%.0f", 0,
274 0xFFFFFF, 1, 0);
275 IUFillNumber(&AxisTwoInfoN[HIGH_SPEED_RATIO], "HIGH_SPEED_RATIO", "High speed ratio", "%.0f", 0, 0xFFFFFF, 1, 0);
276 IUFillNumber(&AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION], "MICROSTEPS_PER_WORM_REVOLUTION",
277 "Microsteps per worm revolution", "%.0f", 0, 0xFFFFFF, 1, 0);
278
279 IUFillNumberVector(&AxisTwoInfoNP, AxisTwoInfoN, 4, getDeviceName(), "AXIS_TWO_INFO", "Axis two information",
280 DetailedMountInfoPage, IP_RO, 60, IPS_IDLE);
281
282 IUFillSwitch(&AxisTwoStateS[FULL_STOP], "FULL_STOP", "FULL_STOP", ISS_OFF);
283 IUFillSwitch(&AxisTwoStateS[SLEWING], "SLEWING", "SLEWING", ISS_OFF);
284 IUFillSwitch(&AxisTwoStateS[SLEWING_TO], "SLEWING_TO", "SLEWING_TO", ISS_OFF);
285 IUFillSwitch(&AxisTwoStateS[SLEWING_FORWARD], "SLEWING_FORWARD", "SLEWING_FORWARD", ISS_OFF);
286 IUFillSwitch(&AxisTwoStateS[HIGH_SPEED], "HIGH_SPEED", "HIGH_SPEED", ISS_OFF);
287 IUFillSwitch(&AxisTwoStateS[NOT_INITIALISED], "NOT_INITIALISED", "NOT_INITIALISED", ISS_ON);
288 IUFillSwitchVector(&AxisTwoStateSP, AxisTwoStateS, 6, getDeviceName(), "AXIS_TWO_STATE", "Axis two state",
289 DetailedMountInfoPage, IP_RO, ISR_NOFMANY, 60, IPS_IDLE);
290
291 IUFillNumber(&AxisOneEncoderValuesN[RAW_MICROSTEPS], "RAW_MICROSTEPS", "Raw Microsteps", "%.0f", 0, 0xFFFFFF, 1, 0);
292 IUFillNumber(&AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC], "MICROSTEPS_PER_ARCSEC", "Microsteps/arcsecond",
293 "%.4f", 0, 0xFFFFFF, 1, 0);
294 IUFillNumber(&AxisOneEncoderValuesN[OFFSET_FROM_INITIAL], "OFFSET_FROM_INITIAL", "Offset from initial", "%.0f", 0,
295 0xFFFFFF, 1, 0);
296 IUFillNumber(&AxisOneEncoderValuesN[DEGREES_FROM_INITIAL], "DEGREES_FROM_INITIAL", "Degrees from initial", "%.2f",
297 -1000.0, 1000.0, 1, 0);
298
299 IUFillNumberVector(&AxisOneEncoderValuesNP, AxisOneEncoderValuesN, 4, getDeviceName(), "AXIS1_ENCODER_VALUES",
300 "Axis 1 Encoder values", DetailedMountInfoPage, IP_RO, 60, IPS_IDLE);
301
302 IUFillNumber(&AxisTwoEncoderValuesN[RAW_MICROSTEPS], "RAW_MICROSTEPS", "Raw Microsteps", "%.0f", 0, 0xFFFFFF, 1, 0);
303 IUFillNumber(&AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC], "MICROSTEPS_PER_ARCSEC", "Microsteps/arcsecond",
304 "%.4f", 0, 0xFFFFFF, 1, 0);
305 IUFillNumber(&AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL], "OFFSET_FROM_INITIAL", "Offset from initial", "%.0f", 0,
306 0xFFFFFF, 1, 0);
307 IUFillNumber(&AxisTwoEncoderValuesN[DEGREES_FROM_INITIAL], "DEGREES_FROM_INITIAL", "Degrees from initial", "%.2f",
308 -1000.0, 1000.0, 1, 0);
309
310 IUFillNumberVector(&AxisTwoEncoderValuesNP, AxisTwoEncoderValuesN, 4, getDeviceName(), "AXIS2_ENCODER_VALUES",
311 "Axis 2 Encoder values", DetailedMountInfoPage, IP_RO, 60, IPS_IDLE);
312 // Register any visible before connection properties
313
314 // Slew modes
315 IUFillSwitch(&SlewModesS[SLEW_SILENT], "SLEW_SILENT", "Silent", ISS_OFF);
316 IUFillSwitch(&SlewModesS[SLEW_NORMAL], "SLEW_NORMAL", "Normal", ISS_OFF);
317 IUFillSwitchVector(&SlewModesSP, SlewModesS, 2, getDeviceName(), "TELESCOPE_MOTION_SLEWMODE", "Slew Mode",
319
320 // Wedge mode
321 IUFillSwitch(&WedgeModeS[WEDGE_SIMPLE], "WEDGE_SIMPLE", "Simple wedge", ISS_OFF);
322 IUFillSwitch(&WedgeModeS[WEDGE_EQ], "WEDGE_EQ", "EQ wedge", ISS_OFF);
323 IUFillSwitch(&WedgeModeS[WEDGE_DISABLED], "WEDGE_DISABLED", "Disabled", ISS_OFF);
324 IUFillSwitchVector(&WedgeModeSP, WedgeModeS, 3, getDeviceName(), "TELESCOPE_MOTION_WEDGEMODE",
325 "Wedge Mode", MOTION_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
326
327 // Track logging mode
328 IUFillSwitch(&TrackLogModeS[TRACKLOG_ENABLED], "TRACKLOG_ENABLED", "Enable logging", ISS_OFF);
329 IUFillSwitch(&TrackLogModeS[TRACKLOG_DISABLED], "TRACKLOG_DISABLED", "Disabled", ISS_ON);
330 IUFillSwitchVector(&TrackLogModeSP, TrackLogModeS, 2, getDeviceName(), "TELESCOPE_MOTION_TRACKLOGMODE",
331 "Track Logging Mode", MOTION_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
332
333 // Guiding rates for RA/DEC axes
334 IUFillNumber(&GuidingRatesN[0], "GUIDERA_RATE", "microsteps/seconds (RA)", "%1.3f", 0.00001, 100000.0, 0.00001, 1.0);
335 IUFillNumber(&GuidingRatesN[1], "GUIDEDEC_RATE", "microsteps/seconds (Dec)", "%1.3f", 0.00001, 100000.0, 0.00001, 1.0);
336 IUFillNumberVector(&GuidingRatesNP, GuidingRatesN, 2, getDeviceName(), "GUIDE_RATES", "Guide Rates", MOTION_TAB,
337 IP_RW, 60, IPS_IDLE);
338
339 // Tracking rate
340 // For Skywatcher Virtuoso:
341 // Alt rate: 0.72, Az rate: 0.72, timeout: 1000 msec
342 // For Skywatcher Merlin:
343 // Alt rate: 0.64, Az rate: 0.64, timeout: 1000 msec
344 IUFillNumber(&TrackingValuesN[0], "TRACKING_RATE_ALT", "rate (Alt)", "%1.3f", 0.001, 10.0, 0.000001, 0.64);
345 IUFillNumber(&TrackingValuesN[1], "TRACKING_RATE_AZ", "rate (Az)", "%1.3f", 0.001, 10.0, 0.000001, 0.64);
346 IUFillNumber(&TrackingValuesN[2], "TRACKING_TIMEOUT", "msec (period)", "%1.3f", 0.001, 10000.0, 0.000001, 1000.0);
347 IUFillNumberVector(&TrackingValuesNP, TrackingValuesN, 3, getDeviceName(), "TRACKING_VALUES", "Tracking Values", MOTION_TAB,
348 IP_RW, 60, IPS_IDLE);
349
350 // Park movement directions
351 IUFillSwitch(&ParkMovementDirectionS[PARK_COUNTERCLOCKWISE], "PMD_COUNTERCLOCKWISE", "Counterclockwise", ISS_ON);
352 IUFillSwitch(&ParkMovementDirectionS[PARK_CLOCKWISE], "PMD_CLOCKWISE", "Clockwise", ISS_OFF);
353 IUFillSwitchVector(&ParkMovementDirectionSP, ParkMovementDirectionS, 2, getDeviceName(), "PARK_DIRECTION",
354 "Park Direction", MOTION_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
355
356 // Park positions
357 IUFillSwitch(&ParkPositionS[PARK_NORTH], "PARK_NORTH", "North", ISS_ON);
358 IUFillSwitch(&ParkPositionS[PARK_EAST], "PARK_EAST", "East", ISS_OFF);
359 IUFillSwitch(&ParkPositionS[PARK_SOUTH], "PARK_SOUTH", "South", ISS_OFF);
360 IUFillSwitch(&ParkPositionS[PARK_WEST], "PARK_WEST", "West", ISS_OFF);
361 IUFillSwitchVector(&ParkPositionSP, ParkPositionS, 4, getDeviceName(), "PARK_POSITION", "Park Position", MOTION_TAB,
363
364 // Unpark positions
365 IUFillSwitch(&UnparkPositionS[PARK_NORTH], "UNPARK_NORTH", "North", ISS_OFF);
366 IUFillSwitch(&UnparkPositionS[PARK_EAST], "UNPARK_EAST", "East", ISS_OFF);
367 IUFillSwitch(&UnparkPositionS[PARK_SOUTH], "UNPARK_SOUTH", "South", ISS_OFF);
368 IUFillSwitch(&UnparkPositionS[PARK_WEST], "UNPARK_WEST", "West", ISS_OFF);
369 IUFillSwitchVector(&UnparkPositionSP, UnparkPositionS, 4, getDeviceName(), "UNPARK_POSITION", "Unpark Position",
371
372 // Guiding support
373 initGuiderProperties(getDeviceName(), GUIDE_TAB);
375
376 return true;
377}
378
380{
381 IDLog("SkywatcherAltAzSimple::ISGetProperties\n");
383
384 if (isConnected())
385 {
386 // Fill in any real values now available MCInit should have been called already
387 UpdateDetailedMountInformation(false);
388
389 // Define our connected only properties to the base driver
390 // e.g. defineProperty(MyNumberVectorPointer);
391 // This will register our properties and send a IDDefXXXX mewssage to any connected clients
392 defineProperty(&BasicMountInfoTP);
393 defineProperty(&AxisOneInfoNP);
394 defineProperty(&AxisOneStateSP);
395 defineProperty(&AxisTwoInfoNP);
396 defineProperty(&AxisTwoStateSP);
397 defineProperty(&AxisOneEncoderValuesNP);
398 defineProperty(&AxisTwoEncoderValuesNP);
399 defineProperty(&SlewModesSP);
400 defineProperty(&WedgeModeSP);
401 defineProperty(&TrackLogModeSP);
402 defineProperty(&GuidingRatesNP);
403 defineProperty(&TrackingValuesNP);
404 defineProperty(&ParkMovementDirectionSP);
405 defineProperty(&ParkPositionSP);
406 defineProperty(&UnparkPositionSP);
409 }
410}
411
412bool SkywatcherAltAzSimple::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
413 char *formats[], char *names[], int n)
414{
415 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
416 {
417 // It is for us
418 }
419 // Pass it up the chain
420 return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
421}
422
423bool SkywatcherAltAzSimple::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
424{
425 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
426 {
427 if (strcmp(name, "GUIDE_RATES") == 0)
428 {
429 ResetGuidePulses();
430 GuidingRatesNP.s = IPS_OK;
431 IUUpdateNumber(&GuidingRatesNP, values, names, n);
432 IDSetNumber(&GuidingRatesNP, nullptr);
433 return true;
434 }
435
436 if (strcmp(name, "TRACKING_VALUES") == 0)
437 {
438 TrackingValuesNP.s = IPS_OK;
439 IUUpdateNumber(&TrackingValuesNP, values, names, n);
440 IDSetNumber(&TrackingValuesNP, nullptr);
441 return true;
442 }
443
444 // Let our driver do sync operation in park position
445 if (strcmp(name, "EQUATORIAL_EOD_COORD") == 0)
446 {
447 double ra = -1;
448 double dec = -100;
449
450 for (int x = 0; x < n; x++)
451 {
452 INumber *eqp = IUFindNumber(&EqNP, names[x]);
453 if (eqp == &EqN[AXIS_RA])
454 {
455 ra = values[x];
456 }
457 else if (eqp == &EqN[AXIS_DE])
458 {
459 dec = values[x];
460 }
461 }
462 if ((ra >= 0) && (ra <= 24) && (dec >= -90) && (dec <= 90))
463 {
464 ISwitch *sw = IUFindSwitch(&CoordSP, "SYNC");
465
466 if (sw != nullptr && sw->s == ISS_ON && isParked())
467 {
468 return Sync(ra, dec);
469 }
470 }
471 }
472
473 processGuiderProperties(name, values, names, n);
474 }
475 // Pass it up the chain
476 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
477}
478
479bool SkywatcherAltAzSimple::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
480{
482 svp = getSwitch(name);
483 if (svp == nullptr)
484 {
485 LOGF_WARN("getSwitch failed for %s", name);
486 }
487 else
488 {
489 LOGF_DEBUG("getSwitch OK %s", name);
490 IUUpdateSwitch(svp, states, names, n);
491 }
492 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
493 {
494 // It is for us
495 }
496 // Pass it up the chain
497 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
498}
499
500bool SkywatcherAltAzSimple::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
501{
502 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
503 {
504 // It is for us
505 }
506 // Pass it up the chain
507 bool Ret = INDI::Telescope::ISNewText(dev, name, texts, names, n);
508
509 // The scope config switch must be updated after the config is saved to disk
510 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
511 {
512 if (name && std::string(name) == "SCOPE_CONFIG_NAME")
513 {
514 UpdateScopeConfigSwitch();
515 }
516 }
517 return Ret;
518}
519
520void SkywatcherAltAzSimple::UpdateScopeConfigSwitch()
521{
522 if (!CheckFile(ScopeConfigFileName, false))
523 {
524 DEBUGF(INDI::Logger::DBG_SESSION, "Can't open XML file (%s) for read", ScopeConfigFileName.c_str());
525 return;
526 }
527 LilXML *XmlHandle = newLilXML();
528 FILE *FilePtr = fopen(ScopeConfigFileName.c_str(), "r");
529 XMLEle *RootXmlNode = nullptr;
530 XMLEle *CurrentXmlNode = nullptr;
531 XMLAtt *Ap = nullptr;
532 bool DeviceFound = false;
533 char ErrMsg[XML_ERROR_SIZE];
534
535 RootXmlNode = readXMLFile(FilePtr, XmlHandle, ErrMsg);
536 delLilXML(XmlHandle);
537 XmlHandle = nullptr;
538 if (!RootXmlNode)
539 {
540 DEBUGF(INDI::Logger::DBG_SESSION, "Failed to parse XML file (%s): %s", ScopeConfigFileName.c_str(), ErrMsg);
541 return;
542 }
543 if (std::string(tagXMLEle(RootXmlNode)) != ScopeConfigRootXmlNode)
544 {
545 DEBUGF(INDI::Logger::DBG_SESSION, "Not a scope config XML file (%s)", ScopeConfigFileName.c_str());
546 delXMLEle(RootXmlNode);
547 return;
548 }
549 CurrentXmlNode = nextXMLEle(RootXmlNode, 1);
550 // Find the current telescope in the config file
551 while (CurrentXmlNode)
552 {
553 if (std::string(tagXMLEle(CurrentXmlNode)) != ScopeConfigDeviceXmlNode)
554 {
555 CurrentXmlNode = nextXMLEle(RootXmlNode, 0);
556 continue;
557 }
558 Ap = findXMLAtt(CurrentXmlNode, ScopeConfigNameXmlNode.c_str());
559 if (Ap && !strcmp(valuXMLAtt(Ap), getDeviceName()))
560 {
561 DeviceFound = true;
562 break;
563 }
564 CurrentXmlNode = nextXMLEle(RootXmlNode, 0);
565 }
566 if (!DeviceFound)
567 {
568 DEBUGF(INDI::Logger::DBG_SESSION, "No a scope config found for %s in the XML file (%s)", getDeviceName(),
569 ScopeConfigFileName.c_str());
570 delXMLEle(RootXmlNode);
571 return;
572 }
573 // Read the values
574 XMLEle *XmlNode = nullptr;
575 XMLEle *DeviceXmlNode = CurrentXmlNode;
576 std::string ConfigName;
577
578 for (int i = 1; i < 7; ++i)
579 {
580 bool Found = true;
581
582 CurrentXmlNode = findXMLEle(DeviceXmlNode, ("config" + std::to_string(i)).c_str());
583 if (CurrentXmlNode)
584 {
585 XmlNode = findXMLEle(CurrentXmlNode, ScopeConfigLabelApXmlNode.c_str());
586 if (XmlNode)
587 {
588 ConfigName = pcdataXMLEle(XmlNode);
589 }
590 }
591 else
592 {
593 Found = false;
594 }
595 // Change the switch label
596 ISwitch *configSwitch = IUFindSwitch(&ScopeConfigsSP, ("SCOPE_CONFIG" + std::to_string(i)).c_str());
597
598 if (configSwitch != nullptr)
599 {
600 // The config is not used yet
601 if (!Found)
602 {
603 snprintf(configSwitch->label, MAXINDILABEL, "%s",
604 ("Config #" + std::to_string(i) + " - Not used").c_str());
605 continue;
606 }
607 // Empty switch label
608 if (ConfigName.empty())
609 {
610 snprintf(configSwitch->label, MAXINDILABEL, "%s",
611 ("Config #" + std::to_string(i) + " - Untitled").c_str());
612 continue;
613 }
614 snprintf(configSwitch->label, MAXINDILABEL, "%s",
615 ("Config #" + std::to_string(i) + " - " + ConfigName).c_str());
616 }
617 }
618 delXMLEle(RootXmlNode);
619 // Delete the joystick control to get the telescope config switch to the bottom of the page
620 deleteProperty("USEJOYSTICK");
621 // Recreate the switch control
622 deleteProperty(ScopeConfigsSP.name);
623 defineProperty(&ScopeConfigsSP);
624}
625
627{
629 double Rate = *((double *)Switch->aux);
630
631 return Rate;
632}
633
635{
636 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::MoveNS");
637
638 double speed =
640 const char *dirStr = (dir == DIRECTION_NORTH) ? "North" : "South";
641
642 if (IsMerlinMount())
643 {
644 speed = -speed;
645 }
646
647 switch (command)
648 {
649 case MOTION_START:
650 DEBUGF(DBG_SCOPE, "Starting Slew %s", dirStr);
651 // Ignore the silent mode because MoveNS() is called by the manual motion UI controls.
652 Slew(AXIS2, speed, true);
653 moving = true;
654 break;
655
656 case MOTION_STOP:
657 DEBUGF(DBG_SCOPE, "Stopping Slew %s", dirStr);
659 moving = false;
660 break;
661 }
662
663 return true;
664}
665
667{
668 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::MoveWE");
669
670 double speed =
672 const char *dirStr = (dir == DIRECTION_WEST) ? "West" : "East";
673
674 speed = -speed;
675
676 switch (command)
677 {
678 case MOTION_START:
679 DEBUGF(DBG_SCOPE, "Starting Slew %s", dirStr);
680 // Ignore the silent mode because MoveNS() is called by the manual motion UI controls.
681 Slew(AXIS1, speed, true);
682 moving = true;
683 break;
684
685 case MOTION_STOP:
686 DEBUGF(DBG_SCOPE, "Stopping Slew %s", dirStr);
688 moving = false;
689 break;
690 }
691
692 return true;
693}
694
696{
697 double Result = 0;
698
699 DEBUGF(DBG_SCOPE, "GetParkDeltaAz: direction %d - position: %d", (int)target_direction, (int)target_position);
700 // Calculate delta degrees (target: NORTH)
701 if (target_position == PARK_NORTH)
702 {
703 if (target_direction == PARK_COUNTERCLOCKWISE)
704 {
705 Result = -CurrentAltAz.azimuth;
706 }
707 else
708 {
709 Result = 360 - CurrentAltAz.azimuth;
710 }
711 }
712 // Calculate delta degrees (target: EAST)
713 if (target_position == PARK_EAST)
714 {
715 if (target_direction == PARK_COUNTERCLOCKWISE)
716 {
717 if (CurrentAltAz.azimuth > 0 && CurrentAltAz.azimuth < 90)
718 Result = -270 - CurrentAltAz.azimuth;
719 else
720 Result = -CurrentAltAz.azimuth + 90;
721 }
722 else
723 {
724 if (CurrentAltAz.azimuth > 0 && CurrentAltAz.azimuth < 90)
725 Result = 90 - CurrentAltAz.azimuth;
726 else
727 Result = 360 - CurrentAltAz.azimuth + 90;
728 }
729 }
730 // Calculate delta degrees (target: SOUTH)
731 if (target_position == PARK_SOUTH)
732 {
733 if (target_direction == PARK_COUNTERCLOCKWISE)
734 {
735 if (CurrentAltAz.azimuth > 0 && CurrentAltAz.azimuth < 180)
736 Result = -180 - CurrentAltAz.azimuth;
737 else
738 Result = -CurrentAltAz.azimuth + 180;
739 }
740 else
741 {
742 if (CurrentAltAz.azimuth > 0 && CurrentAltAz.azimuth < 180)
743 Result = 180 - CurrentAltAz.azimuth;
744 else
745 Result = 360 - CurrentAltAz.azimuth + 180;
746 }
747 }
748 // Calculate delta degrees (target: WEST)
749 if (target_position == PARK_WEST)
750 {
751 if (target_direction == PARK_COUNTERCLOCKWISE)
752 {
753 if (CurrentAltAz.azimuth > 0 && CurrentAltAz.azimuth < 270)
754 Result = -90 - CurrentAltAz.azimuth;
755 else
756 Result = -CurrentAltAz.azimuth + 270;
757 }
758 else
759 {
760 if (CurrentAltAz.azimuth > 0 && CurrentAltAz.azimuth < 270)
761 Result = 270 - CurrentAltAz.azimuth;
762 else
763 Result = 360 - CurrentAltAz.azimuth + 270;
764 }
765 }
766 if (Result >= 360)
767 {
768 Result -= 360;
769 }
770 if (Result <= -360)
771 {
772 Result += 360;
773 }
774 return Result;
775}
776
778{
779 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::Park");
780 ParkPosition_t TargetPosition = PARK_NORTH;
781 ParkDirection_t TargetDirection = PARK_COUNTERCLOCKWISE;
782 double DeltaAlt = 0;
783 double DeltaAz = 0;
784
785 // Determinate the target position and direction
786 if (IUFindSwitch(&ParkPositionSP, "PARK_NORTH") != nullptr &&
787 IUFindSwitch(&ParkPositionSP, "PARK_NORTH")->s == ISS_ON)
788 {
789 TargetPosition = PARK_NORTH;
790 }
791 if (IUFindSwitch(&ParkPositionSP, "PARK_EAST") != nullptr &&
792 IUFindSwitch(&ParkPositionSP, "PARK_EAST")->s == ISS_ON)
793 {
794 TargetPosition = PARK_EAST;
795 }
796 if (IUFindSwitch(&ParkPositionSP, "PARK_SOUTH") != nullptr &&
797 IUFindSwitch(&ParkPositionSP, "PARK_SOUTH")->s == ISS_ON)
798 {
799 TargetPosition = PARK_SOUTH;
800 }
801 if (IUFindSwitch(&ParkPositionSP, "PARK_WEST") != nullptr &&
802 IUFindSwitch(&ParkPositionSP, "PARK_WEST")->s == ISS_ON)
803 {
804 TargetPosition = PARK_WEST;
805 }
806
807 if (IUFindSwitch(&ParkMovementDirectionSP, "PMD_COUNTERCLOCKWISE") != nullptr &&
808 IUFindSwitch(&ParkMovementDirectionSP, "PMD_COUNTERCLOCKWISE")->s == ISS_ON)
809 {
810 TargetDirection = PARK_COUNTERCLOCKWISE;
811 }
812 if (IUFindSwitch(&ParkMovementDirectionSP, "PMD_CLOCKWISE") != nullptr &&
813 IUFindSwitch(&ParkMovementDirectionSP, "PMD_CLOCKWISE")->s == ISS_ON)
814 {
815 TargetDirection = PARK_CLOCKWISE;
816 }
817 DeltaAz = GetParkDeltaAz(TargetDirection, TargetPosition);
818
819 // Move the telescope to the desired position
820 long AltitudeOffsetMicrosteps = DegreesToMicrosteps(AXIS2, DeltaAlt);
821 long AzimuthOffsetMicrosteps = DegreesToMicrosteps(AXIS1, DeltaAz);
822
823 DEBUGF(DBG_SCOPE, "Parking: Delta altitude %1.2f - delta azimuth %1.2f", DeltaAlt, DeltaAz);
824 DEBUGF(DBG_SCOPE, "Parking: Altitude offset %ld microsteps Azimuth offset %ld microsteps",
825 AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
826
827 if (IUFindSwitch(&SlewModesSP, "SLEW_NORMAL")->s == ISS_ON)
828 {
829 SilentSlewMode = false;
830 }
831 else
832 {
833 SilentSlewMode = true;
834 }
835 SlewTo(AXIS1, AzimuthOffsetMicrosteps);
836 SlewTo(AXIS2, AltitudeOffsetMicrosteps);
837
839 return true;
840}
841
843{
844 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::UnPark");
845
846 ParkPosition_t TargetPosition = PARK_NORTH;
847 ParkDirection_t TargetDirection = PARK_COUNTERCLOCKWISE;
848 double DeltaAlt = 0;
849 double DeltaAz = 0;
850
851 // Determinate the target position and direction
852 if (IUFindSwitch(&UnparkPositionSP, "UNPARK_NORTH") != nullptr &&
853 IUFindSwitch(&UnparkPositionSP, "UNPARK_NORTH")->s == ISS_ON)
854 {
855 TargetPosition = PARK_NORTH;
856 }
857 if (IUFindSwitch(&UnparkPositionSP, "UNPARK_EAST") != nullptr &&
858 IUFindSwitch(&UnparkPositionSP, "UNPARK_EAST")->s == ISS_ON)
859 {
860 TargetPosition = PARK_EAST;
861 }
862 if (IUFindSwitch(&UnparkPositionSP, "UNPARK_SOUTH") != nullptr &&
863 IUFindSwitch(&UnparkPositionSP, "UNPARK_SOUTH")->s == ISS_ON)
864 {
865 TargetPosition = PARK_SOUTH;
866 }
867 if (IUFindSwitch(&UnparkPositionSP, "UNPARK_WEST") != nullptr &&
868 IUFindSwitch(&UnparkPositionSP, "UNPARK_WEST")->s == ISS_ON)
869 {
870 TargetPosition = PARK_WEST;
871 }
872
873 // Note: The reverse direction is used for unparking.
874 if (IUFindSwitch(&ParkMovementDirectionSP, "PMD_COUNTERCLOCKWISE") != nullptr &&
875 IUFindSwitch(&ParkMovementDirectionSP, "PMD_COUNTERCLOCKWISE")->s == ISS_ON)
876 {
877 TargetDirection = PARK_CLOCKWISE;
878 }
879 if (IUFindSwitch(&ParkMovementDirectionSP, "PMD_CLOCKWISE") != nullptr &&
880 IUFindSwitch(&ParkMovementDirectionSP, "PMD_CLOCKWISE")->s == ISS_ON)
881 {
882 TargetDirection = PARK_COUNTERCLOCKWISE;
883 }
884 DeltaAz = GetParkDeltaAz(TargetDirection, TargetPosition);
885 // Altitude 3360 points the telescope upwards
886 DeltaAlt = CurrentAltAz.altitude - 3360;
887
888 // Move the telescope to the desired position
889 long AltitudeOffsetMicrosteps = DegreesToMicrosteps(AXIS2, DeltaAlt);
890 long AzimuthOffsetMicrosteps = DegreesToMicrosteps(AXIS1, DeltaAz);
891
892 DEBUGF(DBG_SCOPE, "Unparking: Delta altitude %1.2f - delta azimuth %1.2f", DeltaAlt, DeltaAz);
893 DEBUGF(DBG_SCOPE, "Unparking: Altitude offset %ld microsteps Azimuth offset %ld microsteps",
894 AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
895
896 if (IUFindSwitch(&SlewModesSP, "SLEW_NORMAL")->s == ISS_ON)
897 {
898 SilentSlewMode = false;
899 }
900 else
901 {
902 SilentSlewMode = true;
903 }
904 SlewTo(AXIS1, AzimuthOffsetMicrosteps);
905 SlewTo(AXIS2, AltitudeOffsetMicrosteps);
906
907 SetParked(false);
909 return true;
910}
911
913{
914 // leave the following stuff in for the time being it is mostly harmless
915 // Quick check of the mount
916 if (UpdateCount == 0 && !GetMotorBoardVersion(AXIS1))
917 return false;
918
919 if (!GetStatus(AXIS1))
920 return false;
921
922 if (!GetStatus(AXIS2))
923 return false;
924
925 // Update Axis Position
926 if (!GetEncoder(AXIS1))
927 return false;
928 if (!GetEncoder(AXIS2))
929 return false;
930
931 if (UpdateCount % 5 == 0)
932 UpdateDetailedMountInformation(true);
933
934 UpdateCount++;
936 {
937 if (!IsInMotion(AXIS1) && !IsInMotion(AXIS2))
938 {
939 SetParked(true);
940 }
941 }
942
943 // Calculate new RA DEC
944 INDI::IHorizontalCoordinates AltAz { 0, 0 };
945
947 if (VerboseScopeStatus)
948 {
949 DEBUGF(DBG_SCOPE, "Axis2 encoder %ld initial %ld alt(degrees) %lf",
951 }
953 CurrentAltAz = AltAz;
954 if (VerboseScopeStatus)
955 {
956 DEBUGF(DBG_SCOPE, "Axis1 encoder %ld initial %ld az(degrees) %lf",
958 }
959
960 INDI::IEquatorialCoordinates RaDec { 0, 0 };
961
962 RaDec = GetRaDecPosition(AltAz.altitude, AltAz.azimuth);
963 if (VerboseScopeStatus)
964 {
965 DEBUGF(DBG_SCOPE, "New RA %lf (hours) DEC %lf (degrees)", RaDec.rightascension, RaDec.declination);
966 }
967 LogMessage("STATUS: Ra %lf Dec %lf - Alt %lf Az %lf - microsteps %ld %ld", RaDec.rightascension, RaDec.declination,
968 AltAz.altitude, AltAz.azimuth, CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2],
970 NewRaDec(RaDec.rightascension, RaDec.declination);
971 VerboseScopeStatus = false;
972 return true;
973}
974
975
977{
978 IUSaveConfigSwitch(fp, &SlewModesSP);
979 IUSaveConfigSwitch(fp, &WedgeModeSP);
980 IUSaveConfigSwitch(fp, &TrackLogModeSP);
981 IUSaveConfigNumber(fp, &GuidingRatesNP);
982 IUSaveConfigNumber(fp, &TrackingValuesNP);
983 IUSaveConfigSwitch(fp, &ParkMovementDirectionSP);
984 IUSaveConfigSwitch(fp, &ParkPositionSP);
985 IUSaveConfigSwitch(fp, &UnparkPositionSP);
986
988}
989
990
992{
993 DEBUG(DBG_SCOPE, "SkywatcherAltAzSimple::Sync");
994
995 // Compute a telescope direction vector from the current encoders
996 if (!GetEncoder(AXIS1))
997 return false;
998 if (!GetEncoder(AXIS2))
999 return false;
1000
1001 INDI::IHorizontalCoordinates AltAz { 0, 0 };
1002
1003 AltAz = GetAltAzPosition(ra, dec);
1004 double DeltaAz = CurrentAltAz.azimuth - AltAz.azimuth;
1005 double DeltaAlt = CurrentAltAz.altitude - AltAz.altitude;
1006
1007 LogMessage("SYNC: Ra %lf Dec %lf", ra, dec);
1008 MYDEBUGF(INDI::Logger::DBG_SESSION, "Sync ra: %lf dec: %lf => CurAz: %lf -> NewAz: %lf",
1009 ra, dec, CurrentAltAz.azimuth, AltAz.azimuth);
1014
1015 // The tracking seconds should be reset to restart the drift compensation
1016 ResetTrackingSeconds = true;
1017
1018 // Stop any movements
1020 {
1021 Abort();
1022 }
1023
1024 // Might as well do this
1025 UpdateDetailedMountInformation(true);
1026 return true;
1027}
1028
1029
1031{
1032 static bool Slewing = false;
1033 static bool Tracking = false;
1034 static int ElapsedTime = 0;
1035
1036 if (!ReadScopeStatus())
1037 {
1038 SetTimer(TimeoutDuration);
1039 return;
1040 }
1041
1042 LogMessage("SET TIMER: %d msec", TimeoutDuration);
1043 SetTimer(TimeoutDuration);
1044 ElapsedTime += TimeoutDuration;
1045 if (ElapsedTime >= 5000)
1046 {
1047 ElapsedTime = 0;
1048 VerboseScopeStatus = true;
1049 }
1050
1051 switch (TrackState)
1052 {
1053 case SCOPE_SLEWING:
1054 if (!Slewing)
1055 {
1056 LOG_INFO("Slewing started");
1057 TrackingStartTimer = 0;
1058 }
1059 TrackingMsecs = 0;
1060 GuideDeltaAlt = 0;
1061 GuideDeltaAz = 0;
1062 ResetGuidePulses();
1063 TimeoutDuration = 400;
1064 Tracking = false;
1065 Slewing = true;
1066 GuidingPulses.clear();
1067 if ((AxesStatus[AXIS1].FullStop) && (AxesStatus[AXIS2].FullStop))
1068 {
1069 TrackingStartTimer += TimeoutDuration;
1070 if (TrackingStartTimer < 3000)
1071 return;
1072
1073 if (IUFindSwitch(&WedgeModeSP, "WEDGE_EQ")->s == ISS_ON ||
1074 IUFindSwitch(&CoordSP, "TRACK")->s == ISS_ON)
1075
1076 {
1077 // Goto has finished start tracking
1079 }
1080 else
1081 {
1083 break;
1084 }
1085 }
1086 break;
1087
1088 case SCOPE_TRACKING:
1089 {
1090 if (!Tracking)
1091 {
1092 LOG_INFO("Tracking started");
1093 TrackingMsecs = 0;
1094 TimeoutDuration = (int)IUFindNumber(&TrackingValuesNP, "TRACKING_TIMEOUT")->value;
1095 GuideDeltaAlt = 0;
1096 GuideDeltaAz = 0;
1097 ResetGuidePulses();
1098 }
1099
1100 if (moving)
1101 {
1102 CurrentTrackingTarget.rightascension = EqN[AXIS_RA].value;
1103 CurrentTrackingTarget.declination = EqN[AXIS_DE].value;
1104 }
1105 else
1106 {
1107 // Restart the drift compensation after syncing
1108 if (ResetTrackingSeconds)
1109 {
1110 ResetTrackingSeconds = false;
1111 TrackingMsecs = 0;
1112 GuideDeltaAlt = 0;
1113 GuideDeltaAz = 0;
1114 ResetGuidePulses();
1115 }
1116 TrackingMsecs += TimeoutDuration;
1117 if (TrackingMsecs % 60000 == 0)
1118 {
1119 DEBUGF(INDI::Logger::DBG_SESSION, "Tracking in progress (%d seconds elapsed)", TrackingMsecs / 1000);
1120 }
1121 Tracking = true;
1122 Slewing = false;
1123 // Continue or start tracking
1124 // INDI::IHorizontalCoordinates AltAz { 0, 0 };
1125 INDI::IHorizontalCoordinates FutureAltAz { 0, 0 };
1126
1127 // AltAz.altitude = MicrostepsToDegrees(AXIS2, CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2]);
1128 // AltAz.azimuth = MicrostepsToDegrees(AXIS1, CurrentEncoders[AXIS1] - ZeroPositionEncoders[AXIS1]);
1129 FutureAltAz = GetAltAzPosition(CurrentTrackingTarget.rightascension, CurrentTrackingTarget.declination,
1130 (double)TimeoutDuration / 1000);
1131 // DEBUGF(DBG_SCOPE,
1132 // "Tracking AXIS1 CurrentEncoder %ld OldTrackingTarget %ld AXIS2 CurrentEncoder %ld OldTrackingTarget "
1133 // "%ld",
1134 // CurrentEncoders[AXIS1], OldTrackingTarget[AXIS1], CurrentEncoders[AXIS2], OldTrackingTarget[AXIS2]);
1135 // DEBUGF(DBG_SCOPE,
1136 // "New Tracking Target Altitude %lf degrees %ld microsteps Azimuth %lf degrees %ld microsteps",
1137 // AltAz.altitude, DegreesToMicrosteps(AXIS2, AltAz.altitude), AltAz.azimuth, DegreesToMicrosteps(AXIS1, AltAz.azimuth));
1138
1139 // Calculate the auto-guiding delta degrees
1140 for (auto pulse : GuidingPulses)
1141 {
1142 GuideDeltaAlt += pulse.DeltaAlt;
1143 GuideDeltaAz += pulse.DeltaAz;
1144 }
1145 GuidingPulses.clear();
1146
1147 long AltitudeOffsetMicrosteps = DegreesToMicrosteps(AXIS2, FutureAltAz.altitude - CurrentAltAz.altitude + GuideDeltaAlt);
1148 long AzimuthOffsetMicrosteps = DegreesToMicrosteps(AXIS1, FutureAltAz.azimuth - CurrentAltAz.azimuth + GuideDeltaAz);
1149
1150 // When the Alt/Az mount is on the top of an EQ mount, the EQ mount already tracks in
1151 // sidereal speed. Only autoguiding is enabled in tracking mode.
1152 if (IUFindSwitch(&WedgeModeSP, "WEDGE_EQ")->s == ISS_ON)
1153 {
1154 AltitudeOffsetMicrosteps = (long)((float)IUFindNumber(&GuidingRatesNP, "GUIDEDEC_RATE")->value * GuideDeltaAlt);
1155 AzimuthOffsetMicrosteps = (long)((float)IUFindNumber(&GuidingRatesNP, "GUIDERA_RATE")->value * GuideDeltaAz);
1156 GuideDeltaAlt = 0;
1157 GuideDeltaAz = 0;
1158 // Correct the movements of the EQ mount
1159 double DeltaAz = CurrentAltAz.azimuth - FutureAltAz.azimuth;
1160 double DeltaAlt = CurrentAltAz.altitude - FutureAltAz.altitude;
1161
1166 }
1167
1168 if (AltitudeOffsetMicrosteps > MicrostepsPerRevolution[AXIS2] / 2)
1169 {
1170 // Going the long way round - send it the other way
1171 AltitudeOffsetMicrosteps -= MicrostepsPerRevolution[AXIS2];
1172 }
1173 if (AzimuthOffsetMicrosteps > MicrostepsPerRevolution[AXIS1] / 2)
1174 {
1175 // Going the long way round - send it the other way
1176 AzimuthOffsetMicrosteps -= MicrostepsPerRevolution[AXIS1];
1177 }
1178 if (AltitudeOffsetMicrosteps < -MicrostepsPerRevolution[AXIS2] / 2)
1179 {
1180 // Going the long way round - send it the other way
1181 AltitudeOffsetMicrosteps += MicrostepsPerRevolution[AXIS2];
1182 }
1183 if (AzimuthOffsetMicrosteps < -MicrostepsPerRevolution[AXIS1] / 2)
1184 {
1185 // Going the long way round - send it the other way
1186 AzimuthOffsetMicrosteps += MicrostepsPerRevolution[AXIS1];
1187 }
1188
1189 AltitudeOffsetMicrosteps = (long)((double)AltitudeOffsetMicrosteps * IUFindNumber(&TrackingValuesNP,
1190 "TRACKING_RATE_ALT")->value);
1191 AzimuthOffsetMicrosteps = (long)((double)AzimuthOffsetMicrosteps * IUFindNumber(&TrackingValuesNP,
1192 "TRACKING_RATE_AZ")->value);
1193
1194 LogMessage("TRACKING: now Alt %lf Az %lf - future Alt %lf Az %lf - microsteps_diff Alt %ld Az %ld",
1195 CurrentAltAz.altitude, CurrentAltAz.azimuth, FutureAltAz.altitude, FutureAltAz.azimuth,
1196 AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
1197
1198 // DEBUGF(DBG_SCOPE, "New Tracking Target AltitudeOffset %ld microsteps AzimuthOffset %ld microsteps",
1199 // AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
1200
1201 if (0 != AzimuthOffsetMicrosteps)
1202 {
1203 SlewTo(AXIS1, AzimuthOffsetMicrosteps, false);
1204 }
1205 else
1206 {
1207 // Nothing to do - stop the axis
1208 SlowStop(AXIS1);
1209 }
1210
1211 if (0 != AltitudeOffsetMicrosteps)
1212 {
1213 SlewTo(AXIS2, AltitudeOffsetMicrosteps, false);
1214 }
1215 else
1216 {
1217 // Nothing to do - stop the axis
1218 SlowStop(AXIS2);
1219 }
1220
1221 DEBUGF(DBG_SCOPE, "Tracking - AXIS1 error %d (offset: %ld) AXIS2 error %d (offset: %ld)",
1222 OldTrackingTarget[AXIS1] - CurrentEncoders[AXIS1], AzimuthOffsetMicrosteps,
1223 OldTrackingTarget[AXIS2] - CurrentEncoders[AXIS2], AltitudeOffsetMicrosteps);
1224
1225 OldTrackingTarget[AXIS1] = AzimuthOffsetMicrosteps + CurrentEncoders[AXIS1];
1226 OldTrackingTarget[AXIS2] = AltitudeOffsetMicrosteps + CurrentEncoders[AXIS2];
1227 }
1228 break;
1229 }
1230 break;
1231
1232 default:
1233 if (Slewing)
1234 {
1235 LOG_INFO("Slewing stopped");
1236 }
1237 if (Tracking)
1238 {
1239 LOG_INFO("Tracking stopped");
1240 }
1241 TrackingMsecs = 0;
1242 GuideDeltaAlt = 0;
1243 GuideDeltaAz = 0;
1244 ResetGuidePulses();
1245 TimeoutDuration = 1000;
1246 Tracking = false;
1247 Slewing = false;
1248 GuidingPulses.clear();
1249 break;
1250 }
1251}
1252
1254{
1256
1257 if (isConnected())
1258 {
1259 // Fill in any real values now available MCInit should have been called already
1260 UpdateDetailedMountInformation(false);
1261
1262 // Define our connected only properties to the base driver
1263 // e.g. defineProperty(MyNumberVectorPointer);
1264 // This will register our properties and send a IDDefXXXX message to any connected clients
1265 // I have now idea why I have to do this here as well as in ISGetProperties. It makes me
1266 // concerned there is a design or implementation flaw somewhere.
1267 defineProperty(&BasicMountInfoTP);
1268 defineProperty(&AxisOneInfoNP);
1269 defineProperty(&AxisOneStateSP);
1270 defineProperty(&AxisTwoInfoNP);
1271 defineProperty(&AxisTwoStateSP);
1272 defineProperty(&AxisOneEncoderValuesNP);
1273 defineProperty(&AxisTwoEncoderValuesNP);
1274 defineProperty(&SlewModesSP);
1275 defineProperty(&WedgeModeSP);
1276 defineProperty(&TrackLogModeSP);
1277 defineProperty(&GuidingRatesNP);
1278 defineProperty(&TrackingValuesNP);
1279 defineProperty(&ParkMovementDirectionSP);
1280 defineProperty(&ParkPositionSP);
1281 defineProperty(&UnparkPositionSP);
1282
1285 return true;
1286 }
1287 else
1288 {
1289 // Delete any connected only properties from the base driver's list
1290 // e.g. deleteProperty(MyNumberVector.name);
1291 deleteProperty(BasicMountInfoTP.name);
1292 deleteProperty(AxisOneInfoNP.name);
1293 deleteProperty(AxisOneStateSP.name);
1294 deleteProperty(AxisTwoInfoNP.name);
1295 deleteProperty(AxisTwoStateSP.name);
1296 deleteProperty(AxisOneEncoderValuesNP.name);
1297 deleteProperty(AxisTwoEncoderValuesNP.name);
1298 deleteProperty(SlewModesSP.name);
1299 deleteProperty(WedgeModeSP.name);
1300 deleteProperty(TrackLogModeSP.name);
1301 deleteProperty(GuidingRatesNP.name);
1302 deleteProperty(TrackingValuesNP.name);
1303 deleteProperty(ParkMovementDirectionSP.name);
1304 deleteProperty(ParkPositionSP.name);
1305 deleteProperty(UnparkPositionSP.name);
1306
1309 return true;
1310 }
1311}
1312
1314{
1315 GuidingPulse Pulse;
1316
1317 LogMessage("GUIDE NORTH: %1.4f", ms);
1318 Pulse.DeltaAz = 0;
1319 Pulse.DeltaAlt = ms;
1320 GuidingPulses.push_back(Pulse);
1321 return IPS_OK;
1322}
1323
1325{
1326 GuidingPulse Pulse;
1327
1328 LogMessage("GUIDE SOUTH: %1.4f", ms);
1329 Pulse.DeltaAz = 0;
1330 Pulse.DeltaAlt = -ms;
1331 GuidingPulses.push_back(Pulse);
1332 return IPS_OK;
1333}
1334
1336{
1337 GuidingPulse Pulse;
1338
1339 LogMessage("GUIDE WEST: %1.4f", ms);
1340 Pulse.DeltaAz = ms;
1341 Pulse.DeltaAlt = 0;
1342 GuidingPulses.push_back(Pulse);
1343 return IPS_OK;
1344}
1345
1347{
1348 GuidingPulse Pulse;
1349
1350 LogMessage("GUIDE EAST: %1.4f", ms);
1351 Pulse.DeltaAz = -ms;
1352 Pulse.DeltaAlt = 0;
1353 GuidingPulses.push_back(Pulse);
1354 return IPS_OK;
1355}
1356
1357// Private methods
1358
1359void SkywatcherAltAzSimple::ResetGuidePulses()
1360{
1361 GuidingPulses.clear();
1362}
1363
1364int SkywatcherAltAzSimple::recover_tty_reconnect()
1365{
1366 if (!RecoverAfterReconnection && !SerialPortName.empty() && !FileExists(SerialPortName))
1367 {
1368 RecoverAfterReconnection = true;
1371 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
1372 if (!serialConnection->Connect())
1373 {
1374 RecoverAfterReconnection = true;
1375 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
1376 if (!serialConnection->Connect())
1377 {
1378 RecoverAfterReconnection = false;
1379 return 0;
1380 }
1381 }
1383 SerialPortName = serialConnection->port();
1384 RecoverAfterReconnection = false;
1385 return 1;
1386 }
1387 else
1388 {
1389 return -1;
1390 }
1391}
1392
1393void SkywatcherAltAzSimple::UpdateDetailedMountInformation(bool InformClient)
1394{
1395 bool BasicMountInfoHasChanged = false;
1396
1397 if (std::string(BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION].text) != std::to_string(MCVersion))
1398 {
1399 IUSaveText(&BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION], std::to_string(MCVersion).c_str());
1400 BasicMountInfoHasChanged = true;
1401 }
1402 if (std::string(BasicMountInfoT[MOUNT_CODE].text) != std::to_string(MountCode))
1403 {
1404 IUSaveText(&BasicMountInfoT[MOUNT_CODE], std::to_string(MountCode).c_str());
1405 BasicMountInfoHasChanged = true;
1406 }
1407 if (std::string(BasicMountInfoT[IS_DC_MOTOR].text) != std::to_string(IsDCMotor))
1408 {
1409 IUSaveText(&BasicMountInfoT[IS_DC_MOTOR], std::to_string(IsDCMotor).c_str());
1410 BasicMountInfoHasChanged = true;
1411 }
1412 if (BasicMountInfoHasChanged && InformClient)
1413 IDSetText(&BasicMountInfoTP, nullptr);
1414
1415 if (MountCode == 128)
1416 IUSaveText(&BasicMountInfoT[MOUNT_NAME], "Merlin");
1417 else if (MountCode >= 129 && MountCode <= 143)
1418 IUSaveText(&BasicMountInfoT[MOUNT_NAME], "Az Goto");
1419 else if (MountCode >= 144 && MountCode <= 159)
1420 IUSaveText(&BasicMountInfoT[MOUNT_NAME], "Dob Goto");
1421 else if (MountCode == 161)
1422 IUSaveText(&BasicMountInfoT[MOUNT_NAME], "Virtuoso");
1423 else if (MountCode >= 160)
1424 IUSaveText(&BasicMountInfoT[MOUNT_NAME], "AllView Goto");
1425
1426 bool AxisOneInfoHasChanged = false;
1427
1428 if (AxisOneInfoN[MICROSTEPS_PER_REVOLUTION].value != MicrostepsPerRevolution[0])
1429 {
1430 AxisOneInfoN[MICROSTEPS_PER_REVOLUTION].value = MicrostepsPerRevolution[0];
1431 AxisOneInfoHasChanged = true;
1432 }
1433 if (AxisOneInfoN[STEPPER_CLOCK_FREQUENCY].value != StepperClockFrequency[0])
1434 {
1435 AxisOneInfoN[STEPPER_CLOCK_FREQUENCY].value = StepperClockFrequency[0];
1436 AxisOneInfoHasChanged = true;
1437 }
1438 if (AxisOneInfoN[HIGH_SPEED_RATIO].value != HighSpeedRatio[0])
1439 {
1440 AxisOneInfoN[HIGH_SPEED_RATIO].value = HighSpeedRatio[0];
1441 AxisOneInfoHasChanged = true;
1442 }
1443 if (AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value != MicrostepsPerWormRevolution[0])
1444 {
1445 AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value = MicrostepsPerWormRevolution[0];
1446 AxisOneInfoHasChanged = true;
1447 }
1448 if (AxisOneInfoHasChanged && InformClient)
1449 IDSetNumber(&AxisOneInfoNP, nullptr);
1450
1451 bool AxisOneStateHasChanged = false;
1452 if (AxisOneStateS[FULL_STOP].s != (AxesStatus[0].FullStop ? ISS_ON : ISS_OFF))
1453 {
1454 AxisOneStateS[FULL_STOP].s = AxesStatus[0].FullStop ? ISS_ON : ISS_OFF;
1455 AxisOneStateHasChanged = true;
1456 }
1457 if (AxisOneStateS[SLEWING].s != (AxesStatus[0].Slewing ? ISS_ON : ISS_OFF))
1458 {
1459 AxisOneStateS[SLEWING].s = AxesStatus[0].Slewing ? ISS_ON : ISS_OFF;
1460 AxisOneStateHasChanged = true;
1461 }
1462 if (AxisOneStateS[SLEWING_TO].s != (AxesStatus[0].SlewingTo ? ISS_ON : ISS_OFF))
1463 {
1464 AxisOneStateS[SLEWING_TO].s = AxesStatus[0].SlewingTo ? ISS_ON : ISS_OFF;
1465 AxisOneStateHasChanged = true;
1466 }
1467 if (AxisOneStateS[SLEWING_FORWARD].s != (AxesStatus[0].SlewingForward ? ISS_ON : ISS_OFF))
1468 {
1469 AxisOneStateS[SLEWING_FORWARD].s = AxesStatus[0].SlewingForward ? ISS_ON : ISS_OFF;
1470 AxisOneStateHasChanged = true;
1471 }
1472 if (AxisOneStateS[HIGH_SPEED].s != (AxesStatus[0].HighSpeed ? ISS_ON : ISS_OFF))
1473 {
1474 AxisOneStateS[HIGH_SPEED].s = AxesStatus[0].HighSpeed ? ISS_ON : ISS_OFF;
1475 AxisOneStateHasChanged = true;
1476 }
1477 if (AxisOneStateS[NOT_INITIALISED].s != (AxesStatus[0].NotInitialized ? ISS_ON : ISS_OFF))
1478 {
1479 AxisOneStateS[NOT_INITIALISED].s = AxesStatus[0].NotInitialized ? ISS_ON : ISS_OFF;
1480 AxisOneStateHasChanged = true;
1481 }
1482 if (AxisOneStateHasChanged && InformClient)
1483 IDSetSwitch(&AxisOneStateSP, nullptr);
1484
1485 bool AxisTwoInfoHasChanged = false;
1486 if (AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION].value != MicrostepsPerRevolution[1])
1487 {
1488 AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION].value = MicrostepsPerRevolution[1];
1489 AxisTwoInfoHasChanged = true;
1490 }
1491 if (AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY].value != StepperClockFrequency[1])
1492 {
1493 AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY].value = StepperClockFrequency[1];
1494 AxisTwoInfoHasChanged = true;
1495 }
1496 if (AxisTwoInfoN[HIGH_SPEED_RATIO].value != HighSpeedRatio[1])
1497 {
1498 AxisTwoInfoN[HIGH_SPEED_RATIO].value = HighSpeedRatio[1];
1499 AxisTwoInfoHasChanged = true;
1500 }
1501 if (AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value != MicrostepsPerWormRevolution[1])
1502 {
1503 AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value = MicrostepsPerWormRevolution[1];
1504 AxisTwoInfoHasChanged = true;
1505 }
1506 if (AxisTwoInfoHasChanged && InformClient)
1507 IDSetNumber(&AxisTwoInfoNP, nullptr);
1508
1509 bool AxisTwoStateHasChanged = false;
1510 if (AxisTwoStateS[FULL_STOP].s != (AxesStatus[1].FullStop ? ISS_ON : ISS_OFF))
1511 {
1512 AxisTwoStateS[FULL_STOP].s = AxesStatus[1].FullStop ? ISS_ON : ISS_OFF;
1513 AxisTwoStateHasChanged = true;
1514 }
1515 if (AxisTwoStateS[SLEWING].s != (AxesStatus[1].Slewing ? ISS_ON : ISS_OFF))
1516 {
1517 AxisTwoStateS[SLEWING].s = AxesStatus[1].Slewing ? ISS_ON : ISS_OFF;
1518 AxisTwoStateHasChanged = true;
1519 }
1520 if (AxisTwoStateS[SLEWING_TO].s != (AxesStatus[1].SlewingTo ? ISS_ON : ISS_OFF))
1521 {
1522 AxisTwoStateS[SLEWING_TO].s = AxesStatus[1].SlewingTo ? ISS_ON : ISS_OFF;
1523 AxisTwoStateHasChanged = true;
1524 }
1525 if (AxisTwoStateS[SLEWING_FORWARD].s != (AxesStatus[1].SlewingForward ? ISS_ON : ISS_OFF))
1526 {
1527 AxisTwoStateS[SLEWING_FORWARD].s = AxesStatus[1].SlewingForward ? ISS_ON : ISS_OFF;
1528 AxisTwoStateHasChanged = true;
1529 }
1530 if (AxisTwoStateS[HIGH_SPEED].s != (AxesStatus[1].HighSpeed ? ISS_ON : ISS_OFF))
1531 {
1532 AxisTwoStateS[HIGH_SPEED].s = AxesStatus[1].HighSpeed ? ISS_ON : ISS_OFF;
1533 AxisTwoStateHasChanged = true;
1534 }
1535 if (AxisTwoStateS[NOT_INITIALISED].s != (AxesStatus[1].NotInitialized ? ISS_ON : ISS_OFF))
1536 {
1537 AxisTwoStateS[NOT_INITIALISED].s = AxesStatus[1].NotInitialized ? ISS_ON : ISS_OFF;
1538 AxisTwoStateHasChanged = true;
1539 }
1540 if (AxisTwoStateHasChanged && InformClient)
1541 IDSetSwitch(&AxisTwoStateSP, nullptr);
1542
1543 bool AxisOneEncoderValuesHasChanged = false;
1544 if ((AxisOneEncoderValuesN[RAW_MICROSTEPS].value != CurrentEncoders[AXIS1]) ||
1545 (AxisOneEncoderValuesN[OFFSET_FROM_INITIAL].value != CurrentEncoders[AXIS1] - ZeroPositionEncoders[AXIS1]))
1546 {
1547 AxisOneEncoderValuesN[RAW_MICROSTEPS].value = CurrentEncoders[AXIS1];
1548 AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC].value = MicrostepsPerDegree[AXIS1] / 3600.0;
1549 AxisOneEncoderValuesN[OFFSET_FROM_INITIAL].value = CurrentEncoders[AXIS1] - ZeroPositionEncoders[AXIS1];
1550 AxisOneEncoderValuesN[DEGREES_FROM_INITIAL].value =
1552 AxisOneEncoderValuesHasChanged = true;
1553 }
1554 if (AxisOneEncoderValuesHasChanged && InformClient)
1555 IDSetNumber(&AxisOneEncoderValuesNP, nullptr);
1556
1557 bool AxisTwoEncoderValuesHasChanged = false;
1558 if ((AxisTwoEncoderValuesN[RAW_MICROSTEPS].value != CurrentEncoders[AXIS2]) ||
1559 (AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL].value != CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2]))
1560 {
1561 AxisTwoEncoderValuesN[RAW_MICROSTEPS].value = CurrentEncoders[AXIS2];
1562 AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC].value = MicrostepsPerDegree[AXIS2] / 3600.0;
1563 AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL].value = CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2];
1564 AxisTwoEncoderValuesN[DEGREES_FROM_INITIAL].value =
1566 AxisTwoEncoderValuesHasChanged = true;
1567 }
1568 if (AxisTwoEncoderValuesHasChanged && InformClient)
1569 IDSetNumber(&AxisTwoEncoderValuesNP, nullptr);
1570}
1571
1572
1573INDI::IHorizontalCoordinates SkywatcherAltAzSimple::GetAltAzPosition(double ra, double dec, double offset_in_sec)
1574{
1577 INDI::IGeographicCoordinates Location {LocationN[LOCATION_LONGITUDE].value, LocationN[LOCATION_LATITUDE].value, 0};
1578 double JulianOffset = offset_in_sec / (24.0 * 60 * 60);
1579
1580 // Set the current location
1581 if (IUFindSwitch(&WedgeModeSP, "WEDGE_SIMPLE")->s != ISS_OFF ||
1582 IUFindSwitch(&WedgeModeSP, "WEDGE_EQ")->s != ISS_OFF)
1583 {
1584 if (LocationN[LOCATION_LATITUDE].value > 0)
1585 {
1586 Location.latitude = 90;
1587 Location.longitude = 0;
1588 }
1589 else
1590 {
1591 Location.latitude = -90;
1592 Location.longitude = 0;
1593 }
1594 }
1595
1596 INDI::EquatorialToHorizontal(&Eq, &m_Location, ln_get_julian_from_sys() + JulianOffset, &AltAz);
1597 return AltAz;
1598}
1599
1600
1601INDI::IEquatorialCoordinates SkywatcherAltAzSimple::GetRaDecPosition(double alt, double az)
1602{
1603 INDI::IGeographicCoordinates Location { LocationN[LOCATION_LONGITUDE].value, LocationN[LOCATION_LATITUDE].value, 0 };
1604 INDI::IEquatorialCoordinates Eq { 0, 0 };
1606
1607 // Set the current location
1608 if (IUFindSwitch(&WedgeModeSP, "WEDGE_SIMPLE")->s != ISS_OFF ||
1609 IUFindSwitch(&WedgeModeSP, "WEDGE_EQ")->s != ISS_OFF)
1610 {
1611 if (LocationN[LOCATION_LATITUDE].value > 0)
1612 {
1613 Location.latitude = 90;
1614 Location.longitude = 0;
1615 }
1616 else
1617 {
1618 Location.latitude = -90;
1619 Location.longitude = 0;
1620 }
1621 }
1622
1623 INDI::HorizontalToEquatorial(&AltAz, &m_Location, ln_get_julian_from_sys(), &Eq);
1624 return Eq;
1625}
1626
1627
1628void SkywatcherAltAzSimple::LogMessage(const char* format, ...)
1629{
1630 if (!format || IUFindSwitch(&TrackLogModeSP, "TRACKLOG_ENABLED")->s == ISS_OFF)
1631 return;
1632
1633 va_list Ap;
1634 va_start(Ap, format);
1635
1636 char TempStr[512];
1637 std::ofstream LogFile;
1638
1639 LogFile.open(TrackLogFileName.c_str(), std::ios::out | std::ios::app);
1640 if (!LogFile.is_open())
1641 {
1642 return;
1643 }
1644 vsnprintf(TempStr, sizeof(TempStr), format, Ap);
1645 LogFile << GetLogTimestamp() << " | " << TempStr << "\n";
1646 LogFile.close();
1647 va_end(Ap);
1648}
The Interface class is the base class for all INDI connection plugins.
virtual std::string name()=0
virtual const char * port()
bool Refresh(bool silent=false)
virtual bool Disconnect() override
Disconnect Disconnect from device.
virtual bool Connect() override
Connect Connect to device via the implemented communication medium. Do not perform any handshakes.
bool isConnected() const
INDI::PropertySwitch getSwitch(const char *name) const
void addConfigurationControl()
Add Configuration control to the driver.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getDriverInterface() const
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
Connection::Interface * getActiveConnection()
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber GuideNSNP
INDI::PropertyNumber GuideWENP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
TelescopeStatus TrackState
bool CheckFile(const std::string &file_name, bool writable) const
Check if a file exists and it is readable.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
bool isParked()
isParked is mount currently parked?
Connection::Serial * serialConnection
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void SetSerialPort(int port)
Set the serail port to be usb for mount communication.
bool GetMotorBoardVersion(AXISID Axis)
static constexpr double LOW_SPEED_MARGIN
double MicrostepsToDegrees(AXISID Axis, long Microsteps)
Convert microsteps to angle in degrees.
unsigned long MountCode
void SlewTo(AXISID Axis, long OffsetInMicrosteps, bool verbose=true)
Slew to the given offset and stop.
long ZeroPositionEncoders[2]
Zero position encoder values (microsteps).
unsigned int DBG_SCOPE
long CurrentEncoders[2]
Current encoder values (microsteps).
bool GetStatus(AXISID Axis)
unsigned long MCVersion
long PolarisPositionEncoders[2]
Polaris position (initial) encoder values (microsteps).
bool GetEncoder(AXISID Axis)
Set the CurrentEncoders status variable to the current encoder value in microsteps for the specified ...
long MicrostepsPerWormRevolution[2]
long HighSpeedRatio[2]
double MicrostepsPerDegree[2]
long DegreesToMicrosteps(AXISID Axis, double AngleInDegrees)
Convert angle in degrees to microsteps.
bool SlowStop(AXISID Axis)
Bring the axis to slow stop in the distance specified by SetSlewModeDeccelerationRampLength.
bool InitMount()
Initialize the communication to the mount.
void Slew(AXISID Axis, double SpeedInRadiansPerSecond, bool IgnoreSilentMode=true)
Start the axis slewing at the given rate.
long MicrostepsPerRevolution[2]
bool IsInMotion(AXISID Axis)
Check if an axis is moving.
long StepperClockFrequency[2]
INDI::Telescope * pChildTelescope
AXISSTATUS AxesStatus[2]
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual const char * getDefaultName() override
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool Park() override
Park the telescope to its home position.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual void TimerHit() override
Called when setTimer() time is up.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
double GetParkDeltaAz(ParkDirection_t target_direction, ParkPosition_t target_position)
const char * GUIDE_TAB
GUIDE_TAB Where all the properties for guiding are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
INDI::PropertyNumber EqNP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch CoordSP
double ra
double dec
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
#define MAXINDILABEL
Definition indiapi.h:190
@ ISR_NOFMANY
Definition indiapi.h:173
@ ISR_ATMOST1
Definition indiapi.h:172
#define MAXINDINAME
Definition indiapi.h:189
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_NORTH
void tty_set_generic_udp_format(int enabled)
Definition indicom.c:364
void IDLog(const char *fmt,...)
Definition indicom.c:305
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition indicom.c:130
Implementations for common driver routines.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
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:490
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition indidevapi.c:275
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition indidevapi.c:285
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition indidevapi.c:317
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition indidevapi.c:416
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:398
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
void IDMessage(const char *dev, const char *fmt,...)
Definition indidriver.c:983
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition indilogger.h:56
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_INFO(txt)
Definition indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define getDeviceName
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition lilxml.cpp:555
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition lilxml.cpp:524
LilXML * newLilXML()
Create a new lilxml parser.
Definition lilxml.cpp:150
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition lilxml.cpp:600
XMLEle * findXMLEle(XMLEle *ep, const char *tag)
Find an XML element within an XML element.
Definition lilxml.cpp:537
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition lilxml.cpp:624
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition lilxml.cpp:167
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition lilxml.cpp:606
void delLilXML(LilXML *lp)
Delete a lilxml parser.
Definition lilxml.cpp:159
XMLEle * readXMLFile(FILE *fp, LilXML *lp, char ynot[])
Handy wrapper to read one xml file.
Definition lilxml.cpp:653
#define XML_ERROR_SIZE
Definition lilxml.h:73
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:141
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition libastro.cpp:161
#define SLEWMODES
Definition paramount.cpp:68
#define MYDEBUGF(priority, msg,...)
#define SLEWMODES
double SlewSpeeds[SLEWMODES]
std::unique_ptr< SkywatcherAltAzSimple > SkywatcherAltAzSimplePtr(new SkywatcherAltAzSimple())
void ISPoll(void *p)
Definition fli_wheel.c:393
@ PARK_CLOCKWISE
@ PARK_COUNTERCLOCKWISE
bool NotInitialized
bool SlewingForward
One number descriptor.
One switch descriptor.
char name[MAXINDINAME]
Definition indiapi.h:321
Switch vector property descriptor.
Definition indiapi.h:365
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248