1 /*
2  *  Hamlib ts7400 backend - main file
3  *  Copyright (c) 2001-2009 by Stephane Fillod
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 <stdlib.h>
27 #include <string.h>  /* String function definitions */
28 #include <unistd.h>  /* UNIX standard function definitions */
29 #include <math.h>
30 #include <sys/time.h>
31 #include <time.h>
32 
33 #include <hamlib/rotator.h>
34 #include "serial.h"
35 #include "misc.h"
36 #include "register.h"
37 
38 #include "ts7400.h"
39 
40 struct ts7400_rot_priv_data
41 {
42     azimuth_t az;
43     elevation_t el;
44 
45     struct timeval tv;  /* time last az/el update */
46     azimuth_t target_az;
47     elevation_t target_el;
48 };
49 
50 
51 
ts7400_rot_init(ROT * rot)52 static int ts7400_rot_init(ROT *rot)
53 {
54     struct ts7400_rot_priv_data *priv;
55 
56     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
57 
58     rot->state.priv = (struct ts7400_rot_priv_data *)
59                       malloc(sizeof(struct ts7400_rot_priv_data));
60 
61     if (!rot->state.priv)
62     {
63         return -RIG_ENOMEM;
64     }
65 
66     priv = rot->state.priv;
67 
68     rot->state.rotport.type.rig = RIG_PORT_NONE;
69 
70     priv->az = priv->el = 0;
71 
72     priv->target_az = priv->target_el = 0;
73 
74     return RIG_OK;
75 }
76 
ts7400_rot_cleanup(ROT * rot)77 static int ts7400_rot_cleanup(ROT *rot)
78 {
79     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
80 
81     if (rot->state.priv)
82     {
83         free(rot->state.priv);
84     }
85 
86     rot->state.priv = NULL;
87 
88     return RIG_OK;
89 }
90 
ts7400_rot_open(ROT * rot)91 static int ts7400_rot_open(ROT *rot)
92 {
93     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
94 
95     return RIG_OK;
96 }
97 
ts7400_rot_close(ROT * rot)98 static int ts7400_rot_close(ROT *rot)
99 {
100     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
101 
102     return RIG_OK;
103 }
104 
ts7400_rot_set_position(ROT * rot,azimuth_t az,elevation_t el)105 static int ts7400_rot_set_position(ROT *rot, azimuth_t az, elevation_t el)
106 {
107     struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)
108                                         rot->state.priv;
109 
110     rig_debug(RIG_DEBUG_VERBOSE, "%s called: %.2f %.2f\n", __func__,
111               az, el);
112 
113     priv->target_az = az;
114     priv->target_el = el;
115 
116     gettimeofday(&priv->tv, NULL);
117 
118     return RIG_OK;
119 }
120 
121 
122 /*
123  * Get position of rotor, simulating slow rotation
124  */
ts7400_rot_get_position(ROT * rot,azimuth_t * az,elevation_t * el)125 static int ts7400_rot_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
126 {
127     struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)
128                                         rot->state.priv;
129     struct timeval tv;
130     unsigned elapsed; /* ms */
131 
132     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
133 
134     if (priv->az == priv->target_az &&
135             priv->el == priv->target_el)
136     {
137         *az = priv->az;
138         *el = priv->el;
139         return RIG_OK;
140     }
141 
142     gettimeofday(&tv, NULL);
143 
144     elapsed = (tv.tv_sec - priv->tv.tv_sec) * 1000 +
145               (tv.tv_usec - priv->tv.tv_usec) / 1000;
146 
147     /*
148      * Simulate rotation speed of 360 deg per minute
149      */
150 #define DEG_PER_MS (360./60/1000)
151 
152     if (fabs(priv->target_az - priv->az) / DEG_PER_MS <= elapsed)
153     {
154         /* target reached */
155         priv->az = priv->target_az;
156     }
157     else
158     {
159         if (priv->az < priv->target_az)
160         {
161             priv->az += (azimuth_t)elapsed * DEG_PER_MS;
162         }
163         else
164         {
165             priv->az -= (azimuth_t)elapsed * DEG_PER_MS;
166         }
167     }
168 
169     if (fabs(priv->target_el - priv->el) / DEG_PER_MS <= elapsed)
170     {
171         /* target reached */
172         priv->el = priv->target_el;
173     }
174     else
175     {
176         if (priv->el < priv->target_el)
177         {
178             priv->el += (elevation_t)elapsed * DEG_PER_MS;
179         }
180         else
181         {
182             priv->el -= (elevation_t)elapsed * DEG_PER_MS;
183         }
184     }
185 
186     *az = priv->az;
187     *el = priv->el;
188 
189     priv->tv = tv;
190 
191     return RIG_OK;
192 }
193 
194 
ts7400_rot_stop(ROT * rot)195 static int ts7400_rot_stop(ROT *rot)
196 {
197     struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)
198                                         rot->state.priv;
199     azimuth_t az;
200     elevation_t el;
201 
202     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
203 
204     ts7400_rot_get_position(rot, &az, &el);
205 
206     priv->target_az = priv->az = az;
207     priv->target_el = priv->el = el;
208 
209     return RIG_OK;
210 }
211 
212 
ts7400_rot_park(ROT * rot)213 static int ts7400_rot_park(ROT *rot)
214 {
215     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
216 
217     /* Assume home is 0,0 */
218     ts7400_rot_set_position(rot, 0, 0);
219 
220     return RIG_OK;
221 }
222 
ts7400_rot_reset(ROT * rot,rot_reset_t reset)223 static int ts7400_rot_reset(ROT *rot, rot_reset_t reset)
224 {
225     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
226 
227     return RIG_OK;
228 }
229 
ts7400_rot_move(ROT * rot,int direction,int speed)230 static int ts7400_rot_move(ROT *rot, int direction, int speed)
231 {
232     struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)
233                                         rot->state.priv;
234 
235     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
236     rig_debug(RIG_DEBUG_TRACE, "%s: Direction = %d, Speed = %d\n", __func__,
237               direction, speed);
238 
239     switch (direction)
240     {
241     case ROT_MOVE_UP:
242         return ts7400_rot_set_position(rot, priv->target_az, 90);
243 
244     case ROT_MOVE_DOWN:
245         return ts7400_rot_set_position(rot, priv->target_az, 0);
246 
247     case ROT_MOVE_CCW:
248         return ts7400_rot_set_position(rot, -180, priv->target_el);
249 
250     case ROT_MOVE_CW:
251         return ts7400_rot_set_position(rot, 180, priv->target_el);
252 
253     default:
254         return -RIG_EINVAL;
255     }
256 
257     return RIG_OK;
258 }
259 
ts7400_rot_get_info(ROT * rot)260 static const char *ts7400_rot_get_info(ROT *rot)
261 {
262     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
263 
264     return "ts7400 rotator";
265 }
266 
267 
268 
269 /*
270  * ts7400 rotator capabilities.
271  */
272 
273 const struct rot_caps ts7400_rot_caps =
274 {
275     ROT_MODEL(ROT_MODEL_TS7400),
276     .model_name =     "ts7400",
277     .mfg_name =       "LA7LKA",
278     .version =        "20200113.0",
279     .copyright =      "LGPL",
280     .status =         RIG_STATUS_BETA,
281     .rot_type =       ROT_TYPE_AZEL,
282     .port_type =      RIG_PORT_NONE,
283 
284     .min_az =     -180.,
285     .max_az =     180.,
286     .min_el =     0.,
287     .max_el =     90.,
288 
289     .priv =  NULL,    /* priv */
290 
291     .rot_init =     ts7400_rot_init,
292     .rot_cleanup =  ts7400_rot_cleanup,
293     .rot_open =     ts7400_rot_open,
294     .rot_close =    ts7400_rot_close,
295 
296     .set_position =     ts7400_rot_set_position,
297     .get_position =     ts7400_rot_get_position,
298     .park =     ts7400_rot_park,
299     .stop =     ts7400_rot_stop,
300     .reset =    ts7400_rot_reset,
301     .move =     ts7400_rot_move,
302 
303     .get_info =      ts7400_rot_get_info,
304 };
305 
DECLARE_INITROT_BACKEND(ts7400)306 DECLARE_INITROT_BACKEND(ts7400)
307 {
308     rig_debug(RIG_DEBUG_VERBOSE, "%s: _init called\n", __func__);
309 
310     rot_register(&ts7400_rot_caps);
311 
312     return RIG_OK;
313 }
314