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