1 /*
2 * Hamlib Rotator backend - SPID Rot1Prog & Rot2Prog
3 * Copyright (c) 2009-2011 by Norvald H. Ryeng, LA6YKA
4 *
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <math.h>
31
32 #include "hamlib/rotator.h"
33 #include "serial.h"
34 #include "misc.h"
35 #include "register.h"
36
37 #include "spid.h"
38
39 #define TOK_AZRES 1
40 #define TOK_ELRES 2
41
42 struct spid_rot2prog_priv_data
43 {
44 int az_resolution;
45 int el_resolution;
46 };
47
spid_rot_init(ROT * rot)48 static int spid_rot_init(ROT *rot)
49 {
50 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
51
52 if (!rot || !rot->caps)
53 {
54 return -RIG_EINVAL;
55 }
56
57 if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG ||
58 rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG)
59 {
60 struct spid_rot2prog_priv_data *priv;
61
62 priv = (struct spid_rot2prog_priv_data *)malloc(sizeof(struct
63 spid_rot2prog_priv_data));
64
65 if (!priv)
66 {
67 return -RIG_ENOMEM;
68 }
69
70 rot->state.priv = (void *)priv;
71
72 priv->az_resolution = 0;
73 priv->el_resolution = 0;
74 }
75
76 return RIG_OK;
77 }
78
spid_rot_cleanup(ROT * rot)79 static int spid_rot_cleanup(ROT *rot)
80 {
81 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
82
83 if (!rot)
84 {
85 return -RIG_EINVAL;
86 }
87
88 if (rot->state.priv && (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG ||
89 rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG))
90 {
91 free(rot->state.priv);
92 }
93
94 rot->state.priv = NULL;
95
96 return RIG_OK;
97 }
98
spid_get_conf(ROT * rot,token_t token,char * val)99 static int spid_get_conf(ROT *rot, token_t token, char *val)
100 {
101 struct spid_rot2prog_priv_data *priv = (struct spid_rot2prog_priv_data *)
102 rot->state.priv;
103
104 rig_debug(RIG_DEBUG_TRACE, "%s called %d\n", __func__, (int)token);
105
106 if (rot->caps->rot_model != ROT_MODEL_SPID_ROT2PROG &&
107 rot->caps->rot_model != ROT_MODEL_SPID_MD01_ROT2PROG)
108 {
109 return -RIG_EINVAL;
110 }
111
112 switch (token)
113 {
114 case TOK_AZRES:
115 sprintf(val, "%d", priv->az_resolution);
116 break;
117
118 case TOK_ELRES:
119 sprintf(val, "%d", priv->el_resolution);
120 break;
121
122 default:
123 return -RIG_EINVAL;
124 }
125
126 return RIG_OK;
127 }
128
spid_set_conf(ROT * rot,token_t token,const char * val)129 static int spid_set_conf(ROT *rot, token_t token, const char *val)
130 {
131 struct spid_rot2prog_priv_data *priv = (struct spid_rot2prog_priv_data *)
132 rot->state.priv;
133
134 rig_debug(RIG_DEBUG_TRACE, "%s: called %d=%s\n", __func__, (int)token, val);
135
136 if (rot->caps->rot_model != ROT_MODEL_SPID_ROT2PROG &&
137 rot->caps->rot_model != ROT_MODEL_SPID_MD01_ROT2PROG)
138 {
139 return -RIG_EINVAL;
140 }
141
142 switch (token)
143 {
144 case TOK_AZRES:
145 priv->az_resolution = atoi(val);
146 break;
147
148 case TOK_ELRES:
149 priv->el_resolution = atoi(val);
150 break;
151
152 default:
153 return -RIG_EINVAL;
154 }
155
156 return RIG_OK;
157 }
158
spid_rot1prog_rot_set_position(ROT * rot,azimuth_t az,elevation_t el)159 static int spid_rot1prog_rot_set_position(ROT *rot, azimuth_t az,
160 elevation_t el)
161 {
162 struct rot_state *rs = &rot->state;
163 int retval;
164 char cmdstr[13];
165 unsigned int u_az;
166
167 rig_debug(RIG_DEBUG_TRACE, "%s called: %f %f\n", __func__, az, el);
168
169 u_az = 360 + az;
170
171 cmdstr[0] = 0x57; /* S */
172 cmdstr[1] = 0x30 + u_az / 100; /* H1 */
173 cmdstr[2] = 0x30 + (u_az % 100) / 10; /* H2 */
174 cmdstr[3] = 0x30 + (u_az % 10); /* H3 */
175 cmdstr[4] = 0x30; /* H4 */
176 cmdstr[5] = 0x00; /* PH */
177 cmdstr[6] = 0x00; /* V1 */
178 cmdstr[7] = 0x00; /* V2 */
179 cmdstr[8] = 0x00; /* V3 */
180 cmdstr[9] = 0x00; /* V4 */
181 cmdstr[10] = 0x00; /* PV */
182 cmdstr[11] = 0x2F; /* K */
183 cmdstr[12] = 0x20; /* END */
184
185 retval = write_block(&rs->rotport, cmdstr, 13);
186
187 if (retval != RIG_OK)
188 {
189 return retval;
190 }
191
192 return RIG_OK;
193 }
194
spid_rot2prog_rot_set_position(ROT * rot,azimuth_t az,elevation_t el)195 static int spid_rot2prog_rot_set_position(ROT *rot, azimuth_t az,
196 elevation_t el)
197 {
198 struct rot_state *rs = &rot->state;
199 struct spid_rot2prog_priv_data *priv = (struct spid_rot2prog_priv_data *)
200 rs->priv;
201 int retval;
202 int retry_read = 0;
203 char cmdstr[13];
204 unsigned int u_az, u_el;
205
206 rig_debug(RIG_DEBUG_TRACE, "%s called: %f %f\n", __func__, az, el);
207
208 if (!priv->az_resolution || !priv->el_resolution)
209 {
210 do
211 {
212 retval = write_block(&rs->rotport,
213 "\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1F\x20", 13);
214
215 if (retval != RIG_OK)
216 {
217 return retval;
218 }
219
220 memset(cmdstr, 0, 12);
221 retval = read_block(&rs->rotport, cmdstr, 12);
222 }
223 while (retval < 0 && retry_read++ < rot->state.rotport.retry);
224
225 if (retval < 0)
226 {
227 return retval;
228 }
229 }
230 else
231 {
232 cmdstr[5] = priv->az_resolution; /* PH */
233 cmdstr[10] = priv->el_resolution; /* PV */
234 }
235
236 u_az = cmdstr[5] * (360 + az);
237 u_el = cmdstr[10] * (360 + el);
238
239 cmdstr[0] = 0x57; /* S */
240 cmdstr[1] = 0x30 + u_az / 1000; /* H1 */
241 cmdstr[2] = 0x30 + (u_az % 1000) / 100; /* H2 */
242 cmdstr[3] = 0x30 + (u_az % 100) / 10; /* H3 */
243 cmdstr[4] = 0x30 + (u_az % 10); /* H4 */
244
245 cmdstr[6] = 0x30 + u_el / 1000; /* V1 */
246 cmdstr[7] = 0x30 + (u_el % 1000) / 100; /* V2 */
247 cmdstr[8] = 0x30 + (u_el % 100) / 10; /* V3 */
248 cmdstr[9] = 0x30 + (u_el % 10); /* V4 */
249
250 cmdstr[11] = 0x2F; /* K */
251 cmdstr[12] = 0x20; /* END */
252
253 retval = write_block(&rs->rotport, cmdstr, 13);
254
255 if (retval != RIG_OK)
256 {
257 return retval;
258 }
259
260 /* Unlike the original Rot2Prog, MD-01 and MD-02 return the position
261 after receiving the set position command. */
262 if (rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG)
263 {
264 retry_read = 0;
265
266 do
267 {
268 retval = read_block(&rs->rotport, cmdstr, 12);
269 }
270 while ((retval < 0) && (retry_read++ < rot->state.rotport.retry));
271 }
272
273 return RIG_OK;
274 }
275
spid_rot_get_position(ROT * rot,azimuth_t * az,elevation_t * el)276 static int spid_rot_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
277 {
278 struct rot_state *rs = &rot->state;
279 int retval;
280 int retry_read = 0;
281 char posbuf[12];
282
283 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
284
285 do
286 {
287 retval = write_block(&rs->rotport,
288 "\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1F\x20", 13);
289
290 if (retval != RIG_OK)
291 {
292 return retval;
293 }
294
295 memset(posbuf, 0, 12);
296
297 if (rot->caps->rot_model == ROT_MODEL_SPID_ROT1PROG)
298 {
299 retval = read_block(&rs->rotport, posbuf, 5);
300 }
301 else if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG ||
302 rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG)
303 {
304 retval = read_block(&rs->rotport, posbuf, 12);
305 }
306 else
307 {
308 retval = -RIG_EINVAL;
309 }
310 }
311 while (retval < 0 && retry_read++ < rot->state.rotport.retry);
312
313 if (retval < 0)
314 {
315 return retval;
316 }
317
318 *az = posbuf[1] * 100;
319 *az += posbuf[2] * 10;
320 *az += posbuf[3];
321
322 if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG ||
323 rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG)
324 {
325 *az += posbuf[4] / 10.0;
326 }
327
328 *az -= 360;
329
330 *el = 0.0;
331
332 if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG ||
333 rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG)
334 {
335 *el = posbuf[6] * 100;
336 *el += posbuf[7] * 10;
337 *el += posbuf[8];
338 *el += posbuf[9] / 10.0;
339 *el -= 360;
340 }
341
342 rig_debug(RIG_DEBUG_TRACE, "%s: (az, el) = (%.1f, %.1f)\n",
343 __func__, *az, *el);
344
345 return RIG_OK;
346 }
347
spid_rot_stop(ROT * rot)348 static int spid_rot_stop(ROT *rot)
349 {
350 struct rot_state *rs = &rot->state;
351 int retval;
352 int retry_read = 0;
353 char posbuf[12];
354
355 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
356
357 do
358 {
359 retval = write_block(&rs->rotport,
360 "\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0F\x20", 13);
361
362 if (retval != RIG_OK)
363 {
364 return retval;
365 }
366
367 memset(posbuf, 0, 12);
368
369 if (rot->caps->rot_model == ROT_MODEL_SPID_ROT1PROG)
370 {
371 retval = read_block(&rs->rotport, posbuf, 5);
372 }
373 else if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG ||
374 rot->caps->rot_model == ROT_MODEL_SPID_MD01_ROT2PROG)
375 {
376 retval = read_block(&rs->rotport, posbuf, 12);
377 }
378 }
379 while (retval < 0 && retry_read++ < rot->state.rotport.retry);
380
381 if (retval < 0)
382 {
383 return retval;
384 }
385
386 return RIG_OK;
387 }
388
spid_md01_rot2prog_rot_move(ROT * rot,int direction,int speed)389 static int spid_md01_rot2prog_rot_move(ROT *rot, int direction, int speed)
390 {
391 struct rot_state *rs = &rot->state;
392 char dir = 0x00;
393 int retval;
394 char cmdstr[13];
395
396 rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__);
397
398 switch (direction)
399 {
400 case ROT_MOVE_UP:
401 dir = 0x04;
402 break;
403
404 case ROT_MOVE_DOWN:
405 dir = 0x08;
406 break;
407
408 case ROT_MOVE_LEFT:
409 dir = 0x01;
410 break;
411
412 case ROT_MOVE_RIGHT:
413 dir = 0x02;
414 break;
415 }
416
417 cmdstr[0] = 0x57; /* S */
418 cmdstr[1] = dir; /* H1 */
419 cmdstr[2] = 0x00; /* H2 */
420 cmdstr[3] = 0x00; /* H3 */
421 cmdstr[4] = 0x00; /* H4 */
422 cmdstr[6] = 0x00; /* V1 */
423 cmdstr[7] = 0x00; /* V2 */
424 cmdstr[8] = 0x00; /* V3 */
425 cmdstr[9] = 0x00; /* V4 */
426 cmdstr[11] = 0x14; /* K */
427 cmdstr[12] = 0x20; /* END */
428
429 /* The rotator must be stopped before changing directions. Since
430 we don't know which direction we're already moving in (if
431 moving at all), always send the stop command first. */
432 spid_rot_stop(rot);
433
434 retval = write_block(&rs->rotport, cmdstr, 13);
435 return retval;
436 }
437
438 const struct confparams spid_cfg_params[] =
439 {
440 {
441 TOK_AZRES, "az_resolution", "Azimuth resolution", "Number of pulses per degree, 0 = auto sense",
442 "0", RIG_CONF_NUMERIC, { .n = { 0, 0xff, 1 } }
443 },
444 {
445 TOK_ELRES, "el_resolution", "Eleveation resolution", "Number of pulses per degree, 0 = auto sense",
446 "0", RIG_CONF_NUMERIC, { .n = { 0, 0xff, 1 } }
447 },
448 { RIG_CONF_END, NULL, }
449 };
450
451 const struct rot_caps spid_rot1prog_rot_caps =
452 {
453 ROT_MODEL(ROT_MODEL_SPID_ROT1PROG),
454 .model_name = "Rot1Prog",
455 .mfg_name = "SPID",
456 .version = "20191208.0",
457 .copyright = "LGPL",
458 .status = RIG_STATUS_STABLE,
459 .rot_type = ROT_TYPE_AZIMUTH,
460 .port_type = RIG_PORT_SERIAL,
461 .serial_rate_min = 1200,
462 .serial_rate_max = 1200,
463 .serial_data_bits = 8,
464 .serial_stop_bits = 1,
465 .serial_parity = RIG_PARITY_NONE,
466 .serial_handshake = RIG_HANDSHAKE_NONE,
467 .write_delay = 0,
468 .post_write_delay = 0,
469 .timeout = 400,
470 .retry = 3,
471
472 .min_az = -180.0,
473 .max_az = 540.0,
474 .min_el = 0.0,
475 .max_el = 0.0,
476
477 .cfgparams = spid_cfg_params,
478 .get_conf = spid_get_conf,
479 .set_conf = spid_set_conf,
480
481 .rot_init = spid_rot_init,
482 .rot_cleanup = spid_rot_cleanup,
483 .get_position = spid_rot_get_position,
484 .set_position = spid_rot1prog_rot_set_position,
485 .stop = spid_rot_stop,
486 };
487
488 const struct rot_caps spid_rot2prog_rot_caps =
489 {
490 ROT_MODEL(ROT_MODEL_SPID_ROT2PROG),
491 .model_name = "Rot2Prog",
492 .mfg_name = "SPID",
493 .version = "20191208.0",
494 .copyright = "LGPL",
495 .status = RIG_STATUS_STABLE,
496 .rot_type = ROT_TYPE_AZEL,
497 .port_type = RIG_PORT_SERIAL,
498 .serial_rate_min = 600,
499 .serial_rate_max = 600,
500 .serial_data_bits = 8,
501 .serial_stop_bits = 1,
502 .serial_parity = RIG_PARITY_NONE,
503 .serial_handshake = RIG_HANDSHAKE_NONE,
504 .write_delay = 0,
505 .post_write_delay = 0,
506 .timeout = 400,
507 .retry = 3,
508
509 .min_az = -180.0,
510 .max_az = 540.0,
511 .min_el = -20.0,
512 .max_el = 210.0,
513
514 .cfgparams = spid_cfg_params,
515 .get_conf = spid_get_conf,
516 .set_conf = spid_set_conf,
517
518 .rot_init = spid_rot_init,
519 .rot_cleanup = spid_rot_cleanup,
520 .get_position = spid_rot_get_position,
521 .set_position = spid_rot2prog_rot_set_position,
522 .stop = spid_rot_stop,
523 };
524
525 const struct rot_caps spid_md01_rot2prog_rot_caps =
526 {
527 ROT_MODEL(ROT_MODEL_SPID_MD01_ROT2PROG),
528 .model_name = "MD-01/02 (ROT2 mode)",
529 .mfg_name = "SPID",
530 .version = "20191208.0",
531 .copyright = "LGPL",
532 .status = RIG_STATUS_STABLE,
533 .rot_type = ROT_TYPE_AZEL,
534 .port_type = RIG_PORT_SERIAL,
535 .serial_rate_min = 600,
536 .serial_rate_max = 460800,
537 .serial_data_bits = 8,
538 .serial_stop_bits = 1,
539 .serial_parity = RIG_PARITY_NONE,
540 .serial_handshake = RIG_HANDSHAKE_NONE,
541 .write_delay = 0,
542 .post_write_delay = 0,
543 .timeout = 400,
544 .retry = 3,
545
546 .min_az = -180.0,
547 .max_az = 540.0,
548 .min_el = -20.0,
549 .max_el = 210.0,
550
551 .cfgparams = spid_cfg_params,
552 .get_conf = spid_get_conf,
553 .set_conf = spid_set_conf,
554
555 .rot_init = spid_rot_init,
556 .rot_cleanup = spid_rot_cleanup,
557 .get_position = spid_rot_get_position,
558 .set_position = spid_rot2prog_rot_set_position,
559 .move = spid_md01_rot2prog_rot_move,
560 .stop = spid_rot_stop,
561 };
562
DECLARE_INITROT_BACKEND(spid)563 DECLARE_INITROT_BACKEND(spid)
564 {
565 rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
566
567 rot_register(&spid_rot1prog_rot_caps);
568 rot_register(&spid_rot2prog_rot_caps);
569 rot_register(&spid_md01_rot2prog_rot_caps);
570
571 return RIG_OK;
572 }
573