Add Commradio CTX-10

pull/1573/head
Mike Black W9MDB 2024-06-30 14:58:08 -05:00
rodzic 85ab6944bd
commit 98f6c1aea6
10 zmienionych plików z 541 dodań i 1 usunięć

1
NEWS
Wyświetl plik

@ -13,6 +13,7 @@ Version 5.x -- future
* Change FT1000MP Mark V model names to align with FT1000MP
Version 4.6
* Added Commradio CTX-10
* Added Guoehe PMR-171
* Added csntechnoligies.net S.A.T Satellite rotor control
* Added PSTRotator control

Wyświetl plik

@ -66,7 +66,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/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 rigs/gomspace rigs/mds rigs/anytone rigs/motorola"
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 rigs/gomspace rigs/mds rigs/anytone rigs/motorola rigs/commradio"
ROT_BACKEND_LIST="rotators/amsat rotators/apex rotators/ars rotators/celestron rotators/cnctrk rotators/grbltrk rotators/easycomm rotators/ether6 rotators/flir rotators/fodtrack rotators/gs232a rotators/heathkit rotators/m2 rotators/meade rotators/rotorez rotators/sartek rotators/saebrtrack rotators/spid rotators/ts7400 rotators/prosistel rotators/ioptron rotators/satel rotators/radant"
# Amplifiers are all in the amplifiers directory
AMP_BACKEND_LIST="amplifiers/elecraft amplifiers/gemini amplifiers/expert"
@ -904,6 +904,7 @@ rigs/alinco/Makefile
rigs/aor/Makefile
rigs/barrett/Makefile
rigs/codan/Makefile
rigs/commradio/Makefile
rigs/dorji/Makefile
rigs/drake/Makefile
rigs/dummy/Makefile

Wyświetl plik

@ -688,6 +688,12 @@
#define RIG_BACKEND_MOTOROLA "Motorola"
#define RIG_MODEL_MICOM2 RIG_MAKE_MODEL(RIG_MOTOROLA, 1)
/*
* Commradio / AeroStream Communications
*/
#define RIG_COMMRADIO 39
#define RIG_BACKEND_COMMRADIO "commradio"
#define RIG_MODEL_CTX10 RIG_MAKE_MODEL(RIG_COMMRADIO, 1)
//! @endcond

Wyświetl plik

@ -0,0 +1,6 @@
COMMRADIOSRC = commradio.c commradio.h frame.c frame.h ctx10.c
noinst_LTLIBRARIES = libhamlib-commradio.la
libhamlib_commradio_la_SOURCES = $(COMMRADIOSRC)
EXTRA_DIST = Android.mk

Wyświetl plik

