Add CODAN Envoy and NGS rigs

pull/910/head
Mike Black W9MDB 2021-12-27 11:48:05 -06:00
rodzic 70beeb69e1
commit 0ccf0b480c
6 zmienionych plików z 601 dodań i 1 usunięć

Wyświetl plik

@ -47,7 +47,7 @@ dnl added to AC_CONFIG_FILES near the end of this file. See README.developer
dnl Beware of duplication should a backend directory include both rig and
dnl rotor definitions, e.g. "dummy". Optional backends will not be listed
dnl here but will be added later, e.g. "winradio".
RIG_BACKEND_LIST="rigs/adat rigs/alinco rigs/aor rigs/barrett rigs/dorji rigs/drake rigs/dummy rigs/elad rigs/flexradio rigs/icom rigs/icmarine rigs/jrc rigs/kachina rigs/kenwood rigs/kit rigs/lowe rigs/pcr rigs/prm80 rigs/racal rigs/rft rigs/rs rigs/skanti rigs/tapr rigs/tentec rigs/tuner rigs/uniden rigs/winradio rigs/wj rigs/yaesu"
RIG_BACKEND_LIST="rigs/adat rigs/alinco rigs/aor rigs/barrett rigs/codan rigs/dorji rigs/drake rigs/dummy rigs/elad rigs/flexradio rigs/icom rigs/icmarine rigs/jrc rigs/kachina rigs/kenwood rigs/kit rigs/lowe rigs/pcr rigs/prm80 rigs/racal rigs/rft rigs/rs rigs/skanti rigs/tapr rigs/tentec rigs/tuner rigs/uniden rigs/winradio rigs/wj rigs/yaesu"
ROT_BACKEND_LIST="rotators/amsat rotators/ars rotators/celestron rotators/cnctrk rotators/easycomm rotators/ether6 rotators/fodtrack rotators/gs232a rotators/heathkit rotators/m2 rotators/meade rotators/rotorez rotators/sartek rotators/spid rotators/ts7400 rotators/prosistel rotators/ioptron rotators/satel rotators/radant"
# Amplifiers are all in the amplifiers directory
AMP_BACKEND_LIST="amplifiers/elecraft"
@ -842,6 +842,7 @@ rigs/adat/Makefile
rigs/alinco/Makefile
rigs/aor/Makefile
rigs/barrett/Makefile
rigs/codan/Makefile
rigs/dorji/Makefile
rigs/drake/Makefile
rigs/dummy/Makefile

Wyświetl plik

@ -0,0 +1,6 @@
CODANSRC = codan.c codan.h
noinst_LTLIBRARIES = libhamlib-codan.la
libhamlib_codan_la_SOURCES = $(CODANSRC)
EXTRA_DIST = README.codan Android.mk

Wyświetl plik

@ -0,0 +1,11 @@
CODAN NGT and Envoy Radio Control Process.
User Programming Responsibilities
1. Set Admin password to '' (empty, or NO password)
2. Enable Free TX mode by the sales option available from CODAN USA
3. Must know the serial port connected to "CICS" in the radio
programming. This is most often the GP (general purpose) port which
is a 15-pin D-Sub connector on a flying lead on the back of the radio.
(NGT and Envoy models). Programming must be set to select "CICS" as
the device which is addressed via the GP port.

520
rigs/codan/codan.c 100644
Wyświetl plik

