Instrument Neutral Distributed Interface INDI 2.2.5
lx200_OnStep.cpp
Go to the documentation of this file.
1/*
2 LX200 LX200_OnStep
3 Based on LX200 classic, ([email protected])
4 Contributors:
5 James Lan https://github.com/james-lan
6 Ray Wells https://github.com/blueshawk
7
8 Copyright (C) 2003-2026 Jasem Mutlaq ([email protected])
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
24*/
25
26//#define DEBUG_TRACKSTATE
27
28
29#include "lx200_OnStep.h"
30
31
32#include <mutex>
33
34#define LIBRARY_TAB "Library"
35#define FIRMWARE_TAB "Firmware data"
36#define STATUS_TAB "ONStep Status"
37#define PEC_TAB "PEC"
38#define ALIGN_TAB "Align"
39#define OUTPUT_TAB "Outputs"
40#define ENVIRONMENT_TAB "Weather"
41#define ROTATOR_TAB "Rotator"
42
43#define RA_AXIS 0
44#define DEC_AXIS 1
45
46extern std::mutex lx200CommsLock;
47
48
50{
51 currentCatalog = LX200_STAR_C;
52 currentSubCatalog = 0;
53
54 setVersion(1, 27); // don't forget to update libindi/drivers.xml
55
58
64
65 //CAN_ABORT, CAN_GOTO ,CAN_PARK ,CAN_SYNC ,HAS_LOCATION ,HAS_TIME ,HAS_TRACK_MODE Already inherited from lx200generic,
66 // 4 stands for the number of Slewrate Buttons as defined in Inditelescope.cpp
67 //setLX200Capability(LX200_HAS_FOCUS | LX200_HAS_TRACKING_FREQ | LX200_HAS_ALIGNMENT_TYPE | LX200_HAS_SITES | LX200_HAS_PULSE_GUIDING);
68 //
69 // Get generic capabilities but discard the following:
70 // LX200_HAS_FOCUS
71
72
74 // Unused option: FOCUSER_HAS_VARIABLE_SPEED
75
77 // /*{
78 // ROTATOR_CAN_ABORT = 1 << 0, /*!< Can the Rotator abort motion once started? */
79 // ROTATOR_CAN_HOME = 1 << 1, /*!< Can the Rotator go to home position? */
80 // ROTATOR_CAN_SYNC = 1 << 2, /*!< Can the Rotator sync to specific tick? */ /*Not supported */
81 // ROTATOR_CAN_REVERSE = 1 << 3, /*!< Can the Rotator reverse direction? */ //It CAN reverse, but there's no way to query the direction
82 // ROTATOR_HAS_BACKLASH = 1 << 4 /*!< Can the Rotatorer compensate for backlash? */
83 // //}*/
84
85}
86
88{
89 return "LX200 OnStep";
90}
91
93{
94
100
102
103 //FocuserInterface
104 //Initial, these will be updated later.
105 FocusRelPosNP[0].setMin(0.);
106 FocusRelPosNP[0].setMax(30000.);
107 FocusRelPosNP[0].setValue(0);
108 FocusRelPosNP[0].setStep(10);
109 FocusAbsPosNP[0].setMin(0.);
110 FocusAbsPosNP[0].setMax(60000.);
111 FocusAbsPosNP[0].setValue(0);
112 FocusAbsPosNP[0].setStep(10);
113
114
115 // ============== MAIN_CONTROL_TAB
116 ReticSP[0].fill("PLUS", "Light", ISS_OFF);
117 ReticSP[1].fill("MOINS", "Dark", ISS_OFF);
118 ReticSP.fill(getDeviceName(), "RETICULE_BRIGHTNESS", "Reticule +/-", MAIN_CONTROL_TAB, IP_RW,
119 ISR_ATMOST1, 60, IPS_IDLE);
120
121 ElevationLimitNP[0].fill("minAlt", "Elev Min", "%g", -30, 30, 1, -30);
122 ElevationLimitNP[1].fill("maxAlt", "Elev Max", "%g", 60, 90, 1, 89);
123 ElevationLimitNP.fill(getDeviceName(), "Slew elevation Limit", "", MAIN_CONTROL_TAB, IP_RW, 0, IPS_IDLE);
124
125 ObjectInfoTP[0].fill("Info", "", "");
126 ObjectInfoTP.fill(getDeviceName(), "Object Info", "", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
127
128 // ============== COMMUNICATION_TAB
129
130 // ============== CONNECTION_TAB
131
132 // ============== OPTIONS_TAB
133
134 // ============== FILTER_TAB
135
136 // ============== MOTION_TAB
137 //Override the standard slew rate command. Also add appropriate description. This also makes it work in Ekos Mount Control correctly
138 //Note that SlewRateSP and MaxSlewRateNP BOTH track the rate. I have left them in there because MaxRateNP reports OnStep Values
139
140 MaxSlewRateNP[0].fill("maxSlew", "Rate", "%f", 0.0, 9.0, 1.0, 5.0);
141 MaxSlewRateNP.fill(getDeviceName(), "Max slew Rate", "", MOTION_TAB, IP_RW, 0, IPS_IDLE);
142
143 TrackCompSP[0].fill("1", "Full Compensation", ISS_OFF);
144 TrackCompSP[1].fill("2", "Refraction", ISS_OFF);
145 TrackCompSP[2].fill("3", "Off", ISS_ON);
146 TrackCompSP.fill(getDeviceName(), "Compensation", "Compensation Tracking", MOTION_TAB, IP_RW,
148
149 TrackAxisSP[0].fill("1", "Single Axis", ISS_OFF);
150 TrackAxisSP[1].fill("2", "Dual Axis", ISS_OFF);
151 TrackAxisSP.fill(getDeviceName(), "Multi-Axis", "Multi-Axis Tracking", MOTION_TAB, IP_RW,
153
154 BacklashNP[0].fill("Backlash DEC", "DE", "%g", 0, 3600, 1, 15);
155 BacklashNP[1].fill("Backlash RA", "RA", "%g", 0, 3600, 1, 15);
156 BacklashNP.fill(getDeviceName(), "Backlash", "", MOTION_TAB, IP_RW, 0, IPS_IDLE);
157
158 GuideRateNP[RA_AXIS].fill("GUIDE_RATE_WE", "W/E Rate", "%g", 0, 1, 0.25, 0.5);
159 GuideRateNP[DEC_AXIS].fill("GUIDE_RATE_NS", "N/S Rate", "%g", 0, 1, 0.25, 0.5);
160 GuideRateNP.fill(getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RO, 0, IPS_IDLE);
161
162 AutoFlipSP[0].fill("1", "AutoFlip: OFF", ISS_OFF);
163 AutoFlipSP[1].fill("2", "AutoFlip: ON", ISS_OFF);
164 AutoFlipSP.fill(getDeviceName(), "AutoFlip", "Meridian Auto Flip", MOTION_TAB, IP_RW,
166
167 HomePauseSP[0].fill("1", "HomePause: OFF", ISS_OFF);
168 HomePauseSP[1].fill("2", "HomePause: ON", ISS_OFF);
169 HomePauseSP[2].fill("3", "HomePause: Continue", ISS_OFF);
170 HomePauseSP.fill(getDeviceName(), "HomePause", "Pause at Home", MOTION_TAB, IP_RW,
172
173 FrequencyAdjustSP[0].fill("1", "Frequency -", ISS_OFF);
174 FrequencyAdjustSP[1].fill("2", "Frequency +", ISS_OFF);
175 FrequencyAdjustSP[2].fill("3", "Reset Sidereal Frequency", ISS_OFF);
176 FrequencyAdjustSP.fill(getDeviceName(), "FrequencyAdjust", "Frequency Adjust",
178
179 PreferredPierSideSP[0].fill("1", "West", ISS_OFF);
180 PreferredPierSideSP[1].fill("2", "East", ISS_OFF);
181 PreferredPierSideSP[2].fill("3", "Best", ISS_OFF);
182 PreferredPierSideSP.fill(getDeviceName(), "Preferred Pier Side",
183 "Preferred Pier Side", MOTION_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
184
185 minutesPastMeridianNP[0].fill("East", "East ± 180", "%g", -180, 180, 1, 20);
186 minutesPastMeridianNP[1].fill("West", "West ± 180", "%g", -180, 180, 1, -20);
187 minutesPastMeridianNP.fill(getDeviceName(), "Minutes Past Meridian",
188 "Minutes Past Meridian", MOTION_TAB, IP_RW, 0, IPS_IDLE);
189
190
191 // ============== DATETIME_TAB
192
193 // ============== SITE_TAB
194 // IUFillSwitch(&SetHomeS[0], "RETURN_HOME", "Return Home", ISS_OFF);
195 // IUFillSwitch(&SetHomeS[1], "AT_HOME", "At Home (Reset)", ISS_OFF);
196 // IUFillSwitchVector(&SetHomeSP, SetHomeS, 2, getDeviceName(), "HOME_INIT", "Homing", SITE_TAB, IP_RW, ISR_ATMOST1, 60,
197 // IPS_IDLE);
198
199 // ============== GUIDE_TAB
200
201 // ============== FOCUS_TAB
202 // Focuser 1
203
204 OSFocus1InitializeSP[0].fill("Focus1_0", "Zero", ISS_OFF);
205 OSFocus1InitializeSP[1].fill("Focus1_2", "Mid", ISS_OFF);
206 // OSFocus1InitializeSP[2].fill("Focus1_3", "max", ISS_OFF);
207 OSFocus1InitializeSP.fill(getDeviceName(), "Foc1Rate", "Initialize", FOCUS_TAB,
209 // Focus T° Compensation
210 // Property must be FOCUS_TEMPERATURE to be recognized by Ekos
211 FocusTemperatureNP[0].fill("FOCUS_TEMPERATURE", "TFC T°", "%+2.2f", 0, 1, 0.25,
212 25); //default value is meaningless
213 FocusTemperatureNP[1].fill("TFC Δ T°", "TFC Δ T°", "%+2.2f", 0, 1, 0.25, 25); //default value is meaningless
214 FocusTemperatureNP.fill(getDeviceName(), "FOCUS_TEMPERATURE", "Focuser T°",
215 FOCUS_TAB, IP_RO, 0,
216 IPS_IDLE);
217 TFCCompensationSP[0].fill("Off", "Compensation: OFF", ISS_OFF);
218 TFCCompensationSP[1].fill("On", "Compensation: ON", ISS_OFF);
219 TFCCompensationSP.fill(getDeviceName(), "Compensation T°", "Temperature Compensation",
222
223 TFCCoefficientNP[0].fill("TFC Coefficient", "TFC Coefficient µm/°C", "%+03.5f", -999.99999, 999.99999, 1, 100);
224 TFCCoefficientNP.fill(getDeviceName(), "TFC Coefficient", "", FOCUS_TAB, IP_RW, 0,
225 IPS_IDLE);
226 TFCDeadbandNP[0].fill("TFC Deadband", "TFC Deadband µm", "%g", 1, 32767, 1, 5);
227 TFCDeadbandNP.fill(getDeviceName(), "TFC Deadband", "", FOCUS_TAB, IP_RW, 0, IPS_IDLE);
228 // End Focus T° Compensation
229
230 OSFocusSelectSP[0].fill("Focuser_Primary_1", "Focuser 1", ISS_ON);
231 OSFocusSelectSP[1].fill("Focuser_Primary_2", "Focuser 2/Swap", ISS_OFF);
232 // For when OnStepX comes out
233 OSFocusSelectSP[2].fill("Focuser_Primary_3", "3", ISS_OFF);
234 OSFocusSelectSP[3].fill("Focuser_Primary_4", "4", ISS_OFF);
235 OSFocusSelectSP[4].fill("Focuser_Primary_5", "5", ISS_OFF);
236 OSFocusSelectSP[5].fill("Focuser_Primary_6", "6", ISS_OFF);
237 OSFocusSelectSP[6].fill("Focuser_Primary_7", "7", ISS_OFF);
238 OSFocusSelectSP[7].fill("Focuser_Primary_8", "8", ISS_OFF);
239 OSFocusSelectSP[8].fill("Focuser_Primary_9", "9", ISS_OFF);
240 OSFocusSelectSP[9].fill("Focuser_Primary_10", "10", ISS_OFF);
241
242 OSFocusSelectSP.fill(getDeviceName(), "OSFocusSWAP", "Primary Focuser", FOCUS_TAB,
244
245
246 // Focuser 2
247 //OSFocus2SelSP[0].fill("Focus2_Sel1", "Foc 1", ISS_OFF);
248 //OSFocus2SelSP[1].fill("Focus2_Sel2", "Foc 2", ISS_OFF);
249 //OSFocus2SelSP.fill(getDeviceName(), "Foc2Sel", "Foc 2", FOCUS_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
250
251 OSFocus2MotionSP[0].fill("Focus2_In", "In", ISS_OFF);
252 OSFocus2MotionSP[1].fill("Focus2_Out", "Out", ISS_OFF);
253 OSFocus2MotionSP[2].fill("Focus2_Stop", "Stop", ISS_OFF);
254 OSFocus2MotionSP.fill(getDeviceName(), "Foc2Mot", "Foc 2 Motion", FOCUS_TAB, IP_RW,
256
257 OSFocus2RateSP[0].fill("Focus2_1", "min", ISS_OFF);
258 OSFocus2RateSP[1].fill("Focus2_2", "0.01", ISS_OFF);
259 OSFocus2RateSP[2].fill("Focus2_3", "0.1", ISS_OFF);
260 OSFocus2RateSP[3].fill("Focus2_4", "1", ISS_OFF);
261 OSFocus2RateSP.fill(getDeviceName(), "Foc2Rate", "Foc 2 Rates", FOCUS_TAB, IP_RW,
263
264 OSFocus2TargNP[0].fill("FocusTarget2", "Abs Pos", "%g", -25000, 25000, 1, 0);
265 OSFocus2TargNP.fill(getDeviceName(), "Foc2Targ", "Foc 2 Target", FOCUS_TAB, IP_RW, 0,
266 IPS_IDLE);
267
268 // =========== ROTATOR TAB
269
270 OSRotatorDerotateSP[0].fill("Derotate_OFF", "OFF", ISS_OFF);
271 OSRotatorDerotateSP[1].fill("Derotate_ON", "ON", ISS_OFF);
272 OSRotatorDerotateSP.fill(getDeviceName(), "Derotate_Status", "DEROTATE", ROTATOR_TAB,
273 IP_RW,
275
276 // ============== FIRMWARE_TAB
277 VersionTP[0].fill("Date", "", "");
278 VersionTP[1].fill("Time", "", "");
279 VersionTP[2].fill("Number", "", "");
280 VersionTP[3].fill("Name", "", "");
281 VersionTP.fill(getDeviceName(), "Firmware Info", "", FIRMWARE_TAB, IP_RO, 0, IPS_IDLE);
282
283 //PEC Tab
284 OSPECStatusSP[0].fill("OFF", "OFF", ISS_OFF);
285 OSPECStatusSP[1].fill("Playing", "Playing", ISS_OFF);
286 OSPECStatusSP[2].fill("Recording", "Recording", ISS_OFF);
287 OSPECStatusSP[3].fill("Will Play", "Will Play", ISS_OFF);
288 OSPECStatusSP[4].fill("Will Record", "Will Record", ISS_OFF);
289 OSPECStatusSP.fill(getDeviceName(), "PEC Status", "PEC Status", PEC_TAB, IP_RO,
291
292 OSPECIndexSP[0].fill("Not Detected", "Not Detected", ISS_ON);
293 OSPECIndexSP[1].fill("Detected", "Detected", ISS_OFF);
294 OSPECIndexSP.fill(getDeviceName(), "PEC Index Detect", "PEC Index", PEC_TAB, IP_RO,
296
297 OSPECRecordSP[0].fill("Clear", "Clear", ISS_OFF);
298 OSPECRecordSP[1].fill("Record", "Record", ISS_OFF);
299 OSPECRecordSP[2].fill("Write to EEPROM", "Write to EEPROM", ISS_OFF);
300 OSPECRecordSP.fill(getDeviceName(), "PEC Operations", "PEC Recording", PEC_TAB, IP_RW,
302
303 OSPECReadSP[0].fill("Read", "Read PEC to FILE****", ISS_OFF);
304 OSPECReadSP[1].fill("Write", "Write PEC from FILE***", ISS_OFF);
305 // OSPECReadSP[2].fill("Write to EEPROM", "Write to EEPROM", ISS_OFF);
306 OSPECReadSP.fill(getDeviceName(), "PEC File", "PEC File", PEC_TAB, IP_RW, ISR_ATMOST1, 0,
307 IPS_IDLE);
308 // ============== ALIGNMENT_TAB
309 // Only supports Alpha versions currently (July 2018) Now Beta (Dec 2018)
310 OSNAlignStarsSP[0].fill("1", "1 Star", ISS_OFF);
311 OSNAlignStarsSP[1].fill("2", "2 Stars", ISS_OFF);
312 OSNAlignStarsSP[2].fill("3", "3 Stars", ISS_ON);
313 OSNAlignStarsSP[3].fill("4", "4 Stars", ISS_OFF);
314 OSNAlignStarsSP[4].fill("5", "5 Stars", ISS_OFF);
315 OSNAlignStarsSP[5].fill("6", "6 Stars", ISS_OFF);
316 OSNAlignStarsSP[6].fill("7", "7 Stars", ISS_OFF);
317 OSNAlignStarsSP[7].fill("8", "8 Stars", ISS_OFF);
318 OSNAlignStarsSP[8].fill("9", "9 Stars", ISS_OFF);
319 OSNAlignStarsSP.fill(getDeviceName(), "AlignStars", "Select # of stars",
321
322 OSNAlignSP[0].fill("0", "Start Align", ISS_OFF);
323 OSNAlignSP[1].fill("1", "Issue Align", ISS_OFF);
324 OSNAlignSP.fill(getDeviceName(), "NewAlignStar", "Align using up to 9 stars",
326
327 OSNAlignWriteSP[0].fill("0", "Write Align to NVRAM/Flash", ISS_OFF);
328 OSNAlignWriteSP.fill(getDeviceName(), "NewAlignStar2", "NVRAM", ALIGN_TAB, IP_RW,
330 OSNAlignPolarRealignSP[0].fill("0", "Instructions", ISS_OFF);
331 OSNAlignPolarRealignSP[1].fill("1", "Refine Polar Align (manually)", ISS_OFF);
333 "Polar Correction, See info box", ALIGN_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
334
335 OSNAlignTP[0].fill("0", "Align Process Status", "Align not started");
336 OSNAlignTP[1].fill("1", "1. Manual Process", "Point towards the NCP");
337 OSNAlignTP[2].fill("2", "2. Plate Solver Process", "Point towards the NCP");
338 OSNAlignTP[3].fill("3", "Manual Action after 1", "Press 'Start Align'");
339 OSNAlignTP[4].fill("4", "Current Status", "Not Updated");
340 OSNAlignTP[5].fill("5", "Max Stars", "Not Updated");
341 OSNAlignTP[6].fill("6", "Current Star", "Not Updated");
342 OSNAlignTP[7].fill("7", "# of Align Stars", "Not Updated");
343 OSNAlignTP.fill(getDeviceName(), "Align Process", "", ALIGN_TAB, IP_RO, 0, IPS_IDLE);
344
345 OSNAlignErrTP[0].fill("0", "EQ Polar Error Alt", "Available once Aligned");
346 OSNAlignErrTP[1].fill("1", "EQ Polar Error Az", "Available once Aligned");
347 // OSNAlignErrTP[2].fill("2", "2. Plate Solver Process", "Point towards the NCP");
348 // OSNAlignErrTP[3].fill("3", "After 1 or 2", "Press 'Start Align'");
349 // OSNAlignErrTP[4].fill("4", "Current Status", "Not Updated");
350 // OSNAlignErrTP[5].fill("5", "Max Stars", "Not Updated");
351 // OSNAlignErrTP[6].fill("6", "Current Star", "Not Updated");
352 // OSNAlignErrTP[7].fill("7", "# of Align Stars", "Not Updated");
353 OSNAlignErrTP.fill(getDeviceName(), "Align OnStep results", "", ALIGN_TAB, IP_RO, 0,
354 IPS_IDLE);
355
356 // =============== INFO_TAB
357
358#ifdef ONSTEP_NOTDONE
359 // =============== OUTPUT_TAB
360 // ===============
361 IUFillSwitch(&OSOutput1S[0], "0", "OFF", ISS_ON);
362 IUFillSwitch(&OSOutput1S[1], "1", "ON", ISS_OFF);
363 IUFillSwitchVector(&OSOutput1SP, OSOutput1S, 2, getDeviceName(), "Output 1", "Output 1", OUTPUT_TAB, IP_RW, ISR_ATMOST1, 60,
364 IPS_ALERT);
365
366 IUFillSwitch(&OSOutput2S[0], "0", "OFF", ISS_ON);
367 IUFillSwitch(&OSOutput2S[1], "1", "ON", ISS_OFF);
368 IUFillSwitchVector(&OSOutput2SP, OSOutput2S, 2, getDeviceName(), "Output 2", "Output 2", OUTPUT_TAB, IP_RW, ISR_ATMOST1, 60,
369 IPS_ALERT);
370#endif
371
372 for(int i = 0; i < PORTS_COUNT; i++)
373 {
374 char port_name[30];
375 snprintf(port_name, sizeof(port_name), "Output %d", i);
376 OutputPorts_NP[i].fill(port_name, port_name, "%g", 0, 255, 1, 0);
377 }
378
379 OutputPorts_NP.fill(getDeviceName(), "Outputs", "Outputs", OUTPUT_TAB, IP_WO, 60,
380 IPS_OK);
381
382
383 // ============== STATUS_TAB
384 OnstepStatTP[0].fill(":GU# return", "", "");
385 OnstepStatTP[1].fill("Tracking", "", "");
386 OnstepStatTP[2].fill("Refractoring", "", "");
387 OnstepStatTP[3].fill("Park", "", "");
388 OnstepStatTP[4].fill("Pec", "", "");
389 OnstepStatTP[5].fill("TimeSync", "", "");
390 OnstepStatTP[6].fill("Mount Type", "", "");
391 OnstepStatTP[7].fill("Error", "", "");
392 OnstepStatTP[8].fill("Multi-Axis Tracking", "", "");
393 OnstepStatTP[9].fill("TMC Axis1", "", "");
394 OnstepStatTP[10].fill("TMC Axis2", "", "");
395 OnstepStatTP.fill(getDeviceName(), "OnStep Status", "", STATUS_TAB, IP_RO, 0, IPS_OK);
396
397 // ============== WEATHER TAB
398 // Uses OnStep's defaults for this
399 OSSetTemperatureNP[0].fill("Set Temperature (C)", "C", "%4.2f", -100, 100, 1, 10);
400 OSSetTemperatureNP.fill(getDeviceName(), "Set Temperature (C)", "", ENVIRONMENT_TAB,
401 IP_RW, 0, IPS_IDLE);
402 OSSetHumidityNP[0].fill("Set Relative Humidity (%)", "%", "%5.2f", 0, 100, 1, 70);
403 OSSetHumidityNP.fill(getDeviceName(), "Set Relative Humidity (%)", "", ENVIRONMENT_TAB,
404 IP_RW, 0, IPS_IDLE);
405 OSSetPressureNP[0].fill("Set Pressure (hPa)", "hPa", "%4f", 500, 1500, 1, 1010);
406 OSSetPressureNP.fill(getDeviceName(), "Set Pressure (hPa)", "", ENVIRONMENT_TAB, IP_RW,
407 0, IPS_IDLE);
408
409 //Will eventually pull from the elevation in site settings
410 //TODO: Pull from elevation in site settings
411 OSSetAltitudeNP[0].fill("Set Altitude (m)", "m", "%4f", 0, 20000, 1, 110);
412 OSSetAltitudeNP.fill(getDeviceName(), "Set Altitude (m)", "", ENVIRONMENT_TAB, IP_RW, 0,
413 IPS_IDLE);
414
415
416 addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -40, 85, 15);
417 addParameter("WEATHER_HUMIDITY", "Humidity %", 0, 100, 15);
418 addParameter("WEATHER_BAROMETER", "Pressure (hPa)", 0, 1500, 15);
419 addParameter("WEATHER_DEWPOINT", "Dew Point (C)", 0, 100, 15); // From OnStep
420 addParameter("WEATHER_CPU_TEMPERATURE", "OnStep CPU Temperature", -274, 200, -274); // From OnStep, -274 = unread
421 setCriticalParameter("WEATHER_TEMPERATURE");
422
424
425
427
428 return true;
429}
430
431void LX200_OnStep::ISGetProperties(const char *dev)
432{
433 if (dev != nullptr && strcmp(dev, getDeviceName()) != 0) return;
435}
436
438{
440 //TODO: Properly setup Weather
442
443 if (isConnected())
444 {
445 Connection::Interface *activeConnection = getActiveConnection();
446 if (!activeConnection->name().compare("CONNECTION_TCP"))
447 {
448 LOG_INFO("Network based connection, detection timeouts set to 2 seconds");
451 }
452 else
453 {
454 LOG_INFO("Non-Network based connection, detection timeouts set to 0.1 seconds");
455 OSTimeoutMicroSeconds = 100000;
457 }
458
459 // Firstinitialize some variables
460 // keep sorted by TABs is easier
461 // Main Control
465 // Connection
466
467 // Options
468
469 // OnStep Status
471
472 // Motion Control
473 defineProperty(SlewRateSP); // was missing
484
485 // Site Management
487 //defineProperty(SetHomeSP);
488
489 // Guide
490
491 // Focuser
492
493 // Focuser 1
494 OSNumFocusers = 0; //Reset before detection
495 //if (!sendOnStepCommand(":FA#")) // do we have a Focuser 1
496 char response[RB_MAX_LEN] = {0};
497 int error_or_fail = getCommandSingleCharResponse(PortFD, response, ":FA#"); //0 = failure, 1 = success, no # on reply
498 if (error_or_fail > 0 && response[0] == '1')
499 {
500 LOG_INFO("Focuser 1 found");
501 OSFocuser1 = true;
503 // Focus T° Compensation
508 // End Focus T° Compensation
509 OSNumFocusers = 1;
510 }
511 else
512 {
513 OSFocuser1 = false;
514 LOG_INFO("Focuser 1 NOT found");
515 LOGF_DEBUG("error_or_fail = %u, response = %c", error_or_fail, response[0]);
516 }
517 // Focuser 2
518 if (!sendOnStepCommand(":fA#")) // Do we have a Focuser 2 (:fA# will only work for OnStep, not OnStepX)
519 {
520 LOG_INFO("Focuser 2 found");
521 OSFocuser2 = true;
522 OSNumFocusers = 2;
523 //defineProperty(OSFocus2SelSP);
527 OSFocusSelectSP.fill(getDeviceName(), "OSFocusSWAP", "Primary Focuser",
528 FOCUS_TAB,
530 defineProperty(OSFocusSelectSP); //Swap focusers (only matters if two focusers)
531 }
532 else //For OnStepX, up to 6 focusers
533 {
534 LOG_INFO("Focuser 2 NOT found");
535 OSFocuser2 = false;
537 {
538 LOG_INFO("Version unknown or OnStepX (Checking for OnStepX Focusers)");
539 for (int i = 0; i < 9; i++)
540 {
541 char cmd[CMD_MAX_LEN] = {0};
542 char read_buffer[RB_MAX_LEN] = {0};
543 snprintf(cmd, sizeof(cmd), ":F%dA#", i + 1);
544 int fail_or_error = getCommandSingleCharResponse(PortFD, read_buffer,
545 cmd); //0 = failure, 1 = success, 0 on all prior to OnStepX no # on reply
546 if (!fail_or_error && read_buffer[0] == '1') // Do we have a Focuser X
547 {
548 LOGF_INFO("Focuser %i Found", i);
549 OSNumFocusers = i + 1;
550 }
551 else
552 {
553 if(fail_or_error < 0)
554 {
555 //Non detection = 0, Read errors < 0, stop
556 LOGF_INFO("Function call failed in a way that says OnStep doesn't have this setup, stopping Focuser probing, return: %i",
557 fail_or_error);
558 break;
559 }
560 }
561 }
562 }
563 if (OSNumFocusers > 1)
564 {
565 OSFocusSelectSP.fill(getDeviceName(), "OSFocusSWAP", "Primary Focuser",
566 FOCUS_TAB,
569 }
570 }
571 if (OSNumFocusers == 0)
572 {
573 LOG_INFO("No Focusers found");
574 }
575 else
576 {
577 LOG_INFO("At least one focuser found, showing interface");
579 }
580
581 LOG_DEBUG("Focusers checked Variables:");
582 LOGF_DEBUG("OSFocuser1: %d, OSFocuser2: %d, OSNumFocusers: %i", OSFocuser1, OSFocuser2, OSNumFocusers);
583
584 //Rotation Information
585 char rotator_response[RB_MAX_LEN] = {0};
586 error_or_fail = getCommandSingleCharResponse(PortFD, rotator_response, ":GX98#");
587 if (error_or_fail > 0)
588 {
589 if (rotator_response[0] == 'D' || rotator_response[0] == 'R')
590 {
591 LOG_INFO("Rotator found.");
592 OSRotator1 = true;
596 }
597 if (rotator_response[0] == 'D')
598 {
600 }
601 if (rotator_response[0] == '0')
602 {
603 OSRotator1 = false;
604 }
605 }
606 else
607 {
608 LOGF_WARN("Error: %i", error_or_fail);
609 LOG_WARN("Error on response to rotator check (:GX98#) CHECK CONNECTION");
610 }
611 //=================
612
613 if (OSRotator1 == false)
614 {
615 LOG_INFO("No Rotator found.");
616 OSRotator1 = false;
617 }
618
619 // Firmware Data
621
622 //PEC
623 //TODO: Define later when it might be supported
628 //defineProperty(OSPECCurrentIndexNP);
629 //defineProperty(OSPECRWValuesNP);
630
631 //New Align
638
639#ifdef ONSTEP_NOTDONE
640 //Outputs
641 defineProperty(OSOutput1SP);
642 defineProperty(OSOutput2SP);
643#endif
644 // OSHasOutputs = true; //Set to true on new connection, Init_Outputs will set to false if appropriate
645 Init_Outputs();
646
647 //Weather
652
653
654
655 if (InitPark())
656 {
657 // If loading parking data is successful, we just set the default parking values.
658 SetAxis1ParkDefault(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
660 }
661 else
662 {
663 // Otherwise, we set all parking data to default in case no parking data is found.
664 SetAxis1Park(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
666
667 SetAxis1ParkDefault(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
669 }
670
671 double longitude = -1000, latitude = -1000;
672 // Get value from config file if it exists.
673 IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude);
674 IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude);
675 // if (longitude != -1000 && latitude != -1000)
676 // {
677 // updateLocation(latitude, longitude, 0);
678 // }
679 //NOTE: if updateProperties is called it clobbers this, so added here
680
681
682 }
683 else
684 {
685 // keep sorted by TABs is easier
686 // Main Control
689 // Connection
690
691 // Options
692
693 // Motion Control
694 deleteProperty(SlewRateSP); // was missing
705
706 // Site Management
708 //deleteProperty(SetHomeSP.name);
709 // Guide
710
711 // Focuser
712 // Focuser 1
717 // Focus T° Compensation
719 // End Focus T° Compensation
720
721 // Focuser 2
722 //deleteProperty(OSFocus2SelSP.name);
727
728 // Rotator
730
731 // Firmware Data
733
734
735 //PEC
740 //deleteProperty(OSPECCurrentIndexNP.name);
741 //deleteProperty(OSPECRWValuesNP.name);
742
743 //New Align
750
751#ifdef ONSTEP_NOTDONE
752 //Outputs
753 deleteProperty(OSOutput1SP);
754 deleteProperty(OSOutput2SP);
755#endif
756
758
759 // OnStep Status
761 //Weather
768 OSHasOutputs = true; //Set once per connection, either at startup or on disconnection for next connection;
769 }
770 LOG_INFO("Initialization Complete");
771 return true;
772}
773
774bool LX200_OnStep::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
775{
776 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
777 {
778 if (strstr(name, "FOCUS_"))
779 return FI::processNumber(dev, name, values, names, n);
780 if (strstr(name, "ROTATOR_"))
781 return RI::processNumber(dev, name, values, names, n);
782
783 if (EqNP.isNameMatch(name))
784 //Replace this from inditelescope so it doesn't change state
785 //Most of this needs to be handled by our updates, or it breaks things
786 {
787 // this is for us, and it is a goto
788 bool rc = false;
789 double ra = -1;
790 double dec = -100;
791
792 for (int x = 0; x < n; x++)
793 {
794 if (EqNP[AXIS_RA].isNameMatch(names[x]))
795 ra = values[x];
796 else if (EqNP[AXIS_DE].isNameMatch(names[x]))
797 dec = values[x];
798 }
799
800 if ((ra >= 0) && (ra <= 24) && (dec >= -90) && (dec <= 90))
801 {
802 // Check if it is already parked.
803 if (CanPark())
804 {
805 if (isParked())
806 {
807 LOG_DEBUG("Please unpark the mount before issuing any motion/sync commands.");
808 // EqNP.setState(lastEqState = IPS_IDLE);
809 // EqNP.apply();
810 return false;
811 }
812 }
813
814 // Check if it can sync
815 if (Telescope::CanSync())
816 {
817 auto oneSwitch = CoordSP.findWidgetByName("SYNC");
818 if (oneSwitch && oneSwitch->getState() == ISS_ON)
819 {
820 return Sync(ra, dec);
821 }
822 }
823
824 // Issue GOTO
825 rc = Goto(ra, dec);
826 if (rc)
827 {
828 // Now fill in target co-ords, so domes can start turning
829 TargetNP[AXIS_RA].setValue(ra);
830 TargetNP[AXIS_DE].setValue(dec);
831 TargetNP.apply();
832 }
833 }
834 return rc;
835 }
836
837 if (ObjectNoNP.isNameMatch(name))
838 {
839 char object_name[256];
840
841 if (selectCatalogObject(PortFD, currentCatalog, (int)values[0]) < 0)
842 {
845 LOG_ERROR("Failed to select catalog object.");
846 return false;
847 }
848
851
854
855 if (getObjectInfo(PortFD, object_name) < 0)
856 IDMessage(getDeviceName(), "Getting object info failed.");
857 else
858 {
859 ObjectInfoTP[0].setText(object_name);
861 }
863 return true;
864 }
865
866 if (MaxSlewRateNP.isNameMatch(name))
867 {
868 int ret;
869 char cmd[CMD_MAX_LEN] = {0};
870 snprintf(cmd, 5, ":R%d#", (int)values[0]);
872
873 //if (setMaxSlewRate(PortFD, (int)values[0]) < 0) //(int) MaxSlewRateN[0].value
874 if (ret == -1)
875 {
876 LOGF_DEBUG("Pas OK Return value =%d", ret);
877 LOGF_DEBUG("Setting Max Slew Rate to %f\n", values[0]);
880 LOG_ERROR("Setting Max Slew Rate Failed");
881 return false;
882 }
883 LOGF_DEBUG("OK Return value =%d", ret);
885 MaxSlewRateNP[0].setValue(values[0]);
887 LOGF_INFO("Slewrate set to %04.1f", values[0]);
889 SlewRateSP[int(values[0])].setState(ISS_ON);
892 return true;
893 }
894
895 if (BacklashNP.isNameMatch(name))
896 {
897 //char cmd[CMD_MAX_LEN] = {0};
898 int i, nset;
899 double bklshdec = 0, bklshra = 0;
900
901 for (nset = i = 0; i < n; i++)
902 {
903 if (BacklashNP[0].isNameMatch(names[i]))
904 {
905 bklshdec = values[i];
906 LOGF_DEBUG("===CMD==> Backlash DEC= %f", bklshdec);
907 nset += bklshdec >= 0 && bklshdec <= 999; //range 0 to 999
908 }
909 else if (BacklashNP[1].isNameMatch(names[i]))
910 {
911 bklshra = values[i];
912 LOGF_DEBUG("===CMD==> Backlash RA= %f", bklshra);
913 nset += bklshra >= 0 && bklshra <= 999; //range 0 to 999
914 }
915 }
916 if (nset == 2)
917 {
918 char cmd[CMD_MAX_LEN] = {0};
919 snprintf(cmd, 9, ":$BD%d#", (int)bklshdec);
921 {
924 LOG_ERROR("Error Backlash DEC limit.");
925 }
926 const struct timespec timeout = {0, 100000000L};
927 nanosleep(&timeout, nullptr); // time for OnStep to respond to previous cmd
928 snprintf(cmd, 9, ":$BR%d#", (int)bklshra);
930 {
933 LOG_ERROR("Error Backlash RA limit.");
934 }
935
936 BacklashNP[0].setValue(bklshdec);
937 BacklashNP[1].setValue(bklshra);
940 return true;
941 }
942 else
943 {
946 LOG_ERROR("Backlash invalid.");
947 return false;
948 }
949 }
950
952 {
953 // new elevation limits
954 double minAlt = 0, maxAlt = 0;
955 int i, nset;
956
957 for (nset = i = 0; i < n; i++)
958 {
959 if (ElevationLimitNP[0].isNameMatch(names[i]))
960 {
961 minAlt = values[i];
962 nset += minAlt >= -30.0 && minAlt <= 30.0; //range -30 to 30
963 }
964 else if (ElevationLimitNP[1].isNameMatch(names[i]))
965 {
966 maxAlt = values[i];
967 nset += maxAlt >= 60.0 && maxAlt <= 90.0; //range 60 to 90
968 }
969 }
970 if (nset == 2)
971 {
972 if (setMinElevationLimit(PortFD, (int)maxAlt) < 0)
973 {
975 LOG_ERROR("Error setting min elevation limit.");
976 }
977
978 if (setMaxElevationLimit(PortFD, (int)minAlt) < 0)
979 {
982 LOG_ERROR("Error setting max elevation limit.");
983 return false;
984 }
985 ElevationLimitNP[0].setValue(minAlt);
986 ElevationLimitNP[1].setValue(maxAlt);
989 return true;
990 }
991 else
992 {
995 LOG_ERROR("elevation limit missing or invalid.");
996 return false;
997 }
998 }
999 }
1000
1002 {
1003 //char cmd[CMD_MAX_LEN] ={0};
1004 int i, nset;
1005 double minPMEast = 0, minPMWest = 0;
1006
1007 for (nset = i = 0; i < n; i++)
1008 {
1009 if (minutesPastMeridianNP[0].isNameMatch(names[i]))
1010 {
1011 minPMEast = values[i];
1012 LOGF_DEBUG("===CMD==> minutesPastMeridianN[0]/East = %f", minPMEast);
1013 nset += minPMEast >= -180 && minPMEast <= 180; //range -180 to 180
1014 }
1015 else if (minutesPastMeridianNP[1].isNameMatch(names[i]))
1016 {
1017 minPMWest = values[i];
1018 LOGF_DEBUG("===CMD==> minutesPastMeridianN[1]/West= %f", minPMWest);
1019 nset += minPMWest >= -180 && minPMWest <= 180; //range -180 to 180
1020 }
1021 }
1022 if (nset == 2)
1023 {
1024 char cmd[CMD_MAX_LEN] = {0};
1025 snprintf(cmd, 20, ":SXE9,%d#", (int) minPMEast);
1027 {
1030 LOG_ERROR("Error minutesPastMeridian East.");
1031 }
1032 const struct timespec timeout = {0, 100000000L};
1033 nanosleep(&timeout, nullptr); // time for OnStep to respond to previous cmd
1034 snprintf(cmd, 20, ":SXEA,%d#", (int) minPMWest);
1036 {
1039 LOG_ERROR("Error minutesPastMeridian West.");
1040 }
1041
1042 minutesPastMeridianNP[0].setValue(minPMEast);
1043 minutesPastMeridianNP[1].setValue(minPMWest);
1046 return true;
1047 }
1048 else
1049 {
1052 LOG_ERROR("minutesPastMeridian invalid.");
1053 return false;
1054 }
1055 }
1056 // Focuser
1057 // Focuser 1 Now handled by Focusr Interface
1058
1059 // Focuser 2 Target
1060 if (OSFocus2TargNP.isNameMatch(name))
1061 {
1062 char cmd[CMD_MAX_LEN] = {0};
1063
1064 if ((values[0] >= -25000) && (values[0] <= 25000))
1065 {
1066 snprintf(cmd, 15, ":fR%d#", (int)values[0]);
1069 OSFocus2TargNP.apply("Focuser 2 position (relative) moved by %d", (int)values[0]);
1071 }
1072 else
1073 {
1075 OSFocus2TargNP.apply("Setting Max Slew Rate Failed");
1076 }
1077 return true;
1078 }
1079
1080 if (OutputPorts_NP.isNameMatch(name))
1081 {
1082 //go through all output values and see if any value needs to be changed
1083 for(int i = 0; i < n; i++)
1084 {
1085 int value = (int)values[i];
1086 if(OutputPorts_NP[i].getValue() != value)
1087 {
1088 int ret;
1089 char cmd[CMD_MAX_LEN] = {0};
1090 int port = STARTING_PORT + i;
1091
1092 //This is for newer version of OnStep:
1093 snprintf(cmd, sizeof(cmd), ":SXX%d,V%d#", port, value);
1094 //This is for older version of OnStep:
1095 //snprintf(cmd, sizeof(cmd), ":SXG%d,%d#", port, value);
1097
1098 if (ret == -1)
1099 {
1100 LOGF_ERROR("Set port %d to value =%d failed", port, value);
1102 return false;
1103 }
1104
1106 OutputPorts_NP[i].setValue(value);
1107 OutputPorts_NP.apply("Set port %d to value =%d", port, value);
1108 }
1109 }
1110 return true;
1111 }
1112 //Weather not handled by Weather Interface
1113
1115 {
1116 // char cmd[CMD_MAX_LEN] = {0};
1117
1118 if ((values[0] >= -100) && (values[0] <= 100))
1119 {
1120 char cmd[CMD_MAX_LEN] = {0};
1121 snprintf(cmd, 15, ":SX9A,%d#", (int)values[0]);
1124 OSSetTemperatureNP[0].setValue(values[0]);
1125 OSSetTemperatureNP.apply("Temperature set to %d", (int)values[0]);
1126 }
1127 else
1128 {
1130 OSSetTemperatureNP.apply("Setting Temperature Failed");
1131 }
1132 return true;
1133 }
1134
1135 if (OSSetHumidityNP.isNameMatch(name))
1136 {
1137 char cmd[CMD_MAX_LEN] = {0};
1138
1139 if ((values[0] >= 0) && (values[0] <= 100))
1140 {
1141 snprintf(cmd, 15, ":SX9C,%d#", (int)values[0]);
1144 OSSetHumidityNP[0].setValue(values[0]);
1145 OSSetHumidityNP.apply("Humidity set to %d", (int)values[0]);
1146 }
1147 else
1148 {
1150 OSSetHumidityNP.apply("Setting Humidity Failed");
1151 }
1152 return true;
1153 }
1154
1155 if (OSSetPressureNP.isNameMatch(name))
1156 {
1157 char cmd[CMD_MAX_LEN] = {0};
1158
1159 if ((values[0] >= 500) && (values[0] <= 1100)) // typo
1160 {
1161 snprintf(cmd, 15, ":SX9B,%d#", (int)values[0]);
1164 OSSetPressureNP[0].setValue(values[0]);
1165 OSSetPressureNP.apply("Pressure set to %d", (int)values[0]);
1166 }
1167 else
1168 {
1170 OSSetPressureNP.apply("Setting Pressure Failed");
1171 }
1172 return true;
1173 }
1174
1175 // Focus T° Compensation
1176 if (TFCCoefficientNP.isNameMatch(name))
1177 {
1178 // :FC[sn.n]# Set focuser temperature compensation coefficient in µ/°C
1179 char cmd[CMD_MAX_LEN] = {0};
1180
1181 if (abs(values[0]) < 1000) //Range is -999.999 .. + 999.999
1182 {
1183 snprintf(cmd, 15, ":FC%+3.5f#", values[0]);
1186 TFCCoefficientNP.apply("TFC Coefficient set to %+3.5f", values[0]);
1187 }
1188 else
1189 {
1191 TFCCoefficientNP.apply("Setting TFC Coefficient Failed");
1192 }
1193 return true;
1194 }
1195
1196 if (TFCDeadbandNP.isNameMatch(name))
1197 {
1198 // :FD[n]# Set focuser temperature compensation deadband amount (in steps or microns)
1199 char cmd[CMD_MAX_LEN] = {0};
1200
1201 if ((values[0] >= 1) && (values[0] <= 32768)) //Range is 1 .. 32767
1202 {
1203 snprintf(cmd, 15, ":FD%d#", (int)values[0]);
1206 TFCDeadbandNP.apply("TFC Deadbandset to %d", (int)values[0]);
1207 }
1208 else
1209 {
1211 TFCDeadbandNP.apply("Setting TFC Deadband Failed");
1212 }
1213 return true;
1214 }
1215
1216
1217
1218 // end Focus T° Compensation
1219
1220 if (strstr(name, "WEATHER_"))
1221 {
1222 return WI::processNumber(dev, name, values, names, n);
1223 }
1224
1225 return LX200Generic::ISNewNumber(dev, name, values, names, n);
1226}
1227
1228bool LX200_OnStep::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
1229{
1230 int index = 0;
1231
1232 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1233 {
1234 //Intercept Before inditelescope base can set TrackState
1235 //Next one modification of inditelescope.cpp function
1236 if (TrackStateSP.isNameMatch(name))
1237 {
1238 // int previousState = IUFindOnSwitchIndex(&TrackStateSP);
1239 TrackStateSP.update(states, names, n);
1240 int targetState = TrackStateSP.findOnSwitchIndex();
1241 // LOG_DEBUG("OnStep driver TrackStateSP override called");
1242 // if (previousState == targetState)
1243 // {
1244 // TrackStateSP.apply();
1245 // return true;
1246 // }
1247
1248 if (TrackState == SCOPE_PARKED)
1249 {
1250 LOG_WARN("Telescope is Parked, Unpark before tracking.");
1251 return false;
1252 }
1253
1254 bool rc = SetTrackEnabled((targetState == TRACK_ON) ? true : false);
1255
1256 if (rc)
1257 {
1258 return true;
1259 //TrackStateSP moved to Update
1260 }
1261 else
1262 {
1263 //This is the case for an error on sending the command, so change TrackStateSP
1266 return false;
1267 }
1268
1269 LOG_DEBUG("TrackStateSP intercept, OnStep driver, should never get here");
1270 return false;
1271 }
1272
1273
1274
1275 // Reticlue +/- Buttons
1276 if (ReticSP.isNameMatch(name))
1277 {
1278 int ret = 0;
1279 ReticSP.update(states, names, n);
1281
1282 if (ReticSP[0].getState() == ISS_ON)
1283 {
1284 ret = increaseReticleBrightness(PortFD); //in lx200driver
1286 ReticSP.apply("Bright");
1287 }
1288 else
1289 {
1290 ret = decreaseReticleBrightness(PortFD); //in lx200driver
1292 ReticSP.apply("Dark");
1293 }
1294
1295 INDI_UNUSED(ret);
1296 ReticSP.reset();
1297 ReticSP.apply();
1298 return true;
1299 }
1300 //Move to more standard controls
1301 if (SlewRateSP.isNameMatch(name))
1302 {
1303 SlewRateSP.update(states, names, n);
1304 int ret;
1305 char cmd[CMD_MAX_LEN] = {0};
1306 int index = SlewRateSP.findOnSwitchIndex() ;//-1; //-1 because index is 1-10, OS Values are 0-9
1307 snprintf(cmd, sizeof(cmd), ":R%d#", index);
1309
1310 //if (setMaxSlewRate(PortFD, (int)values[0]) < 0) //(int) MaxSlewRateN[0].value
1311 if (ret == -1)
1312 {
1313 LOGF_DEBUG("Pas OK Return value =%d", ret);
1314 LOGF_DEBUG("Setting Max Slew Rate to %u\n", index);
1316 LOG_ERROR("Setting Max Slew Rate Failed");
1317 SlewRateSP.apply();
1318 return false;
1319 }
1320 LOGF_INFO("Setting Max Slew Rate to %u (%s) \n", index, SlewRateSP[index].getLabel());
1321 LOGF_DEBUG("OK Return value =%d", ret);
1323 MaxSlewRateNP[0].setValue(index);
1324 MaxSlewRateNP.apply("Slewrate set to %d", index);
1325 SlewRateSP.reset();
1326 SlewRateSP[index].setState(ISS_ON);
1328 SlewRateSP.apply();
1329 return true;
1330 }
1331
1332 // Tracking Compensation selection
1333 if (TrackCompSP.isNameMatch(name))
1334 {
1335 TrackCompSP.update(states, names, n);
1337
1338 if (TrackCompSP[0].getState() == ISS_ON)
1339 {
1340 if (!sendOnStepCommand(":To#"))
1341 {
1343 TrackCompSP.apply("Full Compensated Tracking On");
1344 return true;
1345 }
1346 }
1347 if (TrackCompSP[1].getState() == ISS_ON)
1348 {
1349 if (!sendOnStepCommand(":Tr#"))
1350 {
1352 TrackCompSP.apply("Refraction Tracking On");
1353 return true;
1354 }
1355 }
1356 if (TrackCompSP[2].getState() == ISS_ON)
1357 {
1358 if (!sendOnStepCommand(":Tn#"))
1359 {
1361 TrackCompSP.apply("Refraction Tracking Disabled");
1362 return true;
1363 }
1364 }
1368 return true;
1369 }
1370
1371 if (TrackAxisSP.isNameMatch(name))
1372 {
1373 TrackAxisSP.update(states, names, n);
1375
1376 if (TrackAxisSP[0].getState() == ISS_ON)
1377 {
1378 if (!sendOnStepCommand(":T1#"))
1379 {
1381 TrackAxisSP.apply("Single Tracking On");
1382 return true;
1383 }
1384 }
1385 if (TrackAxisSP[1].getState() == ISS_ON)
1386 {
1387 if (!sendOnStepCommand(":T2#"))
1388 {
1390 TrackAxisSP.apply("Dual Axis Tracking On");
1391 return true;
1392 }
1393 }
1397 return true;
1398 }
1399
1400 if (AutoFlipSP.isNameMatch(name))
1401 {
1402 AutoFlipSP.update(states, names, n);
1404
1405 if (AutoFlipSP[0].getState() == ISS_ON)
1406 {
1407 if (sendOnStepCommand(":SX95,0#"))
1408 {
1410 AutoFlipSP.apply("Auto Meridian Flip OFF");
1411 return true;
1412 }
1413 }
1414 if (AutoFlipSP[1].getState() == ISS_ON)
1415 {
1416 if (sendOnStepCommand(":SX95,1#"))
1417 {
1419 AutoFlipSP.apply("Auto Meridian Flip ON");
1420 return true;
1421 }
1422 }
1423 AutoFlipSP.reset();
1424 //AutoFlipSP.setState(IPS_IDLE);
1425 AutoFlipSP.apply();
1426 return true;
1427 }
1428
1429 if (HomePauseSP.isNameMatch(name))
1430 {
1431 HomePauseSP.update(states, names, n);
1433
1434 if (HomePauseSP[0].getState() == ISS_ON)
1435 {
1436 if (sendOnStepCommand(":SX98,0#"))
1437 {
1439 HomePauseSP.apply("Home Pause OFF");
1440 return true;
1441 }
1442 }
1443 if (HomePauseSP[1].getState() == ISS_ON)
1444 {
1445 if (sendOnStepCommand(":SX98,1#"))
1446 {
1448 HomePauseSP.apply("Home Pause ON");
1449 return true;
1450 }
1451 }
1452 if (HomePauseSP[2].getState() == ISS_ON)
1453 {
1454 if (sendOnStepCommand(":SX99,1#"))
1455 {
1458 HomePauseSP.apply("Home Pause: Continue");
1459 return true;
1460 }
1461 }
1465 return true;
1466 }
1467
1468 if (FrequencyAdjustSP.isNameMatch(name)) //
1469
1470 //
1471 {
1472 FrequencyAdjustSP.update(states, names, n);
1474
1475 if (FrequencyAdjustSP[0].getState() == ISS_ON)
1476 {
1477 if (!sendOnStepCommandBlind(":T-#"))
1478 {
1479 FrequencyAdjustSP.apply("Frequency decreased");
1480 return true;
1481 }
1482 }
1483 if (FrequencyAdjustSP[1].getState() == ISS_ON)
1484 {
1485 if (!sendOnStepCommandBlind(":T+#"))
1486 {
1487 FrequencyAdjustSP.apply("Frequency increased");
1488 return true;
1489 }
1490 }
1491 if (FrequencyAdjustSP[2].getState() == ISS_ON)
1492 {
1493 if (!sendOnStepCommandBlind(":TR#"))
1494 {
1495 FrequencyAdjustSP.apply("Frequency Reset (TO saved EEPROM)");
1496 return true;
1497 }
1498 }
1502 return true;
1503 }
1504
1505 //Pier Side
1507 {
1508 PreferredPierSideSP.update(states, names, n);
1510
1511 if (PreferredPierSideSP[0].getState() == ISS_ON) //West
1512 {
1513 if (sendOnStepCommand(":SX96,W#"))
1514 {
1516 PreferredPierSideSP.apply("Preferred Pier Side: West");
1517 return true;
1518 }
1519 }
1520 if (PreferredPierSideSP[1].getState() == ISS_ON) //East
1521 {
1522 if (sendOnStepCommand(":SX96,E#"))
1523 {
1525 PreferredPierSideSP.apply("Preferred Pier Side: East");
1526 return true;
1527 }
1528 }
1529 if (PreferredPierSideSP[2].getState() == ISS_ON) //Best
1530 {
1531 if (sendOnStepCommand(":SX96,B#"))
1532 {
1534 PreferredPierSideSP.apply("Preferred Pier Side: Best");
1535 return true;
1536 }
1537 }
1540 return true;
1541 }
1542
1543
1544 // Focuser
1545 // Focuser 1 Rates
1547 {
1548 char cmd[CMD_MAX_LEN] = {0};
1549 OSFocus1InitializeSP.update(states, names, n);
1551 if (index == 0)
1552 {
1553 snprintf(cmd, 5, ":FZ#");
1558 }
1559 if (index == 1)
1560 {
1561 snprintf(cmd, 5, ":FH#");
1566 }
1567 return true;
1568 }
1569
1570
1571 //Focuser Swap/Select
1572 if (OSFocusSelectSP.isNameMatch(name))
1573 {
1574 char cmd[CMD_MAX_LEN] = {0};
1575 OSFocusSelectSP.update(states, names, n);
1577 LOGF_INFO("Primary focuser set: Focuser 1 in INDI/Controllable Focuser = OnStep Focuser %d", index + 1);
1578 if (index == 0 && OSNumFocusers <= 2)
1579 {
1580 LOG_INFO("If using OnStep: Focuser 2 in INDI = OnStep Focuser 2");
1581 }
1582 if (index == 1 && OSNumFocusers <= 2)
1583 {
1584 LOG_INFO("If using OnStep: Focuser 2 in INDI = OnStep Focuser 1");
1585 }
1586 if (OSNumFocusers > 2)
1587 {
1588 LOGF_INFO("If using OnStepX, There is no swap, and current max number: %d", OSNumFocusers);
1589 }
1590 snprintf(cmd, 7, ":FA%d#", index + 1 );
1593 if (!sendOnStepCommand(cmd))
1594 {
1596 }
1597 else
1598 {
1600 }
1602 return true;
1603 }
1604
1605
1606 // Focuser 2 Rates
1607 if (OSFocus2RateSP.isNameMatch(name))
1608 {
1609 char cmd[CMD_MAX_LEN] = {0};
1610 OSFocus2RateSP.update(states, names, n);
1612 snprintf(cmd, 5, ":F%d#", index + 1);
1617 return true;
1618 }
1619 // Focuser 2 Motion
1620 if (OSFocus2MotionSP.isNameMatch(name))
1621 {
1622 char cmd[CMD_MAX_LEN] = {0};
1623 OSFocus2MotionSP.update(states, names, n);
1625 if (index == 0)
1626 {
1627 strncpy(cmd, ":f+#", sizeof(cmd));
1628 }
1629 if (index == 1)
1630 {
1631 strncpy(cmd, ":f-#", sizeof(cmd));
1632 }
1633 if (index == 2)
1634 {
1635 strncpy(cmd, ":fQ#", sizeof(cmd));
1636 }
1638 const struct timespec timeout = {0, 100000000L};
1639 nanosleep(&timeout, nullptr); // Pulse 0,1 s
1640 if(index != 2)
1641 {
1642 sendOnStepCommandBlind(":fQ#");
1643 }
1647 return true;
1648 }
1649
1650 //Rotator De-rotation
1652 {
1653 char cmd[CMD_MAX_LEN] = {0};
1654 OSRotatorDerotateSP.update(states, names, n);
1656 if (index == 0) //Derotate_OFF
1657 {
1658 strncpy(cmd, ":r-#", sizeof(cmd));
1659 }
1660 if (index == 1) //Derotate_ON
1661 {
1662 strncpy(cmd, ":r+#", sizeof(cmd));
1663 }
1668 return true;
1669 }
1670
1671 // PEC
1672 if (OSPECRecordSP.isNameMatch(name))
1673 {
1674 OSPECRecordSP.update(states, names, n);
1676
1677 if (OSPECRecordSP[0].getState() == ISS_ON)
1678 {
1679 OSPECEnabled = true;
1680 ClearPECBuffer(0);
1682 }
1683 if (OSPECRecordSP[1].getState() == ISS_ON)
1684 {
1685 OSPECEnabled = true;
1686 StartPECRecord(0);
1688 }
1689 if (OSPECRecordSP[2].getState() == ISS_ON)
1690 {
1691 OSPECEnabled = true;
1692 SavePECBuffer(0);
1694 }
1696 return true;
1697 }
1698 if (OSPECReadSP.isNameMatch(name))
1699 {
1700 OSPECReadSP.update(states, names, n);
1701 if (OSPECReadSP[0].getState() == ISS_ON)
1702 {
1703 OSPECEnabled = true;
1704 ReadPECBuffer(0);
1706 }
1707 if (OSPECReadSP[1].getState() == ISS_ON)
1708 {
1709 OSPECEnabled = true;
1710 WritePECBuffer(0);
1712 }
1714 return true;
1715 }
1716 if (PECStateSP.isNameMatch(name))
1717 {
1718 PECStateSP.update(states, names, n);
1719 index = PECStateSP.findOnSwitchIndex();
1720 if (index == 0)
1721 {
1722 OSPECEnabled = true;
1723 StopPECPlayback(0); //Status will set OSPECEnabled to false if that's set by the controller
1726 PECStateSP.apply();
1727 }
1728 else if (index == 1)
1729 {
1730 OSPECEnabled = true;
1734 PECStateSP.apply();
1735 }
1736 return true;
1737 }
1738
1739 // Align Buttons
1740 if (OSNAlignStarsSP.isNameMatch(name))
1741 {
1743 OSNAlignStarsSP.update(states, names, n);
1744 return true;
1745 }
1746
1747 // Alignment
1748 if (OSNAlignSP.isNameMatch(name))
1749 {
1750 OSNAlignSP.update(states, names, n);
1751 index = OSNAlignSP.findOnSwitchIndex();
1752 //NewGeometricAlignment
1753 //End NewGeometricAlignment
1755 if (index == 0)
1756 {
1757 /* Index is 0-8 and represents index+1*/
1758 int index_stars = OSNAlignStarsSP.findOnSwitchIndex();
1759 if ((index_stars <= 8) && (index_stars >= 0))
1760 {
1761 int stars = index_stars + 1;
1763 LOGF_INFO("Align index: %d, stars: %d", index_stars, stars);
1764 AlignStartGeometric(stars);
1765 }
1766 }
1767 if (index == 1)
1768 {
1771 }
1772 //Write to EEPROM moved to new line/variable
1773 OSNAlignSP.apply();
1775 return true;
1776 }
1777
1778 if (OSNAlignWriteSP.isNameMatch(name))
1779 {
1780 OSNAlignWriteSP.update(states, names, n);
1783 if (index == 0)
1784 {
1787 }
1790 return true;
1791 }
1792
1794 {
1795 char cmd[CMD_MAX_LEN] = {0};
1796 char response[RB_MAX_LEN];
1797 OSNAlignPolarRealignSP.update(states, names, n);
1799 if (OSNAlignPolarRealignSP[0].getState() == ISS_ON) //INFO
1800 {
1802 LOG_INFO("Step 1: Goto a bright star between 50 and 80 degrees N/S from the pole. Preferably on the Meridian.");
1803 LOG_INFO("Step 2: Make sure it is centered.");
1804 LOG_INFO("Step 3: Press Refine Polar Alignment.");
1805 LOG_INFO("Step 4: Using the mount's Alt and Az screws manually recenter the star. (Video mode if your camera supports it will be helpful.)");
1806 LOG_INFO("Optional: Start a new alignment.");
1809 return true;
1810 }
1811 if (OSNAlignPolarRealignSP[1].getState() == ISS_ON)
1812 {
1814 // int returncode=sendOnStepCommand("
1815 snprintf(cmd, 5, ":MP#");
1816 // Returns:
1817 // 0=goto is possible
1818 // 1=below the horizon limit
1819 // 2=above overhead limit
1820 // 3=controller in standby
1821 // 4=mount is parked
1822 // 5=goto in progress
1823 // 6=outside limits
1824 // 7=hardware fault
1825 // 8=already in motion
1826 // 9=unspecified error
1827
1828 int res = getCommandSingleCharResponse(PortFD, response, cmd); //0 = 0 Success 1..9 failure, no # on reply
1829 if(res > 0 && response[0] == '0')
1830 {
1831 LOG_INFO("Command for Refine Polar Alignment Successful");
1835 return true;
1836 }
1837 else
1838 {
1839 LOGF_ERROR("Command for Refine Polar Alignment Failed, error=%s", response[0]);
1843 return false;
1844 }
1845 }
1846 return true;
1847 }
1848
1849 // Focus T° Compensation
1851 {
1852 // :Fc[n]# Enable/disable focuser temperature compensation where [n] = 0 or 1
1853 // Return: 0 on failure
1854 // 1 on success
1855 char cmd[CMD_MAX_LEN] = {0};
1856 int ret = 0;
1857 TFCCompensationSP.update(states, names, n);
1859
1860 if (TFCCompensationSP[0].getState() == ISS_ON)
1861 {
1862 snprintf(cmd, sizeof(cmd), ":Fc0#");
1864 //TFCCompensationSP[0].setState(ISS_OFF);
1865 TFCCompensationSP.apply("Idle");
1866 }
1867 else
1868 {
1869 snprintf(cmd, sizeof(cmd), ":Fc1#");
1871 //TFCCompensationSP[1].setState(ISS_OFF);
1872 TFCCompensationSP.apply("Idle");
1873 }
1874
1875 INDI_UNUSED(ret);
1878 return true;
1879 }
1880 //End Focus T° Compensation
1881
1882#ifdef ONSTEP_NOTDONE
1883 if (OSOutput1SP.isNameMatch(name)) //
1884 {
1885 OSOutput1SP.update(states, names, n);
1886 if (OSOutput1SP[0].getState() == ISS_ON)
1887 {
1888 OSDisableOutput(1);
1889 //OSOutput1SP[0].setState(ISS_OFF);
1890 }
1891 else if (OSOutput1SP[1].getState() == ISS_ON)
1892 {
1893 OSEnableOutput(1);
1894 //OSOutput1SP[1].setState(ISS_OFF);
1895 }
1896 OSOutput1SP.apply();
1897 return true;
1898 }
1899 if (OSOutput2SP.isNameMatch(name)) //
1900 {
1901 OSOutput2SP.update(states, names, n);
1902 if (OSOutput2SP[0].getState() == ISS_ON)
1903 {
1904 OSDisableOutput(2);
1905 //OSOutput2SP[0].setState(ISS_OFF);
1906 }
1907 else if (OSOutput2SP[1].getState() == ISS_ON)
1908 {
1909 OSEnableOutput(2);
1910 //OSOutput2SP[1].setState(ISS_OFF);
1911 }
1912 OSOutput2SP.apply();
1913 return true;
1914 }
1915#endif
1916
1917 // Focuser
1918 if (strstr(name, "FOCUS"))
1919 {
1920 return FI::processSwitch(dev, name, states, names, n);
1921 }
1922 // Focuser
1923 if (strstr(name, "ROTATOR"))
1924 {
1925 return RI::processSwitch(dev, name, states, names, n);
1926 }
1927 }
1928
1929 return LX200Generic::ISNewSwitch(dev, name, states, names, n);
1930}
1931
1933{
1934 // process parent
1936
1937 if (!isSimulation())
1938 {
1939 char buffer[128] = {0};
1940 getVersionDate(PortFD, buffer);
1941 VersionTP[0].setText(buffer);
1942 getVersionTime(PortFD, buffer);
1943 VersionTP[1].setText(buffer);
1944 getVersionNumber(PortFD, buffer);
1945 VersionTP[2].setText(buffer);
1946 getProductName(PortFD, buffer);
1947 VersionTP[3].setText(buffer);
1948
1949 VersionTP.apply();
1950 if ((VersionTP[2].getText()[0] == '1' || VersionTP[2].getText()[0] == '2') && (VersionTP[2].getText()[1] == '.' )
1951 && (strcmp(VersionTP[3].getText(), "OnStep") || strcmp(VersionTP[3].getText(), "On-Step")))
1952 {
1953 LOG_INFO("Old OnStep (V1/V2 depreciated) detected, setting some defaults");
1954 LOG_INFO("Note: Everything should work, but it may have timeouts in places, as it's not tested against.");
1955 OSHighPrecision = false;
1957 }
1958 else if (VersionTP[2].getText()[0] == '3' && (strcmp(VersionTP[3].getText(), "OnStep")
1959 || strcmp(VersionTP[3].getText(), "On-Step")))
1960 {
1961 LOG_INFO("V3 OnStep detected, setting some defaults");
1962 OSHighPrecision = false;
1964 }
1965 else if (VersionTP[2].getText()[0] == '4' && (strcmp(VersionTP[3].getText(), "OnStep")
1966 || strcmp(VersionTP[3].getText(), "On-Step")))
1967 {
1968 LOG_INFO("V4 OnStep detected, setting some defaults");
1969 OSHighPrecision = true;
1971 }
1972 else if (VersionTP[2].getText()[0] == '5' && (strcmp(VersionTP[3].getText(), "OnStep")
1973 || strcmp(VersionTP[3].getText(), "On-Step")))
1974 {
1975 LOG_INFO("V5 OnStep detected, setting some defaults");
1976 OSHighPrecision = true;
1978 }
1979 else if (VersionTP[2].getText()[0] == '1' && VersionTP[2].getText()[1] == '0' && VersionTP[2].getText()[2] == '.'
1980 && (strcmp(VersionTP[3].getText(), "OnStepX") || strcmp(VersionTP[3].getText(), "On-Step")))
1981 {
1982 LOG_INFO("OnStepX detected, setting some defaults");
1983 OSHighPrecision = true;
1985 }
1986 else
1987 {
1988 LOG_INFO("OnStep/OnStepX version could not be detected");
1989 OSHighPrecision = false;
1991 }
1992
1993 // Query mount type via :GXEM# - more reliable than ACK (0x06) which has a known bug in OnStepX
1994 // :GXEM# returns a string where the first character indicates mount type:
1995 // E = GEM, K = Fork, k = Fork Alt, A = AltAz
1996 char mountTypeResponse[RB_MAX_LEN] = {0};
1997 int gxem_error = getCommandSingleCharErrorOrLongResponse(PortFD, mountTypeResponse, ":GXEM#");
1998 if (gxem_error > 0)
1999 {
2001 switch (mountTypeResponse[0])
2002 {
2003 case 'A':
2006 LOG_INFO("Mount type detected: AltAz");
2007 break;
2008 case 'K':
2011 LOG_INFO("Mount type detected: Fork (Equatorial)");
2012 break;
2013 case 'k':
2016 LOG_INFO("Mount type detected: Fork Alt (AltAz)");
2017 break;
2018 case 'E':
2019 default:
2022 LOG_INFO("Mount type detected: GEM");
2023 break;
2024 }
2027 }
2028 else
2029 {
2030 LOGF_WARN("Could not query mount type via :GXEM# (error %d), defaulting to GEM", gxem_error);
2031 }
2032
2033 if (InitPark())
2034 {
2035 // If loading parking data is successful, we just set the default parking values.
2036 LOG_INFO("=============== Parkdata loaded");
2037 }
2038 else
2039 {
2040 // Otherwise, we set all parking data to default in case no parking data is found.
2041 LOG_INFO("=============== Parkdata Load Failed");
2042 }
2043 }
2044}
2045
2046//======================== Parking =======================
2048{
2049 char response[RB_MAX_LEN];
2050 // 0 = failure, 1 = success
2051 int error_or_fail = getCommandSingleCharResponse(PortFD, response, ":hQ#");
2052 if(error_or_fail != 1 || response[0] != '1')
2053 {
2054 LOGF_WARN("===CMD==> Set Park Pos %s", response);
2055 return false;
2056 }
2059 LOG_WARN("Park Value set to current position");
2060 return true;
2061}
2062
2064{
2065 IDMessage(getDeviceName(), "Setting Park Data to Default.");
2066 SetAxis1Park(20);
2067 SetAxis2Park(80);
2068 LOG_WARN("Park Position set to Default value, 20/80");
2069 return true;
2070}
2071
2073{
2074 char response[RB_MAX_LEN];
2075
2076
2077 if (!isSimulation())
2078 {
2079 int failure_or_error = getCommandSingleCharResponse(PortFD, response, ":hR#"); //0 = failure, 1 = success, no # on reply
2080 if ((response[0] != '1') || (failure_or_error < 0))
2081 {
2082 return false;
2083 }
2084 }
2085 return true;
2086}
2087
2089{
2090 if (!isSimulation())
2091 {
2092 // If scope is moving, let's stop it first.
2093 if (EqNP.getState() == IPS_BUSY)
2094 {
2095 if (!isSimulation() && abortSlew(PortFD) < 0)
2096 {
2097 Telescope::AbortSP.setState(IPS_ALERT);
2098 LOG_ERROR("Abort slew failed.");
2099 Telescope::AbortSP.apply();
2100 return false;
2101 }
2102 Telescope::AbortSP.setState(IPS_OK);
2104 LOG_ERROR("Slew aborted.");
2105 Telescope::AbortSP.apply();
2106 EqNP.apply();
2107
2109 {
2115
2118 }
2119 }
2120 if (!isSimulation() && slewToPark(PortFD) < 0)
2121 {
2123 LOG_ERROR("Parking Failed.");
2124 ParkSP.apply();
2125 return false;
2126 }
2127 }
2129 return true;
2130}
2131
2132// Periodically Polls OnStep Parameter from controller
2134{
2135 char OSbacklashDEC[RB_MAX_LEN] = {0};
2136 char OSbacklashRA[RB_MAX_LEN] = {0};
2137 char GuideValue[RB_MAX_LEN] = {0};
2138 // int i;
2139 bool pier_not_set = true; // Avoid a call to :Gm if :GU it
2140 Errors Lasterror = ERR_NONE;
2141
2142
2143 if (isSimulation()) //if Simulation is selected
2144 {
2145 mountSim();
2146 return true;
2147 }
2148
2149 tcflush(PortFD, TCIOFLUSH);
2150 flushIO(PortFD);
2151
2152
2153#ifdef OnStep_Alpha
2154 OSSupports_bitfield_Gu = try_bitfield_Gu();
2155
2157 {
2158 //Fall back to :GU parsing
2159#endif
2160
2162 ":GU#"); // :GU# returns a string containing controller status
2163 if (error_or_fail > 1) // check if successful read (strcmp(OSStat, OldOSStat) != 0) //if status changed
2164 {
2165 //If this fails, simply return;
2166 char check_GU_valid1[RB_MAX_LEN] = {0};
2167 char check_GU_valid2[RB_MAX_LEN] = {0};
2168 char check_GU_valid3[RB_MAX_LEN] = {0};
2169 //:GU should always have one of pIPF and 3 numbers
2170 if (sscanf(OSStat, "%s%[pIPF]%s%[0-9]%[0-9]%[0-9]", check_GU_valid1, &check_GU_valid2[0], check_GU_valid3,
2171 &check_GU_valid2[1], &check_GU_valid2[2], &check_GU_valid2[3]) != 1)
2172 {
2173 LOG_WARN(":GU# returned something that can not be right, this update aborted, will try again...");
2174 LOGF_DEBUG("Parameters matched: %u from %s", sscanf(OSStat, "%s%[pIPF]%s%[0-9]%[0-9]%[0-9]", check_GU_valid1,
2175 &check_GU_valid2[0], check_GU_valid3, &check_GU_valid2[1], &check_GU_valid2[2], &check_GU_valid2[3]), OSStat);
2176 flushIO(PortFD);
2177 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
2178 }
2179 if (getLX200RA(PortFD, &currentRA) < 0 || getLX200DEC(PortFD, &currentDEC) < 0) // Update actual position
2180 {
2182 LOG_ERROR("Error reading RA/DEC.");
2183 EqNP.apply();
2184 LOG_INFO("RA/DEC could not be read, possible solution if using (wireless) ethernet: Use port 9998");
2185 LOG_WARN("This update aborted, will try again...");
2186 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
2187 }
2188 snprintf(OldOSStat, sizeof(OldOSStat), "%s", OSStat);
2189
2190 OnstepStatTP[0].setText(OSStat);
2191
2192 // ============= Parkstatus
2193
2194#ifdef DEBUG_TRACKSTATE
2195 LOG_DEBUG("Prior TrackState:");
2197 LOG_DEBUG("^ Prior");
2198#endif
2199 //TelescopeStatus PriorTrackState = TrackState;
2200 // not [p]arked, parking [I]n-progress, [P]arked, Park [F]ailed
2201 // "P" (Parked moved to Telescope Status, since it would override any other TrackState
2202 // Other than parked, none of these affect TrackState
2203 if (strstr(OSStat, "F"))
2204 {
2205 OnstepStatTP[3].setText("Parking Failed");
2206 }
2207 if (strstr(OSStat, "I"))
2208 {
2209 OnstepStatTP[3].setText("Park in Progress");
2210 }
2211 if (strstr(OSStat, "p"))
2212 {
2213 OnstepStatTP[3].setText("UnParked");
2214 }
2215 // ============= End Parkstatus
2216
2217
2218
2219
2220 // ============= Telescope Status
2221
2222 if (strstr(OSStat, "P"))
2223 {
2225 OnstepStatTP[3].setText("Parked");
2226 OnstepStatTP[1].setText("Parked");
2227 if (!isParked()) //Don't call this every time OSStat changes
2228 {
2229 SetParked(true);
2230 }
2232 }
2233 else
2234 {
2235 if (strstr(OSStat, "n") && strstr(OSStat, "N"))
2236 {
2237 OnstepStatTP[1].setText("Idle");
2239 }
2240 if (strstr(OSStat, "n") && !strstr(OSStat, "N"))
2241 {
2242 if (strstr(OSStat, "I"))
2243 {
2244 OnstepStatTP[1].setText("Parking/Slewing");
2246 }
2247 else
2248 {
2249 OnstepStatTP[1].setText("Slewing");
2251 }
2252 }
2253 if (strstr(OSStat, "N") && !strstr(OSStat, "n"))
2254 {
2255 OnstepStatTP[1].setText("Tracking");
2257 }
2258 if (!strstr(OSStat, "N") && !strstr(OSStat, "n"))
2259 {
2260 OnstepStatTP[1].setText("Slewing");
2262 }
2264 if (isParked()) //IMPORTANT: SET AFTER setting TrackState!
2265 {
2266 SetParked(false);
2267 }
2269 }
2270 // Set TrackStateSP based on above, but only change if needed.
2271 // NOTE: Technically during a slew it can have tracking on, but elsewhere there is the assumption:
2272 // Slewing = Not tracking
2273#ifdef DEBUG_TRACKSTATE
2274 LOG_DEBUG("BEFORE UPDATE");
2275 if (EqNP.s == IPS_BUSY)
2276 {
2277 LOG_DEBUG("EqNP is IPS_BUSY (Goto/slew or Parking)");
2278 }
2279 if (EqNP.s == IPS_OK)
2280 {
2281 LOG_DEBUG("EqNP is IPS_OK (Tracking)");
2282 }
2283 if (EqNP.s == IPS_IDLE)
2284 {
2285 LOG_DEBUG("EqNP is IPS_IDLE (Not Tracking or Parked)");
2286 }
2287 if (EqNP.s == IPS_ALERT)
2288 {
2289 LOG_DEBUG("EqNP is IPS_ALERT (Something wrong)");
2290 }
2291 LOG_DEBUG("/BEFORE UPDATE");
2292#endif
2293 // Fewer updates might help with KStars handling.
2294 bool trackStateUpdateNeded = false;
2296 {
2298 {
2300 trackStateUpdateNeded = true;
2301 }
2302 if (TrackStateSP[TRACK_ON].getState() != ISS_ON || TrackStateSP[TRACK_OFF].getState() != ISS_OFF)
2303 {
2306 trackStateUpdateNeded = true;
2307 }
2308 }
2309 else
2310 {
2312 {
2314 trackStateUpdateNeded = true;
2315 }
2316 if (TrackStateSP[TRACK_ON].getState() != ISS_OFF || TrackStateSP[TRACK_OFF].getState() != ISS_ON)
2317 {
2320 trackStateUpdateNeded = true;
2321 }
2322 }
2323 if (trackStateUpdateNeded)
2324 {
2325#ifdef DEBUG_TRACKSTATE
2326 LOG_DEBUG("TRACKSTATE CHANGED");
2327#endif
2329 }
2330 else
2331 {
2332#ifdef DEBUG_TRACKSTATE
2333 LOG_DEBUG("TRACKSTATE UNCHANGED");
2334#endif
2335 }
2336 //TrackState should be set correctly, only update EqNP if actually needed.
2337 bool update_needed = false;
2338 switch (TrackState)
2339 {
2340 case SCOPE_PARKED:
2341 case SCOPE_IDLE:
2342 if (EqNP.getState() != IPS_IDLE)
2343 {
2345 update_needed = true;
2346#ifdef DEBUG_TRACKSTATE
2347 LOG_DEBUG("EqNP set to IPS_IDLE");
2348#endif
2349 }
2350 break;
2351
2352 case SCOPE_SLEWING:
2353 case SCOPE_PARKING:
2354 if (EqNP.getState() != IPS_BUSY)
2355 {
2357 update_needed = true;
2358#ifdef DEBUG_TRACKSTATE
2359 LOG_DEBUG("EqNP set to IPS_BUSY");
2360#endif
2361 }
2362 break;
2363
2364 case SCOPE_TRACKING:
2365 if (EqNP.getState() != IPS_OK)
2366 {
2368 update_needed = true;
2369#ifdef DEBUG_TRACKSTATE
2370 LOG_DEBUG("EqNP set to IPS_OK");
2371#endif
2372 }
2373 break;
2374 }
2375 if (EqNP[AXIS_RA].getValue() != currentRA || EqNP[AXIS_DE].getValue() != currentDEC)
2376 {
2377#ifdef DEBUG_TRACKSTATE
2378 LOG_DEBUG("EqNP coordinates updated");
2379#endif
2380 update_needed = true;
2381 }
2382 if (update_needed)
2383 {
2384#ifdef DEBUG_TRACKSTATE
2385 LOG_DEBUG("EqNP changed state");
2386#endif
2387 EqNP[AXIS_RA].setValue(currentRA);
2388 EqNP[AXIS_DE].setValue(currentDEC);
2389 EqNP.apply();
2390#ifdef DEBUG_TRACKSTATE
2391 if (EqNP.s == IPS_BUSY)
2392 {
2393 LOG_DEBUG("EqNP is IPS_BUSY (Goto/slew or Parking)");
2394 }
2395 if (EqNP.s == IPS_OK)
2396 {
2397 LOG_DEBUG("EqNP is IPS_OK (Tracking)");
2398 }
2399 if (EqNP.s == IPS_IDLE)
2400 {
2401 LOG_DEBUG("EqNP is IPS_IDLE (Not Tracking or Parked)");
2402 }
2403 if (EqNP.s == IPS_ALERT)
2404 {
2405 LOG_DEBUG("EqNP is IPS_ALERT (Something wrong)");
2406 }
2407#endif
2408 }
2409 else
2410 {
2411#ifdef DEBUG_TRACKSTATE
2412 LOG_DEBUG("EqNP UNCHANGED");
2413#endif
2414 }
2416
2417 // ============= End Telescope Status
2418
2419 // ============= Refractoring
2420 if ((strstr(OSStat, "r") || strstr(OSStat, "t"))) //On, either refractory only (r) or full (t)
2421 {
2422 if (strstr(OSStat, "t"))
2423 {
2424 OnstepStatTP[2].setText("Full Comp");
2425 }
2426 if (strstr(OSStat, "r"))
2427 {
2428 OnstepStatTP[2].setText("Refractory Comp");
2429 }
2430 if (strstr(OSStat, "s"))
2431 {
2432 OnstepStatTP[8].setText("Single Axis");
2433 }
2434 else
2435 {
2436 OnstepStatTP[8].setText("2-Axis");
2437 }
2438 }
2439 else
2440 {
2441 OnstepStatTP[2].setText("Refractoring Off");
2442 OnstepStatTP[8].setText("N/A");
2443 }
2444
2445 //if (strstr(OSStat,"H")) { OnstepStatTP[3].setText("At Home"); }
2446 if (strstr(OSStat, "H") && strstr(OSStat, "P"))
2447 {
2448 OnstepStatTP[3].setText("At Home and Parked");
2449 }
2450 if (strstr(OSStat, "H") && strstr(OSStat, "p"))
2451 {
2452 OnstepStatTP[3].setText("At Home and UnParked");
2453 }
2454 //AutoPauseAtHome
2455 if (strstr(OSStat, "u")) // pa[u]se at home enabled?
2456 {
2459 HomePauseSP.apply("Pause at Home Enabled");
2460 }
2461 else
2462 {
2466 }
2467
2468 if (strstr(OSStat, "w"))
2469 {
2470 OnstepStatTP[3].setText("Waiting at Home");
2471 }
2472
2473 // ============= Pec Status
2474 if ((!strstr(OSStat, "R") && !strstr(OSStat, "W")))
2475 {
2476 OnstepStatTP[4].setText("N/A");
2477 }
2478 if (strstr(OSStat, "R"))
2479 {
2480 OnstepStatTP[4].setText("Recorded");
2481 }
2482 if (strstr(OSStat, "W"))
2483 {
2484 OnstepStatTP[4].setText("Autorecord");
2485 }
2486
2487 //Handles pec with :GU, also disables the (old) :$QZ?# command
2488 if (strstr(OSStat, "/"))
2489 {
2490 OnstepStatTP[4].setText("Ignored");
2491 OSPECviaGU = true;
2495 }
2496 if (strstr(OSStat, ";"))
2497 {
2498
2499 OnstepStatTP[4].setText("AutoRecord (waiting on index)");
2500 OSPECviaGU = true;
2504 }
2505 if (strstr(OSStat, ","))
2506 {
2507 OnstepStatTP[4].setText("AutoPlaying (waiting on index)");
2508 OSPECviaGU = true;
2512 }
2513 if (strstr(OSStat, "~"))
2514 {
2515 OnstepStatTP[4].setText("Playing");
2516 OSPECviaGU = true;
2520 }
2521 if (strstr(OSStat, "^"))
2522 {
2523 OnstepStatTP[4].setText("Recording");
2524 OSPECviaGU = true;
2528 }
2529 if (OSPECviaGU)
2530 {
2532 {
2533 //We have PEC reported via :GU already, enable if any are detected, as they are not reported with ALTAZ/FORK_ALT)
2534 //NOTE: Might want to drop the && !strstr(OSStat, "/") as it will startup that way.
2535 uint32_t capabilities = GetTelescopeCapability();
2536 if ((capabilities | TELESCOPE_HAS_PEC) != capabilities)
2537 {
2538 LOG_INFO("Telescope detected having PEC, setting that capability");
2539 LOGF_DEBUG("capabilities = %x", capabilities);
2540 capabilities |= TELESCOPE_HAS_PEC;
2541 SetTelescopeCapability(capabilities, 10 );
2542 initSlewRates();
2544 }
2545 }
2549 }
2550
2551
2552
2553 // ============= Time Sync Status
2554 if (!strstr(OSStat, "S"))
2555 {
2556 OnstepStatTP[5].setText("N/A");
2557 }
2558 if (strstr(OSStat, "S"))
2559 {
2560 OnstepStatTP[5].setText("PPS / GPS Sync Ok");
2561 }
2562
2563 // ============= Mount Types
2564 if (strstr(OSStat, "E"))
2565 {
2566 OnstepStatTP[6].setText("German Equatorial Mount");
2568 }
2569 if (strstr(OSStat, "K"))
2570 {
2571 OnstepStatTP[6].setText("Fork Mount");
2573 }
2574 if (strstr(OSStat, "k"))
2575 {
2576 //NOTE: This seems to have been removed from OnStep, so the chances of encountering it are small, I can't even find, but I think it was Alt-Az mounting of a FORK, now folded into ALTAZ
2577 OnstepStatTP[6].setText("Fork Alt Mount");
2579 }
2580 if (strstr(OSStat, "A"))
2581 {
2582 OnstepStatTP[6].setText("AltAZ Mount");
2584 }
2585
2586
2587 //Pier side:
2588 // o - nOne
2589 // T - easT
2590 // W - West
2592 {
2593 uint32_t capabilities = GetTelescopeCapability();
2594 if ((capabilities | TELESCOPE_HAS_PIER_SIDE) != capabilities)
2595 {
2596 LOG_INFO("Telescope detected having Pier Side, adding that capability (many messages duplicated)");
2597 LOGF_DEBUG("capabilities = %x", capabilities);
2598 capabilities |= TELESCOPE_HAS_PIER_SIDE;
2599 SetTelescopeCapability(capabilities, 10 );
2600 initSlewRates();
2602 }
2603 if (strstr(OSStat, "o"))
2604 {
2606 PIER_UNKNOWN); //Closest match to None, For forks may trigger an extra goto, during imaging if it would do a meridian flip
2607 pier_not_set = false;
2608 }
2609 if (strstr(OSStat, "T"))
2610 {
2612 pier_not_set = false;
2613 }
2614 if (strstr(OSStat, "W"))
2615 {
2617 pier_not_set = false;
2618 }
2619 }
2620
2621 // ============= Error Code
2622 //From OnStep: ERR_NONE, ERR_MOTOR_FAULT, ERR_ALT_MIN, ERR_LIMIT_SENSE, ERR_DEC, ERR_AZM, ERR_UNDER_POLE, ERR_MERIDIAN, ERR_SYNC, ERR_PARK, ERR_GOTO_SYNC, ERR_UNSPECIFIED, ERR_ALT_MAX, ERR_GOTO_ERR_NONE, ERR_GOTO_ERR_BELOW_HORIZON, ERR_GOTO_ERR_ABOVE_OVERHEAD, ERR_GOTO_ERR_STANDBY, ERR_GOTO_ERR_PARK, ERR_GOTO_ERR_GOTO, ERR_GOTO_ERR_OUTSIDE_LIMITS, ERR_GOTO_ERR_HARDWARE_FAULT, ERR_GOTO_ERR_IN_MOTION, ERR_GOTO_ERR_UNSPECIFIED
2623
2624 //For redoing this, quick python, if needed (will only give the error name):
2625 //all_errors=' (Insert)
2626 //split_errors=all_errors.split(', ')
2627 //for specific in split_errors:
2628 // print('case ' +specific+':')
2629 // print('\tIUSaveText(&OnstepStat[7],"'+specific+'");')
2630 // print('\tbreak;')
2631
2632 Lasterror = (Errors)(OSStat[strlen(OSStat) - 1] - '0');
2633
2634 // Refresh current Slew Rate
2635 int idx = OSStat[strlen(OSStat) - 2] - '0';
2636 if (SlewRateSP.findOnSwitchIndex() != idx)
2637 {
2638 SlewRateSP.reset();
2641 SlewRateSP.apply();
2642 LOGF_DEBUG("Slew Rate Index: %d", idx);
2643 }
2644 // End Refresh current Slew Rate
2645 }
2646 else
2647 {
2648 return false;
2649 }
2650
2651#ifdef OnStep_Alpha // For the moment, for :Gu
2652 }
2653 else
2654 {
2655 //TODO: Check and recode :Gu# paths
2657 ":Gu#"); // :Gu# returns a string containing controller status that's bitpacked
2658 if (strcmp(OSStat, OldOSStat) != 0) //if status changed
2659 {
2660 //Ignored for now.
2661 }
2662 //Byte 0: Current Status
2663 if (OSStat[0] & 0b10000001 == 0b10000001)
2664 {
2665 //Not tracking
2666 }
2667 if (OSStat[0] & 0b10000010 == 0b10000010)
2668 {
2669 //No goto
2670 }
2671 if (OSStat[0] & 0b10000100 == 0b10000100)
2672 {
2673 // PPS sync
2674 OnstepStatTP[5].setText("PPS / GPS Sync Ok");
2675 }
2676 else
2677 {
2678 OnstepStatTP[5].setText("N/A");
2679 }
2680 if (OSStat[0] & 0b10001000 == 0b10001000)
2681 {
2682 // Guide active
2683 }
2684 //Refraction and Number of axis handled differently for now, might combine to one variable.
2685 // reply[0]|=0b11010000; // Refr enabled Single axis
2686 // reply[0]|=0b10010000; // Refr enabled
2687 // reply[0]|=0b11100000; // OnTrack enabled Single axis
2688 // reply[0]|=0b10100000; // OnTrack enabled
2689 if ((OSStat[0] & 0b10010000 == 0b10010000
2690 || OSStat[0] & 0b10100000 == 0b10100000)) //On, either refractory only (r) or full (t)
2691 {
2692 if (OSStat[0] & 0b10100000 == 0b10100000)
2693 {
2694 OnstepStatTP[2].setText("Full Comp");
2695 }
2696 if (OSStat[0] & 0b10010000 == 0b10010000)
2697 {
2698 OnstepStatTP[2].setText("Refractory Comp");
2699 }
2700 if (OSStat[0] & 0b11000000 == 0b11000000)
2701 {
2702 OnstepStatTP[8].setText("Single Axis");
2703 }
2704 else
2705 {
2706 OnstepStatTP[8].setText("2-Axis");
2707 }
2708 }
2709 else
2710 {
2711 OnstepStatTP[2].setText("Refractoring Off");
2712 OnstepStatTP[8].setText("N/A");
2713 }
2714 //Byte 1: Standard tracking rates
2715 if (OSStat[1] & 0b10000001 == 0b10000001)
2716 {
2717 // Lunar rate selected
2718 }
2719 if (OSStat[1] & 0b10000010 == 0b10000010)
2720 {
2721 // Solar rate selected
2722 }
2723 if (OSStat[1] & 0b10000011 == 0b10000011)
2724 {
2725 // King rate selected
2726 }
2727 //Byte 2: Flags
2728 if (OSStat[2] & 0b10000001 == 0b10000001)
2729 {
2730 // At home
2731 }
2732 if (OSStat[2] & 0b10000010 == 0b10000010)
2733 {
2734 // Waiting at home
2735 OnstepStatTP[3].setText("Waiting at Home");
2736 }
2737 if (OSStat[2] & 0b10000100 == 0b10000100)
2738 {
2739 // Pause at home enabled?
2740 //AutoPauseAtHome
2743 HomePauseSP.apply("Pause at Home Enabled");
2744 }
2745 else
2746 {
2750 }
2751 if (OSStat[2] & 0b10001000 == 0b10001000)
2752 {
2753 // Buzzer enabled?
2754 }
2755 if (OSStat[2] & 0b10010000 == 0b10010000)
2756 {
2757 // Auto meridian flip
2761 AutoFlipSP.apply();
2762 }
2763 else
2764 {
2768 AutoFlipSP.apply();
2769 }
2770 if (OSStat[2] & 0b10100000 == 0b10100000)
2771 {
2772 // PEC data has been recorded
2773 }
2774
2775
2776
2777
2778 //Byte 3: Mount type and info
2779 if (OSStat[3] & 0b10000001 == 0b10000001)
2780 {
2781 // GEM
2782 OnstepStatTP[6].setText("German Mount");
2784 }
2785 if (OSStat[3] & 0b10000010 == 0b10000010)
2786 {
2787 // FORK
2788 OnstepStatTP[6].setText("Fork Mount");
2790 }
2791 if (OSStat[3] & 0b10000100 == 0b10000100) //Seems depreciated/subsumed into FORK
2792 {
2793 // Fork Alt
2794 OnstepStatTP[6].setText("Fork Alt Mount");
2796 }
2797 if (OSStat[3] & 0b10001000 == 0b10001000)
2798 {
2799 // ALTAZM
2800 OnstepStatTP[6].setText("AltAZ Mount");
2802 }
2803
2804
2806 if (OSStat[3] & 0b10010000 == 0b10010000)
2807 {
2808 // Pier side none
2810 // INDI doesn't account for 'None'
2811 }
2812 if (OSStat[3] & 0b10100000 == 0b10100000)
2813 {
2814 // Pier side east
2816 }
2817 if (OSStat[3] & 0b11000000 == 0b11000000)
2818 {
2819 // Pier side west
2821 }
2822 // Byte 4: PEC
2823 PECStatusGU = OSStat[4] & 0b01111111;
2824 if (OSStat[4] == 0)
2825 {
2826 // AltAZM, no PEC possible
2827 PECStatusGU = 0;
2828 }
2829 else
2830 {
2831 // PEC status: 0 ignore, 1 get ready to play, 2 playing, 3 get ready to record, 4 recording
2832
2833
2834 }
2835 ParkStatusGU = OSStat[5] & 0b01111111;
2836 PulseGuideGU = OSStat[6] & 0b01111111;
2837 GuideRateGU = OSStat[7] & 0b01111111;
2838 LastError = OSStat[8] & 0b01111111;
2839 }
2840#endif
2841
2842
2843 switch (Lasterror)
2844 {
2845 case ERR_NONE:
2846 OnstepStatTP[7].setText("None");
2847 break;
2848 case ERR_MOTOR_FAULT:
2849 OnstepStatTP[7].setText("Motor/Driver Fault");
2850 break;
2851 case ERR_ALT_MIN:
2852 OnstepStatTP[7].setText("Below Horizon Limit");
2853 break;
2854 case ERR_LIMIT_SENSE:
2855 OnstepStatTP[7].setText("Limit Sense");
2856 break;
2857 case ERR_DEC:
2858 OnstepStatTP[7].setText("Dec Limit Exceeded");
2859 break;
2860 case ERR_AZM:
2861 OnstepStatTP[7].setText("Azm Limit Exceeded");
2862 break;
2863 case ERR_UNDER_POLE:
2864 OnstepStatTP[7].setText("Under Pole Limit Exceeded");
2865 break;
2866 case ERR_MERIDIAN:
2867 OnstepStatTP[7].setText("Meridian Limit (W) Exceeded");
2868 break;
2869 case ERR_SYNC:
2870 OnstepStatTP[7].setText("Sync Safety Limit Exceeded");
2871 break;
2872 case ERR_PARK:
2873 OnstepStatTP[7].setText("Park Failed");
2874 break;
2875 case ERR_GOTO_SYNC:
2876 OnstepStatTP[7].setText("Goto Sync Failed");
2877 break;
2878 case ERR_UNSPECIFIED:
2879 OnstepStatTP[7].setText("Unspecified Error");
2880 break;
2881 case ERR_ALT_MAX:
2882 OnstepStatTP[7].setText("Above Overhead Limit");
2883 break;
2884 case ERR_GOTO_ERR_NONE:
2885 OnstepStatTP[7].setText("Goto No Error");
2886 break;
2888 OnstepStatTP[7].setText("Goto Below Horizon");
2889 break;
2891 OnstepStatTP[7].setText("Goto Abv Overhead");
2892 break;
2894 OnstepStatTP[7].setText("Goto Err Standby");
2895 break;
2896 case ERR_GOTO_ERR_PARK:
2897 OnstepStatTP[7].setText("Goto Err Park");
2898 break;
2899 case ERR_GOTO_ERR_GOTO:
2900 OnstepStatTP[7].setText("Goto Err Goto");
2901 break;
2903 OnstepStatTP[7].setText("Goto Outside Limits");
2904 break;
2906 OnstepStatTP[7].setText("Goto H/W Fault");
2907 break;
2909 OnstepStatTP[7].setText("Goto Err Motion");
2910 break;
2912 OnstepStatTP[7].setText("Goto Unspecified Error");
2913 break;
2914 default:
2915 OnstepStatTP[7].setText("Unknown Error");
2916 break;
2917 }
2918
2919#ifndef OnStep_Alpha
2920 // Get actual Pier Side
2921 if (pier_not_set)
2922 {
2924 {
2926 }
2927 else
2928 {
2929 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, OSPier, ":Gm#");
2930 if (error_or_fail > 1)
2931 {
2932 if (strcmp(OSPier, OldOSPier) != 0) // any change ?
2933 {
2934 snprintf(OldOSPier, sizeof(OldOSPier), "%s", OSPier);
2935 switch(OSPier[0])
2936 {
2937 case 'E':
2939 break;
2940 case 'W':
2942 break;
2943 case 'N':
2945 break;
2946 case '?':
2948 break;
2949 }
2950 }
2951 }
2952 else
2953 {
2954 LOG_WARN("Communication error on Pier Side (:Gm#), this update aborted, will try again...");
2955 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
2956 }
2957 }
2958 }
2959#endif
2960
2961
2962 //========== Jasem Mutlaq 2025.01.10: If we are in manual motion, immediately return as rapid updates for RA/DE are far more critical
2963 // then the rest of the measurements below.
2965 return true;
2966
2967 //========== Get actual Backlash values
2968 double backlash_DEC, backlash_RA;
2969 int BD_error = getCommandDoubleResponse(PortFD, &backlash_DEC, OSbacklashDEC, ":%BD#");
2970 int BR_error = getCommandDoubleResponse(PortFD, &backlash_RA, OSbacklashRA, ":%BR#");
2971 if (BD_error > 1 && BR_error > 1)
2972 {
2973 BacklashNP[0].setValue(backlash_DEC);
2974 BacklashNP[1].setValue(backlash_RA);
2975 BacklashNP.apply();
2976 }
2977 else
2978 {
2979 LOG_WARN("Communication error on backlash (:%BD#/:%BR#), this update aborted, will try again...");
2980 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
2981 }
2982
2983 double pulseguiderate = 0.0;
2984 if (getCommandDoubleResponse(PortFD, &pulseguiderate, GuideValue, ":GX90#") > 1)
2985 {
2986 LOGF_DEBUG("Guide Rate String: %s", GuideValue);
2987 pulseguiderate = atof(GuideValue);
2988 LOGF_DEBUG("Guide Rate: %f", pulseguiderate);
2989 GuideRateNP[0].setValue(pulseguiderate);
2990 GuideRateNP[1].setValue(pulseguiderate);
2992 }
2993 else
2994 {
2995 LOGF_DEBUG("Guide Rate String: %s", GuideValue);
2996 LOG_DEBUG("Guide rate error response, Not setting guide rate from :GX90# response, falling back to :GU#, which may not be accurate, if custom settings are used");
2997 int pulseguiderateint = (Errors)(OSStat[strlen(OSStat) - 3] - '0');
2998 switch(pulseguiderateint)
2999 {
3000 case 0:
3001 pulseguiderate = (double)0.25;
3002 break;
3003 case 1:
3004 pulseguiderate = (double)0.5;
3005 break;
3006 case 2:
3007 pulseguiderate = (double)1.0;
3008 break;
3009 default:
3010 pulseguiderate = 0.0;
3011 LOG_DEBUG("Could not get guide rate from :GU# response, not setting");
3012 LOG_WARN("Communication error on Guide Rate (:GX90#/:GU#), this update aborted, will try again...");
3013 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3014 }
3015 if (pulseguiderate != 0.0)
3016 {
3017 LOGF_DEBUG("Guide Rate: %f", pulseguiderate);
3018 GuideRateNP[0].setValue(pulseguiderate);
3019 GuideRateNP[1].setValue(pulseguiderate);
3021 }
3022 }
3023
3024#ifndef OnStep_Alpha
3026 {
3027 //AutoFlip
3028 char merdidianflipauto_response[RB_MAX_LEN] = {0};
3029 int gx95_error = getCommandSingleCharErrorOrLongResponse(PortFD, merdidianflipauto_response, ":GX95#");
3030 if (gx95_error > 1)
3031 {
3032 if (merdidianflipauto_response[0] == '1') // && merdidianflipauto_response[1] == 0) //Only set on 1#
3033 {
3037 AutoFlipSP.apply();
3038 }
3039 else if (merdidianflipauto_response[0] == '0') // && merdidianflipauto_response[1] == 0) //Only set on 1#
3040 {
3044 AutoFlipSP.apply();
3045 }
3046 }
3047 else
3048 {
3049 LOG_WARN("Communication error on meridianAutoFlip (:GX95#), this update aborted, will try again...");
3050 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3051 }
3052 }
3053#endif
3054
3055 if (OSMountType == MOUNTTYPE_GEM) //Doesn't apply to non-GEMs
3056 {
3057 //PreferredPierSide
3058 char preferredpierside_response[RB_MAX_LEN] = {0};
3059 int gx96_error = getCommandSingleCharErrorOrLongResponse(PortFD, preferredpierside_response, ":GX96#");
3060 if (gx96_error > 1)
3061 {
3062 if (strstr(preferredpierside_response, "W"))
3063 {
3067 }
3068 else if (strstr(preferredpierside_response, "E"))
3069 {
3073 }
3074 else if (strstr(preferredpierside_response, "B"))
3075 {
3079 }
3080 /* remove this dead code
3081 else if (strstr(preferredpierside_response, "%"))
3082 {
3083 //NOTE: This bug is only present in very early OnStepX, and should be fixed shortly after 10.03k
3084 LOG_DEBUG(":GX96 returned \% indicating early OnStepX bug");
3085 PreferredPierSideSP.reset();
3086 PreferredPierSideSP.setState(IPS_ALERT);
3087 PreferredPierSideSP.apply();
3088 }
3089 */
3090 else
3091 {
3095 }
3096 }
3097 else
3098 {
3099 LOG_WARN("Communication error on Preferred Pier Side (:GX96#), this update aborted, will try again...");
3100 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3101 }
3102
3104 {
3105 // Minutes past Meridian, Onstep uses angulat values in degree we use minutes: 1° = 4 minutes
3106 char limit1_response[RB_MAX_LEN] = {0};
3107 int gxea_error, gxe9_error;
3108 double minutes_past_Meridian_East, minutes_past_Meridian_West;
3109 gxe9_error = getCommandDoubleResponse(PortFD, &minutes_past_Meridian_East, limit1_response, ":GXE9#");
3110 if (gxe9_error > 1) //NOTE: Possible failure not checked.
3111 {
3112 char limit2_response[RB_MAX_LEN] = {0};
3113 gxea_error = getCommandDoubleResponse(PortFD, &minutes_past_Meridian_West, limit2_response, ":GXEA#");
3114 if (gxea_error > 1) //NOTE: Possible failure not checked.
3115 {
3116 minutesPastMeridianNP[0].setValue(minutes_past_Meridian_East); // E
3117 minutesPastMeridianNP[1].setValue(minutes_past_Meridian_West); //W
3119 }
3120 else
3121 {
3122 LOG_WARN("Communication error on Degrees past Meridian West (:GXEA#), this update aborted, will try again...");
3123 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3124 }
3125 }
3126 else
3127 {
3128 LOG_WARN("Communication error on Degrees past Meridian East (:GXE9#), this update aborted, will try again...");
3129 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3130 }
3131 }
3132 }
3133 // Get Overhead Limits
3134 // :Go# Get Overhead Limit
3135 // Returns: DD*#
3136 // The highest elevation above the horizon that the telescope will goto
3137 char Go[RB_MAX_LEN] = {0};
3138 int Go_int ;
3139 int Go_error = getCommandIntResponse(PortFD, &Go_int, Go, ":Go#");
3140 if (Go_error > 0)
3141 {
3142 ElevationLimitNP[1].setValue(atoi(Go));
3144 LOGF_DEBUG("Elevation Limit Min: %s, %i Go_nbcar: %i", Go, Go_int, Go_error); //typo
3145 }
3146 else
3147 {
3148 LOG_WARN("Communication :Go# error, check connection.");
3149 flushIO(PortFD); //Unlikely to do anything, but just in case.
3150 }
3151
3152 // :Gh# Get Horizon Limit, the minimum elevation of the mount relative to the horizon
3153 // Returns: sDD*#
3154 char Gh[RB_MAX_LEN] = {0};
3155 int Gh_int ;
3156 int Gh_error = getCommandIntResponse(PortFD, &Gh_int, Gh, ":Gh#");
3157 if (Gh_error > 0)
3158 {
3159 ElevationLimitNP[0].setValue(atoi(Gh));
3161 LOGF_DEBUG("Elevation Limit Min: %s, %i Gh_nbcar: %i", Gh, Gh_int, Gh_error); //typo
3162 }
3163 else
3164 {
3165 LOG_WARN("Communication :Gh# error, check connection.");
3166 flushIO(PortFD); //Unlikely to do anything, but just in case.
3167 }
3168 // End Get Overhead Limits
3169
3170 //TODO: Improve Rotator support
3171 if (OSUpdateRotator() != 0)
3172 {
3173 LOG_WARN("Communication error on Rotator Update, this update aborted, will try again...");
3174 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3175 }
3176
3177 //Weather update
3178 char temperature_response[RB_MAX_LEN] = {0};
3179 double temperature_value;
3180 int gx9a_error = getCommandDoubleResponse(PortFD, &temperature_value, temperature_response, ":GX9A#");
3181 if (gx9a_error > 1) //> 1 as an OnStep error would be 1 char in response
3182 {
3183 setParameterValue("WEATHER_TEMPERATURE", temperature_value);
3184 }
3185 else
3186 {
3187 LOG_WARN("Communication error on Temperature (:GX9A#), this update aborted, will try again...");
3188 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3189 }
3190
3191 char humidity_response[RB_MAX_LEN] = {0};
3192 double humidity_value;
3193 int gx9c_error = getCommandDoubleResponse(PortFD, &humidity_value, humidity_response, ":GX9C#");
3194 if (gx9c_error > 1) //> 1 as an OnStep error would be 1 char in response
3195 {
3196 setParameterValue("WEATHER_HUMIDITY", humidity_value);
3197 }
3198 else
3199 {
3200 LOG_WARN("Communication error on Humidity (:GX9C#), this update aborted, will try again...");
3201 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3202 }
3203
3204
3205 char barometer_response[RB_MAX_LEN] = {0};
3206 double barometer_value;
3207 int gx9b_error = getCommandDoubleResponse(PortFD, &barometer_value, barometer_response, ":GX9B#");
3208 if (gx9b_error > 1)
3209 {
3210 setParameterValue("WEATHER_BAROMETER", barometer_value);
3211 }
3212 else
3213 {
3214 LOG_WARN("Communication error on Barometer (:GX9B#), this update aborted, will try again...");
3215 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3216 }
3217
3218 char dewpoint_reponse[RB_MAX_LEN] = {0};
3219 double dewpoint_value;
3220 int gx9e_error = getCommandDoubleResponse(PortFD, &dewpoint_value, dewpoint_reponse, ":GX9E#");
3221 if (gx9e_error > 1)
3222 {
3223 setParameterValue("WEATHER_DEWPOINT", dewpoint_value);
3224 }
3225 else
3226 {
3227 LOG_WARN("Communication error on Dewpoint (:GX9E#), this update aborted, will try again...");
3228 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3229 }
3230
3231 if (OSCpuTemp_good)
3232 {
3233 char cputemp_reponse[RB_MAX_LEN] = {0};
3234 double cputemp_value;
3235 int error_return = getCommandDoubleResponse(PortFD, &cputemp_value, cputemp_reponse, ":GX9F#");
3236 if ( error_return >= 0) // && !strcmp(cputemp_reponse, "0") )
3237 {
3238 setParameterValue("WEATHER_CPU_TEMPERATURE", cputemp_value);
3239 }
3240 else
3241 {
3242 LOGF_DEBUG("CPU Temp not responded to, disabling further checks, return values: error_return: %i, cputemp_reponse: %s",
3243 error_return, cputemp_reponse);
3244 OSCpuTemp_good = false;
3245 }
3246 }
3247 //
3248 //Disabled, because this is supplied via Kstars or other location, no sensor to read this
3249 //double altitude_value;
3250 //int error_or_fail = getCommandDoubleResponse(PortFD, &altitude_value, TempValue, ":GX9D#");
3251 //setParameterValue("WEATHER_ALTITUDE", altitude_value);
3253
3258
3259 if (TMCDrivers)
3260 {
3261 for (int driver_number = 1; driver_number < 3; driver_number++)
3262 {
3263 char TMCDriverTempValue[RB_MAX_LEN] = {0};
3264 char TMCDriverCMD[CMD_MAX_LEN] = {0};
3265 snprintf(TMCDriverCMD, sizeof(TMCDriverCMD), ":GXU%i#", driver_number);
3266 if (TMCDrivers) //Prevent check on :GXU2# if :GXU1# failed
3267 {
3268 int i = getCommandSingleCharErrorOrLongResponse(PortFD, TMCDriverTempValue, TMCDriverCMD);
3269 if (i == -4 && TMCDriverTempValue[0] == '0' )
3270 {
3271 char ResponseText[RB_MAX_LEN] = {0};
3272 snprintf(ResponseText, sizeof(ResponseText), "TMC Reporting not detected, Axis %i", driver_number);
3273 OnstepStatTP[8 + driver_number].setText(ResponseText);
3274 LOG_DEBUG("TMC Drivers responding as if not there, disabling further checks");
3275 TMCDrivers = false;
3276 }
3277 else
3278 {
3279 if (i > 0 )
3280 {
3281 if (TMCDriverTempValue[0] == 0)
3282 {
3283 OnstepStatTP[8 + driver_number].setText("No Condition");
3284 TMCDrivers = false;
3285 }
3286 else
3287 {
3288 char StepperState[1024] = {0};
3289 bool unknown_value = false;
3290 int current_position = 0;
3291 while (TMCDriverTempValue[current_position] != 0 && unknown_value == false)
3292 {
3293 if (TMCDriverTempValue[current_position] == ',')
3294 {
3295 current_position++;
3296 }
3297 else
3298 {
3299 if (TMCDriverTempValue[current_position] == 'S' && TMCDriverTempValue[current_position + 1] == 'T')
3300 {
3301 strcat(StepperState, "Standstill,");
3302 }
3303 else if (TMCDriverTempValue[current_position] == 'O' && TMCDriverTempValue[current_position + 1] == 'A')
3304 {
3305 strcat(StepperState, "Open Load A Pair,");
3306 }
3307 else if (TMCDriverTempValue[current_position] == 'O' && TMCDriverTempValue[current_position + 1] == 'B')
3308 {
3309 strcat(StepperState, "Open Load B Pair,");
3310 }
3311 else if (TMCDriverTempValue[current_position] == 'G' && TMCDriverTempValue[current_position + 1] == 'A')
3312 {
3313 strcat(StepperState, "Short to Ground A Pair,");
3314 }
3315 else if (TMCDriverTempValue[current_position] == 'G' && TMCDriverTempValue[current_position + 1] == 'B')
3316 {
3317 strcat(StepperState, "Short to Ground B Pair,");
3318 }
3319 else if (TMCDriverTempValue[current_position] == 'O' && TMCDriverTempValue[current_position + 1] == 'T')
3320 {
3321 strcat(StepperState, "Over Temp (>150C),");
3322 }
3323 else if (TMCDriverTempValue[current_position] == 'P' && TMCDriverTempValue[current_position + 1] == 'W')
3324 {
3325 strcat(StepperState, "Pre-Warning: Over Temp (>120C),");
3326 }
3327 else if (TMCDriverTempValue[current_position] == 'G' && TMCDriverTempValue[current_position + 1] == 'F')
3328 {
3329 strcat(StepperState, "General Fault,");
3330 }
3331 else
3332 {
3333 unknown_value = true;
3334 break;
3335 }
3336 current_position = current_position + 3;
3337 }
3338 }
3339 if (unknown_value)
3340 {
3341 OnstepStatTP[8 + driver_number].setText(TMCDriverTempValue);
3342 }
3343 else
3344 {
3345 OnstepStatTP[8 + driver_number].setText(StepperState);
3346 }
3347 }
3348 }
3349 else
3350 {
3351 OnstepStatTP[8 + driver_number].setText("Unknown read error");
3352 }
3353 }
3354 }
3355 }
3356 }
3357
3358 // Update OnStep Status TAB
3360 //Align tab, so it doesn't conflict
3361 //May want to reduce frequency of updates
3362 if (!UpdateAlignStatus())
3363 {
3364 LOG_WARN("Fail Align Command");
3365 LOG_WARN("Communication error on Align Status Update, this update aborted, will try again...");
3366 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3367 }
3369
3370
3371 if (OSUpdateFocuser() != 0) // Update Focuser Position
3372 {
3373 LOG_WARN("Communication error on Focuser Update, this update aborted, will try again...");
3374 return true; //COMMUNICATION ERROR, BUT DON'T PUT TELESCOPE IN ERROR STATE
3375 }
3376
3377#ifndef OnStep_Alpha
3378 if (!OSPECviaGU)
3379 {
3380 PECStatus(0);
3381 }
3382 //#Gu# has this built in
3383#endif
3384
3385
3386 return true;
3387}
3388
3389
3390bool LX200_OnStep::SetTrackEnabled(bool enabled) //track On/Off events handled by inditelescope Tested
3391{
3392 char response[RB_MAX_LEN];
3393
3394 if (enabled)
3395 {
3396 int res = getCommandSingleCharResponse(PortFD, response, ":Te#"); //0 = failure, 1 = success, no # on reply
3397 if(res < 0 || response[0] == '0')
3398 {
3399 LOGF_ERROR("===CMD==> Track On %s", response);
3400 return false;
3401 }
3402 }
3403 else
3404 {
3405 int res = getCommandSingleCharResponse(PortFD, response, ":Td#"); //0 = failure, 1 = success, no # on reply
3406 if(res < 0 || response[0] == '0')
3407 {
3408 LOGF_ERROR("===CMD==> Track Off %s", response);
3409 return false;
3410 }
3411 }
3412 return true;
3413}
3414
3415bool LX200_OnStep::setLocalDate(uint8_t days, uint8_t months, uint16_t years)
3416{
3417 years = years % 100;
3418 char cmd[CMD_MAX_LEN] = {0};
3419
3420 snprintf(cmd, CMD_MAX_LEN, ":SC%02d/%02d/%02d#", months, days, years);
3421
3422 if (!sendOnStepCommand(cmd)) return true;
3423 return false;
3424}
3425
3427{
3428 int error_type;
3429 int nbytes_write = 0;
3430
3431 DEBUGF(DBG_SCOPE, "CMD <%s>", cmd);
3432
3433 flushIO(PortFD);
3434 /* Add mutex */
3435 std::unique_lock<std::mutex> guard(lx200CommsLock);
3436 tcflush(PortFD, TCIFLUSH);
3437
3438
3439 if ((error_type = tty_write_string(PortFD, cmd, &nbytes_write)) != TTY_OK)
3440 {
3441 LOGF_ERROR("CHECK CONNECTION: Error sending command %s", cmd);
3442 return 0; //Fail if we can't write
3443 //return error_type;
3444 }
3445
3446 return 1;
3447}
3448
3450{
3451 char response[1] = {0};
3452 int error_type;
3453 int nbytes_write = 0, nbytes_read = 0;
3454
3455 DEBUGF(DBG_SCOPE, "CMD <%s>", cmd);
3456
3457 flushIO(PortFD);
3458 /* Add mutex */
3459 std::unique_lock<std::mutex> guard(lx200CommsLock);
3460 tcflush(PortFD, TCIFLUSH);
3461
3462 if ((error_type = tty_write_string(PortFD, cmd, &nbytes_write)) != TTY_OK)
3463 return error_type;
3464
3465 error_type = tty_read_expanded(PortFD, response, 1, OSTimeoutSeconds, OSTimeoutMicroSeconds, &nbytes_read);
3466
3467 tcflush(PortFD, TCIFLUSH);
3468 DEBUGF(DBG_SCOPE, "RES <%c>", response[0]);
3469
3470 if (nbytes_read < 1)
3471 {
3472 LOG_WARN("Timeout/Error on response. Check connection.");
3473 return false;
3474 }
3475
3476 return (response[0] == '0'); //OnStep uses 0 for success and non zero for failure, in *most* cases;
3477}
3478
3479int LX200_OnStep::getCommandSingleCharResponse(int fd, char *data, const char *cmd)
3480{
3481 char *term;
3482 int error_type;
3483 int nbytes_write = 0, nbytes_read = 0;
3484
3485 DEBUGF(DBG_SCOPE, "CMD <%s>", cmd);
3486
3487 flushIO(fd);
3488 /* Add mutex */
3489 std::unique_lock<std::mutex> guard(lx200CommsLock);
3490
3491 if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
3492 return error_type;
3493
3494 error_type = tty_read_expanded(fd, data, 1, OSTimeoutSeconds, OSTimeoutMicroSeconds, &nbytes_read);
3495 tcflush(fd, TCIFLUSH);
3496
3497 if (error_type != TTY_OK)
3498 return error_type;
3499
3500 term = strchr(data, '#');
3501 if (term)
3502 *term = '\0';
3503 if (nbytes_read < RB_MAX_LEN) //given this function that should always be true, as should nbytes_read always be 1
3504 {
3505 data[nbytes_read] = '\0';
3506 }
3507 else
3508 {
3509 LOG_DEBUG("got RB_MAX_LEN bytes back (which should never happen), last byte set to null and possible overflow");
3510 data[RB_MAX_LEN - 1] = '\0';
3511 }
3512
3513 DEBUGF(DBG_SCOPE, "RES <%s>", data);
3514
3515 return nbytes_read;
3516}
3517
3518
3520{
3521 tcflush(fd, TCIOFLUSH);
3522 int error_type = 0;
3523 int nbytes_read;
3524 std::unique_lock<std::mutex> guard(lx200CommsLock);
3525 tcflush(fd, TCIOFLUSH);
3526 do
3527 {
3528 char discard_data[RB_MAX_LEN] = {0};
3529 error_type = tty_nread_section_expanded(fd, discard_data, sizeof(discard_data), '#', 0, 1000, &nbytes_read);
3530 if (error_type >= 0)
3531 {
3532 LOGF_DEBUG("flushIO: Information in buffer: Bytes: %u, string: %s", nbytes_read, discard_data);
3533 }
3534 //LOGF_DEBUG("flushIO: error_type = %i", error_type);
3535 }
3536 while (error_type > 0);
3537 return 0;
3538}
3539
3540int LX200_OnStep::getCommandDoubleResponse(int fd, double *value, char *data, const char *cmd)
3541{
3542 char *term;
3543 int error_type;
3544 int nbytes_write = 0, nbytes_read = 0;
3545
3546 DEBUGF(DBG_SCOPE, "CMD <%s>", cmd);
3547
3548 flushIO(fd);
3549 /* Add mutex */
3550 std::unique_lock<std::mutex> guard(lx200CommsLock);
3551 tcflush(fd, TCIFLUSH);
3552
3553 if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
3554 return error_type;
3555
3556 error_type = tty_nread_section_expanded(fd, data, RB_MAX_LEN, '#', OSTimeoutSeconds, OSTimeoutMicroSeconds, &nbytes_read);
3557 tcflush(fd, TCIFLUSH);
3558
3559 term = strchr(data, '#');
3560 if (term)
3561 *term = '\0';
3562 if (nbytes_read < RB_MAX_LEN) //If within buffer, terminate string with \0 (in case it didn't find the #)
3563 {
3564 data[nbytes_read] = '\0'; //Indexed at 0, so this is the byte passed it
3565 }
3566 else
3567 {
3568 LOG_DEBUG("got RB_MAX_LEN bytes back, last byte set to null and possible overflow");
3569 data[RB_MAX_LEN - 1] = '\0';
3570 }
3571
3572 DEBUGF(DBG_SCOPE, "RES <%s>", data);
3573
3574 if (error_type != TTY_OK)
3575 {
3576 LOGF_DEBUG("Error %d", error_type);
3577 LOG_DEBUG("Flushing connection");
3578 tcflush(fd, TCIOFLUSH);
3579 return error_type;
3580 }
3581
3582 if (sscanf(data, "%lf", value) != 1)
3583 {
3584 LOG_WARN("Invalid response, check connection");
3585 LOG_DEBUG("Flushing connection");
3586 tcflush(fd, TCIOFLUSH);
3587 return RES_ERR_FORMAT; //-1001, so as not to conflict with TTY_RESPONSE;
3588 }
3589
3590 return nbytes_read;
3591
3592}
3593
3594
3595int LX200_OnStep::getCommandIntResponse(int fd, int *value, char *data, const char *cmd)
3596{
3597 char *term;
3598 int error_type;
3599 int nbytes_write = 0, nbytes_read = 0;
3600
3601 DEBUGF(DBG_SCOPE, "CMD <%s>", cmd);
3602
3603 flushIO(fd);
3604 /* Add mutex */
3605 std::unique_lock<std::mutex> guard(lx200CommsLock);
3606 tcflush(fd, TCIFLUSH);
3607
3608 if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
3609 return error_type;
3610
3611 error_type = tty_nread_section_expanded(fd, data, RB_MAX_LEN, '#', OSTimeoutSeconds, OSTimeoutMicroSeconds, &nbytes_read);
3612 tcflush(fd, TCIFLUSH);
3613
3614 term = strchr(data, '#');
3615 if (term)
3616 *term = '\0';
3617 if (nbytes_read < RB_MAX_LEN) //If within buffer, terminate string with \0 (in case it didn't find the #)
3618 {
3619 data[nbytes_read] = '\0'; //Indexed at 0, so this is the byte passed it
3620 }
3621 else
3622 {
3623 LOG_DEBUG("got RB_MAX_LEN bytes back, last byte set to null and possible overflow");
3624 data[RB_MAX_LEN - 1] = '\0';
3625 }
3626 DEBUGF(DBG_SCOPE, "RES <%s>", data);
3627 if (error_type != TTY_OK)
3628 {
3629 LOGF_DEBUG("Error %d", error_type);
3630 LOG_DEBUG("Flushing connection");
3631 tcflush(fd, TCIOFLUSH);
3632 return error_type;
3633 }
3634 if (sscanf(data, "%i", value) != 1)
3635 {
3636 LOG_WARN("Invalid response, check connection");
3637 LOG_DEBUG("Flushing connection");
3638 tcflush(fd, TCIOFLUSH);
3639 return RES_ERR_FORMAT; //-1001, so as not to conflict with TTY_RESPONSE;
3640 }
3641 return nbytes_read;
3642}
3643
3645{
3646 char *term;
3647 int error_type;
3648 int nbytes_write = 0, nbytes_read = 0;
3649
3650 DEBUGF(DBG_SCOPE, "CMD <%s>", cmd);
3651
3652 flushIO(fd);
3653 /* Add mutex */
3654 std::unique_lock<std::mutex> guard(lx200CommsLock);
3655 tcflush(fd, TCIFLUSH);
3656
3657 if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
3658 return error_type;
3659
3660 error_type = tty_nread_section_expanded(fd, data, RB_MAX_LEN, '#', OSTimeoutSeconds, OSTimeoutMicroSeconds, &nbytes_read);
3661 tcflush(fd, TCIFLUSH);
3662
3663 term = strchr(data, '#');
3664 if (term)
3665 *term = '\0';
3666 if (nbytes_read < RB_MAX_LEN) //If within buffer, terminate string with \0 (in case it didn't find the #)
3667 {
3668 data[nbytes_read] = '\0'; //Indexed at 0, so this is the byte passed it
3669 }
3670 else
3671 {
3672 LOG_DEBUG("got RB_MAX_LEN bytes back, last byte set to null and possible overflow");
3673 data[RB_MAX_LEN - 1] = '\0';
3674 }
3675
3676 DEBUGF(DBG_SCOPE, "RES <%s>", data);
3677
3678 if (error_type != TTY_OK)
3679 {
3680 LOGF_DEBUG("Error %d", error_type);
3681 return error_type;
3682 }
3683 return nbytes_read;
3684
3685}
3686
3687
3688
3689bool LX200_OnStep::updateLocation(double latitude, double longitude, double elevation)
3690{
3691 INDI_UNUSED(elevation);
3692
3693 if (isSimulation())
3694 return true;
3695
3696 double onstep_long = 360 - longitude ;
3697 while (onstep_long < 0)
3698 onstep_long += 360;
3699 while (onstep_long > 360)
3700 onstep_long -= 360;
3701
3702 if (!isSimulation() && setSiteLongitude(PortFD, onstep_long) < 0)
3703 {
3704 LOG_ERROR("Error setting site longitude coordinates");
3705 return false;
3706 }
3707
3708 if (!isSimulation() && setSiteLatitude(PortFD, latitude) < 0)
3709 {
3710 LOG_ERROR("Error setting site latitude coordinates");
3711 return false;
3712 }
3713
3714 char l[32] = {0}, L[32] = {0};
3715 fs_sexa(l, latitude, 3, 360000);
3716 fs_sexa(L, longitude, 4, 360000);
3717
3718 LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
3719
3720 return true;
3721}
3722
3723int LX200_OnStep::setMinElevationLimit(int fd, int max) // According to standard command is :SoDD*# Tested
3724{
3725 LOGF_INFO("<%s>", __FUNCTION__);
3726
3727 char read_buffer[RB_MAX_LEN] = {0};
3728
3729 snprintf(read_buffer, sizeof(read_buffer), ":So%02d#", max);
3730
3731 return (setStandardProcedure(fd, read_buffer));
3732}
3733
3735{
3736 int d, m;
3737 double s;
3738 char read_buffer[32];
3739
3740 getSexComponentsIID(Long, &d, &m, &s);
3741 if (OSHighPrecision)
3742 {
3743 snprintf(read_buffer, sizeof(read_buffer), ":Sg%.03d:%02d:%.02f#", d, m, s);
3744 int result1 = setStandardProcedure(fd, read_buffer);
3745 if (result1 == 0)
3746 {
3747 return 0;
3748 }
3749 else
3750 {
3751 snprintf(read_buffer, sizeof(read_buffer), ":Sg%03d:%02d#", d, m);
3752 return (setStandardProcedure(fd, read_buffer));
3753 }
3754 }
3755 snprintf(read_buffer, sizeof(read_buffer), ":Sg%03d:%02d#", d, m);
3756 return (setStandardProcedure(fd, read_buffer));
3757}
3758
3760{
3761 int d, m;
3762 double s;
3763 char read_buffer[32];
3764
3765 getSexComponentsIID(Long, &d, &m, &s);
3766
3767 if(OSHighPrecision)
3768 {
3769 snprintf(read_buffer, sizeof(read_buffer), ":St%+.02d:%02d:%.02f#", d, m, s);
3770 int result1 = setStandardProcedure(fd, read_buffer);
3771 if (result1 == 0)
3772 {
3773 return 0;
3774 }
3775 else
3776 {
3777 snprintf(read_buffer, sizeof(read_buffer), ":St%+03d:%02d#", d, m);
3778 return (setStandardProcedure(fd, read_buffer));
3779 }
3780 }
3781 snprintf(read_buffer, sizeof(read_buffer), ":St%+03d:%02d#", d, m);
3782 return (setStandardProcedure(fd, read_buffer));
3783}
3784
3785
3786
3787
3788/***** FOCUSER INTERFACE ******
3789
3790NOT USED:
3791virtual bool SetFocuserSpeed (int speed)
3792SetFocuserSpeed Set Focuser speed. More...
3793
3794USED:
3795virtual IPState MoveFocuser (FocusDirection dir, int speed, uint16_t duration)
3796MoveFocuser the focuser in a particular direction with a specific speed for a finite duration. More...
3797
3798USED:
3799virtual IPState MoveAbsFocuser (uint32_t targetTicks)
3800MoveFocuser the focuser to an absolute position. More...
3801
3802USED:
3803virtual IPState MoveRelFocuser (FocusDirection dir, uint32_t ticks)
3804MoveFocuser the focuser to an relative position. More...
3805
3806USED:
3807virtual bool AbortFocuser ()
3808AbortFocuser all focus motion. More...
3809
3810*/
3811
3812
3813IPState LX200_OnStep::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
3814{
3815 INDI_UNUSED(speed);
3816 // :FRsnnn# Set focuser target position relative (in microns)
3817 // Returns: Nothing
3818 double output;
3819 char read_buffer[32];
3820 output = duration;
3821 if (dir == FOCUS_INWARD) output = 0 - output;
3822 snprintf(read_buffer, sizeof(read_buffer), ":FR%5f#", output);
3823 sendOnStepCommandBlind(read_buffer);
3824 return IPS_BUSY; // Normal case, should be set to normal by update.
3825}
3826
3828{
3829 // :FSsnnn# Set focuser target position (in microns)
3830 // Returns: Nothing
3831 if (FocusAbsPosNP[0].getMax() >= int(targetTicks) && FocusAbsPosNP[0].getMin() <= int(targetTicks))
3832 {
3833 char read_buffer[32];
3834 snprintf(read_buffer, sizeof(read_buffer), ":FS%06d#", int(targetTicks));
3835 sendOnStepCommandBlind(read_buffer);
3836 return IPS_BUSY; // Normal case, should be set to normal by update.
3837 }
3838 else
3839 {
3840 LOG_INFO("Unable to move focuser, out of range");
3841 return IPS_ALERT;
3842 }
3843}
3844
3846{
3847 // :FRsnnn# Set focuser target position relative (in microns)
3848 // Returns: Nothing
3849 int output;
3850 char read_buffer[32];
3851 output = ticks;
3852 if (dir == FOCUS_INWARD) output = 0 - ticks;
3853 snprintf(read_buffer, sizeof(read_buffer), ":FR%04d#", output);
3854 sendOnStepCommandBlind(read_buffer);
3855 return IPS_BUSY; // Normal case, should be set to normal by update.
3856}
3857
3859{
3860 // :FQ# Stop the focuser
3861 // Returns: Nothing
3862 char cmd[CMD_MAX_LEN] = {0};
3863 strncpy(cmd, ":FQ#", sizeof(cmd));
3865}
3866
3868{
3869
3870 // double current = 0;
3871 // int temp_value;
3872 // int i;
3873 if (OSFocuser1)
3874 {
3875 // Alternate option:
3876 //if (!sendOnStepCommand(":FA#")) {
3877 char value[RB_MAX_LEN] = {0};
3878 int value_int;
3879 int error_or_fail = getCommandIntResponse(PortFD, &value_int, value, ":FG#");
3880 if (error_or_fail > 1)
3881 {
3882 FocusAbsPosNP[0].setValue( value_int);
3883 // double current = FocusAbsPosNP[0].getValue();
3885 LOGF_DEBUG("Current focuser: %d, %f", value_int, FocusAbsPosNP[0].getValue());
3886 }
3887
3888 // :FT# get status
3889 // Returns: M# (for moving) or S# (for stopped)
3890 char valueStatus[RB_MAX_LEN] = {0};
3891 error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, valueStatus, ":FT#");
3892 if (error_or_fail > 0 )
3893 {
3894 if (valueStatus[0] == 'S')
3895 {
3900 }
3901 else if (valueStatus[0] == 'M')
3902 {
3907 }
3908 else
3909 {
3910 LOG_WARN("Communication :FT# error, check connection.");
3911 //INVALID REPLY
3916 }
3917 }
3918 else
3919 {
3920 //INVALID REPLY
3921 LOG_WARN("Communication :FT# error, check connection.");
3926 }
3927
3928 // :FM# Get max position (in microns)
3929 // Returns: n#
3930 char focus_max[RB_MAX_LEN] = {0};
3931 int focus_max_int;
3932 int fm_error = getCommandIntResponse(PortFD, &focus_max_int, focus_max, ":FM#");
3933 if (fm_error > 0)
3934 {
3935 FocusAbsPosNP[0].setMax(focus_max_int);
3938 LOGF_DEBUG("focus_max: %s, %i, fm_nbchar: %i", focus_max, focus_max_int, fm_error);
3939 }
3940 else
3941 {
3942 LOG_WARN("Communication :FM# error, check connection.");
3943 LOGF_WARN("focus_max: %s, %u, fm_error: %i", focus_max, focus_max[0], fm_error);
3944 flushIO(PortFD); //Unlikely to do anything, but just in case.
3945 }
3946
3947 // :FI# Get full in position (in microns)
3948 // Returns: n#
3949 char focus_min[RB_MAX_LEN] = {0};
3950 int focus_min_int ;
3951 int fi_error = getCommandIntResponse(PortFD, &focus_min_int, focus_min, ":FI#");
3952 if (fi_error > 0)
3953 {
3954 FocusAbsPosNP[0].setMin( focus_min_int);
3957 LOGF_DEBUG("focus_min: %s, %i fi_nbchar: %i", focus_min, focus_min_int, fi_error);
3958 }
3959 else
3960 {
3961 LOG_WARN("Communication :FI# error, check connection.");
3962 flushIO(PortFD); //Unlikely to do anything, but just in case.
3963 }
3964
3965 // :Ft# Get Focuser Temperature
3966 // Returns: n#
3967 char focus_T[RB_MAX_LEN] = {0};
3968 double focus_T_double ;
3969 int ft_error = getCommandDoubleResponse(PortFD, &focus_T_double, focus_T, ":Ft#");
3970 if (ft_error > 0)
3971 {
3972 FocusTemperatureNP[0].setValue(atof(focus_T));
3974 LOGF_DEBUG("focus T°: %s, focus_T_double %i ft_nbcar: %i", focus_T, focus_T_double, ft_error);
3975 }
3976 else
3977 {
3978 LOG_WARN("Communication :Ft# error, check connection.");
3979 LOGF_DEBUG("focus T°: %s, focus_T_double %i ft_nbcar: %i", focus_T, focus_T_double, ft_error);
3980 flushIO(PortFD); //Unlikely to do anything, but just in case.
3981 }
3982
3983 // :Fe# Get Focus Differential T°
3984 // Returns: n#
3985 char focus_TD[RB_MAX_LEN] = {0};
3986 int focus_TD_int ;
3987 int fe_error = getCommandIntResponse(PortFD, &focus_TD_int, focus_TD, ":Fe#");
3988 if (fe_error > 0)
3989 {
3990 FocusTemperatureNP[1].setValue(atof(focus_TD));
3992 LOGF_DEBUG("focus Differential T°: %s, %i fi_nbchar: %i", focus_TD, focus_TD_int, fe_error);
3993 }
3994 else
3995 {
3996 LOG_WARN("Communication :Fe# error, check connection.");
3997 flushIO(PortFD); //Unlikely to do anything, but just in case.
3998 }
3999
4000 // :FC# Get focuser temperature compensation coefficient in microns per °C)
4001 // Return: n.n#
4002 char focus_Coeficient[RB_MAX_LEN] = {0};
4003 int focus_Coefficient_int ;
4004 int fC_error = getCommandIntResponse(PortFD, &focus_Coefficient_int, focus_Coeficient, ":FC#");
4005 if (fC_error > 0)
4006 {
4007 TFCCoefficientNP[0].setValue(atof(focus_Coeficient));
4009 LOGF_DEBUG("TFC Coefficient: %s, %i fC_nbchar: %i", focus_Coeficient, focus_Coefficient_int, fC_error);
4010 }
4011 else
4012 {
4013 LOG_WARN("Communication :FC# error, check connection.");
4014 flushIO(PortFD); //Unlikely to do anything, but just in case.
4015 }
4016
4017 // :FD# Get focuser temperature compensation deadband amount (in steps or microns)
4018 // Return: n#
4019 char focus_Deadband[RB_MAX_LEN] = {0};
4020 int focus_Deadband_int ;
4021 int fD_error = getCommandIntResponse(PortFD, &focus_Deadband_int, focus_Deadband, ":FD#");
4022 if (fD_error > 0)
4023 {
4024 TFCDeadbandNP[0].setValue(focus_Deadband_int);
4026 LOGF_DEBUG("TFC Deadband: %s, %i fD_nbchar: %i", focus_Deadband, focus_Deadband_int, fD_error);
4027 }
4028 else
4029 {
4030 LOG_WARN("Communication :FD# error, check connection.");
4031 flushIO(PortFD); //Unlikely to do anything, but just in case.
4032 }
4033
4034 // :Fc# Get focuser temperature compensation status
4035 // Return: n.n#
4036 char response[RB_MAX_LEN];
4037 int res = getCommandSingleCharResponse(PortFD, response, ":Fc#");
4038 if (res > 0)
4039 {
4040 if (strcmp(response, "0"))
4041 {
4045 }
4046 else if (strcmp(response, "1"))
4047 {
4051 }
4053 LOGF_DEBUG("TFC Enable: fc_nbchar:%d Fc_response: %s", res, response);
4054 }
4055 else
4056 {
4057 //LOGF_DEBUG("TFC Enable1: fc_error:%i Fc_response: %s", res, response);
4058 LOG_WARN("Communication :Fc# error, check connection.");
4059 flushIO(PortFD); //Unlikely to do anything, but just in case.
4060 }
4061
4063 LOGF_DEBUG("After update properties: FocusAbsPosN min: %f max: %f", FocusAbsPosNP[0].getMin(), FocusAbsPosNP[0].getMax());
4064 }
4065
4066 if(OSFocuser2)
4067 {
4068 char value[RB_MAX_LEN] = {0};
4069 int error_return;
4070 //TODO: Check to see if getCommandIntResponse would be better
4071 error_return = getCommandSingleCharErrorOrLongResponse(PortFD, value, ":fG#");
4072 if (error_return >= 0)
4073 {
4074 if ( strcmp(value, "0") )
4075 {
4076 LOG_INFO("Focuser 2 called, but not present, disabling polling");
4077 LOGF_DEBUG("OSFocuser2: %d, OSNumFocusers: %i", OSFocuser2, OSNumFocusers);
4078 OSFocuser2 = false;
4079 }
4080 else
4081 {
4082 OSFocus2TargNP[0].setValue(atoi(value));
4084 }
4085 }
4086 else
4087 {
4088 LOGF_INFO("Focuser 2 called, but returned error %i on read, disabling further polling", error_return);
4089 LOGF_DEBUG("OSFocuser2: %d, OSNumFocusers: %i", OSFocuser2, OSNumFocusers);
4090 OSFocuser2 = false;
4091 }
4092 }
4093
4094 if(OSNumFocusers > 1)
4095 {
4096 char value[RB_MAX_LEN] = {0};
4097 int error_or_fail = getCommandSingleCharResponse(PortFD, value, ":Fa#"); //0 = failure, 1 = success, no # on reply
4098 if (error_or_fail > 0 && value[0] > '0' && value[0] < '9')
4099 {
4100 int temp_value = (unsigned int)(value[0]) - '0';
4101 LOGF_DEBUG(":Fa# return: %d", temp_value);
4103 if (temp_value == 0)
4104 {
4106 }
4107 else if (temp_value > 9 || temp_value < 0) //TODO: Check if completely redundant
4108 {
4109 //To solve issue mentioned https://www.indilib.org/forum/development/1406-driver-onstep-lx200-like-for-indi.html?start=624#71572
4111 LOGF_WARN("Active focuser returned out of range: %d, should be 0-9", temp_value);
4113 return 1;
4114 }
4115 else
4116 {
4117 OSFocusSelectSP[temp_value - 1].setState(ISS_ON);
4118 }
4121 }
4122 else
4123 {
4124 LOGF_DEBUG(":Fa# returned outside values: %c, %u", value[0], value[0]);
4125 }
4126 }
4127 return 0;
4128}
4129
4130
4131//Rotator stuff
4132// IPState MoveRotator(double angle) override;
4133// bool SyncRotator(double angle) override;
4134// IPState HomeRotator() override;
4135// bool ReverseRotator(bool enabled) override;
4136// bool AbortRotator() override;
4137// bool SetRotatorBacklash (int32_t steps) override;
4138// bool SetRotatorBacklashEnabled(bool enabled) override;
4139
4140//OnStep Rotator Commands (For reference, and from 5 1 v 4)
4141// :r+# Enable derotator
4142// Returns: Nothing
4143// :r-# Disable derotator
4144// Returns: Nothing
4145// :rP# Move rotator to the parallactic angle
4146// Returns: Nothing
4147// :rR# Reverse derotator direction
4148// Returns: Nothing
4149// :rT# Get status
4150// Returns: M# (for moving) or S# (for stopped)
4151// :rI# Get mIn position (in degrees)
4152// Returns: n#
4153// :rM# Get Max position (in degrees)
4154// Returns: n#
4155// :rD# Get rotator degrees per step
4156// Returns: n.n#
4157// :rb# Get rotator backlash amount in steps
4158// Return: n#
4159// :rb[n]#
4160// Set rotator backlash amount in steps
4161// Returns: 0 on failure
4162// 1 on success
4163// :rF# Reset rotator at the home position
4164// Returns: Nothing
4165// :rC# Moves rotator to the home position
4166// Returns: Nothing
4167// :rG# Get rotator current position in degrees
4168// Returns: sDDD*MM#
4169// :rc# Set continuous move mode (for next move command)
4170// Returns: Nothing
4171// :r># Move clockwise as set by :rn# command, default = 1 deg (or 0.1 deg/s in continuous mode)
4172// Returns: Nothing
4173// :r<# Move counter clockwise as set by :rn# command
4174// Returns: Nothing
4175// :rQ# Stops movement (except derotator)
4176// Returns: Nothing
4177// :r[n]# Move increment where n = 1 for 1 degrees, 2 for 2 degrees, 3 for 5 degrees, 4 for 10 degrees
4178// Move rate where n = 1 for .01 deg/s, 2 for 0.1 deg/s, 3 for 1.0 deg/s, 4 for 5.0 deg/s
4179// Returns: Nothing
4180// :rS[sDDD*MM'SS]#
4181// Set position in degrees
4182// Returns: 0 on failure
4183// 1 on success
4184
4186{
4187 char value[RB_MAX_LEN];
4188 double double_value;
4189 if(OSRotator1)
4190 {
4191 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, value, ":rG#");
4192 if (error_or_fail == 1 && value[0] == '0') //1 char return, response 0 = no Rotator
4193 {
4194 LOG_INFO("Detected Response that Rotator is not present, disabling further checks");
4195 OSRotator1 = false;
4196 return 0; //Return 0, as this is not a communication error
4197 }
4198 if (error_or_fail < 1) //This does not necessarily mean
4199 {
4200 LOG_WARN("Error talking to rotator, might be timeout (especially on network)");
4201 return -1;
4202 }
4203 if (f_scansexa(value, &double_value))
4204 {
4205 // 0 = good, thus this is the bad
4208 return -1;
4209 }
4210 GotoRotatorNP[0].setValue(double_value);
4211 double min_rotator, max_rotator;
4212 //NOTE: The following commands are only on V4, V5 & OnStepX, not V3
4213 //TODO: Psudo-state for V3 Rotator?
4214 bool changed_minmax = false;
4216 {
4217 memset(value, 0, RB_MAX_LEN);
4218 error_or_fail = getCommandDoubleResponse(PortFD, &min_rotator, value, ":rI#");
4219 if (error_or_fail > 1)
4220 {
4221 changed_minmax = true;
4222 GotoRotatorNP[0].setMin(min_rotator);
4223 }
4224 memset(value, 0, RB_MAX_LEN);
4225 error_or_fail = getCommandDoubleResponse(PortFD, &max_rotator, value, ":rM#");
4226 if (error_or_fail > 1)
4227 {
4228 changed_minmax = true;
4229 GotoRotatorNP[0].setMax(max_rotator);
4230 }
4231 if (changed_minmax)
4232 {
4235 }
4236 //GotoRotatorN
4237 memset(value, 0, RB_MAX_LEN);
4238 error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, value, ":rT#");
4239 if (error_or_fail > 1)
4240 {
4241 if (value[0] == 'S') /*Stopped normal on EQ mounts */
4242 {
4245
4246 }
4247 else if (value[0] == 'M') /* Moving, including de-rotation */
4248 {
4251 }
4252 else
4253 {
4254 //INVALID REPLY
4257 }
4258 }
4259 memset(value, 0, RB_MAX_LEN);
4260 int backlash_value;
4261 error_or_fail = getCommandIntResponse(PortFD, &backlash_value, value, ":rb#");
4262 if (error_or_fail > 1)
4263 {
4264 RotatorBacklashNP[0].setValue(backlash_value);
4267 }
4268 }
4269 }
4270 return 0;
4271}
4272
4274{
4275 char cmd[CMD_MAX_LEN] = {0};
4276 int d, m, s;
4277 getSexComponents(angle, &d, &m, &s);
4278
4279 snprintf(cmd, sizeof(cmd), ":rS%.03d:%02d:%02d#", d, m, s);
4280 LOGF_INFO("Move Rotator: %s", cmd);
4281
4282
4284 {
4285 return IPS_BUSY;
4286 }
4287 else
4288 {
4289 return IPS_ALERT;
4290 }
4291
4292
4293 return IPS_BUSY;
4294}
4295/*
4296bool LX200_OnStep::SyncRotator(double angle) {
4297
4298}*/
4300{
4301 //Not entirely sure if this means attempt to use limit switches and home, or goto home
4302 //Assuming MOVE to Home
4303 LOG_INFO("Moving Rotator to Home");
4304 sendOnStepCommandBlind(":rC#");
4305 return IPS_BUSY;
4306}
4307// bool LX200_OnStep::ReverseRotator(bool enabled) {
4308// sendOnStepCommandBlind(":rR#");
4309// return true;
4310// } //No way to check which way it's going as Indi expects
4311
4313{
4314 LOG_INFO("Aborting Rotation, de-rotation in same state");
4315 sendOnStepCommandBlind(":rQ#"); //Does NOT abort de-rotator
4316 return true;
4317}
4318
4320{
4321 char cmd[CMD_MAX_LEN] = {0};
4322 // char response[RB_MAX_LEN];
4323 snprintf(cmd, sizeof(cmd), ":rb%d#", steps);
4325 {
4326 return true;
4327 }
4328 return false;
4329}
4330
4332{
4333 //Nothing required here.
4334 INDI_UNUSED(enabled);
4335 return true;
4336 // As it's always enabled, which would mean setting it like SetRotatorBacklash to 0, and losing any saved values. So for now, leave it as is (always enabled)
4337}
4338
4339// bool SyncRotator(double angle) override;
4340// IPState HomeRotator(double angle) override;
4341// bool ReverseRotator(bool enabled) override;
4342// bool AbortRotator() override;
4343// bool SetRotatorBacklash (int32_t steps) override;
4344// bool SetRotatorBacklashEnabled(bool enabled) override;
4345
4346// Now, derotation is NOT explicitly handled.
4347
4348
4349
4350//End Rotator stuff
4351
4352
4353
4354
4355//PEC Support
4356//Should probably be added to inditelescope or another interface, because the PEC that's there... is very limited.
4357
4359{
4360 // :$QZ+ Enable RA PEC compensation
4361 // Returns: nothing
4362 INDI_UNUSED(axis); //We only have RA on OnStep
4364 {
4365 if (OSPECEnabled == true)
4366 {
4367 char cmd[CMD_MAX_LEN] = {0};
4368 LOG_INFO("Sending Command to Start PEC Playback");
4369 strncpy(cmd, ":$QZ+#", sizeof(cmd));
4371 return IPS_BUSY;
4372 }
4373 else
4374 {
4375 LOG_DEBUG("Command to Playback PEC called when Controller does not support PEC");
4376 }
4377 return IPS_ALERT;
4378 }
4379 else
4380 {
4381 OSPECEnabled = false;
4382 LOG_INFO("Command to Start Playback PEC called when Controller does not support PEC due to being Alt-Az, PEC Ignored going forward");
4383 return IPS_ALERT;
4384 }
4385
4386}
4387
4389{
4390 // :$QZ- Disable RA PEC Compensation
4391 // Returns: nothing
4392 INDI_UNUSED(axis); //We only have RA on OnStep
4393 if (OSPECEnabled == true)
4394 {
4395 char cmd[CMD_MAX_LEN] = {0};
4396 LOG_INFO("Sending Command to Stop PEC Playback");
4397 strncpy(cmd, ":$QZ-#", sizeof(cmd));
4399 return IPS_BUSY;
4400 }
4401 else
4402 {
4403 LOG_DEBUG("Command to Stop Playing PEC called when Controller does not support PEC");
4404 }
4405 return IPS_ALERT;
4406}
4407
4409{
4410 // :$QZ/ Ready Record PEC
4411 // Returns: nothing
4412 INDI_UNUSED(axis); //We only have RA on OnStep
4413 if (OSPECEnabled == true)
4414 {
4415 char cmd[CMD_MAX_LEN] = {0};
4416 LOG_INFO("Sending Command to Start PEC record");
4417 strncpy(cmd, ":$QZ/#", CMD_MAX_LEN);
4419 return IPS_BUSY;
4420 }
4421 else
4422 {
4423 LOG_DEBUG("Command to Record PEC called when Controller does not support PEC");
4424 }
4425 return IPS_ALERT;
4426}
4427
4429{
4430 // :$QZZ Clear the PEC data buffer
4431 // Return: Nothing
4432 INDI_UNUSED(axis); //We only have RA on OnStep
4433 if (OSPECEnabled == true)
4434 {
4435 char cmd[CMD_MAX_LEN] = {0};
4436 LOG_INFO("Sending Command to Clear PEC record");
4437 strncpy(cmd, ":$QZZ#", CMD_MAX_LEN);
4439 return IPS_BUSY;
4440 }
4441 else
4442 {
4443 LOG_DEBUG("Command to clear PEC called when Controller does not support PEC");
4444 }
4445 return IPS_ALERT;
4446
4447}
4448
4450{
4451 // :$QZ! Write PEC data to EEPROM
4452 // Returns: nothing
4453 INDI_UNUSED(axis); //We only have RA on OnStep
4454 if (OSPECEnabled == true)
4455 {
4456 char cmd[CMD_MAX_LEN] = {0};
4457 LOG_INFO("Sending Command to Save PEC to EEPROM");
4458 strncpy(cmd, ":$QZ!#", CMD_MAX_LEN);
4460 return IPS_BUSY;
4461 }
4462 else
4463 {
4464 LOG_DEBUG("Command to save PEC called when Controller does not support PEC");
4465 }
4466 return IPS_ALERT;
4467}
4468
4469
4471{
4472 // if (!OSPECviaGU) {
4473 INDI_UNUSED(axis); //We only have RA on OnStep
4474 if (OSPECEnabled == true && OSPECviaGU == false) //All current versions report via #GU
4475 {
4477 {
4478 OSPECEnabled = false;
4479 LOG_INFO("Command to give PEC called when Controller does not support PEC due to being Alt-Az Disabled");
4480 return IPS_ALERT;
4481 }
4482 //LOG_INFO("Getting PEC Status");
4483 // :$QZ? Get PEC status
4484 // Returns: S#
4485 // Returns status (pecSense) In the form: Status is one of "IpPrR" (I)gnore, get ready to (p)lay, (P)laying, get ready to (r)ecord, (R)ecording. Or an optional (.) to indicate an index detect.
4486 // IUFillSwitch(&OSPECStatusS[0], "OFF", "OFF", ISS_ON);
4487 // IUFillSwitch(&OSPECStatusS[1], "Playing", "Playing", ISS_OFF);
4488 // IUFillSwitch(&OSPECStatusS[2], "Recording", "Recording", ISS_OFF);
4489 // IUFillSwitch(&OSPECStatusS[3], "Will Play", "Will Play", ISS_OFF);
4490 // IUFillSwitch(&OSPECStatusS[4], "Will Record", "Will Record", ISS_OFF);
4491 char value[RB_MAX_LEN] = {0};
4493 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, value, ":$QZ?#");
4494 if (error_or_fail > 1)
4495 {
4497 if (value[0] == 'I') //Ignore
4498 {
4502 // OSPECEnabled = false;
4503 LOG_INFO("Controller reports PEC Ignored and not supported");
4504 LOG_INFO("No Further PEC Commands will be processed, unless status changed");
4505 }
4506 else if (value[0] == 'R') //Active Recording
4507 {
4511 }
4512 else if (value[0] == 'r') //Waiting for index before recording
4513 {
4517 }
4518 else if (value[0] == 'P') //Active Playing
4519 {
4523 }
4524 else if (value[0] == 'p') //Waiting for index before playing
4525 {
4529 }
4530 else //INVALID REPLY
4531 {
4534 }
4535 if (value[1] == '.')
4536 {
4540 }
4541 else
4542 {
4545 }
4549 return IPS_OK;
4550 }
4551 else
4552 {
4553 LOG_DEBUG("Timeout or other error on :$QZ?#");
4554 }
4555 }
4556 else
4557 {
4558 // LOG_DEBUG("PEC status called when Controller does not support PEC");
4559 }
4560 return IPS_ALERT;
4561}
4562
4563
4565{
4566 INDI_UNUSED(axis); //We only have RA on OnStep
4567 if (OSPECEnabled == true)
4568 {
4569 LOG_WARN("PEC Reading NOT Implemented");
4570 return IPS_OK;
4571 }
4572 else
4573 {
4574 LOG_DEBUG("Command to Read PEC called when Controller does not support PEC");
4575 }
4576 return IPS_ALERT;
4577}
4578
4579
4581{
4582 INDI_UNUSED(axis); //We only have RA on OnStep
4583 if (OSPECEnabled == true)
4584 {
4585 LOG_WARN("PEC Writing NOT Implemented");
4586 return IPS_OK;
4587 }
4588 else
4589 {
4590 LOG_DEBUG("Command to Read PEC called when Controller does not support PEC");
4591 }
4592 return IPS_ALERT;
4593}
4594
4595// New, Multistar alignment goes here:
4596
4598{
4599 //See here https://groups.io/g/onstep/message/3624
4600 char cmd[CMD_MAX_LEN] = {0};
4601
4602 LOG_INFO("Sending Command to Start Alignment");
4603 OSNAlignTP[0].setText("Align STARTED");
4604 OSNAlignTP[1].setText("GOTO a star, center it");
4605 OSNAlignTP[2].setText("GOTO a star, Solve and Sync");
4606 OSNAlignTP[3].setText("Press 'Issue Align' if not solving");
4607 OSNAlignTP.apply("==>Align Started");
4608 // Check for max number of stars and gracefully fall back to max, if more are requested.
4609 char read_buffer[RB_MAX_LEN] = {0};
4610 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, read_buffer, ":A?#");
4611 if(error_or_fail != 4 || read_buffer[0] < '0' || read_buffer[0] > '9' || read_buffer[1] < '0' || read_buffer[1] > ':'
4612 || read_buffer[2] < '0' || read_buffer[2] > '9')
4613 {
4614 LOGF_INFO("Getting Alignment Status: response Error, response = %s>", read_buffer);
4615 return IPS_ALERT;
4616 }
4617 //Check max_stars
4618
4619 int max_stars = read_buffer[0] - '0';
4620 if (stars > max_stars)
4621 {
4622 LOG_INFO("Tried to start Align with too many stars.");
4623 LOGF_INFO("Starting Align with %d stars", max_stars);
4624 stars = max_stars;
4625 }
4626 snprintf(cmd, sizeof(cmd), ":A%.1d#", stars);
4627 LOGF_INFO("Started Align with %s, max possible stars: %d", cmd, max_stars);
4629 {
4630 LOG_INFO("Starting Align failed");
4631 return IPS_BUSY;
4632 }
4633 return IPS_ALERT;
4634}
4635
4636
4638{
4639 //Used if centering a star manually, most will use plate-solving
4640 //See here https://groups.io/g/onstep/message/3624
4641 char cmd[CMD_MAX_LEN] = {0};
4642 LOG_INFO("Sending Command to Record Star");
4643 strncpy(cmd, ":A+#", sizeof(cmd));
4645 {
4646 LOG_INFO("Adding Align failed");
4647 return IPS_BUSY;
4648 }
4649 return IPS_ALERT;
4650}
4651
4653{
4654 // :A?# Align status
4655 // Returns: mno#
4656 // where m is the maximum number of alignment stars
4657 // n is the current alignment star (0 otherwise) or ':' in case 9 stars selected
4658 // o is the last required alignment star when an alignment is in progress (0 otherwise)
4659
4660 char msg[40] = {0};
4661 char stars[5] = {0};
4662 int max_stars, current_star, align_stars;
4663
4664 char read_buffer[RB_MAX_LEN] = {0};
4665 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, read_buffer, ":A?#");
4666 if(error_or_fail != 4 || read_buffer[0] < '0' || read_buffer[0] > '9' || read_buffer[1] < '0' || read_buffer[1] > ':'
4667 || read_buffer[2] < '0' || read_buffer[2] > '9')
4668 {
4669 LOGF_INFO("Getting Alignment Status: response Error, response = %s>", read_buffer);
4670 return false;
4671 }
4672 max_stars = read_buffer[0] - '0';
4673 current_star = read_buffer[1] - '0';
4674 align_stars = read_buffer[2] - '0';
4675 snprintf(stars, sizeof(stars), "%d", max_stars);
4676 OSNAlignTP[5].setText(stars);
4677 snprintf(stars, sizeof(stars), "%d", current_star);
4678 if (read_buffer[1] > '9')
4679 {
4680 OSNAlignTP[6].setText(":");
4681 }
4682 else
4683 {
4684 OSNAlignTP[6].setText(stars);
4685 }
4686 snprintf(stars, sizeof(stars), "%d", align_stars);
4687 OSNAlignTP[7].setText(stars);
4688 LOGF_DEBUG("Align: max_stars: %i current star: %u, align_stars %u", max_stars, current_star, align_stars);
4689
4690 if (current_star <= align_stars)
4691 {
4692 snprintf(msg, sizeof(msg), "%s Alignment: Star %d/%d", read_buffer, current_star, align_stars );
4693 OSNAlignTP[4].setText(msg);
4694 }
4695 if (current_star > align_stars && max_stars > 1)
4696 {
4697 LOGF_DEBUG("Align: current star: %u, align_stars %u", int(current_star), int(align_stars));
4698 snprintf(msg, sizeof(msg), "Align: Completed");
4699 AlignDone();
4700 OSNAlignTP[4].setText(msg);
4702 }
4703 OSNAlignTP.apply();
4704 return true;
4705}
4706
4708{
4709 // :GX0n# Get OnStep value
4710 // Returns: value
4711
4712 // 00 ax1Cor
4713 // 01 ax2Cor
4714 // 02 altCor
4715 // 03 azmCor
4716 // 04 doCor
4717 // 05 pdCor
4718 // 06 ffCor
4719 // 07 dfCor
4720 // 08 tfCor
4721 // 09 Number of stars, reset to first star
4722 // 0A Star #n HA
4723 // 0B Star #n Dec
4724 // 0C Mount #n HA
4725 // 0D Mount #n Dec
4726 // 0E Mount PierSide (and increment n)
4727
4728
4729
4730 char read_buffer[RB_MAX_LEN] = {0};
4731 char polar_error[RB_MAX_LEN] = {0};
4732 char sexabuf[RB_MAX_LEN] = {0};
4733
4734 // LOG_INFO("Getting Align Error Status");
4735 int error_or_fail;
4736 double altCor, azmCor;
4737 error_or_fail = getCommandDoubleResponse(PortFD, &altCor, read_buffer, ":GX02#");
4738 if (error_or_fail < 2)
4739 {
4740 LOGF_INFO("Polar Align Error Status response Error, response = %s>", read_buffer);
4741 return false;
4742 }
4743 error_or_fail = getCommandDoubleResponse(PortFD, &azmCor, read_buffer, ":GX03#");
4744 if (error_or_fail < 2)
4745 {
4746 LOGF_INFO("Polar Align Error Status response Error, response = %s>", read_buffer);
4747 return false;
4748 }
4749 fs_sexa(sexabuf, (double)azmCor / 3600, 4, 3600);
4750 snprintf(polar_error, sizeof(polar_error), "%f'' /%s", azmCor, sexabuf);
4751 OSNAlignErrTP[1].setText(polar_error);
4752 fs_sexa(sexabuf, (double)altCor / 3600, 4, 3600);
4753 snprintf(polar_error, sizeof(polar_error), "%f'' /%s", altCor, sexabuf);
4754 OSNAlignErrTP[0].setText(polar_error);
4756
4757
4758 return true;
4759}
4760
4762{
4763 //See here https://groups.io/g/onstep/message/3624
4764 if (OSAlignCompleted == false)
4765 {
4766 OSAlignCompleted = true;
4767 LOG_INFO("Alignment Done - May still be calculating");
4768 OSNAlignTP[0].setText("Align FINISHED");
4769 OSNAlignTP[1].setText("------");
4770 OSNAlignTP[2].setText("Optionally press:");
4771 OSNAlignTP[3].setText("Write Align to NVRAM/Flash ");
4772 OSNAlignTP.apply();
4773 return IPS_OK;
4774 }
4775 return IPS_BUSY;
4776}
4777
4779{
4780 //See here https://groups.io/g/onstep/message/3624
4781 char cmd[CMD_MAX_LEN] = {0};
4782 char response[RB_MAX_LEN];
4783
4784 LOG_INFO("Sending Command to Finish Alignment and write");
4785 strncpy(cmd, ":AW#", sizeof(cmd));
4786 int res = getCommandSingleCharResponse(PortFD, response, cmd); //1 success , no # on reply
4787 if(res > 0 && response[0] == '1')
4788 {
4789 LOG_INFO("Align Write Successful");
4791 OSNAlignTP[0].setText("Align FINISHED");
4792 OSNAlignTP[1].setText("------");
4793 OSNAlignTP[2].setText("And Written to EEPROM");
4794 OSNAlignTP[3].setText("------");
4795 OSNAlignTP.apply();
4796 return IPS_OK;
4797 }
4798 else
4799 {
4800 LOGF_ERROR("Align Write Failed: error=%s", response);
4802 OSNAlignTP[0].setText("Align WRITE FAILED");
4803 OSNAlignTP.apply();
4804 return IPS_ALERT;
4805 }
4806}
4807
4808#ifdef ONSTEP_NOTDONE
4810{
4811 // :SXnn,VVVVVV...# Set OnStep value
4812 // Return: 0 on failure
4813 // 1 on success
4814 // if (parameter[0]=='G') { // Gn: General purpose output
4815 // :SXGn,value
4816 // value, 0 = low, other = high
4817 LOG_INFO("Not implemented yet");
4818 return IPS_OK;
4819}
4820#endif
4821
4823{
4824 LOG_INFO("Not implemented yet");
4825 OSGetOutputState(output);
4826 return IPS_OK;
4827}
4828
4829/*
4830Reference:
4831 // :GXnn# Get OnStep value
4832 // Returns: value
4833 // Error = 123456789
4834 //
4835 // Double unless noted: integer:i, special:* and values in {}
4836 //
4837 // 00 ax1Cor
4838 // 01 ax2Cor
4839 // 02 altCor //EQ Altitude Correction
4840 // 03 azmCor //EQ Azimuth Correction
4841 // 04 doCor
4842 // 05 pdCor
4843 // 06 ffCor
4844 // 07 dfCor
4845 // 08 tfCor
4846 // 09 Number of stars, reset to first star
4847 // 0A Star #n HA
4848 // 0B Star #n Dec
4849 // 0C Mount #n HA
4850 // 0D Mount #n Dec
4851 // 0E Mount PierSide (and increment n)
4852 // 80 UTC time
4853 // 81 UTC date
4854 // 90 pulse-guide rate
4855 // i 91 pec analog value
4856 // 92 MaxRate
4857 // 93 MaxRate (default) number
4858 // * 94 pierSide (N if never) {Same as :Gm# (E, W, None)}
4859 // i 95 autoMeridianFlip AutoFlip setting {0/1+}
4860 // * 96 preferred pier side {E, W, B}
4861 // 97 slew speed
4862 // * 98 rotator {D, R, N}
4863 // 9A temperature in deg. C
4864 // 9B pressure in mb
4865 // 9C relative humidity in %
4866 // 9D altitude in meters
4867 // 9E dew point in deg. C
4868 // 9F internal MCU temperature in deg. C
4869 // * Un: Get stepper driver statUs
4870 // En: Get settings
4871 // Fn: Debug
4872 // G0-GF (HEX!) = Onstep output status
4873
4874*/
4875
4877{
4878 // :GXnn# Get OnStep value
4879 // Returns: value
4880 // nn= G0-GF (HEX!) - Output status
4881 //
4882 char value[RB_MAX_LEN] = {0};
4883 char command[CMD_MAX_LEN] = {0};
4884 strncpy(command, ":$GXGm#", CMD_MAX_LEN);
4885 LOGF_INFO("Output: %s", char(output));
4886 LOGF_INFO("Command: %s", command);
4887 command[5] = char(output);
4888 LOGF_INFO("Command: %s", command);
4889
4890 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, value, command);
4891 if (error_or_fail > 0)
4892 {
4893 /*
4894 if (value[0] == 0)
4895 {
4896 OSOutput1S[0].s = ISS_ON;
4897 OSOutput1S[1].s = ISS_OFF;
4898 }
4899 else
4900 {
4901 OSOutput1S[0].s = ISS_OFF;
4902 OSOutput1S[1].s = ISS_ON;
4903 }
4904 IDSetSwitch(&OSOutput1SP, nullptr);
4905 */
4906 return true;
4907 }
4908 return false;
4909}
4910
4911bool LX200_OnStep::SetTrackRate(double raRate, double deRate)
4912{
4913 char read_buffer[RB_MAX_LEN];
4914 snprintf(read_buffer, sizeof(read_buffer), ":RA%04f#", raRate);
4915 LOGF_INFO("Setting: Custom RA Rate to %04f", raRate);
4916 if (!sendOnStepCommand(read_buffer))
4917 {
4918 return false;
4919 }
4920 snprintf(read_buffer, sizeof(read_buffer), ":RE%04f#", deRate);
4921 LOGF_INFO("Setting: Custom DE Rate to %04f", deRate);
4922 if (!sendOnStepCommand(read_buffer))
4923 {
4924 return false;
4925 }
4926 LOG_INFO("Custom RA and DE Rates successfully set");
4927 return true;
4928}
4929
4930void LX200_OnStep::slewError(int slewCode)
4931{
4932 // 0=Goto is possible
4933 // 1=below the horizon limit
4934 // 2=above overhead limit
4935 // 3=controller in standby
4936 // 4=mount is parked
4937 // 5=Goto in progress
4938 // 6=outside limits (MaxDec, MinDec, UnderPoleLimit, MeridianLimit)
4939 // 7=hardware fault
4940 // 8=already in motion
4941 // 9=unspecified error
4942 switch(slewCode)
4943 {
4944 case 0:
4945 LOG_ERROR("OnStep slew/syncError called with value 0-goto possible, this is normal operation");
4946 return;
4947 case 1:
4948 LOG_ERROR("OnStep slew/syncError: Below the horizon limit");
4949 break;
4950 case 2:
4951 LOG_ERROR("OnStep slew/syncError: Above Overhead limit");
4952 break;
4953 case 3:
4954 LOG_ERROR("OnStep slew/syncError: Controller in standby, Usual issue fix: Turn tracking on");
4955 break;
4956 case 4:
4957 LOG_ERROR("OnStep slew/syncError: Mount is Parked");
4958 break;
4959 case 5:
4960 LOG_ERROR("OnStep slew/syncError: Goto in progress");
4961 break;
4962 case 6:
4963 LOG_ERROR("OnStep slew/syncError: Outside limits: Max/Min Dec, Under Pole Limit, Meridian Limit, Sync attempted to wrong pier side");
4964 break;
4965 case 7:
4966 LOG_ERROR("OnStep slew/syncError: Hardware Fault");
4967 break;
4968 case 8:
4969 LOG_ERROR("OnStep slew/syncError: Already in motion");
4970 break;
4971 case 9:
4972 LOG_ERROR("OnStep slew/syncError: Unspecified Error");
4973 break;
4974 default:
4975 LOG_ERROR("OnStep slew/syncError: Not in range of values that should be returned! INVALID, Something went wrong!");
4976 }
4978 EqNP.apply();
4979}
4980
4981
4982//Override LX200 sync function, to allow for error returns
4983bool LX200_OnStep::Sync(double ra, double dec)
4984{
4985
4986 char read_buffer[RB_MAX_LEN] = {0};
4987 // int error_code;
4988
4989 if (!isSimulation())
4990 {
4991 if (setObjectRA(PortFD, ra) < 0 || (setObjectDEC(PortFD, dec)) < 0)
4992 {
4994 LOG_ERROR("Error setting RA/DEC. Unable to Sync.");
4995 EqNP.apply();
4996 return false;
4997 }
4998 LOG_DEBUG("CMD <:CM#>");
4999 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, read_buffer, ":CM#");
5000 LOGF_DEBUG("RES <%s>", read_buffer);
5001 if (error_or_fail > 1)
5002 {
5003 if (strcmp(read_buffer, "N/A"))
5004 {
5005 if (read_buffer[0] == 'E' && read_buffer[1] >= '0'
5006 && read_buffer[1] <= '9') //strcmp will be 0 if they match, so this is the case for failure.
5007 {
5008 int error_code = read_buffer[1] - '0';
5009 LOGF_DEBUG("Sync failed with response: %s, Error code: %i", read_buffer, error_code);
5010 slewError(error_code);
5012 LOG_ERROR("Synchronization failed.");
5013 EqNP.apply();
5014 return false;
5015 }
5016 else
5017 {
5018 LOG_ERROR("Unexpected return on sync call!");
5019 LOG_ERROR("Check system & Align if doing align to see if it went through!");
5020 return false;
5021 }
5022 }
5023 }
5024 else
5025 {
5026 LOG_ERROR("Communication error on sync! Re-issue sync!");
5027 return false;
5028 }
5029 }
5030
5031 currentRA = ra;
5032 currentDEC = dec;
5033
5036
5037 LOG_INFO("OnStep: Synchronization successful.");
5038 return true;
5039}
5040
5042{
5045 return true;
5046}
5047
5049{
5050 if (OSHasOutputs)
5051 {
5052 // Features names and type are accessed via :GXYn (where n 1 to 8)
5053 // we take these names to display in Output tab
5054 // return value is ssssss,n where ssssss is the name and n is the type
5055 char port_name[MAXINDINAME] = {0}, getoutp[MAXINDINAME] = {0}, configured[MAXINDINAME] = {0}, p_name[MAXINDINAME] = {0};
5056 size_t k {0};
5057 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, configured,
5058 ":GXY0#"); // returns a string with 1 where Feature is configured
5059 // ex: 10010010 means Feature 1,4 and 7 are configured
5060
5061 if (error_or_fail == -4 && configured[0] == '0')
5062 {
5063 OSHasOutputs = false;
5064 LOG_INFO("Outputs not detected, disabling further checks");
5065 }
5066
5067 OutputPorts_NP[0].fill("Unconfigured", "Unconfigured", "%g", 0, 255, 1, 0);
5068 for(int i = 1; i < PORTS_COUNT; i++)
5069 {
5070 if(configured[i - 1] == '1') // is Feature is configured
5071 {
5072 snprintf(getoutp, sizeof(getoutp), ":GXY%d#", i);
5073 int error_or_fail = getCommandSingleCharErrorOrLongResponse(PortFD, port_name, getoutp);
5074 if (error_or_fail > 0)
5075 {
5076 for(k = 0; k < strlen(port_name); k++) // remove feature type
5077 {
5078 if(port_name[k] == ',') port_name[k] = '_';
5079 p_name[k] = port_name[k];
5080 p_name[k + 1] = 0;
5081 }
5082 OutputPorts_NP[i].fill(p_name, p_name, "%g", 0, 255, 1, 0);
5083 }
5084 else
5085 {
5086 LOGF_ERROR("Communication error on %s, ignoring, disconnect and reconnect to clear", getoutp);
5087 OutputPorts_NP[i].fill("Unconfigured", "Unconfigured", "%g", 0, 255, 1, 0);
5088 }
5089 }
5090 else
5091 {
5092 OutputPorts_NP[i].fill("Unconfigured", "Unconfigured", "%g", 0, 255, 1, 0);
5093 }
5094 }
5096 }
5097}
5098
5099
5101{
5102 char cdate[MAXINDINAME] = {0};
5103 char ctime[MAXINDINAME] = {0};
5104 struct tm ltm;
5105 struct tm utm;
5106 time_t time_epoch;
5107 memset(&ltm, 0, sizeof(ltm));
5108 memset(&utm, 0, sizeof(utm));
5109
5110 double offset = 0;
5111 if (getUTFOffset(&offset))
5112 {
5113 char utcStr[8] = {0};
5114 snprintf(utcStr, 8, "%.2f", offset);
5115 TimeTP[OFFSET].setText(utcStr);
5116 }
5117 else
5118 {
5119 LOG_WARN("Could not obtain UTC offset from mount!");
5120 return false;
5121 }
5122
5123 if (getLocalTime(ctime) == false)
5124 {
5125 LOG_WARN("Could not obtain local time from mount!");
5126 return false;
5127 }
5128
5129 if (getLocalDate(cdate) == false)
5130 {
5131 LOG_WARN("Could not obtain local date from mount!");
5132 return false;
5133 }
5134
5135 // To ISO 8601 format in LOCAL TIME!
5136 char datetime[MAXINDINAME] = {0};
5137 snprintf(datetime, MAXINDINAME, "%sT%s", cdate, ctime);
5138
5139 // Now that date+time are combined, let's get tm representation of it.
5140 if (strptime(datetime, "%FT%T", &ltm) == nullptr)
5141 {
5142 LOGF_WARN("Could not process mount date and time: %s", datetime);
5143 return false;
5144 }
5145
5146 ltm.tm_isdst = 0;
5147 // Get local time epoch in UNIX seconds
5148 time_epoch = mktime(&ltm);
5149
5150 // LOCAL to UTC by subtracting offset.
5151 time_epoch -= static_cast<int>(offset * 3600.0);
5152
5153 // Get UTC (we're using localtime_r, but since we shifted time_epoch above by UTCOffset, we should be getting the real UTC time)
5154 localtime_r(&time_epoch, &utm);
5155
5156 // Format it into the final UTC ISO 8601
5157 strftime(cdate, MAXINDINAME, "%Y-%m-%dT%H:%M:%S", &utm);
5158 TimeTP[UTC].setText(cdate);
5159
5160 LOGF_DEBUG("Mount controller UTC Time: %s", TimeTP[UTC].getText());
5161 LOGF_DEBUG("Mount controller UTC Offset: %s", TimeTP[OFFSET].getText());
5162
5163 // Let's send everything to the client
5165 TimeTP.apply();
5166
5167 return true;
5168}
5169
5171{
5172 int lat_dd = 0, lat_mm = 0, long_dd = 0, long_mm = 0;
5173 double lat_ssf = 0.0, long_ssf = 0.0;
5174 char lat_sexagesimal[MAXINDIFORMAT];
5175 char lng_sexagesimal[MAXINDIFORMAT];
5176
5177 if (isSimulation())
5178 {
5179 LocationNP[LOCATION_LATITUDE].setValue(29.5);
5180 LocationNP[LOCATION_LONGITUDE].setValue(48.0);
5181 LocationNP[LOCATION_ELEVATION].setValue(10);
5183 LocationNP.apply();
5184 return true;
5185 }
5186 if (OSHighPrecision)
5187 {
5188 if (getSiteLatitudeAlt(PortFD, &lat_dd, &lat_mm, &lat_ssf, ":GtH#") < 0)
5189 {
5190 //NOTE: All OnStep pre-31 Aug 2020 will fail the above:
5191 // So Try the normal command, if it fails
5192 if (getSiteLatitude(PortFD, &lat_dd, &lat_mm, &lat_ssf) < 0)
5193 {
5194 LOG_WARN("Failed to get site latitude from device.");
5195 return false;
5196 }
5197 else
5198 {
5199 double value = 0;
5200 OSHighPrecision = false; //Don't check using :GtH again
5201 snprintf(lat_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", lat_dd, lat_mm, lat_ssf);
5202 f_scansexa(lat_sexagesimal, &value);
5203 LocationNP[LOCATION_LATITUDE].setValue(value);
5204 }
5205 }
5206 else
5207 {
5208 double value = 0;
5209 //Got High precision coordinates
5210 snprintf(lat_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", lat_dd, lat_mm, lat_ssf);
5211 f_scansexa(lat_sexagesimal, &value);
5212 LocationNP[LOCATION_LATITUDE].setValue(value);
5213 }
5214 }
5215 if (!OSHighPrecision) //Bypass check
5216 {
5217 if (getSiteLatitude(PortFD, &lat_dd, &lat_mm, &lat_ssf) < 0)
5218 {
5219 LOG_WARN("Failed to get site latitude from device.");
5220 return false;
5221 }
5222 else
5223 {
5224 double value = 0;
5225 snprintf(lat_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", lat_dd, lat_mm, lat_ssf);
5226 f_scansexa(lat_sexagesimal, &value);
5227 LocationNP[LOCATION_LATITUDE].setValue(value);
5228 }
5229 }
5230
5231 if (OSHighPrecision)
5232 {
5233 if (getSiteLongitudeAlt(PortFD, &long_dd, &long_mm, &long_ssf, ":GgH#") < 0)
5234 {
5235 //NOTE: All OnStep pre-31 Aug 2020 will fail the above:
5236 // So Try the normal command, if it fails
5237 if (getSiteLongitude(PortFD, &long_dd, &long_mm, &long_ssf) < 0)
5238 {
5239 LOG_WARN("Failed to get site longitude from device.");
5240 return false;
5241 }
5242 else
5243 {
5244 double value = 0;
5245 OSHighPrecision = false;
5246 snprintf(lng_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", long_dd, long_mm, long_ssf);
5247 f_scansexa(lng_sexagesimal, &value);
5248 LocationNP[LOCATION_LONGITUDE].setValue(value);
5249 }
5250 }
5251 else
5252 {
5253 double value = 0;
5254 //Got High precision coordinates
5255 snprintf(lng_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", long_dd, long_mm, long_ssf);
5256 f_scansexa(lng_sexagesimal, &value);
5257 LocationNP[LOCATION_LONGITUDE].setValue(value);
5258 }
5259 }
5260 if(!OSHighPrecision) //Not using high precision
5261 {
5262 if (getSiteLongitude(PortFD, &long_dd, &long_mm, &long_ssf) < 0)
5263 {
5264 LOG_WARN("Failed to get site longitude from device.");
5265 return false;
5266 }
5267 else
5268 {
5269 double value = 0;
5270 snprintf(lng_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", long_dd, long_mm, long_ssf);
5271
5272 f_scansexa(lng_sexagesimal, &value);
5273 LocationNP[LOCATION_LONGITUDE].setValue(value);
5274 }
5275 }
5276
5277 LOGF_INFO("Mount has Latitude %s (%g) Longitude %s (%g) (Longitude sign in carthography format)",
5278 lat_sexagesimal,
5279 LocationNP[LOCATION_LATITUDE].getValue(),
5280 lng_sexagesimal,
5281 LocationNP[LOCATION_LONGITUDE].getValue());
5282
5283 LocationNP.apply();
5284
5285 saveConfig(true, "GEOGRAPHIC_COORD");
5286
5287 return true;
5288}
5289
5290
5291bool LX200_OnStep::Goto(double ra, double dec)
5292{
5293 const struct timespec timeout = {0, 100000000L};
5294
5295 targetRA = ra;
5296 targetDEC = dec;
5297 char RAStr[64] = {0}, DecStr[64] = {0};
5298 int fracbase = 0;
5299
5300 switch (getLX200EquatorialFormat())
5301 {
5303 fracbase = 360000;
5304 break;
5307 default:
5308 fracbase = 3600;
5309 break;
5310 }
5311
5312 fs_sexa(RAStr, targetRA, 2, fracbase);
5313 fs_sexa(DecStr, targetDEC, 2, fracbase);
5314
5315 // If moving, let's stop it first.
5316 if (EqNP.getState() == IPS_BUSY)
5317 {
5318 if (!isSimulation() && abortSlew(PortFD) < 0)
5319 {
5321 LOG_ERROR("Abort slew failed.");
5322 Telescope::AbortSP.apply();
5323 return false;
5324 }
5325
5328 LOG_ERROR("Slew aborted.");
5329 AbortSP.apply();
5330 EqNP.apply();
5331
5333 {
5341 }
5342
5343 // sleep for 100 mseconds
5344 nanosleep(&timeout, nullptr);
5345 }
5346
5347 if (!isSimulation())
5348 {
5350 {
5352 LOG_ERROR("Error setting RA/DEC.");
5353 EqNP.apply();
5354 return false;
5355 }
5356
5357 int err = 0;
5358
5359 /* Slew reads the '0', that is not the end of the slew */
5360 if ((err = Slew(PortFD)))
5361 {
5362 LOGF_ERROR("Error Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
5363 slewError(err);
5364 return false;
5365 }
5366 }
5367
5368 //OnStep: DON'T set TrackState, this may resolve issues with the autoalign, this is updated by the status updates.
5369 // TrackState = SCOPE_SLEWING;
5370 //EqNP.s = IPS_BUSY;
5371
5372 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
5373
5374 return true;
5375}
5376
5378{
5379 //NOTE: THIS SHOULD ONLY BE CALLED _AFTER_ TrackState is set by the update function.
5380 //Otherwise it will not be consistent.
5381 LOG_DEBUG("OnStep SyncParkStatus called");
5383 IsParked = isparked;
5384 ParkSP.reset();
5386
5387 if (TrackState == SCOPE_PARKED)
5388 {
5390 LOG_INFO("Mount is parked.");
5391 }
5392 else
5393 {
5395 LOG_INFO("Mount is unparked.");
5396 }
5397
5398 ParkSP.apply();
5399}
5400
5401
5402void LX200_OnStep::SetParked(bool isparked)
5403{
5405 SyncParkStatus(isparked);
5407 if (parkDataType != PARK_NONE)
5408 WriteParkData();
5410}
5411
5413{
5414#ifdef DEBUG_TRACKSTATE
5415 switch(TrackState)
5416 {
5417 case(SCOPE_IDLE):
5418 LOG_DEBUG("TrackState: SCOPE_IDLE");
5419 return;
5420 case(SCOPE_SLEWING):
5421 LOG_DEBUG("TrackState: SCOPE_SLEWING");
5422 return;
5423 case(SCOPE_TRACKING):
5424 LOG_DEBUG("TrackState: SCOPE_TRACKING");
5425 return;
5426 case(SCOPE_PARKING):
5427 LOG_DEBUG("TrackState: SCOPE_PARKING");
5428 return;
5429 case(SCOPE_PARKED):
5430 LOG_DEBUG("TrackState: SCOPE_PARKED");
5431 return;
5432 }
5433#endif
5434 return;
5435}
5436
5438{
5439 bool result = true;
5440 char temp_string[RB_MAX_LEN];
5441 int utc_hour, utc_min;
5442 // strange thing offset is rounded up to first decimal so that .75 is .8
5443 utc_hour = int(offset) * -1;
5444 utc_min = abs((offset - int(offset)) * 60); // negative offsets require this abs()
5445 if (utc_min > 30) utc_min = 45;
5446 snprintf(temp_string, sizeof(temp_string), ":SG%03d:%02d#", utc_hour, utc_min);
5447 result = (setStandardProcedure(PortFD, temp_string) == 0);
5448 return result;
5449}
5450
5452{
5453 // Homing, Cold and Warm Init
5454 switch (action)
5455 {
5456 case HOME_GO:
5457 if(!sendOnStepCommandBlind(":hC#"))
5458 return IPS_ALERT;
5459 return IPS_BUSY;
5460
5461 case HOME_SET:
5462 if(!sendOnStepCommandBlind(":hF#"))
5463 return IPS_ALERT;
5464 return IPS_OK;
5465
5466 default:
5467 return IPS_ALERT;
5468 }
5469
5470 return IPS_ALERT;
5471}
5472
5474{
5475 if (checkConnection())
5476 {
5477 return true;
5478 }
5479
5480 /* OnStepX has a tendency to start up in an unresponsive state
5481 * due to grabage in the serial buffer. Try to reset it by sending
5482 * the :GVP# command repeatedly.
5483 *
5484 * First sending should result in a '0' response, the second in
5485 * 'OnStep' so the 2nd sending should return with a failure.
5486 */
5487 if(sendOnStepCommand(":GVP#"))
5488 {
5489 if(!sendOnStepCommand(":GVP#"))
5490 {
5491 return checkConnection();
5492 }
5493 }
5494
5495 return false;
5496}
5497
5499{
5500 SlewRateSP[0].fill("0", "0.25x", ISS_OFF);
5501 SlewRateSP[1].fill("1", "0.5x", ISS_OFF);
5502 SlewRateSP[2].fill("2", "1x", ISS_OFF);
5503 SlewRateSP[3].fill("3", "2x", ISS_OFF);
5504 SlewRateSP[4].fill("4", "4x", ISS_OFF);
5505 SlewRateSP[5].fill("5", "8x", ISS_ON);
5506 SlewRateSP[6].fill("6", "20x", ISS_OFF); //last OnStep - OnStepX
5507 SlewRateSP[7].fill("7", "48x", ISS_OFF);
5508 SlewRateSP[8].fill("8", "Half-Max", ISS_OFF);
5509 SlewRateSP[9].fill("9", "Max", ISS_OFF);
5510
5511 SlewRateSP.fill(getDeviceName(), "TELESCOPE_SLEW_RATE", "Slew Rate", MOTION_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
5512}
5513
5515{
5516 if (command == MOTION_START)
5517 {
5521 }
5522 else
5523 {
5524 // Only restore if WE isn't moving
5526 {
5529 }
5530 }
5531
5532 return LX200Telescope::MoveNS(dir, command);
5533}
5534
5536{
5537 if (command == MOTION_START)
5538 {
5542 }
5543 else
5544 {
5545 // Only restore if NS isn't moving
5547 {
5550 }
5551 }
5552
5553 return LX200Telescope::MoveWE(dir, command);
5554}
5555
The Interface class is the base class for all INDI connection plugins.
virtual std::string name()=0
bool isConnected() const
INDI::PropertyText getText(const char *name) const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void setCurrentPollingPeriod(uint32_t msec)
setCurrentPollingPeriod Change the current polling period to call TimerHit() function in the driver.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
Connection::Interface * getActiveConnection()
INDI::PropertyNumber FocusRelPosNP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
void initProperties(const char *groupName)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
INDI::PropertyNumber FocusAbsPosNP
void setState(IPState state)
WidgetView< T > * findWidgetByName(const char *name) const
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
void initProperties(const char *groupName)
Initialize Rotator properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
INDI::PropertyNumber RotatorBacklashNP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
INDI::PropertyNumber GotoRotatorNP
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
TelescopeParkData parkDataType
bool isParked()
isParked is mount currently parked?
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
Provides interface to implement weather reporting and safety monitoring functionality.
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning, bool flipWarning=false)
Add a physical weather measurable parameter to the weather driver.
bool setCriticalParameter(std::string name)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
bool syncCriticalParameters()
Synchronize and update the state of all critical parameters.
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process weather number properties.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save parameters ranges in the config file.
INDI::PropertyNumber ParametersNP
void initProperties(const char *statusGroup, const char *paramsGroup)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyLight critialParametersLP
virtual bool initProperties() override
Called to initialize basic properties required all the time.
@ LX200_HAS_PRECISE_TRACKING_FREQ
virtual bool getLocalDate(char *dateString)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool getUTFOffset(double *offset)
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool getLocalTime(char *timeString)
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
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 checkConnection()
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual void getBasicData()
void setLX200Capability(uint32_t cap)
bool AbortRotator() override
AbortRotator Abort all motion.
char OldOSStat[RB_MAX_LEN]
IPState ClearPECBuffer(int axis)
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
INDI::PropertyNumber OSSetTemperatureNP
IPState PECStatus(int axis)
virtual void Init_Outputs()
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
int getCommandSingleCharResponse(int fd, char *data, const char *cmd)
IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
int getCommandDoubleResponse(int fd, double *value, char *data, const char *cmd)
INDI::PropertySwitch TFCCompensationSP
uint8_t PECStatusGU
virtual IPState ExecuteHomeAction(TelescopeHomeAction action) override
Execute a homing action.
INDI::PropertyNumber OutputPorts_NP
int getCommandIntResponse(int fd, int *value, char *data, const char *cmd)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
OnStepVersion OnStepMountVersion
virtual bool setLocalDate(uint8_t days, uint8_t months, uint16_t years) override
IPState AlignDone()
INDI::PropertyNumber MaxSlewRateNP
IPState OSDisableOutput(int output)
INDI::PropertySwitch HomePauseSP
INDI::PropertyNumber GuideRateNP
virtual bool setUTCOffset(double offset) override
INDI::PropertyNumber TFCDeadbandNP
INDI::PropertyNumber ElevationLimitNP
INDI::PropertyText VersionTP
INDI::PropertySwitch OSRotatorDerotateSP
IPState OSEnableOutput(int output)
INDI::PropertyNumber ObjectNoNP
INDI::PropertySwitch OSPECStatusSP
IPState AlignStartGeometric(int stars)
IPState StartPECPlayback(int axis)
bool SetRotatorBacklash(int32_t steps) override
SetRotatorBacklash Set the Rotatorer backlash compensation value.
uint32_t m_RememberPollingPeriod
virtual bool UnPark() override
Unpark the telescope if already parked.
IPState StopPECPlayback(int axis)
IPState SavePECBuffer(int axis)
INDI::PropertySwitch OSNAlignWriteSP
bool OSAlignCompleted
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
INDI::PropertyNumber minutesPastMeridianNP
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
INDI::PropertyText OnstepStatTP
virtual void getBasicData() override
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
bool sendOnStepCommand(const char *cmd)
INDI::PropertySwitch OSFocus2RateSP
int flushIO(int fd)
INDI::PropertySwitch OSNAlignSP
virtual int setSiteLongitude(int fd, double Long)
long int OSTimeoutMicroSeconds
uint8_t ParkStatusGU
IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
IPState AlignAddStar()
AlignStartGeometric starts the OnStep Multistar align process.
INDI::PropertyNumber TFCCoefficientNP
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
IPState AlignWrite()
INDI::PropertySwitch OSFocus1InitializeSP
MountType OSMountType
virtual bool initProperties() override
Called to initialize basic properties required all the time.
char OSPier[RB_MAX_LEN]
INDI::PropertySwitch TrackAxisSP
bool sendOnStepCommandBlind(const char *cmd)
IPState StartPECRecord(int axis)
bool OSSupports_bitfield_Gu
virtual int setSiteLatitude(int fd, double Long)
long int OSTimeoutSeconds
virtual void SyncParkStatus(bool isparked) override
SyncParkStatus Update the state and switches for parking.
virtual bool sendScopeLocation() override
INDI::PropertyNumber BacklashNP
int setMinElevationLimit(int fd, int min)
IPState ReadPECBuffer(int axis)
IPState WritePECBuffer(int axis)
INDI::PropertyText OSNAlignErrTP
char OldOSPier[RB_MAX_LEN]
IPState HomeRotator() override
HomeRotator Go to home position.
INDI::PropertyNumber OSSetHumidityNP
virtual bool Park() override
Park the telescope to its home position.
virtual void slewError(int slewCode) override
INDI::PropertyNumber OSSetAltitudeNP
bool SetRotatorBacklashEnabled(bool enabled) override
SetRotatorBacklashEnabled Enables or disables the Rotator backlash compensation.
INDI::PropertyText ObjectInfoTP
INDI::PropertySwitch ReticSP
char OSStat[RB_MAX_LEN]
int getCommandSingleCharErrorOrLongResponse(int fd, char *data, const char *cmd)
virtual bool UpdateAlignErr()
INDI::PropertySwitch OSPECRecordSP
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
INDI::PropertyNumber FocusTemperatureNP
INDI::PropertyNumber OSFocus2TargNP
INDI::PropertySwitch OSPECReadSP
INDI::PropertySwitch OSFocus2MotionSP
virtual bool sendScopeTime() override
bool OSGetOutputState(int output)
virtual bool UpdateAlignStatus()
INDI::PropertySwitch OSFocusSelectSP
IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
void PrintTrackState()
PrintTrackState will print to the debug log the status of TrackState if DEBUG_TRACKSTATE is defined o...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
INDI::PropertyText OSNAlignTP
virtual const char * getDefaultName() override
INDI::PropertySwitch OSPECIndexSP
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...
INDI::PropertySwitch FrequencyAdjustSP
virtual void SetParked(bool isparked) override
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INDI::PropertySwitch OSNAlignPolarRealignSP
INDI::PropertySwitch OSNAlignStarsSP
INDI::PropertySwitch PreferredPierSideSP
virtual bool Handshake() override
perform handshake with device to check communication
INDI::PropertyNumber OSSetPressureNP
INDI::PropertySwitch TrackCompSP
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ReadScopeStatus() override
Read telescope status.
bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
INDI::PropertySwitch AutoFlipSP
Provides interface to implement Rotator functionality.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
const char * FOCUS_TAB
FOCUS_TAB Where all the properties for focuser are located.
double max(void)
#define ROTATOR_TAB
Definition gemini.cpp:39
#define STATUS_TAB
Definition gemini.cpp:38
INDI::PropertySwitch ParkSP
INDI::PropertySwitch AbortSP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch TrackStateSP
INDI::PropertySwitch PECStateSP
INDI::PropertySwitch SlewRateSP
INDI::PropertyText TimeTP
INDI::PropertySwitch MountTypeSP
INDI::PropertySwitch MovementWESP
INDI::PropertySwitch ParkOptionSP
INDI::PropertySwitch CoordSP
INDI::PropertyNumber LocationNP
INDI::PropertyNumber TargetNP
double ra
double dec
#define RA_AXIS
#define DEC_AXIS
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
#define MAXINDIFORMAT
Definition indiapi.h:193
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IP_WO
Definition indiapi.h:183
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
#define MAXINDINAME
Definition indiapi.h:189
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
INDI_DIR_NS
void getSexComponentsIID(double value, int *d, int *m, double *s)
Definition indicom.c:266
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition indicom.c:194
void getSexComponents(double value, int *d, int *m, int *s)
Definition indicom.c:243
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
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
int tty_read_expanded(int fd, char *buf, int nbytes, long timeout_seconds, long timeout_microseconds, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:553
int tty_nread_section_expanded(int fd, char *buf, int nsize, char stop_char, long timeout_seconds, long timeout_microseconds, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:764
@ TTY_OK
Definition indicom.h:54
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
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
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads single number property.
Definition indidriver.c:846
void IDMessage(const char *dev, const char *fmt,...)
Definition indidriver.c:983
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define getDeviceName
int fd
std::mutex lx200CommsLock
#define PEC_TAB
#define OUTPUT_TAB
#define ENVIRONMENT_TAB
#define ALIGN_TAB
@ MOUNTTYPE_GEM
@ MOUNTTYPE_ALTAZ
@ MOUNTTYPE_FORK
@ MOUNTTYPE_FORK_ALT
#define RB_MAX_LEN
#define STARTING_PORT
@ OSV_OnStepV4
@ OSV_OnStepV3
@ OSV_OnStepV5
@ OSV_UNKNOWN
@ OSV_OnStepV1or2
@ OSV_OnStepX
@ RES_ERR_FORMAT
Errors
@ ERR_GOTO_ERR_ABOVE_OVERHEAD
@ ERR_GOTO_ERR_PARK
@ ERR_MERIDIAN
@ ERR_GOTO_ERR_OUTSIDE_LIMITS
@ ERR_GOTO_ERR_BELOW_HORIZON
@ ERR_LIMIT_SENSE
@ ERR_DEC
@ ERR_NONE
@ ERR_GOTO_ERR_GOTO
@ ERR_UNDER_POLE
@ ERR_ALT_MAX
@ ERR_GOTO_ERR_IN_MOTION
@ ERR_GOTO_ERR_UNSPECIFIED
@ ERR_SYNC
@ ERR_ALT_MIN
@ ERR_GOTO_ERR_STANDBY
@ ERR_AZM
@ ERR_UNSPECIFIED
@ ERR_PARK
@ ERR_GOTO_ERR_NONE
@ ERR_GOTO_ERR_HARDWARE_FAULT
@ ERR_MOTOR_FAULT
@ ERR_GOTO_SYNC
#define CMD_MAX_LEN
#define PORTS_COUNT
std::mutex lx200CommsLock
#define FIRMWARE_TAB
int getSiteLongitude(int fd, int *ddd, int *mm, double *ssf)
int getSiteLatitudeAlt(int fd, int *dd, int *mm, double *ssf, const char *cmd)
int setMaxElevationLimit(int fd, int max)
int setObjectRA(int fd, double ra, bool addSpace)
int getLX200EquatorialFormat()
int setStandardProcedure(int fd, const char *data)
int setObjectDEC(int fd, double dec, bool addSpace)
int abortSlew(int fd)
int Slew(int fd)
int getSiteLongitudeAlt(int fd, int *ddd, int *mm, double *ssf, const char *cmd)
int getSiteLatitude(int fd, int *dd, int *mm, double *ssf)
int selectCatalogObject(int fd, int catalog, int NNNN)
#define getVersionDate(fd, x)
#define increaseReticleBrightness(fd)
#define getObjectInfo(fd, x)
#define getVersionTime(fd, x)
#define getLX200DEC(fd, x)
@ LX200_STAR_C
Definition lx200driver.h:83
#define getVersionNumber(fd, x)
#define getLX200RA(fd, x)
#define decreaseReticleBrightness(fd)
#define getProductName(fd, x)
@ LX200_EQ_LONGER_FORMAT
Definition lx200driver.h:52
@ LX200_EQ_SHORT_FORMAT
Definition lx200driver.h:50
@ LX200_EQ_LONG_FORMAT
Definition lx200driver.h:51
#define slewToPark(fd)
uint8_t cmd[4]
Definition pwc-ioctl.h:2