@ -0,0 +1,226 @@
/*
* Hamlib CommRadio backend
* idk, copyright and GPL here
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <hamlib/rig.h>
#include <register.h>
#include <serial.h>
#include "misc.h"
#include "commradio.h"
#include "frame.h"
/*
* As far as I can tell, the commands and their structure are the same for the
* CR-1a as they are for the CTX-10, but I don't have a CR1a to test with.
* I'm putting these functions here in case they are reusable.
*/
int commradio_transaction(RIG *rig, const unsigned char *cmd, int cmd_len,
unsigned char *data, int *data_len)
{
int ret = -RIG_EINTERNAL;
struct rig_state *rs;
ENTERFUNC;
rs = &rig->state;
rs->transaction_active = 1;
/*
* Flush is needed until async mode is done. The CTX-10 sends frames every
* time the VFO changes.
*/
rig_flush(&rs->rigport);
int frame_len;
unsigned char frame[3+2*(cmd_len+2)];
size_t rx_len = CR_FRAMELENGTH;
unsigned char rx[rx_len];
frame_len = frame_message(frame, cmd, cmd_len);
ret = write_block(&rs->rigport, frame, frame_len);
if (ret < RIG_OK)
{
goto transaction_quit;
}
const char stopset[] = { CR_EOF };
ret = read_string(&rs->rigport, rx, rx_len-1, stopset, 1, 0, 1);
if (ret < RIG_OK)
{
goto transaction_quit;
}
ret = unpack_frame(data, rx, ret);
if (ret < RIG_OK)
{
goto transaction_quit;
}
*data_len = ret;
//TODO: check for error response 0x11
transaction_quit:
rs->transaction_active = 0;
RETURNFUNC(ret);
}
int commradio_init(RIG *rig)
{
ENTERFUNC;
// I can't think of anything that goes in here yet.
RETURNFUNC(RIG_OK);
}
int commradio_cleanup(RIG *rig)
{
ENTERFUNC;
// dealloc stuff if it gets added to _init
RETURNFUNC(RIG_OK);
}
int commradio_rig_open(RIG *rig)
{
ENTERFUNC;
// Possibly check if our serial port is configured right and we are not
// doing bad things to the GPIO lines
RETURNFUNC(RIG_OK);
}
int commradio_rig_close(RIG *rig)
{
ENTERFUNC;
// i don't really know
RETURNFUNC(RIG_OK);
}
/*
* The CTX-10 sends VFO frequency updates when the knob is turned by the
* operator, so this is really a good case for async events. Unfortunately
* I can't find any good examples of how to do that, so instead just flush
* and send a request...
*/
int commradio_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
const unsigned char cmd[] = {0x32}; // Get frequency request
unsigned char data[CR_FRAMELENGTH];
int data_len;
int ret = -RIG_EINTERNAL;
ENTERFUNC;
ret = commradio_transaction(rig, cmd, 1, data, &data_len);
if(data_len == 5 && (data[0] == 0x33 || data[0] == 0x34))
{
*freq = (data[1] << 24 | data[2] << 16 | data[3] << 8 | data[4]);
ret = RIG_OK;
}
else
{
rig_debug(RIG_DEBUG_ERR, "%s: Unexpected response to 0x32\n", __func__);
}
RETURNFUNC(ret);
}
int commradio_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
unsigned char data[CR_FRAMELENGTH];
int data_len;
int ret = -RIG_EINTERNAL;
ENTERFUNC;
if(freq < 150000 || freq > 30000000)
{
RETURNFUNC(-RIG_EINVAL);
}
uint32_t int_freq = freq;
rig_debug(RIG_DEBUG_VERBOSE, "%s: Got freq=%f, int_freq=%u\n", __func__,
freq, int_freq);
unsigned char cmd[] =
{
0x30, // Set frequency request
0xFF & (int_freq >> 24),
0xFF & (int_freq >> 16),
0xFF & (int_freq >> 8),
0xFF & (int_freq)
};
ret = commradio_transaction(rig, cmd, 5, data, &data_len);
if(data_len == 5 && (data[0] == 0x31 || data[0] == 0x34))
{
uint32_t new_freq = (data[1] << 24 | data[2] << 16 | data[3] << 8 | data[4]);
if(int_freq == new_freq)
{
RETURNFUNC(RIG_OK);
}
else
{
RETURNFUNC(-RIG_ERJCTED);
}
}
// CTX-10 returns 11 02 30 00 00 00 01 if we try to go out of its
// general-coverage frequency range 150kHz - 30MHz. I'm not sure why Hamlib
// even tries to do this, since its defined in the caps...
else
{
rig_debug(RIG_DEBUG_ERR, "%s: Unexpected response to 0x30\n", __func__);
ret = -RIG_ERJCTED;
}
RETURNFUNC(ret);
}
/*
* Stubs. I'm not aware of a way to get or set the mode on the CTX-10.
*/
int commradio_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
return (RIG_OK);
}
int commradio_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width)
{
*mode = RIG_MODE_NONE;
return (RIG_OK);
}
/*
* Stubs. The CTX-10 has only one VFO and split mode doesn't change how it
* responds via the serial port.
*/
int commradio_set_vfo(RIG *rig, vfo_t vfo)
{
return (RIG_OK);
}
int commradio_get_vfo(RIG *rig, vfo_t *vfo)
{
*vfo = RIG_VFO_A;
return (RIG_OK);
}
DECLARE_INITRIG_BACKEND(commradio)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: _init called\n", __func__);
rig_register(&ctx10_caps);
return (RIG_OK);
}
/*
* For some reason, I can't get this to even link without this function.
*/
DECLARE_PROBERIG_BACKEND(commradio)
{
return (RIG_MODEL_NONE);
}

Wyświetl plik

@ -0,0 +1,21 @@
#ifndef _COMMRADIO_H
#define _COMMRADIO_H
int commradio_transaction(RIG *rig, const unsigned char *cmd, int cmd_len,
unsigned char *data, int *data_len);
int commradio_init(RIG *rig);
int commradio_cleanup(RIG *rig);
int commradio_rig_open(RIG *rig);
int commradio_rig_close(RIG *rig);
int commradio_get_freq(RIG *rig, vfo_t vfo, freq_t *freq);
int commradio_set_freq(RIG *rig, vfo_t vfo, freq_t freq);
int commradio_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width);
int commradio_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width);
int commradio_set_vfo(RIG *rig, vfo_t vfo);
int commradio_get_vfo(RIG *rig, vfo_t *vfo);
extern struct rig_caps ctx10_caps;
#endif /* _COMMRADIO_H */

Wyświetl plik