@ -0,0 +1,520 @@
/*
* Hamlib CODAN backend - main file
* Copyright (c) 2021 by Michael Black W9MDB
*
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <hamlib/rig.h>
#include "serial.h"
#include "misc.h"
#include "cal.h"
#include "token.h"
#include "register.h"
#include "codan.h"
#define MAXCMDLEN 32
#define CODAN_VFOS (RIG_VFO_A|RIG_VFO_B)
#define CODAN_MODES (RIG_MODE_USB)
int codan_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt);
static int codan_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt);
int codan_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
int codan_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode,
pbwidth_t *width);
int codan_transaction(RIG *rig, char *cmd, int expected, char **result)
{
char cmd_buf[MAXCMDLEN];
int retval, cmd_len;
struct rig_state *rs = &rig->state;
struct codan_priv_data *priv = rig->state.priv;
rig_debug(RIG_DEBUG_VERBOSE, "%s: cmd=%s\n", __func__, cmd);
cmd_len = snprintf(cmd_buf, sizeof(cmd_buf), "%s%s", cmd, EOM);
rig_flush(&rs->rigport);
retval = write_block(&rs->rigport, (unsigned char *) cmd_buf, cmd_len);
if (retval < 0)
{
return retval;
}
if (expected == 0)
{
// response format is reponse...0x0d0x0a
retval = read_string(&rs->rigport, (unsigned char *) priv->ret_data,
sizeof(priv->ret_data),
"\x0a", 1, 0, 1);
rig_debug(RIG_DEBUG_VERBOSE, "%s: result=%s, resultlen=%d\n", __func__,
priv->ret_data, (int)strlen(priv->ret_data));
if (retval < 0)
{
return retval;
}
}
else
{
retval = read_string(&rs->rigport, (unsigned char *) priv->ret_data,
sizeof(priv->ret_data),
"\x0a", 1, 0, 1);
if (retval < 0)
{
return retval;
}
if (strncmp(priv->ret_data, "LEVELS:", 7) == 0)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: %s\n", __func__, priv->ret_data);
retval = read_string(&rs->rigport, (unsigned char *) priv->ret_data,
sizeof(priv->ret_data),
"\x0a", 1, 0, 1);
rig_debug(RIG_DEBUG_VERBOSE, "%s: %s\n", __func__, priv->ret_data);
}
}
rig_debug(RIG_DEBUG_VERBOSE, "%s: retval=%d\n", __func__, retval);
rig_debug(RIG_DEBUG_VERBOSE, "%s: %s\n", __func__, priv->ret_data);
if (result != NULL)
{
*result = &(priv->ret_data[0]);
rig_debug(RIG_DEBUG_VERBOSE, "%s: returning result=%s\n", __func__,
*result);
}
else
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: no result requested\n", __func__);
}
return RIG_OK;
}
int codan_init(RIG *rig)
{
char *results = NULL;
rig_debug(RIG_DEBUG_VERBOSE, "%s version %s\n", __func__,
rig->caps->version);
// cppcheck claims leak here but it's freed in cleanup
rig->state.priv = (struct codan_priv_data *)calloc(1,
sizeof(struct codan_priv_data));
if (!rig->state.priv)
{
return -RIG_ENOMEM;
}
codan_transaction(rig, "login admin ''\r", 0, NULL);
codan_transaction(rig, "login\r", 1, &results);
return RIG_OK;
}
int codan_cleanup(RIG *rig)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
if (!rig)
{
return -RIG_EINVAL;
}
if (rig->state.priv)
{
free(rig->state.priv);
}
rig->state.priv = NULL;
return RIG_OK;
}
/*
* codan_get_mode
* Assumes rig!=NULL
*/
int codan_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width)
{
char *result = NULL;
char modeA[8], modeB[8];
int widthA, widthB;
int retval;
rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s\n", __func__, rig_strvfo(vfo));
retval = codan_transaction(rig, "mode\r", 0, &result);
if (retval != RIG_OK)
{
rig_debug(RIG_DEBUG_ERR, "%s: bad response=%s\n", __func__, result);
return retval;
}
rig_debug(RIG_DEBUG_VERBOSE, "%s: result=%s", __func__, result);
int n = sscanf(result, "MODE: %[A-Z], %[A-Z], %d, %d", modeA, modeB, &widthA,
&widthB);
if (n != 4)
{
rig_debug(RIG_DEBUG_ERR, "%s: sscanf expected 4, got %d, %s\n", __func__, n,
result);
return -RIG_EPROTO;
}
if (strncmp(modeA, "USB", 3) == 0) { *mode = RIG_MODE_USB; }
else if (strncmp(modeA, "LSB", 3) == 0) { *mode = RIG_MODE_LSB; }
else
{
rig_debug(RIG_DEBUG_ERR, "%s: Unknown mode=%s'\n", __func__, modeA)
return -RIG_EPROTO;
}
*width = widthA; // we'll default this to 3000 for now
rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s mode=%s width=%d\n", __func__,
rig_strvfo(vfo), rig_strrmode(*mode), (int)*width);
return RIG_OK;
}
/*
* codan_set_mode
* Assumes rig!=NULL
*/
int codan_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
char cmd_buf[32], *ttmode;
char *response = NULL;
int retval;
rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s mode=%s width=%d\n", __func__,
rig_strvfo(vfo), rig_strrmode(mode), (int)width);
switch (mode)
{
case RIG_MODE_USB:
ttmode = "USBW";
break;
case RIG_MODE_LSB:
ttmode = "LSBW";
break;
default:
rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode %s\n", __func__,
rig_strrmode(mode));
return -RIG_EINVAL;
}
sprintf((char *) cmd_buf, "mode %s\r", ttmode);
retval = codan_transaction(rig, cmd_buf, 0, &response);
if (retval < 0)
{
return retval;
}
return RIG_OK;
}
/*
* codan_set_freq
* assumes rig!=NULL, rig->state.priv!=NULL
*/
int codan_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
char cmd_buf[MAXCMDLEN];
int retval;
rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s freq=%.0f\n", __func__,
rig_strvfo(vfo), freq);
// Purportedly can't do split so we just set VFOB=VFOA
sprintf(cmd_buf, "connect tcvr rf %.0f %.0f\r", freq, freq);
char *response = NULL;
retval = codan_transaction(rig, cmd_buf, 0, &response);
if (retval < 0)
{
return retval;
}
return retval;
}
/*
* codan_get_freq
* Assumes rig!=NULL, rig->state.priv!=NULL, freq!=NULL
*/
int codan_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
int retval;
char *response = NULL;
rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s\n", __func__, rig_strvfo(vfo));
*freq = 0;
retval = codan_transaction(rig, "freq\r", 0, &response);
if (retval != RIG_OK)
{
rig_debug(RIG_DEBUG_ERR, "%s: invalid response=%s\n", __func__, response);
return retval;
}
retval = sscanf(response, "FREQ: %lg", freq);
if (retval != 1)
{
rig_debug(RIG_DEBUG_ERR, "%s: Unable to parse response\n", __func__);
return -RIG_EPROTO;
}
return RIG_OK;
}
/*
* codan_get_ptt
* Assumes rig!=NULL
*/
int codan_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt)
{
int retval;
char *response = NULL;
rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s\n", __func__, rig_strvfo(vfo));
retval = codan_transaction(rig, "connect tcvr rf ptt", 0, &response);
if (retval != RIG_OK)
{
rig_debug(RIG_DEBUG_ERR, "%s: error response?='%s'\n", __func__, response);
return retval;
}
char *p = strstr(response, "Ptt");
if (p)
{
if (strcmp(p, "Ptt=Off") == 0) { *ptt = 0; }
else { *ptt = 1; }
}
else
{
rig_debug(RIG_DEBUG_ERR, "%s: unable to find Ptt in %s\n", __func__, response);
return -RIG_EPROTO;
}
return RIG_OK;
}
/*
* codan_set_ptt
* Assumes rig!=NULL
*/
int codan_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt)
{
int retval;
char cmd_buf[MAXCMDLEN];
char *response;
rig_debug(RIG_DEBUG_VERBOSE, "%s: ptt=%d\n", __func__, ptt);
sprintf(cmd_buf, "connect tcvr rf ptt %s\r", ptt == 0 ? "off" : "on");
response = NULL;
retval = codan_transaction(rig, cmd_buf, 0, &response);
if (retval < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s: invalid response=%s\n", __func__, response);
return retval;
}
rig_debug(RIG_DEBUG_VERBOSE, "%s: cmd result=%s\n", __func__, response);
return RIG_OK;
}
const struct rig_caps envoy_caps =
{
RIG_MODEL(RIG_MODEL_CODAN_ENVOY),
.model_name = "Envoy",
.mfg_name = "CODAN",
.version = BACKEND_VER ".0",
.copyright = "LGPL",
.status = RIG_STATUS_ALPHA,
.rig_type = RIG_TYPE_TRANSCEIVER,
.targetable_vfo = RIG_TARGETABLE_FREQ,
.ptt_type = RIG_PTT_RIG,
.dcd_type = RIG_DCD_NONE,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 9600,
.serial_rate_max = 115200,
.serial_data_bits = 8,
.serial_stop_bits = 1,
.serial_parity = RIG_PARITY_NONE,
.serial_handshake = RIG_HANDSHAKE_NONE,
.write_delay = 0,
.post_write_delay = 50,
.timeout = 1000,
.retry = 3,
.has_get_func = RIG_FUNC_NONE,
.has_set_func = RIG_FUNC_NONE,
.has_get_level = RIG_LEVEL_NONE,
.has_set_level = RIG_LEVEL_NONE,
.has_get_parm = RIG_PARM_NONE,
.has_set_parm = RIG_PARM_NONE,
.transceive = RIG_TRN_RIG,
.rx_range_list1 = {{
.startf = kHz(1600), .endf = MHz(30), .modes = CODAN_MODES,
.low_power = -1, .high_power = -1, CODAN_VFOS, RIG_ANT_1
},
RIG_FRNG_END,
},
.rx_range_list2 = {RIG_FRNG_END,},
.tx_range_list1 = {RIG_FRNG_END,},
.tx_range_list2 = {RIG_FRNG_END,},
.tuning_steps = { {CODAN_MODES, 1}, {CODAN_MODES, RIG_TS_ANY}, RIG_TS_END, },
.filters = {
{RIG_MODE_SSB, kHz(2.4)},
{RIG_MODE_SSB, kHz(0.5)},
{RIG_MODE_SSB, kHz(2.7)},
{RIG_MODE_SSB, kHz(3.0)},
RIG_FLT_END,
},
.priv = NULL,
.rig_init = codan_init,
.rig_cleanup = codan_cleanup,
.set_freq = codan_set_freq,
.get_freq = codan_get_freq,
.set_mode = codan_set_mode,
.get_mode = codan_get_mode,
.set_ptt = codan_set_ptt,
.get_ptt = codan_get_ptt,
};
const struct rig_caps ngs_caps =
{
RIG_MODEL(RIG_MODEL_CODAN_NGT),
.model_name = "NGT",
.mfg_name = "CODAN",
.version = BACKEND_VER ".0",
.copyright = "LGPL",
.status = RIG_STATUS_ALPHA,
.rig_type = RIG_TYPE_TRANSCEIVER,
.targetable_vfo = RIG_TARGETABLE_FREQ,
.ptt_type = RIG_PTT_RIG,
.dcd_type = RIG_DCD_NONE,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 9600,
.serial_rate_max = 115200,
.serial_data_bits = 8,
.serial_stop_bits = 1,
.serial_parity = RIG_PARITY_NONE,
.serial_handshake = RIG_HANDSHAKE_NONE,
.write_delay = 0,
.post_write_delay = 50,
.timeout = 1000,
.retry = 3,
.has_get_func = RIG_FUNC_NONE,
.has_set_func = RIG_FUNC_NONE,
.has_get_level = RIG_LEVEL_NONE,
.has_set_level = RIG_LEVEL_NONE,
.has_get_parm = RIG_PARM_NONE,
.has_set_parm = RIG_PARM_NONE,
.transceive = RIG_TRN_RIG,
.rx_range_list1 = {{
.startf = kHz(1600), .endf = MHz(30), .modes = CODAN_MODES,
.low_power = -1, .high_power = -1, CODAN_VFOS, RIG_ANT_1
},
RIG_FRNG_END,
},
.rx_range_list2 = {RIG_FRNG_END,},
.tx_range_list1 = {RIG_FRNG_END,},
.tx_range_list2 = {RIG_FRNG_END,},
.tuning_steps = { {CODAN_MODES, 1}, {CODAN_MODES, RIG_TS_ANY}, RIG_TS_END, },
.filters = {
{RIG_MODE_SSB, kHz(2.4)},
{RIG_MODE_SSB, kHz(0.5)},
{RIG_MODE_SSB, kHz(2.7)},
{RIG_MODE_SSB, kHz(3.0)},
RIG_FLT_END,
},
.priv = NULL,
.rig_init = codan_init,
.rig_cleanup = codan_cleanup,
.set_freq = codan_set_freq,
.get_freq = codan_get_freq,
.set_mode = codan_set_mode,
.get_mode = codan_get_mode,
.set_ptt = codan_set_ptt,
.get_ptt = codan_get_ptt,
};
DECLARE_INITRIG_BACKEND(codan)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: _init called\n", __func__);
rig_register(&envoy_caps);
rig_register(&ngt_caps);
rig_debug(RIG_DEBUG_VERBOSE, "%s: _init back from rig_register\n", __func__);
return RIG_OK;
}

