Hamlib/rigs/kit/hiqsdr.c

573 wiersze
14 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, value_t option);
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 = "20200323.0",
.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 = { {
.startf = kHz(100), .endf = MHz(66), .modes = HIQSDR_MODES,
.low_power = -1, .high_power = -1, HIQSDR_VFO, HIQSDR_ANT
},
RIG_FRNG_END,
},
.tx_range_list1 = { {
.startf = kHz(100), .endf = MHz(66), .modes = HIQSDR_MODES,
.low_power = mW(1), .high_power = mW(50), HIQSDR_VFO, HIQSDR_ANT
},
RIG_FRNG_END,
},
.rx_range_list2 = { {
.startf = kHz(100), .endf = MHz(66), .modes = HIQSDR_MODES,
.low_power = -1, .high_power = -1, HIQSDR_VFO, HIQSDR_ANT
},
RIG_FRNG_END,
},
.tx_range_list2 = { {
.startf = kHz(100), .endf = 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;
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
rig->state.priv = (struct hiqsdr_priv_data *)malloc(sizeof(
struct hiqsdr_priv_data));
if (!rig->state.priv)
{
return -RIG_ENOMEM;
}
priv = rig->state.priv;
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",
HAMLIB_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, value_t option)
{
struct hiqsdr_priv_data *priv = (struct hiqsdr_priv_data *)rig->state.priv;
int ret = RIG_OK;
rig_debug(RIG_DEBUG_VERBOSE, "%s called: %u\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;
}