Hamlib/flexradio/dttsp.c

782 wiersze
19 KiB
C

/*
* Hamlib DttSP backend - main file
* Copyright (c) 2001-2009 by Stephane Fillod
*
* Some code derived from DttSP
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 by Frank Brickle, AB2KT and Bob McGwier, N4HY
*
* $Id: dttsp.c,v 1.2 2008-05-07 22:18:25 fillods Exp $
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <math.h>
#include "hamlib/rig.h"
#include "iofunc.h"
#include "misc.h"
#include "token.h"
#include "register.h"
#include "cal.h"
#include "flexradio.h"
/*
* This backend is a two layer rig control: DttSP core over a mundane tuner
*
* 2 interfaces of DttSP are supported: IPC & UDP
*
* TODO: Transmit setup
*/
#define DEFAULT_DTTSP_CMD_PATH "/dev/shm/SDRcommands"
#define DEFAULT_SAMPLE_RATE 48000
/* DttSP constants */
#define MAXRX 4
#define RXMETERPTS 5
#define TXMETERPTS 9
#define MAXMETERPTS 9
#define DTTSP_PORT_CLIENT_COMMAND 19001
#define DTTSP_PORT_CLIENT_SPECTRUM 19002
#define DTTSP_PORT_CLIENT_METER 19003
#define DTTSP_PORT_CLIENT_BUFSIZE 65536
struct dttsp_priv_data {
/* tuner providing IF */
rig_model_t tuner_model;
RIG *tuner;
shortfreq_t IF_center_freq;
int sample_rate;
int rx_delta_f;
hamlib_port_t meter_port;
#if 0
union {
/* IPC specific */
struct {
/* DttSP meter handle */
int meter_fd;
};
/* UDP specific RIG_PORT_NETWORK_UDP */
struct {
unsigned short port;
struct sockaddr_in clnt;
int clen, flags, sock;
char buff[DTTSP_PORT_CLIENT_BUFSIZE];
int size, used;
};
#endif
};
static int dttsp_ipc_init(RIG *rig);
static int dttsp_ipc_cleanup(RIG *rig);
static int dttsp_ipc_open(RIG *rig);
static int dttsp_ipc_close(RIG *rig);
static int dttsp_udp_init(RIG *rig);
static int dttsp_udp_cleanup(RIG *rig);
static int dttsp_udp_open(RIG *rig);
static int dttsp_udp_close(RIG *rig);
static int dttsp_set_freq(RIG *rig, vfo_t vfo, freq_t freq);
static int dttsp_get_freq(RIG *rig, vfo_t vfo, freq_t *freq);
static int dttsp_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
static int dttsp_set_conf(RIG *rig, token_t token, const char *val);
static int dttsp_get_conf(RIG *rig, token_t token, char *val);
static int dttsp_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val);
static int dttsp_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val);
static int dttsp_set_func(RIG *rig, vfo_t vfo, setting_t func, int status);
static int dttsp_set_ant(RIG * rig, vfo_t vfo, ant_t ant);
static int dttsp_set_rit(RIG *rig, vfo_t vfo, shortfreq_t rit);
static int dttsp_get_rit(RIG *rig, vfo_t vfo, shortfreq_t *rit);
#define TOK_TUNER_MODEL TOKEN_BACKEND(1)
const struct confparams dttsp_cfg_params[] = {
{ TOK_TUNER_MODEL, "tuner_model", "Tuner model", "Hamlib rig tuner model number",
"1" /* RIG_MODEL_DUMMY */, RIG_CONF_NUMERIC, { /* .n = */ { 0, 100000, 1 } }
},
/*
* TODO: IF_center_freq, etc.
*/
{ RIG_CONF_END, NULL, }
};
/*
* DttSP dsr-core rig capabilities.
*/
#define DTTSP_FUNC (RIG_FUNC_NB|RIG_FUNC_ANF|RIG_FUNC_NR|RIG_FUNC_MUTE)
#define DTTSP_LEVEL (RIG_LEVEL_RAWSTR|RIG_LEVEL_STRENGTH|RIG_LEVEL_AGC)
#define DTTSP_PARM RIG_PARM_NONE
#define DTTSP_VFO_OP RIG_OP_NONE
#define DTTSP_SCAN RIG_SCAN_NONE
#define DTTSP_VFO (RIG_VFO_A)
#define DTTSP_MODES (RIG_MODE_FM|RIG_MODE_AM|RIG_MODE_SAM|RIG_MODE_DSB| \
RIG_MODE_CW|RIG_MODE_CWR|RIG_MODE_SSB)
enum dttsp_mode_e
{ LSB, USB, DSB, CWL, CWU, FMN, AM, DIGU, SPEC, DIGL, SAM, DRM };
static const struct hamlib_vs_dttsp {
rmode_t hamlib_mode;
enum dttsp_mode_e dttsp_mode;
} hamlib_vs_dttsp_modes[] = {
{ RIG_MODE_USB, USB },
{ RIG_MODE_LSB, LSB },
{ RIG_MODE_CW, CWU },
{ RIG_MODE_CWR, CWL },
{ RIG_MODE_AM, AM },
{ RIG_MODE_SAM, SAM },
{ RIG_MODE_FM, FMN },
{ RIG_MODE_DSB, DSB },
};
#define HAMLIB_VS_DTTSP_MODES_COUNT (sizeof(hamlib_vs_dttsp_modes)/sizeof(struct hamlib_vs_dttsp))
#define DTTSP_REAL_STR_CAL { 16, \
{ \
{ -73, -73 }, /* S0 */ \
{ 50, 40 }, /* +40 */ \
} }
const struct rig_caps dttsp_rig_caps = {
.rig_model = RIG_MODEL_DTTSP,
.model_name = "DttSP IPC",
.mfg_name = "DTTS Microwave Society",
.version = "0.1",
.copyright = "GPL",
.status = RIG_STATUS_ALPHA,
.rig_type = RIG_TYPE_COMPUTER,
.targetable_vfo = RIG_TARGETABLE_ALL,
.ptt_type = RIG_PTT_RIG,
.dcd_type = RIG_DCD_RIG,
.port_type = RIG_PORT_DEVICE,
.has_get_func = DTTSP_FUNC,
.has_set_func = DTTSP_FUNC,
.has_get_level = DTTSP_LEVEL,
.has_set_level = RIG_LEVEL_SET(DTTSP_LEVEL),
.has_get_parm = DTTSP_PARM,
.has_set_parm = RIG_PARM_SET(DTTSP_PARM),
.ctcss_list = NULL,
.dcs_list = NULL,
.chan_list = {
RIG_CHAN_END,
},
.scan_ops = DTTSP_SCAN,
.vfo_ops = DTTSP_VFO_OP,
.transceive = RIG_TRN_OFF,
.attenuator = { RIG_DBLST_END, },
.preamp = { RIG_DBLST_END, },
/* In fact, RX and TX ranges are dependant on the tuner */
.rx_range_list1 = { {.start=kHz(150),.end=MHz(1500),.modes=DTTSP_MODES,
.low_power=-1,.high_power=-1,DTTSP_VFO},
RIG_FRNG_END, },
.tx_range_list1 = { RIG_FRNG_END, }, /* TODO */
.rx_range_list2 = { {.start=kHz(150),.end=MHz(1500),.modes=DTTSP_MODES,
.low_power=-1,.high_power=-1,DTTSP_VFO},
RIG_FRNG_END, },
.tx_range_list2 = { RIG_FRNG_END, }, /* TODO */
.tuning_steps = { {DTTSP_MODES,1}, {DTTSP_MODES,RIG_TS_ANY}, RIG_TS_END, },
.filters = {
{RIG_MODE_SSB|RIG_MODE_CW|RIG_MODE_CWR, kHz(2.4)},
{RIG_MODE_AM|RIG_MODE_DSB|RIG_MODE_SAM, kHz(8)},
{RIG_MODE_FM, kHz(15)},
{DTTSP_MODES, RIG_FLT_ANY},
RIG_FLT_END,
},
.str_cal = DTTSP_REAL_STR_CAL,
.priv = NULL,
.rig_init = dttsp_ipc_init,
.rig_cleanup = dttsp_ipc_cleanup,
.rig_open = dttsp_ipc_open,
.rig_close = dttsp_ipc_close,
.cfgparams = dttsp_cfg_params,
.set_conf = dttsp_set_conf,
.get_conf = dttsp_get_conf,
.set_freq = dttsp_set_freq,
.get_freq = dttsp_get_freq,
.set_mode = dttsp_set_mode,
.set_level = dttsp_set_level,
.get_level = dttsp_get_level,
.set_func = dttsp_set_func,
.set_rit = dttsp_set_rit,
.get_rit = dttsp_get_rit,
.set_ant = dttsp_set_ant,
};
static int send_command(RIG *rig, const char *cmdstr, size_t buflen)
{
int ret;
if (rig->state.rigport.type.rig == RIG_PORT_NETWORK) {
ret = -RIG_ENIMPL;
} else {
/* IPC */
ret = write_block (&rig->state.rigport, cmdstr, buflen);
}
return ret;
}
static int fetch_meter (RIG *rig, int *label, float *data, int npts)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv;
int ret, buf_len;
char buf[sizeof(float)*MAXMETERPTS*MAXRX];
if (priv->meter_port.type.rig == RIG_PORT_NETWORK) {
#if 0
if (!select(cp->sock + 1, &fds, 0, 0, &tv))
return -1;
if (recvfrom(cp->sock, cp->buff, cp->size, cp->flags,
(struct sockaddr *) &cp->clnt, &cp->clen) <= 0)
return -2;
#endif
buf_len = sizeof(int) + npts * sizeof(float);
ret = read_block(&priv->meter_port, buf, buf_len);
if (ret != buf_len)
ret = -RIG_EIO;
/* copy payload back to client space */
memcpy((char *) label, buf, sizeof(int));
memcpy((char *) data, buf + sizeof(int), npts * sizeof(float));
} else {
/* IPC */
buf_len = sizeof(int);
ret = read_block(&priv->meter_port, (char*)label, buf_len);
if (ret != buf_len)
ret = -RIG_EIO;
if (ret < 0)
return ret;
buf_len = sizeof(float) * npts;
ret = read_block(&priv->meter_port, (char*)data, buf_len);
if (ret != buf_len)
ret = -RIG_EIO;
if (ret < 0)
return ret;
}
return ret;
}
/*
* Assumes rig!=NULL, rig->state.priv!=NULL
*/
int dttsp_set_conf(RIG *rig, token_t token, const char *val)
{
struct dttsp_priv_data *priv;
struct rig_state *rs;
rs = &rig->state;
priv = (struct dttsp_priv_data*)rs->priv;
switch(token) {
case TOK_TUNER_MODEL:
priv->tuner_model = atoi(val);
break;
default:
/* if it's not for the dttsp backend, maybe it's for the tuner */
if (priv->tuner)
return rig_set_conf(priv->tuner, token, val);
else
return -RIG_EINVAL;
}
return RIG_OK;
}
/*
* assumes rig!=NULL,
* Assumes rig!=NULL, rig->state.priv!=NULL
* and val points to a buffer big enough to hold the conf value.
*/
int dttsp_get_conf(RIG *rig, token_t token, char *val)
{
struct dttsp_priv_data *priv;
struct rig_state *rs;
rs = &rig->state;
priv = (struct dttsp_priv_data*)rs->priv;
switch(token) {
case TOK_TUNER_MODEL:
sprintf(val, "%d", priv->tuner_model);
break;
default:
/* if it's not for the dttsp backend, maybe it's for the tuner */
if (priv->tuner)
return rig_get_conf(priv->tuner, token, val);
else
return -RIG_EINVAL;
}
return RIG_OK;
}
int dttsp_ipc_init(RIG *rig)
{
struct dttsp_priv_data *priv;
const char *cmdpath;
char *p;
priv = (struct dttsp_priv_data*)malloc(sizeof(struct dttsp_priv_data));
if (!priv)
return -RIG_ENOMEM;
rig->state.priv = (void*)priv;
rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__ );
priv->tuner = NULL;
priv->tuner_model = RIG_MODEL_DUMMY;
priv->IF_center_freq = 0;
p = getenv ( "SDR_DEFRATE" );
if (p)
priv->sample_rate = atoi(p);
else
priv->sample_rate = DEFAULT_SAMPLE_RATE;
cmdpath = getenv ( "SDR_PARMPATH" );
if (!cmdpath)
cmdpath = DEFAULT_DTTSP_CMD_PATH;
strncpy(rig->state.rigport.pathname, cmdpath, FILPATHLEN);
return RIG_OK;
}
int dttsp_ipc_open(RIG *rig)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv;
int ret;
char *p;
char *meterpath;
rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__);
/*
* prevent l8ps
*/
if (priv->tuner_model == RIG_MODEL_DTTSP) {
return -RIG_ECONF;
}
priv->tuner = rig_init(priv->tuner_model);
if (!priv->tuner) {
/* FIXME: wrong rig model? */
return -RIG_ENOMEM;
}
ret = rig_open(priv->tuner);
if (ret != RIG_OK)
{
rig_cleanup(priv->tuner);
priv->tuner = NULL;
return ret;
}
/* open DttSP meter pipe */
p = getenv ( "SDR_METERPATH" );
if (!p) {
meterpath = priv->meter_port.pathname;
strncpy(meterpath, rig->state.rigport.pathname, FILPATHLEN);
p = strrchr(meterpath, '/');
strcpy(p+1, "SDRmeter");
p = meterpath;
}
if (!p) {
/* disabled */
priv->meter_port.fd = -1;
} else {
priv->meter_port.type.rig = RIG_PORT_DEVICE;
ret = port_open(&priv->meter_port);
if (ret < 0)
return ret;
}
/* TODO:
* copy priv->tuner->rx_range/tx_range to rig->state
*/
#if 1
rig->state.has_set_func |= priv->tuner->state.has_set_func;
rig->state.has_get_func |= priv->tuner->state.has_get_func;
rig->state.has_set_level |= priv->tuner->state.has_set_level;
rig->state.has_get_level |= priv->tuner->state.has_get_level;
rig->state.has_set_parm |= priv->tuner->state.has_set_parm;
rig->state.has_get_parm |= priv->tuner->state.has_get_parm;
#endif
/* Because model dummy has funky init value */
if (priv->tuner_model == RIG_MODEL_DUMMY)
dttsp_set_freq(rig, RIG_VFO_CURR, priv->IF_center_freq);
dttsp_set_func(rig, RIG_VFO_CURR, RIG_FUNC_MUTE, 0);
return RIG_OK;
}
int dttsp_ipc_close(RIG *rig)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv;
rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__);
port_close(&priv->meter_port, priv->meter_port.type.rig);
rig_close(priv->tuner);
return RIG_OK;
}
int dttsp_ipc_cleanup(RIG *rig)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv;
rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__);
if (priv->tuner)
rig_cleanup(priv->tuner);
priv->tuner = NULL;
if (rig->state.priv)
free(rig->state.priv);
rig->state.priv = NULL;
return RIG_OK;
}
/*
* rig_set_freq is a good candidate for the GNUradio GUI setFrequency callback?
*/
int dttsp_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data *)rig->state.priv;
freq_t tuner_freq;
int ret;
char fstr[20];
char buf[32];
int buf_len;
shortfreq_t max_delta;
freq_t freq_offset;
max_delta = priv->sample_rate/2 - kHz(2);
sprintf_freq(fstr, freq);
rig_debug(RIG_DEBUG_TRACE,"%s called: %s %s\n",
__FUNCTION__, rig_strvfo(vfo), fstr);
ret = rig_get_freq(priv->tuner, RIG_VFO_CURR, &tuner_freq);
if (ret != RIG_OK)
return ret;
freq_offset = freq - tuner_freq;
/* TODO: take into account the mode width and the IF dead zone */
if (fabs(freq_offset) > max_delta)
{
tuner_freq = priv->IF_center_freq + freq -kHz(6);
ret = rig_set_freq(priv->tuner, RIG_VFO_CURR, tuner_freq);
if (ret != RIG_OK)
return ret;
/* reread the tuner freq because some rigs can't tune to exact frequency.
* The rx_delta_f will correct that */
ret = rig_get_freq(priv->tuner, RIG_VFO_CURR, &tuner_freq);
if (ret != RIG_OK)
return ret;
}
priv->rx_delta_f = freq - tuner_freq;
sprintf_freq(fstr, tuner_freq);
rig_debug(RIG_DEBUG_TRACE,"%s: tuner=%s, rx_delta=%d Hz\n",
__FUNCTION__, fstr, priv->rx_delta_f);
/* setRxFrequenc */
buf_len = sprintf (buf, "setOsc %d\n", priv->rx_delta_f );
ret = send_command (rig, buf, buf_len);
return ret;
}
int dttsp_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data *)rig->state.priv;
freq_t tuner_freq;
int ret;
ret = rig_get_freq(priv->tuner, RIG_VFO_CURR, &tuner_freq);
if (ret != RIG_OK)
return ret;
rig_debug(RIG_DEBUG_TRACE,"%s called\n", __FUNCTION__);
*freq = tuner_freq - priv->rx_delta_f;
return RIG_OK;
}
static enum dttsp_mode_e rmode2dttsp(rmode_t mode)
{
int i;
for (i=0; i< HAMLIB_VS_DTTSP_MODES_COUNT; i++) {
if (hamlib_vs_dttsp_modes[i].hamlib_mode == mode)
return hamlib_vs_dttsp_modes[i].dttsp_mode;
}
return 0;
}
/*
* WIP
*/
int dttsp_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
char buf[32];
int buf_len;
int ret = RIG_OK;
int filter_l, filter_h;
if (width == RIG_PASSBAND_NORMAL)
width = rig_passband_normal(rig, mode);
sprintf_freq(buf, width);
rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s %s\n",
__FUNCTION__, rig_strrmode(mode), buf);
switch (mode) {
case RIG_MODE_USB:
case RIG_MODE_CW:
filter_l = 10;
filter_h = width;
break;
case RIG_MODE_LSB:
case RIG_MODE_CWR:
filter_l = -width;
filter_h = -10;
break;
case RIG_MODE_AM:
case RIG_MODE_SAM:
case RIG_MODE_FM:
case RIG_MODE_DSB:
filter_l = -width/2;
filter_h = width/2;
break;
default:
return -RIG_EINVAL;
}
/* DttSP set mode */
buf_len = sprintf (buf, "setMode %d\n", rmode2dttsp(mode) );
ret = send_command (rig, buf, buf_len);
buf_len = sprintf (buf, "setFilter %d %d\n", filter_l, filter_h );
ret = send_command (rig, buf, buf_len);
rig_debug(RIG_DEBUG_VERBOSE,"%s: %s\n",
__FUNCTION__, buf);
return ret;
}
static int agc_level2dttsp(enum agc_level_e agc)
{
switch (agc) {
case RIG_AGC_OFF: return 0;
case RIG_AGC_SLOW: return 2;
case RIG_AGC_MEDIUM: return 3;
case RIG_AGC_FAST: return 4;
default:
return 0;
}
return 0;
}
/*
*/
int dttsp_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data *)rig->state.priv;
int ret = RIG_OK;
int buf_len;
char buf[32];
switch (level) {
case RIG_LEVEL_AGC:
buf_len = sprintf (buf, "setRXAGC %d\n", agc_level2dttsp(val.i));
ret = send_command (rig, buf, buf_len);
break;
default:
rig_debug(RIG_DEBUG_TRACE, "%s: level %s, try tuner\n",
__FUNCTION__, rig_strlevel(level));
return rig_set_level(priv->tuner, vfo, level, val);
}
return ret;
}
int dttsp_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data *)rig->state.priv;
int ret = RIG_OK;
int buf_len;
char buf[32];
float rxm[MAXRX][RXMETERPTS];
rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n",__FUNCTION__,
rig_strlevel(level));
switch (level) {
case RIG_LEVEL_RAWSTR:
case RIG_LEVEL_STRENGTH:
buf_len = sprintf (buf, "reqRXMeter %d\n", getpid());
ret = send_command (rig, buf, buf_len);
if (ret < 0)
return ret;
ret = fetch_meter (rig, (int*)buf, (float*)rxm, MAXRX * RXMETERPTS);
if (ret < 0)
return ret;
val->i = (int)rxm[0][0];
if (level == RIG_LEVEL_STRENGTH)
val->i = (int)rig_raw2val(val->i,&rig->state.str_cal);
ret = RIG_OK;
break;
default:
rig_debug(RIG_DEBUG_TRACE, "%s: level %s, try tuner\n",
__FUNCTION__, rig_strlevel(level));
return rig_get_level(priv->tuner, vfo, level, val);
}
return ret;
}
int dttsp_set_func(RIG *rig, vfo_t vfo, setting_t func, int status)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data *)rig->state.priv;
char buf[32];
const char *cmd;
int buf_len;
int ret;
status = status ? 1 : 0;
switch ( func ) {
case RIG_FUNC_MUTE:
cmd = "setRunState";
status = status ? 0 : 2;
break;
case RIG_FUNC_NB:
cmd = "setNB";
break;
case RIG_FUNC_ANF:
cmd = "setANF";
break;
case RIG_FUNC_NR:
cmd = "setNR";
break;
default:
rig_debug(RIG_DEBUG_TRACE, "%s: func %s, try tuner\n",
__FUNCTION__, rig_strfunc(func));
return rig_set_func(priv->tuner, vfo, func, status);
}
buf_len = sprintf (buf, "%s %d\n", cmd, status);
ret = send_command (rig, buf, buf_len);
return ret;
}
/*
* TODO
*/
int dttsp_set_rit(RIG *rig, vfo_t vfo, shortfreq_t rit)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__);
return RIG_OK;
}
int dttsp_get_rit(RIG *rig, vfo_t vfo, shortfreq_t *rit)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__);
return RIG_OK;
}
int dttsp_set_ant(RIG * rig, vfo_t vfo, ant_t ant)
{
struct dttsp_priv_data *priv = (struct dttsp_priv_data *)rig->state.priv;
rig_debug(RIG_DEBUG_TRACE, "%s: ant %d, try tuner\n",
__FUNCTION__, ant);
return rig_set_ant(priv->tuner, vfo, ant);
}