1 /*
2 * Hamlib Tentec 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 <stdio.h>
27 #include <stdlib.h>
28 #include <string.h> /* String function definitions */
29 #include <unistd.h> /* UNIX standard function definitions */
30 #include <math.h>
31
32 #include "hamlib/rig.h"
33 #include "serial.h"
34 #include "misc.h"
35 #include "cal.h"
36 #include "register.h"
37
38 #include "tentec.h"
39
40 static void tentec_tuning_factor_calc(RIG *rig);
41
42 #define EOM "\015" /* CR */
43
44 #define TT_AM '0'
45 #define TT_USB '1'
46 #define TT_LSB '2'
47 #define TT_CW '3'
48 #define TT_FM '4'
49
50 static int tentec_filters[] =
51 {
52 6000, 5700, 5400, 5100, 4800, 4500, 4200, 3900, 3600, 3300, 3000, 2850, 2700, 2550, 2400,
53 2250, 2100, 1950, 1800,
54 1650, 1500, 1350, 1200, 1050, 900, 750, 675, 600, 525, 450, 375, 330, 300, 8000
55 };
56
57
58
59 /*
60 * tentec_transaction
61 * read exactly data_len bytes
62 * We assume that rig!=NULL, rig->state!= NULL, data!=NULL, data_len!=NULL
63 * Otherwise, you'll get a nice seg fault. You've been warned!
64 */
tentec_transaction(RIG * rig,const char * cmd,int cmd_len,char * data,int * data_len)65 int tentec_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
66 int *data_len)
67 {
68 int retval;
69 struct rig_state *rs;
70
71 rs = &rig->state;
72
73 rig_flush(&rs->rigport);
74
75 retval = write_block(&rs->rigport, cmd, cmd_len);
76
77 if (retval != RIG_OK)
78 {
79 return retval;
80 }
81
82 /* no data expected, TODO: flush input? */
83 if (!data || !data_len)
84 {
85 return 0;
86 }
87
88 retval = read_string(&rs->rigport, data, *data_len, NULL, 0);
89
90 if (retval == -RIG_ETIMEOUT)
91 {
92 retval = 0;
93 }
94
95 if (retval < 0)
96 {
97 return retval;
98 }
99
100 *data_len = retval;
101
102 return RIG_OK;
103 }
104
105
106 /*
107 * tentec_init:
108 * Basically, it just sets up *priv
109 */
tentec_init(RIG * rig)110 int tentec_init(RIG *rig)
111 {
112 struct tentec_priv_data *priv;
113
114 rig->state.priv = (struct tentec_priv_data *)malloc(sizeof(
115 struct tentec_priv_data));
116
117 if (!rig->state.priv)
118 {
119 /* whoops! memory shortage! */
120 return -RIG_ENOMEM;
121 }
122
123 priv = rig->state.priv;
124
125 memset(priv, 0, sizeof(struct tentec_priv_data));
126
127 /*
128 * set arbitrary initial status
129 */
130 priv->freq = MHz(10);
131 priv->mode = RIG_MODE_AM;
132 priv->width = kHz(6);
133 priv->pbt = 0;
134 priv->cwbfo = 1000;
135 priv->agc = RIG_AGC_MEDIUM; /* medium */
136 priv->lnvol = priv->spkvol = 0.0; /* mute */
137
138 /* tentec_tuning_factor_calc needs rig->state.priv */
139 tentec_tuning_factor_calc(rig);
140
141 return RIG_OK;
142 }
143
144 /*
145 * Tentec generic tentec_cleanup routine
146 * the serial port is closed by the frontend
147 */
tentec_cleanup(RIG * rig)148 int tentec_cleanup(RIG *rig)
149 {
150 if (rig->state.priv)
151 {
152 free(rig->state.priv);
153 }
154
155 rig->state.priv = NULL;
156
157 return RIG_OK;
158 }
159
160 /*
161 * Tentec transceiver only open routine
162 * Restart and set program to execute.
163 */
tentec_trx_open(RIG * rig)164 int tentec_trx_open(RIG *rig)
165 {
166 int retval;
167
168 /*
169 * be kind: use XX first, and do 'Dsp Program Execute' only
170 * in " DSP START" state.
171 */
172 retval = tentec_transaction(rig, "P1" EOM, 3, NULL, NULL);
173
174 if (retval != RIG_OK)
175 {
176 return retval;
177 }
178
179 return RIG_OK;
180 }
181
182
183 /*
184 * Tuning Factor Calculations
185 * assumes rig!=NULL, rig->state.priv!=NULL
186 * assumes priv->mode in supported modes.
187 */
tentec_tuning_factor_calc(RIG * rig)188 static void tentec_tuning_factor_calc(RIG *rig)
189 {
190 struct tentec_priv_data *priv;
191 freq_t tfreq;
192 int adjtfreq, mcor, fcor, cwbfo;
193
194 priv = (struct tentec_priv_data *)rig->state.priv;
195 cwbfo = 0;
196
197 /* computed fcor only used if mode is not CW */
198 fcor = (int)floor((double)priv->width / 2.0) + 200;
199
200 switch (priv->mode)
201 {
202 case RIG_MODE_AM:
203 case RIG_MODE_FM:
204 mcor = 0; break;
205
206 case RIG_MODE_CW:
207 mcor = -1; cwbfo = priv->cwbfo; fcor = 0; break;
208
209 case RIG_MODE_LSB:
210 mcor = -1; break;
211
212 case RIG_MODE_USB:
213 mcor = 1; break;
214
215 default:
216 rig_debug(RIG_DEBUG_BUG, "%s: invalid mode %s\n", __func__,
217 rig_strrmode(priv->mode));
218 mcor = 1; break;
219 }
220
221 tfreq = priv->freq / (freq_t)Hz(1);
222
223 adjtfreq = (int)tfreq - 1250 + (int)(mcor * (fcor + priv->pbt));
224
225 priv->ctf = (adjtfreq / 2500) + 18000;
226 priv->ftf = (int)floor((double)(adjtfreq % 2500) * 5.46);
227 priv->btf = (int)floor((double)(fcor + priv->pbt + cwbfo + 8000) * 2.73);
228 }
229
230 /*
231 * tentec_set_freq
232 * assumes rig!=NULL, rig->state.priv!=NULL
233 * assumes priv->mode in AM,CW,LSB or USB.
234 */
tentec_set_freq(RIG * rig,vfo_t vfo,freq_t freq)235 int tentec_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
236 {
237 struct tentec_priv_data *priv;
238 struct rig_state *rs = &rig->state;
239 int freq_len, retval;
240 char freqbuf[16];
241 freq_t old_freq;
242
243 priv = (struct tentec_priv_data *)rig->state.priv;
244
245 old_freq = priv->freq;
246 priv->freq = freq;
247 tentec_tuning_factor_calc(rig);
248
249 freq_len = sprintf(freqbuf, "N%c%c%c%c%c%c" EOM,
250 priv->ctf >> 8, priv->ctf & 0xff,
251 priv->ftf >> 8, priv->ftf & 0xff,
252 priv->btf >> 8, priv->btf & 0xff);
253
254 retval = write_block(&rs->rigport, freqbuf, freq_len);
255
256 if (retval != RIG_OK)
257 {
258 priv->freq = old_freq;
259 return retval;
260 }
261
262 return RIG_OK;
263 }
264
265 /*
266 * tentec_get_freq
267 * Assumes rig!=NULL, freq!=NULL
268 */
tentec_get_freq(RIG * rig,vfo_t vfo,freq_t * freq)269 int tentec_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
270 {
271 struct tentec_priv_data *priv = (struct tentec_priv_data *)rig->state.priv;
272
273 *freq = priv->freq;
274
275 return RIG_OK;
276 }
277
278 /*
279 * tentec_set_mode
280 * Assumes rig!=NULL
281 */
tentec_set_mode(RIG * rig,vfo_t vfo,rmode_t mode,pbwidth_t width)282 int tentec_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
283 {
284 struct tentec_priv_data *priv = (struct tentec_priv_data *)rig->state.priv;
285 struct rig_state *rs = &rig->state;
286 char ttmode;
287 rmode_t saved_mode;
288 pbwidth_t saved_width;
289 int mdbuf_len, ttfilter = -1, retval;
290 char mdbuf[32];
291
292 switch (mode)
293 {
294 case RIG_MODE_USB: ttmode = TT_USB; break;
295
296 case RIG_MODE_LSB: ttmode = TT_LSB; break;
297
298 case RIG_MODE_CW: ttmode = TT_CW; break;
299
300 case RIG_MODE_AM: ttmode = TT_AM; break;
301
302 case RIG_MODE_FM: ttmode = TT_FM; break;
303
304 default:
305 rig_debug(RIG_DEBUG_ERR,
306 "%s: unsupported mode %s\n", __func__, rig_strrmode(mode));
307 return -RIG_EINVAL;
308 }
309
310 /* backup current values
311 * in case we fail to write to port
312 */
313 saved_mode = priv->mode;
314 saved_width = priv->width;
315
316 if (width != RIG_PASSBAND_NOCHANGE)
317 {
318 if (width == RIG_PASSBAND_NORMAL)
319 {
320 width = rig_passband_normal(rig, mode);
321 }
322
323 for (ttfilter = 0; tentec_filters[ttfilter] != 0; ttfilter++)
324 {
325 if (tentec_filters[ttfilter] == width)
326 {
327 break;
328 }
329 }
330
331 if (tentec_filters[ttfilter] != width)
332 {
333 rig_debug(RIG_DEBUG_ERR,
334 "%s: unsupported width %d\n", __func__, (int)width);
335 return -RIG_EINVAL;
336 }
337
338 priv->width = width;
339 }
340
341 priv->mode = mode;
342
343 tentec_tuning_factor_calc(rig);
344
345 if (width != RIG_PASSBAND_NOCHANGE)
346 {
347 mdbuf_len = sprintf(mdbuf, "W%c" EOM
348 "N%c%c%c%c%c%c" EOM
349 "M%c" EOM,
350 ttfilter,
351 priv->ctf >> 8, priv->ctf & 0xff,
352 priv->ftf >> 8, priv->ftf & 0xff,
353 priv->btf >> 8, priv->btf & 0xff,
354 ttmode);
355 retval = write_block(&rs->rigport, mdbuf, mdbuf_len);
356
357 if (retval != RIG_OK)
358 {
359 priv->mode = saved_mode;
360 priv->width = saved_width;
361 return retval;
362 }
363 }
364 else
365 {
366 mdbuf_len = sprintf(mdbuf,
367 "N%c%c%c%c%c%c" EOM
368 "M%c" EOM,
369 priv->ctf >> 8, priv->ctf & 0xff,
370 priv->ftf >> 8, priv->ftf & 0xff,
371 priv->btf >> 8, priv->btf & 0xff,
372 ttmode);
373 retval = write_block(&rs->rigport, mdbuf, mdbuf_len);
374
375 if (retval != RIG_OK)
376 {
377 priv->mode = saved_mode;
378 return retval;
379 }
380 }
381
382 return RIG_OK;
383 }
384
385 /*
386 * tentec_get_mode
387 * Assumes rig!=NULL, mode!=NULL
388 */
tentec_get_mode(RIG * rig,vfo_t vfo,rmode_t * mode,pbwidth_t * width)389 int tentec_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width)
390 {
391 struct tentec_priv_data *priv = (struct tentec_priv_data *)rig->state.priv;
392
393 *mode = priv->mode;
394 *width = priv->width;
395
396 return RIG_OK;
397 }
398
399
400 /*
401 * tentec_set_level
402 * Assumes rig!=NULL
403 * FIXME: cannot support PREAMP and ATT both at same time (make sens though)
404 */
tentec_set_level(RIG * rig,vfo_t vfo,setting_t level,value_t val)405 int tentec_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
406 {
407 struct tentec_priv_data *priv = (struct tentec_priv_data *)rig->state.priv;
408 struct rig_state *rs = &rig->state;
409 int cmd_len, retval = RIG_OK;
410 char cmdbuf[32];
411
412 /* Optimize:
413 * sort the switch cases with the most frequent first
414 */
415 switch (level)
416 {
417 case RIG_LEVEL_AGC:
418 /* default to MEDIUM */
419 cmd_len = sprintf(cmdbuf, "G%c" EOM,
420 val.i == RIG_AGC_SLOW ? '1' : (
421 val.i == RIG_AGC_FAST ? '3' : '2'));
422 retval = write_block(&rs->rigport, cmdbuf, cmd_len);
423
424 if (retval == RIG_OK)
425 {
426 priv->agc = val.i;
427 }
428
429 return retval;
430
431 case RIG_LEVEL_AF:
432 /* FIXME: support also separate Lineout setting
433 * -> need to create RIG_LEVEL_LINEOUT ?
434 */
435 cmd_len = sprintf(cmdbuf, "C\x7f%c" EOM, (int)((1.0 - val.f) * 63.0));
436 retval = write_block(&rs->rigport, cmdbuf, cmd_len);
437
438 if (retval == RIG_OK)
439 {
440 priv->lnvol = priv->spkvol = val.f;
441 }
442
443 return retval;
444
445 case RIG_LEVEL_IF:
446 priv->pbt = val.i;
447 retval = tentec_set_freq(rig, vfo, priv->freq);
448 return retval;
449
450 case RIG_LEVEL_CWPITCH:
451 priv->cwbfo = val.i;
452
453 if (priv->mode == RIG_MODE_CW)
454 {
455 retval = tentec_set_freq(rig, vfo, priv->freq);
456 }
457
458 return retval;
459
460 default:
461 rig_debug(RIG_DEBUG_ERR, "%s: unsupported set_level %s\n", __func__,
462 rig_strlevel(level));
463 return -RIG_EINVAL;
464 }
465
466 return RIG_OK;
467 }
468
469
470 /*
471 * tentec_get_level
472 * Assumes rig!=NULL, val!=NULL
473 */
tentec_get_level(RIG * rig,vfo_t vfo,setting_t level,value_t * val)474 int tentec_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
475 {
476 struct tentec_priv_data *priv = (struct tentec_priv_data *)rig->state.priv;
477 int retval, lvl_len;
478 unsigned char lvlbuf[32];
479
480
481 /* Optimize:
482 * sort the switch cases with the most frequent first
483 */
484 switch (level)
485 {
486 case RIG_LEVEL_RAWSTR:
487 /* read A/D converted value */
488 lvl_len = 4;
489 retval = tentec_transaction(rig, "X" EOM, 2, (char *) lvlbuf, &lvl_len);
490
491 if (retval != RIG_OK)
492 {
493 return retval;
494 }
495
496 if (lvl_len != 3)
497 {
498 rig_debug(RIG_DEBUG_ERR, "tentec_get_level: wrong answer"
499 "len=%d\n", lvl_len);
500 return -RIG_ERJCTED;
501 }
502
503 lvlbuf[3] = '\0';
504 rig_debug(RIG_DEBUG_VERBOSE, "tentec_get_level: cmd=%c,hi=%d,lo=%d\n",
505 lvlbuf[0], lvlbuf[1], lvlbuf[2]);
506 val->i = (lvlbuf[1] << 8) + lvlbuf[2];
507 break;
508
509 case RIG_LEVEL_AGC:
510 val->i = priv->agc;
511 break;
512
513 case RIG_LEVEL_AF:
514 val->f = priv->spkvol;
515 break;
516
517 case RIG_LEVEL_IF:
518 val->i = priv->pbt;
519 break;
520
521 case RIG_LEVEL_CWPITCH:
522 val->i = priv->cwbfo;
523 break;
524
525 default:
526 rig_debug(RIG_DEBUG_ERR, "%s: unsupported get_level %s\n", __func__,
527 rig_strlevel(level));
528 return -RIG_EINVAL;
529 }
530
531 return RIG_OK;
532 }
533
534
535 /*
536 * tentec_get_info
537 * Assumes rig!=NULL
538 */
tentec_get_info(RIG * rig)539 const char *tentec_get_info(RIG *rig)
540 {
541 static char buf[100]; /* FIXME: reentrancy */
542 int firmware_len, retval;
543
544 /*
545 * protocol version
546 */
547 firmware_len = 10;
548 retval = tentec_transaction(rig, "?" EOM, 2, buf, &firmware_len);
549
550 if ((retval != RIG_OK) || (firmware_len > 10))
551 {
552 rig_debug(RIG_DEBUG_ERR, "tentec_get_info: ack NG, len=%d\n",
553 firmware_len);
554 return NULL;
555 }
556
557 return buf;
558 }
559
560
561 /*
562 * initrigs_tentec is called by rig_backend_load
563 */
DECLARE_INITRIG_BACKEND(tentec)564 DECLARE_INITRIG_BACKEND(tentec)
565 {
566 rig_debug(RIG_DEBUG_VERBOSE, "%s: _init called\n", __func__);
567
568 rig_register(&tt550_caps);
569 rig_register(&tt516_caps);
570 rig_register(&tt565_caps);
571 rig_register(&tt538_caps);
572 rig_register(&tt585_caps);
573 rig_register(&tt588_caps);
574 rig_register(&tt599_caps);
575 rig_register(&rx320_caps);
576 rig_register(&rx331_caps);
577 rig_register(&rx340_caps);
578 rig_register(&rx350_caps);
579
580 return RIG_OK;
581 }
582
583