@ -0,0 +1,95 @@
#include <stdlib.h>
#include "hamlib/rig.h"
#include "bandplan.h"
#include "commradio.h"
/*
* The CTX-10 has only one VFO, but can be set into some sort of "split" mode
* where the screen shows two different frequencies.
* So far I have not figured out how to access these via serial.
*/
#define CTX10_VFO (RIG_VFO_A)
#define CTX10_RX_MODES (RIG_MODE_CW|RIG_MODE_SSB|RIG_MODE_AM)
#define CTX10_TX_MODES (RIG_MODE_CW|RIG_MODE_SSB)
struct rig_caps ctx10_caps =
{
RIG_MODEL(RIG_MODEL_CTX10),
.model_name = "CTX-10",
.mfg_name = "Commradio",
.version = "20240118" ".0",
.copyright = "LGPL",
.status = RIG_STATUS_STABLE,
.rig_type = RIG_TYPE_TRANSCEIVER,
.ptt_type = RIG_PTT_NONE,
.dcd_type = RIG_DCD_NONE,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 3000000,
.serial_rate_max = 3000000,
.serial_data_bits = 8,
.serial_stop_bits = 1,
.serial_parity = RIG_PARITY_NONE,
.serial_handshake = RIG_HANDSHAKE_NONE,
.write_delay = 0,
.post_write_delay = 0,
.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,
.level_gran = {},
.parm_gran = {},
.ctcss_list = NULL,
.dcs_list = NULL,
.preamp = { RIG_DBLST_END, },
.attenuator = { RIG_DBLST_END, },
.max_rit = Hz(0),
.max_xit = Hz(0),
.max_ifshift = Hz(0),
.targetable_vfo = 0,
.vfo_ops = (RIG_OP_FROM_VFO|RIG_OP_TO_VFO),
.scan_ops = RIG_SCAN_NONE,
.transceive = RIG_TRN_OFF,
.bank_qty = 0,
.chan_desc_sz = 0,
.chan_list = { RIG_CHAN_END, },
.rx_range_list1 = {
{kHz(150), MHz(30), CTX10_RX_MODES, -1, -1, CTX10_VFO, 0},
RIG_FRNG_END,
},
.tx_range_list1 = {
FRQ_RNG_80m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_60m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_40m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_30m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_20m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_17m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_15m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_12m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
FRQ_RNG_10m_REGION2(CTX10_TX_MODES, W(1), W(10), CTX10_VFO, 0),
},
.tuning_steps = {
{CTX10_RX_MODES, 10},
RIG_TS_END,
},
// .async_data_supported = 1, //TODO: Revisit this
// .decode_event = commradio_decode_event,
.rig_init = commradio_init,
// .rig_cleanup = commradio_cleanup,
// .rig_open = commradio_rig_open,
// .rig_close = commradio_rig_close,
.get_freq = commradio_get_freq,
.set_freq = commradio_set_freq,
.get_mode = commradio_get_mode,
.set_mode = commradio_set_mode,
.get_vfo = commradio_get_vfo,
.set_vfo = commradio_set_vfo,
.hamlib_check_rig_caps = HAMLIB_CHECK_RIG_CAPS
};

Wyświetl plik

