/* * Hamlib backend - SmartSDR TCP on port 4952 * See https://github.com/flexradio/smartsdr-api-docs/wiki/SmartSDR-TCPIP-API * Copyright (c) 2024 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 * */ #include #include #include #include #include "hamlib/rig.h" #include "parallel.h" #include "misc.h" #include "bandplan.h" #include "cache.h" #include "network.h" static int smartsdr_set_freq(RIG *rig, vfo_t vfo, freq_t freq); static int smartsdr_get_freq(RIG *rig, vfo_t vfo, freq_t *freq); //static int smartsdr_reset(RIG *rig, reset_t reset); static int smartsdr_init(RIG *rig); static int smartsdr_open(RIG *rig); static int smartsdr_close(RIG *rig); static int smartsdr_cleanup(RIG *rig); static int smartsdr_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt); static int smartsdr_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt); static int smartsdr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width); static int smartsdr_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width); //static int smartsdr_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val); struct smartsdr_priv_data { int slicenum; // slice 0-7 maps to A-H int seqnum; int ptt; int tx; // when 1 this slice has PTT control double freqA; double freqB; rmode_t modeA; rmode_t modeB; int widthA; int widthB; }; #define DEFAULTPATH "127.0.0.1:4992" #define SMARTSDR_FUNC RIG_FUNC_MUTE #define SMARTSDR_LEVEL RIG_LEVEL_PREAMP #define SMARTSDR_PARM RIG_PARM_NONE #define SMARTSDR_MODES (RIG_MODE_USB|RIG_MODE_LSB|RIG_MODE_PKTUSB|RIG_MODE_PKTLSB|RIG_MODE_CW|RIG_MODE_AM|RIG_MODE_FM|RIG_MODE_FMN|RIG_MODE_SAM) #define SMARTSDR_VFO (RIG_VFO_A) #define SMARTSDR_ANTS 3 static int smartsdr_parse_S(RIG *rig, char *s); struct rig_caps smartsdr_a_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_A), .model_name = "SmartSDR Slice A", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_b_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_B), .model_name = "SmartSDR Slice B", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_c_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_C), .model_name = "SmartSDR Slice C", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_d_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_D), .model_name = "SmartSDR Slice D", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_e_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_E), .model_name = "SmartSDR Slice E", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_f_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_F), .model_name = "SmartSDR Slice F", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_g_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_G), .model_name = "SmartSDR Slice G", #include "smartsdr_caps.h" }; struct rig_caps smartsdr_h_rig_caps = { RIG_MODEL(RIG_MODEL_SMARTSDR_H), .model_name = "SmartSDR Slice H", #include "smartsdr_caps.h" }; /* ************************************************************************* */ int smartsdr_init(RIG *rig) { struct smartsdr_priv_data *priv; struct rig_state *rs = STATE(rig); hamlib_port_t *rp = RIGPORT(rig); ENTERFUNC; rs->priv = (struct smartsdr_priv_data *)calloc(1, sizeof( struct smartsdr_priv_data)); if (!rs->priv) { /* whoops! memory shortage! */ RETURNFUNC(-RIG_ENOMEM); } priv = rs->priv; strncpy(rp->pathname, DEFAULTPATH, sizeof(rp->pathname)); switch (rs->rig_model) { case RIG_MODEL_SMARTSDR_A: priv->slicenum = 0; break; case RIG_MODEL_SMARTSDR_B: priv->slicenum = 1; break; case RIG_MODEL_SMARTSDR_C: priv->slicenum = 2; break; case RIG_MODEL_SMARTSDR_D: priv->slicenum = 3; break; case RIG_MODEL_SMARTSDR_E: priv->slicenum = 4; break; case RIG_MODEL_SMARTSDR_F: priv->slicenum = 5; break; case RIG_MODEL_SMARTSDR_G: priv->slicenum = 6; break; case RIG_MODEL_SMARTSDR_H: priv->slicenum = 7; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unknown rig model=%s\n", __func__, rs->model_name); RETURNFUNC(-RIG_ENIMPL); } priv->ptt = 0; RETURNFUNC(RIG_OK); } // flush any messages in the queue and process them too // return 0 if OK, otherwise SMARTSDR error code static int smartsdr_flush(RIG *rig) { char buf[8192]; int buf_len = 8192; char stopset[1] = { 0x0a }; int len = 0; int retval = RIG_OK; ENTERFUNC; #if 0 // for this flush routine we expect at least one message for sure -- might be more len = read_string(RIGPORT(rig), (unsigned char *)buf, buf_len, stopset, 1, 0, 1); if (buf[0] == 'S') { smartsdr_parse_S(rig, buf); } rig_debug(RIG_DEBUG_VERBOSE, "%s: read %d bytes\n", __func__, len); #endif do { buf[0] = 0; len = network_flush2(RIGPORT(rig), (unsigned char *)stopset, buf, buf_len); if (buf[0] == 'S') { smartsdr_parse_S(rig, buf); } else if (buf[0] == 'R') { sscanf(buf, "R%d", &retval); } else if (strlen(buf) > 0) { rig_debug(RIG_DEBUG_WARN, "%s: Unknown packet type=%s\n", __func__, buf); } } while (len > 0); RETURNFUNC(retval); } static int smartsdr_transaction(RIG *rig, char *buf) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; char cmd[4096]; int retval; if (priv->seqnum > 999999) { priv->seqnum = 0; } if (buf) { sprintf(cmd, "C%d|%s%c", priv->seqnum++, buf, 0x0a); retval = write_block(RIGPORT(rig), (unsigned char *) cmd, strlen(cmd)); if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s: SmartSDR write_block err=0x%x\n", __func__, retval); } } retval = smartsdr_flush(rig); if (retval != 0) { rig_debug(RIG_DEBUG_ERR, "%s: SmartSDR flush err=0x%x\n", __func__, retval); retval = -RIG_EPROTO; } return retval; } int smartsdr_open(RIG *rig) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; char cmd[64]; int loops = 20; ENTERFUNC; // Once we've connected and hit here we should have two messages queued from the initial connect sprintf(cmd, "sub slice %d", priv->slicenum); //sprintf(cmd, "sub slice all"); smartsdr_transaction(rig, cmd); do { hl_usleep(100 * 1000); smartsdr_transaction(rig, NULL); } while (priv->freqA == 0 && --loops > 0); //smartsdr_transaction(rig, "info", buf, sizeof(buf)); //rig_debug(RIG_DEBUG_VERBOSE, "%s: info=%s", __func__, buf); RETURNFUNC(RIG_OK); } int smartsdr_close(RIG *rig) { ENTERFUNC; RETURNFUNC(RIG_OK); } int smartsdr_cleanup(RIG *rig) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; ENTERFUNC; if (priv) { free(priv); } STATE(rig)->priv = NULL; RETURNFUNC(RIG_OK); } #if 0 #if defined(HAVE_PTHREAD) typedef struct smartsdr_data_handler_args_s { RIG *rig; } smartsdr_data_handler_args; typedef struct smartsdr_data_handler_priv_data_s { pthread_t thread_id; smartsdr_data_handler_args args; int smartsdr_data_handler_thread_run; } smartsdr_data_handler_priv_data; void *smartsdr_data_handler(void *arg) { struct smartsdr_priv_data *priv; struct smartsdr_data_handler_args_s *args = (struct smartsdr_data_handler_args_s *) arg; smartsdr_data_handler_priv_data *smartsdr_data_handler_priv; //RIG *rig = args->rig; //const struct rig_state *rs = STATE(rig); //int result; rig_debug(RIG_DEBUG_VERBOSE, "%s: Starting morse data handler thread\n", __func__); while (priv->smartsdr_data_handler_priv_data->smartsdr_data_handler_thread_run) { } pthread_exit(NULL); return NULL; } static int smartsdr_data_handler_start(RIG *rig) { struct smartsdr_priv_data *priv; smartsdr_data_handler_priv_data *smartsdr_data_handler_priv; ENTERFUNC; priv->smartsdr_data_handler_thread_run = 1; priv->smartsdr_data_handler_priv_data = calloc(1, sizeof(smartsdr_data_handler_priv_data)); if (priv->smartsdr_data_handler_priv_data == NULL) { RETURNFUNC(-RIG_ENOMEM); } smartsdr_data_handler_priv = (smartsdr_data_handler_priv_data *) priv->smartsdr_data_handler_priv_data; smartsdr_data_handler_priv->args.rig = rig; int err = pthread_create(&smartsdr_data_handler_priv->thread_id, NULL, smartsdr_data_handler, &smartsdr_data_handler_priv->args); if (err) { rig_debug(RIG_DEBUG_ERR, "%s: pthread_create error: %s\n", __func__, strerror(errno)); RETURNFUNC(-RIG_EINTERNAL); } RETURNFUNC(RIG_OK); fset = 1500 post_demod_bypass = 0 rfgain = 24 tx_ant_list = ANT1, ANT2, XVTA, XVTB } #endif #endif /* Example response to "sub slice 0" 511+511+35 S67319A86|slice 0 in_use=1 sample_rate=24000 RF_frequency=10.137000 client_handle=0x76AF7C73 index_letter=A rit_on=0 rit_freq=0 xit_on=0 xit_freq=0 rxant=ANT2 mode=DIGU wide=0 filter_lo=0 filter_hi=3510 step=10 step_list=1,5,10,20,100,250,500,1000 agc_mode=fast agc_threshold=65 agc_off_level=10 pan=0x40000000 txant=ANT2 loopa=0 loopb=0 qsk=0 dax=1 dax_clients=1 lock=0 tx=1 active=1 audio_level=100 audio_pan=51 audio_mute=1 record=0 play=disabled record_time=0.0 anf=0 anf_level=0 nr=0 nr_level=0 nb=0 nb_lev direct=1 el=50 wnb=0 wnb_level=100 apf=0 apf_level=0 squelch=1 squelch_level=20 diversity=0 diversity_parent=0 diversity_child=0 diversity_index=1342177293 ant_list=ANT1,ANT2,RX_A,RX_B,XVTA,XVTB mode_list=LSB,USB,AM,CW,DIGL,DIGU,SAM,FM,NFM,DFM,RTTY fm_tone_mode=OFF fm_tone_value=67.0 fm_repeater_offset_freq=0.000000 tx_offset_freq=0.000000 repeater_offset_dir=SIMPLEX fm_tone_burst=0 fm_deviation=5000 dfm_pre_de_emphasis=0 post_demod_low=300 post_demod_high=3300 rtty_mark=2125 rtty_shift=170 digl_offset=2210 digu_offset=1500 post_demod_bypass=0 rfgain=24 tx_ant_list=ANT1,ANT2,XVTA,XVTB S67319A86|waveform installed_list= R0|0| */ int smartsdr_set_freq(RIG *rig, vfo_t vfo, freq_t freq) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; char buf[4096]; char cmd[64]; ENTERFUNC; sprintf(cmd, "slice tune %d %.6f autopan=1", priv->slicenum, freq / 1e6); smartsdr_transaction(rig, cmd); rig_debug(RIG_DEBUG_VERBOSE, "%s: set_freq answer: %s", __func__, buf); rig_set_cache_freq(rig, vfo, freq); if (vfo == RIG_VFO_A) { priv->freqA = freq; } else { priv->freqB = freq; } RETURNFUNC(RIG_OK); } static int smartsdr_parse_S(RIG *rig, char *s) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; freq_t freq; char mode[16]; char state[16]; char *s2 = strdup(s); char *sep = "| \n"; char *p = strtok(s2, sep); int gotFreq = 0, gotMode = 0; do { rig_debug(RIG_DEBUG_VERBOSE, "%s: parsing '%s'\n", __func__, p); if (sscanf(p, "RF_frequency=%lf", &freq) == 1) { priv->freqA = freq * 1e6; rig_debug(RIG_DEBUG_VERBOSE, "%s: got freq=%.0f\n", __func__, priv->freqA); gotFreq = 1; rig_set_cache_freq(rig, RIG_VFO_A, priv->freqA); } else if (sscanf(p, "filter_hi=%d\n", &priv->widthA) == 1) { rig_debug(RIG_DEBUG_VERBOSE, "%s: got width=%d\n", __func__, priv->widthA); rig_set_cache_mode(rig, RIG_VFO_A, priv->modeA, priv->widthA); } else if (sscanf(p, "mode=%s\n", mode) == 1) { if (strcmp(mode, "USB") == 0) { priv->modeA = RIG_MODE_USB; } else if (strcmp(mode, "LSB") == 0) { priv->modeA = RIG_MODE_LSB; } else if (strcmp(mode, "DIGU") == 0) { priv->modeA = RIG_MODE_PKTUSB; } else if (strcmp(mode, "DIGL") == 0) { priv->modeA = RIG_MODE_PKTLSB; } else if (strcmp(mode, "AM") == 0) { priv->modeA = RIG_MODE_AM; } else if (strcmp(mode, "CW") == 0) { priv->modeA = RIG_MODE_CW; } else if (strcmp(mode, "SAM") == 0) { priv->modeA = RIG_MODE_SAM; } else if (strcmp(mode, "FM") == 0) { priv->modeA = RIG_MODE_FM; } else if (strcmp(mode, "FMN") == 0) { priv->modeA = RIG_MODE_FMN; } else if (strcmp(mode, "RTTY") == 0) { priv->modeA = RIG_MODE_RTTY; } else { priv->modeA = RIG_MODE_NONE; rig_debug(RIG_DEBUG_ERR, "%s: unknown mode=%s\n", __func__, mode); return -RIG_EPROTO; } rig_set_cache_mode(rig, RIG_VFO_A, priv->modeA, priv->widthA); gotMode = 1; rig_debug(RIG_DEBUG_VERBOSE, "%s: got mode=%s\n", __func__, rig_strrmode(priv->modeA)); } else if (sscanf(p, "state=%s\n", state) == 1) { if (strcmp(state, "TRANSMITTING") == 0) { priv->ptt = 1; } else { priv->ptt = 0; } rig_debug(RIG_DEBUG_VERBOSE, "%s: PTT state=%s, ptt=%d\n", __func__, state, priv->ptt); } else if (sscanf(p, "tx=%d\n", &priv->tx) == 1) { rig_debug(RIG_DEBUG_VERBOSE, "%s: tx=%d\n", __func__, priv->tx); } } while ((p = strtok(NULL, sep))); free(s2); rig_debug(RIG_DEBUG_VERBOSE, "%s gotFreq=%d, gotMode=%d\n", __func__, gotFreq, gotMode); return RIG_OK; } int smartsdr_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; //char cmd[64]; ENTERFUNC; //int retval = -RIG_EINTERNAL; // doing the sub slice causes audio problems //sprintf(cmd, "sub slice %d", priv->slicenum); //sprintf(cmd, "info"); smartsdr_transaction(rig, NULL); if (vfo == RIG_VFO_A || vfo == RIG_VFO_CURR) { *freq = priv->freqA; } else { *freq = priv->freqB; } RETURNFUNC(RIG_OK); } int smartsdr_reset(RIG *rig, reset_t reset) { return -RIG_ENIMPL; } int smartsdr_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; char cmd[64]; char slicechar[] = { '?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; ENTERFUNC; if (priv->ptt && ptt) // abort ptt if we're already transmitting { rig_debug(RIG_DEBUG_ERR, "%s: abort PTT on slice %c, another slice has PTT control\n", __func__, slicechar[priv->slicenum]); RETURNFUNC(-RIG_ENTARGET); } priv->ptt = ptt; if (ptt) { sprintf(cmd, "dax audio set %d tx=1", priv->slicenum + 1); smartsdr_transaction(rig, cmd); rig_debug(RIG_DEBUG_VERBOSE, "%s: slice set answer: %s", __func__, cmd); } sprintf(cmd, "slice set %d tx=1", priv->slicenum); smartsdr_transaction(rig, cmd); sprintf(cmd, "xmit %d", ptt); smartsdr_transaction(rig, cmd); RETURNFUNC(RIG_OK); } int smartsdr_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; ENTERFUNC; smartsdr_transaction(rig, NULL); *ptt = 0; if (priv->tx) { *ptt = priv->ptt; } rig_debug(RIG_DEBUG_VERBOSE, "%s: ptt=%d\n", __func__, *ptt); RETURNFUNC(RIG_OK); } int smartsdr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; char cmd[64]; char *rmode = RIG_MODE_NONE; ENTERFUNC; switch (mode) { case RIG_MODE_CW: rmode = "CW"; break; case RIG_MODE_USB: rmode = "USB"; break; case RIG_MODE_LSB: rmode = "LSB"; break; case RIG_MODE_PKTUSB: rmode = "DIGU"; break; case RIG_MODE_PKTLSB: rmode = "DIGL"; break; case RIG_MODE_AM: rmode = "AM"; break; case RIG_MODE_FM: rmode = "FM"; break; case RIG_MODE_FMN: rmode = "FMN"; break; case RIG_MODE_SAM: rmode = "SAM"; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unknown mode=%s\n", __func__, rig_strrmode(mode)); } sprintf(cmd, "slice set %d mode=%s", priv->slicenum, rmode); smartsdr_transaction(rig, cmd); if (width != RIG_PASSBAND_NOCHANGE) { sprintf(cmd, "filt %d 0 %ld", priv->slicenum, width); } RETURNFUNC(RIG_OK); } int smartsdr_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) { struct smartsdr_priv_data *priv = (struct smartsdr_priv_data *)STATE(rig)->priv; ENTERFUNC; *mode = priv->modeA; *width = priv->widthA; RETURNFUNC(RIG_OK); } #if 0 int sdr1k_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) { rig_debug(RIG_DEBUG_TRACE, "%s: %s %d\n", __func__, rig_strlevel(level), val.i); switch (level) { case RIG_LEVEL_PREAMP: return set_bit(rig, L_EXT, 7, !(val.i == rig->caps->preamp[0])); int smartsdr_set_ptt(RIG * rig, vfo_t vfo, ptt_t ptt) break; default: return -RIG_EINVAL; } } #endif