From d39662348642f2c8d9fe2977721403a1b7b0eb79 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Wed, 25 Oct 2023 11:15:49 -0500 Subject: [PATCH 01/32] Fix compile warning with kenwood.c --- rigs/kenwood/kenwood.c | 2 +- rigs/kenwood/kenwood.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rigs/kenwood/kenwood.c b/rigs/kenwood/kenwood.c index 250a83840..c5fd97688 100644 --- a/rigs/kenwood/kenwood.c +++ b/rigs/kenwood/kenwood.c @@ -2861,7 +2861,7 @@ static int kenwood_get_micgain_minmax(RIG *rig, int *micgain_now, if (retval != RIG_OK) { RETURNFUNC(retval); } retval = read_string(&rs->rigport, (unsigned char *) levelbuf, sizeof(levelbuf), - NULL, ";", 1, 1); + NULL, 0, 1, 1); rig_debug(RIG_DEBUG_TRACE, "%s: retval=%d\n", __func__, retval); diff --git a/rigs/kenwood/kenwood.h b/rigs/kenwood/kenwood.h index 174ed9364..be0a28dd8 100644 --- a/rigs/kenwood/kenwood.h +++ b/rigs/kenwood/kenwood.h @@ -28,7 +28,7 @@ #include "token.h" #include "idx_builtin.h" -#define BACKEND_VER "20231023" +#define BACKEND_VER "20231025" #define EOM_KEN ';' #define EOM_TH '\r' From b5653852b382eefc19273adc53ffea350f1f35f2 Mon Sep 17 00:00:00 2001 From: George Baltz N3GB Date: Wed, 25 Oct 2023 13:49:32 -0400 Subject: [PATCH 02/32] Enable set_level checking for all Yaesu/newcat rigs. --- rigs/yaesu/newcat.c | 46 +++------------------------------------------ 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/rigs/yaesu/newcat.c b/rigs/yaesu/newcat.c index e51040e17..52ab54f3d 100644 --- a/rigs/yaesu/newcat.c +++ b/rigs/yaesu/newcat.c @@ -4030,12 +4030,8 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) main_sub_vfo = (RIG_VFO_B == vfo || RIG_VFO_SUB == vfo) ? '1' : '0'; } - //TODO Replace the next line - level_info = &rig->caps->level_gran[rig_setting2idx(level)]; - // with the next 2 lines - //err = check_level_param(rig, level, val, &level_info); - //if (err != RIG_OK ) { RETURNFUNC(err); } - //endTODO + err = check_level_param(rig, level, val, &level_info); + if (err != RIG_OK ) { RETURNFUNC(err); } switch (level) { @@ -4054,19 +4050,6 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) fpf = (int)((val.f / level_info->step.f) + 0.5f ); } - //TODO Remove when global level checking enabled - if (is_ft950 || is_ft891 || is_ft991 || is_ftdx3000 || is_ftdx3000dm - || is_ftdx101d - || is_ftdx101mp || is_ftdx10) - { - // Minimum is 5 watts on these rigs - if (fpf < 5) - { - fpf = 5; - } - } - //endTODO - SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "PC%03d%c", fpf, cat_term); break; @@ -4076,9 +4059,6 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) RETURNFUNC(-RIG_ENAVAIL); } - //TODO Remove when common level checking enabled - if (val.f > 1.0) { RETURNFUNC(-RIG_EINVAL); } - fpf = (int)((val.f / level_info->step.f) + 0.5f); if (is_ftdx10) { main_sub_vfo = '0'; } @@ -4207,22 +4187,8 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) RETURNFUNC(-RIG_ENAVAIL); } - //TODO Get rid of these checks when limit checking enabled - if (val.i < 300) - { - i = 300; - } - else if (val.i > 1050) - { - i = 1050; - } - else - { - i = val.i; - } - // Most Yaesu rigs seem to use range of 0-75 to represent pitch of 300..1050 Hz in 10 Hz steps - kp = (i - level_info->min.i + (level_info->step.i / 2)) / level_info->step.i; + kp = (val.i - level_info->min.i + (level_info->step.i / 2)) / level_info->step.i; SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "KP%02d%c", kp, cat_term); break; @@ -4264,9 +4230,6 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) newcat_get_mode(rig, vfo, &mode, &width); } - //TODO Remove when level_gran check enabled - if (val.f > 1.0) { RETURNFUNC(-RIG_EINVAL); } - fpf = (int) (( val.f / level_info->step.f ) + 0.5f ); SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "MG%03d%c", fpf, cat_term); @@ -4790,9 +4753,6 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) RETURNFUNC(-RIG_ENAVAIL); } - //TODO Remove when full level checking enabled - if (val.f > 1.0) { RETURNFUNC(-RIG_EINVAL); } - fpf = (int)((val.f / level_info->step.f) + 0.5f); if (is_ftdx9000) From 228aac34ca9e47bd30cf20d8b637013d320dc790 Mon Sep 17 00:00:00 2001 From: George Baltz N3GB Date: Wed, 25 Oct 2023 14:08:07 -0400 Subject: [PATCH 03/32] Replace hard coded constants with table values Make sure table values exist. --- rigs/yaesu/ftdx101.c | 1 + rigs/yaesu/ftdx101mp.c | 1 + rigs/yaesu/newcat.c | 11 ++++++----- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rigs/yaesu/ftdx101.c b/rigs/yaesu/ftdx101.c index 57cdc80c1..0473b7b85 100644 --- a/rigs/yaesu/ftdx101.c +++ b/rigs/yaesu/ftdx101.c @@ -204,6 +204,7 @@ const struct rig_caps ftdx101d_caps = [LVL_MONITOR_GAIN] = { .min = { .f = 0 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, [LVL_RFPOWER] = { .min = { .f = .05 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, [LVL_USB_AF] = { .min = { .f = .0 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, + [LVL_USB_AF_INPUT] = { .min = { .f = .0 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, }, .parm_gran = { [PARM_BANDSELECT] = {.min = {.f = 0.0f}, .max = {.f = 1.0f}, .step = {.s = "BAND160M,BAND80M,BAND60M,BAND40M,BAND30M,BAND20M,BAND17M,BAND15M,BAND12M,BAND10M,BAND6M,BANDGEN,BANDMW,BANDUNUSED,BANDUNUSED,BANDUNUSED,BANDUNUSED,BAND4M"}} diff --git a/rigs/yaesu/ftdx101mp.c b/rigs/yaesu/ftdx101mp.c index 801fa3dbe..bf0cd4a0a 100644 --- a/rigs/yaesu/ftdx101mp.c +++ b/rigs/yaesu/ftdx101mp.c @@ -106,6 +106,7 @@ const struct rig_caps ftdx101mp_caps = [LVL_MONITOR_GAIN] = { .min = { .f = 0 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, [LVL_RFPOWER] = { .min = { .f = .05 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, [LVL_USB_AF] = { .min = { .f = .0 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, + [LVL_USB_AF_INPUT] = { .min = { .f = .0 }, .max = { .f = 1.0 }, .step = { .f = 1.0f/100.0f } }, }, .parm_gran = { [PARM_BANDSELECT] = {.min = {.f = 0.0f}, .max = {.f = 1.0f}, .step = {.s = "BAND160M,BAND80M,BANDUNUSED,BAND40M,BAND30M,BAND20M,BAND17M,BAND15M,BAND12M,BAND10M,BAND6M,BANDGEN,BANDMW,BANDUNUSED,BANDUNUSED,BANDUNUSED,BANDUNUSED,BAND4M"}} diff --git a/rigs/yaesu/newcat.c b/rigs/yaesu/newcat.c index 52ab54f3d..594076a25 100644 --- a/rigs/yaesu/newcat.c +++ b/rigs/yaesu/newcat.c @@ -4814,24 +4814,25 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) if (is_ftdx101d || is_ftdx101mp) { rmode_t curmode = rig->state.current_vfo == RIG_VFO_A? rig->state.cache.modeMainA : rig->state.cache.modeMainB; + float valf = (val.f / level_info->step.f) + 0.5f; switch(curmode) { case RIG_MODE_USB: case RIG_MODE_LSB: - SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010113%03.0f%c", val.f*100, cat_term); + SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010113%03.0f%c", valf, cat_term); break; case RIG_MODE_AM: - SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010214%03.0f%c", val.f*100, cat_term); + SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010214%03.0f%c", valf, cat_term); break; case RIG_MODE_FM: case RIG_MODE_FMN: - SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010313%03.0f%c", val.f*100, cat_term); + SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010313%03.0f%c", valf, cat_term); break; case RIG_MODE_PKTFM: // is this the right place for this? case RIG_MODE_PKTFMN: // is this the right place for this? case RIG_MODE_PKTUSB: case RIG_MODE_PKTLSB: - SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010415%03.0f%c", val.f*100, cat_term); + SNPRINTF(priv->cmd_str, sizeof(priv->cmd_str), "EX010415%03.0f%c", valf, cat_term); break; default: rig_debug(RIG_DEBUG_ERR, "%s: unknown how to set USB_AF for mode=%s\n", __func__, rig_strrmode(curmode)); @@ -5910,7 +5911,7 @@ int newcat_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) case RIG_LEVEL_USB_AF_INPUT: i = 0; sscanf(retlvl, "%3d", &i); - val->f = i / 100.0; + val->f = i * level_info->step.f; break; default: From 66e8548d606d666ed863ece111f1c1150d622e20 Mon Sep 17 00:00:00 2001 From: George Baltz N3GB Date: Wed, 25 Oct 2023 16:12:11 -0400 Subject: [PATCH 04/32] f conversion already rounds, so no external rounding needed. --- rigs/yaesu/newcat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rigs/yaesu/newcat.c b/rigs/yaesu/newcat.c index 594076a25..8f69532c7 100644 --- a/rigs/yaesu/newcat.c +++ b/rigs/yaesu/newcat.c @@ -4814,7 +4814,7 @@ int newcat_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) if (is_ftdx101d || is_ftdx101mp) { rmode_t curmode = rig->state.current_vfo == RIG_VFO_A? rig->state.cache.modeMainA : rig->state.cache.modeMainB; - float valf = (val.f / level_info->step.f) + 0.5f; + float valf = val.f / level_info->step.f; switch(curmode) { case RIG_MODE_USB: From 1d57136d699ed8e4fa7cc829a1060338382542ea Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Wed, 25 Oct 2023 15:18:33 -0500 Subject: [PATCH 05/32] Fix TS590 USB_AF and USB_AF_INPUT maximum level https://github.com/Hamlib/Hamlib/issues/1402 --- rigs/kenwood/ts590.c | 8 ++++---- simulators/simts590.c | 32 +++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/rigs/kenwood/ts590.c b/rigs/kenwood/ts590.c index 3389778e7..1b80a3831 100644 --- a/rigs/kenwood/ts590.c +++ b/rigs/kenwood/ts590.c @@ -461,14 +461,14 @@ static int ts590_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) switch (level) { case RIG_LEVEL_USB_AF: - kenwood_val = val.f * 10; + kenwood_val = val.f * 9; cmd = 65; // TS-590S if (rig->caps->rig_model == RIG_MODEL_TS590SG) cmd=72; SNPRINTF(levelbuf, sizeof(levelbuf), "EX%03d0000%d", cmd, kenwood_val); break; case RIG_LEVEL_USB_AF_INPUT: - kenwood_val = val.f * 10; + kenwood_val = val.f * 9; cmd = 64; // TS-590S if (rig->caps->rig_model == RIG_MODEL_TS590SG) cmd=71; SNPRINTF(levelbuf, sizeof(levelbuf), "EX%03d0000%d", cmd, kenwood_val); @@ -673,14 +673,14 @@ static int ts590_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) cmd = 65; // TS-590S if (rig->caps->rig_model == RIG_MODEL_TS590SG) cmd=72; retval = ts590_get_ex_menu(rig, cmd, 1, &levelint); - val->f = levelint / 10.0; + val->f = levelint / 9.0; return retval; case RIG_LEVEL_USB_AF_INPUT: cmd = 65; // TS-590S if (rig->caps->rig_model == RIG_MODEL_TS590SG) cmd=71; retval = ts590_get_ex_menu(rig, cmd, 1, &levelint); - val->f = levelint / 10.0; + val->f = levelint / 9.0; return retval; case RIG_LEVEL_AF: diff --git a/simulators/simts590.c b/simulators/simts590.c index 978b982ef..699d6e266 100644 --- a/simulators/simts590.c +++ b/simulators/simts590.c @@ -30,8 +30,8 @@ int keyspd = 25; int width_high = 0; int width_low = 0; int afgain = 50; -int usb_af = 5; -int usb_af_input = 2; +int usb_af = 9; +int usb_af_input = 9; int mic_gain = 50; int @@ -234,24 +234,42 @@ int main(int argc, char *argv[]) pbuf = "VS0;"; WRITE(fd, pbuf, strlen(pbuf)); } - else if (strcmp(buf, "EX0640000;") == 0) + else if (strcmp(buf, "EX0640000;") == 0) // TS-590S version { SNPRINTF(buf, sizeof(buf), "EX0640000%d;", usb_af_input); WRITE(fd, buf, strlen(buf)); } - else if (strncmp(buf, "EX0640000", 9) == 0) + else if (strncmp(buf, "EX0640000", 9) == 0) // TS-590S version { sscanf(buf, "EX0640000%d", &usb_af_input); } - else if (strcmp(buf, "EX0650000;") == 0) + else if (strcmp(buf, "EX0650000;") == 0) // TS-590S version { - SNPRINTF(buf, sizeof(buf), "EX0650000%d;", usb_af); + SNPRINTF(buf, sizeof(buf), "EX0650000%d;", usb_af); // TS-590S version WRITE(fd, buf, strlen(buf)); } - else if (strncmp(buf, "EX0650000", 9) == 0) + else if (strncmp(buf, "EX0650000", 9) == 0) // TS-590S version { sscanf(buf, "EX0650000%d", &usb_af); } + else if (strcmp(buf, "EX0710000;") == 0) // TS-590SG version + { + SNPRINTF(buf, sizeof(buf), "EX0710000%d;", usb_af_input); + WRITE(fd, buf, strlen(buf)); + } + else if (strncmp(buf, "EX0710000", 9) == 0) // TS-590SG version + { + sscanf(buf, "EX0710000%d", &usb_af_input); + } + else if (strcmp(buf, "EX0720000;") == 0) // TS-590SG version + { + SNPRINTF(buf, sizeof(buf), "EX0720000%d;", usb_af); // TS-590SG version + WRITE(fd, buf, strlen(buf)); + } + else if (strncmp(buf, "EX0720000", 9) == 0) // TS-590S version + { + sscanf(buf, "EX0720000%d", &usb_af); + } else if (strcmp(buf, "EX032;") == 0) { static int ant = 0; From 7c5d4db2fe14addd7c57235229f610b0ee931207 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Fri, 27 Oct 2023 15:11:19 -0500 Subject: [PATCH 06/32] Fix ptt status for multicast object to show ptt correct VFO --- src/snapshot_data.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/snapshot_data.c b/src/snapshot_data.c index d8d6aa8bf..2465eee4f 100644 --- a/src/snapshot_data.c +++ b/src/snapshot_data.c @@ -161,19 +161,26 @@ static int snapshot_serialize_vfo(cJSON *vfo_node, RIG *rig, vfo_t vfo) } } + split = rig->state.cache.split; + split_vfo = rig->state.cache.split_vfo; + + is_rx = (split == RIG_SPLIT_OFF && vfo == rig->state.current_vfo) + || (split == RIG_SPLIT_ON && vfo != split_vfo); + is_tx = (split == RIG_SPLIT_OFF && vfo == rig->state.current_vfo) + || (split == RIG_SPLIT_ON && vfo == split_vfo); ptt = rig->state.cache.ptt; + + if (is_tx) node = cJSON_AddBoolToObject(vfo_node, "ptt", ptt == RIG_PTT_OFF ? 0 : 1); + else + node = cJSON_AddBoolToObject(vfo_node, "ptt", 0); if (node == NULL) { goto error; } - split = rig->state.cache.split; - split_vfo = rig->state.cache.split_vfo; - is_rx = (split == RIG_SPLIT_OFF && vfo == rig->state.current_vfo) - || (split == RIG_SPLIT_ON && vfo != split_vfo); node = cJSON_AddBoolToObject(vfo_node, "rx", is_rx); if (node == NULL) @@ -181,8 +188,6 @@ static int snapshot_serialize_vfo(cJSON *vfo_node, RIG *rig, vfo_t vfo) goto error; } - is_tx = (split == RIG_SPLIT_OFF && vfo == rig->state.current_vfo) - || (split == RIG_SPLIT_ON && vfo == split_vfo); node = cJSON_AddBoolToObject(vfo_node, "tx", is_tx); if (node == NULL) From f9bd07bbf5faee04129158606e4c207db85115e9 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Mon, 30 Oct 2023 10:20:52 -0500 Subject: [PATCH 07/32] Add nonblocking mode to multicast for recv_from use https://github.com/Hamlib/Hamlib/issues/695 --- src/network.c | 6 ++++++ tests/rigctl_parse.c | 1 + 2 files changed, 7 insertions(+) diff --git a/src/network.c b/src/network.c index 15b7fbe2c..8505f5bc1 100644 --- a/src/network.c +++ b/src/network.c @@ -1094,6 +1094,12 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, } socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + u_long mode = 1; // Enable non-blocking mode +#ifdef __MINGW32__ + ioctlsocket(socket_fd, FIONBIO, &mode); +#else + ioctl(socket_fd, FIONBIO, &mode); +#endif if (socket_fd < 0) { diff --git a/tests/rigctl_parse.c b/tests/rigctl_parse.c index d488ec0f2..af1f97985 100644 --- a/tests/rigctl_parse.c +++ b/tests/rigctl_parse.c @@ -270,6 +270,7 @@ declare_proto_rig(hamlib_version); declare_proto_rig(test); declare_proto_rig(cm108_get_bit); declare_proto_rig(cm108_set_bit); +declare_proto_rig(set_conf); /* From 6b558c5d2ad5dffa9887495688882fd6f1560ed0 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Mon, 30 Oct 2023 10:21:58 -0500 Subject: [PATCH 08/32] Revert "Add nonblocking mode to multicast for recv_from use" This reverts commit f9bd07bbf5faee04129158606e4c207db85115e9. --- src/network.c | 6 ------ tests/rigctl_parse.c | 1 - 2 files changed, 7 deletions(-) diff --git a/src/network.c b/src/network.c index 8505f5bc1..15b7fbe2c 100644 --- a/src/network.c +++ b/src/network.c @@ -1094,12 +1094,6 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, } socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - u_long mode = 1; // Enable non-blocking mode -#ifdef __MINGW32__ - ioctlsocket(socket_fd, FIONBIO, &mode); -#else - ioctl(socket_fd, FIONBIO, &mode); -#endif if (socket_fd < 0) { diff --git a/tests/rigctl_parse.c b/tests/rigctl_parse.c index af1f97985..d488ec0f2 100644 --- a/tests/rigctl_parse.c +++ b/tests/rigctl_parse.c @@ -270,7 +270,6 @@ declare_proto_rig(hamlib_version); declare_proto_rig(test); declare_proto_rig(cm108_get_bit); declare_proto_rig(cm108_set_bit); -declare_proto_rig(set_conf); /* From 012534a628f6025bbaed6d0189f686c776388945 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Mon, 30 Oct 2023 10:23:13 -0500 Subject: [PATCH 09/32] Add nonblock mode to multicast for recv_from use https://github.com/Hamlib/Hamlib/issues/695 --- src/network.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/network.c b/src/network.c index 15b7fbe2c..679c0b57d 100644 --- a/src/network.c +++ b/src/network.c @@ -1094,6 +1094,13 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, } socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + u_long mode = 1; // Enable non-blocking mode +#ifdef __MINGW32__ + ioctlsocket(socket_fd, FIONBIO, &mode); +#else + ioctl(socket_fd, FIONBIO, &mode); +#endif + if (socket_fd < 0) { From fb03d095dff23b7efd1794a9ce2ebf9425889579 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Mon, 30 Oct 2023 22:53:13 -0500 Subject: [PATCH 10/32] Add recvfrom for client to send commands to multicast server https://github.com/Hamlib/Hamlib/issues/695 --- src/network.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/network.c b/src/network.c index 679c0b57d..9b4b5126f 100644 --- a/src/network.c +++ b/src/network.c @@ -978,6 +978,16 @@ void *multicast_publisher(void *arg) rig_debug(RIG_DEBUG_ERR, "%s: error sending UDP packet: %s\n", __func__, strerror(errno)); } + + struct sockaddr_in client_addr; + char buf[4096]; + socklen_t client_len = sizeof(client_addr); + int n = recvfrom(socket_fd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &client_len); + if (n > 0) + { + // To-do handle commands from multicast clients + rig_debug(RIG_DEBUG_ERR, "%s: received %d bytes=%s\n", __func__, n, buf); + } for(i=0;i<5;++i) { hl_usleep(200*1000); @@ -1120,11 +1130,6 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_SPECTRUM enabled\n", __FILE__, __LINE__); } - else - { - rig_debug(RIG_DEBUG_ERR, "%s(%d) unknown MULTICAST item requested=0x%x\n", - __FILE__, __LINE__, items); - } rs->snapshot_packet_sequence_number = 0; rs->multicast_publisher_run = 1; From c8c8869a3c19661a73e321079d35a3cc1ce58262 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Wed, 1 Nov 2023 00:26:37 +0200 Subject: [PATCH 11/32] Refactor multicast publisher and rig poll routine. Create skeleton for multicast command server. --- include/hamlib/rig.h | 8 +- src/conf.c | 54 +++++++- src/event.c | 116 +++++++++++++++- src/network.c | 319 ++++++++++++++++++++++++++++++------------- src/network.h | 2 + src/rig.c | 42 ++++-- src/token.h | 9 ++ tests/rigctld.c | 47 ------- 8 files changed, 433 insertions(+), 164 deletions(-) diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index 3816bbab0..6a0647518 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2758,7 +2758,13 @@ struct rig_state { volatile int morse_data_handler_thread_run; void *morse_data_handler_priv_data; FIFO_RIG *fifo_morse; - int port_multicast; /*!< May be different so this is initially a copy of rigctl'd port selection */ + char *multicast_data_addr; /*!< Multicast data UDP address for publishing rig data and state */ + int multicast_data_port; /*!< Multicast data UDP port for publishing rig data and state */ + char *multicast_cmd_addr; /*!< Multicast command server UDP address for sending commands to rig */ + int multicast_cmd_port; /*!< Multicast command server UDP port for sending commands to rig */ + + volatile int multicast_receiver_run; + void *multicast_receiver_priv_data; }; /** diff --git a/src/conf.c b/src/conf.c index cc9291e3a..3c17ecb11 100644 --- a/src/conf.c +++ b/src/conf.c @@ -92,8 +92,8 @@ static const struct confparams frontend_cfg_params[] = }, { TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in milliseconds", - "Polling interval in milliseconds for transceive emulation, value of 0 disables polling", - "0", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + "Polling interval in milliseconds for transceive emulation, defaults to 1000, value of 0 disables polling", + "1000", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { TOK_PTT_TYPE, "ptt_type", "PTT type", @@ -190,6 +190,26 @@ static const struct confparams frontend_cfg_params[] = "Add Hz to VFOB/Sub frequency set", "0", RIG_CONF_NUMERIC, { .n = {0, 1e12, 1}} }, + { + TOK_MULTICAST_DATA_ADDR, "multicast_data_addr", "Multicast data UDP address", + "Multicast data UDP address for publishing rig data and state, value of 0.0.0.0 disables multicast data publishing", + "224.0.0.1", RIG_CONF_STRING, + }, + { + TOK_MULTICAST_DATA_PORT, "multicast_data_port", "Multicast data UDP port", + "Multicast data UDP port for publishing rig data and state", + "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + }, + { + TOK_MULTICAST_CMD_ADDR, "multicast_cmd_addr", "Multicast command server UDP address", + "Multicast command UDP address for sending commands to rig, value of 0.0.0.0 disables multicast command server", + "224.0.0.2", RIG_CONF_STRING, + }, + { + TOK_MULTICAST_CMD_PORT, "multicast_cmd_port", "Multicast command server UDP port", + "Multicast data UDP port for sending commands to rig", + "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + }, { RIG_CONF_END, NULL, } }; @@ -608,7 +628,11 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val) break; case TOK_POLL_INTERVAL: - rs->poll_interval = atof(val); + if (1 != sscanf(val, "%ld", &val_i)) + { + return -RIG_EINVAL; + } + rs->poll_interval = val_i; // Make sure cache times out before next poll cycle rig_set_cache_timeout_ms(rig, HAMLIB_CACHE_ALL, atol(val)); break; @@ -735,7 +759,31 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val) rig_debug(RIG_DEBUG_VERBOSE, "%s: offset_vfob=%ld\n", __func__, val_i); break; + case TOK_MULTICAST_DATA_ADDR: + rs->multicast_data_addr = strdup(val); + break; + case TOK_MULTICAST_DATA_PORT: + if (1 != sscanf(val, "%ld", &val_i)) + { + return -RIG_EINVAL; + } + + rs->multicast_data_port = val_i; + break; + + case TOK_MULTICAST_CMD_ADDR: + rs->multicast_cmd_addr = strdup(val); + break; + + case TOK_MULTICAST_CMD_PORT: + if (1 != sscanf(val, "%ld", &val_i)) + { + return -RIG_EINVAL; + } + + rs->multicast_cmd_port = val_i; + break; default: return -RIG_EINVAL; diff --git a/src/event.c b/src/event.c index e6f2ea51e..5cdedc43c 100644 --- a/src/event.c +++ b/src/event.c @@ -75,12 +75,11 @@ void *rig_poll_routine(void *arg) int update_occurred; vfo_t vfo = RIG_VFO_NONE, vfo_prev = RIG_VFO_NONE; - freq_t freq_main = 0, freq_sub = 0, freq_main_prev = 0, freq_sub_prev = 0; - rmode_t mode_main = RIG_MODE_NONE, mode_sub = RIG_MODE_NONE, - mode_main_prev = RIG_MODE_NONE, mode_sub_prev = RIG_MODE_NONE; - pbwidth_t width_main = 0, width_sub = 0, width_main_prev = 0, - width_sub_prev = 0; - split_t split, split_prev = -1; + freq_t freq_main_a = 0, freq_main_b = 0, freq_main_c = 0, freq_sub_a = 0, freq_sub_b = 0, freq_sub_c = 0; + rmode_t mode_main_a = 0, mode_main_b = 0, mode_main_c = 0, mode_sub_a = 0, mode_sub_b = 0, mode_sub_c = 0; + pbwidth_t width_main_a = 0, width_main_b = 0, width_main_c = 0, width_sub_a = 0, width_sub_b = 0, width_sub_c = 0; + ptt_t ptt = RIG_PTT_OFF; + split_t split = RIG_SPLIT_OFF; rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting rig poll routine thread\n", __FILE__, __LINE__); @@ -92,6 +91,109 @@ void *rig_poll_routine(void *arg) while (rs->poll_routine_thread_run) { + if (rig->state.cache.freqMainA != freq_main_a) + { + freq_main_a = rig->state.cache.freqMainA; + update_occurred = 1; + } + else if (rig->state.cache.freqMainB != freq_main_b) + { + freq_main_b = rig->state.cache.freqMainB; + update_occurred = 1; + } + else if (rig->state.cache.freqMainC != freq_main_c) + { + freq_main_b = rig->state.cache.freqMainC; + update_occurred = 1; + } + else if (rig->state.cache.freqSubA != freq_sub_a) + { + freq_sub_a = rig->state.cache.freqSubA; + update_occurred = 1; + } + else if (rig->state.cache.freqSubB != freq_sub_b) + { + freq_sub_b = rig->state.cache.freqSubB; + update_occurred = 1; + } + else if (rig->state.cache.freqSubC != freq_sub_c) + { + freq_sub_c = rig->state.cache.freqSubC; + update_occurred = 1; + } + else if (rig->state.cache.ptt != ptt) + { + ptt = rig->state.cache.ptt; + update_occurred = 1; + } + else if (rig->state.cache.split != split) + { + split = rig->state.cache.split; + update_occurred = 1; + } + else if (rig->state.cache.modeMainA != mode_main_a) + { + mode_main_a = rig->state.cache.modeMainA; + update_occurred = 1; + } + else if (rig->state.cache.modeMainB != mode_main_b) + { + mode_main_b = rig->state.cache.modeMainB; + update_occurred = 1; + } + else if (rig->state.cache.modeMainC != mode_main_c) + { + mode_main_c = rig->state.cache.modeMainC; + update_occurred = 1; + } + else if (rig->state.cache.modeSubA != mode_sub_a) + { + mode_sub_a = rig->state.cache.modeSubA; + update_occurred = 1; + } + else if (rig->state.cache.modeSubB != mode_sub_b) + { + mode_sub_b = rig->state.cache.modeSubB; + update_occurred = 1; + } + else if (rig->state.cache.modeSubC != mode_sub_c) + { + mode_sub_c = rig->state.cache.modeSubC; + update_occurred = 1; + } + else if (rig->state.cache.widthMainA != width_main_a) + { + width_main_a = rig->state.cache.widthMainA; + update_occurred = 1; + } + else if (rig->state.cache.widthMainB != width_main_b) + { + width_main_b = rig->state.cache.widthMainB; + update_occurred = 1; + } + else if (rig->state.cache.widthMainC != width_main_c) + { + width_main_c = rig->state.cache.widthMainC; + update_occurred = 1; + } + else if (rig->state.cache.widthSubA != width_sub_a) + { + width_sub_a = rig->state.cache.widthSubA; + update_occurred = 1; + } + else if (rig->state.cache.widthSubB != width_sub_b) + { + width_sub_b = rig->state.cache.widthSubB; + update_occurred = 1; + } + else if (rig->state.cache.widthSubC != width_sub_c) + { + width_sub_c = rig->state.cache.widthSubC; + update_occurred = 1; + } + +// The original code here actively reads rig state, which can be too intensive and intrusive +#if 0 if (rig->caps->get_vfo) { result = rig_get_vfo(rig, &vfo); @@ -225,10 +327,12 @@ void *rig_poll_routine(void *arg) split_prev = split; } } +#endif if (update_occurred) { network_publish_rig_poll_data(rig); + update_occurred = 0; } hl_usleep(rs->poll_interval * 1000); diff --git a/src/network.c b/src/network.c index 9b4b5126f..cde6e473a 100644 --- a/src/network.c +++ b/src/network.c @@ -1,6 +1,6 @@ /* * Hamlib Interface - network communication low-level support - * Copyright (c) 2021 by Mikael Nousiainen + * Copyright (c) 2021-2023 by Mikael Nousiainen * Copyright (c) 2000-2012 by Stephane Fillod * * This library is free software; you can redistribute it and/or @@ -126,6 +126,20 @@ typedef struct multicast_publisher_priv_data_s multicast_publisher_args args; } multicast_publisher_priv_data; +typedef struct multicast_receiver_args_s +{ + RIG *rig; + int socket_fd; + const char *multicast_addr; + int multicast_port; +} multicast_receiver_args; + +typedef struct multicast_receiver_priv_data_s +{ + pthread_t thread_id; + multicast_receiver_args args; +} multicast_receiver_priv_data; + static void handle_error(enum rig_debug_level_e lvl, const char *msg) { int e; @@ -515,7 +529,6 @@ static int multicast_publisher_write_data(multicast_publisher_args return (RIG_OK); } -#if 0 // disable until we figure out what to do about Windows poor performance static int multicast_publisher_read_data(multicast_publisher_args const *mcast_publisher_args, size_t length, unsigned char *data) { @@ -558,7 +571,6 @@ static int multicast_publisher_read_data(multicast_publisher_args return (RIG_OK); } -#endif #else @@ -638,7 +650,6 @@ static int multicast_publisher_write_data(const multicast_publisher_args return (RIG_OK); } -#if 0 static int multicast_publisher_read_data(const multicast_publisher_args *mcast_publisher_args, size_t length, unsigned char *data) { @@ -703,7 +714,6 @@ static int multicast_publisher_read_data(const multicast_publisher_args return (RIG_OK); } -#endif #endif @@ -777,7 +787,6 @@ int network_publish_rig_transceive_data(RIG *rig) return multicast_publisher_write_packet_header(rig, &packet); } -// cppcheck-suppress unusedFunction int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line) { int result; @@ -827,7 +836,6 @@ int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line) RETURNFUNC2(RIG_OK); } -#if 0 static int multicast_publisher_read_packet(multicast_publisher_args const *mcast_publisher_args, uint8_t *type, struct rig_spectrum_line *spectrum_line, @@ -893,11 +901,10 @@ static int multicast_publisher_read_packet(multicast_publisher_args return (RIG_OK); } -#endif void *multicast_publisher(void *arg) { - //unsigned char spectrum_data[HAMLIB_MAX_SPECTRUM_DATA]; + unsigned char spectrum_data[HAMLIB_MAX_SPECTRUM_DATA]; char snapshot_buffer[HAMLIB_MAX_SNAPSHOT_PACKET_SIZE]; struct multicast_publisher_args_s *args = (struct multicast_publisher_args_s *) @@ -923,29 +930,20 @@ void *multicast_publisher(void *arg) while (rs->multicast_publisher_run == 1) { - int i; int result; - static freq_t freqA, freqB, freqC; - static mode_t modeA, modeB, modeC; - static pbwidth_t widthA, widthB, widthC; - static ptt_t ptt; - static split_t split; -#if 0 result = multicast_publisher_read_packet(args, &packet_type, &spectrum_line, spectrum_data); -#endif if (result != RIG_OK) { if (result == -RIG_ETIMEOUT) { - // continue; + continue; } // TODO: how to detect closing of pipe, indicate with error code // TODO: error handling, flush pipe in case of error? - //hl_usleep(500 * 1000); -// continue; + continue; } result = snapshot_serialize(sizeof(snapshot_buffer), snapshot_buffer, rig, @@ -959,10 +957,8 @@ void *multicast_publisher(void *arg) continue; } -#if 0 rig_debug(RIG_DEBUG_CACHE, "%s: sending rig snapshot data: %s\n", __func__, snapshot_buffer); -#endif send_result = sendto( socket_fd, @@ -978,84 +974,73 @@ void *multicast_publisher(void *arg) rig_debug(RIG_DEBUG_ERR, "%s: error sending UDP packet: %s\n", __func__, strerror(errno)); } - - struct sockaddr_in client_addr; - char buf[4096]; - socklen_t client_len = sizeof(client_addr); - int n = recvfrom(socket_fd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &client_len); - if (n > 0) - { - // To-do handle commands from multicast clients - rig_debug(RIG_DEBUG_ERR, "%s: received %d bytes=%s\n", __func__, n, buf); - } - for(i=0;i<5;++i) - { - hl_usleep(200*1000); - if (rig->state.cache.freqMainA != freqA) - { - freqA = rig->state.cache.freqMainA; - break; - } - if (rig->state.cache.freqMainB != freqB) - { - freqB = rig->state.cache.freqMainB; - break; - } - if (rig->state.cache.freqMainC != freqC) - { - freqC = rig->state.cache.freqMainC; - break; - } - if (rig->state.cache.ptt != ptt) - { - ptt = rig->state.cache.ptt; - break; - } - if (rig->state.cache.split != split) - { - split = rig->state.cache.split; - break; - } - if (rig->state.cache.modeMainA != modeA) - { - modeA = rig->state.cache.modeMainA; - break; - } - if (rig->state.cache.modeMainB != modeB) - { - modeB = rig->state.cache.modeMainB; - break; - } - if (rig->state.cache.modeMainC != modeC) - { - modeC = rig->state.cache.modeMainC; - break; - } - if (rig->state.cache.widthMainA != widthA) - { - widthA = rig->state.cache.widthMainA; - break; - } - if (rig->state.cache.widthMainB != widthB) - { - widthB = rig->state.cache.widthMainB; - break; - } - if (rig->state.cache.widthMainC != widthC) - { - widthC = rig->state.cache.widthMainC; - break; - } - } - } - rs->multicast_publisher_run = 2; // stop value rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast publisher\n", __FILE__, __LINE__); return NULL; } +void *multicast_receiver(void *arg) +{ + char data[4096]; + + struct multicast_receiver_args_s *args = (struct multicast_receiver_args_s *) + arg; + RIG *rig = args->rig; + struct rig_state *rs = &rig->state; + + struct sockaddr_in dest_addr; + int socket_fd = args->socket_fd; + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, + __LINE__); + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); + dest_addr.sin_port = htons(args->multicast_port); + + if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) { + rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n", __func__, + args->multicast_addr, args->multicast_port, strerror(errno)); + return NULL; + } + + rs->multicast_receiver_run = 1; + + while (rs->multicast_receiver_run == 1) + { + ssize_t result; + struct sockaddr_in client_addr; + socklen_t client_len = sizeof(client_addr); + + result = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *) &client_addr, &client_len); + + rig_debug(RIG_DEBUG_ERR, "%s: received multicast packet with result %ld\n", __func__, result); + + if (result <= 0) + { + if (result < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n", __func__, + args->multicast_addr, args->multicast_port, strerror(errno)); + } + break; + } + + // TODO: handle commands from multicast clients + rig_debug(RIG_DEBUG_ERR, "%s: received %ld bytes=%s\n", __func__, result, data); + + // TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot + // TODO: new logic in publisher needs to be written for other types of responses + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast receiver\n", __FILE__, + __LINE__); + return NULL; +} + //! @endcond /** @@ -1077,11 +1062,11 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, ENTERFUNC; - rig_debug(RIG_DEBUG_VERBOSE, "%s(%d):multicast address=%s, port=%d\n", __FILE__, + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): multicast publisher address=%s, port=%d\n", __FILE__, __LINE__, multicast_addr, multicast_port); - if (strcmp(multicast_addr, "0.0.0.0") == 0) + if (multicast_addr == NULL || strcmp(multicast_addr, "0.0.0.0") == 0) { rig_debug(RIG_DEBUG_TRACE, "%s(%d): not starting multicast publisher\n", __FILE__, __LINE__); @@ -1231,5 +1216,149 @@ int network_multicast_publisher_stop(RIG *rig) RETURNFUNC(RIG_OK); } + + +/** + * \brief Start multicast receiver + * + * Start multicast receiver. + * + * \param multicast_addr UDP address + * \param multicast_port UDP socket port + * \return RIG_OK or < 0 if error + */ +int network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int multicast_port) +{ + struct rig_state *rs = &rig->state; + multicast_receiver_priv_data *mcast_receiver_priv; + int socket_fd; + int status; + + ENTERFUNC; + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): multicast receiver address=%s, port=%d\n", __FILE__, + __LINE__, + multicast_addr, multicast_port); + + if (multicast_addr == NULL || strcmp(multicast_addr, "0.0.0.0") == 0) + { + rig_debug(RIG_DEBUG_TRACE, "%s(%d): not starting multicast receiver\n", + __FILE__, __LINE__); + return RIG_OK; + } + + if (rs->multicast_receiver_priv_data != NULL) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d): multicast receiver already running\n", + __FILE__, + __LINE__); + RETURNFUNC(-RIG_EINVAL); + } + + status = network_init(); + + if (status != RIG_OK) + { + RETURNFUNC(status); + } + + socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (socket_fd < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error opening new UDP socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } + + rs->multicast_receiver_run = 1; + rs->multicast_receiver_priv_data = calloc(1, + sizeof(multicast_receiver_priv_data)); + + if (rs->multicast_receiver_priv_data == NULL) + { + close(socket_fd); + RETURNFUNC(-RIG_ENOMEM); + } + + mcast_receiver_priv = (multicast_receiver_priv_data *) + rs->multicast_receiver_priv_data; + mcast_receiver_priv->args.socket_fd = socket_fd; + mcast_receiver_priv->args.multicast_addr = multicast_addr; + mcast_receiver_priv->args.multicast_port = multicast_port; + mcast_receiver_priv->args.rig = rig; + + int err = pthread_create(&mcast_receiver_priv->thread_id, NULL, + multicast_receiver, + &mcast_receiver_priv->args); + + if (err) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error %s\n", __FILE__, __LINE__, + strerror(errno)); + free(mcast_receiver_priv); + rs->multicast_receiver_priv_data = NULL; + close(socket_fd); + RETURNFUNC(-RIG_EINTERNAL); + } + + RETURNFUNC(RIG_OK); +} + +/** + * \brief Stop multicast receiver + * + * Stop multicast receiver + * + * \return RIG_OK or < 0 if error + */ +int network_multicast_receiver_stop(RIG *rig) +{ + struct rig_state *rs = &rig->state; + multicast_receiver_priv_data *mcast_receiver_priv; + + ENTERFUNC; + + rs->multicast_receiver_run = 0; + + mcast_receiver_priv = (multicast_receiver_priv_data *) + rs->multicast_receiver_priv_data; + + if (mcast_receiver_priv == NULL) + { + RETURNFUNC(RIG_OK); + } + + // Close the socket first to stop the routine + if (mcast_receiver_priv->args.socket_fd >= 0) + { + close(mcast_receiver_priv->args.socket_fd); + } + + if (mcast_receiver_priv->thread_id != 0) + { + int err = pthread_join(mcast_receiver_priv->thread_id, NULL); + + if (err) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error %s\n", __FILE__, __LINE__, + strerror(errno)); + // just ignore it + } + + mcast_receiver_priv->thread_id = 0; + } + + if (mcast_receiver_priv->args.socket_fd >= 0) + { + mcast_receiver_priv->args.socket_fd = -1; + } + + free(rs->multicast_receiver_priv_data); + rs->multicast_receiver_priv_data = NULL; + + RETURNFUNC(RIG_OK); +} + #endif /** @} */ diff --git a/src/network.h b/src/network.h index c46c7b22c..997b8874c 100644 --- a/src/network.h +++ b/src/network.h @@ -36,6 +36,8 @@ int network_publish_rig_transceive_data(RIG *rig); int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line); HAMLIB_EXPORT(int) network_multicast_publisher_start(RIG *rig, const char *multicast_addr, int multicast_port, enum multicast_item_e items); HAMLIB_EXPORT(int) network_multicast_publisher_stop(RIG *rig); +HAMLIB_EXPORT(int) network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int multicast_port); +HAMLIB_EXPORT(int) network_multicast_receiver_stop(RIG *rig); __END_DECLS diff --git a/src/rig.c b/src/rig.c index 3c1d496b3..1165c8c70 100644 --- a/src/rig.c +++ b/src/rig.c @@ -670,7 +670,11 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->vfo_comp = 0.0; /* override it with preferences */ rs->current_vfo = RIG_VFO_CURR; /* we don't know yet! */ rs->tx_vfo = RIG_VFO_CURR; /* we don't know yet! */ - rs->poll_interval = 0; // disable polling by default + rs->poll_interval = 1000; // enable polling by default + rs->multicast_data_addr = "224.0.0.1"; // enable multicast data publishing by default + rs->multicast_data_port = 4532; + rs->multicast_cmd_addr = "224.0.0.2"; // enable multicast command server by default + rs->multicast_cmd_port = 4532; rs->lo_freq = 0; rs->cache.timeout_ms = 500; // 500ms cache timeout by default rs->cache.ptt = 0; @@ -1484,22 +1488,36 @@ int HAMLIB_API rig_open(RIG *rig) memcpy(&rs->pttport_deprecated, &rs->pttport, sizeof(hamlib_port_t_deprecated)); memcpy(&rs->dcdport_deprecated, &rs->dcdport, sizeof(hamlib_port_t_deprecated)); rig_flush_force(&rs->rigport, 1); -// if (rig->caps->rig_model != RIG_MODEL_NETRIGCTL) multicast_init(rig, "224.0.0.1", 4532); -// multicast_init(rig, "224.0.0.1", 4532); - char *multicast_addr = "224.0.0.1"; - int multicast_port = 4532; - enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE; -// | RIG_MULTICAST_SPECTRUM; - retval = network_multicast_publisher_start(rig, multicast_addr, - multicast_port, items); + + enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE + | RIG_MULTICAST_SPECTRUM; + retval = network_multicast_publisher_start(rig, rs->multicast_data_addr, + rs->multicast_data_port, items); if (retval != RIG_OK) { - rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_server failed: %s\n", __FILE__, + rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_publisher_start failed: %s\n", __FILE__, rigerror(retval)); // we will consider this non-fatal for now } + retval = network_multicast_receiver_start(rig, rs->multicast_cmd_addr, rs->multicast_cmd_port); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_receiver_start failed: %s\n", __FILE__, + rigerror(retval)); + // we will consider this non-fatal for now + } + + retval = rig_poll_routine_start(rig); + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: rig_poll_routine_start failed: %s\n", __FILE__, + rigerror(retval)); + // we will consider this non-fatal for now + } + RETURNFUNC2(RIG_OK); } @@ -1541,9 +1559,9 @@ int HAMLIB_API rig_close(RIG *rig) morse_data_handler_stop(rig); async_data_handler_stop(rig); + rig_poll_routine_stop(rig); + network_multicast_receiver_stop(rig); network_multicast_publisher_stop(rig); - //while(rs->multicast_publisher_run != 2) hl_usleep(10*1000); - //multicast_stop(rig); /* * Let the backend say 73s to the rig. diff --git a/src/token.h b/src/token.h index f3c4ad648..53d568798 100644 --- a/src/token.h +++ b/src/token.h @@ -131,6 +131,15 @@ #define TOK_OFFSET_VFOA TOKEN_FRONTEND(130) /** \brief rig: Add Hz to VFOB/Sub frequency set */ #define TOK_OFFSET_VFOB TOKEN_FRONTEND(131) +/** \brief rig: Multicast data UDP address for publishing rig data and state, default 224.0.0.1, value of 0.0.0.0 disables multicast data publishing */ +#define TOK_MULTICAST_DATA_ADDR TOKEN_FRONTEND(132) +/** \brief rig: Multicast data UDP port, default 4532 */ +#define TOK_MULTICAST_DATA_PORT TOKEN_FRONTEND(133) +/** \brief rig: Multicast command server UDP address for sending commands to rig, default 224.0.0.2, value of 0.0.0.0 disables multicast command server */ +#define TOK_MULTICAST_CMD_ADDR TOKEN_FRONTEND(134) +/** \brief rig: Multicast command server UDP port, default 4532 */ +#define TOK_MULTICAST_CMD_PORT TOKEN_FRONTEND(135) + /* * rotator specific tokens * (strictly, should be documented as rotator_internal) diff --git a/tests/rigctld.c b/tests/rigctld.c index dbf648ddc..33d0d6110 100644 --- a/tests/rigctld.c +++ b/tests/rigctld.c @@ -105,8 +105,6 @@ static struct option long_options[] = {"twiddle_rit", 1, 0, 'w'}, {"uplink", 1, 0, 'x'}, {"debug-time-stamps", 0, 0, 'Z'}, - {"multicast-addr", 1, 0, 'M'}, - {"multicast-port", 1, 0, 'n'}, {"password", 1, 0, 'A'}, {"rigctld-idle", 0, 0, 'R'}, {"bind-all", 0, 0, 'b'}, @@ -145,8 +143,6 @@ static int volatile ctrl_c; const char *portno = "4532"; const char *src_addr = NULL; /* INADDR_ANY */ -const char *multicast_addr = "0.0.0.0"; -int multicast_port = 4532; extern char rigctld_password[65]; char resp_sep = '\n'; extern int lock_mode; @@ -618,33 +614,6 @@ int main(int argc, char *argv[]) rig_set_debug_time_stamp(1); break; - case 'M': - if (!optarg) - { - usage(); /* wrong arg count */ - exit(1); - } - - multicast_addr = optarg; - break; - - case 'n': - if (!optarg) - { - usage(); /* wrong arg count */ - exit(1); - } - - multicast_port = atoi(optarg); - - if (multicast_port == 0) - { - fprintf(stderr, "Invalid multicast port: %s\n", optarg); - exit(1); - } - - break; - default: usage(); /* unknown option? */ exit(1); @@ -867,18 +836,6 @@ int main(int argc, char *argv[]) saved_result = result; - enum multicast_item_e items = RIG_MULTICAST_POLL | RIG_MULTICAST_TRANSCEIVE | - RIG_MULTICAST_SPECTRUM; - retcode = network_multicast_publisher_start(my_rig, multicast_addr, - multicast_port, items); - - if (retcode != RIG_OK) - { - rig_debug(RIG_DEBUG_ERR, "%s: network_multicast_server failed: %s\n", __FILE__, - rigerror(retcode)); - // we will consider this non-fatal for now - } - do { sock_listen = socket(result->ai_family, @@ -1148,8 +1105,6 @@ int main(int argc, char *argv[]) rig_close(my_rig); /* close port */ #endif - network_multicast_publisher_stop(my_rig); - rig_cleanup(my_rig); /* if you care about memory */ #ifdef __MINGW32__ @@ -1464,8 +1419,6 @@ void usage(void) " -w, --twiddle_rit suppress VFOB getfreq so RIT can be twiddled\n" " -x, --uplink set uplink get_freq ignore, 1=Sub, 2=Main\n" " -Z, --debug-time-stamps enable time stamps for debug messages\n" - " -M, --multicast-addr=addr set multicast UDP address, default 0.0.0.0 (off), recommend 224.0.1.1\n" - " -n, --multicast-port=port set multicast UDP port, default 4532\n" " -A, --password set password for rigctld access\n" " -R, --rigctld-idle make rigctld close the rig when no clients are connected\n" " -h, --help display this help and exit\n" From 6cb17e49dcf1219d9c44b684356797128daf4209 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Tue, 31 Oct 2023 17:34:31 -0500 Subject: [PATCH 12/32] Add post_ptt_delay for situations where more time is needed for external relay operations Detect Doppler frequency changes for Kenwood rigs and avoid querying frequency...just set it...to provide stable timing https://github.com/Hamlib/Hamlib/issues/1412 --- include/hamlib/rig.h | 2 ++ rigs/kenwood/kenwood.c | 16 +++++++++------- rigs/kenwood/kenwood.h | 2 +- src/conf.c | 23 ++++++++++++++++------- src/rig.c | 21 +++++++++++++++++++-- src/token.h | 1 + 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index 3816bbab0..163d008a6 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2380,6 +2380,7 @@ typedef struct hamlib_port { int fd_sync_error_read; /*!< file descriptor for reading synchronous data error codes */ #endif short timeout_retry; /*!< number of retries to make in case of read timeout errors, some serial interfaces may require this, 0 to disable */ + int post_ptt_delay; /*!< delay after PTT to allow for relays and such */ } hamlib_port_t; @@ -2759,6 +2760,7 @@ struct rig_state { void *morse_data_handler_priv_data; FIFO_RIG *fifo_morse; int port_multicast; /*!< May be different so this is initially a copy of rigctl'd port selection */ + int doppler; /*!< True if doppler changing detected */ }; /** diff --git a/rigs/kenwood/kenwood.c b/rigs/kenwood/kenwood.c index c5fd97688..4e87543ba 100644 --- a/rigs/kenwood/kenwood.c +++ b/rigs/kenwood/kenwood.c @@ -1862,15 +1862,17 @@ int kenwood_set_freq(RIG *rig, vfo_t vfo, freq_t freq) } // Malchite is so slow we don't do the get_freq - if (!RIG_IS_MALACHITE) + // And when we have detected Doppler operations we just set the freq all the time + // This should provide stable timing for set_ptt operation so relay delays are consistent + if (!RIG_IS_MALACHITE && rig->state.doppler == 0) { - rig_get_freq(rig, tvfo, &tfreq); + rig_get_freq(rig, tvfo, &tfreq); - if (tfreq == freq) - { - rig_debug(RIG_DEBUG_TRACE, "%s: no freq change needed\n", __func__); - RETURNFUNC2(RIG_OK); - } + if (tfreq == freq) + { + rig_debug(RIG_DEBUG_TRACE, "%s: no freq change needed\n", __func__); + RETURNFUNC2(RIG_OK); + } } switch (tvfo) diff --git a/rigs/kenwood/kenwood.h b/rigs/kenwood/kenwood.h index be0a28dd8..c455685ff 100644 --- a/rigs/kenwood/kenwood.h +++ b/rigs/kenwood/kenwood.h @@ -28,7 +28,7 @@ #include "token.h" #include "idx_builtin.h" -#define BACKEND_VER "20231025" +#define BACKEND_VER "20231031" #define EOM_KEN ';' #define EOM_TH '\r' diff --git a/src/conf.c b/src/conf.c index cc9291e3a..d5a47e32a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -61,6 +61,11 @@ static const struct confparams frontend_cfg_params[] = "Delay in ms between each command sent out", "0", RIG_CONF_NUMERIC, { .n = { 0, 1000, 1 } } }, + { + TOK_POST_PTT_DELAY, "post_ptt_delay", "Post ptt delay", + "Delay in ms after PTT is asserted", + "0", RIG_CONF_NUMERIC, { .n = { 0, 2000, 1 } } // 2000ms should be more than enough + }, { TOK_TIMEOUT, "timeout", "Timeout", "Timeout in ms", "0", RIG_CONF_NUMERIC, { .n = { 0, 10000, 1 } } @@ -71,7 +76,7 @@ static const struct confparams frontend_cfg_params[] = }, { TOK_TIMEOUT_RETRY, "timeout_retry", "Number of retries for read timeouts", - "Set the number of retries for data read timeouts that may occur especially with some serial interfaces", + "Set the # of retries for read timeouts that may occur with some serial interfaces", "1", RIG_CONF_NUMERIC, { .n = { 0, 100, 1 } } }, { @@ -91,8 +96,8 @@ static const struct confparams frontend_cfg_params[] = "0", RIG_CONF_NUMERIC, { .n = { 0.0, 1000.0, .001 } } }, { - TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in milliseconds", - "Polling interval in milliseconds for transceive emulation, value of 0 disables polling", + TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in ms", + "Polling interval in ms for transceive emulation, value of 0 disables polling", "0", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { @@ -102,7 +107,7 @@ static const struct confparams frontend_cfg_params[] = }, { TOK_PTT_PATHNAME, "ptt_pathname", "PTT path name", - "Path name to the device file of the Push-To-Talk", + "Path to the device of the Push-To-Talk", "/dev/rig", RIG_CONF_STRING, }, { @@ -117,7 +122,7 @@ static const struct confparams frontend_cfg_params[] = }, { TOK_DCD_PATHNAME, "dcd_pathname", "DCD path name", - "Path name to the device file of the Data Carrier Detect (or squelch)", + "Path to the device of the Data Carrier Detect (or squelch)", "/dev/rig", RIG_CONF_STRING, }, { @@ -172,12 +177,12 @@ static const struct confparams frontend_cfg_params[] = }, { TOK_ASYNC, "async", "Asynchronous data transfer support", - "True enables asynchronous data transfer for backends that support it. This allows use of transceive and spectrum data.", + "True enables async data for rigs that support it to allow use of transceive and spectrum data", "0", RIG_CONF_CHECKBUTTON, { } }, { TOK_TUNER_CONTROL_PATHNAME, "tuner_control_pathname", "Tuner script/program path name", - "Path name to a script/program to control a tuner with 1 argument of 0/1 for Tuner Off/On", + "Path to a program to control a tuner with 1 argument of 0/1 for Tuner Off/On", "hamlib_tuner_control", RIG_CONF_STRING, }, { @@ -770,6 +775,10 @@ static int frontend_get_conf2(RIG *rig, token_t token, char *val, int val_len) SNPRINTF(val, val_len, "%d", rs->rigport.post_write_delay); break; + case TOK_POST_PTT_DELAY: + SNPRINTF(val, val_len, "%d", rs->rigport.post_ptt_delay); + break; + case TOK_TIMEOUT: SNPRINTF(val, val_len, "%d", rs->rigport.timeout); break; diff --git a/src/rig.c b/src/rig.c index 3c1d496b3..2327426ab 100644 --- a/src/rig.c +++ b/src/rig.c @@ -1887,8 +1887,24 @@ int rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) rig_strvfo(vfo), freq); #endif - if (vfo == RIG_VFO_A || vfo == RIG_VFO_MAIN) { freq += rig->state.offset_vfoa; } - else if (vfo == RIG_VFO_B || vfo == RIG_VFO_SUB) { freq += rig->state.offset_vfob; } + if (vfo == RIG_VFO_A || vfo == RIG_VFO_MAIN) + { + if (rig->state.cache.freqMainA != freq && ((int)freq % 10) != 0) + { + rig->state.doppler = 1; + rig_debug(RIG_DEBUG_VERBOSE, "%s: doppler detected because old freq %f != new && new freq has 1Hz or such values\n", __func__, rig->state.cache.freqMainA); + } + freq += rig->state.offset_vfoa; + } + else if (vfo == RIG_VFO_B || vfo == RIG_VFO_SUB) + { + if (rig->state.cache.freqMainB != freq && ((int)freq % 10) != 0) + { + rig->state.doppler = 1; + rig_debug(RIG_DEBUG_VERBOSE, "%s: doppler detected because old freq %f != new && new freq has 1Hz or such values\n", __func__, rig->state.cache.freqMainA); + } + freq += rig->state.offset_vfob; + } if (rig->state.twiddle_state == TWIDDLE_ON) { @@ -3499,6 +3515,7 @@ int HAMLIB_API rig_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) memcpy(&rig->state.pttport_deprecated, &rig->state.pttport, sizeof(rig->state.pttport_deprecated)); + if (rig->state.rigport.post_ptt_delay > 0) hl_usleep(rig->state.rigport.post_ptt_delay*1000); ELAPSED2; RETURNFUNC(retcode); diff --git a/src/token.h b/src/token.h index f3c4ad648..29fbe9a1e 100644 --- a/src/token.h +++ b/src/token.h @@ -97,6 +97,7 @@ #define TOK_TUNER_CONTROL_PATHNAME TOKEN_FRONTEND(38) /** \brief Number of retries permitted in case of read timeouts */ #define TOK_TIMEOUT_RETRY TOKEN_FRONTEND(39) +#define TOK_POST_PTT_DELAY TOKEN_FRONTEND(40) /* * rig specific tokens From fccc540353269ccf89aa70c37ec060fe30c78039 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Tue, 31 Oct 2023 22:40:30 -0500 Subject: [PATCH 13/32] Add 60M exception for FT897/D https://github.com/Hamlib/Hamlib/issues/1414 --- rigs/yaesu/newcat.c | 6 +++++- rigs/yaesu/newcat.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rigs/yaesu/newcat.c b/rigs/yaesu/newcat.c index 8f69532c7..2759a8d0f 100644 --- a/rigs/yaesu/newcat.c +++ b/rigs/yaesu/newcat.c @@ -211,6 +211,8 @@ const cal_table_float_t yaesu_default_id_meter_cal = static ncboolean is_ft450; static ncboolean is_ft710; static ncboolean is_ft891; +static ncboolean is_ft897; +static ncboolean is_ft897d; static ncboolean is_ft950; static ncboolean is_ft991; static ncboolean is_ft2000; @@ -497,6 +499,8 @@ int newcat_init(RIG *rig) is_ft450 = newcat_is_rig(rig, RIG_MODEL_FT450); is_ft450 |= newcat_is_rig(rig, RIG_MODEL_FT450D); is_ft891 = newcat_is_rig(rig, RIG_MODEL_FT891); + is_ft897 = newcat_is_rig(rig, RIG_MODEL_FT897); + is_ft897d = newcat_is_rig(rig, RIG_MODEL_FT897D); is_ft950 = newcat_is_rig(rig, RIG_MODEL_FT950); is_ft991 = newcat_is_rig(rig, RIG_MODEL_FT991); is_ft2000 = newcat_is_rig(rig, RIG_MODEL_FT2000); @@ -847,7 +851,7 @@ int newcat_60m_exception(RIG *rig, freq_t freq, mode_t mode) } // some rigs need to skip freq/mode settings as 60M only operates in memory mode - if (is_ft991) { return 1; } + if (is_ft991 || is_ft897 || is_ft897d) { return 1; } if (!is_ftdx10 && !is_ft710 && !is_ftdx101d && !is_ftdx101mp) { return 0; } diff --git a/rigs/yaesu/newcat.h b/rigs/yaesu/newcat.h index d7c451587..789b1ea74 100644 --- a/rigs/yaesu/newcat.h +++ b/rigs/yaesu/newcat.h @@ -50,7 +50,7 @@ typedef char ncboolean; /* shared function version */ -#define NEWCAT_VER "20231024" +#define NEWCAT_VER "20231031" /* Hopefully large enough for future use, 128 chars plus '\0' */ #define NEWCAT_DATA_LEN 129 From 701bc365599ee0cfe0174da58e5be56d38ac0a5a Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Tue, 31 Oct 2023 23:03:17 -0500 Subject: [PATCH 14/32] Update simts450.c --- simulators/simts450.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/simulators/simts450.c b/simulators/simts450.c index e29bea21d..08ab439bb 100644 --- a/simulators/simts450.c +++ b/simulators/simts450.c @@ -27,6 +27,7 @@ int filternum = 7; int datamode = 0; int vfo, vfo_tx, ptt, ptt_data, ptt_mic, ptt_tune; int tomode = 0; +int keyspd = 25; int getmyline(int fd, char *buf) @@ -203,17 +204,6 @@ int main(int argc, char *argv[]) WRITE(fd, buf, strlen(buf)); } - else if (strncmp(buf, "AI", 2) == 0) - { - if (strcmp(buf, "AI;")) - { - printf("%s\n", buf); - hl_usleep(mysleep * 1000); - pbuf = "AI0;"; - WRITE(fd, pbuf, strlen(pbuf)); - } - } - else if (strcmp(buf, "VS;") == 0) { printf("%s\n", buf); @@ -251,10 +241,9 @@ int main(int argc, char *argv[]) { sscanf(buf, "FB%d", &freqb); } - else if (strncmp(buf, "AI;", 3) == 0) + else if (strncmp(buf, "AI", 2) == 0) { - SNPRINTF(buf, sizeof(buf), "AI0;"); - WRITE(fd, buf, strlen(buf)); + // nothing to do yet } else if (strncmp(buf, "PS;", 3) == 0) @@ -314,7 +303,7 @@ int main(int argc, char *argv[]) } else if (strcmp(buf, "FT;") == 0) { - SNPRINTF(buf, sizeof(buf), "FR%d;", vfo_tx); + SNPRINTF(buf, sizeof(buf), "FT%d;", vfo_tx); WRITE(fd, buf, strlen(buf)); } else if (strncmp(buf, "FT", 2) == 0) @@ -358,12 +347,11 @@ int main(int argc, char *argv[]) } } + else if (strlen(buf) > 0) { fprintf(stderr, "Unknown command: %s\n", buf); } - - } return 0; From 7f8f470ef75b3cb9424ab01667da6b5b1a23d03d Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Wed, 1 Nov 2023 09:17:53 -0500 Subject: [PATCH 15/32] Hopefully fix compile warning in newcat.c --- rigs/yaesu/newcat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rigs/yaesu/newcat.c b/rigs/yaesu/newcat.c index 2759a8d0f..5a82853ed 100644 --- a/rigs/yaesu/newcat.c +++ b/rigs/yaesu/newcat.c @@ -5463,7 +5463,7 @@ int newcat_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) } break; default: - rig_debug(RIG_DEBUG_ERR, "%s: unknown level=%08lx\n", __func__, level); + rig_debug(RIG_DEBUG_ERR, "%s: unknown level=%08llx\n", __func__, (long long unsigned int)level); RETURNFUNC(-RIG_EINVAL); } From 8484b132f265e99257f64dcbc99d9e2da732e96c Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Wed, 1 Nov 2023 10:14:18 -0500 Subject: [PATCH 16/32] Add FTDX5000 to 60M exception handling --- rigs/yaesu/newcat.c | 2 +- rigs/yaesu/newcat.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rigs/yaesu/newcat.c b/rigs/yaesu/newcat.c index 5a82853ed..556e065d2 100644 --- a/rigs/yaesu/newcat.c +++ b/rigs/yaesu/newcat.c @@ -851,7 +851,7 @@ int newcat_60m_exception(RIG *rig, freq_t freq, mode_t mode) } // some rigs need to skip freq/mode settings as 60M only operates in memory mode - if (is_ft991 || is_ft897 || is_ft897d) { return 1; } + if (is_ft991 || is_ft897 || is_ft897d || is_ftdx5000) { return 1; } if (!is_ftdx10 && !is_ft710 && !is_ftdx101d && !is_ftdx101mp) { return 0; } diff --git a/rigs/yaesu/newcat.h b/rigs/yaesu/newcat.h index 789b1ea74..29a639eb7 100644 --- a/rigs/yaesu/newcat.h +++ b/rigs/yaesu/newcat.h @@ -50,7 +50,7 @@ typedef char ncboolean; /* shared function version */ -#define NEWCAT_VER "20231031" +#define NEWCAT_VER "20231101" /* Hopefully large enough for future use, 128 chars plus '\0' */ #define NEWCAT_DATA_LEN 129 From f4f4d122a850bd2cffe586a2850b45fc5fa1b6ca Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Wed, 1 Nov 2023 15:54:36 -0500 Subject: [PATCH 17/32] Improve post_ptt_delay behavior https://github.com/Hamlib/Hamlib/issues/1412 --- src/rig.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/rig.c b/src/rig.c index 2327426ab..b4e031c33 100644 --- a/src/rig.c +++ b/src/rig.c @@ -1887,24 +1887,27 @@ int rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) rig_strvfo(vfo), freq); #endif - if (vfo == RIG_VFO_A || vfo == RIG_VFO_MAIN) + if (rig->state.doppler == 0) + { + if (vfo == RIG_VFO_A || vfo == RIG_VFO_MAIN || (vfo == RIG_VFO_CURR && rig->state.current_vfo == RIG_VFO_A)) { - if (rig->state.cache.freqMainA != freq && ((int)freq % 10) != 0) + if (rig->state.cache.freqMainA != freq && (((int)freq % 10) != 0)) { rig->state.doppler = 1; - rig_debug(RIG_DEBUG_VERBOSE, "%s: doppler detected because old freq %f != new && new freq has 1Hz or such values\n", __func__, rig->state.cache.freqMainA); + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): potential doppler detected because old freq %f != new && new freq has 1Hz or such values\n", __func__, __LINE__, rig->state.cache.freqMainA); } freq += rig->state.offset_vfoa; } - else if (vfo == RIG_VFO_B || vfo == RIG_VFO_SUB) + else if (vfo == RIG_VFO_B || vfo == RIG_VFO_SUB || (vfo == RIG_VFO_CURR && rig->state.current_vfo == RIG_VFO_B)) { if (rig->state.cache.freqMainB != freq && ((int)freq % 10) != 0) { rig->state.doppler = 1; - rig_debug(RIG_DEBUG_VERBOSE, "%s: doppler detected because old freq %f != new && new freq has 1Hz or such values\n", __func__, rig->state.cache.freqMainA); + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): potential doppler detected because old freq %f != new && new freq has 1Hz or such values\n", __func__, __LINE__, rig->state.cache.freqMainB); } freq += rig->state.offset_vfob; } + } if (rig->state.twiddle_state == TWIDDLE_ON) { @@ -2311,9 +2314,11 @@ int HAMLIB_API rig_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) __LINE__, rig->state.vfo_opt, rig->caps->rig_model); // If we're in vfo_mode then rigctld will do any VFO swapping we need + // If we detected doppler we skip the frequency check to make timing more consistent for relay control if ((caps->targetable_vfo & RIG_TARGETABLE_FREQ) || vfo == RIG_VFO_CURR || vfo == rig->state.current_vfo - || (rig->state.vfo_opt == 1 && rig->caps->rig_model == RIG_MODEL_NETRIGCTL)) + || (rig->state.vfo_opt == 1 && rig->caps->rig_model == RIG_MODEL_NETRIGCTL + && rig->state.doppler == 0)) { // If rig does not have set_vfo we need to change vfo if (vfo == RIG_VFO_CURR && caps->set_vfo == NULL) From 0dbe24ff424854ce849c0c159bcd6f938f61d995 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Wed, 1 Nov 2023 16:25:09 -0500 Subject: [PATCH 18/32] Restore vfo offset lines accidentally removed in rig.c --- src/rig.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rig.c b/src/rig.c index b4e031c33..aab981a94 100644 --- a/src/rig.c +++ b/src/rig.c @@ -1909,6 +1909,9 @@ int rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) } } + if (vfo == RIG_VFO_A || vfo == RIG_VFO_MAIN) { freq += rig->state.offset_vfoa; } + else if (vfo == RIG_VFO_B || vfo == RIG_VFO_SUB) { freq += rig->state.offset_vfob; } + if (rig->state.twiddle_state == TWIDDLE_ON) { // we keep skipping set_freq while the vfo knob is in motion From f618540f227d634c05dfa69d2237fb1350a0eb77 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:11:57 +0200 Subject: [PATCH 19/32] Join multicast group to enable UDP packet reception --- src/network.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/network.c b/src/network.c index cde6e473a..0c57ba2b9 100644 --- a/src/network.c +++ b/src/network.c @@ -728,7 +728,7 @@ static int multicast_publisher_write_packet_header(RIG *rig, if (rs->multicast_publisher_priv_data == NULL) { // Silently ignore if multicast publisher is not enabled - RETURNFUNC2(RIG_OK); + return RIG_OK; } mcast_publisher_priv = (multicast_publisher_priv_data *) @@ -741,10 +741,10 @@ static int multicast_publisher_write_packet_header(RIG *rig, if (result != RIG_OK) { - RETURNFUNC2(result); + return result; } - RETURNFUNC2(RIG_OK); + return RIG_OK; } // cppcheck-suppress unusedFunction @@ -996,17 +996,38 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); + int optval = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, + strerror(errno)); + return NULL; + } + memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); dest_addr.sin_port = htons(args->multicast_port); - if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) { + if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) + { rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); return NULL; } + struct ip_mreq mreq; + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = inet_addr(args->multicast_addr); + mreq.imr_interface.s_addr = INADDR_ANY; + + if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error joining multicast group %s:%d: %s\n", __func__, + args->multicast_addr, args->multicast_port, strerror(errno)); + return NULL; + } + rs->multicast_receiver_run = 1; while (rs->multicast_receiver_run == 1) @@ -1017,12 +1038,15 @@ void *multicast_receiver(void *arg) result = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *) &client_addr, &client_len); - rig_debug(RIG_DEBUG_ERR, "%s: received multicast packet with result %ld\n", __func__, result); - if (result <= 0) { if (result < 0) { + if (errno == EAGAIN) + { + hl_usleep(100 * 1000); + continue; + } rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); } @@ -1030,7 +1054,7 @@ void *multicast_receiver(void *arg) } // TODO: handle commands from multicast clients - rig_debug(RIG_DEBUG_ERR, "%s: received %ld bytes=%s\n", __func__, result, data); + rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__, result, (int) result, data); // TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot // TODO: new logic in publisher needs to be written for other types of responses From f36822ac20b43d7257ded7ba25b8162b4c5eda31 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:18:27 +0200 Subject: [PATCH 20/32] Fix socket option --- src/network.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network.c b/src/network.c index 0c57ba2b9..a1208fba9 100644 --- a/src/network.c +++ b/src/network.c @@ -996,10 +996,10 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); - int optval = 1; - if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) + char optval = 1; + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { - rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, + rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP address reuse: %s\n", __func__, strerror(errno)); return NULL; } From 5fb2e68e07c8b972d82b68dc4b0a5b2d79045623 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:20:15 +0200 Subject: [PATCH 21/32] Fix warning --- src/network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network.c b/src/network.c index a1208fba9..de3687873 100644 --- a/src/network.c +++ b/src/network.c @@ -1054,7 +1054,7 @@ void *multicast_receiver(void *arg) } // TODO: handle commands from multicast clients - rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__, result, (int) result, data); + rig_debug(RIG_DEBUG_VERBOSE, "%s: received %ld bytes of data: %.*s\n", __func__, (long) result, (int) result, data); // TODO: if a new snapshot needs to be sent, call network_publish_rig_poll_data() and the publisher routine will send out a snapshot // TODO: new logic in publisher needs to be written for other types of responses From 53ff60d98156b277c369fbdd63ce3da2893594d2 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:32:05 +0200 Subject: [PATCH 22/32] Fix socket option again --- src/network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network.c b/src/network.c index de3687873..e07e2f4fe 100644 --- a/src/network.c +++ b/src/network.c @@ -996,7 +996,7 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); - char optval = 1; + int optval = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP address reuse: %s\n", __func__, @@ -1009,7 +1009,7 @@ void *multicast_receiver(void *arg) dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); dest_addr.sin_port = htons(args->multicast_port); - if ((bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr))) < 0) + if (bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0) { rig_debug(RIG_DEBUG_ERR, "%s: error binding UDP socket to %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); From 88e86fb62a17a14dc681a1ed4336d6268af709cc Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 00:35:47 +0200 Subject: [PATCH 23/32] Comment out SO_REUSEADDR for now --- src/network.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/network.c b/src/network.c index e07e2f4fe..3b590fb7a 100644 --- a/src/network.c +++ b/src/network.c @@ -996,6 +996,8 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); +// Not working right now +#if 0 int optval = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { @@ -1003,6 +1005,7 @@ void *multicast_receiver(void *arg) strerror(errno)); return NULL; } +#endif memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; From 5b86d4efffe86b9893093790bcb0f74c86e11a9e Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Thu, 2 Nov 2023 18:00:16 +0200 Subject: [PATCH 24/32] Bind multicast receiver to INADDR_ANY on Windows, because binding to multicast group IPs doesn't seem to work. Use a different port number for (4351) multicast receiver commands to avoid multicast publisher packets ending up in the receiver (loopback issue). --- src/conf.c | 2 +- src/network.c | 18 +++++++++++++++--- src/rig.c | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/conf.c b/src/conf.c index 3c17ecb11..349456d35 100644 --- a/src/conf.c +++ b/src/conf.c @@ -208,7 +208,7 @@ static const struct confparams frontend_cfg_params[] = { TOK_MULTICAST_CMD_PORT, "multicast_cmd_port", "Multicast command server UDP port", "Multicast data UDP port for sending commands to rig", - "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + "4531", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { RIG_CONF_END, NULL, } diff --git a/src/network.c b/src/network.c index 3b590fb7a..877ab510a 100644 --- a/src/network.c +++ b/src/network.c @@ -996,8 +996,6 @@ void *multicast_receiver(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast receiver\n", __FILE__, __LINE__); -// Not working right now -#if 0 int optval = 1; if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { @@ -1005,11 +1003,25 @@ void *multicast_receiver(void *arg) strerror(errno)); return NULL; } + +#if defined(SO_REUSEPORT) + // Windows does not have SO_REUSEPORT. However, SO_REUSEADDR works in a similar way. + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, + strerror(errno)); + return NULL; + } #endif memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; +#ifdef __MINGW32__ + // Windows cannot bind to multicast group addresses for some unknown reason + dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); +#else dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); +#endif dest_addr.sin_port = htons(args->multicast_port); if (bind(socket_fd, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0) @@ -1022,7 +1034,7 @@ void *multicast_receiver(void *arg) struct ip_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = inet_addr(args->multicast_addr); - mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { diff --git a/src/rig.c b/src/rig.c index 1165c8c70..1998ddfc6 100644 --- a/src/rig.c +++ b/src/rig.c @@ -674,7 +674,7 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->multicast_data_addr = "224.0.0.1"; // enable multicast data publishing by default rs->multicast_data_port = 4532; rs->multicast_cmd_addr = "224.0.0.2"; // enable multicast command server by default - rs->multicast_cmd_port = 4532; + rs->multicast_cmd_port = 4531; rs->lo_freq = 0; rs->cache.timeout_ms = 500; // 500ms cache timeout by default rs->cache.ptt = 0; From 40a8cedc9da307c00ca7b4bd591ffb69542a9cde Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Thu, 2 Nov 2023 17:06:39 -0500 Subject: [PATCH 25/32] get_conf/set_conf now shows parameters for rigctl and rotctl. Using it with rigctld and rotctld sends output to console instead of remote client https://github.com/Hamlib/Hamlib/issues/971 --- tests/dumpcaps.h | 3 +++ tests/dumpcaps_rot.h | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/dumpcaps.h create mode 100644 tests/dumpcaps_rot.h diff --git a/tests/dumpcaps.h b/tests/dumpcaps.h new file mode 100644 index 000000000..6b1562d4c --- /dev/null +++ b/tests/dumpcaps.h @@ -0,0 +1,3 @@ +#include + +int dumpconf_list(RIG *rig, FILE *fout); diff --git a/tests/dumpcaps_rot.h b/tests/dumpcaps_rot.h new file mode 100644 index 000000000..4e00e4181 --- /dev/null +++ b/tests/dumpcaps_rot.h @@ -0,0 +1,3 @@ +#include + +int dumpconf_list(ROT *rot, FILE *fout); From 41c891251f4641d2aeb292e76654ed88b9f85a90 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Fri, 3 Nov 2023 17:18:49 +0200 Subject: [PATCH 26/32] Fix multicast receiver shutdown by using non-blocking sockets and select(). Use the same port number 4532 for both state snapshots and commands -- it is enough to have different multicast groups for them. Publish multicast state snapshots more often (change detection interval now set to 50ms), but at least at interval defined by poll_interval. --- src/conf.c | 2 +- src/event.c | 15 ++++++++- src/network.c | 87 +++++++++++++++++++++++++++++++++++++++++++++------ src/rig.c | 2 +- 4 files changed, 93 insertions(+), 13 deletions(-) diff --git a/src/conf.c b/src/conf.c index 349456d35..3c17ecb11 100644 --- a/src/conf.c +++ b/src/conf.c @@ -208,7 +208,7 @@ static const struct confparams frontend_cfg_params[] = { TOK_MULTICAST_CMD_PORT, "multicast_cmd_port", "Multicast command server UDP port", "Multicast data UDP port for sending commands to rig", - "4531", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + "4532", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } }, { RIG_CONF_END, NULL, } diff --git a/src/event.c b/src/event.c index 5cdedc43c..c9f6ca9de 100644 --- a/src/event.c +++ b/src/event.c @@ -87,6 +87,10 @@ void *rig_poll_routine(void *arg) // Rig cache time should be equal to rig poll interval (should be set automatically by rigctld at least) rig_set_cache_timeout_ms(rig, HAMLIB_CACHE_ALL, rs->poll_interval); + // Attempt to detect changes with the interval below (in milliseconds) + int change_detection_interval = 50; + int interval_count = 0; + update_occurred = 0; while (rs->poll_routine_thread_run) @@ -333,9 +337,18 @@ void *rig_poll_routine(void *arg) { network_publish_rig_poll_data(rig); update_occurred = 0; + interval_count = 0; } - hl_usleep(rs->poll_interval * 1000); + hl_usleep(change_detection_interval * 1000); + interval_count++; + + // Publish updates every poll_interval if no changes have been detected + if (interval_count >= (rs->poll_interval / change_detection_interval)) + { + interval_count = 0; + network_publish_rig_poll_data(rig); + } } rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping rig poll routine thread\n", diff --git a/src/network.c b/src/network.c index 877ab510a..4d34527a9 100644 --- a/src/network.c +++ b/src/network.c @@ -1047,9 +1047,43 @@ void *multicast_receiver(void *arg) while (rs->multicast_receiver_run == 1) { - ssize_t result; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); + fd_set rfds, efds; + struct timeval timeout; + int select_result; + ssize_t result; + + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(socket_fd, &rfds); + efds = rfds; + + select_result = select(socket_fd + 1, &rfds, NULL, &efds, &timeout); + if (select_result == 0) + { + // Select timed out + continue; + } + + if (select_result < 0) + { + rig_debug(RIG_DEBUG_ERR, + "%s(): select() failed when reading UDP multicast socket data: %s\n", + __func__, + strerror(errno)); + + break; + } + + if (FD_ISSET(socket_fd, &efds)) + { + rig_debug(RIG_DEBUG_ERR, + "%s(): fd error when reading UDP multicast socket data\n", __func__); + break; + } result = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *) &client_addr, &client_len); @@ -1057,9 +1091,8 @@ void *multicast_receiver(void *arg) { if (result < 0) { - if (errno == EAGAIN) + if (errno == 0 || errno == EAGAIN || errno == EWOULDBLOCK) { - hl_usleep(100 * 1000); continue; } rig_debug(RIG_DEBUG_ERR, "%s: error receiving from UDP socket %s:%d: %s\n", __func__, @@ -1128,13 +1161,6 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, } socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - u_long mode = 1; // Enable non-blocking mode -#ifdef __MINGW32__ - ioctlsocket(socket_fd, FIONBIO, &mode); -#else - ioctl(socket_fd, FIONBIO, &mode); -#endif - if (socket_fd < 0) { @@ -1143,6 +1169,24 @@ int network_multicast_publisher_start(RIG *rig, const char *multicast_addr, RETURNFUNC(-RIG_EIO); } + // Enable non-blocking mode + u_long mode = 1; +#ifdef __MINGW32__ + if (ioctlsocket(socket_fd, FIONBIO, &mode) == SOCKET_ERROR) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#else + if (ioctl(socket_fd, FIONBIO, &mode) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#endif + if (items & RIG_MULTICAST_TRANSCEIVE) { rig_debug(RIG_DEBUG_VERBOSE, "%s(%d) MULTICAST_TRANSCEIVE enabled\n", __FILE__, @@ -1310,6 +1354,24 @@ int network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int m RETURNFUNC(-RIG_EIO); } + // Enable non-blocking mode + u_long mode = 1; +#ifdef __MINGW32__ + if (ioctlsocket(socket_fd, FIONBIO, &mode) == SOCKET_ERROR) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#else + if (ioctl(socket_fd, FIONBIO, &mode) < 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: error enabling non-blocking mode for socket: %s", __func__, + strerror(errno)); + RETURNFUNC(-RIG_EIO); + } +#endif + rs->multicast_receiver_run = 1; rs->multicast_receiver_priv_data = calloc(1, sizeof(multicast_receiver_priv_data)); @@ -1371,6 +1433,11 @@ int network_multicast_receiver_stop(RIG *rig) // Close the socket first to stop the routine if (mcast_receiver_priv->args.socket_fd >= 0) { +#ifdef __MINGW32__ + shutdown(mcast_receiver_priv->args.socket_fd, SD_BOTH); +#else + shutdown(mcast_receiver_priv->args.socket_fd, SHUT_RDWR); +#endif close(mcast_receiver_priv->args.socket_fd); } diff --git a/src/rig.c b/src/rig.c index 1998ddfc6..1165c8c70 100644 --- a/src/rig.c +++ b/src/rig.c @@ -674,7 +674,7 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->multicast_data_addr = "224.0.0.1"; // enable multicast data publishing by default rs->multicast_data_port = 4532; rs->multicast_cmd_addr = "224.0.0.2"; // enable multicast command server by default - rs->multicast_cmd_port = 4531; + rs->multicast_cmd_port = 4532; rs->lo_freq = 0; rs->cache.timeout_ms = 500; // 500ms cache timeout by default rs->cache.ptt = 0; From 31298bb91711d71356b2309670321a88a11f4582 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Sun, 5 Nov 2023 16:39:49 -0600 Subject: [PATCH 27/32] Reduce debug in icom.c --- rigs/icom/icom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rigs/icom/icom.c b/rigs/icom/icom.c index b6b3cdfd1..adf8898ce 100644 --- a/rigs/icom/icom.c +++ b/rigs/icom/icom.c @@ -3937,7 +3937,7 @@ int icom_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) for (i = 0; extcmds && extcmds[i].id.s != 0; i++) { - rig_debug(RIG_DEBUG_TRACE, "%s: i=%d\n", __func__, i); + //rig_debug(RIG_DEBUG_TRACE, "%s: i=%d\n", __func__, i); if (extcmds[i].cmdparamtype == CMD_PARAM_TYPE_LEVEL && extcmds[i].id.s == level) { From 43d1fbb3230c74e62f47dc510aeafb072443aca5 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Mon, 6 Nov 2023 00:42:02 +0200 Subject: [PATCH 28/32] Add more detailed rig communication status to rig_state and multicast state data packets. Handle SIGINT and SIGTERM in a consistent way in both rigctl and rigctld. --- include/hamlib/rig.h | 12 ++++ src/conf.c | 29 ++++++++ src/event.c | 10 ++- src/misc.c | 36 ++++++++++ src/network.c | 2 + src/network.h | 1 + src/rig.c | 19 ++++-- src/snapshot_data.c | 29 +++----- src/snapshot_data.h | 1 + src/token.h | 1 + tests/rigctl.c | 154 ++++++++++++++++++++++++++++++++++++++++++- tests/rigctld.c | 26 +++++++- 12 files changed, 291 insertions(+), 29 deletions(-) diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index dc62832e7..a9080cee2 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2577,6 +2577,15 @@ struct multicast_s //#endif }; +typedef unsigned int rig_comm_status_t; + +#define RIG_COMM_STATUS_OK 0x00 +#define RIG_COMM_STATUS_CONNECTING 0x01 +#define RIG_COMM_STATUS_DISCONNECTED 0x02 +#define RIG_COMM_STATUS_TERMINATED 0x03 +#define RIG_COMM_STATUS_WARNING 0x04 +#define RIG_COMM_STATUS_ERROR 0x05 + /** * \brief Rig state containing live data and customized fields. * @@ -2766,6 +2775,8 @@ struct rig_state { int multicast_cmd_port; /*!< Multicast command server UDP port for sending commands to rig */ volatile int multicast_receiver_run; void *multicast_receiver_priv_data; + rig_comm_status_t comm_status; /*!< Detailed rig control status */ + char device_id[HAMLIB_RIGNAMSIZ]; }; /** @@ -3710,6 +3721,7 @@ extern HAMLIB_EXPORT(const char *) rig_strscan(scan_t scan); extern HAMLIB_EXPORT(const char *) rig_strstatus(enum rig_status_e status); extern HAMLIB_EXPORT(const char *) rig_strmtype(chan_type_t mtype); extern HAMLIB_EXPORT(const char *) rig_strspectrummode(enum rig_spectrum_mode_e mode); +extern HAMLIB_EXPORT(const char *) rig_strcommstatus(rig_comm_status_t vfo); extern HAMLIB_EXPORT(rmode_t) rig_parse_mode(const char *s); extern HAMLIB_EXPORT(vfo_t) rig_parse_vfo(const char *s); diff --git a/src/conf.c b/src/conf.c index 24ea22d78..99fad9154 100644 --- a/src/conf.c +++ b/src/conf.c @@ -89,6 +89,11 @@ static const struct confparams frontend_cfg_params[] = "The tx/rx range list name", "Default", RIG_CONF_STRING }, + { + TOK_DEVICE_ID, "device_id", "Device ID", + "User-specified device ID for multicast state data and commands", + "", RIG_CONF_STRING, + }, { TOK_VFO_COMP, "vfo_comp", "VFO compensation", @@ -627,6 +632,10 @@ static int frontend_set_conf(RIG *rig, token_t token, const char *val) strncpy(rs->dcdport_deprecated.pathname, val, HAMLIB_FILPATHLEN - 1); break; + case TOK_DEVICE_ID: + strncpy(rs->device_id, val, HAMLIB_RIGNAMSIZ - 1); + break; + case TOK_VFO_COMP: rs->vfo_comp = atof(val); @@ -989,6 +998,10 @@ static int frontend_get_conf2(RIG *rig, token_t token, char *val, int val_len) strcpy(val, s); break; + case TOK_DEVICE_ID: + SNPRINTF(val, val_len, "%s", rs->device_id); + break; + case TOK_VFO_COMP: SNPRINTF(val, val_len, "%f", rs->vfo_comp); break; @@ -1149,6 +1162,22 @@ static int frontend_get_conf2(RIG *rig, token_t token, char *val, int val_len) SNPRINTF(val, val_len, "%d", rs->rigport.timeout_retry); break; + case TOK_MULTICAST_DATA_ADDR: + SNPRINTF(val, val_len, "%s", rs->multicast_data_addr); + break; + + case TOK_MULTICAST_DATA_PORT: + SNPRINTF(val, val_len, "%d", rs->multicast_data_port); + break; + + case TOK_MULTICAST_CMD_ADDR: + SNPRINTF(val, val_len, "%s", rs->multicast_cmd_addr); + break; + + case TOK_MULTICAST_CMD_PORT: + SNPRINTF(val, val_len, "%d", rs->multicast_cmd_port); + break; + default: return -RIG_EINVAL; } diff --git a/src/event.c b/src/event.c index c9f6ca9de..6f70e37af 100644 --- a/src/event.c +++ b/src/event.c @@ -64,8 +64,6 @@ typedef struct rig_poll_routine_priv_data_s rig_poll_routine_args args; } rig_poll_routine_priv_data; -// TODO: Where to start/stop rig poll routine? - void *rig_poll_routine(void *arg) { rig_poll_routine_args *args = (rig_poll_routine_args *)arg; @@ -93,6 +91,8 @@ void *rig_poll_routine(void *arg) update_occurred = 0; + network_publish_rig_poll_data(rig); + while (rs->poll_routine_thread_run) { if (rig->state.cache.freqMainA != freq_main_a) @@ -351,6 +351,8 @@ void *rig_poll_routine(void *arg) } } + network_publish_rig_poll_data(rig); + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping rig poll routine thread\n", __FILE__, __LINE__); @@ -408,6 +410,8 @@ int rig_poll_routine_start(RIG *rig) RETURNFUNC(-RIG_EINTERNAL); } + network_publish_rig_poll_data(rig); + RETURNFUNC(RIG_OK); } @@ -453,6 +457,8 @@ int rig_poll_routine_stop(RIG *rig) poll_routine_priv->thread_id = 0; } + network_publish_rig_poll_data(rig); + free(rs->poll_routine_priv_data); rs->poll_routine_priv_data = NULL; diff --git a/src/misc.c b/src/misc.c index 2a13709dd..67a96205f 100644 --- a/src/misc.c +++ b/src/misc.c @@ -2707,6 +2707,42 @@ const char *HAMLIB_API rig_get_caps_cptr(rig_model_t rig_model, } } +static const struct +{ + rig_comm_status_t status; + const char *str; +} comm_status_str[] = +{ + { RIG_COMM_STATUS_OK, "OK" }, + { RIG_COMM_STATUS_CONNECTING, "CONNECTING" }, + { RIG_COMM_STATUS_DISCONNECTED, "DISCONNECTED" }, + { RIG_COMM_STATUS_TERMINATED, "TERMINATIED" }, + { RIG_COMM_STATUS_WARNING, "WARNING" }, + { RIG_COMM_STATUS_ERROR, "ERROR" }, + { 0xffffffff, "" }, +}; + +/** + * \brief Convert enum RIG_COMM_STATUS... to alpha string + * \param vfo RIG_COMM_STATUS_... + * \return alpha string + */ +const char *HAMLIB_API rig_strcommstatus(rig_comm_status_t status) +{ + int i; + + for (i = 0; comm_status_str[i].str[0] != '\0'; i++) + { + if (status == comm_status_str[i].status) + { + return comm_status_str[i].str; + } + } + + return ""; +} + + void errmsg(int err, char *s, const char *func, const char *file, int line) { rig_debug(RIG_DEBUG_ERR, "%s(%s:%d): %s: %s\b", __func__, file, line, s, diff --git a/src/network.c b/src/network.c index 4d34527a9..c4754cbd1 100644 --- a/src/network.c +++ b/src/network.c @@ -921,6 +921,8 @@ void *multicast_publisher(void *arg) rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Starting multicast publisher\n", __FILE__, __LINE__); + snapshot_init(); + memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr(args->multicast_addr); diff --git a/src/network.h b/src/network.h index 997b8874c..7dc9c517c 100644 --- a/src/network.h +++ b/src/network.h @@ -34,6 +34,7 @@ void network_flush(hamlib_port_t *rp); int network_publish_rig_poll_data(RIG *rig); int network_publish_rig_transceive_data(RIG *rig); int network_publish_rig_spectrum_data(RIG *rig, struct rig_spectrum_line *line); +int network_publish_rig_status_change(RIG *rig, int32_t status); HAMLIB_EXPORT(int) network_multicast_publisher_start(RIG *rig, const char *multicast_addr, int multicast_port, enum multicast_item_e items); HAMLIB_EXPORT(int) network_multicast_publisher_stop(RIG *rig); HAMLIB_EXPORT(int) network_multicast_receiver_start(RIG *rig, const char *multicast_addr, int multicast_port); diff --git a/src/rig.c b/src/rig.c index 57d6a951d..a080cd7d8 100644 --- a/src/rig.c +++ b/src/rig.c @@ -593,6 +593,7 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) #if defined(HAVE_PTHREAD) rs->rigport.asyncio = 0; #endif + rig->state.comm_status = RIG_COMM_STATUS_CONNECTING; rs->tuner_control_pathname = DEFAULT_TUNER_CONTROL_PATHNAME; @@ -1046,6 +1047,8 @@ int HAMLIB_API rig_open(RIG *rig) RETURNFUNC2(-RIG_EINVAL); } + rs->comm_status = RIG_COMM_STATUS_CONNECTING; + rs->rigport.fd = -1; if (rs->rigport.type.rig == RIG_PORT_SERIAL) @@ -1094,6 +1097,7 @@ int HAMLIB_API rig_open(RIG *rig) rig_debug(RIG_DEBUG_VERBOSE, "%s: rs->comm_state==0?=%d\n", __func__, rs->comm_state); rs->comm_state = 0; + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } @@ -1312,6 +1316,7 @@ int HAMLIB_API rig_open(RIG *rig) if (status < 0) { port_close(&rs->rigport, rs->rigport.type.rig); + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } @@ -1320,11 +1325,10 @@ int HAMLIB_API rig_open(RIG *rig) if (status < 0) { port_close(&rs->rigport, rs->rigport.type.rig); + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } - add_opened_rig(rig); - rs->comm_state = 1; rig_debug(RIG_DEBUG_VERBOSE, "%s: %p rs->comm_state==1?=%d\n", __func__, &rs->comm_state, @@ -1377,6 +1381,7 @@ int HAMLIB_API rig_open(RIG *rig) port_close(&rs->rigport, rs->rigport.type.rig); memcpy(&rs->rigport_deprecated, &rs->rigport, sizeof(hamlib_port_t_deprecated)); rs->comm_state = 0; + rig->state.comm_status = RIG_COMM_STATUS_ERROR; RETURNFUNC2(status); } } @@ -1518,6 +1523,10 @@ int HAMLIB_API rig_open(RIG *rig) // we will consider this non-fatal for now } + rig->state.comm_status = RIG_COMM_STATUS_OK; + + add_opened_rig(rig); + RETURNFUNC2(RIG_OK); } @@ -1557,6 +1566,10 @@ int HAMLIB_API rig_close(RIG *rig) RETURNFUNC(-RIG_EINVAL); } + remove_opened_rig(rig); + + rig->state.comm_status = RIG_COMM_STATUS_DISCONNECTED; + morse_data_handler_stop(rig); async_data_handler_stop(rig); rig_poll_routine_stop(rig); @@ -1676,8 +1689,6 @@ int HAMLIB_API rig_close(RIG *rig) port_close(&rs->rigport, rs->rigport.type.rig); - remove_opened_rig(rig); - // zero split so it will allow it to be set again on open for rigctld rig->state.cache.split = 0; rs->comm_state = 0; diff --git a/src/snapshot_data.c b/src/snapshot_data.c index 2465eee4f..8d99af7e9 100644 --- a/src/snapshot_data.c +++ b/src/snapshot_data.c @@ -15,35 +15,21 @@ #define SPECTRUM_MODE_FIXED "FIXED" #define SPECTRUM_MODE_CENTER "CENTER" +char snapshot_data_pid[20]; + static int snapshot_serialize_rig(cJSON *rig_node, RIG *rig) { cJSON *node; char buf[1024]; -#if 0 - // TODO: need to assign rig an ID, e.g. from command line - snprintf(buf, sizeof(buf), "%s:%s:%d", rig->caps->model_name, - rig->state.rigport.pathname, getpid()); - - node = cJSON_AddStringToObject(rig_node, "id", buf); - if (node == NULL) - { - goto error; - } - -#else cJSON *id_node = cJSON_CreateObject(); cJSON_AddStringToObject(id_node, "model", rig->caps->model_name); cJSON_AddStringToObject(id_node, "endpoint", rig->state.rigport.pathname); - char pid[16]; - sprintf(pid,"%d",getpid()); - cJSON_AddStringToObject(id_node, "process", pid); + cJSON_AddStringToObject(id_node, "process", snapshot_data_pid); + cJSON_AddStringToObject(id_node, "deviceId", rig->state.device_id); cJSON_AddItemToObject(rig_node, "id", id_node); -#endif - // TODO: what kind of status should this reflect? - node = cJSON_AddStringToObject(rig_node, "status", - rig->state.comm_state ? "OK" : "CLOSED"); + node = cJSON_AddStringToObject(rig_node, "status", rig_strcommstatus(rig->state.comm_status)); if (node == NULL) { @@ -330,6 +316,11 @@ error: RETURNFUNC2(-RIG_EINTERNAL); } +void snapshot_init() +{ + snprintf(snapshot_data_pid, sizeof(snapshot_data_pid), "%d", getpid()); +} + int snapshot_serialize(size_t buffer_length, char *buffer, RIG *rig, struct rig_spectrum_line *spectrum_line) { diff --git a/src/snapshot_data.h b/src/snapshot_data.h index ab1536429..cbe4a1ab8 100644 --- a/src/snapshot_data.h +++ b/src/snapshot_data.h @@ -1,6 +1,7 @@ #ifndef _SNAPSHOT_DATA_H #define _SNAPSHOT_DATA_H +void snapshot_init(); int snapshot_serialize(size_t buffer_length, char *buffer, RIG *rig, struct rig_spectrum_line *spectrum_line); #endif diff --git a/src/token.h b/src/token.h index 58b1f6aaa..b2118ac18 100644 --- a/src/token.h +++ b/src/token.h @@ -98,6 +98,7 @@ /** \brief Number of retries permitted in case of read timeouts */ #define TOK_TIMEOUT_RETRY TOKEN_FRONTEND(39) #define TOK_POST_PTT_DELAY TOKEN_FRONTEND(40) +#define TOK_DEVICE_ID TOKEN_FRONTEND(41) /* * rig specific tokens diff --git a/tests/rigctl.c b/tests/rigctl.c index f388fb00e..6ae1bd9e1 100644 --- a/tests/rigctl.c +++ b/tests/rigctl.c @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef HAVE_LIBREADLINE # if defined(HAVE_READLINE_READLINE_H) @@ -113,11 +114,86 @@ static struct option long_options[] = extern char rig_resp_sep; extern powerstat_t rig_powerstat; +static RIG *my_rig; /* handle to rig (instance) */ + +#ifdef HAVE_SIG_ATOMIC_T +static sig_atomic_t volatile ctrl_c = 0; +#else +static int volatile ctrl_c = 0; +#endif + #define MAXCONFLEN 2048 +#ifdef WIN32 +static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s: called\n", __func__); + + switch (fdwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_CLOSE_EVENT: + ctrl_c = 1; + return TRUE; + + default: + return FALSE; + } +} +#else +static void signal_handler(int sig) +{ + switch (sig) + { + case SIGINT: + case SIGTERM: + fprintf(stderr, "\nTerminating application, caught signal %d\n", sig); + // Close stdin to stop reading input + fclose(stdin); + ctrl_c = 1; + break; + + default: + /* do nothing */ + break; + } +} +#endif + +static void handle_error(enum rig_debug_level_e lvl, const char *msg) +{ + int e; +#ifdef __MINGW32__ + LPVOID lpMsgBuf; + + lpMsgBuf = (LPVOID)"Unknown error"; + e = WSAGetLastError(); + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, e, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + // Default language + (LPTSTR)&lpMsgBuf, 0, NULL)) + { + + rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, (char *)lpMsgBuf); + LocalFree(lpMsgBuf); + } + else + { + rig_debug(lvl, "%s: Network error %d\n", msg, e); + } + +#else + e = errno; + rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, strerror(e)); +#endif +} + int main(int argc, char *argv[]) { - RIG *my_rig; /* handle to rig (instance) */ rig_model_t my_model = RIG_MODEL_DUMMY; int retcode; /* generic return code from functions */ @@ -151,6 +227,9 @@ int main(int argc, char *argv[]) char rigstartup[1024]; char vbuf[1024]; rig_powerstat = RIG_POWER_ON; // defaults to power on +#if HAVE_SIGACTION + struct sigaction act; +#endif int err = setvbuf(stderr, vbuf, _IOFBF, sizeof(vbuf)); @@ -648,6 +727,77 @@ int main(int argc, char *argv[]) #endif /* HAVE_LIBREADLINE */ int rig_opened = 1; // our rig is already open +#if HAVE_SIGACTION + +#ifdef SIGPIPE + /* Ignore SIGPIPE as we will handle it at the write()/send() calls + that will consequently fail with EPIPE. All child threads will + inherit this disposition which is what we want. */ + memset(&act, 0, sizeof act); + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART; + + if (sigaction(SIGPIPE, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGPIPE"); + } + +#endif + +#ifdef SIGINT + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + + if (sigaction(SIGINT, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGINT"); + } + +#endif +#ifdef SIGTERM + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + + if (sigaction(SIGTERM, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGTERM"); + } + +#endif +#elif defined (WIN32) + + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) + { + handle_error(RIG_DEBUG_ERR, "SetConsoleCtrlHandler"); + } + +#elif HAVE_SIGNAL +#ifdef SIGPIPE + + if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGPIPE"); + } + +#endif +#ifdef SIGINT + + if (SIG_ERR == signal(SIGINT, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGINT"); + } + +#endif +#ifdef SIGTERM + + if (SIG_ERR == signal(SIGTERM, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGTERM"); + } + +#endif +#endif + do { if (!rig_opened) @@ -695,7 +845,7 @@ int main(int argc, char *argv[]) while (retry-- > 0 && retcode != RIG_OK); } } - while (retcode == RIG_OK || RIG_IS_SOFT_ERRCODE(-retcode)); + while (!ctrl_c && (retcode == RIG_OK || RIG_IS_SOFT_ERRCODE(-retcode))); if (interactive && prompt) { diff --git a/tests/rigctld.c b/tests/rigctld.c index 33d0d6110..dda18b891 100644 --- a/tests/rigctld.c +++ b/tests/rigctld.c @@ -136,9 +136,9 @@ static volatile int rig_opened = 0; static int verbose; #ifdef HAVE_SIG_ATOMIC_T -static sig_atomic_t volatile ctrl_c; +static sig_atomic_t volatile ctrl_c = 0; #else -static int volatile ctrl_c; +static int volatile ctrl_c = 0; #endif const char *portno = "4532"; @@ -196,6 +196,10 @@ static void signal_handler(int sig) switch (sig) { case SIGINT: + case SIGTERM: + fprintf(stderr, "\nTerminating application, caught signal %d\n", sig); + // Close stdin to stop reading input + fclose(stdin); ctrl_c = 1; break; @@ -949,6 +953,16 @@ int main(int argc, char *argv[]) handle_error(RIG_DEBUG_ERR, "sigaction SIGINT"); } +#endif +#ifdef SIGTERM + memset(&act, 0, sizeof act); + act.sa_handler = signal_handler; + + if (sigaction(SIGTERM, &act, NULL)) + { + handle_error(RIG_DEBUG_ERR, "sigaction SIGTERM"); + } + #endif #elif defined (WIN32) @@ -973,6 +987,14 @@ int main(int argc, char *argv[]) handle_error(RIG_DEBUG_ERR, "signal SIGINT"); } +#endif +#ifdef SIGTERM + + if (SIG_ERR == signal(SIGTERM, signal_handler)) + { + handle_error(RIG_DEBUG_ERR, "signal SIGTERM"); + } + #endif #endif From 337fb0f3e5b7b83c8d28481d516587cc9469e304 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Sun, 5 Nov 2023 16:45:15 -0600 Subject: [PATCH 29/32] Add SWR reading for PowerSDR/Thetis --- include/hamlib/rig.h | 1 + rigs/kenwood/flex6xxx.c | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index dc62832e7..e9f87c109 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -2551,6 +2551,7 @@ struct rig_cache { struct timespec time_ptt; struct timespec time_split; int satmode; // if rig is in satellite mode + double swr; // keep swr }; /** diff --git a/rigs/kenwood/flex6xxx.c b/rigs/kenwood/flex6xxx.c index e8541ee6f..1d03cd1fa 100644 --- a/rigs/kenwood/flex6xxx.c +++ b/rigs/kenwood/flex6xxx.c @@ -49,7 +49,7 @@ #define POWERSDR_FUNC_ALL (RIG_FUNC_VOX|RIG_FUNC_SQL|RIG_FUNC_NB|RIG_FUNC_ANF|RIG_FUNC_MUTE|RIG_FUNC_RIT|RIG_FUNC_XIT|RIG_FUNC_TUNER) -#define POWERSDR_LEVEL_ALL (RIG_LEVEL_SLOPE_HIGH|RIG_LEVEL_SLOPE_LOW|RIG_LEVEL_KEYSPD|RIG_LEVEL_RFPOWER|RIG_LEVEL_RFPOWER_METER|RIG_LEVEL_RFPOWER_METER_WATTS|RIG_LEVEL_MICGAIN|RIG_LEVEL_VOXGAIN|RIG_LEVEL_SQL|RIG_LEVEL_AF|RIG_LEVEL_AGC|RIG_LEVEL_RF|RIG_LEVEL_IF|RIG_LEVEL_STRENGTH) +#define POWERSDR_LEVEL_ALL (RIG_LEVEL_SLOPE_HIGH|RIG_LEVEL_SLOPE_LOW|RIG_LEVEL_KEYSPD|RIG_LEVEL_RFPOWER|RIG_LEVEL_RFPOWER_METER|RIG_LEVEL_RFPOWER_METER_WATTS|RIG_LEVEL_MICGAIN|RIG_LEVEL_VOXGAIN|RIG_LEVEL_SQL|RIG_LEVEL_AF|RIG_LEVEL_AGC|RIG_LEVEL_RF|RIG_LEVEL_IF|RIG_LEVEL_STRENGTH|RIG_LEVEL_SWR) #define POWERSDR_LEVEL_SET (RIG_LEVEL_SLOPE_HIGH|RIG_LEVEL_SLOPE_LOW|RIG_LEVEL_KEYSPD|RIG_LEVEL_RFPOWER|RIG_LEVEL_MICGAIN|RIG_LEVEL_VOXGAIN|RIG_LEVEL_SQL|RIG_LEVEL_AF|RIG_LEVEL_AGC|RIG_LEVEL_RF|RIG_LEVEL_IF) @@ -837,6 +837,27 @@ int powersdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) ans = 3; break; + case RIG_LEVEL_SWR: + { + // if not PTT we'll return the last SWR value + // seems desirable to able to see this always + if (!rig->state.cache.ptt) { val->f = rig->state.cache.swr; return RIG_OK; } + double forward=0, reverse=0; + cmd = "ZZRM5"; // get forward power + len = 5; + ans = 4; + retval = kenwood_safe_transaction(rig, cmd, lvlbuf, sizeof(lvlbuf), len + ans); + if (retval != RIG_OK) { val->f = 0; return RIG_OK;}; + sscanf(lvlbuf,"ZZRM5%lg", &forward); + if (forward == 0) { val->f = 1.0; return RIG_OK;} + cmd = "ZZRM7"; + retval = kenwood_safe_transaction(rig, cmd, lvlbuf, sizeof(lvlbuf), len + ans); + if (retval != RIG_OK) { val->f = 0; return RIG_OK;}; + sscanf(lvlbuf,"ZZRM7%lg", &reverse); + rig->state.cache.swr = val->f = (1.0 + sqrt(reverse/forward)) / (1.0 - sqrt(reverse/forward)); + return RIG_OK; + } + default: return kenwood_get_level(rig, vfo, level, val); } @@ -1321,7 +1342,7 @@ const struct rig_caps powersdr_caps = RIG_MODEL(RIG_MODEL_POWERSDR), .model_name = "PowerSDR/Thetis", .mfg_name = "FlexRadio/ANAN", - .version = "20230819.0", + .version = "20231104.0", .copyright = "LGPL", .status = RIG_STATUS_STABLE, .rig_type = RIG_TYPE_TRANSCEIVER, From 1e3e1a9499f6cdc6738e311fdf07fb14e52ebbed Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Sun, 5 Nov 2023 16:53:53 -0600 Subject: [PATCH 30/32] Only do swr and power for PowerSDR then ptt is active --- rigs/kenwood/flex6xxx.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/rigs/kenwood/flex6xxx.c b/rigs/kenwood/flex6xxx.c index 1d03cd1fa..98699b212 100644 --- a/rigs/kenwood/flex6xxx.c +++ b/rigs/kenwood/flex6xxx.c @@ -841,7 +841,9 @@ int powersdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) { // if not PTT we'll return the last SWR value // seems desirable to able to see this always - if (!rig->state.cache.ptt) { val->f = rig->state.cache.swr; return RIG_OK; } + ptt_t ptt; + rig_get_ptt(rig, RIG_VFO_TX, &ptt); + if (!ptt) { val->f = rig->state.cache.swr; return RIG_OK; } double forward=0, reverse=0; cmd = "ZZRM5"; // get forward power len = 5; @@ -942,14 +944,20 @@ int powersdr_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) case RIG_LEVEL_RFPOWER_METER: case RIG_LEVEL_RFPOWER_METER_WATTS: - n = sscanf(lvlbuf, "ZZRM5%f", &val->f); - - if (n != 1) { - rig_debug(RIG_DEBUG_ERR, "%s: Error parsing value from lvlbuf='%s'\n", + // if not ptt then no power is going out so return 0W + ptt_t ptt; + rig_get_ptt(rig, RIG_VFO_TX, &ptt); + if (!ptt) { val->f = 0; return RIG_OK; } + n = sscanf(lvlbuf, "ZZRM5%f", &val->f); + + if (n != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s: Error parsing value from lvlbuf='%s'\n", __func__, lvlbuf); - val->f = 0; - return -RIG_EPROTO; + val->f = 0; + return -RIG_EPROTO; + } } if (level != RIG_LEVEL_RFPOWER_METER_WATTS) From 02ffc6d6dcaa34100e2b7ff93e7713999505c45c Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Sun, 5 Nov 2023 17:03:21 -0600 Subject: [PATCH 31/32] More set_conf/get_conf work https://github.com/Hamlib/Hamlib/issues/971 --- src/network.c | 10 ++- tests/ampctl.c | 24 +++++-- tests/ampctl_parse.c | 2 +- tests/ampctld.c | 27 +++++--- tests/dumpcaps.c | 13 ++++ tests/dumpcaps_rot.c | 7 +++ tests/rigctl.c | 26 ++++++-- tests/rigctl_parse.c | 130 +++++++++++++++++++++++--------------- tests/rigctl_parse.h | 2 + tests/rigctlcom.c | 3 +- tests/rigctld.c | 27 ++++++-- tests/rigctltcp.c | 3 +- tests/rotctl.c | 24 +++++-- tests/rotctl_parse.c | 146 +++++++++++++++++++++++++++---------------- tests/rotctl_parse.h | 1 + tests/rotctld.c | 24 +++++-- 16 files changed, 329 insertions(+), 140 deletions(-) diff --git a/src/network.c b/src/network.c index c4754cbd1..a68329149 100644 --- a/src/network.c +++ b/src/network.c @@ -999,15 +999,19 @@ void *multicast_receiver(void *arg) __LINE__); int optval = 1; +#ifdef __MINGW32__ + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (PCHAR)&optval, sizeof(optval)) < 0) +#else if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) +#endif { rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP address reuse: %s\n", __func__, strerror(errno)); return NULL; } -#if defined(SO_REUSEPORT) // Windows does not have SO_REUSEPORT. However, SO_REUSEADDR works in a similar way. +#if defined(SO_REUSEPORT) if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) { rig_debug(RIG_DEBUG_ERR, "%s: error enabling UDP port reuse: %s\n", __func__, @@ -1038,7 +1042,11 @@ void *multicast_receiver(void *arg) mreq.imr_multiaddr.s_addr = inet_addr(args->multicast_addr); mreq.imr_interface.s_addr = htonl(INADDR_ANY); +#ifdef __MINGW32__ + if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (PCHAR)&mreq, sizeof(mreq)) < 0) +#else if (setsockopt(socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) +#endif { rig_debug(RIG_DEBUG_ERR, "%s: error joining multicast group %s:%d: %s\n", __func__, args->multicast_addr, args->multicast_port, strerror(errno)); diff --git a/tests/ampctl.c b/tests/ampctl.c index 6a179206f..5c75e5034 100644 --- a/tests/ampctl.c +++ b/tests/ampctl.c @@ -298,12 +298,28 @@ int main(int argc, char *argv[]) exit(2); } - retcode = set_conf(my_amp, conf_parms); + char *token=strtok(conf_parms,","); - if (retcode != RIG_OK) + while(token) { - fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); - exit(2); + char mytoken[100], myvalue[100]; + token_t lookup; + sscanf(token,"%99[^=]=%99s", mytoken, myvalue); + //printf("mytoken=%s,myvalue=%s\n",mytoken, myvalue); + lookup = amp_token_lookup(my_amp,mytoken); + if (lookup == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no such token as '%s'\n", __func__, mytoken); + token = strtok(NULL, ","); + continue; + } + retcode = amp_set_conf(my_amp, amp_token_lookup(my_amp,mytoken), myvalue); + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + token = strtok(NULL, ","); } if (amp_file) diff --git a/tests/ampctl_parse.c b/tests/ampctl_parse.c index 01ffba765..41d080d7a 100644 --- a/tests/ampctl_parse.c +++ b/tests/ampctl_parse.c @@ -1630,7 +1630,7 @@ int set_conf(AMP *my_amp, char *conf_parms) } else { - rig_debug(RIG_DEBUG_WARN, "%s: invalid token %s for this rig\n", __func__, p); + rig_debug(RIG_DEBUG_WARN, "%s: invalid token %s for this amp\n", __func__, p); } p = n; diff --git a/tests/ampctld.c b/tests/ampctld.c index 6aeef098c..8244a5441 100644 --- a/tests/ampctld.c +++ b/tests/ampctld.c @@ -323,17 +323,30 @@ int main(int argc, char *argv[]) exit(2); } -#if 0 - retcode = set_conf(my_amp, conf_parms); + char *token=strtok(conf_parms,","); - if (retcode != RIG_OK) + while(token) { - fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); - exit(2); + char mytoken[100], myvalue[100]; + token_t lookup; + sscanf(token,"%99[^=]=%99s", mytoken, myvalue); + //printf("mytoken=%s,myvalue=%s\n",mytoken, myvalue); + lookup = amp_token_lookup(my_amp,mytoken); + if (lookup == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no such token as '%s'\n", __func__, mytoken); + token = strtok(NULL, ","); + continue; + } + retcode = amp_set_conf(my_amp, amp_token_lookup(my_amp,mytoken), myvalue); + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + token = strtok(NULL, ","); } -#endif - if (amp_file) { strncpy(my_amp->state.ampport.pathname, amp_file, HAMLIB_FILPATHLEN - 1); diff --git a/tests/dumpcaps.c b/tests/dumpcaps.c index f1d99de0d..db473caab 100644 --- a/tests/dumpcaps.c +++ b/tests/dumpcaps.c @@ -28,6 +28,7 @@ #include "sprintflst.h" #include "rigctl_parse.h" #include "../rigs/icom/icom.h" +#include "dumpcaps.h" void range_print(FILE *fout, const struct freq_range_list range_list[], int rx); int range_sanity_check(const struct freq_range_list range_list[], int rx); @@ -1238,3 +1239,15 @@ int dumpconf(RIG *rig, FILE *fout) return 0; } + +int dumpconf_list(RIG *rig, FILE *fout) +{ + rig_token_foreach(rig, print_conf_list2, (rig_ptr_t)rig); + return 0; +} + +int dumpconf_list_rot(ROT *rot, FILE *fout) +{ + rot_token_foreach(rot, print_conf_list2, rot); + return 0; +} diff --git a/tests/dumpcaps_rot.c b/tests/dumpcaps_rot.c index 69626dd46..408defc70 100644 --- a/tests/dumpcaps_rot.c +++ b/tests/dumpcaps_rot.c @@ -242,3 +242,10 @@ int dumpcaps_rot(ROT *rot, FILE *fout) return backend_warnings; } + +int dumpconf_list(ROT *rot, FILE *fout) +{ + rot_token_foreach(rot, print_conf_list, rot); + return 0; +} + diff --git a/tests/rigctl.c b/tests/rigctl.c index 6ae1bd9e1..73e5380ad 100644 --- a/tests/rigctl.c +++ b/tests/rigctl.c @@ -555,12 +555,28 @@ int main(int argc, char *argv[]) exit(2); } - retcode = set_conf(my_rig, conf_parms); - - if (retcode != RIG_OK) + char *token=strtok(conf_parms,","); + + while(token) { - fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); - exit(2); + char mytoken[100], myvalue[100]; + token_t lookup; + sscanf(token,"%99[^=]=%99s", mytoken, myvalue); + //printf("mytoken=%s,myvalue=%s\n",mytoken, myvalue); + lookup = rig_token_lookup(my_rig,mytoken); + if (lookup == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no such token as '%s'\n", __func__, mytoken); + token = strtok(NULL, ","); + continue; + } + retcode = rig_set_conf(my_rig, rig_token_lookup(my_rig,mytoken), myvalue); + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + token = strtok(NULL, ","); } if (rig_file) diff --git a/tests/rigctl_parse.c b/tests/rigctl_parse.c index d488ec0f2..668034b8b 100644 --- a/tests/rigctl_parse.c +++ b/tests/rigctl_parse.c @@ -270,6 +270,8 @@ declare_proto_rig(hamlib_version); declare_proto_rig(test); declare_proto_rig(cm108_get_bit); declare_proto_rig(cm108_set_bit); +declare_proto_rig(set_conf); +declare_proto_rig(get_conf); /* @@ -390,6 +392,8 @@ static struct test_table test_list[] = { 0xa8, "hamlib_version", ACTION(hamlib_version), ARG_NOVFO }, { 0xa9, "get_gpio", ACTION(cm108_get_bit), ARG_NOVFO | ARG_IN1 | ARG_OUT1, "GPIO#", "0/1" }, { 0xaa, "set_gpio", ACTION(cm108_set_bit), ARG_NOVFO | ARG_IN , "GPIO#", "0/1" }, + { 0xac, "set_conf", ACTION(set_conf), ARG_NOVFO | ARG_IN , "Token", "Token Value" }, + { 0xad, "get_conf", ACTION(get_conf), ARG_NOVFO | ARG_IN1 | ARG_OUT2, "Token", "Value"}, { 0x00, "", NULL }, }; @@ -1989,6 +1993,21 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data) return 1; /* !=0, we want them all ! */ } +// short list for rigctl/rigctld display +int print_conf_list2(const struct confparams *cfp, rig_ptr_t data) +{ + RIG *rig = (RIG *) data; + char buf[128] = ""; + + rig_get_conf(rig, cfp->token, buf); + fprintf(stdout,"%s: \"%s\"\n" "\t" "Default: %s, Value: %s\n", + cfp->name, + cfp->tooltip, + cfp->dflt, + buf); + + return 1; /* !=0, we want them all ! */ +} static int hash_model_list(const struct rig_caps *caps, void *data) { @@ -2040,58 +2059,6 @@ void list_models() hash_delete_all(); } - -int set_conf(RIG *my_rig, char *conf_parms) -{ - char *p, *n; - int token; - - rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__); - p = conf_parms; - - while (p && *p != '\0') - { - int ret; - - /* FIXME: left hand value of = cannot be null */ - char *q = strchr(p, '='); - - if (!q) - { - return (-RIG_EINVAL); - } - - *q++ = '\0'; - n = strchr(q, ','); - - if (n) - { - *n++ = '\0'; - } - - token = rig_token_lookup(my_rig, p); - - if (token != 0) - { - ret = rig_set_conf(my_rig, rig_token_lookup(my_rig, p), q); - - if (ret != RIG_OK) - { - return (ret); - } - } - else - { - rig_debug(RIG_DEBUG_WARN, "%s: invalid token %s for this rig\n", __func__, p); - } - - p = n; - } - - return (RIG_OK); -} - - /* * static int (f)(RIG *rig, FILE *fout, int interactive, const struct test_table *cmd, * vfo_t vfo, const void *arg1, const void *arg2, const void *arg3) @@ -5790,3 +5757,62 @@ declare_proto_rig(cm108_set_bit) } return retval; } + +declare_proto_rig(get_conf) +{ + int ret; + rig_debug(RIG_DEBUG_ERR, "%s: \n", __func__); + if (arg1 == NULL || arg1[0] == '?') + { + dumpconf_list(rig, stdout); + debugmsgsave[0] = 0; + debugmsgsave2[0] = 0; + return RIG_OK; + } + token_t mytoken = rig_token_lookup(rig, arg1); + if (mytoken == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: unknown token '%s' for this rig\n", __func__, arg1); + ret = -RIG_EINVAL; + } + else + { + char value[4096]; // no max value known -- should we limit it? + ret = rig_get_conf(rig, mytoken, value); + if (ret != RIG_OK) + { + return ret; + } + fprintf(fout, "%s=%s\n", arg1, value); + } + + return (ret); +} + +declare_proto_rig(set_conf) +{ + int ret; + rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__); + + + if (arg1[0] == '?') + { + dumpconf_list(rig, fout); + debugmsgsave[0] = 0; + debugmsgsave2[0] = 0; + return RIG_OK; + } + token_t mytoken = rig_token_lookup(rig, arg1); + if (mytoken == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: unknown token '%s' for this rig\n", __func__, arg1); + ret = -RIG_EINVAL; + } + else + { + ret = rig_set_conf(rig, rig_token_lookup(rig, arg1), arg2); + } + + return (ret); +} + diff --git a/tests/rigctl_parse.h b/tests/rigctl_parse.h index 1553d2096..72fdeb9fb 100644 --- a/tests/rigctl_parse.h +++ b/tests/rigctl_parse.h @@ -38,6 +38,7 @@ int dumpcaps(RIG *, FILE *); int dumpstate(RIG *, FILE *); int dumpconf(RIG *, FILE *); +int dumpconf_list(RIG *, FILE *); /* * Prototypes @@ -47,6 +48,7 @@ void version(); void list_models(); int dump_chan(FILE *, RIG *, channel_t *); int print_conf_list(const struct confparams *cfp, rig_ptr_t data); +int print_conf_list2(const struct confparams *cfp, rig_ptr_t data); int set_conf(RIG *my_rig, char *conf_parms); typedef void (*sync_cb_t)(int); diff --git a/tests/rigctlcom.c b/tests/rigctlcom.c index 329337467..b81f03a97 100644 --- a/tests/rigctlcom.c +++ b/tests/rigctlcom.c @@ -492,7 +492,8 @@ int main(int argc, char *argv[]) exit(2); } - retcode = set_conf(my_rig, conf_parms); + retcode = -RIG_ENIMPL; +// retcode = set_conf(my_rig, conf_parms); if (retcode != RIG_OK) { diff --git a/tests/rigctld.c b/tests/rigctld.c index dda18b891..2bce5b814 100644 --- a/tests/rigctld.c +++ b/tests/rigctld.c @@ -659,13 +659,28 @@ int main(int argc, char *argv[]) fprintf(stderr, "Please check with --list option.\n"); exit(2); } - - retcode = set_conf(my_rig, conf_parms); - - if (retcode != RIG_OK) + + char *token=strtok(conf_parms,","); + while(token) { - fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); - exit(2); + char mytoken[100], myvalue[100]; + token_t lookup; + sscanf(token,"%99[^=]=%99s", mytoken, myvalue); + //printf("mytoken=%s,myvalue=%s\n",mytoken, myvalue); + lookup = rig_token_lookup(my_rig,mytoken); + if (lookup == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no such token as '%s'\n", __func__, mytoken); + token = strtok(NULL, ","); + continue; + } + retcode = rig_set_conf(my_rig, rig_token_lookup(my_rig,mytoken), myvalue); + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + token = strtok(NULL, ","); } if (rig_file) diff --git a/tests/rigctltcp.c b/tests/rigctltcp.c index 7053b49d4..51beb5433 100644 --- a/tests/rigctltcp.c +++ b/tests/rigctltcp.c @@ -674,7 +674,8 @@ int main(int argc, char *argv[]) exit(2); } - retcode = set_conf(my_rig, conf_parms); + retcode = -RIG_ENIMPL; +// retcode = set_conf(my_rig, conf_parms); if (retcode != RIG_OK) { diff --git a/tests/rotctl.c b/tests/rotctl.c index 3a680719c..cdf2b7775 100644 --- a/tests/rotctl.c +++ b/tests/rotctl.c @@ -339,12 +339,28 @@ int main(int argc, char *argv[]) exit(2); } - retcode = set_conf(my_rot, conf_parms); + char *token=strtok(conf_parms,","); - if (retcode != RIG_OK) + while(token) { - fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); - exit(2); + char mytoken[100], myvalue[100]; + token_t lookup; + sscanf(token,"%99[^=]=%99s", mytoken, myvalue); + //printf("mytoken=%s,myvalue=%s\n",mytoken, myvalue); + lookup = rot_token_lookup(my_rot,mytoken); + if (lookup == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no such token as '%s', use -L switch to see\n", __func__, mytoken); + token = strtok(NULL, ","); + continue; + } + retcode = rot_set_conf(my_rot, rot_token_lookup(my_rot,mytoken), myvalue); + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + token = strtok(NULL, ","); } if (rot_file) diff --git a/tests/rotctl_parse.c b/tests/rotctl_parse.c index acbe40df7..4191c751a 100644 --- a/tests/rotctl_parse.c +++ b/tests/rotctl_parse.c @@ -24,8 +24,6 @@ * */ -// TODO: Add "symmetric" set_conf + get_conf to rigctl+rotctl - #include #include @@ -65,7 +63,7 @@ extern int read_history(); #include #include "iofunc.h" #include "misc.h" - +#include "dumpcaps_rot.h" /* HAVE_SSLEEP is defined when Windows Sleep is found * HAVE_SLEEP is defined when POSIX sleep is found @@ -189,10 +187,12 @@ declare_proto_rot(set_parm); declare_proto_rot(get_parm); declare_proto_rot(get_info); declare_proto_rot(get_status); -declare_proto_rot(inter_set_conf); /* interactive mode set_conf */ +declare_proto_rot(set_conf); +declare_proto_rot(get_conf); declare_proto_rot(send_cmd); declare_proto_rot(dump_state); declare_proto_rot(dump_caps); +declare_proto_rot(dump_conf); /* Follows are functions from locator.c */ declare_proto_rot(loc2lonlat); declare_proto_rot(lonlat2loc); @@ -224,11 +224,12 @@ struct test_table test_list[] = { 'u', "get_func", ACTION(get_func), ARG_IN1 | ARG_OUT2, "Func", "Func Status" }, { 'X', "set_parm", ACTION(set_parm), ARG_IN, "Parm", "Parm Value" }, { 'x', "get_parm", ACTION(get_parm), ARG_IN1 | ARG_OUT2, "Parm", "Parm Value" }, - { 'C', "set_conf", ACTION(inter_set_conf), ARG_IN, "Token", "Value" }, + { 'C', "set_conf", ACTION(set_conf), ARG_IN, "Token", "Value" }, { '_', "get_info", ACTION(get_info), ARG_OUT, "Info" }, { 's', "get_status", ACTION(get_status), ARG_OUT, "Status flags" }, { 'w', "send_cmd", ACTION(send_cmd), ARG_IN1 | ARG_IN_LINE | ARG_OUT2, "Cmd", "Reply" }, { '1', "dump_caps", ACTION(dump_caps), }, + { '3', "dump_conf", ACTION(dump_conf), }, { 0x8f, "dump_state", ACTION(dump_state), ARG_OUT }, { 'L', "lonlat2loc", ACTION(lonlat2loc), ARG_IN1 | ARG_IN2 | ARG_IN3 | ARG_OUT1, "Longitude", "Latitude", "Loc Len [2-12]", "Locator" }, { 'l', "loc2lonlat", ACTION(loc2lonlat), ARG_IN1 | ARG_OUT1 | ARG_OUT2, "Locator", "Longitude", "Latitude" }, @@ -240,6 +241,7 @@ struct test_table test_list[] = { 'A', "a_sp2a_lp", ACTION(az_sp2az_lp), ARG_IN1 | ARG_OUT1, "Short Path Deg", "Long Path Deg" }, { 'a', "d_sp2d_lp", ACTION(dist_sp2dist_lp), ARG_IN1 | ARG_OUT1, "Short Path km", "Long Path km" }, { 0x8c, "pause", ACTION(pause), ARG_IN, "Seconds" }, + { 0xad, "get_conf", ACTION(get_conf), ARG_IN1 | ARG_OUT2, "Token", "Value"}, { 0x00, "", NULL }, }; @@ -1574,7 +1576,7 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data) char buf[128] = ""; rot_get_conf2(rot, cfp->token, buf, sizeof(buf)); - printf("%s: \"%s\"\n" "\tDefault: %s, Value: %s\n", + fprintf(stdout,"%s: \"%s\"\n" "\tDefault: %s, Value: %s\n", cfp->name, cfp->tooltip, cfp->dflt, @@ -1583,14 +1585,14 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data) switch (cfp->type) { case RIG_CONF_NUMERIC: - printf("\tRange: %.1f..%.1f, step %.1f\n", + fprintf(stdout,"\tRange: %.1f..%.1f, step %.1f\n", cfp->u.n.min, cfp->u.n.max, cfp->u.n.step); break; case RIG_CONF_CHECKBUTTON: - printf("\tCheckbox: 0,1\n"); + fprintf(stdout,"\tCheckbox: 0,1\n"); break; case RIG_CONF_COMBO: @@ -1599,14 +1601,14 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data) break; } - printf("\tCombo: %s", cfp->u.c.combostr[0]); + fprintf(stdout,"\tCombo: %s", cfp->u.c.combostr[0]); for (i = 1 ; i < RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++) { - printf(", %s", cfp->u.c.combostr[i]); + fprintf(stdout,", %s", cfp->u.c.combostr[i]); } - printf("\n"); + fprintf(stdout,"\n"); break; default: @@ -1666,55 +1668,64 @@ void list_models() hash_delete_all(); } - -int set_conf(ROT *my_rot, char *conf_parms) +declare_proto_rot(get_conf) { - char *p; - - rot_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__); - p = conf_parms; - - while (p && *p != '\0') + int ret; + rig_debug(RIG_DEBUG_ERR, "%s: \n", __func__); + if (arg1 == NULL || arg1[0] == '?') { - int token; - char *q, *n = NULL; - /* FIXME: left hand value of = cannot be null */ - q = strchr(p, '='); - - if (!q) + dumpconf_list(rot, fout); + debugmsgsave[0] = 0; + debugmsgsave2[0] = 0; + return RIG_OK; + } + token_t mytoken = rot_token_lookup(rot, arg1); + if (mytoken == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: unknown token '%s' for this rot\n", __func__, arg1); + ret = -RIG_EINVAL; + } + else + { + char value[4096]; + ret = rot_get_conf(rot, rot_token_lookup(rot, arg1), value); + if (ret != RIG_OK) { - return RIG_EINVAL; + return ret; } - - *q++ = '\0'; - n = strchr(q, ','); - - if (n) - { - *n++ = '\0'; - } - - token = rot_token_lookup(my_rot, p); - - if (token != 0) - { - int ret; - ret = rot_set_conf(my_rot, token, q); - - if (ret != RIG_OK) - { - return ret; - } - } - else - { - rig_debug(RIG_DEBUG_WARN, "%s: invalid token %s for this rig\n", __func__, p); - } - - p = n; + fprintf(fout, "%s=%s\n", arg1, value); } - return RIG_OK; + return (ret); +} + + +/* 'C' */ +declare_proto_rot(set_conf) +{ + int ret; + rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__); + + if (arg1[0] == '?') + { + dumpconf_list(rot, fout); + debugmsgsave[0] = 0; + debugmsgsave2[0] = 0; + return RIG_OK; + } + + token_t mytoken = rot_token_lookup(rot, arg1); + if (mytoken == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: unknown token '%s' for this rot\n", __func__, arg1); + ret = -RIG_EINVAL; + } + else + { + ret = rot_set_conf(rot, rot_token_lookup(rot, arg1), arg2); + } + + return (ret); } @@ -2344,6 +2355,7 @@ declare_proto_rot(get_parm) } +#if 0 // replace by set_conf /* 'C' */ declare_proto_rot(inter_set_conf) { @@ -2361,6 +2373,7 @@ declare_proto_rot(inter_set_conf) SNPRINTF(buf, sizeof(buf), "%s=%s", arg1, arg2); return set_conf(rot, buf); } +#endif /* '1' */ declare_proto_rot(dump_caps) @@ -2370,6 +2383,14 @@ declare_proto_rot(dump_caps) return RIG_OK; } +declare_proto_rot(dump_conf) +{ + ENTERFUNC2; + + dumpconf_list(rot, fout); + + RETURNFUNC2(RIG_OK); +} /* For rotctld internal use * '0x8f' @@ -2862,3 +2883,20 @@ declare_proto_rot(pause) sleep(seconds); return RIG_OK; } + +// short list for rigctl/rigctld display +int print_conf_list2(const struct confparams *cfp, rig_ptr_t data, FILE *fout) +{ + ROT *rot = (ROT *) data; + char buf[128] = ""; + + rot_get_conf(rot, cfp->token, buf); + fprintf(fout,"%s: \"%s\"\n" "\t" "Default: %s, Value: %s\n", + cfp->name, + cfp->tooltip, + cfp->dflt, + buf); + + return 1; /* !=0, we want them all ! */ +} + diff --git a/tests/rotctl_parse.h b/tests/rotctl_parse.h index 269d4a9c9..8743fc84f 100644 --- a/tests/rotctl_parse.h +++ b/tests/rotctl_parse.h @@ -42,6 +42,7 @@ void usage_rot(FILE *); void version(); void list_models(); int print_conf_list(const struct confparams *cfp, rig_ptr_t data); +int print_conf_list2(const struct confparams *cfp, rig_ptr_t data, FILE *fout); int set_conf(ROT *my_rot, char *conf_parms); int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, const char **argv, int argc, diff --git a/tests/rotctld.c b/tests/rotctld.c index f3c5e2f41..e0a37ffe3 100644 --- a/tests/rotctld.c +++ b/tests/rotctld.c @@ -345,12 +345,28 @@ int main(int argc, char *argv[]) exit(2); } - retcode = set_conf(my_rot, conf_parms); + char *token=strtok(conf_parms,","); - if (retcode != RIG_OK) + while(token) { - fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); - exit(2); + char mytoken[100], myvalue[100]; + token_t lookup; + sscanf(token,"%99[^=]=%99s", mytoken, myvalue); + //printf("mytoken=%s,myvalue=%s\n",mytoken, myvalue); + lookup = rot_token_lookup(my_rot,mytoken); + if (lookup == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no such token as '%s', use -L switch to see\n", __func__, mytoken); + token = strtok(NULL, ","); + continue; + } + retcode = rot_set_conf(my_rot, rot_token_lookup(my_rot,mytoken), myvalue); + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + token = strtok(NULL, ","); } if (rot_file) From 45dca3be598c028f08e6db1edb0a7e78525c2fe2 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Sun, 5 Nov 2023 17:34:49 -0600 Subject: [PATCH 32/32] Add dumpcap include files to test/Makefile.am --- tests/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index b2ec16650..11831e511 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -19,8 +19,8 @@ bin_PROGRAMS = rigctl rigctld rigmem rigsmtr rigswr rotctl rotctld rigctlcom rig #check_PROGRAMS = dumpmem testrig testrigopen testrigcaps testtrn testbcd testfreq listrigs testloc rig_bench testcache cachetest cachetest2 testcookie testgrid testsecurity check_PROGRAMS = dumpmem testrig testrigopen testrigcaps testtrn testbcd testfreq listrigs testloc rig_bench testcache cachetest cachetest2 testcookie testgrid hamlibmodels testmW2power -RIGCOMMONSRC = rigctl_parse.c rigctl_parse.h dumpcaps.c dumpstate.c uthash.h rig_tests.c rig_tests.h -ROTCOMMONSRC = rotctl_parse.c rotctl_parse.h dumpcaps_rot.c uthash.h +RIGCOMMONSRC = rigctl_parse.c rigctl_parse.h dumpcaps.c dumpstate.c uthash.h rig_tests.c rig_tests.h dumpcaps.h +ROTCOMMONSRC = rotctl_parse.c rotctl_parse.h dumpcaps_rot.c uthash.h dumpcaps_rot.h AMPCOMMONSRC = ampctl_parse.c ampctl_parse.h dumpcaps_amp.c uthash.h rigctl_SOURCES = rigctl.c $(RIGCOMMONSRC)