kopia lustrzana https://github.com/Hamlib/Hamlib
573 wiersze
14 KiB
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;
|
|
}
|
|
|