diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index 3816bbab0..bc16a1f0c 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; @@ -2550,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 }; /** @@ -2576,6 +2578,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. * @@ -2758,7 +2769,15 @@ 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 */ + int doppler; /*!< True if doppler changing detected */ + 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; + rig_comm_status_t comm_status; /*!< Detailed rig control status */ + char device_id[HAMLIB_RIGNAMSIZ]; }; /** @@ -3703,6 +3722,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/rigs/icom/icom.c b/rigs/icom/icom.c index ae1f2216d..2b598f207 100644 --- a/rigs/icom/icom.c +++ b/rigs/icom/icom.c @@ -3619,7 +3619,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) { diff --git a/rigs/kenwood/flex6xxx.c b/rigs/kenwood/flex6xxx.c index e8541ee6f..98699b212 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,29 @@ 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 + 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; + 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); } @@ -921,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) @@ -1321,7 +1350,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, diff --git a/rigs/kenwood/kenwood.c b/rigs/kenwood/kenwood.c index 250a83840..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) @@ -2861,7 +2863,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..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 "20231023" +#define BACKEND_VER "20231031" #define EOM_KEN ';' #define EOM_TH '\r' 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/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 e51040e17..556e065d2 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 || is_ftdx5000) { return 1; } if (!is_ftdx10 && !is_ft710 && !is_ftdx101d && !is_ftdx101mp) { return 0; } @@ -4030,12 +4034,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 +4054,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 +4063,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 +4191,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 +4234,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 +4757,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) @@ -4854,24 +4818,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; 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)); @@ -5498,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); } @@ -5950,7 +5915,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: diff --git a/rigs/yaesu/newcat.h b/rigs/yaesu/newcat.h index d7c451587..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 "20231024" +#define NEWCAT_VER "20231101" /* Hopefully large enough for future use, 128 chars plus '\0' */ #define NEWCAT_DATA_LEN 129 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; 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; diff --git a/src/conf.c b/src/conf.c index cc9291e3a..99fad9154 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 } } }, { @@ -84,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", @@ -91,9 +101,9 @@ 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", - "0", RIG_CONF_NUMERIC, { .n = { 0, 1000000, 1 } } + TOK_POLL_INTERVAL, "poll_interval", "Rig state poll interval in ms", + "Polling interval in ms 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", @@ -102,7 +112,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 +127,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 +182,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, }, { @@ -190,6 +200,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, } }; @@ -602,13 +632,21 @@ 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); 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 +773,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; @@ -770,6 +832,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; @@ -932,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; @@ -1092,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 e6f2ea51e..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; @@ -75,12 +73,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__); @@ -88,10 +85,119 @@ 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; + network_publish_rig_poll_data(rig); + 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,15 +331,28 @@ void *rig_poll_routine(void *arg) split_prev = split; } } +#endif if (update_occurred) { 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); + } } + network_publish_rig_poll_data(rig); + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping rig poll routine thread\n", __FILE__, __LINE__); @@ -291,6 +410,8 @@ int rig_poll_routine_start(RIG *rig) RETURNFUNC(-RIG_EINTERNAL); } + network_publish_rig_poll_data(rig); + RETURNFUNC(RIG_OK); } @@ -336,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 f5890fe68..b90ab2fac 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; @@ -714,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 *) @@ -727,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 @@ -773,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; @@ -908,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); @@ -930,7 +945,7 @@ void *multicast_publisher(void *arg) // TODO: how to detect closing of pipe, indicate with error code // TODO: error handling, flush pipe in case of error? - hl_usleep(500 * 1000); + hl_usleep(100 * 1000); continue; } @@ -969,6 +984,146 @@ void *multicast_publisher(void *arg) 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__); + + 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; + } + + // 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__, + 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) + { + 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 = 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)); + return NULL; + } + + rs->multicast_receiver_run = 1; + + while (rs->multicast_receiver_run == 1) + { + 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); + + if (result <= 0) + { + if (result < 0) + { + if (errno == 0 || errno == EAGAIN || errno == EWOULDBLOCK) + { + 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)); + } + break; + } + + // TODO: handle commands from multicast clients + 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 + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s(%d): Stopping multicast receiver\n", __FILE__, + __LINE__); + return NULL; +} + //! @endcond /** @@ -990,11 +1145,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__); @@ -1025,6 +1180,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__, @@ -1036,11 +1209,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; @@ -1142,5 +1310,172 @@ 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); + } + + // 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)); + + 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) + { +#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); + } + + 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..7dc9c517c 100644 --- a/src/network.h +++ b/src/network.h @@ -34,8 +34,11 @@ 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); +HAMLIB_EXPORT(int) network_multicast_receiver_stop(RIG *rig); __END_DECLS diff --git a/src/rig.c b/src/rig.c index f0fae4803..c5368d234 100644 --- a/src/rig.c +++ b/src/rig.c @@ -604,6 +604,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; @@ -682,7 +683,11 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->current_vfo = RIG_VFO_CURR; /* we don't know yet! */ rs->rx_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; @@ -1054,6 +1059,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) @@ -1102,6 +1109,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); } @@ -1320,6 +1328,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); } @@ -1328,11 +1337,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, @@ -1385,6 +1393,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); } } @@ -1489,22 +1498,40 @@ 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 + } + + rig->state.comm_status = RIG_COMM_STATUS_OK; + + add_opened_rig(rig); + RETURNFUNC2(RIG_OK); } @@ -1544,11 +1571,15 @@ 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); + 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. @@ -1663,8 +1694,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; @@ -1892,6 +1921,28 @@ int rig_set_freq(RIG *rig, vfo_t vfo, freq_t freq) rig_strvfo(vfo), freq); #endif + 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)) + { + rig->state.doppler = 1; + 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 || (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(%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 (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; } @@ -2300,9 +2351,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) @@ -3484,6 +3537,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/snapshot_data.c b/src/snapshot_data.c index d8d6aa8bf..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) { @@ -161,19 +147,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 +174,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) @@ -325,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 f3c4ad648..b2118ac18 100644 --- a/src/token.h +++ b/src/token.h @@ -97,6 +97,8 @@ #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) +#define TOK_DEVICE_ID TOKEN_FRONTEND(41) /* * rig specific tokens @@ -131,6 +133,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/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) 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 0aef644cb..4d6adb175 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); @@ -1255,3 +1256,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.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.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/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); diff --git a/tests/rigctl.c b/tests/rigctl.c index f388fb00e..73e5380ad 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)); @@ -476,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) @@ -648,6 +743,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 +861,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/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 dbf648ddc..2bce5b814 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'}, @@ -138,15 +136,13 @@ 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"; 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; @@ -200,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; @@ -618,33 +618,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); @@ -686,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) @@ -867,18 +855,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, @@ -992,6 +968,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) @@ -1016,6 +1002,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 @@ -1148,8 +1142,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 +1456,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" 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)