diff --git a/flexradio/dttsp.c b/flexradio/dttsp.c new file mode 100644 index 000000000..4fcdf0eef --- /dev/null +++ b/flexradio/dttsp.c @@ -0,0 +1,698 @@ +/* + * Hamlib DttSP backend - main file + * Copyright (c) 2001-2007 by Stephane Fillod + * + * $Id: dttsp.c,v 1.1 2007-11-07 19:08:30 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 +#include /* Standard input/output definitions */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ +#include /* File control definitions */ +#include /* Error number definitions */ +#include + +#include "hamlib/rig.h" +#include "misc.h" +#include "token.h" +#include "register.h" +#include "cal.h" +#include "flexradio.h" + +/* + * TODO: + * + */ + +#define DEFAULT_DTTSP_CMD_PATH "/dev/shm/SDRcommands" +#define DEFAULT_SAMPLE_RATE 48000 + +/* DttSP constants */ +#define MAXRX 4 +#define RXMETERPTS 5 +#define TXMETERPTS 9 + +struct dttsp_priv_data { + /* tuner providing IF */ + rig_model_t tuner_model; + RIG *tuner; + shortfreq_t IF_center_freq; + + /* DttSP meter handle */ + int meter_fd; + + int sample_rate; + int rx_delta_f; +}; + +static int dttsp_init(RIG *rig); +static int dttsp_cleanup(RIG *rig); +static int dttsp_open(RIG *rig); +static int dttsp_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", + .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, }, + .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, }, + .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, }, + .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_init, + .rig_cleanup = dttsp_cleanup, + .rig_open = dttsp_open, + .rig_close = dttsp_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, +}; + + + +int dttsp_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_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; +} + + +/* + * 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 */ + return rig_set_conf(priv->tuner, token, val); + } + 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 */ + return rig_get_conf(priv->tuner, token, val); + } + return RIG_OK; +} + +int dttsp_open(RIG *rig) +{ + struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv; + int ret; + char *p; + char meterpath[FILPATHLEN]; + + + 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); + return ret; + } + + /* open DttSP meter pipe */ + p = getenv ( "SDR_METERPATH" ); + if (!p) { + strncpy(meterpath, rig->state.rigport.pathname, FILPATHLEN); + p = strrchr(meterpath, '/'); + strcpy(p+1, "SDRmeter"); + p = meterpath; + } + if (!p) { + /* disabled */ + priv->meter_fd = -1; + } else { + priv->meter_fd = open(p, O_RDWR); + } + + + /* 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_close(RIG *rig) +{ + struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv; + + rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__); + + close(priv->meter_fd); + rig_close(priv->tuner); + /* TODO: close meter */ + + return RIG_OK; +} + +int dttsp_cleanup(RIG *rig) +{ + struct dttsp_priv_data *priv = (struct dttsp_priv_data*)rig->state.priv; + + rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__); + + rig_cleanup(priv->tuner); + + 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 = write (rig->state.rigport.fd, buf, buf_len ); + if (ret == buf_len) + ret = RIG_OK; + else + ret = -RIG_EIO; + + 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 = write (rig->state.rigport.fd, buf, buf_len ); + if (ret == buf_len) + ret = RIG_OK; + else + ret = -RIG_EIO; + + buf_len = sprintf (buf, "setFilter %d %d\n", filter_l, filter_h ); + ret = write (rig->state.rigport.fd, buf, buf_len ); + if (ret == buf_len) + ret = RIG_OK; + else + ret = -RIG_EIO; + + 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 = write (rig->state.rigport.fd, buf, buf_len ); + if (ret == buf_len) + ret = RIG_OK; + else + ret = -RIG_EIO; + 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 = write (rig->state.rigport.fd, buf, buf_len ); + if (ret != buf_len) + ret = -RIG_EIO; + + buf_len = sizeof(int); + ret = read(priv->meter_fd, buf, buf_len); + if (ret != buf_len) + ret = -RIG_EIO; + + buf_len = sizeof(float) * MAXRX * RXMETERPTS; + ret = read(priv->meter_fd, rxm, buf_len); + if (ret != buf_len) + ret = -RIG_EIO; + + val->i = 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 = write (rig->state.rigport.fd, buf, buf_len ); + if (ret == buf_len) + ret = RIG_OK; + else + ret = -RIG_EIO; + + 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); +} +