From b8cd8c0a9a4b3bf613a9b3d5a586ec2e5d57c183 Mon Sep 17 00:00:00 2001 From: Michael Black W9MDB Date: Wed, 9 May 2018 23:01:55 -0500 Subject: [PATCH] FLRig thread safety improved and now works WSJT-X split mode with multiple connections --- doc/man1/rigctld.1 | 2 +- dummy/flrig.README | 10 +- dummy/flrig.c | 1369 ++++++++++++++++++++++++++++++-------------- dummy/flrig.h | 3 +- 4 files changed, 960 insertions(+), 424 deletions(-) diff --git a/doc/man1/rigctld.1 b/doc/man1/rigctld.1 index 05d86f800..ae53814e5 100644 --- a/doc/man1/rigctld.1 +++ b/doc/man1/rigctld.1 @@ -41,7 +41,7 @@ sockets. This allows multiple user programs to share one radio (this needs more development). Multiple radios can be controlled on different TCP ports by use of multiple .B rigctld -processes. The syntax of the commands are the same as +processes. Note that multiple processes/ports are also necessary if some clients use extended responses and/or vfo mode. So up to 4 processes/ports may be needed for each combination of extended response/vfo mode. The syntax of the commands are the same as .BR rigctl (1). It is hoped that .B rigctld diff --git a/dummy/flrig.README b/dummy/flrig.README index 4946c3833..47be8bfd6 100644 --- a/dummy/flrig.README +++ b/dummy/flrig.README @@ -1,5 +1,7 @@ -As of version 0.1 this works well with rigs have a full ack/nak protocol as coded in FLRig. So Kenwood, Icom and such. -But Yaesu rigs for example are not stable with applications like WSJT-X since the FLRig driver does not wait or obtain status after frequency change commands for example (fire and forget). Hamlib's Yaesu drivers always send and ID request after such commands and wait for an appropriate response but FLRig does not so can miss commands as a result. Slow operation (manual command from rigctl) works fine. -Rig drivers in FLRig will have to be changed to allow stable operation with fast comm programs like WJST-X. - +As of version 1.0 20180508 this works very well with flrig-1.3.40.50 and WSJT-X split mode. +Log4OM can be run conconcurrently connecting to "rigctld -M 4". +Note that for that Log4OM instance only clients that use extended protocol can connect to the client (since Log4OM uses extended protocol). If you want to use a client that does not have extended protocol you must run another rigctld on a separate TCP port. +The same thing holds for any vfo mode clients. +So you could end up with 4 rigctld's. One for each combination of extended/vfo mode. +Flrig can now handle numerous clients connecting at once thanks to a lot of work done by Dave W1HKJ de Mike W9MDB diff --git a/dummy/flrig.c b/dummy/flrig.c index e156076d6..a96c96426 100644 --- a/dummy/flrig.c +++ b/dummy/flrig.c @@ -1,7 +1,7 @@ - /* * Hamlib FLRig backend - main file * Copyright (c) 2017 by Michael Black W9MDB + * Copyright (c) 2018 by Michael Black W9MDB * * * This library is free software; you can redistribute it and/or @@ -40,25 +40,34 @@ #include "flrig.h" #define DEBUG 1 +#define DEBUG_TRACE DEBUG_VERBOSE #define MAXCMDLEN 8192 +#define MAXXMLLEN 8192 +#define MAXBANDWIDTHLEN 4096 -#define DEFAULTPATH "localhost:12345" +#define DEFAULTPATH "127.0.0.1:12345" -#define FLRIG_VFOS (RIG_VFO_A|RIG_VFO_B|RIG_VFO_TX) +#define FLRIG_VFOS (RIG_VFO_A|RIG_VFO_B) -#define FLRIG_MODES (RIG_MODE_AM | RIG_MODE_CW | RIG_MODE_RTTY | \ - RIG_MODE_SSB | RIG_MODE_FM) +#define FLRIG_MODES (RIG_MODE_AM | RIG_MODE_PKTAM | RIG_MODE_CW | RIG_MODE_CWR |\ + RIG_MODE_RTTY | RIG_MODE_RTTYR |\ + RIG_MODE_PKTLSB | RIG_MODE_PKTUSB |\ + RIG_MODE_SSB | RIG_MODE_LSB |\ + RIG_MODE_FM | RIG_MODE_WFM | RIG_MODE_FMN |RIG_MODE_PKTFM ) -#define RIG_DEBUG_TRACE RIG_DEBUG_VERBOSE +#define streq(s1,s2) (strcmp(s1,s2)==0) static int flrig_init(RIG *rig); -//int flrig_cleanup(RIG *rig); +static int flrig_open(RIG *rig); +static int flrig_close(RIG *rig); +static int flrig_cleanup(RIG *rig); static int flrig_set_freq(RIG *rig, vfo_t vfo, freq_t freq); static int flrig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq); static int flrig_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt); -static int flrig_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, - pbwidth_t width); +static int flrig_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width); +static int flrig_set_split_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width); +static int flrig_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width); static int flrig_get_vfo(RIG *rig, vfo_t *vfo); static int flrig_set_vfo(RIG *rig, vfo_t vfo); static int flrig_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt); @@ -69,9 +78,24 @@ static int flrig_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo); static int flrig_get_split_vfo(RIG *rig, vfo_t vfo, split_t *split, vfo_t *tx_vfo); +static int flrig_set_split_freq_mode(RIG *rig, vfo_t vfo, freq_t freq, rmode_t mode, pbwidth_t width); +static int flrig_get_split_freq_mode(RIG *rig, vfo_t vfo, freq_t *freq, rmode_t *mode, pbwidth_t *width); + +static const char *flrig_get_info(RIG *rig); struct flrig_priv_data { - vfo_t vfo_curr; + vfo_t curr_vfo; + char bandwidths[MAXBANDWIDTHLEN]; /* pipe delimited set returned from flrig */ + int nbandwidths; + char info[8192]; + ptt_t ptt; + split_t split; + rmode_t curr_modeA; + rmode_t curr_modeB; + freq_t curr_freqA; + freq_t curr_freqB; + pbwidth_t curr_widthA; + pbwidth_t curr_widthB; }; const struct rig_caps flrig_caps = { @@ -80,131 +104,109 @@ const struct rig_caps flrig_caps = { .mfg_name = "FLRig", .version = BACKEND_VER, .copyright = "LGPL", - .status = RIG_STATUS_ALPHA, + .status = RIG_STATUS_BETA, .rig_type = RIG_TYPE_TRANSCEIVER, - .targetable_vfo = 0, + .targetable_vfo = RIG_TARGETABLE_FREQ, .ptt_type = RIG_PTT_RIG, .port_type = RIG_PORT_NETWORK, .write_delay = 0, - .post_write_delay = 50, + .post_write_delay = 0, .timeout = 1000, - .retry = 3, + .retry = 5, .has_get_func = RIG_FUNC_NONE, .has_set_func = RIG_FUNC_NONE, - .has_get_level = (RIG_LEVEL_RAWSTR | RIG_LEVEL_STRENGTH), + .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 = { [LVL_CWPITCH] = { .step = { .i = 10 } } }, -// .ctcss_list = common_ctcss_list, -// .dcs_list = full_dcs_list, -// .chan_list = { -// { 0, 18, RIG_MTYPE_MEM, DUMMY_MEM_CAP }, -// { 19, 19, RIG_MTYPE_CALL }, -// { 20, NB_CHAN-1, RIG_MTYPE_EDGE }, -// RIG_CHAN_END, -// }, -// .vfo_ops = DUMMY_VFO_OP, - .transceive = RIG_TRN_RIG, -// .attenuator = { 10, 20, 30, RIG_DBLST_END, }, -// .preamp = { 10, RIG_DBLST_END, }, -// .rx_range_list1 = {{.start = kHz(150),.end = MHz(1500),.modes = FLRIG_MODES, -// .low_power = -1,.high_power = -1, FLRIG_VFOS, RIG_ANT_1 | RIG_ANT_2}, -// RIG_FRNG_END,}, -// .tx_range_list1 = {RIG_FRNG_END,}, -// .rx_range_list2 = {{.start = kHz(150),.end = MHz(1500),.modes = FLRIG_MODES, -// .low_power = -1,.high_power = -1, FLRIG_VFOS, RIG_ANT_1 | RIG_ANT_2}, -// RIG_FRNG_END,}, -// .tx_range_list2 = {RIG_FRNG_END,}, -// .tuning_steps = { {DUMMY_MODES,1}, {DUMMY_MODES,RIG_TS_ANY}, RIG_TS_END, }, -// .filters = { -// {RIG_MODE_SSB | RIG_MODE_CW | RIG_MODE_RTTY, kHz(2.4)}, -// {RIG_MODE_CW, Hz(500)}, -// {RIG_MODE_AM, kHz(8)}, -// {RIG_MODE_AM, kHz(2.4)}, -// {RIG_MODE_FM, kHz(15)}, -// {RIG_MODE_FM, kHz(8)}, -// {RIG_MODE_WFM, kHz(230)}, -// RIG_FLT_END, -// }, -// .max_rit = 9990, -// .max_xit = 9990, -// .max_ifshift = 10000, + .filters = { + RIG_FLT_END + }, + + .rx_range_list1 = {{ + .start = kHz(1),.end = GHz(10),.modes = FLRIG_MODES, + .low_power = -1,.high_power = -1, FLRIG_VFOS, RIG_ANT_1 + }, + RIG_FRNG_END, + }, + .tx_range_list1 = {RIG_FRNG_END,}, + .rx_range_list2 = {{ + .start = kHz(1),.end = GHz(10),.modes = FLRIG_MODES, + .low_power = -1,.high_power = -1, FLRIG_VFOS, RIG_ANT_1 + }, + RIG_FRNG_END, + }, + .tx_range_list2 = {RIG_FRNG_END,}, + .tuning_steps = { {FLRIG_MODES,1}, {FLRIG_MODES,RIG_TS_ANY}, RIG_TS_END, }, .priv = NULL, /* priv */ -// .extlevels = dummy_ext_levels, -// .extparms = dummy_ext_parms, -// .cfgparams = dummy_cfg_params, - .rig_init = flrig_init, -// .rig_cleanup = dummy_cleanup, -// .rig_open = dummy_open, -// .rig_close = dummy_close, - -// .set_conf = dummy_set_conf, -// .get_conf = dummy_get_conf, + .rig_open = flrig_open, + .rig_close = flrig_close, + .rig_cleanup = flrig_cleanup, .set_freq = flrig_set_freq, .get_freq = flrig_get_freq, .set_mode = flrig_set_mode, -// .get_mode = dummy_get_mode, + .get_mode = flrig_get_mode, .set_vfo = flrig_set_vfo, .get_vfo = flrig_get_vfo, - -// .set_powerstat = dummy_set_powerstat, -// .get_powerstat = dummy_get_powerstat, -// .set_level = dummy_set_level, -// .get_level = dummy_get_level, -// .set_func = dummy_set_func, -// .get_func = dummy_get_func, -// .set_parm = dummy_set_parm, -// .get_parm = dummy_get_parm, -// .set_ext_level = dummy_set_ext_level, -// .get_ext_level = dummy_get_ext_level, -// .set_ext_parm = dummy_set_ext_parm, -// .get_ext_parm = dummy_get_ext_parm, - -// .get_info = dummy_get_info, - + .get_info = flrig_get_info, .set_ptt = flrig_set_ptt, .get_ptt = flrig_get_ptt, + .set_split_mode = flrig_set_split_mode, .set_split_freq = flrig_set_split_freq, .get_split_freq = flrig_get_split_freq, -// .set_split_mode = flrig_set_split_mode, -// .get_split_mode = flrig_get_split_mode, .set_split_vfo = flrig_set_split_vfo, .get_split_vfo = flrig_get_split_vfo, -// .set_ant = dummy_set_ant, -// .get_ant = dummy_get_ant, -// .set_bank = dummy_set_bank, -// .set_mem = dummy_set_mem, -// .get_mem = dummy_get_mem, -// .vfo_op = dummy_vfo_op, -// .set_trn = dummy_set_trn, -// .get_trn = dummy_get_trn, + .set_split_freq_mode = flrig_set_split_freq_mode, + .get_split_freq_mode = flrig_get_split_freq_mode }; -DECLARE_INITRIG_BACKEND(flrig) -{ +// Structure for mapping flrig dynmamic modes to hamlib modes +// flrig displays modes as the rig displays them +// hamlib displays modes in generic form +#define MAXMODELEN 8 +struct s_modeMap { + int mode_hamlib; + char mode_flrig[MAXMODELEN]; +}; - rig_debug(RIG_DEBUG_TRACE, "flrig: _init called\n"); - - rig_register(&flrig_caps); - - return RIG_OK; -} +// FLRig will provide us the modes for the selected rig +// We will then put them in this struct +static struct s_modeMap modeMap[]= { + {RIG_MODE_USB,""}, + {RIG_MODE_LSB,""}, + {RIG_MODE_PKTUSB,""}, + {RIG_MODE_PKTLSB,""}, + {RIG_MODE_PKTUSB,""}, + {RIG_MODE_PKTLSB,""}, + {RIG_MODE_AM,""}, + {RIG_MODE_FM,""}, + {RIG_MODE_FMN,""}, + {RIG_MODE_WFM,""}, + {RIG_MODE_CW,""}, + {RIG_MODE_CW,""}, + {RIG_MODE_CWR,""}, + {RIG_MODE_CWR,""}, + {RIG_MODE_RTTY,""}, + {RIG_MODE_RTTYR,""}, + {0,""} +}; +/* + * check_vfo + * No assumptions + */ static int check_vfo(vfo_t vfo) { - switch (vfo) { // Omni VII only has A & B + switch (vfo) { case RIG_VFO_A: break; - case RIG_VFO_B: - break; - case RIG_VFO_TX: + case RIG_VFO_B: break; case RIG_VFO_CURR: @@ -217,70 +219,112 @@ static int check_vfo(vfo_t vfo) return TRUE; } +/* + * vfo_curr + * Assumes rig!=NULL + */ static int vfo_curr(RIG *rig, vfo_t vfo) { int retval = 0; - struct flrig_priv_data *priv = - (struct flrig_priv_data *) rig->state.priv; - retval = (vfo == priv->vfo_curr); + vfo_t vfocurr; + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + // get the current VFO from flrig in case user changed it + if ((retval = flrig_get_vfo(rig, &vfocurr)) != RIG_OK) { + return retval; + } + priv->curr_vfo = vfocurr; + retval = (vfo == vfocurr); return retval; } -// Rather than use some huge XML library we only need a few things -// So we'll hand craft them -static char *xml_build(char *cmd, char *value) +/* Rather than use some huge XML library we only need a few things + * So we'll hand craft them + * xml_build takes a value and return an xml string for FLRig + */ +static char *xml_build(char *cmd, char *value, char *xmlbuf,int xmllen) { - char xml[4096]; - char tmp[32]; - static char xmlpost[4096]; - // Standard 50ms sleep borrowed from ts200.c settings - // Tested with ANAN 100 - usleep(50 * 1000); - sprintf(xmlpost, - "POST /RPC2 HTTP/1.1\n" "User-Agent: XMLRPC++ 0.8\n" - "Host: 127.0.0.1:12345\n" "Content-type: text/xml\n"); - sprintf(xml, "\n"); + char xml[MAXXMLLEN]; + // We want at least a 4K buf to play with + if (xmllen < 4096) { + rig_debug(RIG_DEBUG_ERR, "%s: xmllen < 4096\n"); + return NULL; + } + sprintf(xmlbuf, + "POST /RPC2 HTTP/1.1\r\n" "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:12345\r\n" "Content-type: text/xml\r\n"); + sprintf(xml, "\r\n"); strcat(xml, ""); strcat(xml, cmd); - strcat(xml, ""); + strcat(xml, "\r\n"); if (value && strlen(value) > 0) { strcat(xml, value); } - strcat(xml, "\n"); - strcat(xmlpost, "Content-length: "); - sprintf(tmp, "%d\n\n", (int)strlen(xml)); - strcat(xmlpost, tmp); - strcat(xmlpost, xml); - rig_debug(RIG_DEBUG_VERBOSE, "XML:\n%s", xmlpost); - return xmlpost; + strcat(xml, "\r\n"); + strcat(xmlbuf, "Content-length: "); + char tmp[32]; + sprintf(tmp, "%d\r\n\r\n", (int)strlen(xml)); + strcat(xmlbuf, tmp); + strcat(xmlbuf, xml); + return xmlbuf; } -// This is a very crude xml parse specific to what we need from FLRig -// This will not handle array returns for example yet...only simple values -// It simply grabs the first element before the first closing tag -// This works for strings, doubles, and I4-type values -char *xml_parse2(char *xml, char *value, int valueLen) +/* This is a very crude xml parse specific to what we need from FLRig + * This works for strings, doubles, I4-type values, and arrays + * Arrays are returned pipe delimited + */ +static char *xml_parse2(char *xml, char *value, int valueLen) { - char *pstart = strchr(xml, '<'); - - while (pstart[0] == '<' && pstart[1] != '/') { - char *p2 = strchr(pstart, '>') + 1; - pstart = strchr(p2, '<'); - strncpy(value, p2, pstart - p2); - value[pstart - p2] = 0; + char* delims = "<>\r\n "; + char *xmltmp = strdup(xml); + //rig_debug(RIG_DEBUG_TRACE, "%s: xml='%s'\n", __FUNCTION__,xml); + char *p = strtok(xmltmp,delims); + value[0]=0; + while(p) { + if (streq(p,"value")) { + p=strtok(NULL,delims); + if (streq(p,"array")) continue; + if (streq(p,"/value")) continue; // empty value + if (streq(p,"i4") || streq(p,"double")) { + p = strtok(NULL,delims); + } + else if (streq(p,"array")) { + p = strtok(NULL,delims); + p = strtok(NULL,delims); + } + if (strlen(value)+strlen(p)+1 < valueLen) { + if (value[0]!=0) strcat(value,"|"); + strcat(value,p); + } + else { // we'll just stop adding stuff + rig_debug(RIG_DEBUG_ERR, "%s: max value length exceeded\n", __FUNCTION__); + } + } + else { + p=strtok(NULL,delims); + } + } + rig_debug(RIG_DEBUG_TRACE, "%s: value returned='%s'\n", __FUNCTION__,value); + if (rig_need_debug(RIG_DEBUG_WARN) && value != NULL && strlen(value)==0) { + rig_debug(RIG_DEBUG_ERR, "%s: xml='%s'\n", __FUNCTION__,xml); } - return value; } +/* + * xml_parse + * Assumes xml!=NULL, value!=NULL, value_len big enough + * returns the string value contained in the xml string + */ static char *xml_parse(char *xml, char *value, int value_len) { /* first off we should have an OK on the 1st line */ if (strstr(xml, " 200 OK") == NULL) { return NULL; } + rig_debug(RIG_DEBUG_VERBOSE, "%s XML:\n%s\n", __FUNCTION__, xml); // find the xml skipping the other stuff above it char *pxml = strstr(xml, "=MAXXMLLEN + */ static int read_transaction(RIG *rig, char *xml, int xml_len) { + int retval; + char *terminator = ""; + + rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); struct rig_state *rs = &rig->state; - int retval; - char cmd_buf[16384]; // plenty big for expected flrig responses - char delim[1]; - delim[0] = 0x0a; - xml[0] = cmd_buf[0] = 0; + rs->rigport.timeout = 4000; // 3 second read string timeout + int retry=5; + char *delims="\n"; + xml[0]=0; do { - retval = - read_string(&rs->rigport, cmd_buf, sizeof(cmd_buf), delim, - sizeof(delim)); - - if (strlen(xml) > 8192 - retval) { - return -RIG_EINVAL; + char tmp_buf[MAXXMLLEN]; // plenty big for expected flrig responses + int len = read_string(&rs->rigport, tmp_buf, sizeof(tmp_buf), delims, strlen(delims)); + rig_debug(RIG_DEBUG_WARN,"%s: string='%s'",__FUNCTION__,tmp_buf); + if (len > 0) retry = 3; + if (len <= 0) { + rig_debug(RIG_DEBUG_ERR,"%s: read_string error=%d\n",__FUNCTION__,len); + return -(100+RIG_EPROTO); } - - if (retval > 0) { - strcat(xml, cmd_buf); - } - } while (retval > 0 && strstr(cmd_buf, "") == NULL); - - return RIG_OK; + strcat(xml,tmp_buf); + } while (retry-- > 0 && strstr(xml,terminator)==NULL); + if (retry == 0) { + rig_debug(RIG_DEBUG_WARN,"%s: retry timeout\n",__FUNCTION__); + } + if (strstr(xml,terminator)) { + rig_debug(RIG_DEBUG_VERBOSE,"%s: got %s\n",__FUNCTION__,terminator); + // Testing slowing down 3 of commands per second + usleep(2*1000); + retval = RIG_OK; + } + else { + rig_debug(RIG_DEBUG_VERBOSE,"%s: did not get %s\n",__FUNCTION__,terminator); + retval = -(101+RIG_EPROTO); + } + return retval; } -int flrig_init(RIG *rig) +/* + * write_transaction + * Assumes rig!=NULL, xml!=NULL, xml_len=total size of xml for response + */ +static int write_transaction(RIG *rig, char *xml, int xml_len) { - rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); + int try=rig->caps->retry; + int retval=-RIG_EPROTO; + char xmltmp[MAXXMLLEN]; + + struct rig_state *rs = &rig->state; + + // This shouldn't ever happen...but just in case + // We need to avoid and empty write as rigctld replies with blank line + if (xml_len == 0) { + rig_debug(RIG_DEBUG_ERR,"%s: len==0??\n",__FUNCTION__); + } + + while(try-- >= 0 && retval != RIG_OK) { + retval = write_block(&rs->rigport, xml, strlen(xml)); + if (retval < 0) { + return -RIG_EIO; + } + } + strcpy(xml,xmltmp); + return retval; +} + +/* + * flrig_init + * Assumes rig!=NULL + */ +static int flrig_init(RIG *rig) +{ + + rig_debug(RIG_DEBUG_TRACE, "%s version %s\n", __FUNCTION__, BACKEND_VER); struct flrig_priv_data *priv = (struct flrig_priv_data *)malloc(sizeof(struct flrig_priv_data)); @@ -338,8 +432,14 @@ int flrig_init(RIG *rig) /* * set arbitrary initial status */ - priv->vfo_curr = RIG_VFO_A; rig->state.priv = (rig_ptr_t) priv; + priv->curr_vfo = RIG_VFO_A; + priv->split = 0; + priv->ptt = 0; + priv->curr_modeA = -1; + priv->curr_modeB = -1; + priv->curr_widthA = -1; + priv->curr_widthB = -1; if (!rig || !rig->caps) { return -RIG_EINVAL; @@ -347,6 +447,193 @@ int flrig_init(RIG *rig) strncpy(rig->state.rigport.pathname, DEFAULTPATH, sizeof(rig->state.rigport.pathname)); + + return RIG_OK; +} + +/* + * modeMapGetFLRig + * Assumes mode!=NULL + * Return the string for FLRig for the given hamlib mode + */ +static char * modeMapGetFLRig(unsigned int modeHamlib) +{ + int i; + for(i=0; modeMap[i].mode_hamlib!=0; ++i) { + if (modeMap[i].mode_hamlib==modeHamlib) { + return modeMap[i].mode_flrig; + } + } + rig_debug(RIG_DEBUG_ERR,"%s: Unknown mode requested: %s\n",__FUNCTION__,rig_strrmode(modeHamlib)); + return "ERROR"; +} + +/* + * modeMapGetHamlib + * Assumes mode!=NULL + * Return the hamlib mode from the given FLRig string + */ +static unsigned int modeMapGetHamlib(const char *modeFLRig) +{ + int i; + rig_debug(RIG_DEBUG_VERBOSE,"%s: get hamlib mode from %s\n",__FUNCTION__,modeFLRig); + for(i=0; modeMap[i].mode_hamlib!=0; ++i) { + if (streq(modeMap[i].mode_flrig,modeFLRig)) { + rig_debug(RIG_DEBUG_VERBOSE,"%s: got hamlib mode %s\n",__FUNCTION__,rig_strrmode(modeMap[i].mode_hamlib)); + return modeMap[i].mode_hamlib; + } + } + rig_debug(RIG_DEBUG_ERR,"%s: Unknown mode requested: %s\n",__FUNCTION__,modeFLRig); + return -RIG_EINVAL; +} + + +/* + * modeMapAdd + * Assumes modes!=NULL + */ +static void modeMapAdd(unsigned int *modes,int mode_hamlib,char *mode_flrig) +{ + int i; + for(i=0; modeMap[i].mode_hamlib!=0; ++i) { + if (modeMap[i].mode_hamlib==mode_hamlib) { + *modes |= modeMap[i].mode_hamlib; + strncpy(modeMap[i].mode_flrig,mode_flrig,sizeof(modeMap[i].mode_flrig)); + return; + } + } +} + +/* + * flrig_open + * Assumes rig!=NULL, rig->state.priv!=NULL + */ +static int flrig_open(RIG *rig) { + int retval; + char xml[MAXXMLLEN]; + char value[MAXXMLLEN]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s version %s\n", __FUNCTION__, BACKEND_VER); + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + char *pxml = xml_build("rig.get_xcvr", NULL,xml,sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); + if (retval < 0) { + return retval; + } + read_transaction(rig, xml, sizeof(xml)); + xml_parse(xml, value, sizeof(value)); + strncpy(priv->info,value,sizeof(priv->info)); + rig_debug(RIG_DEBUG_VERBOSE,"Transceiver=%s\n", value); + + pxml = xml_build("rig.get_AB", value, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); + read_transaction(rig, xml, sizeof(xml)); + xml_parse(xml, value, sizeof(value)); + if (streq(value,"A")) { + priv->curr_vfo = RIG_VFO_A; + } + else { + priv->curr_vfo = RIG_VFO_B; + } + rig_debug(RIG_DEBUG_VERBOSE, "%s: currvfo=%s value=%s\n",__FUNCTION__, rig_strvfo(priv->curr_vfo), value); + //vfo_t vfo=RIG_VFO_A; + //vfo_t vfo_tx=RIG_VFO_B; // split is always VFOB + //flrig_get_split_vfo(rig, vfo, &priv->split, &vfo_tx); + + /* find out available widths and modes */ + pxml = xml_build("rig.get_modes", NULL,xml,sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); + if (retval < 0) { + return retval; + } + read_transaction(rig, xml, sizeof(xml)); + xml_parse(xml, value, sizeof(value)); + rig_debug(RIG_DEBUG_VERBOSE, "%s: modes=%s\n",__FUNCTION__, value); + unsigned int modes = 0; + char *p; + for(p=strtok(value,"|"); p!=NULL; p=strtok(NULL,"|")) { + if (streq(p,"USB")) modeMapAdd(&modes,RIG_MODE_USB,p); + else if (streq(p,"LSB")) modeMapAdd(&modes,RIG_MODE_LSB,p); + else if (streq(p,"USB-D")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"USB-D1")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"USB-D2")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"USB-D3")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"LSB-D")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"LSB-D1")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"LSB-D2")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"LSB-D3")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"DATA-USB")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"D-USB")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"DATA-U")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"DATA-LSB")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"D-LSB")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"DATA-L")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"DATA-R")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"PKT")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"PKT-U")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"PKT(U)")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"PKT-L")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"PKT(L)")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"FSK")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"FSK-R")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"PSK")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"PSK-R")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"PSK-U")) modeMapAdd(&modes,RIG_MODE_PKTUSB,p); + else if (streq(p,"PSK-L")) modeMapAdd(&modes,RIG_MODE_PKTLSB,p); + else if (streq(p,"AM")) modeMapAdd(&modes,RIG_MODE_AM,p); + else if (streq(p,"FM")) modeMapAdd(&modes,RIG_MODE_FM,p); + else if (streq(p,"AM-D")) modeMapAdd(&modes,RIG_MODE_PKTAM,p); + else if (streq(p,"FM-D")) modeMapAdd(&modes,RIG_MODE_PKTFM,p); + else if (streq(p,"FMN")) modeMapAdd(&modes,RIG_MODE_FMN,p); + else if (streq(p,"FM-N")) modeMapAdd(&modes,RIG_MODE_FMN,p); + else if (streq(p,"FMW")) modeMapAdd(&modes,RIG_MODE_WFM,p); + else if (streq(p,"WFM")) modeMapAdd(&modes,RIG_MODE_WFM,p); + else if (streq(p,"W-FM")) modeMapAdd(&modes,RIG_MODE_WFM,p); + else if (streq(p,"CW")) modeMapAdd(&modes,RIG_MODE_CW,p); + else if (streq(p,"CWU")) modeMapAdd(&modes,RIG_MODE_CW,p); + else if (streq(p,"CW-USB")) modeMapAdd(&modes,RIG_MODE_CW,p); + else if (streq(p,"CW-U")) modeMapAdd(&modes,RIG_MODE_CW,p); + else if (streq(p,"CW-LSB")) modeMapAdd(&modes,RIG_MODE_CWR,p); + else if (streq(p,"CW-L")) modeMapAdd(&modes,RIG_MODE_CWR,p); + else if (streq(p,"CW-R")) modeMapAdd(&modes,RIG_MODE_CWR,p); + else if (streq(p,"CWL")) modeMapAdd(&modes,RIG_MODE_CWR,p); + else if (streq(p,"RTTY")) modeMapAdd(&modes,RIG_MODE_RTTY,p); + else if (streq(p,"RTTY-U")) modeMapAdd(&modes,RIG_MODE_RTTY,p); + else if (streq(p,"RTTY-R")) modeMapAdd(&modes,RIG_MODE_RTTYR,p); + else if (streq(p,"RTTY-L")) modeMapAdd(&modes,RIG_MODE_RTTYR,p); + else if (streq(p,"RTTY(U)")) modeMapAdd(&modes,RIG_MODE_RTTY,p); + else if (streq(p,"RTTY(R")) modeMapAdd(&modes,RIG_MODE_RTTYR,p); + else rig_debug(RIG_DEBUG_ERR,"%s: Unknown mode for this rig='%s'\n",__FUNCTION__,p); + } + rig->state.mode_list = modes; + rig_debug(RIG_DEBUG_VERBOSE, "%s: hamlib modes=0x%08x\n",__FUNCTION__, modes); + + return RIG_OK; +} + +/* + * flrig_close + * Assumes rig!=NULL + */ +static int flrig_close(RIG *rig) { + rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); + return RIG_OK; +} + +/* + * flrig_cleanup + * Assumes rig!=NULL, rig->state.priv!=NULL + */ +static int flrig_cleanup(RIG *rig) { + + if (!rig) + return -RIG_EINVAL; + + free(rig->state.priv); + rig->state.priv = NULL; + return RIG_OK; } @@ -354,17 +641,14 @@ int flrig_init(RIG *rig) * flrig_get_freq * Assumes rig!=NULL, rig->state.priv!=NULL, freq!=NULL */ -int flrig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) +static int flrig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) { - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, rig_strvfo(vfo)); - rs = &rig->state; + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; if (check_vfo(vfo) == FALSE) { rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", @@ -373,24 +657,49 @@ int flrig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) } if (vfo == RIG_VFO_CURR) { - vfo = RIG_VFO_A; + vfo = priv->curr_vfo; + rig_debug(RIG_DEBUG_VERBOSE, "%s: get_freq2 vfo=%s\n", + __FUNCTION__, rig_strvfo(vfo)); } - flrig_set_vfo(rig, vfo); + int retries=10; + char xml[MAXXMLLEN]; + char value[MAXCMDLEN]; + do { + char *pxml; + if (vfo == RIG_VFO_A) { + pxml = xml_build("rig.get_vfoA", NULL,xml,sizeof(xml)); + } + else { + pxml = xml_build("rig.get_vfoB", NULL,xml,sizeof(xml)); + } + retval = write_transaction(rig, pxml, strlen(pxml)); + if (retval < 0) { + return retval; + } - pxml = xml_build("rig.get_vfo", NULL); + read_transaction(rig, xml, sizeof(xml)); + xml_parse(xml, value, sizeof(value)); + if (strlen(value)==0) { + rig_debug(RIG_DEBUG_ERR, "%s: retries=%d\n",__FUNCTION__, retries); + //usleep(10*1000); + } + } while (--retries && strlen(value)==0); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); - - if (retval < 0) { - return retval; - } - - read_transaction(rig, xml, sizeof(xml)); - xml_parse(xml, value, sizeof(value)); *freq = atof(value); - rig_debug(RIG_DEBUG_VERBOSE, "%s: '%s'\n", __FUNCTION__, value); - + if (*freq == 0) { + rig_debug(RIG_DEBUG_ERR, "%s: freq==0??\nvalue=%s\nxml=%s\n", __FUNCTION__,value,xml); + return -(102+RIG_EPROTO); + } + else { + rig_debug(RIG_DEBUG_VERBOSE, "%s: freq=%.0f\n", __FUNCTION__,*freq); + } + if (vfo == RIG_VFO_A) { + priv->curr_freqA = *freq; + } + else { + priv->curr_freqB = *freq; + } return RIG_OK; } @@ -398,17 +707,14 @@ int flrig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) * flrig_set_freq * assumes rig!=NULL, rig->state.priv!=NULL */ -int flrig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) +static int flrig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) { - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; - rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s freq=%.1f\n", __FUNCTION__, + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s freq=%.0f\n", __FUNCTION__, rig_strvfo(vfo), freq); - rs = &rig->state; + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; if (check_vfo(vfo) == FALSE) { rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", @@ -417,32 +723,38 @@ int flrig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) } if (vfo == RIG_VFO_CURR) { - if ((retval = flrig_get_vfo(rig, &vfo)) != RIG_OK) { - return retval; - } - - rig_debug(RIG_DEBUG_VERBOSE, "%s: set_freq2 vfo=%s\n", - __FUNCTION__, rig_strvfo(vfo)); + vfo = priv->curr_vfo; + } + else if (vfo == RIG_VFO_TX && priv->split) { + vfo = RIG_VFO_B; // if split always TX on VFOB } - if (!vfo_curr(rig, vfo)) { - flrig_set_vfo(rig, vfo); + char value[MAXXMLLEN]; + sprintf(value, "%.0f", freq); + char xml[MAXXMLLEN]; + char *pxml; + if (vfo == RIG_VFO_B) { + pxml = xml_build("rig.set_vfoB", value, xml, sizeof(xml)); + rig_debug(RIG_DEBUG_VERBOSE,"rig.set_vfoB %s",value); + priv->curr_freqB = freq; } - - sprintf(value, - "%.6f", - freq); - pxml = xml_build("rig.set_vfo", value); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); - + else { + pxml = xml_build("rig.set_vfoA", value, xml, sizeof(xml)); + rig_debug(RIG_DEBUG_VERBOSE,"rig.set_vfoA %s",value); + priv->curr_freqA = freq; + } + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } + if (vfo == RIG_VFO_B) { + priv->curr_freqB = freq; + } + else { + priv->curr_freqA = freq; + } - read_transaction(rig, xml, sizeof(xml)); - //don't care about the response right now - //xml_parse(xml, value, sizeof(value)); - flrig_set_vfo(rig, vfo); + read_transaction(rig, xml, sizeof(xml)); // get response but don't care return RIG_OK; } @@ -451,16 +763,13 @@ int flrig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) * flrig_set_ptt * Assumes rig!=NULL */ -int flrig_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) +static int flrig_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) { int retval; - char cmd_buf[MAXCMDLEN]; - char *pxml; - char xml[8192]; - struct rig_state *rs; rig_debug(RIG_DEBUG_TRACE, "%s: ptt=%d\n", __FUNCTION__, ptt); - rs = &rig->state; + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; if (check_vfo(vfo) == FALSE) { rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", @@ -468,205 +777,375 @@ int flrig_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) return -RIG_EINVAL; } - if (vfo == RIG_VFO_CURR) { - vfo = RIG_VFO_A; - } - - if (!vfo_curr(rig, vfo)) { - flrig_set_vfo(rig, vfo); - } - + char cmd_buf[MAXCMDLEN]; sprintf(cmd_buf, "%d", ptt); - pxml = xml_build("rig.set_ptt", cmd_buf); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + char xml[MAXXMLLEN]; + char *pxml = xml_build("rig.set_ptt", cmd_buf, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } - read_transaction(rig, xml, sizeof(xml)); - xml_parse(xml, cmd_buf, sizeof(cmd_buf)); + read_transaction(rig, xml, sizeof(xml)); // get response but don't care + + priv->ptt = ptt; + return RIG_OK; } -int flrig_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt) +/* + * flrig_get_ptt + * Assumes rig!=NUL, ptt!=NULL + */ +static int flrig_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt) { - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, rig_strvfo(vfo)); - rs = &rig->state; - pxml = xml_build("rig.get_ptt", NULL); + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + char xml[MAXXMLLEN]; + char *pxml = xml_build("rig.get_ptt", NULL, xml, sizeof(xml)); + + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } read_transaction(rig, xml, sizeof(xml)); + char value[MAXCMDLEN]; xml_parse(xml, value, sizeof(value)); *ptt = atoi(value); rig_debug(RIG_DEBUG_VERBOSE, "%s: '%s'\n", __FUNCTION__, value); + priv->ptt = *ptt; + return RIG_OK; } +/* + * flrig_set_split_mode + * Assumes rig!=NULL + */ +static int flrig_set_split_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) +{ + int retval; + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s mode=%s width=%d\n", + __FUNCTION__, rig_strvfo(vfo), rig_strrmode(mode), width); + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + switch(vfo) { + case RIG_VFO_CURR: + vfo = priv->curr_vfo; + break; + case RIG_VFO_TX: + vfo = RIG_VFO_B; + break; + } + // If no change don't do it...modes are kept up to date by client calls + // to get_mode and set_mode so should be current here + rig_debug(RIG_DEBUG_TRACE, "%s: vfoa privmode=%s\n",__FUNCTION__,rig_strrmode(priv->curr_modeA)); + rig_debug(RIG_DEBUG_TRACE, "%s: vfob privmode=%s\n",__FUNCTION__,rig_strrmode(priv->curr_modeB)); + // save some VFO swapping .. may replace with VFO specific calls that won't cause VFO change + if (vfo==RIG_VFO_A && mode==priv->curr_modeA) return RIG_OK; + if (vfo==RIG_VFO_B && mode==priv->curr_modeB) return RIG_OK; + retval = flrig_set_mode(rig,RIG_VFO_B,mode,width); + rig_debug(RIG_DEBUG_TRACE, "%s: set mode=%s\n",__FUNCTION__,rig_strrmode(mode)); + return retval; +} + /* * flrig_set_mode * Assumes rig!=NULL */ -int flrig_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) +static int flrig_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { - char cmd_buf[32], ttmode; - int cmd_len, retval; - struct rig_state *rs; - - //struct tt588_priv_data *priv = (struct tt588_priv_data *) rig->state.priv; - - rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s mode=%d width=%d\n", - __FUNCTION__, rig_strvfo(vfo), mode, width); - rs = &rig->state; - - if (check_vfo(vfo) == FALSE) { - rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", - __FUNCTION__, rig_strvfo(vfo)); - return -RIG_EINVAL; - } - - switch (mode) { - case RIG_MODE_USB: - ttmode = 'U'; - break; - - case RIG_MODE_LSB: - ttmode = 'L'; - break; - - case RIG_MODE_CW: - ttmode = 'C'; - break; - - case RIG_MODE_AM: - ttmode = 'A'; - break; - - //case RIG_MODE_FSK: ttmode = 'F'; break; - default: - rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode %d\n", - __FUNCTION__, mode); - return -RIG_EINVAL; - } - - cmd_len = sprintf((char *) cmd_buf, "XB%c" EOM, ttmode); - - retval = write_block(&rs->rigport, cmd_buf, cmd_len); - - if (retval < 0) { - return retval; - } - - return RIG_OK; - -} - -#if 0 -static int flrig_flush(RIG *rig, vfo_t vfo) -{ - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; - rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, - rig_strvfo(vfo)); - rs = &rig->state; + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s mode=%s width=%d\n", + __FUNCTION__, rig_strvfo(vfo), rig_strrmode(mode), width); - sprintf(value, "%s", - vfo == RIG_VFO_A ? "A" : "B"); - pxml = xml_build("rig.flush", value); - network_flush(&rs->rigport); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; - if (retval < 0) { - return retval; - } - - read_transaction(rig, xml, sizeof(xml)); - return RIG_OK; -} -#endif - -int flrig_set_vfo(RIG *rig, vfo_t vfo) -{ - char value[MAXCMDLEN]; - char *pxml; - int retval; - struct rig_state *rs; - char xml[8192]; - - rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, - rig_strvfo(vfo)); - rs = &rig->state; - - if (check_vfo(vfo) == FALSE) { - rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", - __FUNCTION__, rig_strvfo(vfo)); - return -RIG_EINVAL; - } + // if ptt is on do not set mode + if (priv->ptt) return RIG_OK; if (vfo == RIG_VFO_CURR) { - struct flrig_priv_data *priv = - (struct flrig_priv_data *) rig->state.priv; - vfo = priv->vfo_curr; + vfo = priv->curr_vfo; } - sprintf(value, "%s", - vfo == RIG_VFO_A ? "A" : "B"); - pxml = xml_build("rig.set_AB", value); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + if (check_vfo(vfo) == FALSE) { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __FUNCTION__, rig_strvfo(vfo)); + return -RIG_EINVAL; + } + // + // Don't touch mode or width if PTT=true + ptt_t ptt; + retval = flrig_get_ptt(rig,RIG_VFO_A,&ptt); + if (ptt) { + priv->ptt = 1; + rig_debug(RIG_DEBUG_ERR,"%s call not made as PTT=1\n",__FUNCTION__); + return RIG_OK; // just return OK and ignore this + } + // Switch to VFOB if appropriate since we can't set mode directly + // MDB + int vfoSwitched = 0; + rig_debug(RIG_DEBUG_WARN,"%s: curr_vfo = %s\n",__FUNCTION__,rig_strvfo(priv->curr_vfo)); + if (vfo == RIG_VFO_B && priv->curr_vfo != RIG_VFO_B) { + vfoSwitched = 1; + rig_debug(RIG_DEBUG_WARN,"%s: switch to VFOB = %d\n",__FUNCTION__,vfoSwitched); + } + + if (vfoSwitched) { // swap to B and we'll swap back later + rig_debug(RIG_DEBUG_WARN,"%s: switching to VFOB = %d\n",__FUNCTION__,vfoSwitched); + retval = flrig_set_vfo(rig, RIG_VFO_B); + if (retval < 0) { + return retval; + } + } + + // Set the mode + char *ttmode = modeMapGetFLRig(mode); + + char cmd_buf[MAXCMDLEN]; + sprintf(cmd_buf, "%s", ttmode); + char xml[MAXXMLLEN]; + char *pxml = xml_build("rig.set_mode", cmd_buf, xml, sizeof(xml)); + + retval = write_transaction(rig, pxml, strlen(pxml)); + if (retval < 0) { + return retval; + } + + // Get the response + read_transaction(rig, xml, sizeof(xml)); + rig_debug(RIG_DEBUG_VERBOSE, "%s: response=%s\n", __FUNCTION__,xml); + + // Need to update the available bandwidths for this mode + if (width > 0) { + sprintf(cmd_buf, "%ld", width); + pxml = xml_build("rig.set_bandwidth", cmd_buf, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); + if (retval < 0) { + return retval; + } + read_transaction(rig, xml, sizeof(xml)); + } + + // Return to VFOA if needed + rig_debug(RIG_DEBUG_WARN,"%s: switch to VFOA? = %d\n",__FUNCTION__,vfoSwitched); + if (vfoSwitched) { + rig_debug(RIG_DEBUG_WARN,"%s: switching to VFOA\n",__FUNCTION__); + retval = flrig_set_vfo(rig, RIG_VFO_A); + if (retval < 0) { + return retval; + } + } + + if (vfo == RIG_VFO_A) { + priv->curr_modeA = mode; + priv->curr_widthA = width; + } + else { + priv->curr_modeB = mode; + priv->curr_widthB = width; + } + + return RIG_OK; +} + +/* + * flrig_get_mode + * Assumes rig!=NULL, rig->state.priv!=NULL, mode!=NULL + */ +static int flrig_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) +{ + int retval; + + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, + rig_strvfo(vfo)); + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + if (check_vfo(vfo) == FALSE) { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __FUNCTION__, rig_strvfo(vfo)); + return -RIG_EINVAL; + } + + vfo_t curr_vfo = priv->curr_vfo; + if (vfo == RIG_VFO_CURR) { + vfo = priv->curr_vfo; + } + rig_debug(RIG_DEBUG_TRACE, "%s: using vfo=%s\n", __FUNCTION__, + rig_strvfo(vfo)); + // + // Don't touch mode or width if PTT=true + ptt_t ptt; + retval = flrig_get_ptt(rig,RIG_VFO_A,&ptt); + if (ptt) { + priv->ptt = 1; + if (vfo == RIG_VFO_A) *mode = priv->curr_modeA; + else *mode = priv->curr_modeB; + rig_debug(RIG_DEBUG_ERR,"%s call not made as PTT=1\n",__FUNCTION__); + return RIG_OK; // just return OK and ignore this + } + + // Switch to VFOB if appropriate + int vfoSwitched=0; + if (vfo == RIG_VFO_B && curr_vfo != RIG_VFO_B) { + vfoSwitched = 1; + } + + if (vfoSwitched) { + retval = flrig_set_vfo(rig, RIG_VFO_B); + if (retval < 0) { + return retval; + } + } + + char xml[MAXXMLLEN]; + char *pxml = xml_build("rig.get_mode", NULL, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } read_transaction(rig, xml, sizeof(xml)); - //xml_parse(xml,value,sizeof(value)); - vfo_t vfotmp; - flrig_get_vfo(rig, &vfotmp); - - return RIG_OK; - -} - -int flrig_get_vfo(RIG *rig, vfo_t *vfo) -{ char value[MAXCMDLEN]; - char *pxml; - int retval; - struct rig_state *rs; - char xml[8192]; + xml_parse(xml, value, sizeof(value)); + retval = modeMapGetHamlib(value); + if (retval < 0 ) { + return retval; + } + *mode = retval; + rig_debug(RIG_DEBUG_VERBOSE, "%s: mode='%s'\n", __FUNCTION__, rig_strrmode(*mode)); + if (vfo == RIG_VFO_A) { + priv->curr_modeA = *mode; + } + else { + priv->curr_modeB = *mode; + } - rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); - rs = &rig->state; - - pxml = xml_build("rig.get_AB", NULL); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + // Get the bandwidth + pxml = xml_build("rig.get_bw", NULL, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } read_transaction(rig, xml, sizeof(xml)); xml_parse(xml, value, sizeof(value)); + rig_debug(RIG_DEBUG_VERBOSE, "%s: mode=%s width='%s'\n", __FUNCTION__, rig_strrmode(*mode), value); + // we get 2 entries pipe separated for bandwidth, lower and upper + if(strlen(value)>0) { + char *p=value; + /* we might get two values and then we want the 2nd one */ + if (strchr(value,'|')!=NULL) p=strchr(value,'|')+1; + *width = atoi(p); + } + if (vfo == RIG_VFO_A) { + priv->curr_widthA = *width; + } + else { + priv->curr_widthB = *width; + } + + // Return to VFOA if needed + if (vfoSwitched) { + retval = flrig_set_vfo(rig, RIG_VFO_A); + if (retval < 0) { + return retval; + } + } + + return RIG_OK; +} + +/* + * flrig_get_vfo + * assumes rig!=NULL + */ +static int flrig_set_vfo(RIG *rig, vfo_t vfo) +{ + int retval; + + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, + rig_strvfo(vfo)); + + struct rig_state *rs = &rig->state; + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + if (check_vfo(vfo) == FALSE) { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __FUNCTION__, rig_strvfo(vfo)); + return -RIG_EINVAL; + } + if (vfo == RIG_VFO_TX) { + rig_debug(RIG_DEBUG_VERBOSE, "%s: RIG_VFO_TX used\n"); + vfo = RIG_VFO_B; // always TX on VFOB + } + + if (vfo == RIG_VFO_CURR) { + vfo = priv->curr_vfo; + } + + // Force PTT off + // Why were we turning off PTT here? + // flrig_set_ptt(rig,RIG_VFO_A,0); + + char value[MAXCMDLEN]; + char xml[MAXXMLLEN]; + sprintf(value, "%s", + vfo == RIG_VFO_A ? "A" : "B"); + char *pxml = xml_build("rig.set_AB", value, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); + + if (retval < 0) { + return retval; + } + //usleep(50*1000); // temporary sleep until flrig is fixed + priv->curr_vfo = vfo; + rs->tx_vfo = RIG_VFO_B; // always VFOB + read_transaction(rig, xml, sizeof(xml)); + + return RIG_OK; +} + +/* + * flrig_get_vfo + * assumes rig!=NULL, vfo != NULL + */ +static int flrig_get_vfo(RIG *rig, vfo_t *vfo) +{ + int retval; + + rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + char xml[MAXXMLLEN]; + char *pxml = xml_build("rig.get_AB", NULL, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); + + if (retval < 0) { + return retval; + } + + read_transaction(rig, xml, sizeof(xml)); + char value[MAXCMDLEN]; + xml_parse(xml, value, sizeof(value)); + rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo value=%s\n", __FUNCTION__,value); switch (value[0]) { case 'A': @@ -688,10 +1167,7 @@ int flrig_get_vfo(RIG *rig, vfo_t *vfo) return -RIG_EINVAL; } - struct flrig_priv_data *priv = - (struct flrig_priv_data *) rig->state.priv; - - priv->vfo_curr = *vfo; + priv->curr_vfo = *vfo; rig_debug(RIG_DEBUG_VERBOSE, "%s: vfo=%s\n", __FUNCTION__, rig_strvfo(*vfo)); @@ -701,25 +1177,16 @@ int flrig_get_vfo(RIG *rig, vfo_t *vfo) /* * flrig_set_split_freq - * Note that split doesn't work for FLRig models that don't have reply codes - * Like most Yaesu rigs. The commands will overrun FLRig in those cases. - * Rigs that do have replies for all cat commands will work with split + * assumes rig!=NULL */ -int flrig_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq) +static int flrig_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq) { - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s freq=%.1f\n", __FUNCTION__, rig_strvfo(vfo), tx_freq); - rs = &rig->state; - if (vfo == RIG_VFO_SUB) { - vfo = RIG_VFO_B; - } + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; if (check_vfo(vfo) == FALSE) { rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", @@ -727,22 +1194,18 @@ int flrig_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq) return -RIG_EINVAL; } - if (!vfo_curr(rig, vfo)) { - flrig_set_vfo(rig, vfo); - } - + char xml[MAXXMLLEN]; + char value[MAXCMDLEN]; sprintf(value, - "%.6f", - tx_freq); - pxml = xml_build("rig.set_vfo", value); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); - + "%.6f", tx_freq); + char *pxml = xml_build("rig.set_vfoB", value, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } + priv->curr_freqB = tx_freq; - read_transaction(rig, xml, sizeof(xml)); - xml_parse(xml, value, sizeof(value)); + read_transaction(rig, xml, sizeof(xml)); // get response but don't care return RIG_OK; } @@ -751,51 +1214,58 @@ int flrig_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq) * flrig_get_split_freq * assumes rig!=NULL, tx_freq!=NULL */ -int flrig_get_split_freq(RIG *rig, vfo_t vfo, freq_t *tx_freq) +static int flrig_get_split_freq(RIG *rig, vfo_t vfo, freq_t *tx_freq) { rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __FUNCTION__, rig_strvfo(vfo)); - return flrig_get_freq(rig, vfo, tx_freq); + int retval = flrig_get_freq(rig, RIG_VFO_B, tx_freq); + return retval; } /* * flrig_set_split_vfo * assumes rig!=NULL, tx_freq!=NULL */ -int flrig_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo) +static int flrig_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo) { - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; rig_debug(RIG_DEBUG_TRACE, "%s: tx_vfo=%s\n", __FUNCTION__, rig_strvfo(tx_vfo)); - rs = &rig->state; - if (tx_vfo == RIG_VFO_SUB) { + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + if (tx_vfo == RIG_VFO_SUB || tx_vfo == RIG_VFO_TX) { tx_vfo = RIG_VFO_B; } - if (!vfo_curr(rig, vfo)) { - flrig_set_vfo(rig, vfo); + + // Don't touch split if PTT=true + ptt_t ptt; + retval = flrig_get_ptt(rig,RIG_VFO_A,&ptt); + if (ptt) { + priv->ptt = 1; + rig_debug(RIG_DEBUG_ERR,"%s call not made as PTT=1\n",__FUNCTION__); + return RIG_OK; // just return OK and ignore this } - sprintf(value, - "%d", - split); - pxml = xml_build("rig.set_split", value); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + /* for flrig we have to be on VFOA when we set split for VFOB Tx */ + /* we can keep the rig on VFOA since we can set freq by VFO now */ + if (!vfo_curr(rig, RIG_VFO_A)) { + flrig_set_vfo(rig, RIG_VFO_A); + } + char xml[MAXXMLLEN]; + char value[MAXCMDLEN]; + sprintf(value, "%d", split); + char *pxml = xml_build("rig.set_split", value, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } - - read_transaction(rig, xml, sizeof(xml)); - //xml_parse(xml, value, sizeof(value)); - - //flrig_set_vfo(rig,RIG_VFO_A); + priv->split = split; + + read_transaction(rig, xml, sizeof(xml)); // get response but don't care return RIG_OK; } @@ -804,40 +1274,105 @@ int flrig_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo) * flrig_get_split_vfo * assumes rig!=NULL, tx_freq!=NULL */ -int flrig_get_split_vfo(RIG *rig, vfo_t vfo, split_t *split, - vfo_t *tx_vfo) +static int flrig_get_split_vfo(RIG *rig, vfo_t vfo, split_t *split, + vfo_t *tx_vfo) { - char value[MAXCMDLEN]; - char *pxml; int retval; - struct rig_state *rs; - char xml[8192]; rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); - rs = &rig->state; - //flrig_set_vfo(rig,RIG_VFO_B); + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; - pxml = xml_build("rig.get_split", NULL); - retval = write_block(&rs->rigport, pxml, strlen(pxml)); + char xml[MAXXMLLEN]; + char *pxml = xml_build("rig.get_split", NULL, xml, sizeof(xml)); + retval = write_transaction(rig, pxml, strlen(pxml)); if (retval < 0) { return retval; } + char value[MAXCMDLEN]; read_transaction(rig, xml, sizeof(xml)); xml_parse(xml, value, sizeof(value)); - //flrig_set_vfo(rig,RIG_VFO_A); - *tx_vfo = RIG_VFO_B; *split = atoi(value); - + priv->split = *split; + rig_debug(RIG_DEBUG_VERBOSE,"%s tx_vfo=%s, split=%d\n",__FUNCTION__,rig_strvfo(*tx_vfo),*split); return RIG_OK; } /* - .set_split_freq = flrig_set_split_freq, - .get_split_freq = flrig_get_split_freq, - .set_split_mode = flrig_set_split_mode, - .get_split_mode = flrig_get_split_mode, -*/ + * flrig_set_split_freq_mode + * assumes rig!=NULL + */ +static int flrig_set_split_freq_mode(RIG *rig, vfo_t vfo, freq_t freq, rmode_t mode, pbwidth_t width) +{ + int retval; + + rig_debug(RIG_DEBUG_TRACE, "%s\n", __FUNCTION__); + + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + + if (vfo != RIG_VFO_CURR && vfo != RIG_VFO_TX) + return -RIG_ENTARGET; + + + // Don't touch split if PTT=true + ptt_t ptt; + retval = flrig_get_ptt(rig,RIG_VFO_A,&ptt); + if (ptt) { + priv->ptt = 1; + rig_debug(RIG_DEBUG_ERR,"%s call not made as PTT=1\n",__FUNCTION__); + return RIG_OK; // just return OK and ignore this + } + + // assume split is on B + // swap to VFOB to prevent lots of vfo swapping while seting up VFOB + flrig_set_vfo(rig,RIG_VFO_B); + retval = flrig_set_freq (rig, RIG_VFO_B, freq); + if (retval != RIG_OK) { + rig_debug(RIG_DEBUG_ERR, "%s flrig_set_freq failed\n", __FUNCTION__); + return retval; + } + // Make VFOB mode match VFOA mode, keep VFOB width + retval = flrig_set_mode(rig,RIG_VFO_B,priv->curr_modeA,width); + if (retval != RIG_OK) { + rig_debug(RIG_DEBUG_ERR, "%s flrig_set_mode failed\n", __FUNCTION__); + return retval; + } + + retval = flrig_set_vfo(rig,RIG_VFO_A); + + return retval; +} + +/* + * flrig_get_split_freq_mode + * assumes rig!=NULL, freq!=NULL, mode!=NULL, width!=NULL + */ +static int flrig_get_split_freq_mode(RIG *rig, vfo_t vfo, freq_t *freq, rmode_t *mode, pbwidth_t *width) +{ + int retval; + + if (vfo != RIG_VFO_CURR && vfo != RIG_VFO_TX) + return -RIG_ENTARGET; + + retval = flrig_get_freq (rig, RIG_VFO_B, freq); + if (RIG_OK == retval) { + retval = flrig_get_mode(rig,vfo,mode,width); + } + + return retval; +} + +/* + * flrig_get_info + * assumes rig!=NULL + */ +static const char *flrig_get_info(RIG *rig) +{ + struct flrig_priv_data *priv = (struct flrig_priv_data *) rig->state.priv; + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + return priv->info; +} diff --git a/dummy/flrig.h b/dummy/flrig.h index 85f5b26b3..db8d02bfe 100644 --- a/dummy/flrig.h +++ b/dummy/flrig.h @@ -28,12 +28,11 @@ #include #endif -#define BACKEND_VER "0.1" +#define BACKEND_VER "1.0" #define EOM "\r" #define TRUE 1 #define FALSE 0 -#define BUF_MAX 32768 extern const struct rig_caps flrig_caps;