1 /*
2     Celestron driver
3 
4     Copyright (C) 2015 Jasem Mutlaq
5     Copyright (C) 2017 Juan Menendez
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21 
22 /*
23     Version with experimental pulse guide support. GC 04.12.2015
24 */
25 
26 #pragma once
27 
28 #include <string>
29 #include "indicom.h"
30 
31 #ifdef __FreeBSD__
32 #include <stdint.h>
33 typedef uint8_t u_int8_t;
34 #endif
35 
36 //#include <thread>
37 //#include <condition_variable>
38 //#include <atomic>
39 
40 /* Starsense specific constants */
41 #define ISNEXSTAR       0x11
42 #define ISSTARSENSE     0x13
43 #define MINSTSENSVER    1.18
44 #define MAX_RESP_SIZE   20
45 
46 // device IDs
47 #define CELESTRON_DEV_RA  0x10
48 #define CELESTRON_DEV_DEC 0x11
49 #define CELESTRON_DEV_GPS 0xb0
50 // focuser device
51 #define CELESTRON_DEV_FOC 0x12
52 
53 // motor commands
54 #define MC_GET_POSITION         0x01    // return 24 bit position
55 #define MC_GOTO_FAST            0x02    // send 24 bit target
56 #define MC_SET_POS_GUIDERATE    0x06    // use the 2 byte CelestronTrackRates to set the rate
57 #define MC_SET_NEG_GUIDERATE    0x07    // for Southern hemisphere, track mode EQ_S
58 #define MC_LEVEL_START          0x0b    // move to switch position
59 #define MC_PEC_RECORD_START     0x0C    // n/a          Ack     Start recording PEC position
60 #define MC_PEC_PLAYBACK         0x0D    // 8 bits       Ack     Start(01)/stop(00) PEC playback
61 
62 #define MTR_PECBIN              0x0E    // current PEC index - 1 byte 0 - 255(88)
63 
64 #define MC_LEVEL_DONE           0x12    // return 0xFF when move finished
65 #define MC_SLEW_DONE            0x13    // return 0xFF when move finished
66 #define MC_PEC_RECORD_DONE      0x15    // n/a          8 bits  != 0 is PEC record completed
67 #define MC_PEC_RECORD_STOP      0x16    // n/a          n/a     Stop PEC recording
68 
69 #define MC_GOTO_SLOW            0x17    //  16/24 bits  Ack     Goto position with slow, variable rate. Either 16 or 24 bit accuracy.
70 //                      Position is a signed fraction of a full rotation
71 #define MC_AT_INDEX             0x18    // n/a          8 bits  FFH at index, 00H not
72 #define MC_SEEK_INDEX           0x19    // n/a          n/a     Seek PEC Index
73 #define MC_MOVE_POS             0x24    // start move positive direction, rate 0-9, 0 is stop
74 #define MC_MOVE_NEG             0x25    // start move negative direction, rate 0-9, 0 is stop
75 
76 #define MTR_AUX_GUIDE           0x26    // aux guide command, rate -100 to 100, duration centiseconds
77 #define MTR_IS_AUX_GUIDE_ACTIVE 0x27    // return 0x00 when aux guide is not in progress
78 
79 // command 0x30 and 0x31 are read/ write memory commands
80 #define MC_PEC_READ_DATA        0x30    // 8            PEC data value  return 1 byte of data:
81 // 0x3f         number of PEC bins (88)
82 // 0x40+i       PEC data for bin i
83 #define MC_PEC_WRITE_DATA       0x31    // 16  PEC data address, PEC data value
84 // 0x40+i, value bin i
85 
86 #define MC_SET_AUTOGUIDE_RATE   0x46    // 0 to 99 as % sidereal
87 #define MC_GET_AUTOGUIDE_RATE   0x47    // 0 to 99 as % sidereal
88 
89 // focuser passthrough commands
90 #define FOC_CALIB_ENABLE        42      // send 0 to start or 1 to stop
91 #define FOC_CALIB_DONE          43      // returns 2 bytes [0] done, [1] state 0-12
92 #define FOC_GET_HS_POSITIONS    44      // returns 2 ints, low and high limits
93 
94 // generic device commands
95 #define GET_VER                 0xfe    // return 2 or 4 bytes major.minor.build
96 
97 typedef enum { GPS_OFF, GPS_ON } CELESTRON_GPS_STATUS;
98 typedef enum { SR_1, SR_2, SR_3, SR_4, SR_5, SR_6, SR_7, SR_8, SR_9 } CELESTRON_SLEW_RATE;
99 typedef enum { CTM_OFF, CTM_ALTAZ, CTM_EQN, CTM_EQS, CTM_RADEC } CELESTRON_TRACK_MODE;
100 typedef enum { RA_AXIS, DEC_AXIS } CELESTRON_AXIS;
101 typedef enum { CELESTRON_N, CELESTRON_S, CELESTRON_W, CELESTRON_E } CELESTRON_DIRECTION;
102 typedef enum { FW_MODEL, FW_VERSION, FW_RA, FW_DEC, FW_ISGEM, FW_CAN_AUX, FW_HAS_FOC } CELESTRON_FIRMWARE;
103 
104 
105 // PEC state machine
106 // the order matters because it's used to check what states are available.
107 // they do not match the the base TelescopePECState
108 typedef enum
109 {
110     NotKnown,       // PEC has not been checked.
111 
112     /// <summary>
113     /// PEC is not available, hardware has been checked, no other state is possible
114     /// </summary>
115     PEC_NOT_AVAILABLE,
116 
117     /// <summary>
118     /// PEC is available but inactive, can seek index
119     /// Seek index is only available command
120     /// </summary>
121     PEC_AVAILABLE,
122 
123     /// <summary>
124     /// The PEC index is being searched for, goes to PEC_INDEXED when found
125     /// </summary>
126     PEC_SEEKING,
127 
128     /// <summary>
129     /// the PEC index has been found, can go to Playback or Recording
130     /// this is equivalent to TelescopePECState PEC_OFF
131     /// </summary>
132     PEC_INDEXED,
133 
134     /// <summary>
135     /// PEC is being played back, stays in this state until stopped
136     /// equivalent to TelescopePECState PEC_ON
137     /// </summary>
138     PEC_PLAYBACK,
139 
140     /// <summary>
141     /// PEC is being recorded, goes to PEC_INDEXED when completed
142     /// </summary>
143     PEC_RECORDING
144 
145 } PEC_STATE;
146 
147 // These values are sent to the hour angle axis motor using the MC_SET_POS|NEG_GUIDERATE
148 // commands to set the tracking rate.
149 typedef enum
150 {
151     CTR_SIDEREAL = 0xFFFF,
152     CTR_SOLAR    = 0xFFFE,
153     CTR_LUNAR    = 0xFFFD
154 } CELESTRON_TRACK_RATE;
155 
156 typedef struct
157 {
158     std::string Model;
159     std::string Version;
160     //std::string GPSFirmware;
161     std::string RAFirmware;
162     std::string DEFirmware;
163     double controllerVersion;
164     char controllerVariant;
165     bool isGem;
166     bool canPec;
167     bool hasHomeIndex;
168     bool hasFocuser;
169     CELESTRON_TRACK_MODE celestronTrackMode;
170 } FirmwareInfo;
171 
172 typedef struct
173 {
174     double ra;
175     double dec;
176     double az;
177     double alt;
178     CELESTRON_SLEW_RATE slewRate;
179     CELESTRON_TRACK_MODE trackMode;
180     CELESTRON_GPS_STATUS gpsStatus;
181     bool isSlewing;
182     uint32_t foc_position = 20000;
183     uint32_t foc_target = 20000;
184 } SimData;
185 
186 
187 /**************************************************************************
188  Utility functions
189 **************************************************************************/
190 namespace Celestron
191 {
192 double trimDecAngle(double angle);
193 uint16_t dd2nex(double angle);
194 uint32_t dd2pnex(double angle);
195 double nex2dd(uint32_t value);
196 double pnex2dd(uint32_t value);
197 }
198 
199 class CelestronDriver
200 {
201     public:
CelestronDriver()202         CelestronDriver() {}
203         virtual ~CelestronDriver() = default;
204 
205         // Misc.
206         const char *getDeviceName();
set_port_fd(int port_fd)207         void set_port_fd(int port_fd)
208         {
209             fd = port_fd;
210         }
set_simulation(bool enable)211         void set_simulation(bool enable)
212         {
213             simulation = enable;
214         }
215         void set_device(const char *name);
216 
217         // Simulation
set_sim_slew_rate(CELESTRON_SLEW_RATE val)218         void set_sim_slew_rate(CELESTRON_SLEW_RATE val)
219         {
220             sim_data.slewRate = val;
221         }
set_sim_track_mode(CELESTRON_TRACK_MODE val)222         void set_sim_track_mode(CELESTRON_TRACK_MODE val)
223         {
224             sim_data.trackMode = val;
225         }
set_sim_gps_status(CELESTRON_GPS_STATUS val)226         void set_sim_gps_status(CELESTRON_GPS_STATUS val)
227         {
228             sim_data.gpsStatus = val;
229         }
set_sim_slewing(bool isSlewing)230         void set_sim_slewing(bool isSlewing)
231         {
232             sim_data.isSlewing = isSlewing;
233         }
set_sim_ra(double ra)234         void set_sim_ra(double ra)
235         {
236             sim_data.ra = ra;
237         }
set_sim_dec(double dec)238         void set_sim_dec(double dec)
239         {
240             sim_data.dec = dec;
241         }
set_sim_az(double az)242         void set_sim_az(double az)
243         {
244             sim_data.az = az;
245         }
set_sim_alt(double alt)246         void set_sim_alt(double alt)
247         {
248             sim_data.alt = alt;
249         }
get_sim_ra()250         double get_sim_ra()
251         {
252             return sim_data.ra;
253         }
get_sim_dec()254         double get_sim_dec()
255         {
256             return sim_data.dec;
257         }
get_sim_foc_offset()258         int get_sim_foc_offset()
259         {
260             return sim_data.foc_target - sim_data.foc_position;
261         }
move_sim_foc(int offset)262         void move_sim_foc(int offset)
263         {
264             sim_data.foc_position += offset;
265         }
266 
267         bool echo();
268         bool check_connection();
269 
270         // Get info
271         bool get_firmware(FirmwareInfo *info);
272         bool get_version(char *version, size_t size);
273         bool get_variant(char *variant);
274         int model();        // returns model number, -1 if failed
275         bool get_model(char *model, size_t size, bool *isGem, bool *canPec, bool *hasHomeIndex);
276         bool get_dev_firmware(int dev, char *version, size_t size);
277         bool get_radec(double *ra, double *dec, bool precise);
278         bool get_azalt(double *az, double *alt, bool precise);
279         bool get_utc_date_time(double *utc_hours, int *yy, int *mm, int *dd, int *hh, int *minute, int *ss, bool *dst,
280                                bool precise);
281 
282         // Motion
283         bool start_motion(CELESTRON_DIRECTION dir, CELESTRON_SLEW_RATE rate);
284         bool stop_motion(CELESTRON_DIRECTION dir);
285         bool abort();
286         bool slew_radec(double ra, double dec, bool precise);
287         bool slew_azalt(double az, double alt, bool precise);
288         bool sync(double ra, double dec, bool precise);
289         bool unsync();
290 
291         // Time & Location
292         bool set_location(double longitude, double latitude);
293         bool get_location(double* longitude, double *latitude);
294         bool set_datetime(struct ln_date *utc, double utc_offset, bool dst = false, bool precise = false);
295 
296         // Track Mode, this is not the Indi track mode
297         bool get_track_mode(CELESTRON_TRACK_MODE *mode);
298         bool set_track_mode(CELESTRON_TRACK_MODE mode);
299 
300         bool is_slewing(bool *slewing);
301 
302         // Hibernate/Wakeup/ align
303         bool hibernate();
304         bool wakeup();
305         bool lastalign();
306         bool startmovetoindex();
307         bool indexreached(bool *atIndex);
308 
309         // Pulse Guide
310         size_t send_pulse(CELESTRON_DIRECTION direction, unsigned char rate, unsigned char duration_msec);
311         bool get_pulse_status(CELESTRON_DIRECTION direction);
312 
313         // get and set guide rate
314         // 0 to 255 corresponding to 0 to 100% sidereal
315         bool get_guide_rate(CELESTRON_AXIS axis, u_int8_t  * rate);
316         bool set_guide_rate(CELESTRON_AXIS axis, u_int8_t  rate);
317 
318         // Pointing state, pier side, returns 'E' or 'W'
319         bool get_pier_side(char * sop);
320 
321         // check if the mount is aligned using the mount J command
322         bool check_aligned(bool *isAligned);
323 
324         // set the tracking rate, sidereal, solar or lunar
325         bool set_track_rate(CELESTRON_TRACK_RATE rate, CELESTRON_TRACK_MODE mode);
326 
327         // focuser commands
328         bool foc_exists();      // read version
329         int foc_position();     // read position, return -1 if failed
330         bool foc_move(uint32_t steps);   // start move
331         bool foc_moving();      // return true if moving
332         bool foc_limits(int * low, int * high);     // read limits
333         bool foc_abort();       // stop move
334 
335         // PEC management
336 
337         PEC_STATE pecState { PEC_STATE::NotKnown };
338 
339         PEC_STATE updatePecState();
340 
341         bool PecSeekIndex();
342         bool isPecAtIndex(bool force = false);            // returns true if the PEC index has been found
343 
344         size_t pecIndex();              // reads the current PEC index
345         int getPecValue(size_t index);     // reads the current PEC value
346         bool setPecValue(size_t index, int data);
347 
348         bool PecPlayback(bool start);
349 
350         bool PecRecord(bool start);
351         bool isPecRecordDone();
352 
353         size_t getPecNumBins();
354 
355         const char *PecStateStr(PEC_STATE);
356         const char *PecStateStr();
357 
358         // PEC simulation properties
359         int simIndex;
360         int simRecordStart;
361         bool simSeekIndex = false;
362 
363     protected:
364         void set_sim_response(const char *fmt, ...);
365         virtual int serial_write(const char *cmd, int nbytes, int *nbytes_written);
366         virtual int serial_read(int nbytes, int *nbytes_read);
367         virtual int serial_read_section(char stop_char, int *nbytes_read);
368 
369         size_t send_command(const char *cmd, size_t cmd_len, char *resp, size_t resp_len,
370                             bool ascii_cmd, bool ascii_resp);
371         size_t send_passthrough(int dest, int cmd_id, const char *payload,
372                                 size_t payload_len, char *resp, size_t response_len);
373 
374         char response[MAX_RESP_SIZE];
375         bool simulation = false;
376         SimData sim_data;
377         int fd = 0;
378 
379         char sim_ra_guide_rate = 50;
380         char sim_dec_guide_rate = 50;
381 };
382 
383 class PecData
384 {
385     public:
386         PecData();
387 
388         // save PEC data to a file
389         bool Save(const char * filename);
390 
391         // saves PEC data to mount
392         bool Save(CelestronDriver * driver);
393 
394         // Loads PEC data from mount
395         bool Load(CelestronDriver * driver);
396 
397         // Loads PEC data from file
398         bool Load(const char * fileName);
399 
NumBins()400         size_t NumBins()
401         {
402             return numBins;
403         }
404 
405         void RemoveDrift();
406 
407         const char *getDeviceName();
408         //    void set_device(const char *name);
409 
410     private:
411         double wormArcSeconds = 7200;
412         double rateScale = 1024;
413         size_t numBins = 88;
414         const double SIDEREAL_ARCSEC_PER_SEC = 360.0 * 60.0 * 60.0 / (23.0 * 3600.0 + 56 * 60 + 4.09);
415 
416         double data[255];   // numbins + 1 values, accumulated PEC offset in arc secs. First one zero
417 
418         void Kalman(PecData newData, int num);
419 };
420 
421 
422