@ -0,0 +1,160 @@
/*
* Hamlib CommRadio backend
* idk, copyright and GPL here
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <hamlib/rig.h>
#include <serial.h>
#include "frame.h"
/*
* Brute-force determined to be polynomial = 0x1021.
* Seems to be the same CRC16 as Kermit. Initialized with 0.
*/
uint16_t crc16tab[] = {
0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, 35912, 40385,
44890, 48851, 51820, 56293, 59774, 63735, 4225, 264, 13203, 8730,
22181, 18220, 30135, 25662, 40137, 36160, 49115, 44626, 56045, 52068,
63999, 59510, 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949,
44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, 12675, 8202,
4753, 792, 30631, 26158, 21685, 17724, 48587, 44098, 40665, 36688,
64495, 60006, 55549, 51572, 16900, 21389, 24854, 28831, 1056, 5545,
10034, 14011, 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859,
21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, 57037, 53060,
64991, 60502, 39145, 35168, 48123, 43634, 25350, 29327, 16404, 20893,
9506, 13483, 1584, 6073, 61262, 65223, 52316, 56789, 43370, 47331,
35448, 39921, 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848,
65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, 33800, 38273,
42778, 46739, 49708, 54181, 57662, 61623, 2112, 6601, 11090, 15067,
20068, 24557, 28022, 31999, 38025, 34048, 47003, 42514, 53933, 49956,
61887, 57398, 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774,
42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, 10562, 14539,
2640, 7129, 28518, 32495, 19572, 24061, 46475, 41986, 38553, 34576,
62383, 57894, 53437, 49460, 14787, 10314, 6865, 2904, 32743, 28270,
23797, 19836, 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747,
19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, 54925, 50948,
62879, 58390, 37033, 33056, 46011, 41522, 23237, 19276, 31191, 26718,
7393, 3432, 16371, 11898, 59150, 63111, 50204, 54677, 41258, 45219,
33336, 37809, 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185,
63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, 31687, 27214,
22741, 18780, 15843, 11370, 7921, 3960
};
static uint16_t crc16byte(const unsigned char byte, uint16_t crc)
{
return crc16tab[(crc & 0xFF) ^ byte] ^ (crc >> 8);
}
static uint16_t crc16(const unsigned char *data, int data_len, uint16_t crc)
{
for (int i = 0; i < data_len; i++)
{
crc = crc16byte(data[i], crc);
}
return crc;
}
static int esc_append(unsigned char frame[], int idx, const unsigned char a)
{
switch(a)
{
case CR_SOF:
case CR_EOF:
case CR_ESC:
frame[idx] = CR_ESC;
frame[idx+1] = a ^ 20;
return idx+2;
break;
default:
frame[idx] = a;
return idx+1;
}
}
/*
* frame[] might be 2x the size of data and crc +3 bytes if it is just a long
* string of 0xFC, 0xFD, or 0xFE for some insane reason.
*
*/
int frame_message(unsigned char frame[], const unsigned char *data,
const int data_len)
{
uint16_t crc = 0;
frame[0] = CR_SOF;
frame[1] = 0x21; /* Messages to the radio are always 0x21 */
crc = crc16byte(frame[1], crc);
frame[2] = data[0];
crc = crc16byte(frame[2], crc);
int frame_len = 3;
for (int i = 1; i < data_len; i++)
{
crc = crc16byte(data[i], crc);
frame_len = esc_append(frame, frame_len, data[i]);
}
frame_len = esc_append(frame, frame_len, crc >> 8);
frame_len = esc_append(frame, frame_len, crc & 0xFF);
frame[frame_len] = CR_EOF;
frame_len++;
return frame_len;
}
int unpack_frame(unsigned char msg[], const unsigned char *frame,
const int frame_len)
{
if (frame_len < 5)
{
rig_debug(RIG_DEBUG_ERR,
"%s Got a frame that was too small (<5) to be valid\n",
__func__);
return -RIG_ETRUNC;
}
if ((frame[0] != CR_SOF) || (frame[frame_len-1] != CR_EOF))
{
rig_debug(RIG_DEBUG_ERR,
"%s Tried to unpack a frame without start or end\n", __func__);
return -RIG_EPROTO;
}
if (frame[1] != 0x11)
{
rig_debug(RIG_DEBUG_ERR,
"%s Message address is not for host (0x11)\n", __func__);
return -RIG_EPROTO;
}
int msg_len = 0;
for (int i = 2; i < frame_len; i++)
{
switch (frame[i])
{
case CR_SOF:
return -RIG_EPROTO;
break;
case CR_EOF:
i = frame_len;
break;
case CR_ESC:
i++;
msg[msg_len] = frame[i] ^ 20;
msg_len++;
break;
default:
msg[msg_len] = frame[i];
msg_len++;
}
}
uint16_t msg_crc = (msg[msg_len-2] << 8) | msg[msg_len-1];
msg_len = msg_len-2;
uint16_t crc = crc16(msg, msg_len, crc16byte(frame[1],0));
if (msg_crc != crc)
{
rig_debug(RIG_DEBUG_ERR, "%s CRC check failed. msg_crc=%x, crc=%x\n", __func__, msg_crc, crc);
}
return msg_len;
}

Wyświetl plik

@ -0,0 +1,22 @@
/*
* Hamlib CommRadio backend
* idk, copyright and GPL here
*/
#ifndef _FRAME_H
#define _FRAME_H
#define CR_SOF 0xFE
#define CR_EOF 0xFD
#define CR_ESC 0xFC
//TODO: Find out what the frame length actually is for IQ/spectrum data
#define CR_FRAMELENGTH 256
int frame_message(unsigned char frame[], const unsigned char *data,
int data_len);
int unpack_frame(unsigned char msg[], const unsigned char *frame, int frame_len);
#endif /* _FRAME_H */

Wyświetl plik

@ -91,6 +91,7 @@ DEFINE_INITRIG_BACKEND(gomspace);
DEFINE_INITRIG_BACKEND(mds);
DEFINE_INITRIG_BACKEND(anytone);
DEFINE_INITRIG_BACKEND(motorola);
DEFINE_INITRIG_BACKEND(commradio);
//! @endcond
#ifdef HAVE_WINRADIO
@ -152,6 +153,7 @@ static struct
{ RIG_MDS, RIG_BACKEND_MDS, RIG_FUNCNAMA(mds) },
{ RIG_ANYTONE, RIG_BACKEND_ANYTONE, RIG_FUNCNAMA(anytone) },
{ RIG_MOTOROLA, RIG_BACKEND_MOTOROLA, RIG_FUNCNAMA(motorola) },
{ RIG_COMMRADIO, RIG_BACKEND_COMMRADIO, RIG_FUNCNAM(commradio) },
{ 0, NULL }, /* end */
};