Hamlib/kit/hiqsdr.c

507 wiersze
13 KiB
C

/*
* Hamlib HiQSDR backend
* Copyright (c) 20012 by Stephane Fillod
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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"
/*
* http://www.hiqsdr.org
*/
/* HiQSDR constants */
#define REFCLOCK 122880000
#define DEFAULT_SAMPLE_RATE 48000
/* V1.1 */
#define CTRL_FRAME_LEN 22
struct hiqsdr_priv_data {
split_t split;
int sample_rate;
double ref_clock;
unsigned char control_frame[CTRL_FRAME_LEN];
};
static int hiqsdr_init(RIG *rig);
static int hiqsdr_cleanup(RIG *rig);
static int hiqsdr_open(RIG *rig);
static int hiqsdr_close(RIG *rig);
static int hiqsdr_set_freq(RIG *rig, vfo_t vfo, freq_t freq);
static int hiqsdr_get_freq(RIG *rig, vfo_t vfo, freq_t *freq);
static int hiqsdr_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo);
static int hiqsdr_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq);
static int hiqsdr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
static int hiqsdr_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt);
static int hiqsdr_set_ant(RIG *rig, vfo_t vfo, ant_t ant);
static int hiqsdr_set_conf(RIG *rig, token_t token, const char *val);
static int hiqsdr_get_conf(RIG *rig, token_t token, char *val);
static int hiqsdr_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val);
static int hiqsdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val);
#define TOK_OSCFREQ TOKEN_BACKEND(1)
#define TOK_SAMPLE_RATE TOKEN_BACKEND(2)
const struct confparams hiqsdr_cfg_params[] = {
{ TOK_OSCFREQ, "osc_freq", "Oscillator freq", "Oscillator frequency of reference clock in Hz",
"122880000", RIG_CONF_NUMERIC, { .n = { 0, MHz(256), 1 } }
},
{ TOK_SAMPLE_RATE, "sample_rate", "Sample rate", "Sample rate",
"48000", RIG_CONF_NUMERIC, { /* .n = */ { 48000, 1920000, 1 } }
},
{ RIG_CONF_END, NULL, }
};
/*
* HiQSDR rig capabilities.
*/
#define HIQSDR_FUNC RIG_FUNC_NONE
#define HIQSDR_LEVEL (RIG_LEVEL_RFPOWER|RIG_LEVEL_PREAMP|RIG_LEVEL_ATT)
#define HIQSDR_PARM RIG_PARM_NONE
#define HIQSDR_VFO_OP RIG_OP_NONE
#define HIQSDR_SCAN RIG_SCAN_NONE
#define HIQSDR_VFO (RIG_VFO_A)
#define HIQSDR_ANT (RIG_ANT_1|RIG_ANT_2)
#define HIQSDR_MODES (RIG_MODE_CW|RIG_MODE_DSB)
const struct rig_caps hiqsdr_caps = {
.rig_model = RIG_MODEL_HIQSDR,
.model_name = "HiQSDR",
.mfg_name = "N2ADR",
.version = "0.1",
.copyright = "LGPL",
.status = RIG_STATUS_UNTESTED,
.rig_type = RIG_TYPE_TUNER,
.targetable_vfo = RIG_TARGETABLE_NONE,
.ptt_type = RIG_PTT_RIG,
.dcd_type = RIG_DCD_NONE,
.port_type = RIG_PORT_UDP_NETWORK,
.timeout = 500,
.has_get_func = HIQSDR_FUNC,
.has_set_func = HIQSDR_FUNC,
.has_get_level = HIQSDR_LEVEL,
.has_set_level = RIG_LEVEL_SET(HIQSDR_LEVEL),
.has_get_parm = HIQSDR_PARM,
.has_set_parm = RIG_PARM_SET(HIQSDR_PARM),
.ctcss_list = NULL,
.dcs_list = NULL,
.chan_list = { RIG_CHAN_END, },
.scan_ops = HIQSDR_SCAN,
.vfo_ops = HIQSDR_VFO_OP,
.transceive = RIG_TRN_OFF,
.attenuator = { 2, 4, 6, 10, 20, 30, 44, RIG_DBLST_END }, // -2dB steps in fact
.preamp = { 10, RIG_DBLST_END, }, // TODO
.rx_range_list1 = { {.start=kHz(100),.end=MHz(66),.modes=HIQSDR_MODES,
.low_power=-1,.high_power=-1,HIQSDR_VFO,HIQSDR_ANT},
RIG_FRNG_END, },
.tx_range_list1 = { {.start=kHz(100),.end=MHz(66),.modes=HIQSDR_MODES,
.low_power=mW(1),.high_power=mW(50),HIQSDR_VFO,HIQSDR_ANT},
RIG_FRNG_END, },
.rx_range_list2 = { {.start=kHz(100),.end=MHz(66),.modes=HIQSDR_MODES,
.low_power=-1,.high_power=-1,HIQSDR_VFO,HIQSDR_ANT},
RIG_FRNG_END, },
.tx_range_list2 = { {.start=kHz(100),.end=MHz(66),.modes=HIQSDR_MODES,
.low_power=mW(1),.high_power=mW(50),HIQSDR_VFO,HIQSDR_ANT},
RIG_FRNG_END, },
.tuning_steps = { {HIQSDR_MODES,1}, RIG_TS_END, },
.filters = {
{RIG_MODE_CW, kHz(2.4)},
{HIQSDR_MODES, RIG_FLT_ANY},
RIG_FLT_END,
},
.priv = NULL,
.rig_init = hiqsdr_init,
.rig_cleanup = hiqsdr_cleanup,
.rig_open = hiqsdr_open,
.rig_close = hiqsdr_close,
.cfgparams = hiqsdr_cfg_params,
.set_conf = hiqsdr_set_conf,
.get_conf = hiqsdr_get_conf,
.set_freq = hiqsdr_set_freq,
.get_freq = hiqsdr_get_freq,
.set_split_freq = hiqsdr_set_split_freq,
.set_split_vfo = hiqsdr_set_split_vfo,
.set_mode = hiqsdr_set_mode,
.set_ptt = hiqsdr_set_ptt,
.set_ant = hiqsdr_set_ant,
.set_level = hiqsdr_set_level,
.get_level = hiqsdr_get_level,
};
static int send_command(RIG *rig)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data*)rig->state.priv;
int ret;
ret = write_block(&rig->state.rigport, (const char*)priv->control_frame, CTRL_FRAME_LEN);
#if 0
ret = read_block(&rig->state.rigport, (char*)priv->control_frame, CTRL_FRAME_LEN);
if (ret != CTRL_FRAME_LEN)
ret = ret < 0 ? ret : -RIG_EPROTO;
#endif
return ret;
}
static unsigned compute_sample_rate(const struct hiqsdr_priv_data *priv)
{
unsigned rx_control;
rx_control = (unsigned)(priv->ref_clock / (8. * 8. * priv->sample_rate)) - 1;
if (rx_control > 39)
rx_control = 39;
return rx_control;
}
/*
* Assumes rig!=NULL, rig->state.priv!=NULL
*/
int hiqsdr_set_conf(RIG *rig, token_t token, const char *val)
{
struct hiqsdr_priv_data *priv;
struct rig_state *rs;
rs = &rig->state;
priv = (struct hiqsdr_priv_data*)rs->priv;
switch(token) {
case TOK_OSCFREQ:
priv->ref_clock = atof(val);
priv->control_frame[12] = compute_sample_rate(priv);
break;
case TOK_SAMPLE_RATE:
priv->sample_rate = atoi(val);
priv->control_frame[12] = compute_sample_rate(priv);
break;
default:
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 hiqsdr_get_conf(RIG *rig, token_t token, char *val)
{
struct hiqsdr_priv_data *priv;
struct rig_state *rs;
rs = &rig->state;
priv = (struct hiqsdr_priv_data*)rs->priv;
switch(token) {
case TOK_OSCFREQ:
sprintf(val, "%f", priv->ref_clock);
break;
case TOK_SAMPLE_RATE:
sprintf(val, "%d", priv->sample_rate);
break;
default:
return -RIG_EINVAL;
}
return RIG_OK;
}
int hiqsdr_init(RIG *rig)
{
struct hiqsdr_priv_data *priv;
priv = (struct hiqsdr_priv_data*)malloc(sizeof(struct hiqsdr_priv_data));
if (!priv)
return -RIG_ENOMEM;
rig->state.priv = (void*)priv;
rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __func__ );
priv->split = RIG_SPLIT_OFF;
priv->ref_clock = REFCLOCK;
priv->sample_rate = DEFAULT_SAMPLE_RATE;
strncpy(rig->state.rigport.pathname, "192.168.2.196:48248", FILPATHLEN - 1);
return RIG_OK;
}
int hiqsdr_open(RIG *rig)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data*)rig->state.priv;
#if 0
const char buf_send_to_me[] = { 0x72, 0x72 };
int ret;
#endif
rig_debug(RIG_DEBUG_TRACE,"%s called\n", __func__);
/* magic value */
priv->control_frame[0] = 'S';
priv->control_frame[1] = 't';
/* zero tune phase */
memset(priv->control_frame+2, 0, 8);
/* TX output level */
priv->control_frame[10] = 120;
/* Tx control: non-CW */
priv->control_frame[11] = 0x02;
/* decimation: 48 kSpls */
priv->control_frame[12] = compute_sample_rate(priv);
/* firmware version */
priv->control_frame[13] = 0x00;
/* X1 connector */
priv->control_frame[14] = 0x00;
/* Attenuator */
priv->control_frame[15] = 0x00;
/* AntSwitch */
priv->control_frame[16] = 0x00;
/* RFU */
memset(priv->control_frame+17, 0, 5);
#if 0
/* Send the samples to me. FIXME: send to port 48247 */
ret = write_block(&rig->state.rigport, buf_send_to_me, sizeof(buf_send_to_me));
if (ret != RIG_OK)
return RIG_OK;
#endif
return RIG_OK;
}
int hiqsdr_close(RIG *rig)
{
rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __func__);
return RIG_OK;
}
int hiqsdr_cleanup(RIG *rig)
{
rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __func__);
if (rig->state.priv)
free(rig->state.priv);
rig->state.priv = NULL;
return RIG_OK;
}
/*
*/
int hiqsdr_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data*)rig->state.priv;
int ret;
double rxphase;
uint32_t rxphase32;
rig_debug(RIG_DEBUG_TRACE,"%s called\n", __func__);
rxphase = (freq/priv->ref_clock) * (1ULL<<32) + 0.5;
rxphase32 = (uint32_t)rxphase;
priv->control_frame[2] = rxphase32 & 0xff;
priv->control_frame[3] = (rxphase32 >> 8) & 0xff;
priv->control_frame[4] = (rxphase32 >> 16) & 0xff;
priv->control_frame[5] = (rxphase32 >> 24) & 0xff;
if (priv->split == RIG_SPLIT_OFF) {
priv->control_frame[6] = priv->control_frame[2];
priv->control_frame[7] = priv->control_frame[3];
priv->control_frame[8] = priv->control_frame[4];
priv->control_frame[9] = priv->control_frame[5];
}
ret = send_command (rig);
return ret;
}
static int hiqsdr_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data*)rig->state.priv;
priv->split = split;
return RIG_OK;
}
int hiqsdr_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data*)rig->state.priv;
int ret;
double rxphase;
uint32_t rxphase32;
rig_debug(RIG_DEBUG_TRACE,"%s called\n", __func__);
rxphase = (tx_freq/priv->ref_clock) * (1ULL<<32) + 0.5;
rxphase32 = (uint32_t)rxphase;
priv->control_frame[6] = rxphase32 & 0xff;
priv->control_frame[7] = (rxphase32 >> 8) & 0xff;
priv->control_frame[8] = (rxphase32 >> 16) & 0xff;
priv->control_frame[9] = (rxphase32 >> 24) & 0xff;
ret = send_command (rig);
return ret;
}
int hiqsdr_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
return -RIG_ENIMPL;
}
int hiqsdr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data *)rig->state.priv;
int ret = RIG_OK;
rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n",
__func__, rig_strrmode(mode));
if (mode == RIG_MODE_CW) {
priv->control_frame[11] = 0x01;
} else {
priv->control_frame[11] = 0x02;
}
ret = send_command (rig);
return ret;
}
int hiqsdr_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data *)rig->state.priv;
int ret = RIG_OK;
rig_debug(RIG_DEBUG_VERBOSE,"%s called: %d\n",
__func__, ptt);
/* not allowed in CW mode */
if (priv->control_frame[11] & 0x01)
return -RIG_ERJCTED;
if (ptt == RIG_PTT_ON) {
priv->control_frame[11] |= 0x08;
} else {
priv->control_frame[11] &= ~0x08;
}
ret = send_command (rig);
return ret;
}
int hiqsdr_set_ant(RIG *rig, vfo_t vfo, ant_t ant)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data *)rig->state.priv;
int ret = RIG_OK;
rig_debug(RIG_DEBUG_VERBOSE,"%s called: %d\n",
__func__, ant);
if (ant == RIG_ANT_2) {
priv->control_frame[16] |= 0x01;
} else {
priv->control_frame[16] &= ~0x01;
}
ret = send_command (rig);
return ret;
}
/*
*/
int hiqsdr_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data *)rig->state.priv;
int ret = RIG_OK;
switch (level) {
case RIG_LEVEL_PREAMP:
if (val.i)
priv->control_frame[14] |= 0x02;
else
priv->control_frame[14] &= ~0x02;
break;
case RIG_LEVEL_ATT:
/* FIXME: val->i should be looked up from the att list */
priv->control_frame[14] = val.i & 0x1f;
break;
case RIG_LEVEL_RFPOWER:
/* TX output level */
priv->control_frame[10] = 0xff & (unsigned)(255 * val.f);
break;
default:
return -RIG_EINVAL;
}
ret = send_command (rig);
return ret;
}
static int hiqsdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
{
return -RIG_ENIMPL;
}