1
|
/*
|
2
|
* Tvheadend - Linux DVB DiseqC Rotor
|
3
|
*
|
4
|
* Copyright (C) 2013 Adam Sutton
|
5
|
*
|
6
|
* This program is free software: you can redistribute it and/or modify
|
7
|
* it under the terms of the GNU General Public License as published by
|
8
|
* the Free Software Foundation, either version 3 of the License, or
|
9
|
* (at your option) any later version.
|
10
|
*
|
11
|
* This program 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
|
14
|
* GNU General Public License for more details.
|
15
|
*
|
16
|
* You should have received a copy of the GNU General Public License
|
17
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
*/
|
19
|
|
20
|
#include "tvheadend.h"
|
21
|
#include "linuxdvb_private.h"
|
22
|
#include "settings.h"
|
23
|
|
24
|
#include <sys/ioctl.h>
|
25
|
#include <sys/types.h>
|
26
|
#include <sys/stat.h>
|
27
|
#include <unistd.h>
|
28
|
#include <fcntl.h>
|
29
|
#include <assert.h>
|
30
|
#include <math.h>
|
31
|
#include <linux/dvb/frontend.h>
|
32
|
|
33
|
/* **************************************************************************
|
34
|
* Class definition
|
35
|
* *************************************************************************/
|
36
|
|
37
|
#define MAX_FAR_AWAY_SAT 5.0
|
38
|
double old_pos = MAX_FAR_AWAY_SAT;
|
39
|
|
40
|
typedef struct linuxdvb_rotor
|
41
|
{
|
42
|
linuxdvb_diseqc_t;
|
43
|
|
44
|
/* USALS */
|
45
|
double lr_site_lat;
|
46
|
double lr_site_lon;
|
47
|
double lr_sat_lon;
|
48
|
|
49
|
/* GOTOX */
|
50
|
uint32_t lr_position;
|
51
|
|
52
|
uint32_t lr_rate;
|
53
|
|
54
|
} linuxdvb_rotor_t;
|
55
|
|
56
|
static const char *
|
57
|
linuxdvb_rotor_class_get_title ( idnode_t *o )
|
58
|
{
|
59
|
static char buf[256];
|
60
|
linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o;
|
61
|
snprintf(buf, sizeof(buf), "Rotor: %s", ld->ld_type);
|
62
|
return buf;
|
63
|
}
|
64
|
|
65
|
extern const idclass_t linuxdvb_diseqc_class;
|
66
|
|
67
|
const idclass_t linuxdvb_rotor_class = {
|
68
|
.ic_super = &linuxdvb_diseqc_class,
|
69
|
.ic_class = "linuxdvb_rotor",
|
70
|
.ic_caption = "DiseqC Rotor",
|
71
|
.ic_get_title = linuxdvb_rotor_class_get_title,
|
72
|
};
|
73
|
|
74
|
const idclass_t linuxdvb_rotor_gotox_class =
|
75
|
{
|
76
|
.ic_super = &linuxdvb_rotor_class,
|
77
|
.ic_class = "linuxdvb_rotor_gotox",
|
78
|
.ic_caption = "GOTOX Rotor",
|
79
|
.ic_properties = (const property_t[]) {
|
80
|
{
|
81
|
.type = PT_U16,
|
82
|
.id = "position",
|
83
|
.name = "Position",
|
84
|
.off = offsetof(linuxdvb_rotor_t, lr_position),
|
85
|
},
|
86
|
{
|
87
|
.type = PT_U16,
|
88
|
.id = "rate",
|
89
|
.name = "Rate (millis/click)",
|
90
|
.off = offsetof(linuxdvb_rotor_t, lr_rate),
|
91
|
},
|
92
|
{}
|
93
|
}
|
94
|
};
|
95
|
|
96
|
const idclass_t linuxdvb_rotor_usals_class =
|
97
|
{
|
98
|
.ic_super = &linuxdvb_rotor_class,
|
99
|
.ic_class = "linuxdvb_rotor_usals",
|
100
|
.ic_caption = "USALS Rotor",
|
101
|
.ic_properties = (const property_t[]) {
|
102
|
{
|
103
|
.type = PT_DBL,
|
104
|
.id = "site_lat",
|
105
|
.name = "Site Latitude",
|
106
|
.off = offsetof(linuxdvb_rotor_t, lr_site_lat),
|
107
|
},
|
108
|
{
|
109
|
.type = PT_DBL,
|
110
|
.id = "site_lon",
|
111
|
.name = "Site Longitude",
|
112
|
.off = offsetof(linuxdvb_rotor_t, lr_site_lon),
|
113
|
},
|
114
|
{
|
115
|
.type = PT_DBL,
|
116
|
.id = "sat_lon",
|
117
|
.name = "Satellite Longitude",
|
118
|
.off = offsetof(linuxdvb_rotor_t, lr_sat_lon),
|
119
|
},
|
120
|
{
|
121
|
.type = PT_U16,
|
122
|
.id = "rate",
|
123
|
.name = "Rate (millis/deg)",
|
124
|
.off = offsetof(linuxdvb_rotor_t, lr_rate),
|
125
|
},
|
126
|
|
127
|
{}
|
128
|
}
|
129
|
};
|
130
|
|
131
|
/* **************************************************************************
|
132
|
* Class methods
|
133
|
* *************************************************************************/
|
134
|
|
135
|
static int
|
136
|
linuxdvb_rotor_grace
|
137
|
( linuxdvb_diseqc_t *ld, dvb_mux_t *lm )
|
138
|
{
|
139
|
linuxdvb_rotor_t *lr = (linuxdvb_rotor_t*)ld;
|
140
|
// linuxdvb_satconf_t *ls = ld->ld_satconf->lse_parent;
|
141
|
double newpos;
|
142
|
int delta;
|
143
|
#if 0
|
144
|
if (!ls->ls_orbital_dir || lr->lr_rate == 0)
|
145
|
return 120;
|
146
|
#endif
|
147
|
if (idnode_is_instance(&lr->ld_id, &linuxdvb_rotor_gotox_class))
|
148
|
newpos = lr->lr_position; /* GotoX */
|
149
|
else
|
150
|
// newpos = (lr->lr_sat_lon + 0.005) * 100; /* USALS */
|
151
|
newpos = lr->lr_sat_lon;
|
152
|
#if 0
|
153
|
if (ls->ls_orbital_dir == 'W')
|
154
|
curpos = -(curpos);
|
155
|
delta = abs(deltaI32(curpos, newpos));
|
156
|
#endif
|
157
|
delta = fabs(ceil(newpos - old_pos));
|
158
|
old_pos = newpos;
|
159
|
|
160
|
/* ignore very small movements like 0.8W and 1W */
|
161
|
if (delta <= 2)
|
162
|
return 0;
|
163
|
/* add one extra second, because of the rounding issue */
|
164
|
// return ((lr->lr_rate*delta+999)/1000) + 1;
|
165
|
|
166
|
return ((lr->lr_rate*delta)/1000) + 1;
|
167
|
}
|
168
|
|
169
|
static int
|
170
|
linuxdvb_rotor_check_orbital_pos
|
171
|
( dvb_mux_t *lm, linuxdvb_satconf_ele_t *ls )
|
172
|
{
|
173
|
int pos;
|
174
|
char dir;
|
175
|
|
176
|
|
177
|
if (dvb_network_get_orbital_pos(lm->mm_network, &pos, &dir) < 0)
|
178
|
return 0;
|
179
|
|
180
|
if (dir != ls->lse_parent->ls_orbital_dir)
|
181
|
return 0;
|
182
|
|
183
|
if (abs(pos - ls->lse_parent->ls_orbital_pos) > 2)
|
184
|
return 0;
|
185
|
|
186
|
tvhdebug("diseqc", "rotor already positioned to %i.%i%c",
|
187
|
pos / 10, pos % 10, dir);
|
188
|
return 1;
|
189
|
}
|
190
|
|
191
|
/* GotoX */
|
192
|
static int
|
193
|
linuxdvb_rotor_gotox_tune
|
194
|
( linuxdvb_rotor_t *lr, dvb_mux_t *lm, linuxdvb_satconf_ele_t *ls, int fd )
|
195
|
{
|
196
|
int i;
|
197
|
|
198
|
if (linuxdvb_rotor_check_orbital_pos(lm, ls))
|
199
|
return 0;
|
200
|
|
201
|
for (i = 0; i <= ls->lse_parent->ls_diseqc_repeats; i++) {
|
202
|
if (linuxdvb_diseqc_send(fd, 0xE0, 0x31, 0x6B, 1, (int)lr->lr_position)) {
|
203
|
tvherror("diseqc", "failed to set GOTOX pos %d", lr->lr_position);
|
204
|
return -1;
|
205
|
}
|
206
|
usleep(25000);
|
207
|
}
|
208
|
|
209
|
tvhdebug("diseqc", "rotor GOTOX pos %d sent", lr->lr_position);
|
210
|
|
211
|
return linuxdvb_rotor_grace((linuxdvb_diseqc_t*)lr,lm);
|
212
|
}
|
213
|
|
214
|
/* USALS */
|
215
|
static int
|
216
|
linuxdvb_rotor_usals_tune
|
217
|
( linuxdvb_rotor_t *lr, dvb_mux_t *lm, linuxdvb_satconf_ele_t *ls, int fd )
|
218
|
{
|
219
|
/*
|
220
|
* Code originally written in PR #238 by Jason Millard jsm174
|
221
|
*
|
222
|
* USALS rotor logic adapted from tune-s2
|
223
|
* http://updatelee.blogspot.com/2010/09/tune-s2.html
|
224
|
*
|
225
|
* Antenna Alignment message data format:
|
226
|
* http://www.dvb.org/technology/standards/A155-3_DVB-RCS2_Higher_layer_satellite_spec.pdf
|
227
|
*/
|
228
|
|
229
|
#define TO_DEG(x) ((x * 180.0) / M_PI)
|
230
|
#define TO_RAD(x) ((x * M_PI) / 180.0)
|
231
|
|
232
|
int i;
|
233
|
|
234
|
static const double r_eq = 6378.14;
|
235
|
static const double r_sat = 42164.57;
|
236
|
|
237
|
double lat = TO_RAD(lr->lr_site_lat);
|
238
|
double lon = TO_RAD(lr->lr_site_lon);
|
239
|
double pos = TO_RAD(lr->lr_sat_lon);
|
240
|
|
241
|
double dishVector[3] = {
|
242
|
(r_eq * cos(lat)),
|
243
|
0,
|
244
|
(r_eq * sin(lat)),
|
245
|
};
|
246
|
|
247
|
double satVector[3] = {
|
248
|
(r_sat * cos(lon - pos)),
|
249
|
(r_sat * sin(lon - pos)),
|
250
|
0
|
251
|
};
|
252
|
|
253
|
double satPointing[3] = {
|
254
|
(satVector[0] - dishVector[0]),
|
255
|
(satVector[1] - dishVector[1]),
|
256
|
(satVector[2] - dishVector[2])
|
257
|
};
|
258
|
|
259
|
double motor_angle = TO_DEG(atan(satPointing[1] / satPointing[0]));
|
260
|
|
261
|
int sixteenths = ((fabs(motor_angle) * 16.0) + 0.5);
|
262
|
|
263
|
int angle_1 = (((motor_angle > 0.0) ? 0xd0 : 0xe0) | (sixteenths >> 8));
|
264
|
int angle_2 = (sixteenths & 0xff);
|
265
|
|
266
|
#if 0
|
267
|
if (linuxdvb_rotor_check_orbital_pos(lm, ls))
|
268
|
return 0;
|
269
|
#endif
|
270
|
|
271
|
if(lr->lr_sat_lon == old_pos) /* Dish already at same position, no need to move */
|
272
|
return 0;
|
273
|
|
274
|
tvhtrace("diseqc", "rotor USALS goto %0.1f%c (motor %0.2f %sclockwise)",
|
275
|
fabs(lr->lr_sat_lon), (lr->lr_sat_lon > 0.0) ? 'E' : 'W',
|
276
|
motor_angle, (motor_angle > 0.0) ? "counter-" : "");
|
277
|
|
278
|
for (i = 0; i <= ls->lse_parent->ls_diseqc_repeats; i++) {
|
279
|
if (linuxdvb_diseqc_send(fd, 0xE0, 0x31, 0x6E, 2, angle_1, angle_2)) {
|
280
|
tvherror("diseqc", "failed to send USALS command");
|
281
|
return -1;
|
282
|
}
|
283
|
usleep(25000);
|
284
|
}
|
285
|
|
286
|
return linuxdvb_rotor_grace((linuxdvb_diseqc_t*)lr,lm);
|
287
|
|
288
|
#undef TO_RAD
|
289
|
#undef TO_DEG
|
290
|
}
|
291
|
|
292
|
static int
|
293
|
linuxdvb_rotor_tune
|
294
|
( linuxdvb_diseqc_t *ld, dvb_mux_t *lm, linuxdvb_satconf_ele_t *ls, int fd )
|
295
|
{
|
296
|
linuxdvb_rotor_t *lr = (linuxdvb_rotor_t*)ld;
|
297
|
|
298
|
/* Force to 18v (quicker movement) */
|
299
|
if (ioctl(fd, FE_SET_VOLTAGE, SEC_VOLTAGE_18)) {
|
300
|
tvherror("diseqc", "failed to set 18v for rotor movement");
|
301
|
return -1;
|
302
|
}
|
303
|
usleep(15000);
|
304
|
|
305
|
/* GotoX */
|
306
|
if (idnode_is_instance(&lr->ld_id, &linuxdvb_rotor_gotox_class))
|
307
|
return linuxdvb_rotor_gotox_tune(lr, lm, ls, fd);
|
308
|
|
309
|
/* USALS */
|
310
|
return linuxdvb_rotor_usals_tune(lr, lm, ls, fd);
|
311
|
}
|
312
|
|
313
|
/* **************************************************************************
|
314
|
* Create / Config
|
315
|
* *************************************************************************/
|
316
|
|
317
|
struct {
|
318
|
const char *name;
|
319
|
const idclass_t *idc;
|
320
|
} linuxdvb_rotor_all[] = {
|
321
|
{
|
322
|
.name = "GOTOX",
|
323
|
.idc = &linuxdvb_rotor_gotox_class
|
324
|
},
|
325
|
{
|
326
|
.name = "USALS",
|
327
|
.idc = &linuxdvb_rotor_usals_class
|
328
|
}
|
329
|
};
|
330
|
|
331
|
htsmsg_t *
|
332
|
linuxdvb_rotor_list ( void *o )
|
333
|
{
|
334
|
int i;
|
335
|
htsmsg_t *m = htsmsg_create_list();
|
336
|
htsmsg_add_str(m, NULL, "None");
|
337
|
for (i = 0; i < ARRAY_SIZE(linuxdvb_rotor_all); i++)
|
338
|
htsmsg_add_str(m, NULL, linuxdvb_rotor_all[i].name);
|
339
|
return m;
|
340
|
}
|
341
|
|
342
|
linuxdvb_diseqc_t *
|
343
|
linuxdvb_rotor_create0
|
344
|
( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls )
|
345
|
{
|
346
|
int i;
|
347
|
linuxdvb_diseqc_t *ld = NULL;
|
348
|
|
349
|
for (i = 0; i < ARRAY_SIZE(linuxdvb_rotor_all); i++) {
|
350
|
if (!strcmp(name ?: "", linuxdvb_rotor_all[i].name)) {
|
351
|
ld = linuxdvb_diseqc_create0(calloc(1, sizeof(linuxdvb_rotor_t)),
|
352
|
NULL, linuxdvb_rotor_all[i].idc, conf,
|
353
|
linuxdvb_rotor_all[i].name, ls);
|
354
|
if (ld) {
|
355
|
ld->ld_tune = linuxdvb_rotor_tune;
|
356
|
ld->ld_grace = linuxdvb_rotor_grace;
|
357
|
}
|
358
|
}
|
359
|
}
|
360
|
|
361
|
return ld;
|
362
|
}
|
363
|
|
364
|
void
|
365
|
linuxdvb_rotor_destroy ( linuxdvb_diseqc_t *lr )
|
366
|
{
|
367
|
linuxdvb_diseqc_destroy(lr);
|
368
|
free(lr);
|
369
|
}
|
370
|
|
371
|
/******************************************************************************
|
372
|
* Editor Configuration
|
373
|
*
|
374
|
* vim:sts=2:ts=2:sw=2:et
|
375
|
*****************************************************************************/
|