60
rigs/codan/codan.h 100644
Wyświetl plik

@ -0,0 +1,60 @@
/*
* Hamlib Barrett backend - main header
* Copyright (c) 2017 by Michael Black W9MDB
*
*
* 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
*
*/
#ifndef _CODAN_H
#define _CODAN_H 1
#include "hamlib/rig.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#define BACKEND_VER "20181111"
#define EOM "\x0d"
#define TRUE 1
#define FALSE 0
// For the current implemented command set 64 is long enough
// This will need a lot more room for some channel commands like IDFA which return all channels
// But that would 9999*41 or 406KB so didn't do that right now
#define CODAN_DATA_LEN 64
extern const struct rig_caps envoy_caps;
extern const struct rig_caps ngt_caps;
struct codan_priv_data {
char cmd_str[CODAN_DATA_LEN]; /* command string buffer */
char ret_data[CODAN_DATA_LEN]; /* returned data--max value, most are less */
};
extern int codan_transaction(RIG *rig, char *cmd, int expected, char **result);
extern int codan_init(RIG *rig);
extern int codan_cleanup(RIG *rig);
extern int codan_set_freq(RIG *rig, vfo_t vfo, freq_t freq);
extern int codan_get_freq(RIG *rig, vfo_t vfo, freq_t *freq);
extern int codan_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
extern int codan_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode,
pbwidth_t *width);
extern int codan_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt);
#endif /* _CODAN_H */

Wyświetl plik

@ -90,6 +90,7 @@ DEFINE_INITRIG_BACKEND(adat);
DEFINE_INITRIG_BACKEND(dorji);
DEFINE_INITRIG_BACKEND(barrett);
DEFINE_INITRIG_BACKEND(elad);
DEFINE_INITRIG_BACKEND(codan);
//! @endcond
#ifdef HAVE_WINRADIO
@ -146,6 +147,7 @@ static struct
{ RIG_DORJI, RIG_BACKEND_DORJI, RIG_FUNCNAMA(dorji) },
{ RIG_BARRETT, RIG_BACKEND_BARRETT, RIG_FUNCNAMA(barrett) },
{ RIG_ELAD, RIG_BACKEND_ELAD, RIG_FUNCNAMA(elad) },
{ RIG_CODAN, RIG_BACKEND_CODAN, RIG_FUNCNAMA(codan) },
{ 0, NULL }, /* end */
};