1 /*
2 * Hamlib Rotator backend - Celestron
3 * Copyright (c) 2011 by Stephane Fillod
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <math.h>
30 #include <ctype.h>
31 #include <stddef.h>
32 #include <stdint.h>
33
34 #include "hamlib/rotator.h"
35 #include "serial.h"
36 #include "misc.h"
37 #include "register.h"
38
39 #include "rot_ioptron.h"
40
41 #define ACK "#"
42 #define ACK1 '1'
43
44 #define BUFSZ 128
45
46 /**
47 * ioptron_transaction
48 *
49 * cmdstr - Command to be sent to the rig.
50 * data - Buffer for reply string. Can be NULL, indicating that no reply is
51 * is needed, but answer will still be read.
52 * data_len - in: Size of buffer. It is the caller's responsibily to provide
53 * a large enough buffer for all possible replies for a command.
54 *
55 * COMMANDS note: as of 12/2018 a mixture of V2 and V3
56 * | TTTTTTTT(T) .01 arc seconds
57 * | alt- sign with 8 digits, az - 9 digits |
58 * | Command | Attribute | Return value | Description |
59 * -------------------------------------------------------------------|
60 * | :GAC# | .01 arcsec | sTTTTTTTTTTTTTTTTT# | gets alt(s8), az(9) |
61 * | :SzTTTTTTTTT# | .01 arcsec | '1' == OK | Set Target azimuth |
62 * | :SasTTTTTTTT# |.01 arcsec | '1' == OK | Set Target elevation |
63 * | :Q# | - | '1' == OK | Halt all slewing |
64 * | :ST0# | - | '1' == OK | Halt tracking |
65 * | :MS# | - | '1' == OK | GoTo Target |
66 * |
67 * returns:
68 * RIG_OK - if no error occurred.
69 * RIG_EIO - if an I/O error occurred while sending/receiving data.
70 * RIG_ETIMEOUT - if timeout expires without any characters received.
71 */
72
73 static int
ioptron_transaction(ROT * rot,const char * cmdstr,char * data,size_t data_len)74 ioptron_transaction(ROT *rot, const char *cmdstr,
75 char *data, size_t data_len)
76 {
77 struct rot_state *rs;
78 int retval;
79 int retry_read = 0;
80 char replybuf[BUFSZ];
81
82 rs = &rot->state;
83
84 transaction_write:
85
86 rig_flush(&rs->rotport);
87
88 if (cmdstr)
89 {
90 retval = write_block(&rs->rotport, cmdstr, strlen(cmdstr));
91
92 if (retval != RIG_OK)
93 {
94 goto transaction_quit;
95 }
96 }
97
98 /** Always read the reply to know whether the cmd went OK */
99 if (!data)
100 {
101 data = replybuf;
102 }
103
104 if (!data_len)
105 {
106 data_len = BUFSZ;
107 }
108
109 /** the answer */
110 memset(data, 0, data_len);
111 retval = read_string(&rs->rotport, data, data_len, ACK, strlen(ACK));
112
113 if (retval < 0)
114 {
115 if (retry_read++ < rot->state.rotport.retry)
116 {
117 goto transaction_write;
118 }
119
120 goto transaction_quit;
121 }
122
123 /** check for acknowledge */
124 if (retval < 1)
125 {
126 rig_debug(RIG_DEBUG_ERR, "%s: unexpected response, len %d: '%s'\n", __func__,
127 retval, data);
128 return -RIG_EPROTO;
129 }
130
131 retval = RIG_OK;
132 transaction_quit:
133 return retval;
134 }
135
136 /**
137 * Opens the Port and sets all needed parameters for operation
138 * as of 12/2018 initiates mount with V3 :MountInfo#
139 */
ioptron_open(ROT * rot)140 static int ioptron_open(ROT *rot)
141 {
142 rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
143
144 return ioptron_transaction(rot, ":Mountinfo#", NULL, 0);
145 }
146
147 /** sets mount position, requires 4 steps
148 * set azmiuth
149 * set altitude
150 * goto set
151 * stop tracking - mount starts tracking after goto
152 */
153 static int
ioptron_set_position(ROT * rot,azimuth_t az,elevation_t el)154 ioptron_set_position(ROT *rot, azimuth_t az, elevation_t el)
155 {
156 char cmdstr[32];
157 char retbuf[10];
158 int retval;
159 float faz, fel;
160
161 rig_debug(RIG_DEBUG_TRACE, "%s called: %f %f\n", __func__, az, el);
162
163 /* units .01 arc sec */
164 faz = az * 360000;
165 fel = el * 360000;
166 /* set azmiuth, returns '1" if OK */
167 sprintf(cmdstr, ":Sz%09.0f#", faz);
168 retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf));
169
170 if (retval != RIG_OK || retbuf[0] != ACK1)
171 {
172 return -RIG_EPROTO;
173 }
174
175 /* set altitude, returns '1" if OK */
176 sprintf(cmdstr, ":Sa+%08.0f#", fel);
177 retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf));
178
179 if (retval != RIG_OK || retbuf[0] != ACK1)
180 {
181 return -RIG_EPROTO;
182 }
183
184 /* move to set target, V2 command, returns '1" if OK */
185 sprintf(cmdstr, ":MS#"); //
186 retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf));
187
188 if (retval != RIG_OK || retbuf[0] != ACK1)
189 {
190 return -RIG_EPROTO;
191 }
192
193 /* stop tracking, V2 command, returns '1" if OK */
194 sprintf(cmdstr, ":ST0#");
195 retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf));
196
197 if (retval != RIG_OK || retbuf[0] != ACK1)
198 {
199 return -RIG_EPROTO;
200 }
201
202 return retval;
203 }
204
205 /** gets current position */
206 static int
ioptron_get_position(ROT * rot,azimuth_t * az,elevation_t * el)207 ioptron_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
208 {
209 char posbuf[32];
210 int retval;
211 float w;
212
213 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
214
215 /** Get Az-Alt */
216 retval = ioptron_transaction(rot, ":GAC#", posbuf, sizeof(posbuf));
217
218 if (retval != RIG_OK || strlen(posbuf) < 18)
219 {
220 return retval < 0 ? retval : -RIG_EPROTO;
221 }
222
223 if (sscanf(posbuf, "%9f", &w) != 1)
224 {
225 return -RIG_EPROTO;
226 }
227
228 /** convert from .01 arc sec to degrees */
229 *el = ((elevation_t)w / 360000.);
230
231 if (sscanf(posbuf + 9, "%9f", &w) != 1)
232 {
233 return -RIG_EPROTO;
234 }
235
236 *az = ((azimuth_t)w / 360000.);
237
238 rig_debug(RIG_DEBUG_TRACE, "%s: (az, el) = (%.1f, %.1f)\n",
239 __func__, *az, *el);
240
241 return RIG_OK;
242 }
243
244 /** stop everything **/
245 static int
ioptron_stop(ROT * rot)246 ioptron_stop(ROT *rot)
247 {
248 int retval;
249 char retbuf[10];
250
251 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
252
253 /** stop slew, returns "1" if OK */
254 retval = ioptron_transaction(rot, ":Q#", retbuf, 10);
255
256 if (retval != RIG_OK || retbuf[0] != ACK1)
257 {
258 return -RIG_EPROTO;
259 }
260
261 /** stops tracking returns "1" if OK */
262 retval = ioptron_transaction(rot, ":ST0#", retbuf, 10);
263
264 if (retval != RIG_OK || retbuf[0] != ACK1)
265 {
266 return -RIG_EPROTO;
267 }
268
269 return retval;
270 }
271
272 /** get mount type code, initializes mount */
273 static const char *
ioptron_get_info(ROT * rot)274 ioptron_get_info(ROT *rot)
275 {
276 static char info[16];
277 char str[6];
278 int retval;
279
280 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
281
282 retval = ioptron_transaction(rot, ":MountInfo#", str, sizeof(str));
283
284 rig_debug(RIG_DEBUG_TRACE, "retval, RIG_OK str %d %d %str\n", retval, RIG_OK,
285 str);
286
287 sprintf(info, "MountInfo %s", str);
288
289 return info;
290 }
291
292
293
294 /** *************************************************************************
295 *
296 * ioptron mount capabilities.
297 *
298 * Protocol documentation:
299 * from ioptron:
300 * RS232-Command_Language pdf
301 * note that iOptron is currently (12/2018) using a mix of V2 and V3 commands :(
302 */
303
304 const struct rot_caps ioptron_rot_caps =
305 {
306 ROT_MODEL(ROT_MODEL_IOPTRON),
307 .model_name = "iOptron",
308 .mfg_name = "iOptron",
309 .version = "20191209.0",
310 .copyright = "LGPL",
311 .status = RIG_STATUS_ALPHA,
312 .rot_type = ROT_TYPE_AZEL,
313 .port_type = RIG_PORT_SERIAL,
314 .serial_rate_min = 9600,
315 .serial_rate_max = 9600,
316 .serial_data_bits = 8,
317 .serial_stop_bits = 1,
318 .serial_parity = RIG_PARITY_NONE,
319 .serial_handshake = RIG_HANDSHAKE_NONE,
320 .write_delay = 0,
321 .post_write_delay = 0,
322 .timeout = 1000, /* worst case scenario 3500 */
323 .retry = 1,
324
325 .min_az = 0.0,
326 .max_az = 360.0,
327 .min_el = 0.0,
328 .max_el = 180.0,
329
330 .rot_open = ioptron_open,
331 .get_position = ioptron_get_position,
332 .set_position = ioptron_set_position,
333 .stop = ioptron_stop,
334 .get_info = ioptron_get_info,
335 };
336
337 /* ****************************************************************** */
338
DECLARE_INITROT_BACKEND(ioptron)339 DECLARE_INITROT_BACKEND(ioptron)
340 {
341 rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
342
343 rot_register(&ioptron_rot_caps);
344
345 return RIG_OK;
346 }
347
348 /* ****************************************************************** */
349 /* end of file */
350
351