/* * Hamlib Interface - rotator configuration interface * Copyright (c) 2000-2010 by Stephane Fillod * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /** * \addtogroup rotator * @{ */ /** * \brief Rotator Configuration Interface. * * \file rot_conf.c */ #include #include #include #include /* Standard input/output definitions */ #include /* String function definitions */ #include #include "rot_conf.h" #include "token.h" /* * Configuration options available in the rot_state struct. */ static const struct confparams rotfrontend_cfg_params[] = { { TOK_PATHNAME, "rot_pathname", "Rig path name", "Path name to the device file of the rotator", "/dev/rotator", RIG_CONF_STRING, }, { TOK_WRITE_DELAY, "write_delay", "Write delay", "Delay in ms between each byte sent out", "0", RIG_CONF_NUMERIC, { .n = { 0, 1000, 1 } } }, { TOK_POST_WRITE_DELAY, "post_write_delay", "Post write delay", "Delay in ms between each command sent out", "0", RIG_CONF_NUMERIC, { .n = { 0, 1000, 1 } } }, { TOK_TIMEOUT, "timeout", "Timeout", "Timeout in ms", "0", RIG_CONF_NUMERIC, { .n = { 0, 10000, 1 } } }, { TOK_RETRY, "retry", "Retry", "Max number of retry", "0", RIG_CONF_NUMERIC, { .n = { 0, 10, 1 } } }, { TOK_FLUSHX, "flushx", "Flush with read instead of TCFLUSH", "True enables flushing serial port with read instead of TCFLUSH -- MicroHam", "0", RIG_CONF_CHECKBUTTON, { } }, { TOK_MIN_AZ, "min_az", "Minimum azimuth", "Minimum rotator azimuth in degrees", "-180", RIG_CONF_NUMERIC, { .n = { -360, 360, .001 } } }, { TOK_MAX_AZ, "max_az", "Maximum azimuth", "Maximum rotator azimuth in degrees", "180", RIG_CONF_NUMERIC, { .n = { -360, 360, .001 } } }, { TOK_MIN_EL, "min_el", "Minimum elevation", "Minimum rotator elevation in degrees", "0", RIG_CONF_NUMERIC, { .n = { -90, 180, .001 } } }, { TOK_MAX_EL, "max_el", "Maximum elevation", "Maximum rotator elevation in degrees", "90", RIG_CONF_NUMERIC, { .n = { -90, 180, .001 } } }, { TOK_SOUTH_ZERO, "south_zero", "South is zero deg", "Adjust azimuth 180 degrees for south oriented rotators", "0", RIG_CONF_CHECKBUTTON, }, { RIG_CONF_END, NULL, } }; static const struct confparams rotfrontend_serial_cfg_params[] = { #include "serial_cfg_params.h" }; /** @} */ /* rotator definitions */ /** * \addtogroup rot_internal * @{ */ /** * \brief Set a rotator state value from alpha input. * \param rot The #ROT handle. * \param token TOK_... specify which value to set. * \param val Input. * * Assumes rot != NULL and val != NULL. * * \return RIG_OK or a **negative value** on error. * * \retval RIG_OK TOK_... value set successfully. * \retval RIG_EINVAL TOK_.. value not set. * * \sa frontrot_get_conf() */ int frontrot_set_conf(ROT *rot, hamlib_token_t token, const char *val) { struct rot_state *rs; hamlib_port_t *rotp = ROTPORT(rot); int val_i; rs = ROTSTATE(rot); rot_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); switch (token) { case TOK_PATHNAME: strncpy(rotp->pathname, val, HAMLIB_FILPATHLEN - 1); strncpy(rs->rotport_deprecated.pathname, val, HAMLIB_FILPATHLEN - 1); break; case TOK_WRITE_DELAY: if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->write_delay = val_i; rs->rotport_deprecated.write_delay = val_i; break; case TOK_POST_WRITE_DELAY: if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->post_write_delay = val_i; rs->rotport_deprecated.post_write_delay = val_i; break; case TOK_TIMEOUT: if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->timeout = val_i; rs->rotport_deprecated.timeout = val_i; break; case TOK_RETRY: if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->retry = val_i; rs->rotport_deprecated.retry = val_i; break; case TOK_SERIAL_SPEED: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->parm.serial.rate = val_i; rs->rotport_deprecated.parm.serial.rate = val_i; break; case TOK_DATA_BITS: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->parm.serial.data_bits = val_i; rs->rotport_deprecated.parm.serial.data_bits = val_i; break; case TOK_STOP_BITS: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (1 != sscanf(val, "%d", &val_i)) { return -RIG_EINVAL; } rotp->parm.serial.stop_bits = val_i; rs->rotport_deprecated.parm.serial.stop_bits = val_i; break; case TOK_PARITY: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (!strcmp(val, "None")) { val_i = RIG_PARITY_NONE; } else if (!strcmp(val, "Odd")) { val_i = RIG_PARITY_ODD; } else if (!strcmp(val, "Even")) { val_i = RIG_PARITY_EVEN; } else if (!strcmp(val, "Mark")) { val_i = RIG_PARITY_MARK; } else if (!strcmp(val, "Space")) { val_i = RIG_PARITY_SPACE; } else { return -RIG_EINVAL; } rotp->parm.serial.parity = val_i; rs->rotport_deprecated.parm.serial.parity = val_i; break; case TOK_HANDSHAKE: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (!strcmp(val, "None")) { val_i = RIG_HANDSHAKE_NONE; } else if (!strcmp(val, "XONXOFF")) { val_i = RIG_HANDSHAKE_XONXOFF; } else if (!strcmp(val, "Hardware")) { val_i = RIG_HANDSHAKE_HARDWARE; } else { return -RIG_EINVAL; } rotp->parm.serial.handshake = val_i; rs->rotport_deprecated.parm.serial.handshake = val_i; break; case TOK_FLUSHX: rotp->flushx = atoi(val); rs->rotport_deprecated.flushx = atoi(val); break; case TOK_MIN_AZ: rs->min_az = atof(val); break; case TOK_MAX_AZ: rs->max_az = atof(val); break; case TOK_MIN_EL: rs->min_el = atof(val); break; case TOK_MAX_EL: rs->max_el = atof(val); break; case TOK_SOUTH_ZERO: rs->south_zero = atoi(val); break; case TOK_RTS_STATE: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (!strcmp(val, "Unset")) { val_i = RIG_SIGNAL_UNSET; } else if (!strcmp(val, "ON")) { val_i = RIG_SIGNAL_ON; } else if (!strcmp(val, "OFF")) { val_i = RIG_SIGNAL_OFF; } else { return -RIG_EINVAL; } rotp->parm.serial.rts_state = val_i; rs->rotport_deprecated.parm.serial.rts_state = val_i; break; case TOK_DTR_STATE: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } if (!strcmp(val, "Unset")) { val_i = RIG_SIGNAL_UNSET; } else if (!strcmp(val, "ON")) { val_i = RIG_SIGNAL_ON; } else if (!strcmp(val, "OFF")) { val_i = RIG_SIGNAL_OFF; } else { return -RIG_EINVAL; } rotp->parm.serial.dtr_state = val_i; rs->rotport_deprecated.parm.serial.dtr_state = val_i; break; default: return -RIG_EINVAL; } return RIG_OK; } /** * \brief Query data from a rotator state in alpha form. * * \param rot The #ROT handle. * \param token TOK_... specify which data to query. * \param val Result. * * Assumes rot != NULL and val != NULL. * * \return RIG_OK or a **negative value** on error. * * \retval RIG_OK TOK_... value queried successfully. * \retval RIG_EINVAL TOK_.. value not queried. * * \sa frontrot_set_conf() */ int frontrot_get_conf(ROT *rot, hamlib_token_t token, char *val, int val_len) { struct rot_state *rs; hamlib_port_t *rotp = ROTPORT(rot); const char *s; rs = ROTSTATE(rot); rot_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); switch (token) { case TOK_PATHNAME: strncpy(val, rotp->pathname, val_len - 1); break; case TOK_WRITE_DELAY: SNPRINTF(val, val_len, "%d", rotp->write_delay); break; case TOK_POST_WRITE_DELAY: SNPRINTF(val, val_len, "%d", rotp->post_write_delay); break; case TOK_TIMEOUT: SNPRINTF(val, val_len, "%d", rotp->timeout); break; case TOK_RETRY: SNPRINTF(val, val_len, "%d", rotp->retry); break; case TOK_SERIAL_SPEED: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } SNPRINTF(val, val_len, "%d", rotp->parm.serial.rate); break; case TOK_DATA_BITS: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } SNPRINTF(val, val_len, "%d", rotp->parm.serial.data_bits); break; case TOK_STOP_BITS: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } SNPRINTF(val, val_len, "%d", rotp->parm.serial.stop_bits); break; case TOK_PARITY: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } switch (rotp->parm.serial.parity) { case RIG_PARITY_NONE: s = "None"; break; case RIG_PARITY_ODD: s = "Odd"; break; case RIG_PARITY_EVEN: s = "Even"; break; case RIG_PARITY_MARK: s = "Mark"; break; case RIG_PARITY_SPACE: s = "Space"; break; default: return -RIG_EINVAL; } strncpy(val, s, val_len); break; case TOK_HANDSHAKE: if (rotp->type.rig != RIG_PORT_SERIAL) { return -RIG_EINVAL; } switch (rotp->parm.serial.handshake) { case RIG_HANDSHAKE_NONE: s = "None"; break; case RIG_HANDSHAKE_XONXOFF: s = "XONXOFF"; break; case RIG_HANDSHAKE_HARDWARE: s = "Hardware"; break; default: return -RIG_EINVAL; } strcpy(val, s); break; case TOK_MIN_AZ: SNPRINTF(val, val_len, "%f", rs->min_az); break; case TOK_MAX_AZ: SNPRINTF(val, val_len, "%f", rs->max_az); break; case TOK_MIN_EL: SNPRINTF(val, val_len, "%f", rs->min_el); break; case TOK_MAX_EL: SNPRINTF(val, val_len, "%f", rs->max_el); break; case TOK_SOUTH_ZERO: SNPRINTF(val, val_len, "%d", rs->south_zero); break; default: return -RIG_EINVAL; } return RIG_OK; } /** @} */ /* rot_internal definitions */ /** * \addtogroup rotator * @{ */ /** * \brief Executes cfunc on all the elements stored in the configuration * parameters table. * * \param rot The #ROT handle. * \param cfunc Pointer to the callback function(...). * \param data Data for the callback function. * * Start first with backend configuration parameters table, then finish with * frontend configuration parameters table. * * \return RIG_OK if the operation has been successful, otherwise a **negative * value** if an error occurred (in which case, cause is set appropriately). * * \retval RIG_OK The \a cfunc action completed successfully. * \retval RIG_EINVAL \a rot is NULL or inconsistent or \a cfunc is NULL. */ int HAMLIB_API rot_token_foreach(ROT *rot, int (*cfunc)(const struct confparams *, rig_ptr_t), rig_ptr_t data) { const struct confparams *cfp; rot_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rot || !rot->caps || !cfunc) { return -RIG_EINVAL; } for (cfp = rotfrontend_cfg_params; cfp->name; cfp++) { if ((*cfunc)(cfp, data) == 0) { return RIG_OK; } } if (rot->caps->port_type == RIG_PORT_SERIAL) { for (cfp = rotfrontend_serial_cfg_params; cfp->name; cfp++) { if ((*cfunc)(cfp, data) == 0) { return RIG_OK; } } } for (cfp = rot->caps->cfgparams; cfp && cfp->name; cfp++) { if ((*cfunc)(cfp, data) == 0) { return RIG_OK; } } return RIG_OK; } /** * \brief Query a rotator configuration parameter token by its name. * * \param rot The #ROT handle. * \param name Configuration parameter token name string. * \return confparams or NULL * * Use this function to get a pointer to the token in the #confparams * structure. Searches the backend config params table first, then falls back * to the frontend config params table. * * \return A pointer to the token in the #confparams structure or NULL if * \a rot is NULL or inconsistent or if \a name is not found (how can the * caller know which occurred?). * * \sa rot_token_lookup() * * TODO: Should use Lex to speed it up, strcmp() hurts! */ const struct confparams *HAMLIB_API rot_confparam_lookup(ROT *rot, const char *name) { const struct confparams *cfp; hamlib_token_t token; //rot_debug(RIG_DEBUG_VERBOSE, "%s called lookup=%s\n", __func__, name); if (!rot || !rot->caps) { return NULL; } /* 0 returned for invalid format */ token = strtol(name, NULL, 0); //rig_debug(RIG_DEBUG_TRACE, "%s: token=%d\n", __func__, (int)token); for (cfp = rot->caps->cfgparams; cfp && cfp->name; cfp++) { //rig_debug(RIG_DEBUG_TRACE, "%s: name=%s cfp->name=%s cfp->token=%d, token=%d\n", __func__, name, cfp->name, (int)cfp->token, (int)token); if (!strcmp(cfp->name, name) || token == cfp->token) { return cfp; } } //rig_debug(RIG_DEBUG_TRACE, "%s: frontend check\n", __func__); for (cfp = rotfrontend_cfg_params; cfp->name; cfp++) { //rig_debug(RIG_DEBUG_TRACE, "%s: name=%s cfp->name=%s cfp->token=%d, token=%d\n", __func__, name, cfp->name, (int)cfp->token, (int)token); if (!strcmp(cfp->name, name) || token == cfp->token) { return cfp; } } if (rot->caps->port_type == RIG_PORT_SERIAL) { for (cfp = rotfrontend_serial_cfg_params; cfp->name; cfp++) { if (!strcmp(cfp->name, name) || token == cfp->token) { return cfp; } } } return NULL; } /** * \brief Search for the token ID associated with a rotator configuration * parameter token name. * * \param rot The #ROT handle. * \param name Configuration parameter token name string. * * Searches the backend and frontend configuration parameters tables for the * token ID. * * \return The token ID value or #RIG_CONF_END if the lookup failed. * * \sa rot_confparam_lookup() */ hamlib_token_t HAMLIB_API rot_token_lookup(ROT *rot, const char *name) { const struct confparams *cfp; rot_debug(RIG_DEBUG_VERBOSE, "%s called lookup %s\n", __func__, name); cfp = rot_confparam_lookup(rot, name); if (!cfp) { return RIG_CONF_END; } return cfp->token; } /** * \brief Set a rotator configuration parameter. * * \param rot The #ROT handle. * \param token The token of the parameter to set. * \param val The value to set the parameter to. * * Sets a rotator configuration parameter to \a val. * * \return RIG_OK if the operation has been successful, otherwise a **negative * value** if an error occurred (in which case, cause is set appropriately). * * \retval RIG_OK The parameter was set successfully. * \retval RIG_EINVAL \a rot is NULL or inconsistent or \a token is invalid. * \retval RIG_ENAVAIL rot_caps#set_conf() capability is not available. * * \sa rot_get_conf() */ int HAMLIB_API rot_set_conf(ROT *rot, hamlib_token_t token, const char *val) { rot_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rot || !rot->caps) { return -RIG_EINVAL; } if (rig_need_debug(RIG_DEBUG_VERBOSE)) { const struct confparams *cfp; char tokenstr[32]; SNPRINTF(tokenstr, sizeof(tokenstr), "%ld", token); cfp = rot_confparam_lookup(rot, tokenstr); if (!cfp) { return -RIG_EINVAL; } rot_debug(RIG_DEBUG_VERBOSE, "%s: %s='%s'\n", __func__, cfp->name, val); } if (IS_TOKEN_FRONTEND(token)) { return frontrot_set_conf(rot, token, val); } if (rot->caps->set_conf == NULL) { return -RIG_ENAVAIL; } return rot->caps->set_conf(rot, token, val); } /** * \brief Query the value of a rotator configuration parameter. * * \param rot The #ROT handle. * \param token The token of the parameter to query. * \param val The location where to store the value of the configuration \a token. * * Retrieves the value of a configuration parameter associated with \a token. * * \return RIG_OK if the operation has been successful, otherwise a **negative * value** if an error occurred (in which case, cause is set appropriately). * * \retval RIG_OK Querying the parameter was successful. * \retval RIG_EINVAL \a rot is NULL or inconsistent. * \retval RIG_ENAVAIL rot_caps#get_conf() capability is not available. * * \sa rot_set_conf() */ // This call will change in Hamlib 5.0 to pass val_len in //int HAMLIB_API rot_get_conf(ROT *rot, hamlib_token_t token, char *val, int val_len) int HAMLIB_API rot_get_conf(ROT *rot, hamlib_token_t token, char *val) { // 128 is the default size we are called with return rot_get_conf2(rot, token, val, 128); } int HAMLIB_API rot_get_conf2(ROT *rot, hamlib_token_t token, char *val, int val_len) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rot || !rot->caps || !val) { return -RIG_EINVAL; } if (IS_TOKEN_FRONTEND(token)) { return frontrot_get_conf(rot, token, val, val_len); } if (rot->caps->get_conf2) { return rot->caps->get_conf2(rot, token, val, val_len); } if (rot->caps->get_conf == NULL) { return -RIG_ENAVAIL; } return rot->caps->get_conf(rot, token, val); } /** @} */