From 7e9c5669445cd5f576a75a7e319e1fdfae170e82 Mon Sep 17 00:00:00 2001 From: Michael Black Date: Wed, 12 Jun 2019 15:52:35 -0500 Subject: [PATCH] Add first cut at amplifier control --- Makefile.am | 4 +- README.developer | 19 +- amplifiers/elecraft/Android.mk | 12 + amplifiers/elecraft/Makefile.am | 8 + amplifiers/elecraft/elecraft.c | 0 amplifiers/elecraft/kpa.c | 593 ++++++++ amplifiers/elecraft/kpa.h | 60 + amplifiers/elecraft/kpa1500.c | 207 +++ amplifiers/elecraft/make.log | 34 + configure.ac | 18 + dummy/Makefile.am | 2 +- dummy/amp_dummy.c | 374 +++++ dummy/amp_dummy.h | 29 + dummy/netampctl.c | 221 +++ include/hamlib/ampclass.h | 65 + include/hamlib/amplifier.h | 475 +++++++ include/hamlib/amplist.h | 104 ++ include/hamlib/rig.h | 14 +- src/Makefile.am | 7 +- src/amp_conf.c | 695 ++++++++++ src/amp_conf.h | 31 + src/amp_reg.c | 381 +++++ src/amplifier.c | 708 ++++++++++ src/ext.c | 1 - src/extamp.c | 249 ++++ src/misc.c | 73 + src/register.h | 16 + src/rig.c | 8 +- src/settings.c | 30 + tests/Makefile.am | 10 +- tests/ampctl.c | 479 +++++++ tests/ampctl_parse.c | 2305 +++++++++++++++++++++++++++++++ tests/ampctl_parse.h | 50 + tests/ampctld.c | 715 ++++++++++ tests/dumpcaps_amp.c | 155 +++ tests/sprintflst.c | 29 + tests/sprintflst.h | 1 + 37 files changed, 8159 insertions(+), 23 deletions(-) create mode 100644 amplifiers/elecraft/Android.mk create mode 100644 amplifiers/elecraft/Makefile.am create mode 100644 amplifiers/elecraft/elecraft.c create mode 100644 amplifiers/elecraft/kpa.c create mode 100644 amplifiers/elecraft/kpa.h create mode 100644 amplifiers/elecraft/kpa1500.c create mode 100644 amplifiers/elecraft/make.log create mode 100644 dummy/amp_dummy.c create mode 100644 dummy/amp_dummy.h create mode 100644 dummy/netampctl.c create mode 100644 include/hamlib/ampclass.h create mode 100644 include/hamlib/amplifier.h create mode 100644 include/hamlib/amplist.h create mode 100644 src/amp_conf.c create mode 100644 src/amp_conf.h create mode 100644 src/amp_reg.c create mode 100644 src/amplifier.c create mode 100644 src/extamp.c create mode 100644 tests/ampctl.c create mode 100644 tests/ampctl_parse.c create mode 100644 tests/ampctl_parse.h create mode 100644 tests/ampctld.c create mode 100644 tests/dumpcaps_amp.c diff --git a/Makefile.am b/Makefile.am index 42373f927..1ff17cd20 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ doc_DATA = ChangeLog COPYING COPYING.LIB LICENSE \ SUBDIRS = macros include lib \ $(BACKEND_LIST) \ $(ROT_BACKEND_LIST) \ + $(AMP_BACKEND_LIST) \ src \ $(BINDINGS) \ tests doc @@ -25,7 +26,8 @@ DIST_SUBDIRS = macros include lib src c++ bindings tests doc android scripts\ adat alinco aor barrett drake dorji dummy elad flexradio icom icmarine jrc\ kachina kenwood kit lowe pcr prm80 racal rft rs skanti tapr tentec\ tuner uniden wj yaesu winradio\ - $(ROT_BACKEND_LIST) + amplifiers/elecraft + $(ROT_BACKEND_LIST) $(AMP_BACKEND_LIST) # Install any third party macros into our tree for distribution ACLOCAL_AMFLAGS = -I macros --install diff --git a/README.developer b/README.developer index a842cb693..1c85d525e 100644 --- a/README.developer +++ b/README.developer @@ -9,14 +9,14 @@ See README for frontend/backend outline. See README.betatester for background on testing Hamlib. -The library provides functions for both radio and rotator control, -and data retrieval from the radio or rotator. A number of functions useful +The library provides functions for both radio, rotator, and amplifier control, +and data retrieval from the radio, rotator, or amplifier. A number of functions useful for calculating distance and bearing and grid square conversion are included. libhamlib.so - library that provides generic API for all RIG types. This is what Application programmers will "see". Will have different names on other platforms, e.g. libhamlib-2.dll on MS windows. Also - contains all radio and rotator "backends" (formerly in their own + contains all radio, rotator, and amplifier "backends" (formerly in their own dlopen'ed libraries) provided by Hamlib. Backend Examples are: @@ -401,7 +401,8 @@ So far, Hamlib has been tested successfully under the following systems: Makefile.am (not needed for rotators) 2.3. Add the backend name to the BACKEND_LIST variable (add to - ROT_BACKEND_LIST for a new rotor backend) in configure.ac. + ROT_BACKEND_LIST for a new rotor backend or to AMP_BACKEND_LIST for + a new amplifier) in configure.ac. 2.4. Add "mybackend/Makefile" in the AC_CONFIG_FILES macro at the bottom of configure.ac. @@ -414,8 +415,10 @@ So far, Hamlib has been tested successfully under the following systems: the rig_backend_list structure in src/register.c or, add { ROT_MYROTBACKEND, ROT_BACKEND_MYROTBACKEND, ROT_FUNCNAMA(myrotbackend) }, to the rot_backend_list structure in src/rot_reg.c. + { AMP_MYAMPBACKEND, AMP_BACKEND_MYAMPBACKEND, AMP_FUNCNAMA(myaotbackend) }, + to the aot_backend_list structure in src/amp_reg.c. - 2.7. Add the new backend to include/hamlib/riglist.h or include/hamlib/rotlist.h + 2.7. Add the new backend to include/hamlib/riglist.h or include/hamlib/rotlist.h or include/hamlib/amplist.h by selecting the next higher backend ID number. 2.8. Create mybackend/Makefile.am, mybackend.c mybackend.h @@ -465,7 +468,7 @@ So far, Hamlib has been tested successfully under the following systems: 3. How to add a new model to an existing backend 3.1. make sure there's already a (unique) ID for the model to be added - in include/hamlib/riglist.h or include/hamlib/rotlist.h + in include/hamlib/riglist.h or include/hamlib/rotlist.h or include/hamlib/amplist.h 3.2. locate the existing backend 3.3. Clone the most similar model in the backend @@ -539,7 +542,7 @@ Contributed code to the Hamlib frontend must be released under the LGPL. Contributed code to Hamlib backends must follow backend current license. Needless to say, the LGPL is the license of choice. -End user applications like rigctl, rotctl and networked daemons should be +End user applications like rigctl, rotctl, ampctl and networked daemons should be released under the GPL, so any contributed code must follow the license. @@ -577,7 +580,7 @@ Hamlib should also compile with the following common compilers: * gcc-3.0 and gcc-3.2+ (nearly deprecated?) * gcc-4.x and newer * in shared and static - * C++ compiler against rig.h, riglist.h, rotator.h + * C++ compiler against rig.h, riglist.h, rotator.h, amplifier.h * clang compiler Portability issues to watch: diff --git a/amplifiers/elecraft/Android.mk b/amplifiers/elecraft/Android.mk new file mode 100644 index 000000000..44cea9eaf --- /dev/null +++ b/amplifiers/elecraft/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := elecraft.c kpa.c kpa1500.c +LOCAL_MODULE := elecraft + +LOCAL_CFLAGS := -DHAVE_CONFIG_H +LOCAL_C_INCLUDES := android include src +LOCAL_LDLIBS := -lhamlib + +include $(BUILD_STATIC_LIBRARY) diff --git a/amplifiers/elecraft/Makefile.am b/amplifiers/elecraft/Makefile.am new file mode 100644 index 000000000..a3b182a71 --- /dev/null +++ b/amplifiers/elecraft/Makefile.am @@ -0,0 +1,8 @@ +SRC = kpa1500.c + +ELECRAFTSRC = kpa.c kpa.h + +noinst_LTLIBRARIES = libhamlib-elecraft.la +libhamlib_elecraft_la_SOURCES = $(SRC) $(ELECRAFTSRC) + +EXTRA_DIST = README.elecraft Android.mk diff --git a/amplifiers/elecraft/elecraft.c b/amplifiers/elecraft/elecraft.c new file mode 100644 index 000000000..e69de29bb diff --git a/amplifiers/elecraft/kpa.c b/amplifiers/elecraft/kpa.c new file mode 100644 index 000000000..2114d1424 --- /dev/null +++ b/amplifiers/elecraft/kpa.c @@ -0,0 +1,593 @@ +/* + * Hamlib Elecraft amplifier backend - low level communication routines + * Copyright (c) 2019 by Michael Black W9MDB + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include "kpa.h" + +struct fault_list +{ + int code; + char *errmsg; +}; +const struct fault_list kpa_fault_list [] = +{ + {0, "No fault condition"}, + {0x10, "Watchdog Timer was reset"}, + {0x20, "PA Current is too high"}, + {0x40, "Temperature is too high"}, + {0x60, "Input power is too high"}, + {0x61, "Gain is too low"}, + {0x70, "Invalid frequency"}, + {0x80, "50V supply voltage too low or too high"}, + {0x81, "5V supply voltage too low or too high"}, + {0x82, "10V supply voltage too low or too high"}, + {0x83, "12V supply voltage too low or too high"}, + {0x84, "-12V supply voltage too low or too high"}, + {0x85, "5V or 400V LPF board supply voltages not detected"}, + {0x90, "Reflected power is too high"}, + {0x91, "SWR very high"}, + {0x92, "ATU no match"}, + {0xB0, "Dissipated power too high"}, + {0xC0, "Forward power too high"}, + {0xE0, "Forward power too high for current setting"}, + {0xF0, "Gain is too high"}, + {0, NULL} +}; + +/* + * Initialize data structures + */ + +int kpa_init(AMP *amp) +{ + struct kpa_priv_data *priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) + { + return -RIG_EINVAL; + } + + priv = (struct kpa_priv_data *) + malloc(sizeof(struct kpa_priv_data)); + + if (!priv) + { + return -RIG_ENOMEM; + } + + amp->state.priv = (void *)priv; + + amp->state.ampport.type.rig = RIG_PORT_SERIAL; + + return RIG_OK; +} + +int kpa_flushbuffer(AMP *amp) +{ + struct amp_state *rs; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called, cmd=%s\n", __func__); + + rs = &->state; + + return serial_flush(&rs->ampport); +} + +int kpa_transaction(AMP *amp, const char *cmd, char *response, int response_len) +{ + struct amp_state *rs; + int err; + int len = 0; + char responsebuf[KPABUFSZ]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called, cmd=%s\n", __func__, cmd); + + if (!amp) { return -RIG_EINVAL; } + + kpa_flushbuffer(amp); + + rs = &->state; + + int loop = 3; + + do // wake up the amp by sending ; until we receive ; + { + rig_debug(RIG_DEBUG_VERBOSE, "%s waiting for ;\n", __func__); + char c = ';'; + err = write_block(&rs->ampport, &c, 1); + + if (err != RIG_OK) { return err; } + + int len = read_string(&rs->ampport, responsebuf, KPABUFSZ, ";", 1); + + if (len < 0) { return len; } + } + while (--loop > 0 && (len != 1 || responsebuf[0] != ';')); + + // Now send our command + err = write_block(&rs->ampport, cmd, strlen(cmd)); + + if (err != RIG_OK) { return err; } + + if (response) // if response expected get it + { + responsebuf[0] = 0; + int len = read_string(&rs->ampport, responsebuf, KPABUFSZ, ";", 1); + + if (len < 0) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s called, error=%s\n", __func__, + rigerror(len)); + return len; + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s called, response='%s'\n", __func__, + responsebuf); + } + else // if no response expected try to get one + { + loop = 3; + + do + { + rig_debug(RIG_DEBUG_VERBOSE, "%s waiting for ;\n", __func__); + char c = ';'; + err = write_block(&rs->ampport, &c, 1); + + if (err != RIG_OK) { return err; } + + int len = read_string(&rs->ampport, responsebuf, KPABUFSZ, ";", 1); + + if (len < 0) { return len; } + } + while (--loop > 0 && (len != 1 || responsebuf[0] != ';')); + } + + return RIG_OK; +} + +/* + * Get Info + * returns the model name string + */ +const char *kpa_get_info(AMP *amp) +{ + const struct amp_caps *rc; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) { return (const char *) - RIG_EINVAL; } + + rc = amp->caps; + + return rc->model_name; +} + +int kpa_get_freq(AMP *amp, freq_t *freq) +{ + char responsebuf[KPABUFSZ]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) { return -RIG_EINVAL; } + + int retval = kpa_transaction(amp, "^FR;", responsebuf, sizeof(responsebuf)); + + if (retval != RIG_OK) { return retval; } + + unsigned long tfreq; + int nargs = sscanf(responsebuf, "^FR%ld", &tfreq); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s Error: ^FR response='%s'\n", __func__, + responsebuf); + return -RIG_EPROTO; + } + + *freq = tfreq * 1000; + return RIG_OK; +} + +int kpa_set_freq(AMP *amp, freq_t freq) +{ + char responsebuf[KPABUFSZ]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called, freq=%ld\n", __func__, freq); + + if (!amp) { return -RIG_EINVAL; } + + char cmd[KPABUFSZ]; + sprintf(cmd, "^FR%05ld;", (long)freq / 1000); + int retval = kpa_transaction(amp, cmd, NULL, 0); + + if (retval != RIG_OK) { return retval; } + + unsigned long tfreq; + int nargs = sscanf(responsebuf, "^FR%ld", &tfreq); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s Error: ^FR response='%s'\n", __func__, + responsebuf); + return -RIG_EPROTO; + } + + if (tfreq * 1000 != freq) + { + rig_debug(RIG_DEBUG_ERR, + "%s Error setting freq: ^FR freq!=freq2, %ld!=%ld '%s'\n", __func__, + freq, tfreq * 1000); + return -RIG_EPROTO; + } + + return RIG_OK; +} + +int kpa_get_level(AMP *amp, setting_t level, value_t *val) +{ + char responsebuf[KPABUFSZ]; + char *cmd; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) { return -RIG_EINVAL; } + + // get the current antenna selected + cmd = "^AE;"; + int retval = kpa_transaction(amp, cmd, responsebuf, sizeof(responsebuf)); + + if (retval != RIG_OK) { return retval; } + + int antenna = 0; + int nargs = sscanf(responsebuf, "^AE%d", &antenna); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s antenna=%d\n", __func__, cmd, antenna); + + switch (level) + { + case AMP_LEVEL_SWR: + cmd = "^SW;"; + break; + + case AMP_LEVEL_NH: + cmd = "^DF;"; + break; + + case AMP_LEVEL_PF: + cmd = "^DF;"; + break; + + case AMP_LEVEL_PWR_INPUT: + cmd = "^PWI;"; + break; + + case AMP_LEVEL_PWR_FWD: + cmd = "^PWF;"; + break; + + case AMP_LEVEL_PWR_REFLECTED: + cmd = "^PWR;"; + break; + + case AMP_LEVEL_PWR_PEAK: + cmd = "^PWK;"; + break; + + case AMP_LEVEL_FAULT: + cmd = "^SF;"; + break; + } + + retval = kpa_transaction(amp, cmd, responsebuf, sizeof(responsebuf)); + + if (retval != RIG_OK) { return retval; } + + float float_value = 0; + int int_value = 0, int_value2 = 0; + struct amp_state *rs = &->state; + + switch (level) + { + case AMP_LEVEL_SWR: + nargs = sscanf(responsebuf, "^SW%f", &float_value); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + val->f = float_value / 10.0f; + return RIG_OK; + + case AMP_LEVEL_NH: + case AMP_LEVEL_PF: + nargs = sscanf(responsebuf, "^DF%d,%d", &int_value, &int_value2); + + if (nargs != 2) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s freq range=%dKHz,%dKHz\n", __func__, cmd, + int_value, int_value2); + + // + do + { + retval = read_string(&rs->ampport, responsebuf, sizeof(responsebuf), ";", 1); + + if (retval != RIG_OK) { return retval; } + + if (strstr(responsebuf, "BYPASS") != 0) + { + int antenna2 = 0; + nargs = sscanf(responsebuf, "AN%d Side TX %d %*s %*s %d", &antenna2, &int_value, + &int_value2); + rig_debug(RIG_DEBUG_VERBOSE, "%s response='%s'\n", __func__, responsebuf); + + if (nargs != 3) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s antenna=%d,nH=%d\n", __func__, antenna2, + int_value); + + val->i = level == AMP_LEVEL_NH ? int_value : int_value2; + return RIG_OK; + } + } + while (strstr(responsebuf, "BYPASS")); + + + break; + + + case AMP_LEVEL_PWR_INPUT: + cmd = "^PWI;"; + int pwrinput; + nargs = sscanf(responsebuf, "^SW%d", &pwrinput); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + val->i = pwrinput; + return RIG_OK; + + break; + + case AMP_LEVEL_PWR_FWD: + cmd = "^PWF;"; + int pwrfwd; + nargs = sscanf(responsebuf, "^SW%d", &pwrfwd); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + val->i = pwrfwd; + return RIG_OK; + + break; + + case AMP_LEVEL_PWR_REFLECTED: + cmd = "^PWR;"; + int pwrref; + nargs = sscanf(responsebuf, "^SW%d", &pwrref); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + val->i = pwrref; + return RIG_OK; + + break; + + case AMP_LEVEL_PWR_PEAK: + cmd = "^PWK;"; + int pwrpeak; + nargs = sscanf(responsebuf, "^SW%d", &pwrpeak); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + val->i = pwrpeak; + return RIG_OK; + + break; + + case AMP_LEVEL_FAULT: + cmd = "^SF;"; + int fault; + nargs = sscanf(responsebuf, "^SW%d", &fault); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_ERR, "%s invalid value %s='%s'\n", __func__, cmd, + responsebuf); + return -RIG_EPROTO; + } + + int i; + + for (i = 0; kpa_fault_list[i].errmsg != NULL; ++i) + { + if (kpa_fault_list[i].code == fault) + { + val->s = kpa_fault_list[i].errmsg; + return RIG_OK; + } + } + + rig_debug(RIG_DEBUG_ERR, "%s unknown fault=%s\n", __func__, cmd, responsebuf); + struct kpa_priv_data *priv = amp->state.priv; + sprintf(priv->tmpbuf, "Unknown fault code=0x%02x", fault); + val->s = priv->tmpbuf; + return RIG_OK; + + break; + + default: + rig_debug(RIG_DEBUG_ERR, "%s unknown level=%s\n", __func__, cmd, level); + + } + + return -RIG_EINVAL; +} + +int kpa_get_powerstat(AMP *amp, powerstat_t *status) +{ + char responsebuf[KPABUFSZ]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + *status = RIG_POWER_UNKNOWN; + + if (!amp) { return -RIG_EINVAL; } + + int retval = kpa_transaction(amp, "^ON;", responsebuf, sizeof(responsebuf)); + + if (retval != RIG_OK) { return retval; } + + int ampon; + int nargs = sscanf(responsebuf, "^ON%d", &on); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s Error: ^ON response='%s'\n", __func__, + responsebuf); + return -RIG_EPROTO; + } + + switch (ampon) + { + case 0: *status = RIG_POWER_OFF; return RIG_OK; + + case 1: *status = RIG_POWER_ON; break; + + default: + rig_debug(RIG_DEBUG_VERBOSE, "%s Error: ^ON unknown response='%s'\n", __func__, + responsebuf); + return -RIG_EPROTO; + } + + retval = kpa_transaction(amp, "^OP;", responsebuf, sizeof(responsebuf)); + + if (retval != RIG_OK) { return retval; } + + int operate; + nargs = sscanf(responsebuf, "^ON%d", &operate); + + if (nargs != 1) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s Error: ^ON response='%s'\n", __func__, + responsebuf); + return -RIG_EPROTO; + } + + *status = operate == 1 ? RIG_POWER_OPERATE : RIG_POWER_STANDBY; + + return RIG_OK; +} + +int kpa_set_powerstat(AMP *amp, powerstat_t status) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) { return -RIG_EINVAL; } + + char *cmd = NULL; + + switch (status) + { + case RIG_POWER_UNKNOWN: break; + + case RIG_POWER_OFF: cmd = "^ON0;"; break; + + case RIG_POWER_ON: cmd = "^ON1;"; break; + + case RIG_POWER_OPERATE: cmd = "^OS1;"; break; + + case RIG_POWER_STANDBY: cmd = "^OS0;"; break; + + + default: + rig_debug(RIG_DEBUG_ERR, "%s invalid status=%d\n", __func__, status); + + } + + int retval = kpa_transaction(amp, cmd, NULL, 0); + + if (retval != RIG_OK) { return retval; } + + return RIG_OK; +} + +int kpa_reset(AMP *amp, amp_reset_t reset) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + // toggling from standby to operate supposed to reset + int retval = kpa_set_powerstat(amp, RIG_POWER_STANDBY); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s error setting RIG_POWER_STANDBY\n", __func__, + strerror(retval)); + + } + + return kpa_set_powerstat(amp, RIG_POWER_OPERATE); +} + + diff --git a/amplifiers/elecraft/kpa.h b/amplifiers/elecraft/kpa.h new file mode 100644 index 000000000..e8e7a1689 --- /dev/null +++ b/amplifiers/elecraft/kpa.h @@ -0,0 +1,60 @@ +/* + * Hamlib backend library for the Elecraft amplifier set. + * + * elecraft.h - (C) Michael Black W9MDB 2019 + * + * This shared library provides an API for communicating + * via serial interface to Elecraft amplifiers. + * + * + * 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 + * + */ + +#ifndef _AMP_ELECRAFT_H +#define _AMP_ELECRAFT_H 1 + +#include +#include +#include + +// Is this big enough? +#define KPABUFSZ 100 + +extern const struct amp_caps kpa1500_rot_caps; + +/* + * Private data structure + */ +struct kpa_priv_data +{ + char tmpbuf[256]; // for unknown error msg +}; + + +int kpa_init(AMP *amp); +int kpa_reset(AMP *amp, amp_reset_t reset); +int kpa_flush_buffer(AMP *amp); +int kpa_transaction(AMP *amp, const char *cmd, char *reponse, int reponse_len); +const char *kpa_get_info (AMP *amp); +int kpa_get_freq (AMP *amp, freq_t *freq); +int kpa_set_freq (AMP *amp, freq_t freq); + +int kpa_get_level (AMP *amp, setting_t level, value_t *val); +int kpa_get_powerstat (AMP *amp, powerstat_t *status); +int kpa_set_powerstat (AMP *amp, powerstat_t status); + +#endif /* _AMP_ELECRAFT_H */ + diff --git a/amplifiers/elecraft/kpa1500.c b/amplifiers/elecraft/kpa1500.c new file mode 100644 index 000000000..ff59a3984 --- /dev/null +++ b/amplifiers/elecraft/kpa1500.c @@ -0,0 +1,207 @@ +/* + * Hamlib backend library for the Elecraft KPA1500 command set. + * + * kpa1500.c - (C) Michael Black W9MDB 2019 + * + * This shared library provides an API for communicating + * to an Elecraft KPA1500 amplifier. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* Standard library definitions */ +#include /* String function definitions */ + +#include "register.h" + +#include "kpa.h" + + +struct kpa_priv_data *kpa1500_priv; +/* + * API local implementation + * + */ + +static int kpa1500_init(AMP *amp); +static int kpa1500_cleanup(AMP *amp); + +/* + * Private helper function prototypes + */ + +//static int kpa1500_send_priv_cmd(AMP *amp, const char *cmd); +//static int kpa1500_flush_buffer(AMP *amp); + +/* ************************************* + * + * Separate model capabilities + * + * ************************************* + */ + + +/* + * Elecraft KPA1500 + */ + +const struct amp_caps kpa1500_amp_caps = +{ + .amp_model = AMP_MODEL_ELECRAFT_KPA1500, + .model_name = "KPA1500", + .mfg_name = "Elecraft", + .version = "2019-05-26", + .copyright = "LGPL", + .status = RIG_STATUS_ALPHA, + .amp_type = AMP_TYPE_OTHER, + .port_type = RIG_PORT_SERIAL, + .serial_rate_min = 4800, + .serial_rate_max = 230400, + .serial_data_bits = 8, + .serial_stop_bits = 1, + .serial_parity = RIG_PARITY_NONE, + .serial_handshake = RIG_HANDSHAKE_NONE, + .write_delay = 0, + .post_write_delay = 0, + .timeout = 2000, + .retry = 2, + + .amp_open = amp_open, + .amp_init = kpa1500_init, + .amp_cleanup = kpa1500_cleanup, + .reset = kpa_reset, + .get_info = kpa_get_info, + .get_powerstat = kpa_get_powerstat, + .set_powerstat = kpa_set_powerstat, + .set_freq = kpa_set_freq, + .get_freq = kpa_get_freq, + .get_level = kpa_get_level, +}; + + +/* ************************************ + * + * API functions + * + * ************************************ + */ + + +/* + * Initialize data structures + */ + +static int kpa1500_init(AMP *amp) +{ + struct kpa_priv_data *priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) + { + return -RIG_EINVAL; + } + + priv = (struct kpa_priv_data *) + malloc(sizeof(struct kpa_priv_data)); + + if (!priv) + { + return -RIG_ENOMEM; + } + + amp->state.priv = (void *)priv; + + amp->state.ampport.type.rig = RIG_PORT_SERIAL; + + return RIG_OK; +} + +/* + * Clean up allocated memory structures + */ + +static int kpa1500_cleanup(AMP *amp) +{ + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) + { + return -RIG_EINVAL; + } + + if (amp->state.priv) + { + free(amp->state.priv); + } + + amp->state.priv = NULL; + + return RIG_OK; +} + + +/* + * + */ + +#if 0 +/* + * Send command string to amplifier + */ + +static int kpa1500_send_priv_cmd(AMP *amp, const char *cmdstr) +{ + struct amp_state *rs; + int err; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp) + { + return -RIG_EINVAL; + } + + rs = &->state; + err = write_block(&rs->ampport, cmdstr, strlen(cmdstr)); + + if (err != RIG_OK) + { + return err; + } + + return RIG_OK; +} +#endif + +/* + * Initialize backend + */ + +DECLARE_INITAMP_BACKEND(kpa1500) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + amp_register(&kpa1500_amp_caps); + + return RIG_OK; +} diff --git a/amplifiers/elecraft/make.log b/amplifiers/elecraft/make.log new file mode 100644 index 000000000..66d372466 --- /dev/null +++ b/amplifiers/elecraft/make.log @@ -0,0 +1,34 @@ + CC kpa.lo +kpa.c: In function ‘kpa_get_freq’: +kpa.c:150:12: warning: returning ‘const char *’ from a function with return type ‘int’ makes integer from pointer without a cast [-Wint-conversion] + return (const char *) - RIG_EINVAL; + ^ +kpa.c:153:3: error: ‘rs’ undeclared (first use in this function) + rs = &->state; + ^~ +kpa.c:153:3: note: each undeclared identifier is reported only once for each function it appears in +kpa.c:156:45: warning: passing argument 3 of ‘kpa_transaction’ makes integer from pointer without a cast [-Wint-conversion] + int retval = kpa_transaction(amp, "^FR;", reponsebuf, sizeof(reponsebuf)); + ^~~~~~~~~~ +kpa.c:68:52: note: expected ‘int’ but argument is of type ‘char *’ + int kpa_transaction(AMP *amp, const char *cmd, int cmd_len, char *response, + ~~~~^~~~~~~ +kpa.c:156:57: warning: passing argument 4 of ‘kpa_transaction’ makes pointer from integer without a cast [-Wint-conversion] + int retval = kpa_transaction(amp, "^FR;", reponsebuf, sizeof(reponsebuf)); + ^~~~~~~~~~~~~~~~~~ +kpa.c:68:67: note: expected ‘char *’ but argument is of type ‘long unsigned int’ + int kpa_transaction(AMP *amp, const char *cmd, int cmd_len, char *response, + ~~~~~~^~~~~~~~ +kpa.c:156:16: error: too few arguments to function ‘kpa_transaction’ + int retval = kpa_transaction(amp, "^FR;", reponsebuf, sizeof(reponsebuf)); + ^~~~~~~~~~~~~~~ +kpa.c:68:5: note: declared here + int kpa_transaction(AMP *amp, const char *cmd, int cmd_len, char *response, + ^~~~~~~~~~~~~~~ +kpa.c:160:12: error: ‘err’ undeclared (first use in this function) + return err; + ^~~ +kpa.c:167:1: warning: control reaches end of non-void function [-Wreturn-type] + } + ^ +make: *** [Makefile:479: kpa.lo] Error 1 diff --git a/configure.ac b/configure.ac index 3d4df381f..e252ad6d8 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,8 @@ dnl rotor definitions, e.g. "dummy". Optional backends will not be listed dnl here but will be added later, e.g. "winradio". BACKEND_LIST="adat alinco aor barrett dorji drake dummy elad flexradio icom icmarine jrc kachina kenwood kit lowe pcr prm80 racal rft rs skanti tapr tentec tuner uniden wj yaesu" ROT_BACKEND_LIST="amsat ars celestron cnctrk easycomm ether6 fodtrack gs232a heathkit m2 meade rotorez sartek spid ts7400 prosistel ioptron" +# Amplifiers are all in the amplifiers directory +AMP_BACKEND_LIST="amplifiers/elecraft" dnl See README.release on setting these values # Values given to -version-info when linking. See libtool documentation. @@ -721,6 +723,21 @@ done AC_SUBST([ROT_BACKEND_LIST]) AC_SUBST([ROT_BACKENDEPS]) +## ---------------------------------- ## +## Prepare amplifier backend dependencies ## +## ---------------------------------- ## + +# otherwise parallel 'make -jn' will fail + +for be in ${AMP_BACKEND_LIST} ; do + AMPDIR=`echo $be | awk -F "/" '{print $2}'` + AMP_BACKENDEPS="${AMP_BACKENDEPS} \$(top_builddir)/amplifiers/${AMPDIR}/libhamlib-${AMPDIR}.la" +done + +AC_SUBST([AMP_BACKEND_LIST]) +AC_SUBST([AMP_BACKENDEPS]) + + ## ------------ ## ## Final output ## @@ -790,6 +807,7 @@ dorji/Makefile barrett/Makefile meade/Makefile ioptron/Makefile +amplifiers/elecraft/Makefile hamlib.pc ]) diff --git a/dummy/Makefile.am b/dummy/Makefile.am index d6c8f6287..dcadbed8b 100644 --- a/dummy/Makefile.am +++ b/dummy/Makefile.am @@ -1,4 +1,4 @@ -DUMMYSRC = dummy.c dummy.h rot_dummy.c rot_dummy.h netrigctl.c netrotctl.c flrig.c flrig.h trxmanager.c trxmanager.h +DUMMYSRC = dummy.c dummy.h rot_dummy.c rot_dummy.h netrigctl.c netrotctl.c flrig.c flrig.h trxmanager.c trxmanager.h amp_dummy.c amp_dummy.h netampctl.c noinst_LTLIBRARIES = libhamlib-dummy.la libhamlib_dummy_la_SOURCES = $(DUMMYSRC) diff --git a/dummy/amp_dummy.c b/dummy/amp_dummy.c new file mode 100644 index 000000000..97993a561 --- /dev/null +++ b/dummy/amp_dummy.c @@ -0,0 +1,374 @@ +/* + * Hamlib Dummy backend - main file + * Copyright (c) 2001-2009 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* String function definitions */ +#include /* UNIX standard function definitions */ +#include +#include +#include + +#include "hamlib/amplifier.h" +#include "serial.h" +#include "misc.h" +#include "register.h" + +#include "amp_dummy.h" + +#if 0 +static const struct confparams dummy_amp_ext_levels[] = +{ + { + AMP_LEVEL_SWR, "SWR", "SWR", "SWR", + NULL, RIG_CONF_NUMERIC, { .n = { 1, 99, .1 } } + } +}; +#endif + +#define AMP_LEVELS (AMP_LEVEL_SWR|AMP_LEVEL_PF|AMP_LEVEL_NH|AMP_LEVEL_PWR_INPUT|AMP_LEVEL_PWR_FWD|AMP_LEVEL_PWR_REFLECTED|AMP_LEVEL_PWR_PEAK|AMP_LEVEL_FAULT) + +struct dummy_amp_priv_data +{ + freq_t freq; + powerstat_t powerstat; +}; + + +static int dummy_amp_init(AMP *amp) +{ + struct dummy_amp_priv_data *priv; + + priv = (struct dummy_amp_priv_data *) + malloc(sizeof(struct dummy_amp_priv_data)); + + if (!priv) + { + return -RIG_ENOMEM; + } + + amp->state.priv = (void *)priv; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + amp->state.ampport.type.rig = RIG_PORT_NONE; + + priv->freq = 0; + + return RIG_OK; +} + +static int dummy_amp_cleanup(AMP *amp) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (amp->state.priv) + { + free(amp->state.priv); + } + + amp->state.priv = NULL; + + return RIG_OK; +} + +static int dummy_amp_open(AMP *amp) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + return RIG_OK; +} + +static int dummy_amp_close(AMP *amp) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + return RIG_OK; +} + +static int dummy_amp_reset(AMP *amp, amp_reset_t reset) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + switch (reset) + { + case AMP_RESET_MEM: + rig_debug(RIG_DEBUG_VERBOSE, "Reset memory\n"); + break; + + case AMP_RESET_FAULT: + rig_debug(RIG_DEBUG_VERBOSE, "Reset fault\n"); + break; + + case AMP_RESET_AMP: + rig_debug(RIG_DEBUG_VERBOSE, "Reset amplifier\n"); + break; + + default: + rig_debug(RIG_DEBUG_VERBOSE, "Reset unknown=%d\n", reset); + return -RIG_EINVAL; + } + + return RIG_OK; +} + +/* +^ON1 turns amp on +^ON0 turns amp off +^OP0 amp in standby +^OP1 amp in operate + +^PWF which gets forward power from amp. Reply would be ^PWFnnnn format + +^PWK which gets peak forward power. Response is ^PWKnnnn + +^PWR peak reflected power. Response is ^PWRnnnn + +^SW gets SWR. Response is ^SWnnn. Example return of ^SW025 would be 2.5:1 + +Also a way to display faults (there are commands) +*/ + +static int dummy_amp_get_freq(AMP *amp, freq_t *freq) +{ + struct dummy_amp_priv_data *priv = (struct dummy_amp_priv_data *) + amp->state.priv; + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + *freq = priv->freq; + return RIG_OK; +} + +static int dummy_amp_set_freq(AMP *amp, freq_t freq) +{ + struct dummy_amp_priv_data *priv = (struct dummy_amp_priv_data *) + amp->state.priv; + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + priv->freq = freq; + return RIG_OK; +} + +static const char *dummy_amp_get_info(AMP *amp) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + return "Dummy amplifier"; +} + +static int dummy_amp_get_level(AMP *amp, setting_t level, value_t *val) +{ + static int flag = 1; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + flag = !flag; + + // values toggle between two expected min/~max values for dev purposes + switch (level) + { + case AMP_LEVEL_SWR: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_SWR\n", __func__); + val->f = flag == 0 ? 1.0 : 99.0; + return RIG_OK; + + case AMP_LEVEL_PF: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_PF\n", __func__); + val->f = flag == 0 ? 0 : 2701.2; + return RIG_OK; + + case AMP_LEVEL_NH: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_UH\n", __func__); + val->i = flag == 0 ? 0 : 8370; + return RIG_OK; + + case AMP_LEVEL_PWR_INPUT: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_PWRINPUT\n", __func__); + val->i = flag == 0 ? 0 : 1499 ; + return RIG_OK; + + case AMP_LEVEL_PWR_FWD: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_PWRFWD\n", __func__); + val->i = flag == 0 ? 0 : 1499 ; + return RIG_OK; + + case AMP_LEVEL_PWR_REFLECTED: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_PWRREFLECTED\n", __func__); + val->i = flag == 0 ? 0 : 1499 ; + return RIG_OK; + + case AMP_LEVEL_PWR_PEAK: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_PWRPEAK\n", __func__); + val->i = flag == 0 ? 0 : 1500 ; + return RIG_OK; + + case AMP_LEVEL_FAULT: + rig_debug(RIG_DEBUG_VERBOSE, "%s AMP_LEVEL_FAULT\n", __func__); + val->s = flag == 0 ? "No Fault" : "SWR too high"; // SWR too high for KPA1500 + return RIG_OK; + + default: + rig_debug(RIG_DEBUG_VERBOSE, "%s Unknownd AMP_LEVEL=%d\n", __func__, level); + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s flag=%d\n", __func__, flag); + return -RIG_EINVAL; +} + +static int dummy_amp_set_powerstat(AMP *amp, powerstat_t status) +{ + struct dummy_amp_priv_data *priv = (struct dummy_amp_priv_data *) + amp->state.priv; + + switch (status) + { + case RIG_POWER_OFF: + rig_debug(RIG_DEBUG_VERBOSE, "%s called POWER_OFF\n", __FUNCTION__); + break; + + case RIG_POWER_ON: + rig_debug(RIG_DEBUG_VERBOSE, "%s called POWER_ON\n", __FUNCTION__); + break; + + case RIG_POWER_STANDBY: + rig_debug(RIG_DEBUG_VERBOSE, "%s called POWER_STANDBY\n", __FUNCTION__); + break; + + case RIG_POWER_OPERATE: + rig_debug(RIG_DEBUG_VERBOSE, "%s called POWER_OPERATE\n", __FUNCTION__); + break; + + case RIG_POWER_UNKNOWN: + rig_debug(RIG_DEBUG_VERBOSE, "%s called POWER_UNKNOWN\n", __FUNCTION__); + break; + + default: + rig_debug(RIG_DEBUG_VERBOSE, "%s called invalid power status=%d\n", + __FUNCTION__, status); + return -RIG_EINVAL; + break; + } + + priv->powerstat = status; + + return RIG_OK; +} + + +static int dummy_amp_get_powerstat(AMP *amp, powerstat_t *status) +{ + struct dummy_amp_priv_data *priv = (struct dummy_amp_priv_data *) + amp->state.priv; + + *status = priv->powerstat; + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + return RIG_OK; +} + +#if 0 +static int dummy_amp_get_ext_level(AMP *amp, token_t token, value_t *val) +{ + struct dummy_amp_priv_data *priv = (struct dummy_amp_priv_data *) + amp->state.priv; + const struct confparams *cfp; + struct ext_list *elp; + + cfp = amp_ext_lookup_tok(amp, token); + + if (!cfp) + { + return -RIG_EINVAL; + } + + switch (token) + { + case AMP_LEVEL_SWR: + break; + + default: + return -RIG_EINVAL; + } + + elp = find_ext(curr->ext_levels, token); + + if (!elp) + { + return -RIG_EINTERNAL; + } + + /* load value */ + *val = elp->val; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called: %s\n", __FUNCTION__, + cfp->name); + + return RIG_OK; +} +#endif + + + +/* + * Dummy amplifier capabilities. + */ + +const struct amp_caps dummy_amp_caps = +{ + .amp_model = AMP_MODEL_DUMMY, + .model_name = "Dummy", + .mfg_name = "Hamlib", + .version = "0.1", + .copyright = "LGPL", + .status = RIG_STATUS_ALPHA, + .amp_type = AMP_TYPE_OTHER, + .port_type = RIG_PORT_NONE, + + .has_get_level = AMP_LEVELS, + .has_set_level = RIG_LEVEL_SET(AMP_LEVELS), + + .priv = NULL, /* priv */ + + .amp_init = dummy_amp_init, + .amp_cleanup = dummy_amp_cleanup, + .amp_open = dummy_amp_open, + .amp_close = dummy_amp_close, + + .get_freq = dummy_amp_get_freq, + .set_freq = dummy_amp_set_freq, + .get_info = dummy_amp_get_info, + .get_level = dummy_amp_get_level, + + .set_powerstat = dummy_amp_set_powerstat, + .get_powerstat = dummy_amp_get_powerstat, + + .reset = dummy_amp_reset, + +}; + +DECLARE_INITAMP_BACKEND(dummy) +{ + rig_debug(RIG_DEBUG_VERBOSE, "dummy: _init called\n"); + + amp_register(&dummy_amp_caps); + amp_register(&netampctl_caps); + + return RIG_OK; +} diff --git a/dummy/amp_dummy.h b/dummy/amp_dummy.h new file mode 100644 index 000000000..bda8cdf6f --- /dev/null +++ b/dummy/amp_dummy.h @@ -0,0 +1,29 @@ +/* + * Hamlib Dummy backend - main header + * Copyright (c) 2001-2008 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 + * + */ + +#ifndef _AMP_DUMMY_H +#define _AMP_DUMMY_H 1 + + +extern const struct amp_caps dummy_amp_caps; +extern const struct amp_caps netampctl_caps; + +#endif /* _AMP_DUMMY_H */ diff --git a/dummy/netampctl.c b/dummy/netampctl.c new file mode 100644 index 000000000..36614318f --- /dev/null +++ b/dummy/netampctl.c @@ -0,0 +1,221 @@ +/* + * Hamlib Netampctl backend - main file + * Copyright (c) 2001-2009 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* String function definitions */ +#include /* UNIX standard function definitions */ +#include +#include + +#include "hamlib/amplifier.h" +#include "iofunc.h" +#include "misc.h" + +#include "amp_dummy.h" + +#define CMD_MAX 32 +#define BUF_MAX 64 + +/* + * Helper function with protocol return code parsing + */ +static int netampctl_transaction(AMP *amp, char *cmd, int len, char *buf) +{ + int ret; + + ret = write_block(&->state.ampport, cmd, len); + + if (ret != RIG_OK) + { + return ret; + } + + ret = read_string(&->state.ampport, buf, BUF_MAX, "\n", sizeof("\n")); + + if (ret < 0) + { + return ret; + } + + if (!memcmp(buf, NETAMPCTL_RET, strlen(NETAMPCTL_RET))) + { + return atoi(buf + strlen(NETAMPCTL_RET)); + } + + return ret; +} + +static int netampctl_open(AMP *amp) +{ + int ret, len; + //struct amp_state *rs = &->state; + int pamp_ver; + char cmd[CMD_MAX]; + char buf[BUF_MAX]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + + len = sprintf(cmd, "\\dump_state\n"); + + ret = netampctl_transaction(amp, cmd, len, buf); + + if (ret <= 0) + { + return (ret < 0) ? ret : -RIG_EPROTO; + } + + pamp_ver = atoi(buf); +#define AMPCTLD_PAMP_VER 0 + + if (pamp_ver < AMPCTLD_PAMP_VER) + { + return -RIG_EPROTO; + } + + ret = read_string(&->state.ampport, buf, BUF_MAX, "\n", sizeof("\n")); + + if (ret <= 0) + { + return (ret < 0) ? ret : -RIG_EPROTO; + } + + do + { + ret = read_string(&->state.ampport, buf, BUF_MAX, "\n", sizeof("\n")); + + if (ret <= 0) + { + return (ret < 0) ? ret : -RIG_EPROTO; + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s called, string=%s\n", __FUNCTION__, buf); + } + while (ret > 0); + + return RIG_OK; +} + +static int netampctl_close(AMP *amp) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + /* clean signoff, no read back */ + write_block(&->state.ampport, "q\n", 2); + + return RIG_OK; +} + +static int netampctl_set_freq(AMP *amp, freq_t freq) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + return -RIG_ENIMPL; +} + +static int netampctl_get_freq(AMP *amp, freq_t *freq) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + *freq = 12345; + return -RIG_ENIMPL; +} + +#if 0 +static int netampctl_reset(AMP *amp, amp_reset_t reset) +{ + int ret, len; + char cmd[CMD_MAX]; + char buf[BUF_MAX]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + len = sprintf(cmd, "R %d\n", reset); + + ret = netampctl_transaction(amp, cmd, len, buf); + + if (ret > 0) + { + return -RIG_EPROTO; + } + else + { + return ret; + } +} +#endif + +static const char *netampctl_get_info(AMP *amp) +{ + int ret, len; + char cmd[CMD_MAX]; + static char buf[BUF_MAX]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + len = sprintf(cmd, "_\n"); + + ret = netampctl_transaction(amp, cmd, len, buf); + + if (ret < 0) + { + return NULL; + } + + buf [ret] = '\0'; + + return buf; +} + + + +/* + * NET ampctl capabilities. + */ + +const struct amp_caps netampctl_caps = +{ + .amp_model = AMP_MODEL_NETAMPCTL, + .model_name = "NET ampctl", + .mfg_name = "Hamlib", + .version = "0.1", + .copyright = "LGPL", + .status = RIG_STATUS_ALPHA, + .amp_type = AMP_TYPE_OTHER, + .port_type = RIG_PORT_NETWORK, + .timeout = 2000, + .retry = 3, + + .priv = NULL, /* priv */ + + /* .amp_init = netampctl_init, */ + /* .amp_cleanup = netampctl_cleanup, */ + .amp_open = netampctl_open, + .amp_close = netampctl_close, + + .get_freq = netampctl_get_freq, + .set_freq = netampctl_set_freq, + + .get_info = netampctl_get_info +}; + diff --git a/include/hamlib/ampclass.h b/include/hamlib/ampclass.h new file mode 100644 index 000000000..a9c830950 --- /dev/null +++ b/include/hamlib/ampclass.h @@ -0,0 +1,65 @@ +/* + * Hamlib C++ bindings - rotator API header + * Copyright (c) 2002 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 + * + */ + +#ifndef _ROTCLASS_H +#define _ROTCLASS_H 1 + +#include + + + +class BACKEND_IMPEXP Rotator +{ +private: + ROT *theRot; // Global ref. to the rot + +protected: +public: + Rotator(rot_model_t rot_model); + + virtual ~Rotator(); + + const struct rot_caps *caps; + + // This method opens the communication port to the rot + void open(void); + + // This method closes the communication port to the rot + void close(void); + + void setConf(token_t token, const char *val); + void setConf(const char *name, const char *val); + void getConf(token_t token, char *val); + void getConf(const char *name, char *val); + token_t tokenLookup(const char *name); + + void setPosition(azimuth_t az, elevation_t el); + void getPosition(azimuth_t& az, elevation_t& el); + void stop(); + void park(); + void reset(rot_reset_t reset); + + void move(int direction, int speed); +}; + + + +#endif // _ROTCLASS_H diff --git a/include/hamlib/amplifier.h b/include/hamlib/amplifier.h new file mode 100644 index 000000000..5284e090d --- /dev/null +++ b/include/hamlib/amplifier.h @@ -0,0 +1,475 @@ +/* + * Hamlib Interface - Amplifier API header + * Copyright (c) 2000-2005 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 + * + */ + +#ifndef _AMPLIFIER_H +#define _AMPLIFIER_H 1 + +#include +#include + +/** + * \addtogroup amp + * @{ + */ + +/** + * \file amp.h + * \brief Hamlib amplifier data structures. + * + * This file contains the data structures and declarations for the Hamlib + * amplifier API. see the amplifier.c file for more details on the amplifier API. + */ + + + +__BEGIN_DECLS + +/* Forward struct references */ + +struct amp; +struct amp_state; + + +/** + * \typedef typedef struct amp AMP + * \brief Amplifier structure definition (see amp for details). + */ +typedef struct amp AMP; + + +/** + * \typedef typedef float swr_t + * \brief Type definition for SWR. + * + * The swr_t type is used as a parameter for the amp_get_swr() function. + * + * Unless specified otherwise, the unit of swr_t is 1.0 to max reported by tuner + */ +typedef float swr_t; + + +/** + * \typedef typedef float tune_value_t + * \brief Type definition for tuning values capacitance and resistance. + * + * The tune_value_t type is used as a parameter for the amp_get_level() + * + * Unless specified otherwise, the units of tune_value_t is pF and nH + */ +typedef int tune_value_t; + + +/** + * \brief Token in the netampctl protocol for returning error code + */ +#define NETAMPCTL_RET "RPRT " + + +typedef enum +{ + AMP_RESET_MEM, // erase tuner memory + AMP_RESET_FAULT, // reset any fault + AMP_RESET_AMP // for kpa1500 +} amp_reset_t; + +/** + * \brief Amplifier type flags + */ +typedef enum +{ + AMP_FLAG_1 = (1 << 1), /*!< TBD */ + AMP_FLAG_2 = (1 << 2) /*!< TBD */ +} amp_type_t; + +// TBD AMP_TYPE +#define AMP_TYPE_MASK (AMP_FLAG_1|AMP_FLAG_2) + +#define AMP_TYPE_OTHER 0 +#define AMP_TYPE_1 AMP_FLAG_1 +#define AMP_TYPE_2 AMP_FLAG_2 +#define AMP_TYPE_ALL (AMP_FLAG_1|AMP_FLAG_2) + + +enum amp_level_e +{ + AMP_LEVEL_NONE = 0, /*!< '' -- No Level */ + AMP_LEVEL_SWR = (1 << 0), /*!< \c SWR 1.0 or greater */ + AMP_LEVEL_NH = (1 << 1), /*!< \c Tune setting nanohenries */ + AMP_LEVEL_PF = (1 << 2), /*!< \c Tune setting picofarads */ + AMP_LEVEL_PWR_INPUT = (1 << 3), /*!< \c Power reading from amp */ + AMP_LEVEL_PWR_FWD = (1 << 4), /*!< \c Power reading forward */ + AMP_LEVEL_PWR_REFLECTED = (1 << 5), /*!< \c Power reading reverse */ + AMP_LEVEL_PWR_PEAK = (1 << 6), /*!< \c Power reading peak */ + AMP_LEVEL_FAULT = (1 << 7) /*!< \c Fault code */ +}; + +#define AMP_LEVEL_FLOAT_LIST (AMP_LEVEL_SWR) +#define AMP_LEVEL_STRING_LIST (AMP_LEVEL_FAULT) +#define AMP_LEVEL_IS_FLOAT(l) ((l)&_LEVEL_FLOAT_LIST) +#define AMP_LEVEL_IS_STRING(l) ((l)&_LEVEL_STRING_LIST) + +/** + * \def AMP_MOVE_UP + * \brief A macro that returns the flag for the \b UP direction. + * + * This macro defines the value of the \b UP direction which can be + * used with the amp_move() function. + * + * \sa amp_move(), AMP_MOVE_DOWN, AMP_MOVE_LEFT, AMP_MOVE_CCW, + * AMP_MOVE_RIGHT, AMP_MOVE_CW + */ +#define AMP_MOVE_UP (1<<1) + +/** + * \def AMP_MOVE_DOWN + * \brief A macro that returns the flag for the \b DOWN direction. + * + * This macro defines the value of the \b DOWN direction which can be + * used with the amp_move() function. + * + * \sa amp_move(), AMP_MOVE_UP, AMP_MOVE_LEFT, AMP_MOVE_CCW, AMP_MOVE_RIGHT, + * AMP_MOVE_CW +*/ +#define AMP_MOVE_DOWN (1<<2) + +/** + * \def AMP_MOVE_LEFT + * \brief A macro that returns the flag for the \b LEFT direction. + * + * This macro defines the value of the \b LEFT direction which can be + * used with the amp_move function. + * + * \sa amp_move(), AMP_MOVE_UP, AMP_MOVE_DOWN, AMP_MOVE_CCW, AMP_MOVE_RIGHT, + * AMP_MOVE_CW + */ +#define AMP_MOVE_LEFT (1<<3) + +/** + * \def AMP_MOVE_CCW + * \brief A macro that returns the flag for the \b counterclockwise direction. + * + * This macro defines the value of the \b counterclockwise direction which + * can be used with the amp_move() function. This value is equivalent to + * AMP_MOVE_LEFT . + * + * \sa amp_move(), AMP_MOVE_UP, AMP_MOVE_DOWN, AMP_MOVE_LEFT, AMP_MOVE_RIGHT, + * AMP_MOVE_CW + */ +#define AMP_MOVE_CCW AMP_MOVE_LEFT + +/** + * \def AMP_MOVE_RIGHT + * \brief A macro that returns the flag for the \b RIGHT direction. + * + * This macro defines the value of the \b RIGHT direction which can be used + * with the amp_move() function. + * + * \sa amp_move(), AMP_MOVE_UP, AMP_MOVE_DOWN, AMP_MOVE_LEFT, AMP_MOVE_CCW, + * AMP_MOVE_CW + */ +#define AMP_MOVE_RIGHT (1<<4) + +/** + * \def AMP_MOVE_CW + * \brief A macro that returns the flag for the \b clockwise direction. + * + * This macro defines the value of the \b clockwise direction wich can be + * used with the amp_move() function. This value is equivalent to + * AMP_MOVE_RIGHT . + * + * \sa amp_move(), AMP_MOVE_UP, AMP_MOVE_DOWN, AMP_MOVE_LEFT, AMP_MOVE_CCW, + * AMP_MOVE_RIGHT + */ +#define AMP_MOVE_CW AMP_MOVE_RIGHT + + +/* Basic amp type, can store some useful info about different amplifiers. Each + * lib must be able to populate this structure, so we can make useful + * enquiries about capablilities. + */ + +/** + * Amplifier Caps + * \struct amp_caps + * \brief Amplifier data structure. + * + * The main idea of this struct is that it will be defined by the backend + * amplifier driver, and will remain readonly for the application. Fields that + * need to be modifiable by the application are copied into the struct + * amp_state, which is a kind of private of the AMP instance. + * + * This way, you can have several rigs running within the same application, + * sharing the struct amp_caps of the backend, while keeping their own + * customized data. + * + * n.b.: Don't move fields around, as the backends depend on it when + * initializing their caps. + */ +struct amp_caps +{ + amp_model_t amp_model; /*!< Amplifier model. */ + const char *model_name; /*!< Model name. */ + const char *mfg_name; /*!< Manufacturer. */ + const char *version; /*!< Driver version. */ + const char *copyright; /*!< Copyright info. */ + enum rig_status_e status; /*!< Driver status. */ + + int amp_type; /*!< Amplifier type. */ + enum rig_port_e port_type; /*!< Type of communication port. */ + + int serial_rate_min; /*!< Minimal serial speed. */ + int serial_rate_max; /*!< Maximal serial speed. */ + int serial_data_bits; /*!< Number of data bits. */ + int serial_stop_bits; /*!< Number of stop bits. */ + enum serial_parity_e serial_parity; /*!< Parity. */ + enum serial_handshake_e serial_handshake; /*!< Handshake. */ + + int write_delay; /*!< Write delay. */ + int post_write_delay; /*!< Post-write delay. */ + int timeout; /*!< Timeout. */ + int retry; /*!< Number of retry if command fails. */ + + const struct confparams *cfgparams; /*!< Configuration parametres. */ + const rig_ptr_t priv; /*!< Private data. */ + + setting_t has_get_level; + setting_t has_set_level; + + gran_t level_gran[RIG_SETTING_MAX]; /*!< level granularity */ + gran_t parm_gran[RIG_SETTING_MAX]; /*!< level granularity */ + + /* + * Amp Admin API + * + */ + + int (*amp_init)(AMP *amp); + int (*amp_cleanup)(AMP *amp); + int (*amp_open)(AMP *amp); + int (*amp_close)(AMP *amp); + + int (*set_freq)(AMP *amp, freq_t val); + int (*get_freq)(AMP *amp, freq_t *val); + + int (*set_conf)(AMP *amp, token_t token, const char *val); + int (*get_conf)(AMP *amp, token_t token, char *val); + + /* + * General API commands, from most primitive to least.. :() + * List Set/Get functions pairs + */ + + int (*reset)(AMP *amp, amp_reset_t reset); + int (*get_level)(AMP *amp, setting_t level, value_t *val); + int (*get_ext_level)(AMP *amp, token_t level, value_t *val); + int (*set_powerstat)(AMP *amp, powerstat_t status); + int (*get_powerstat)(AMP *amp, powerstat_t *status); + + + /* get firmware info, etc. */ + const char *(*get_info)(AMP *amp); + + setting_t levels; + unsigned ext_levels; + const struct confparams *extlevels; + const struct confparams *extparms; +}; + + +/** + * Amplifier state + * \struct amp_state + * \brief Live data and customized fields. + * + * This struct contains live data, as well as a copy of capability fields + * that may be updated (ie. customized) + * + * It is fine to move fields around, as this kind of struct should + * not be initialized like caps are. + */ +struct amp_state +{ + /* + * overridable fields + */ + + /* + * non overridable fields, internal use + */ + hamlib_port_t ampport; /*!< Amplifier port (internal use). */ + + int comm_state; /*!< Comm port state, opened/closed. */ + rig_ptr_t priv; /*!< Pointer to private amplifier state data. */ + rig_ptr_t obj; /*!< Internal use by hamlib++ for event handling. */ + + setting_t has_get_level; + + gran_t level_gran[RIG_SETTING_MAX]; /*!< level granularity */ + gran_t parm_gran[RIG_SETTING_MAX]; /*!< level granularity */ +}; + + +/** + * Amplifier structure + * \struct amp + * \brief This is the master data structure, + * acting as a handle for the controlled amplifier. + * + * This is the master data structure, acting as a handle for the controlled + * amplifier. A pointer to this structure is returned by the amp_init() API + * function and is passed as a parameter to every amplifier specific API call. + * + * \sa amp_init(), amp_caps(), amp_state() + */ +struct amp +{ + struct amp_caps *caps; /*!< Amplifier caps. */ + struct amp_state state; /*!< Amplifier state. */ +}; + + +/* --------------- API function prototypes -----------------*/ + +extern HAMLIB_EXPORT(AMP *) +amp_init HAMLIB_PARAMS((amp_model_t amp_model)); + +extern HAMLIB_EXPORT(int) +amp_open HAMLIB_PARAMS((AMP *amp)); + +extern HAMLIB_EXPORT(int) +amp_close HAMLIB_PARAMS((AMP *amp)); + +extern HAMLIB_EXPORT(int) +amp_cleanup HAMLIB_PARAMS((AMP *amp)); + +extern HAMLIB_EXPORT(int) +amp_set_conf HAMLIB_PARAMS((AMP *amp, + token_t token, + const char *val)); +extern HAMLIB_EXPORT(int) +amp_get_conf HAMLIB_PARAMS((AMP *amp, + token_t token, + char *val)); +extern HAMLIB_EXPORT(int) +amp_set_powerstat HAMLIB_PARAMS((AMP *amp, + powerstat_t status)); +extern HAMLIB_EXPORT(int) +amp_get_powerstat HAMLIB_PARAMS((AMP *amp, + powerstat_t *status)); + + +/* + * General API commands, from most primitive to least.. ) + * List Set/Get functions pairs + */ +extern HAMLIB_EXPORT(int) +amp_get_freq HAMLIB_PARAMS((AMP *amp, + freq_t *freq)); +extern HAMLIB_EXPORT(int) +amp_set_freq HAMLIB_PARAMS((AMP *amp, + freq_t freq)); + +extern HAMLIB_EXPORT(int) +amp_reset HAMLIB_PARAMS((AMP *amp, + amp_reset_t reset)); + +extern HAMLIB_EXPORT(const char *) +amp_get_info HAMLIB_PARAMS((AMP *amp)); + +extern HAMLIB_EXPORT(int) +amp_get_level HAMLIB_PARAMS((AMP *amp, setting_t level, value_t *val)); + +extern HAMLIB_EXPORT(int) +amp_register HAMLIB_PARAMS((const struct amp_caps *caps)); + +extern HAMLIB_EXPORT(int) +amp_unregister HAMLIB_PARAMS((amp_model_t amp_model)); + +extern HAMLIB_EXPORT(int) +amp_list_foreach HAMLIB_PARAMS((int (*cfunc)(const struct amp_caps *, + rig_ptr_t), + rig_ptr_t data)); + +extern HAMLIB_EXPORT(int) +amp_load_backend HAMLIB_PARAMS((const char *be_name)); + +extern HAMLIB_EXPORT(int) +amp_check_backend HAMLIB_PARAMS((amp_model_t amp_model)); + +extern HAMLIB_EXPORT(int) +amp_load_all_backends HAMLIB_PARAMS((void)); + +extern HAMLIB_EXPORT(amp_model_t) +amp_probe_all HAMLIB_PARAMS((hamlib_port_t *p)); + +extern HAMLIB_EXPORT(int) +amp_token_foreach HAMLIB_PARAMS((AMP *amp, + int (*cfunc)(const struct confparams *, + rig_ptr_t), + rig_ptr_t data)); + +extern HAMLIB_EXPORT(const struct confparams *) +amp_confparam_lookup HAMLIB_PARAMS((AMP *amp, + const char *name)); + +extern HAMLIB_EXPORT(token_t) +amp_token_lookup HAMLIB_PARAMS((AMP *amp, + const char *name)); + +extern HAMLIB_EXPORT(const struct amp_caps *) +amp_get_caps HAMLIB_PARAMS((amp_model_t amp_model)); + +extern HAMLIB_EXPORT(setting_t) +amp_has_get_level HAMLIB_PARAMS((AMP *amp, + setting_t level)); + +extern HAMLIB_EXPORT(const struct confparams *) +amp_ext_lookup HAMLIB_PARAMS((AMP *amp, + const char *name)); + +extern HAMLIB_EXPORT(int) +amp_get_ext_level HAMLIB_PARAMS((AMP *amp, + token_t token, + value_t *val)); + +extern HAMLIB_EXPORT(const char *) amp_strlevel(setting_t); + +extern HAMLIB_EXPORT(const struct confparams *) +rig_ext_lookup HAMLIB_PARAMS((RIG *rig, + const char *name)); + + +/** + * \def amp_debug + * \brief Convenience definition for debug level. + * + * This is just as convenience definition of the amplifier debug level, + * and is the same as for the rig debug level. + * + * \sa rig_debug() + */ +#define amp_debug rig_debug + +__END_DECLS + +#endif /* _AMPLIFIER_H */ + +/** @} */ diff --git a/include/hamlib/amplist.h b/include/hamlib/amplist.h new file mode 100644 index 000000000..fbeea553f --- /dev/null +++ b/include/hamlib/amplist.h @@ -0,0 +1,104 @@ +/* + * Hamlib Interface - list of known amplifiers + * Copyright (c) 2000-2011 by Stephane Fillod + * Copyright (c) 2000-2002 by Frank Singleton + * Copyright (C) 2019 by Michael Black W9MDB. Derived from rotlist.h + * + * + * 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 + * + */ + +#ifndef _AMPLIST_H +#define _AMPLIST_H 1 + +#define AMP_MAKE_MODEL(a,b) ((a)*100+(b)) +#define AMP_BACKEND_NUM(a) ((a)/100) + + +/** + * \addtogroup amplifier + * @{ + */ + +/** + * \file rotlist.h + * \brief Hamlib amplifier model definitions. + * + * This file contains amplifier model definitions for the Hamlib amplifier API. + * Each distinct amplifier type has a unique model number (ID) and is used by + * hamlib to identify and distinguish between the different hardware drivers. + * The exact model numbers can be acquired using the macros in this file. To + * obtain a list of supported amplifier branches, one can use the statically + * defined AMP_BACKEND_LIST macro. To obtain a full list of supported + * amplifier (including each model in every branch), the foreach_opened_rot() + * API function can be used. + * + * The model number, or ID, is used to tell Hamlib which amplifier the client + * whishes to use. It is done with the amp_init() API call. + */ + + +#define AMP_MODEL_NONE 0 + + +/** + * \def AMP_MODEL_DUMMY + * \brief A macro that returns the model number for the dummy backend. + * + * The dummy backend, as the name suggests, is a backend which performs + * no hardware operations and always behaves as one would expect. It can + * be thought of as a hardware simulator and is very usefull for testing + * client applications. + */ +/** + * \def AMP_MODEL_NETAMPCTL + * \brief A macro that returns the model number for the Network backend. + * + * This backend allows use of the rotctld daemon through the normal + * Hamlib API. + */ +#define AMP_DUMMY 0 +#define AMP_BACKEND_DUMMY "dummy" +#define AMP_MODEL_DUMMY AMP_MAKE_MODEL(AMP_DUMMY, 1) +#define AMP_MODEL_NETAMPCTL AMP_MAKE_MODEL(AMP_DUMMY, 2) + + +/* + * Elecraft + */ + +/** + * \def AMP_MODEL_ELECRAFT1 + * \brief A macro that returns the model number of the EasyComm 1 backend. + * + * The Elecraft 1 backend can be used with amplifiers that support the + * KPA1500 protocol. + */ +#define AMP_ELECRAFT 2 +#define AMP_BACKEND_ELECRAFT "elecraft" +#define AMP_MODEL_ELECRAFT_KPA1500 AMP_MAKE_MODEL(AMP_ELECRAFT, 1) +//#define AMP_MODEL_ELECRAFT_KPA500 AMP_MAKE_MODEL(AMP_ELECRAFT, 2) + +/** + * \typedef typedef int amp_model_t + * \brief Convenience type definition for amplifier model. +*/ +typedef int amp_model_t; + + +#endif /* _AMPLIST_H */ + +/** @} */ diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index 44c2fa6c9..4be9d0819 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -69,9 +69,11 @@ || defined(__cplusplus) # define HAMLIB_PARAMS(protos) protos # define rig_ptr_t void * +# define amp_ptr_t void * #else # define HAMLIB_PARAMS(protos) () # define rig_ptr_t char * +# define amp_ptr_t char * #endif #include @@ -482,7 +484,9 @@ typedef enum { typedef enum { RIG_POWER_OFF = 0, /*!< Power off */ RIG_POWER_ON = (1 << 0), /*!< Power on */ - RIG_POWER_STANDBY = (1 << 1) /*!< Standby */ + RIG_POWER_STANDBY = (1 << 1), /*!< Standby */ + RIG_POWER_OPERATE = (1 << 2), /*!< Operate (from Standby) */ + RIG_POWER_UNKNOWN = (1 << 3) /*!< Unknown power status */ } powerstat_t; @@ -728,6 +732,7 @@ enum rig_level_e { /* RIG_LEVEL_BWC = (1<<31) */ /*!< Bandwidth Control, arg int (Hz) */ }; + #define RIG_LEVEL_FLOAT_LIST (RIG_LEVEL_AF|RIG_LEVEL_RF|RIG_LEVEL_SQL|RIG_LEVEL_APF|RIG_LEVEL_NR|RIG_LEVEL_PBT_IN|RIG_LEVEL_PBT_OUT|RIG_LEVEL_RFPOWER|RIG_LEVEL_MICGAIN|RIG_LEVEL_COMP|RIG_LEVEL_BALANCE|RIG_LEVEL_SWR|RIG_LEVEL_ALC|RIG_LEVEL_VOXGAIN|RIG_LEVEL_ANTIVOX) #define RIG_LEVEL_READONLY_LIST (RIG_LEVEL_SQLSTAT|RIG_LEVEL_SWR|RIG_LEVEL_ALC|RIG_LEVEL_STRENGTH|RIG_LEVEL_RAWSTR) @@ -761,9 +766,6 @@ enum rig_parm_e { #define RIG_PARM_IS_FLOAT(l) ((l)&RIG_PARM_FLOAT_LIST) #define RIG_PARM_SET(l) ((l)&~RIG_PARM_READONLY_LIST) - -#define RIG_SETTING_MAX 64 - /** * \brief Setting * @@ -772,6 +774,9 @@ enum rig_parm_e { */ typedef uint64_t setting_t; + +#define RIG_SETTING_MAX 64 + /** * \brief Tranceive mode * @@ -2343,6 +2348,7 @@ extern HAMLIB_EXPORT(rmode_t) rig_parse_mode(const char *s); extern HAMLIB_EXPORT(vfo_t) rig_parse_vfo(const char *s); extern HAMLIB_EXPORT(setting_t) rig_parse_func(const char *s); extern HAMLIB_EXPORT(setting_t) rig_parse_level(const char *s); +extern HAMLIB_EXPORT(setting_t) amp_parse_level(const char *s); extern HAMLIB_EXPORT(setting_t) rig_parse_parm(const char *s); extern HAMLIB_EXPORT(vfo_op_t) rig_parse_vfo_op(const char *s); extern HAMLIB_EXPORT(scan_t) rig_parse_scan(const char *s); diff --git a/src/Makefile.am b/src/Makefile.am index d8ace1c1b..3bef03e46 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,15 +4,16 @@ RIGSRC = rig.c serial.c serial.h misc.c misc.h register.c register.h event.c \ event.h cal.c cal.h conf.c tones.c tones.h rotator.c locator.c rot_reg.c \ rot_conf.c rot_conf.h iofunc.c iofunc.h ext.c mem.c settings.c \ parallel.c parallel.h usb_port.c usb_port.h debug.c network.c network.h \ - cm108.c cm108.h gpio.c gpio.h idx_builtin.h token.h par_nt.h microham.c microham.h + cm108.c cm108.h gpio.c gpio.h idx_builtin.h token.h par_nt.h microham.c microham.h \ + amplifier.c amp_reg.c amp_conf.c amp_conf.h extamp.c lib_LTLIBRARIES = libhamlib.la libhamlib_la_SOURCES = $(RIGSRC) libhamlib_la_LDFLAGS = $(WINLDFLAGS) $(OSXLDFLAGS) -no-undefined -version-info $(ABI_VERSION):$(ABI_REVISION):$(ABI_AGE) libhamlib_la_LIBADD = $(top_builddir)/lib/libmisc.la \ - $(BACKENDEPS) $(ROT_BACKENDEPS) $(NET_LIBS) $(MATH_LIBS) $(LIBUSB_LIBS) + $(BACKENDEPS) $(ROT_BACKENDEPS) $(AMP_BACKENDEPS) $(NET_LIBS) $(MATH_LIBS) $(LIBUSB_LIBS) -libhamlib_la_DEPENDENCIES = $(top_builddir)/lib/libmisc.la $(BACKENDEPS) $(ROT_BACKENDEPS) +libhamlib_la_DEPENDENCIES = $(top_builddir)/lib/libmisc.la $(BACKENDEPS) $(ROT_BACKENDEPS) $(AMP_BACKENDEPS) EXTRA_DIST = Android.mk diff --git a/src/amp_conf.c b/src/amp_conf.c new file mode 100644 index 000000000..875a86433 --- /dev/null +++ b/src/amp_conf.c @@ -0,0 +1,695 @@ +/* + * Hamlib Interface - amplifier 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 + * + */ + +/** + * \addtogroup amplifier + * @{ + */ + +/** + * \brief Amplifier Configuration Interface + * \file amp_conf.c + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include /* Standard input/output definitions */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ + +#include + +#include "amp_conf.h" +#include "token.h" + + +/* + * Configuration options available in the amp->state struct. + */ +static const struct confparams ampfrontend_cfg_params[] = +{ + { + TOK_PATHNAME, "amp_pathname", "Rig path name", + "Path name to the device file of the amplifier", + "/dev/amplifier", 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_MIN_AZ, "min_az", "Minimum azimuth", + "Minimum amplifier azimuth in degrees", + "-180", RIG_CONF_NUMERIC, { .n = { -360, 360, .001 } } + }, + { + TOK_MAX_AZ, "max_az", "Maximum azimuth", + "Maximum amplifier azimuth in degrees", + "180", RIG_CONF_NUMERIC, { .n = { -360, 360, .001 } } + }, + { + TOK_MIN_EL, "min_el", "Minimum elevation", + "Minimum amplifier elevation in degrees", + "0", RIG_CONF_NUMERIC, { .n = { -90, 180, .001 } } + }, + { + TOK_MAX_EL, "max_el", "Maximum elevation", + "Maximum amplifier elevation in degrees", + "90", RIG_CONF_NUMERIC, { .n = { -90, 180, .001 } } + }, + + { RIG_CONF_END, NULL, } +}; + + +static const struct confparams ampfrontend_serial_cfg_params[] = +{ + { + TOK_SERIAL_SPEED, "serial_speed", "Serial speed", + "Serial port baud rate", + "0", RIG_CONF_NUMERIC, { .n = { 300, 115200, 1 } } + }, + { + TOK_DATA_BITS, "data_bits", "Serial data bits", + "Serial port data bits", + "8", RIG_CONF_NUMERIC, { .n = { 5, 8, 1 } } + }, + { + TOK_STOP_BITS, "stop_bits", "Serial stop bits", + "Serial port stop bits", + "1", RIG_CONF_NUMERIC, { .n = { 0, 3, 1 } } + }, + { + TOK_PARITY, "serial_parity", "Serial parity", + "Serial port parity", + "None", RIG_CONF_COMBO, { .c = {{ "None", "Odd", "Even", "Mark", "Space", NULL }} } + }, + { + TOK_HANDSHAKE, "serial_handshake", "Serial handshake", + "Serial port handshake", + "None", RIG_CONF_COMBO, { .c = {{ "None", "XONXOFF", "Hardware", NULL }} } + }, + + { RIG_CONF_END, NULL, } +}; + + +/** + * \brief Set amplifier state info from alpha input + * \param amp + * \param token TOK_... specifying which info to set + * \param val input + * \return RIG_OK or < 0 error + * + * assumes amp!=NULL, val!=NULL + */ +int frontamp_set_conf(AMP *amp, token_t token, const char *val) +{ + struct amp_state *rs; + int val_i; + + rs = &->state; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + switch (token) + { + case TOK_PATHNAME: + strncpy(rs->ampport.pathname, val, FILPATHLEN - 1); + break; + + case TOK_WRITE_DELAY: + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.write_delay = val_i; + break; + + case TOK_POST_WRITE_DELAY: + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.post_write_delay = val_i; + break; + + case TOK_TIMEOUT: + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.timeout = val_i; + break; + + case TOK_RETRY: + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.retry = val_i; + break; + + case TOK_SERIAL_SPEED: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.parm.serial.rate = val_i; + break; + + case TOK_DATA_BITS: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.parm.serial.data_bits = val_i; + break; + + case TOK_STOP_BITS: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + if (1 != sscanf(val, "%d", &val_i)) + { + return -RIG_EINVAL; + } + + rs->ampport.parm.serial.stop_bits = val_i; + break; + + case TOK_PARITY: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + if (!strcmp(val, "None")) + { + rs->ampport.parm.serial.parity = RIG_PARITY_NONE; + } + else if (!strcmp(val, "Odd")) + { + rs->ampport.parm.serial.parity = RIG_PARITY_ODD; + } + else if (!strcmp(val, "Even")) + { + rs->ampport.parm.serial.parity = RIG_PARITY_EVEN; + } + else if (!strcmp(val, "Mark")) + { + rs->ampport.parm.serial.parity = RIG_PARITY_MARK; + } + else if (!strcmp(val, "Space")) + { + rs->ampport.parm.serial.parity = RIG_PARITY_SPACE; + } + else + { + return -RIG_EINVAL; + } + + break; + + case TOK_HANDSHAKE: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + if (!strcmp(val, "None")) + { + rs->ampport.parm.serial.handshake = RIG_HANDSHAKE_NONE; + } + else if (!strcmp(val, "XONXOFF")) + { + rs->ampport.parm.serial.handshake = RIG_HANDSHAKE_XONXOFF; + } + else if (!strcmp(val, "Hardware")) + { + rs->ampport.parm.serial.handshake = RIG_HANDSHAKE_HARDWARE; + } + else + { + return -RIG_EINVAL; + } + + break; + +#if 0 + + 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; +#endif + + default: + return -RIG_EINVAL; + } + + return RIG_OK; +} + + +/** + * \brief Get data from amplifier state in alpha form + * \param amp non-null + * \param token TOK_... specifying which data to get + * \param val result non-null + * \return RIG_OK or < 0 if error + */ +int frontamp_get_conf(AMP *amp, token_t token, char *val) +{ + struct amp_state *rs; + const char *s; + + rs = &->state; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + switch (token) + { + case TOK_PATHNAME: + strcpy(val, rs->ampport.pathname); + break; + + case TOK_WRITE_DELAY: + sprintf(val, "%d", rs->ampport.write_delay); + break; + + case TOK_POST_WRITE_DELAY: + sprintf(val, "%d", rs->ampport.post_write_delay); + break; + + case TOK_TIMEOUT: + sprintf(val, "%d", rs->ampport.timeout); + break; + + case TOK_RETRY: + sprintf(val, "%d", rs->ampport.retry); + break; + + case TOK_SERIAL_SPEED: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + sprintf(val, "%d", rs->ampport.parm.serial.rate); + break; + + case TOK_DATA_BITS: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + sprintf(val, "%d", rs->ampport.parm.serial.data_bits); + break; + + case TOK_STOP_BITS: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + sprintf(val, "%d", rs->ampport.parm.serial.stop_bits); + break; + + case TOK_PARITY: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + switch (rs->ampport.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; + } + + strcpy(val, s); + break; + + case TOK_HANDSHAKE: + if (rs->ampport.type.rig != RIG_PORT_SERIAL) + { + return -RIG_EINVAL; + } + + switch (rs->ampport.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; + +#if 0 + + case TOK_MIN_AZ: + sprintf(val, "%f", rs->min_az); + break; + + case TOK_MAX_AZ: + sprintf(val, "%f", rs->max_az); + break; + + case TOK_MIN_EL: + sprintf(val, "%f", rs->min_el); + break; + + case TOK_MAX_EL: + sprintf(val, "%f", rs->max_el); + break; +#endif + + default: + return -RIG_EINVAL; + } + + return RIG_OK; +} + + +/** + * \brief Executes cfunc on all the elements stored in the conf table + * \param amp non-null + * \param cfunc function(..) + * \param data + * + * start first with backend conf table, then finish with frontend table + */ +int HAMLIB_API amp_token_foreach(AMP *amp, + int (*cfunc)(const struct confparams *, + rig_ptr_t), + rig_ptr_t data) +{ + const struct confparams *cfp; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps || !cfunc) + { + return -RIG_EINVAL; + } + + for (cfp = ampfrontend_cfg_params; cfp->name; cfp++) + { + if ((*cfunc)(cfp, data) == 0) + { + return RIG_OK; + } + } + + if (amp->caps->port_type == RIG_PORT_SERIAL) + { + for (cfp = ampfrontend_serial_cfg_params; cfp->name; cfp++) + { + if ((*cfunc)(cfp, data) == 0) + { + return RIG_OK; + } + } + } + + for (cfp = amp->caps->cfgparams; cfp && cfp->name; cfp++) + { + if ((*cfunc)(cfp, data) == 0) + { + return RIG_OK; + } + } + + return RIG_OK; +} + + +/** + * \brief lookup conf token by its name, return pointer to confparams struct. + * \param amp + * \param name + * \return confparams or NULL + * + * lookup backend config table first, then fall back to frontend. + * TODO: should use Lex to speed it up, strcmp hurts! + */ +const struct confparams *HAMLIB_API amp_confparam_lookup(AMP *amp, + const char *name) +{ + const struct confparams *cfp; + token_t token; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return NULL; + } + + /* 0 returned for invalid format */ + token = strtol(name, NULL, 0); + + for (cfp = amp->caps->cfgparams; cfp && cfp->name; cfp++) + { + if (!strcmp(cfp->name, name) || token == cfp->token) + { + return cfp; + } + } + + for (cfp = ampfrontend_cfg_params; cfp->name; cfp++) + { + if (!strcmp(cfp->name, name) || token == cfp->token) + { + return cfp; + } + } + + if (amp->caps->port_type == RIG_PORT_SERIAL) + { + for (cfp = ampfrontend_serial_cfg_params; cfp->name; cfp++) + { + if (!strcmp(cfp->name, name) || token == cfp->token) + { + return cfp; + } + } + } + + return NULL; +} + + +/** + * \brief Simple lookup returning token id associated with name + * \param amp + * \param name + * \return token enum + */ +token_t HAMLIB_API amp_token_lookup(AMP *amp, const char *name) +{ + const struct confparams *cfp; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + cfp = amp_confparam_lookup(amp, name); + + if (!cfp) + { + return RIG_CONF_END; + } + + return cfp->token; +} + + +/** + * \brief set a amplifier configuration parameter + * \param amp The amp handle + * \param token The parameter + * \param val The value to set the parameter to + * + * Sets a configuration parameter. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa amp_get_conf() + */ +int HAMLIB_API amp_set_conf(AMP *amp, token_t token, const char *val) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return -RIG_EINVAL; + } + + if (rig_need_debug(RIG_DEBUG_VERBOSE)) + { + const struct confparams *cfp; + char tokenstr[12]; + sprintf(tokenstr, "%ld", token); + cfp = amp_confparam_lookup(amp, tokenstr); + + if (!cfp) + { + return -RIG_EINVAL; + } + + amp_debug(RIG_DEBUG_VERBOSE, "%s: %s='%s'\n", __func__, cfp->name, val); + } + + if (IS_TOKEN_FRONTEND(token)) + { + return frontamp_set_conf(amp, token, val); + } + + if (amp->caps->set_conf == NULL) + { + return -RIG_ENAVAIL; + } + + return amp->caps->set_conf(amp, token, val); +} + + +/** + * \brief get the value of a configuration parameter + * \param amp The amp handle + * \param token The parameter + * \param val The location where to store the value of config \a token + * + * Retrieves the value of a configuration paramter associated with \a token. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa amp_set_conf() + */ +int HAMLIB_API amp_get_conf(AMP *amp, token_t token, char *val) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps || !val) + { + return -RIG_EINVAL; + } + + if (IS_TOKEN_FRONTEND(token)) + { + return frontamp_get_conf(amp, token, val); + } + + if (amp->caps->get_conf == NULL) + { + return -RIG_ENAVAIL; + } + + return amp->caps->get_conf(amp, token, val); +} + +/** @} */ diff --git a/src/amp_conf.h b/src/amp_conf.h new file mode 100644 index 000000000..31a4b3aff --- /dev/null +++ b/src/amp_conf.h @@ -0,0 +1,31 @@ +/* + * Hamlib Interface - configuration header + * Copyright (c) 2000,2001,2002 by Stephane Fillod and Frank Singleton + * + * + * 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 + * + */ + +#ifndef _AMP_CONF_H +#define _AMP_CONF_H 1 + +#include + +int frontamp_set_conf(AMP *amp, token_t token, const char *val); +int frontamp_get_conf(AMP *amp, token_t token, char *val); + + +#endif /* _AMP_CONF_H */ diff --git a/src/amp_reg.c b/src/amp_reg.c new file mode 100644 index 000000000..8a777f6f6 --- /dev/null +++ b/src/amp_reg.c @@ -0,0 +1,381 @@ +/* + * Hamlib Interface - provides registering for dynamically loadable backends. + * Copyright (c) 2000-2004 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 + * + */ + +/** + * \brief Dynamic registration of amplifier backends + * \file amp_reg.c + * + * Similar to register.c + * doc todo: Let's explain what's going on here! + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "register.h" + +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +#define AMP_BACKEND_MAX 32 + +#define DEFINE_INITAMP_BACKEND(backend) \ + int MAKE_VERSIONED_FN(PREFIX_INITAMPS, \ + ABI_VERSION, \ + backend(void *be_handle)); \ + rig_model_t MAKE_VERSIONED_FN(PREFIX_PROBEAMPS, \ + ABI_VERSION, \ + backend(hamlib_port_t *port, \ + rig_probe_func_t cfunc, \ + rig_ptr_t data)) + +#define AMP_FUNCNAMA(backend) MAKE_VERSIONED_FN(PREFIX_INITAMPS, ABI_VERSION, backend) +#define AMP_FUNCNAMB(backend) MAKE_VERSIONED_FN(PREFIX_PROBEAMPS, ABI_VERSION, backend) + +#define AMP_FUNCNAM(backend) AMP_FUNCNAMA(backend),AMP_FUNCNAMB(backend) + + +DEFINE_INITAMP_BACKEND(dummy); +DEFINE_INITAMP_BACKEND(kpa1500); + +/** + * \def AMP_BACKEND_LIST + * \brief Static list of amplifier models. + * + * This is a NULL terminated list of available amplifier backends. Each entry + * in the list consists of two fields: The branch number, which is an integer, + * and the branch name, which is a character string. + * An external library, loaded dynamically, could add its own functions + * pointers in this array. + */ +static struct +{ + int be_num; + const char *be_name; + int (*be_init)(void *); + amp_model_t (*be_probe)(hamlib_port_t *); +} amp_backend_list[AMP_BACKEND_MAX] = +{ + { AMP_DUMMY, AMP_BACKEND_DUMMY, AMP_FUNCNAMA(dummy) }, + { AMP_ELECRAFT, AMP_BACKEND_ELECRAFT, AMP_FUNCNAMA(kpa1500) }, + { 0, NULL }, /* end */ +}; + + +// Apparently, no amplifier can be probed. + +/* + * AMP_BACKEND_LIST is here, please keep it up to date, + * i.e. each time you implement a new backend. + */ + + +/* + * This struct to keep track of known amp models. + * It is chained, and used in a hash table, see below. + */ +struct amp_list +{ + const struct amp_caps *caps; + struct amp_list *next; +}; + + +#define AMPLSTHASHSZ 16 +#define HASH_FUNC(a) ((a)%AMPLSTHASHSZ) + + +/* + * The amp_hash_table is a hash table pointing to a list of next==NULL + * terminated caps. + */ +static struct amp_list *amp_hash_table[AMPLSTHASHSZ] = { NULL, }; + + +static int amp_lookup_backend(amp_model_t amp_model); + + +/* + * Basically, this is a hash insert function that doesn't check for dup! + */ +int HAMLIB_API amp_register(const struct amp_caps *caps) +{ + int hval; + struct amp_list *p; + + if (!caps) + { + return -RIG_EINVAL; + } + + amp_debug(RIG_DEBUG_VERBOSE, "amp_register (%d)\n", caps->amp_model); + +#ifndef DONT_WANT_DUP_CHECK + + if (amp_get_caps(caps->amp_model) != NULL) + { + return -RIG_EINVAL; + } + +#endif + + p = (struct amp_list *)malloc(sizeof(struct amp_list)); + + if (!p) + { + return -RIG_ENOMEM; + } + + hval = HASH_FUNC(caps->amp_model); + p->caps = caps; + // p->handle = NULL; + p->next = amp_hash_table[hval]; + amp_hash_table[hval] = p; + + return RIG_OK; +} + + +/* + * Get amp capabilities. + * i.e. amp_hash_table lookup + */ +const struct amp_caps *HAMLIB_API amp_get_caps(amp_model_t amp_model) +{ + struct amp_list *p; + + for (p = amp_hash_table[HASH_FUNC(amp_model)]; p; p = p->next) + { + if (p->caps->amp_model == amp_model) + { + return p->caps; + } + } + + return NULL; /* sorry, caps not registered! */ +} + + +/* + * lookup for backend index in amp_backend_list table, + * according to BACKEND_NUM + * return -1 if not found. + */ +static int amp_lookup_backend(amp_model_t amp_model) +{ + int i; + + for (i = 0; i < AMP_BACKEND_MAX && amp_backend_list[i].be_name; i++) + { + if (AMP_BACKEND_NUM(amp_model) == + amp_backend_list[i].be_num) + { + return i; + } + } + + return -1; +} + + +/* + * amp_check_backend + * check the backend declaring this model has been loaded + * and if not loaded already, load it! + * This permits seamless operation in amp_init. + */ +int HAMLIB_API amp_check_backend(amp_model_t amp_model) +{ + const struct amp_caps *caps; + int be_idx; + int retval; + + /* already loaded ? */ + caps = amp_get_caps(amp_model); + + if (caps) + { + return RIG_OK; + } + + be_idx = amp_lookup_backend(amp_model); + + /* + * Never heard about this backend family! + */ + if (be_idx == -1) + { + amp_debug(RIG_DEBUG_VERBOSE, + "%s: unsupported backend %d for model %d\n", + __func__, + AMP_BACKEND_NUM(amp_model), + amp_model); + + return -RIG_ENAVAIL; + } + + retval = amp_load_backend(amp_backend_list[be_idx].be_name); + + return retval; +} + + +int HAMLIB_API amp_unregister(amp_model_t amp_model) +{ + int hval; + struct amp_list *p, *q; + + hval = HASH_FUNC(amp_model); + q = NULL; + + for (p = amp_hash_table[hval]; p; p = p->next) + { + if (p->caps->amp_model == amp_model) + { + if (q == NULL) + { + amp_hash_table[hval] = p->next; + } + else + { + q->next = p->next; + } + + free(p); + return RIG_OK; + } + + q = p; + } + + return -RIG_EINVAL; /* sorry, caps not registered! */ +} + + +/* + * amp_list_foreach + * executes cfunc on all the elements stored in the amp hash list + */ +int HAMLIB_API amp_list_foreach(int (*cfunc)(const struct amp_caps *, + rig_ptr_t), + rig_ptr_t data) +{ + struct amp_list *p; + int i; + + if (!cfunc) + { + return -RIG_EINVAL; + } + + for (i = 0; i < AMPLSTHASHSZ; i++) + { + for (p = amp_hash_table[i]; p; p = p->next) + if ((*cfunc)(p->caps, data) == 0) + { + return RIG_OK; + } + } + + return RIG_OK; +} + + +/* + * amp_probe_all + * called straight by amp_probe + */ +amp_model_t HAMLIB_API amp_probe_all(hamlib_port_t *p) +{ + int i; + amp_model_t amp_model; + + for (i = 0; i < AMP_BACKEND_MAX && amp_backend_list[i].be_name; i++) + { + if (amp_backend_list[i].be_probe) + { + amp_model = (*amp_backend_list[i].be_probe)(p); + + if (amp_model != AMP_MODEL_NONE) + { + return amp_model; + } + } + } + + return AMP_MODEL_NONE; +} + + +int amp_load_all_backends() +{ + int i; + + for (i = 0; i < AMP_BACKEND_MAX && amp_backend_list[i].be_name; i++) + { + amp_load_backend(amp_backend_list[i].be_name); + } + + return RIG_OK; +} + + +/* + * amp_load_backend + * Dynamically load a amp backend through dlopen mechanism + */ +int HAMLIB_API amp_load_backend(const char *be_name) +{ + int status; + int (*be_init)(rig_ptr_t); + int i; + + for (i = 0; i < AMP_BACKEND_MAX && amp_backend_list[i].be_name; i++) + { + if (!strcmp(be_name, amp_backend_list[i].be_name)) + { + be_init = amp_backend_list[i].be_init; + + if (be_init == NULL) + { + printf("Null\n"); + return -EINVAL; + } + + status = (*be_init)(NULL); + return status; + } + } + + return -EINVAL; + +} diff --git a/src/amplifier.c b/src/amplifier.c new file mode 100644 index 000000000..6338049c8 --- /dev/null +++ b/src/amplifier.c @@ -0,0 +1,708 @@ +/* + * Hamlib Interface - main file + * Copyright (c) 2000-2012 by Stephane Fillod + * Copyright (c) 2000-2003 by Frank Singleton + * + * + * 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 + * + */ + +/** + * \addtogroup amplifier + * @{ + */ + +/** + * \file src/amplifier.c + * \brief Amplifier interface + * \author Stephane Fillod + * \date 2000-2012 + * + * Hamlib interface is a frontend implementing amplifier wrapper functions. + */ + + +/** + * \page amp Amplifier interface + * + * Amplifier can be any kind of azimuth or azimuth and elevation controlled + * antenna system. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "serial.h" +#include "parallel.h" +#include "usb_port.h" +#include "network.h" +#include "amp_conf.h" +#include "token.h" + +#define CHECK_AMP_ARG(r) (!(r) || !(r)->caps || !(r)->state.comm_state) + +/* + * Data structure to track the opened amp (by amp_open) + */ +struct opened_amp_l +{ + AMP *amp; + struct opened_amp_l *next; +}; +static struct opened_amp_l *opened_amp_list = { NULL }; + + +/* + * track which amp is opened (with amp_open) + * needed at least for transceive mode + */ +static int add_opened_amp(AMP *amp) +{ + struct opened_amp_l *p; + p = (struct opened_amp_l *)malloc(sizeof(struct opened_amp_l)); + + if (!p) + { + return -RIG_ENOMEM; + } + + p->amp = amp; + p->next = opened_amp_list; + opened_amp_list = p; + return RIG_OK; +} + + +static int remove_opened_amp(AMP *amp) +{ + struct opened_amp_l *p, *q; + q = NULL; + + for (p = opened_amp_list; p; p = p->next) + { + if (p->amp == amp) + { + if (q == NULL) + { + opened_amp_list = opened_amp_list->next; + } + else + { + q->next = p->next; + } + + free(p); + return RIG_OK; + } + + q = p; + } + + return -RIG_EINVAL; /* Not found in list ! */ +} + + +/** + * \brief execs cfunc() on each opened amp + * \param cfunc The function to be executed on each amp + * \param data Data pointer to be passed to cfunc() + * + * Calls cfunc() function for each opened amp. The contents of the opened + * amp table is processed in random order according to a function pointed to + * by \a cfunc, whic is called with two arguments, the first pointing to the + * #AMP handle, the second to a data pointer \a data. + * + * If \a data is not needed, then it can be set to NULL. The processing of + * the opened amp table is stopped when cfunc() returns 0. + * \internal + * + * \return always RIG_OK. + */ +int foreach_opened_amp(int (*cfunc)(AMP *, rig_ptr_t), rig_ptr_t data) +{ + struct opened_amp_l *p; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + for (p = opened_amp_list; p; p = p->next) + { + if ((*cfunc)(p->amp, data) == 0) + { + return RIG_OK; + } + } + + return RIG_OK; +} + + +/** + * \brief allocate a new #AMP handle + * \param amp_model The amp model for this new handle + * + * Allocates a new #AMP handle and initializes the associated data + * for \a amp_model. + * + * \return a pointer to the #AMP handle otherwise NULL if memory allocation + * failed or \a amp_model is unknown (e.g. backend autoload failed). + * + * \sa amp_cleanup(), amp_open() + */ +AMP *HAMLIB_API amp_init(amp_model_t amp_model) +{ + AMP *amp; + const struct amp_caps *caps; + struct amp_state *rs; + int retcode; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + amp_check_backend(amp_model); + + caps = amp_get_caps(amp_model); + + if (!caps) + { + return NULL; + } + + /* + * okay, we've found it. Allocate some memory and set it to zeros, + * and especially the initialize the callbacks + */ + amp = calloc(1, sizeof(AMP)); + + if (amp == NULL) + { + /* + * FIXME: how can the caller know it's a memory shortage, + * and not "amp not found" ? + */ + return NULL; + } + + /* caps is const, so we need to tell compiler + that we know what we are doing */ + amp->caps = (struct amp_caps *) caps; + + /* + * populate the amp->state + * TODO: read the Preferences here! + */ + rs = &->state; + + rs->comm_state = 0; + rs->ampport.type.rig = caps->port_type; /* default from caps */ + + rs->ampport.write_delay = caps->write_delay; + rs->ampport.post_write_delay = caps->post_write_delay; + rs->ampport.timeout = caps->timeout; + rs->ampport.retry = caps->retry; + rs->has_get_level = caps->has_get_level; + + switch (caps->port_type) + { + case RIG_PORT_SERIAL: + // Dont' think we need a default port here + //strncpy(rs->ampport.pathname, DEFAULT_SERIAL_PORT, FILPATHLEN - 1); + rs->ampport.parm.serial.rate = caps->serial_rate_max; /* fastest ! */ + rs->ampport.parm.serial.data_bits = caps->serial_data_bits; + rs->ampport.parm.serial.stop_bits = caps->serial_stop_bits; + rs->ampport.parm.serial.parity = caps->serial_parity; + rs->ampport.parm.serial.handshake = caps->serial_handshake; + break; + + case RIG_PORT_NETWORK: + case RIG_PORT_UDP_NETWORK: + strncpy(rs->ampport.pathname, "127.0.0.1:4534", FILPATHLEN - 1); + break; + + default: + strncpy(rs->ampport.pathname, "", FILPATHLEN - 1); + } + + rs->ampport.fd = -1; + + /* + * let the backend a chance to setup his private data + * This must be done only once defaults are setup, + * so the backend init can override amp_state. + */ + if (caps->amp_init != NULL) + { + retcode = caps->amp_init(amp); + + if (retcode != RIG_OK) + { + amp_debug(RIG_DEBUG_VERBOSE, + "%s: backend_init failed!\n", + __func__); + /* cleanup and exit */ + free(amp); + return NULL; + } + } + + return amp; +} + + +/** + * \brief open the communication to the amp + * \param amp The #AMP handle of the amplifier to be opened + * + * Opens communication to a amplifier which \a AMP handle has been passed + * by argument. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \retval RIG_EINVAL \a amp is NULL or unconsistent. + * \retval RIG_ENIMPL port type communication is not implemented yet. + * + * \sa amp_init(), amp_close() + */ +int HAMLIB_API amp_open(AMP *amp) +{ + const struct amp_caps *caps; + struct amp_state *rs; + int status; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return -RIG_EINVAL; + } + + caps = amp->caps; + rs = &->state; + + if (rs->comm_state) + { + return -RIG_EINVAL; + } + + rs->ampport.fd = -1; + + switch (rs->ampport.type.rig) + { + case RIG_PORT_SERIAL: + status = serial_open(&rs->ampport); + + if (status != 0) + { + return status; + } + + break; + + case RIG_PORT_PARALLEL: + status = par_open(&rs->ampport); + + if (status < 0) + { + return status; + } + + break; + + case RIG_PORT_DEVICE: + status = open(rs->ampport.pathname, O_RDWR, 0); + + if (status < 0) + { + return -RIG_EIO; + } + + rs->ampport.fd = status; + break; + + case RIG_PORT_USB: + status = usb_port_open(&rs->ampport); + + if (status < 0) + { + return status; + } + + break; + + case RIG_PORT_NONE: + case RIG_PORT_RPC: + break; /* ez :) */ + + case RIG_PORT_NETWORK: + case RIG_PORT_UDP_NETWORK: + /* FIXME: default port */ + status = network_open(&rs->ampport, 4533); + + if (status < 0) + { + return status; + } + + break; + + default: + return -RIG_EINVAL; + } + + + add_opened_amp(amp); + + rs->comm_state = 1; + + /* + * Maybe the backend has something to initialize + * In case of failure, just close down and report error code. + */ + if (caps->amp_open != NULL) + { + status = caps->amp_open(amp); + + if (status != RIG_OK) + { + return status; + } + } + + return RIG_OK; +} + + +/** + * \brief close the communication to the amp + * \param amp The #AMP handle of the amplifier to be closed + * + * Closes communication to a amplifier which \a AMP handle has been passed + * by argument that was previously open with amp_open(). + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa amp_cleanup(), amp_open() + */ +int HAMLIB_API amp_close(AMP *amp) +{ + const struct amp_caps *caps; + struct amp_state *rs; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return -RIG_EINVAL; + } + + caps = amp->caps; + rs = &->state; + + if (!rs->comm_state) + { + return -RIG_EINVAL; + } + + /* + * Let the backend say 73s to the amp. + * and ignore the return code. + */ + if (caps->amp_close) + { + caps->amp_close(amp); + } + + + if (rs->ampport.fd != -1) + { + switch (rs->ampport.type.rig) + { + case RIG_PORT_SERIAL: + ser_close(&rs->ampport); + break; + + case RIG_PORT_PARALLEL: + par_close(&rs->ampport); + break; + + case RIG_PORT_USB: + usb_port_close(&rs->ampport); + break; + + case RIG_PORT_NETWORK: + case RIG_PORT_UDP_NETWORK: + network_close(&rs->ampport); + break; + + default: + close(rs->ampport.fd); + } + + rs->ampport.fd = -1; + } + + remove_opened_amp(amp); + + rs->comm_state = 0; + + return RIG_OK; +} + + +/** + * \brief release a amp handle and free associated memory + * \param amp The #AMP handle of the radio to be closed + * + * Releases a amp struct which port has eventualy been closed already + * with amp_close(). + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa amp_init(), amp_close() + */ +int HAMLIB_API amp_cleanup(AMP *amp) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return -RIG_EINVAL; + } + + /* + * check if they forgot to close the amp + */ + if (amp->state.comm_state) + { + amp_close(amp); + } + + /* + * basically free up the priv struct + */ + if (amp->caps->amp_cleanup) + { + amp->caps->amp_cleanup(amp); + } + + free(amp); + + return RIG_OK; +} + +/** + * \brief reset the amplifier + * \param amp The amp handle + * \param reset The reset operation to perform + * + * Resets the amplifier. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + */ +int HAMLIB_API amp_reset(AMP *amp, amp_reset_t reset) +{ + const struct amp_caps *caps; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + caps = amp->caps; + + if (caps->reset == NULL) + { + return -RIG_ENAVAIL; + } + + return caps->reset(amp, reset); +} + +int HAMLIB_API amp_get_freq(AMP *amp, freq_t *freq) +{ + const struct amp_caps *caps; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + caps = amp->caps; + + if (caps->get_freq == NULL) + { + return -RIG_ENAVAIL; + } + + return caps->get_freq(amp, freq); +} + +int HAMLIB_API amp_set_freq(AMP *amp, freq_t freq) +{ + const struct amp_caps *caps; + + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + caps = amp->caps; + + if (caps->set_freq == NULL) + { + return -RIG_ENAVAIL; + } + + return caps->set_freq(amp, freq); +} + +/** + * \brief get general information from the amplifier + * \param amp The amp handle + * + * Retrieves some general information from the amplifier. + * This can include firmware revision, exact model name, or just nothing. + * + * \return a pointer to static memory containing the ASCIIZ string + * if the operation has been sucessful, otherwise NULL if an error occured + * or get_info not part of capabilities. + */ +const char *HAMLIB_API amp_get_info(AMP *amp) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return NULL; + } + + if (amp->caps->get_info == NULL) + { + return NULL; + } + + return amp->caps->get_info(amp); +} + +int HAMLIB_API amp_get_level(AMP *amp, setting_t level, value_t *val) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + if (amp->caps->get_level == NULL) + { + return -RIG_ENIMPL; + } + + return amp->caps->get_level(amp, level, val); +} + +int HAMLIB_API amp_get_ext_level(AMP *amp, token_t level, value_t *val) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + if (amp->caps->get_ext_level == NULL) + { + return -RIG_ENIMPL; + } + + return amp->caps->get_ext_level(amp, level, val); +} + +/** + * \brief turn on/off the amplifier or standby/operate toggle + * \param amp The amp handle + * \param status The status to set to + * + * turns on/off the amplifier. + * See #RIG_POWER_ON, #RIG_POWER_OFF and #RIG_POWER_STANDBY #RIG_POWER_OPERATE defines + * for the \a status. + * + * \return RIG_OK if the operation has been sucessful, ortherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa amp_get_powerstat() + */ + +int HAMLIB_API amp_set_powerstat(AMP *amp, powerstat_t status) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + if (amp->caps->set_powerstat == NULL) + { + return -RIG_ENIMPL; + } + + return amp->caps->set_powerstat(amp, status); +} + +int HAMLIB_API amp_get_powerstat(AMP *amp, powerstat_t *status) +{ + amp_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (CHECK_AMP_ARG(amp)) + { + return -RIG_EINVAL; + } + + if (amp->caps->get_powerstat == NULL) + { + return -RIG_ENIMPL; + } + + return amp->caps->get_powerstat(amp, status); +} + + +/*! @} */ diff --git a/src/ext.c b/src/ext.c index 6b88db645..1a068ea69 100644 --- a/src/ext.c +++ b/src/ext.c @@ -182,7 +182,6 @@ const struct confparams *HAMLIB_API rig_ext_lookup(RIG *rig, const char *name) return NULL; } - /** * \param rig * \param token diff --git a/src/extamp.c b/src/extamp.c new file mode 100644 index 000000000..66db3e7b6 --- /dev/null +++ b/src/extamp.c @@ -0,0 +1,249 @@ +/* + * Hamlib Interface - extra parameter interface for amplifiers + * Copyright (c) 2000-2008 by Stephane Fillod + * Derived from ext.c + * Copyright (c) 2019 by Michael Black W9MDB + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/** + * \addtogroup amp + * @{ + */ + +/** + * \file ext.c + * \brief Extension request parameter interface + * + * An open-ended set of extension parameters and levels are available for each + * amp, as provided in the ampcaps extparms and extlevels lists. These + * provide a way to work with amp-specific functions that don't fit into the + * basic "virtual amp" of Hamlib. See amplifiers/kpa.c for an example. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include /* Standard input/output definitions */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ + +#include + +#include "token.h" + + +/** + * \param amp The amp handle + * \param cfunc callback function of each extlevel + * \param data cookie to be passed to \a cfunc callback + * \brief Executes cfunc on all the elements stored in the extlevels table + * + * The callback \a cfunc is called until it returns a value which is not + * strictly positive. A zero value means a normal end of iteration, and a + * negative value an abnormal end, which will be the return value of + * amp_ext_level_foreach. + */ +int HAMLIB_API amp_ext_level_foreach(AMP *amp, + int (*cfunc)(AMP *, + const struct confparams *, + amp_ptr_t), + amp_ptr_t data) +{ + const struct confparams *cfp; + int ret; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps || !cfunc) + { + return -RIG_EINVAL; + } + + for (cfp = amp->caps->extlevels; cfp && cfp->name; cfp++) + { + ret = (*cfunc)(amp, cfp, data); + + if (ret == 0) + { + return RIG_OK; + } + + if (ret < 0) + { + return ret; + } + } + + return RIG_OK; +} + + +/** + * \param amp The amp handle + * \param cfunc callback function of each extparm + * \param data cookie to be passed to \a cfunc callback + * \brief Executes cfunc on all the elements stored in the extparms table + * + * The callback \a cfunc is called until it returns a value which is not + * strictly positive. A zero value means a normal end of iteration, and a + * negative value an abnormal end, which will be the return value of + * amp_ext_parm_foreach. + */ +int HAMLIB_API amp_ext_parm_foreach(AMP *amp, + int (*cfunc)(AMP *, + const struct confparams *, + amp_ptr_t), + amp_ptr_t data) +{ + const struct confparams *cfp; + int ret; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps || !cfunc) + { + return -RIG_EINVAL; + } + + for (cfp = amp->caps->extparms; cfp && cfp->name; cfp++) + { + ret = (*cfunc)(amp, cfp, data); + + if (ret == 0) + { + return RIG_OK; + } + + if (ret < 0) + { + return ret; + } + } + + return RIG_OK; +} + + +/** + * \param amp + * \param name + * \brief lookup ext token by its name, return pointer to confparams struct. + * + * Lookup extlevels table first, then fall back to extparms. + * + * Returns NULL if nothing found + * + * TODO: should use Lex to speed it up, strcmp hurts! + */ +const struct confparams *HAMLIB_API amp_ext_lookup(AMP *amp, const char *name) +{ + const struct confparams *cfp; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return NULL; + } + + for (cfp = amp->caps->extlevels; cfp && cfp->name; cfp++) + { + if (!strcmp(cfp->name, name)) + { + return cfp; + } + } + + for (cfp = amp->caps->extparms; cfp && cfp->name; cfp++) + { + if (!strcmp(cfp->name, name)) + { + return cfp; + } + } + + return NULL; +} + + +/** + * \param amp + * \param token + * \brief lookup ext token, return pointer to confparams struct. + * + * lookup extlevels table first, then fall back to extparms. + * + * Returns NULL if nothing found + */ +const struct confparams * HAMLIB_API amp_ext_lookup_tok(AMP *amp, token_t token) +{ + const struct confparams *cfp; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return NULL; + } + + for (cfp = amp->caps->extlevels; cfp && cfp->token; cfp++) + { + if (cfp->token == token) + { + return cfp; + } + } + + for (cfp = amp->caps->extparms; cfp && cfp->token; cfp++) + { + if (cfp->token == token) + { + return cfp; + } + } + + return NULL; +} + + +/** + * \param amp + * \param name + * \brief Simple lookup returning token id assicated with name + */ +token_t HAMLIB_API amp_ext_token_lookup(AMP *amp, const char *name) +{ + const struct confparams *cfp; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + cfp = amp_ext_lookup(amp, name); + + if (!cfp) + { + return RIG_CONF_END; + } + + return cfp->token; +} + +/** @} */ diff --git a/src/misc.c b/src/misc.c index 3c28bf173..d9a88b959 100644 --- a/src/misc.c +++ b/src/misc.c @@ -51,6 +51,7 @@ #include #include +#include #include "misc.h" @@ -628,6 +629,23 @@ static struct { RIG_LEVEL_NONE, "" }, }; +static struct +{ + setting_t level; + const char *str; +} levelamp_str[] = +{ + { AMP_LEVEL_SWR, "SWR" }, + { AMP_LEVEL_NH, "NH" }, + { AMP_LEVEL_PF, "PF" }, + { AMP_LEVEL_PWR_INPUT, "PWRINPUT" }, + { AMP_LEVEL_PWR_FWD, "PWRFORWARD" }, + { AMP_LEVEL_PWR_REFLECTED, "PWRREFLECTED" }, + { AMP_LEVEL_PWR_PEAK, "PWRPEAK" }, + { AMP_LEVEL_FAULT, "FAULT" }, + { AMP_LEVEL_NONE, "" }, +}; + /** * \brief Convert alpha string to enum RIG_LEVEL_... @@ -653,6 +671,32 @@ setting_t HAMLIB_API rig_parse_level(const char *s) return RIG_LEVEL_NONE; } +/** + * \brief Convert alpha string to enum AMP_LEVEL_... + * \param s input alpha string + * \return AMP_LEVEL_... + * + * \sa amp_level_e() + */ +setting_t HAMLIB_API amp_parse_level(const char *s) +{ + int i; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called level=%s\n", __func__, s); + + rig_debug(RIG_DEBUG_VERBOSE, "%s called str=%s\n", __func__, levelamp_str[0].str); + for (i = 0 ; levelamp_str[i].str[0] != '\0'; i++) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s called checking=%s\n", __func__, levelamp_str[i].str); + if (!strcmp(s, levelamp_str[i].str)) + { + return levelamp_str[i].level; + } + } + + return RIG_LEVEL_NONE; +} + /** * \brief Convert enum RIG_LEVEL_... to alpha string @@ -683,6 +727,35 @@ const char * HAMLIB_API rig_strlevel(setting_t level) return ""; } +/** + * \brief Convert enum AMP_LEVEL_... to alpha string + * \param level AMP_LEVEL_... + * \return alpha string + * + * \sa amp_level_e() + */ +const char * HAMLIB_API amp_strlevel(setting_t level) +{ + int i; + + //rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (level == AMP_LEVEL_NONE) + { + return ""; + } + + for (i = 0; levelamp_str[i].str[0] != '\0'; i++) + { + if (level == levelamp_str[i].level) + { + return levelamp_str[i].str; + } + } + + return ""; +} + static struct { diff --git a/src/register.h b/src/register.h index fe952cd21..0d15dc3b8 100644 --- a/src/register.h +++ b/src/register.h @@ -24,6 +24,7 @@ #include #include +#include #ifdef __cplusplus #define EXTERN_C extern "C" @@ -69,4 +70,19 @@ rig_probe_func_t cfunc, \ rig_ptr_t data)) +#define PREFIX_INITAMPS initamps +#define PREFIX_PROBEAMPS probeallamps + +#define DECLARE_INITAMP_BACKEND(backend) \ + EXTERN_C BACKEND_EXPORT(int) \ + MAKE_VERSIONED_FN(PREFIX_INITAMPS, ABI_VERSION, backend(void *be_handle)) + +#define DECLARE_PROBEAMP_BACKEND(backend) \ + EXTERN_C BACKEND_EXPORT(amp_model_t) \ + MAKE_VERSIONED_FN(PREFIX_PROBEAMPS, \ + ABI_VERSION, \ + backend(hamlib_port_t *port, \ + rig_probe_func_t cfunc, \ + rig_ptr_t data)) + #endif /* _REGISTER_H */ diff --git a/src/rig.c b/src/rig.c index 6eb923386..a674cefca 100644 --- a/src/rig.c +++ b/src/rig.c @@ -1962,10 +1962,10 @@ int HAMLIB_API rig_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt) return par_ptt_get(&rig->state.pttport, ptt); case RIG_PTT_CM108: - if (caps->get_ptt) - { - return caps->get_ptt(rig, vfo, ptt); - } + //if (caps->get_ptt) + //{ + // return caps->get_ptt(rig, vfo, ptt); + //} return cm108_ptt_get(&rig->state.pttport, ptt); diff --git a/src/settings.c b/src/settings.c index bfd7f425b..92d58ac68 100644 --- a/src/settings.c +++ b/src/settings.c @@ -46,6 +46,7 @@ #include #include +#include #include "cal.h" @@ -310,6 +311,35 @@ setting_t HAMLIB_API rig_has_get_level(RIG *rig, setting_t level) } +/** + * \brief check retrieval ability of level settings + * \param amp The amp handle + * \param level The level settings + * + * Checks if an amp is capable of *getting* a level setting. + * Since the \a level is an OR'ed bitwise argument, more than + * one level can be checked at the same time. + * + * EXAMPLE: if (amp_has_get_level(my_amp, AMP_LVL_SWR)) disp_SWR(); + * + * \return a bit map of supported level settings that can be retrieved, + * otherwise 0 if none supported. + * + * \sa amp_has_set_level(), amp_get_level() + */ +setting_t HAMLIB_API amp_has_get_level(AMP *amp, setting_t level) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + if (!amp || !amp->caps) + { + return 0; + } + + return (amp->state.has_get_level & level); +} + + /** * \brief check settable ability of level settings * \param rig The rig handle diff --git a/tests/Makefile.am b/tests/Makefile.am index 5c9bd01dc..b4ab7301c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,18 +6,21 @@ DISTCLEANFILES = rigctl.log rigctl.sum testbcd.log testbcd.sum -bin_PROGRAMS = rigctl rigctld rigmem rigsmtr rigswr rotctl rotctld rigctlcom +bin_PROGRAMS = rigctl rigctld rigmem rigsmtr rigswr rotctl rotctld rigctlcom ampctl ampctld check_PROGRAMS = dumpmem testrig testtrn testbcd testfreq listrigs testloc rig_bench RIGCOMMONSRC = rigctl_parse.c rigctl_parse.h dumpcaps.c sprintflst.c sprintflst.h uthash.h ROTCOMMONSRC = rotctl_parse.c rotctl_parse.h dumpcaps_rot.c uthash.h +AMPCOMMONSRC = ampctl_parse.c ampctl_parse.h dumpcaps_amp.c sprintflst.c sprintflst.h uthash.h rigctl_SOURCES = rigctl.c $(RIGCOMMONSRC) rigctld_SOURCES = rigctld.c $(RIGCOMMONSRC) rigctlcom_SOURCES = rigctlcom.c $(RIGCOMMONSRC) rotctl_SOURCES = rotctl.c $(ROTCOMMONSRC) rotctld_SOURCES = rotctld.c $(ROTCOMMONSRC) +ampctl_SOURCES = ampctl.c $(AMPCOMMONSRC) +ampctld_SOURCES = ampctld.c $(AMPCOMMONSRC) rigswr_SOURCES = rigswr.c rigsmtr_SOURCES = rigsmtr.c rigmem_SOURCES = rigmem.c memsave.c memload.c memcsv.c sprintflst.c sprintflst.h @@ -29,12 +32,15 @@ LDADD = $(top_builddir)/src/libhamlib.la $(top_builddir)/lib/libmisc.la rigmem_CFLAGS = $(AM_CFLAGS) $(LIBXML2_CFLAGS) rigctld_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) rotctld_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) +ampctld_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) rigctlcom_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) rigctl_LDADD = $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) rigctld_LDADD = $(NET_LIBS) $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) rotctl_LDADD = $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) rotctld_LDADD = $(NET_LIBS) $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) +ampctl_LDADD = $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) +ampctld_LDADD = $(NET_LIBS) $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) rigmem_LDADD = $(LIBXML2_LIBS) $(LDADD) rigctlcom_LDADD = $(NET_LIBS) $(PTHREAD_LIBS) $(LDADD) $(READLINE_LIBS) @@ -44,8 +50,10 @@ rigswr_LDFLAGS = $(WINEXELDFLAGS) rigsmtr_LDFLAGS = $(WINEXELDFLAGS) rigmem_LDFLAGS = $(WINEXELDFLAGS) rotctl_LDFLAGS = $(WINEXELDFLAGS) +ampctl_LDFLAGS = $(WINEXELDFLAGS) rigctld_LDFLAGS = $(WINEXELDFLAGS) rotctld_LDFLAGS = $(WINEXELDFLAGS) +ampctld_LDFLAGS = $(WINEXELDFLAGS) rigctlcom_LDFLAGS = $(WINEXELDFLAGS) diff --git a/tests/ampctl.c b/tests/ampctl.c new file mode 100644 index 000000000..bf8708d02 --- /dev/null +++ b/tests/ampctl.c @@ -0,0 +1,479 @@ +/* + * ampctl.c - (C) Stephane Fillod 2000-2010 + * (C) Nate Bargmann 2003,2007,2010,2011,2012,2013 + * (C) The Hamlib Group 2002,2006 + * (C) Derived from rotctl.c Michael Black W9MDB 2019 + * + * This program tests/controls an amplifier using Hamlib. + * It takes commands in interactive mode as well as + * from command line options. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBREADLINE +# if defined(HAVE_READLINE_READLINE_H) +# include +# elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */ +# include +# else /* !defined(HAVE_READLINE_H) */ +extern char *readline(); +# endif /* HAVE_READLINE_H */ +#else +/* no readline */ +#endif /* HAVE_LIBREADLINE */ + +#ifdef HAVE_READLINE_HISTORY +# include +# define HST_SHRT_OPTS "iI" +# if defined(HAVE_READLINE_HISTORY_H) +# include +# elif defined(HAVE_HISTORY_H) +# include +# else /* !defined(HAVE_HISTORY_H) */ +extern void add_history(); +extern int write_history(); +extern int read_history(); +# endif /* defined(HAVE_READLINE_HISTORY_H) */ +#else +/* no history */ +# define HST_SHRT_OPTS "" +#endif /* HAVE_READLINE_HISTORY */ + + +#include +#include "misc.h" + +#include "ampctl_parse.h" + +/* + * Prototypes + */ +void usage(); + +/* + * Reminder: when adding long options, + * keep up to date SHORT_OPTIONS, usage()'s output and man page. thanks. + * NB: do NOT use -W since it's reserved by POSIX. + * TODO: add an option to read from a file + */ +#define SHORT_OPTIONS "+m:r:s:C:t:LvhVluZ" +static struct option long_options[] = +{ + {"model", 1, 0, 'm'}, + {"amp-file", 1, 0, 'r'}, + {"serial-speed", 1, 0, 's'}, + {"send-cmd-term", 1, 0, 't'}, + {"list", 0, 0, 'l'}, + {"set-conf", 1, 0, 'C'}, + {"show-conf", 0, 0, 'L'}, + {"dump-caps", 0, 0, 'u'}, + {"debug-time-stamps", 0, 0, 'Z'}, +#ifdef HAVE_READLINE_HISTORY + {"read-history", 0, 0, 'i'}, + {"save-history", 0, 0, 'I'}, +#endif + {"verbose", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0} +}; + +#define MAXCONFLEN 128 + +/* variable for readline support */ +#ifdef HAVE_LIBREADLINE +static const int have_rl = 1; +#endif + + +int interactive = 1; /* if no cmd on command line, switch to interactive */ +int prompt = 1; /* Print prompt in ampctl */ + +char send_cmd_term = '\r'; /* send_cmd termination char */ + + +int main(int argc, char *argv[]) +{ + AMP *my_amp; /* handle to amp (instance) */ + amp_model_t my_model = AMP_MODEL_DUMMY; + + int retcode; /* generic return code from functions */ + int exitcode; + + int verbose = 0; + int show_conf = 0; + int dump_caps_opt = 0; + +#ifdef HAVE_READLINE_HISTORY + int rd_hist = 0; + int sv_hist = 0; + const char *hist_dir = NULL; + const char hist_file[] = "/.ampctl_history"; + char *hist_path = NULL; + struct stat hist_dir_stat; +#endif /* HAVE_READLINE_HISTORY */ + + const char *amp_file = NULL; + int serial_rate = 0; + char conf_parms[MAXCONFLEN] = ""; + + while (1) + { + int c; + int option_index = 0; + + c = getopt_long(argc, + argv, + SHORT_OPTIONS HST_SHRT_OPTS, + long_options, + &option_index); + + if (c == -1) + { + break; + } + + switch (c) + { + case 'h': + usage(); + exit(0); + + case 'V': + version(); + exit(0); + + case 'm': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + my_model = atoi(optarg); + break; + + case 'r': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + amp_file = optarg; + break; + + case 's': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + serial_rate = atoi(optarg); + break; + + case 'C': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + if (*conf_parms != '\0') + { + strcat(conf_parms, ","); + } + + strncat(conf_parms, optarg, MAXCONFLEN - strlen(conf_parms)); + break; + + case 't': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + if (strlen(optarg) > 1) + { + send_cmd_term = strtol(optarg, NULL, 0); + } + else + { + send_cmd_term = optarg[0]; + } + + break; +#ifdef HAVE_READLINE_HISTORY + + case 'i': + rd_hist++; + break; + + case 'I': + sv_hist++; + break; +#endif /* HAVE_READLINE_HISTORY */ + + case 'v': + verbose++; + break; + + case 'L': + show_conf++; + break; + + case 'l': + rig_set_debug(0); + list_models(); + exit(0); + + case 'u': + dump_caps_opt++; + break; + + case 'Z': + rig_set_debug_time_stamp(1); + break; + + default: + usage(); /* unknown option? */ + exit(1); + } + } + + rig_set_debug(verbose); + + rig_debug(RIG_DEBUG_VERBOSE, "ampctl, %s\n", hamlib_version); + rig_debug(RIG_DEBUG_VERBOSE, + "Report bugs to \n\n"); + + /* + * at least one command on command line, + * disable interactive mode + */ + if (optind < argc) + { + interactive = 0; + } + + my_amp = amp_init(my_model); + + if (!my_amp) + { + fprintf(stderr, + "Unknown amp num %d, or initialization error.\n", + my_model); + + fprintf(stderr, "Please check with --list option.\n"); + exit(2); + } + + retcode = set_conf(my_amp, conf_parms); + + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + + if (amp_file) + { + strncpy(my_amp->state.ampport.pathname, amp_file, FILPATHLEN - 1); + } + + /* FIXME: bound checking and port type == serial */ + if (serial_rate != 0) + { + my_amp->state.ampport.parm.serial.rate = serial_rate; + } + +#if 0 + + /* + * print out conf parameters + */ + if (show_conf) + { + amp_token_foreach(my_amp, print_conf_list, (rig_ptr_t)my_amp); + } + +#endif + + /* + * Print out capabilities, and exits immediately as we may be interested + * only in caps, and rig_open may fail. + */ + if (dump_caps_opt) + { + dumpcaps_amp(my_amp, stdout); + amp_cleanup(my_amp); /* if you care about memory */ + exit(0); + } + + retcode = amp_open(my_amp); + + if (retcode != RIG_OK) + { + fprintf(stderr, "amp_open: error = %s \n", rigerror(retcode)); + exit(2); + } + + if (verbose > 0) + { + printf("Opened amp model %d, '%s'\n", + my_amp->caps->amp_model, + my_amp->caps->model_name); + } + + rig_debug(RIG_DEBUG_VERBOSE, + "Backend version: %s, Status: %s\n", + my_amp->caps->version, + rig_strstatus(my_amp->caps->status)); + + exitcode = 0; + +#ifdef HAVE_LIBREADLINE + + if (interactive && prompt && have_rl) + { + rl_readline_name = "ampctl"; +#ifdef HAVE_READLINE_HISTORY + using_history(); /* Initialize Readline History */ + + if (rd_hist || sv_hist) + { + if (!(hist_dir = getenv("AMPCTL_HIST_DIR"))) + { + hist_dir = getenv("HOME"); + } + + if (((stat(hist_dir, &hist_dir_stat) == -1) && (errno == ENOENT)) + || !(S_ISDIR(hist_dir_stat.st_mode))) + { + + fprintf(stderr, "Warning: %s is not a directory!\n", hist_dir); + } + + int hist_path_size = sizeof(char) * (strlen(hist_dir) + strlen(hist_file) + 1); + hist_path = (char *)calloc(hist_path_size, sizeof(char)); + + snprintf(hist_path, hist_path_size, "%s%s", hist_dir, hist_file); + + } + + if (rd_hist && hist_path) + { + if (read_history(hist_path) == ENOENT) + { + fprintf(stderr, + "Warning: Could not read history from %s\n", + hist_path); + } + } + +#endif /* HAVE_READLINE_HISTORY */ + } + +#endif /* HAVE_LIBREADLINE */ + + do + { + retcode = ampctl_parse(my_amp, stdin, stdout, argv, argc); + + if (retcode == 2) + { + exitcode = 2; + } + } + while (retcode == 0 || retcode == 2); + +#ifdef HAVE_LIBREADLINE + + if (interactive && prompt && have_rl) + { +#ifdef HAVE_READLINE_HISTORY + + if (sv_hist && hist_path) + { + if (write_history(hist_path) == ENOENT) + { + fprintf(stderr, + "\nWarning: Could not write history to %s\n", + hist_path); + } + } + + if ((rd_hist || sv_hist) && hist_path) + { + free(hist_path); + hist_path = (char *)NULL; + } + +#endif /* HAVE_READLINE_HISTORY */ + } + +#endif /* HAVE_LIBREADLINE */ + amp_close(my_amp); /* close port */ + amp_cleanup(my_amp); /* if you care about memory */ + + return exitcode; +} + + +void usage() +{ + printf("Usage: ampctl [OPTION]... [COMMAND]...\n" + "Send COMMANDs to a connected amplifier.\n\n"); + + printf( + " -m, --model=ID select amplifier model number. See model list\n" + " -r, --amp-file=DEVICE set device of the amplifier to operate on\n" + " -s, --serial-speed=BAUD set serial speed of the serial port\n" + " -t, --send-cmd-term=CHAR set send_cmd command termination char\n" + " -C, --set-conf=PARM=VAL set config parameters\n" + " -L, --show-conf list all config parameters\n" + " -l, --list list all model numbers and exit\n" + " -u, --dump-caps dump capabilities and exit\n" +#ifdef HAVE_READLINE_HISTORY + " -i, --read-history read prior interactive session history\n" + " -I, --save-history save current interactive session history\n" +#endif + " -v, --verbose set verbose mode, cumulative\n" + " -Z, --debug-time-stamps enable time stamps for debug messages\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n\n" + ); + + usage_amp(stdout); + + printf("\nReport bugs to .\n"); +} diff --git a/tests/ampctl_parse.c b/tests/ampctl_parse.c new file mode 100644 index 000000000..7178e4496 --- /dev/null +++ b/tests/ampctl_parse.c @@ -0,0 +1,2305 @@ +/* + * ampctl_parse.c - (C) Stephane Fillod 2000-2010 + * (C) Nate Bargmann 2003,2007,2010,2011,2012,2013 + * (C) The Hamlib Group 2002,2006,2011 + * Derived from rotctl_parse.c by Michael Black 2019 + * + * This program test/control an amplifier using Hamlib. + * It takes commands in interactive mode as well as + * from command line options. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBREADLINE +# if defined(HAVE_READLINE_READLINE_H) +# include +# elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */ +# include +# else /* !defined(HAVE_READLINE_H) */ +extern char *readline(); +# endif /* HAVE_READLINE_H */ +#else +/* no readline */ +#endif /* HAVE_LIBREADLINE */ + +#ifdef HAVE_READLINE_HISTORY +# if defined(HAVE_READLINE_HISTORY_H) +# include +# elif defined(HAVE_HISTORY_H) +# include +# else /* !defined(HAVE_HISTORY_H) */ +extern void add_history(); +extern int write_history(); +extern int read_history(); +# endif /* defined(HAVE_READLINE_HISTORY_H) */ +/* no history */ +#endif /* HAVE_READLINE_HISTORY */ + +#include +#include "serial.h" +#include "misc.h" +#include "sprintflst.h" + + +/* HAVE_SSLEEP is defined when Windows Sleep is found + * HAVE_SLEEP is defined when POSIX sleep is found + * _WIN32 is defined when compiling with MinGW + * + * When cross-compiling from POSIX to Windows using MinGW, HAVE_SLEEP + * will often be defined by configure although it is not supported by + * MinGW. So substitute the sleep definition below in such a case and + * when compiling on Windows using MinGW where HAVE_SLEEP will be + * undefined. + * + * FIXME: Needs better handling for all versions of MinGW. + * + */ +#if (defined(HAVE_SSLEEP) || defined(_WIN32)) && (!defined(HAVE_SLEEP)) +# include "hl_sleep.h" +#endif + +#include "ampctl_parse.h" + +/* Hash table implementation See: http://uthash.sourceforge.net/ */ +#include "uthash.h" + +#ifdef HAVE_PTHREAD +# include + +static pthread_mutex_t amp_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +#define STR1(S) #S +#define STR(S) STR1(S) + +#define MAXNAMSIZ 32 +#define MAXNBOPT 100 /* max number of different options */ +#define MAXARGSZ 127 + +#define ARG_IN1 0x01 +#define ARG_OUT1 0x02 +#define ARG_IN2 0x04 +#define ARG_OUT2 0x08 +#define ARG_IN3 0x10 +#define ARG_OUT3 0x20 +#define ARG_IN4 0x40 +#define ARG_OUT4 0x80 +#define ARG_IN_LINE 0x4000 + +#define ARG_NONE 0 +#define ARG_IN (ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4) +#define ARG_OUT (ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4) + +/* variables for readline support */ +#ifdef HAVE_LIBREADLINE +static char *input_line = (char *)NULL; +static char *result = (char *)NULL; +static char *parsed_input[sizeof(char *) * 7]; +static const int have_rl = 1; + +#ifdef HAVE_READLINE_HISTORY +static char *rp_hist_buf = (char *)NULL; +#endif + +#else /* no readline */ +static const int have_rl = 0; +#endif + +struct test_table +{ + unsigned char cmd; + const char *name; + int (*amp_routine)(AMP *, + FILE *, + int, + const struct test_table *, + const char *, + const char *, + const char *, + const char *, + const char *, + const char *); + int flags; + const char *arg1; + const char *arg2; + const char *arg3; + const char *arg4; + const char *arg5; + const char *arg6; +}; + +#define CHKSCN1ARG(a) if ((a) != 1) return -RIG_EINVAL; else do {} while(0) + +#define ACTION(f) ampctl_##f +#define declare_proto_amp(f) static int (ACTION(f))(AMP *amp, \ + FILE *fout, \ + int interactive, \ + const struct test_table *cmd, \ + const char *arg1, \ + const char *arg2, \ + const char *arg3, \ + const char *arg4, \ + const char *arg5, \ + const char *arg6) + +declare_proto_amp(set_freq); +declare_proto_amp(get_freq); +declare_proto_amp(send_cmd); +declare_proto_amp(dump_state); +declare_proto_amp(dump_caps); +declare_proto_amp(get_info); +declare_proto_amp(reset); +declare_proto_amp(get_level); +declare_proto_amp(set_powerstat); +declare_proto_amp(get_powerstat); +//declare_proto_amp(dump_caps); + +/* + * convention: upper case cmd is set, lowercase is get + * + * NB: 'q' 'Q' '?' are reserved by interactive mode interface + */ +struct test_table test_list[] = +{ + { 'F', "set_freq", ACTION(set_freq), ARG_IN, "Frequency(Hz)" }, + { 'f', "get_freq", ACTION(get_freq), ARG_OUT, "Frequency(Hz)" }, + { 'l', "get_level", ACTION(get_level), ARG_IN1 | ARG_OUT2, "Level", "Level Value" }, + { 'w', "send_cmd", ACTION(send_cmd), ARG_IN1 | ARG_IN_LINE | ARG_OUT2, "Cmd", "Reply" }, + { 0x8f, "dump_state", ACTION(dump_state), ARG_OUT }, + { '1', "dump_caps", ACTION(dump_caps), }, + { '_', "get_info", ACTION(get_info), ARG_OUT, "Info" }, + { 'R', "reset", ACTION(reset), ARG_IN, "Reset" }, + { 0x87, "set_powerstat", ACTION(set_powerstat), ARG_IN, "Power Status" }, + { 0x88, "get_powerstat", ACTION(get_powerstat), ARG_OUT, "Power Status" }, + { 0x00, "", NULL }, +}; + + +struct test_table *find_cmd_entry(int cmd) +{ + int i; + + for (i = 0; i < MAXNBOPT && test_list[i].cmd != 0x00; i++) + if (test_list[i].cmd == cmd) + { + break; + } + + if (i >= MAXNBOPT || test_list[i].cmd == 0x00) + { + return NULL; + } + + return &test_list[i]; +} + + +/* Structure for hash table provided by uthash.h + * + * Structure and hash funtions patterned after/copied from example.c + * distributed with the uthash package. See: http://uthash.sourceforge.net/ + */ +struct mod_lst +{ + int id; /* caps->amp_model This is the hash key */ + char mfg_name[32]; /* caps->mfg_name */ + char model_name[32]; /* caps->model_name */ + char version[32]; /* caps->version */ + char status[32]; /* caps->status */ + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +/* Hash declaration. Must be initialized to NULL */ +struct mod_lst *models = NULL; + +/* Add model information to the hash */ +void hash_add_model(int id, + const char *mfg_name, + const char *model_name, + const char *version, + const char *status) +{ + struct mod_lst *s; + + s = (struct mod_lst *)malloc(sizeof(struct mod_lst)); + + s->id = id; + snprintf(s->mfg_name, sizeof(s->mfg_name), "%s", mfg_name); + snprintf(s->model_name, sizeof(s->model_name), "%s", model_name); + snprintf(s->version, sizeof(s->version), "%s", version); + snprintf(s->status, sizeof(s->status), "%s", status); + + HASH_ADD_INT(models, id, s); /* id: name of key field */ +} + + +/* Hash sorting functions */ +int hash_model_id_sort(struct mod_lst *a, struct mod_lst *b) +{ + return (a->id - b->id); +} + + +void hash_sort_by_model_id() +{ + HASH_SORT(models, hash_model_id_sort); +} + + +/* Delete hash */ +void hash_delete_all() +{ + struct mod_lst *current_model, *tmp; + + HASH_ITER(hh, models, current_model, tmp) + { + /* delete it (models advances to next) */ + HASH_DEL(models, current_model); + free(current_model); /* free it */ + } +} + + +#ifdef HAVE_LIBREADLINE +/* Frees allocated memory and sets pointers to NULL before calling readline + * and then parses the input into space separated tokens. + */ +static void rp_getline(const char *s) +{ + int i; + + /* free allocated memory and set pointers to NULL */ + if (input_line) + { + free(input_line); + input_line = (char *)NULL; + } + + if (result) + { + result = (char *)NULL; + } + + /* cmd, arg1, arg2, arg3, arg4, arg5, arg6 + * arg5 and arg 6 are currently unused. + */ + for (i = 0; i < 7; i++) + { + parsed_input[i] = NULL; + } + + /* Action! Returns typed line with newline stripped. */ + input_line = readline(s); +} + + +#endif + + +/* + * TODO: use Lex? + */ +char parse_arg(const char *arg) +{ + int i; + + for (i = 0; i < MAXNBOPT && test_list[i].cmd != 0; i++) + { + if (!strncmp(arg, test_list[i].name, MAXNAMSIZ)) + { + return test_list[i].cmd; + } + } + + return 0; +} + + +/* + * This scanf works even in presence of signals (timer, SIGIO, ..) + */ +static int scanfc(FILE *fin, const char *format, void *p) +{ + int ret; + + do + { + ret = fscanf(fin, format, p); + + if (ret < 0) + { + if (errno == EINTR) + { + continue; + } + + rig_debug(RIG_DEBUG_ERR, "fscanf: %s\n", strerror(errno)); + rig_debug(RIG_DEBUG_ERR, "fscanf: parsing '%s' with '%s'\n", p, format); + } + + return ret; + } + while (1); +} + + +/* + * function to get the next word from the command line or from stdin + * until stdin exhausted. stdin is read if the special token '-' is + * found on the command line. + * + * returns EOF when words exhausted + * returns <0 is error number + * returns >=0 when successful + */ +static int next_word(char *buffer, int argc, char *argv[], int newline) +{ + int ret; + char c; + static int reading_stdin; + + if (!reading_stdin) + { + if (optind >= argc) + { + return EOF; + } + else if (newline + && '-' == argv[optind][0] + && 1 == strlen(argv[optind])) + { + + ++optind; + reading_stdin = 1; + } + } + + if (reading_stdin) + { + do + { + do + { + ret = scanf(" %c%" STR(MAXARGSZ) "[^ \t\n#]", &c, &buffer[1]); + } + while (EINTR == ret); + + if (ret > 0 && '#' == c) + { + do + { + ret = scanf("%*[^\n]"); + } + while (EINTR == ret); /* consume comments */ + + ret = 0; + } + } + while (!ret); + + if (EOF == ret) + { + reading_stdin = 0; + } + else if (ret < 0) + { + rig_debug(RIG_DEBUG_ERR, "scanf: %s\n", strerror(errno)); + reading_stdin = 0; + } + else + { + buffer[0] = c; + buffer[1 == ret ? 1 : MAXARGSZ] = '\0'; + + if (newline) + { + putchar('\n'); + } + + fputs(buffer, stdout); + putchar(' '); + } + } + + if (!reading_stdin) + { + if (optind < argc) + { + strncpy(buffer, argv[optind++], MAXARGSZ); + buffer[MAXARGSZ] = '\0'; + ret = 1; + } + else + { + ret = EOF; + } + } + + return ret; +} + + +#define fprintf_flush(f, a...) \ + ({ int __ret; \ + __ret = fprintf((f), a); \ + fflush((f)); \ + __ret; \ + }) + + +extern int interactive; +extern int prompt; +extern char send_cmd_term; +int ext_resp = 0; +unsigned char resp_sep = '\n'; /* Default response separator */ + + +int ampctl_parse(AMP *my_amp, FILE *fin, FILE *fout, char *argv[], int argc) +{ + int retcode; /* generic return code from functions */ + unsigned char cmd; + struct test_table *cmd_entry; + + char command[MAXARGSZ + 1]; + char arg1[MAXARGSZ + 1], *p1 = NULL; + char arg2[MAXARGSZ + 1], *p2 = NULL; + char arg3[MAXARGSZ + 1], *p3 = NULL; + char arg4[MAXARGSZ + 1], *p4 = NULL; + char *p5 = NULL; + char *p6 = NULL; + static int last_was_ret = 1; + + /* cmd, internal, ampctld */ + if (!(interactive && prompt && have_rl)) + { + if (interactive) + { + if (prompt) + { + fprintf_flush(fout, "\nAmplifier command: "); + } + + do + { + if (scanfc(fin, "%c", &cmd) < 1) + { + return -1; + } + + /* Extended response protocol requested with leading '+' on command + * string--ampctld only! + */ + if (cmd == '+' && !prompt) + { + ext_resp = 1; + + if (scanfc(fin, "%c", &cmd) < 1) + { + return -1; + } + } + else if (cmd == '+' && prompt) + { + return 0; + } + + if (cmd != '\\' + && cmd != '_' + && cmd != '#' + && ispunct(cmd) + && !prompt) + { + + ext_resp = 1; + resp_sep = cmd; + + if (scanfc(fin, "%c", &cmd) < 1) + { + return -1; + } + } + else if (cmd != '\\' + && cmd != '?' + && cmd != '_' + && cmd != '#' + && ispunct(cmd) + && prompt) + { + return 0; + } + + /* command by name */ + if (cmd == '\\') + { + unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name; + int c_len = MAXNAMSIZ; + + if (scanfc(fin, "%c", pcmd) < 1) + { + return -1; + } + + while (c_len-- && (isalnum(*pcmd) || *pcmd == '_')) + { + if (scanfc(fin, "%c", ++pcmd) < 1) + { + return -1; + } + } + + *pcmd = '\0'; + cmd = parse_arg((char *) cmd_name); + break; + } + + if (cmd == 0x0a || cmd == 0x0d) + { + if (last_was_ret) + { + if (prompt) + { + fprintf_flush(fout, "? for help, q to quit.\n"); + } + + return 0; + } + + last_was_ret = 1; + } + } + while (cmd == 0x0a || cmd == 0x0d); + + last_was_ret = 0; + + /* comment line */ + if (cmd == '#') + { + while (cmd != '\n' && cmd != '\r') + { + if (scanfc(fin, "%c", &cmd) < 1) + { + return -1; + } + } + + return 0; + } + + if (cmd == 'Q' || cmd == 'q') + { + return 1; + } + + if (cmd == '?') + { + usage_amp(fout); + fflush(fout); + return 0; + } + } + else + { + /* parse rest of command line */ + retcode = next_word(command, argc, argv, 1); + + if (EOF == retcode) + { + return 1; + } + else if (retcode < 0) + { + return retcode; + } + else if ('\0' == command[1]) + { + cmd = command[0]; + } + else + { + cmd = parse_arg(command); + } + } + + cmd_entry = find_cmd_entry(cmd); + + if (!cmd_entry) + { + fprintf_flush(stderr, "Command '%c' not found!\n", cmd); + return 0; + } + + if ((cmd_entry->flags & ARG_IN_LINE) + && (cmd_entry->flags & ARG_IN1) + && cmd_entry->arg1) + { + + if (interactive) + { + char *nl; + + if (prompt) + { + fprintf_flush(fout, "%s: ", cmd_entry->arg1); + } + + if (fgets(arg1, MAXARGSZ, fin) == NULL) + { + return -1; + } + + if (arg1[0] == 0xa) + { + if (fgets(arg1, MAXARGSZ, fin) == NULL) + { + return -1; + } + } + + nl = strchr(arg1, 0xa); + + if (nl) + { + *nl = '\0'; /* chomp */ + } + + p1 = arg1[0] == ' ' ? arg1 + 1 : arg1; + } + else + { + retcode = next_word(arg1, argc, argv, 0); + + if (EOF == retcode) + { + fprintf(stderr, + "Invalid arg for command '%s'\n", + cmd_entry->name); + return 1; + } + else if (retcode < 0) + { + return retcode; + } + + p1 = arg1; + } + } + else if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) + { + if (interactive) + { + if (prompt) + { + fprintf_flush(fout, "%s: ", cmd_entry->arg1); + } + + if (scanfc(fin, "%s", arg1) < 1) + { + return -1; + } + + p1 = arg1; + } + else + { + retcode = next_word(arg1, argc, argv, 0); + + if (EOF == retcode) + { + fprintf(stderr, + "Invalid arg for command '%s'\n", + cmd_entry->name); + return 1; + } + else if (retcode < 0) + { + return retcode; + } + + p1 = arg1; + } + } + + if (p1 + && p1[0] != '?' + && (cmd_entry->flags & ARG_IN2) + && cmd_entry->arg2) + { + + if (interactive) + { + if (prompt) + { + fprintf_flush(fout, "%s: ", cmd_entry->arg2); + } + + if (scanfc(fin, "%s", arg2) < 1) + { + return -1; + } + + p2 = arg2; + } + else + { + retcode = next_word(arg2, argc, argv, 0); + + if (EOF == retcode) + { + fprintf(stderr, + "Invalid arg for command '%s'\n", + cmd_entry->name); + return 1; + } + else if (retcode < 0) + { + return retcode; + } + + p2 = arg2; + } + } + + if (p1 + && p1[0] != '?' + && (cmd_entry->flags & ARG_IN3) + && cmd_entry->arg3) + { + + if (interactive) + { + if (prompt) + { + fprintf_flush(fout, "%s: ", cmd_entry->arg3); + } + + if (scanfc(fin, "%s", arg3) < 1) + { + return -1; + } + + p3 = arg3; + } + else + { + retcode = next_word(arg3, argc, argv, 0); + + if (EOF == retcode) + { + fprintf(stderr, + "Invalid arg for command '%s'\n", + cmd_entry->name); + return 1; + } + else if (retcode < 0) + { + return retcode; + } + + p3 = arg3; + } + } + + if (p1 + && p1[0] != '?' + && (cmd_entry->flags & ARG_IN4) + && cmd_entry->arg4) + { + + if (interactive) + { + if (prompt) + { + fprintf_flush(fout, "%s: ", cmd_entry->arg4); + } + + if (scanfc(fin, "%s", arg4) < 1) + { + return -1; + } + + p4 = arg4; + } + else + { + retcode = next_word(arg4, argc, argv, 0); + + if (EOF == retcode) + { + fprintf(stderr, + "Invalid arg for command '%s'\n", + cmd_entry->name); + return 1; + } + else if (retcode < 0) + { + return retcode; + } + + p4 = arg4; + } + } + } + +#ifdef HAVE_LIBREADLINE + + if (interactive && prompt && have_rl) + { + int j, x; + +#ifdef HAVE_READLINE_HISTORY + /* Minimum space for 32+1+128+1+128+1+128+1+128+1+128+1+128+1 = 807 + * chars, so allocate 896 chars cleared to zero for safety. + */ + rp_hist_buf = (char *)calloc(896, sizeof(char)); +#endif + + rl_instream = fin; + rl_outstream = fout; + + rp_getline("\nAmplifier command: "); + + /* EOF (Ctl-D) received on empty input line, bail out gracefully. */ + if (!input_line) + { + fprintf_flush(fout, "\n"); + return 1; + } + + /* Q or q to quit */ + if (!(strncasecmp(input_line, "q", 1))) + { + return 1; + } + + /* '?' for help */ + if (!(strncmp(input_line, "?", 1))) + { + usage_amp(fout); + fflush(fout); + return 0; + } + + /* '#' for comment */ + if (!(strncmp(input_line, "#", 1))) + { + return 0; + } + + /* Blank line entered */ + if (!(strcmp(input_line, ""))) + { + fprintf(fout, "? for help, q to quit.\n"); + fflush(fout); + return 0; + } + + rig_debug(RIG_DEBUG_BUG, "%s: input_line: %s\n", __func__, input_line); + + /* Split input_line on any number of spaces to get the command token + * Tabs are intercepted by readline for completion and a newline + * causes readline to return the typed text. If more than one + * argument is given, it will be parsed out later. + */ + result = strtok(input_line, " "); + + /* parsed_input stores pointers into input_line where the token strings + * start. + */ + if (result) + { + parsed_input[0] = result; + } + else + { + /* Oops! Invoke GDB!! */ + fprintf_flush(fout, "\n"); + return 1; + } + + /* At this point parsed_input contains the typed text of the command + * with surrounding space characters removed. If Readline History is + * available, copy the command string into a history buffer. + */ + + /* Single character command */ + if ((strlen(parsed_input[0]) == 1) && (*parsed_input[0] != '\\')) + { + cmd = *parsed_input[0]; + +#ifdef HAVE_READLINE_HISTORY + + /* Store what is typed, not validated, for history. */ + if (rp_hist_buf) + { + strncpy(rp_hist_buf, parsed_input[0], 1); + } + +#endif + } + /* Test the command token, parsed_input[0] */ + else if ((*parsed_input[0] == '\\') && (strlen(parsed_input[0]) > 1)) + { + char cmd_name[MAXNAMSIZ]; + + /* if there is no terminating '\0' character in the source string, + * srncpy() doesn't add one even if the supplied length is less + * than the destination array. Truncate the source string here. + */ + if (strlen(parsed_input[0] + 1) >= MAXNAMSIZ) + { + *(parsed_input[0] + MAXNAMSIZ) = '\0'; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + strncpy(rp_hist_buf, parsed_input[0], MAXNAMSIZ); + } + +#endif + /* The starting position of the source string is the first + * character past the initial '\'. + */ + snprintf(cmd_name, sizeof(cmd_name), "%s", parsed_input[0] + 1); + + /* Sanity check as valid multiple character commands consist of + * alpha-numeric characters and the underscore ('_') character. + */ + for (j = 0; cmd_name[j] != '\0'; j++) + { + if (!(isalnum((int)cmd_name[j]) || cmd_name[j] == '_')) + { + fprintf(stderr, + "Valid multiple character command names contain alpha-numeric characters plus '_'\n"); + return 0; + } + } + + cmd = parse_arg(cmd_name); + } + /* Single '\' entered, prompt again */ + else if ((*parsed_input[0] == '\\') && (strlen(parsed_input[0]) == 1)) + { + return 0; + } + /* Multiple characters but no leading '\' */ + else + { + fprintf(stderr, "Precede multiple character command names with '\\'\n"); + return 0; + } + + cmd_entry = find_cmd_entry(cmd); + + if (!cmd_entry) + { + if (cmd == '\0') + { + fprintf(stderr, "Command '%s' not found!\n", parsed_input[0]); + } + else + { + fprintf(stderr, "Command '%c' not found!\n", cmd); + } + + return 0; + } + + /* \send_cmd */ + if ((cmd_entry->flags & ARG_IN_LINE) + && (cmd_entry->flags & ARG_IN1) + && cmd_entry->arg1) + { + + /* Check for a non-existent delimiter so as to not break up + * remaining line into separate tokens (spaces OK). + */ + result = strtok(NULL, "\0"); + + if (result) + { + x = 1; + parsed_input[x] = result; + } + else + { + x = 0; + char pmptstr[(strlen(cmd_entry->arg1) + 3)]; + + strcpy(pmptstr, cmd_entry->arg1); + strcat(pmptstr, ": "); + + rp_getline(pmptstr); + + /* Blank line entered */ + if (!(strcmp(input_line, ""))) + { + fprintf(fout, "? for help, q to quit.\n"); + fflush(fout); + return 0; + } + + if (input_line) + { + parsed_input[x] = input_line; + } + else + { + fprintf_flush(fout, "\n"); + return 1; + } + } + + /* The arg1 array size is MAXARGSZ + 1 so truncate it to fit if larger. */ + if (strlen(parsed_input[x]) > MAXARGSZ) + { + parsed_input[x][MAXARGSZ] = '\0'; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + strncat(rp_hist_buf, " ", 2); + strncat(rp_hist_buf, parsed_input[x], MAXARGSZ); + } + +#endif + strcpy(arg1, parsed_input[x]); + p1 = arg1; + } + + /* Normal argument parsing. */ + else if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) + { + result = strtok(NULL, " "); + + if (result) + { + x = 1; + parsed_input[x] = result; + } + else + { + x = 0; + char pmptstr[(strlen(cmd_entry->arg1) + 3)]; + + strcpy(pmptstr, cmd_entry->arg1); + strcat(pmptstr, ": "); + + rp_getline(pmptstr); + + if (!(strcmp(input_line, ""))) + { + fprintf(fout, "? for help, q to quit.\n"); + fflush(fout); + return 0; + } + + result = strtok(input_line, " "); + + if (result) + { + parsed_input[x] = result; + } + else + { + fprintf_flush(fout, "\n"); + return 1; + } + } + + if (strlen(parsed_input[x]) > MAXARGSZ) + { + parsed_input[x][MAXARGSZ] = '\0'; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + strncat(rp_hist_buf, " ", 2); + strncat(rp_hist_buf, parsed_input[x], MAXARGSZ); + } + +#endif + strcpy(arg1, parsed_input[x]); + p1 = arg1; + } + + if (p1 + && p1[0] != '?' + && (cmd_entry->flags & ARG_IN2) + && cmd_entry->arg2) + { + + result = strtok(NULL, " "); + + if (result) + { + x = 2; + parsed_input[x] = result; + } + else + { + x = 0; + char pmptstr[(strlen(cmd_entry->arg2) + 3)]; + + strcpy(pmptstr, cmd_entry->arg2); + strcat(pmptstr, ": "); + + rp_getline(pmptstr); + + if (!(strcmp(input_line, ""))) + { + fprintf(fout, "? for help, q to quit.\n"); + fflush(fout); + return 0; + } + + result = strtok(input_line, " "); + + if (result) + { + parsed_input[x] = result; + } + else + { + fprintf_flush(fout, "\n"); + return 1; + } + } + + if (strlen(parsed_input[x]) > MAXARGSZ) + { + parsed_input[x][MAXARGSZ] = '\0'; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + strncat(rp_hist_buf, " ", 2); + strncat(rp_hist_buf, parsed_input[x], MAXARGSZ); + } + +#endif + strcpy(arg2, parsed_input[x]); + p2 = arg2; + } + + if (p1 + && p1[0] != '?' + && (cmd_entry->flags & ARG_IN3) + && cmd_entry->arg3) + { + + result = strtok(NULL, " "); + + if (result) + { + x = 3; + parsed_input[x] = result; + } + else + { + x = 0; + char pmptstr[(strlen(cmd_entry->arg3) + 3)]; + + strcpy(pmptstr, cmd_entry->arg3); + strcat(pmptstr, ": "); + + rp_getline(pmptstr); + + if (!(strcmp(input_line, ""))) + { + fprintf(fout, "? for help, q to quit.\n"); + fflush(fout); + return 0; + } + + result = strtok(input_line, " "); + + if (result) + { + parsed_input[x] = result; + } + else + { + fprintf_flush(fout, "\n"); + return 1; + } + } + + if (strlen(parsed_input[x]) > MAXARGSZ) + { + parsed_input[x][MAXARGSZ] = '\0'; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + strncat(rp_hist_buf, " ", 2); + strncat(rp_hist_buf, parsed_input[x], MAXARGSZ); + } + +#endif + strcpy(arg3, parsed_input[x]); + p3 = arg3; + } + + if (p1 + && p1[0] != '?' + && (cmd_entry->flags & ARG_IN4) + && cmd_entry->arg4) + { + + result = strtok(NULL, " "); + + if (result) + { + x = 4; + parsed_input[x] = result; + } + else + { + x = 0; + char pmptstr[(strlen(cmd_entry->arg4) + 3)]; + + strcpy(pmptstr, cmd_entry->arg4); + strcat(pmptstr, ": "); + + rp_getline(pmptstr); + + if (!(strcmp(input_line, ""))) + { + fprintf(fout, "? for help, q to quit.\n"); + fflush(fout); + return 0; + } + + result = strtok(input_line, " "); + + if (result) + { + parsed_input[x] = result; + } + else + { + fprintf_flush(fout, "\n"); + return 1; + } + } + + if (strlen(parsed_input[x]) > MAXARGSZ) + { + parsed_input[x][MAXARGSZ] = '\0'; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + strncat(rp_hist_buf, " ", 2); + strncat(rp_hist_buf, parsed_input[x], MAXARGSZ); + } + +#endif + strcpy(arg4, parsed_input[x]); + p4 = arg4; + } + +#ifdef HAVE_READLINE_HISTORY + + if (rp_hist_buf) + { + add_history(rp_hist_buf); + free(rp_hist_buf); + rp_hist_buf = (char *)NULL; + } + +#endif + } + +#endif /* HAVE_LIBREADLINE */ + + + /* + * mutex locking needed because ampctld is multithreaded + * and hamlib is not MT-safe + */ +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&_mutex); +#endif + + if (!prompt) + { + rig_debug(RIG_DEBUG_TRACE, + "ampctl(d): %c '%s' '%s' '%s' '%s'\n", + cmd, + p1 ? p1 : "", + p2 ? p2 : "", + p3 ? p3 : "", + p4 ? p4 : ""); + } + + /* + * Extended Response protocol: output received command name and arguments + * response. + */ + if (interactive && ext_resp && !prompt) + { + char a1[MAXARGSZ + 2]; + char a2[MAXARGSZ + 2]; + char a3[MAXARGSZ + 2]; + char a4[MAXARGSZ + 2]; + + p1 == NULL ? a1[0] = '\0' : snprintf(a1, sizeof(a1), " %s", p1); + p2 == NULL ? a2[0] = '\0' : snprintf(a2, sizeof(a2), " %s", p2); + p3 == NULL ? a3[0] = '\0' : snprintf(a3, sizeof(a3), " %s", p3); + p4 == NULL ? a4[0] = '\0' : snprintf(a4, sizeof(a4), " %s", p4); + + fprintf(fout, "%s:%s%s%s%s%c", cmd_entry->name, a1, a2, a3, a4, resp_sep); + } + + retcode = (*cmd_entry->amp_routine)(my_amp, + fout, + interactive, + cmd_entry, + p1, + p2 ? p2 : "", + p3 ? p3 : "", + p4 ? p4 : "", + p5 ? p5 : "", + p6 ? p6 : ""); + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&_mutex); +#endif + + if (retcode == RIG_EIO) { return retcode; } + + if (retcode != RIG_OK) + { + /* only for ampctld */ + if (interactive && !prompt) + { + fprintf(fout, NETAMPCTL_RET "%d\n", retcode); + ext_resp = 0; + resp_sep = '\n'; + } + else + { + fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode)); + } + } + else + { + /* only for ampctld */ + if (interactive && !prompt) + { + /* netampctl RIG_OK */ + if (!(cmd_entry->flags & ARG_OUT) && !ext_resp) + { + fprintf(fout, NETAMPCTL_RET "0\n"); + } + + /* Extended Response protocol */ + else if (ext_resp && cmd != 0xf0) + { + fprintf(fout, NETAMPCTL_RET "0\n"); + ext_resp = 0; + resp_sep = '\n'; + } + } + } + + fflush(fout); + + return retcode != RIG_OK ? 2 : 0; +} + + + +void version() +{ + printf("ampctl(d), %s\n\n", hamlib_version); + printf("%s\n", hamlib_copyright); +} + + +void usage_amp(FILE *fout) +{ + int i, nbspaces; + + fprintf(fout, "Commands (some may not be available for this amplifier):\n"); + + for (i = 0; test_list[i].cmd != 0; i++) + { + fprintf(fout, + "%c: %-12s(", + isprint(test_list[i].cmd) ? test_list[i].cmd : '?', + test_list[i].name); + + nbspaces = 16; + + if (test_list[i].arg1 && (test_list[i].flags & ARG_IN1)) + { + nbspaces -= fprintf(fout, "%s", test_list[i].arg1); + } + + if (test_list[i].arg2 && (test_list[i].flags & ARG_IN2)) + { + nbspaces -= fprintf(fout, ", %s", test_list[i].arg2); + } + + if (test_list[i].arg3 && (test_list[i].flags & ARG_IN3)) + { + nbspaces -= fprintf(fout, ", %s", test_list[i].arg3); + } + + if (test_list[i].arg4 && (test_list[i].flags & ARG_IN4)) + { + nbspaces -= fprintf(fout, ", %s", test_list[i].arg4); + } + + fprintf(fout, ")\n"); + } + + fprintf(fout, + "\n\nIn interactive mode prefix long command names with '\\', e.g. '\\dump_state'\n\n" + "The special command '-' is used to read further commands from standard input\n" + "Commands and arguments read from standard input must be white space separated,\n" + "comments are allowed, comments start with the # character and continue to the\n" + "end of the line.\n"); +} + + +#if 0 +int print_conf_list(const struct confparams *cfp, rig_ptr_t data) +{ + AMP *amp = (AMP *) data; + int i; + char buf[128] = ""; + + amp_get_conf(amp, cfp->token, buf); + printf("%s: \"%s\"\n" "\tDefault: %s, Value: %s\n", + cfp->name, + cfp->tooltip, + cfp->dflt, + buf); + + switch (cfp->type) + { + case RIG_CONF_NUMERIC: + printf("\tRange: %.1f..%.1f, step %.1f\n", + cfp->u.n.min, + cfp->u.n.max, + cfp->u.n.step); + break; + + case RIG_CONF_COMBO: + if (!cfp->u.c.combostr[0]) + { + break; + } + + printf("\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]); + } + + printf("\n"); + break; + + default: + break; + } + + return 1; /* != 0, we want them all ! */ +} +#endif + +static int hash_model_list(const struct amp_caps *caps, void *data) +{ + + hash_add_model(caps->amp_model, + caps->mfg_name, + caps->model_name, + caps->version, + rig_strstatus(caps->status)); + + return 1; /* !=0, we want them all ! */ +} + +void print_model_list() +{ + struct mod_lst *s; + + for (s = models; s != NULL; s = (struct mod_lst *)(s->hh.next)) + { + printf("%6d %-23s%-24s%-16s%s\n", + s->id, + s->mfg_name, + s->model_name, + s->version, + s->status); + } +} + + +void list_models() +{ + int status; + + amp_load_all_backends(); + + printf(" Amp # Mfg Model Version Status\n"); + status = amp_list_foreach(hash_model_list, NULL); + + if (status != RIG_OK) + { + printf("amp_list_foreach: error = %s \n", rigerror(status)); + exit(2); + } + + hash_sort_by_model_id(); + print_model_list(); + hash_delete_all(); +} + +int set_conf(AMP *my_amp, char *conf_parms) +{ + char *p, *q, *n; + int ret; + + p = conf_parms; + + while (p && *p != '\0') + { + /* FIXME: left hand value of = cannot be null */ + q = strchr(p, '='); + + if (!q) + { + return RIG_EINVAL; + } + + *q++ = '\0'; + n = strchr(q, ','); + + if (n) + { + *n++ = '\0'; + } + + ret = amp_set_conf(my_amp, amp_token_lookup(my_amp, p), q); + + if (ret != RIG_OK) + { + return ret; + } + + p = n; + } + + return RIG_OK; +} + +/* + * static int (f)(AMP *amp, int interactive, const void *arg1, const void *arg2, const void *arg3, const void *arg4) + */ + + +/* 'f' */ +declare_proto_amp(get_freq) +{ + int status; + freq_t freq; + + status = amp_get_freq(amp, &freq); + + if (status != RIG_OK) + { + return status; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg1); /* i.e. "Frequency" */ + } + + fprintf(fout, "%"PRIll"%c", (int64_t)freq, resp_sep); + + return status; +} + +/* 'F' */ +declare_proto_amp(set_freq) +{ + freq_t freq; + + CHKSCN1ARG(sscanf(arg1, "%"SCNfreq, &freq)); + return amp_set_freq(amp, freq); +} + +/* 'l' */ +declare_proto_amp(get_level) +{ + int status; + setting_t level; + value_t val; + + if (!strcmp(arg1, "?")) + { + char s[SPRINTF_MAX_SIZE]; + sprintf_level_amp(s, amp->state.has_get_level); + + fputs(s, fout); + + if (amp->caps->get_ext_level) + { + sprintf_level_ext(s, amp->caps->extlevels); + fputs(s, fout); + } + + fputc('\n', fout); + return RIG_OK; + } + + level = amp_parse_level(arg1); + + if (!amp_has_get_level(amp, level)) + { + const struct confparams *cfp; + + cfp = amp_ext_lookup(amp, arg1); + + if (!cfp) + { + return -RIG_EINVAL; /* no such parameter */ + } + + status = amp_get_ext_level(amp, cfp->token, &val); + + if (status != RIG_OK) + { + return status; + } + + if (interactive && prompt) + { + fprintf(fout, "%s: ", cmd->arg2); + } +printf("cfp->type=%d\n",cfp->type); + switch (cfp->type) + { + case RIG_CONF_BUTTON: + /* there's no sense in retrieving value of stateless button */ + return -RIG_EINVAL; + + case RIG_CONF_CHECKBUTTON: + case RIG_CONF_COMBO: + fprintf(fout, "%d\n", val.i); + break; + + case RIG_CONF_NUMERIC: + fprintf(fout, "%f\n", val.f); + break; + + case RIG_CONF_STRING: + fprintf(fout, "%s\n", val.s); + break; + + default: + return -RIG_ECONF; + } + + return status; + } + + status = amp_get_level(amp, level, &val); + + if (status != RIG_OK) + { + return status; + } + + if (interactive && prompt) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + if (AMP_LEVEL_IS_FLOAT(level)) + { + fprintf(fout, "%f\n", val.f); + } + else if (AMP_LEVEL_IS_STRING(level)) + { + fprintf(fout, "%s\n", val.s); + } + else + { + fprintf(fout, "%d\n", val.i); + } + + return status; +} + +/* 'R' */ +declare_proto_amp(reset) +{ + amp_reset_t reset; + + CHKSCN1ARG(sscanf(arg1, "%d", (int *)&reset)); + return amp_reset(amp, reset); +} + + +/* '_' */ +declare_proto_amp(get_info) +{ + const char *s; + + s = amp_get_info(amp); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg1); + } + + fprintf(fout, "%s%c", s ? s : "None", resp_sep); + + return RIG_OK; +} + +/* '1' */ +declare_proto_amp(dump_caps) +{ + dumpcaps_amp(amp, fout); + + return RIG_OK; +} + + +/* For ampctld internal use + * '0x8f' + */ +declare_proto_amp(dump_state) +{ + /* + * - Protocol version + */ +#define AMPCTLD_PROT_VER 0 + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "ampctld Protocol Ver: "); + } + + fprintf(fout, "%d%c", AMPCTLD_PROT_VER, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "Amplifier Model: "); + + switch (amp->caps->amp_model) + { + case AMP_MODEL_DUMMY: + fprintf(fout, "dummy\n"); + break; + + case AMP_MODEL_ELECRAFT_KPA1500: + fprintf(fout, "Elecraft KPA1500\n"); + break; + + default: + fprintf(fout, "unknown=%02x\n", amp->caps->amp_model); + break; + } + } + + return RIG_OK; +} + +/* '0x87' */ +declare_proto_amp(set_powerstat) +{ + int stat; + + CHKSCN1ARG(sscanf(arg1, "%d", &stat)); + return amp_set_powerstat(amp, (powerstat_t) stat); +} + + +/* '0x88' */ +declare_proto_amp(get_powerstat) +{ + int status; + powerstat_t stat; + + status = amp_get_powerstat(amp, &stat); + + if (status != RIG_OK) + { + return status; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg1); + } + + fprintf(fout, "%d\n", stat); + + return status; +} + +/* + * Special debugging purpose send command display reply until there's a + * timeout. + * + * 'w' + */ +declare_proto_amp(send_cmd) +{ + int retval; + struct amp_state *rs; + int backend_num, cmd_len; +#define BUFSZ 128 + char bufcmd[BUFSZ]; + char buf[BUFSZ]; + char eom_buf[4] = { 0xa, 0xd, 0, 0 }; + + /* + * binary protocols enter values as \0xZZ\0xYY.. + * + * Rem: no binary protocol for amplifier as of now + */ + backend_num = AMP_BACKEND_NUM(amp->caps->amp_model); + + if (send_cmd_term == -1 || backend_num == -1) + { + const char *p = arg1, *pp = NULL; + int i; + + for (i = 0; i < BUFSZ - 1 && p != pp; i++) + { + pp = p + 1; + bufcmd[i] = strtol(p + 1, (char **) &p, 0); + } + + /* must save length to allow 0x00 to be sent as part of a command */ + cmd_len = i - 1; + + /* no End Of Message chars */ + eom_buf[0] = '\0'; + } + else + { + /* text protocol */ + strncpy(bufcmd, arg1, BUFSZ); + bufcmd[BUFSZ - 2] = '\0'; + + cmd_len = strlen(bufcmd); + + /* Automatic termination char */ + if (send_cmd_term != 0) + { + bufcmd[cmd_len++] = send_cmd_term; + } + + eom_buf[2] = send_cmd_term; + } + + rs = &->state; + + serial_flush(&rs->ampport); + + retval = write_block(&rs->ampport, bufcmd, cmd_len); + + if (retval != RIG_OK) + { + return retval; + } + + if (interactive && prompt) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + do + { + /* + * assumes CR or LF is end of line char + * for all ascii protocols + */ + retval = read_string(&rs->ampport, buf, BUFSZ, eom_buf, strlen(eom_buf)); + + if (retval < 0) + { + break; + } + + if (retval < BUFSZ) + { + buf[retval] = '\0'; + } + else + { + buf[BUFSZ - 1] = '\0'; + } + + fprintf(fout, "%s\n", buf); + + } + while (retval > 0); + + if (retval > 0 || retval == -RIG_ETIMEOUT) + { + retval = RIG_OK; + } + + return retval; +} + + +/* 'L' */ +/* +declare_proto_amp(lonlat2loc) +{ + unsigned char loc[MAXARGSZ + 1]; + double lat, lon; + int err, pair; + + CHKSCN1ARG(sscanf(arg1, "%lf", &lon)); + CHKSCN1ARG(sscanf(arg2, "%lf", &lat)); + CHKSCN1ARG(sscanf(arg3, "%d", &pair)); + + pair /= 2; + + err = longlat2locator(lon, lat, (char *)&loc, pair); + + if (err != RIG_OK) + { + return err; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg4); + } + + fprintf(fout, "%s%c", loc, resp_sep); + + return err; +} +*/ + + +/* 'l' */ +/* +declare_proto_amp(loc2lonlat) +{ + unsigned char loc[MAXARGSZ + 1]; + double lat, lon; + int status; + + CHKSCN1ARG(sscanf(arg1, "%s", (char *)&loc)); + + status = locator2longlat(&lon, &lat, (const char *)loc); + + if (status != RIG_OK) + { + return status; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + fprintf(fout, "%f%c", lon, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg3); + } + + fprintf(fout, "%f%c", lat, resp_sep); + + return status; +} +*/ + + +/* 'D' */ +/* +declare_proto_amp(d_m_s2dec) +{ + int deg, min, sw; + double sec, dec_deg; + + CHKSCN1ARG(sscanf(arg1, "%d", °)); + CHKSCN1ARG(sscanf(arg2, "%d", &min)); + CHKSCN1ARG(sscanf(arg3, "%lf", &sec)); + CHKSCN1ARG(sscanf(arg4, "%d", &sw)); + + dec_deg = dms2dec(deg, min, sec, sw); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg5); + } + + fprintf(fout, "%lf%c", dec_deg, resp_sep); + + return RIG_OK; +} +*/ + + +/* 'd' */ +/* +declare_proto_amp(dec2d_m_s) +{ + int deg, min, sw, err; + double sec, dec_deg; + + CHKSCN1ARG(sscanf(arg1, "%lf", &dec_deg)); + + err = dec2dms(dec_deg, °, &min, &sec, &sw); + + if (err != RIG_OK) + { + return err; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + fprintf(fout, "%d%c", deg, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg3); + } + + fprintf(fout, "%d%c", min, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg4); + } + + fprintf(fout, "%lf%c", sec, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg5); + } + + fprintf(fout, "%d%c", sw, resp_sep); + + return err; +} +*/ + + +/* 'E' */ +/* +declare_proto_amp(d_mm2dec) +{ + int deg, sw; + double dec_deg, min; + + CHKSCN1ARG(sscanf(arg1, "%d", °)); + CHKSCN1ARG(sscanf(arg2, "%lf", &min)); + CHKSCN1ARG(sscanf(arg3, "%d", &sw)); + + dec_deg = dmmm2dec(deg, min, sw); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg4); + } + + fprintf(fout, "%lf%c", dec_deg, resp_sep); + + return RIG_OK; +} +*/ + + +/* 'e' */ +/* +declare_proto_amp(dec2d_mm) +{ + int deg, sw, err; + double min, dec_deg; + + CHKSCN1ARG(sscanf(arg1, "%lf", &dec_deg)); + + err = dec2dmmm(dec_deg, °, &min, &sw); + + if (err != RIG_OK) + { + return err; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + fprintf(fout, "%d%c", deg, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg3); + } + + fprintf(fout, "%lf%c", min, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg4); + } + + fprintf(fout, "%d%c", sw, resp_sep); + + return err; +} +*/ + + +/* 'B' */ +/* +declare_proto_amp(coord2qrb) +{ + double lon1, lat1, lon2, lat2, dist, az; + int err; + + CHKSCN1ARG(sscanf(arg1, "%lf", &lon1)); + CHKSCN1ARG(sscanf(arg2, "%lf", &lat1)); + CHKSCN1ARG(sscanf(arg3, "%lf", &lon2)); + CHKSCN1ARG(sscanf(arg4, "%lf", &lat2)); + + err = qrb(lon1, lat1, lon2, lat2, &dist, &az); + + if (err != RIG_OK) + { + return err; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg5); + } + + fprintf(fout, "%lf%c", dist, resp_sep); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg6); + } + + fprintf(fout, "%lf%c", az, resp_sep); + + return err; +} +*/ + + +/* 'A' */ +/* +declare_proto_amp(az_sp2az_lp) +{ + double az_sp, az_lp; + + CHKSCN1ARG(sscanf(arg1, "%lf", &az_sp)); + + az_lp = azimuth_long_path(az_sp); + + if (az_lp < 0) + { + return -RIG_EINVAL; + } + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + fprintf(fout, "%lf%c", az_lp, resp_sep); + + return RIG_OK; +} +*/ + + +/* 'a' */ +/* +declare_proto_amp(dist_sp2dist_lp) +{ + double dist_sp, dist_lp; + + CHKSCN1ARG(sscanf(arg1, "%lf", &dist_sp)); + + dist_lp = distance_long_path(dist_sp); + + if ((interactive && prompt) || (interactive && !prompt && ext_resp)) + { + fprintf(fout, "%s: ", cmd->arg2); + } + + fprintf(fout, "%lf%c", dist_lp, resp_sep); + + return RIG_OK; +} +*/ + + +/* '0x8c'--pause processing */ +/* +declare_proto_amp(pause) +{ + unsigned seconds; + CHKSCN1ARG(sscanf(arg1, "%u", &seconds)); + sleep(seconds); + return RIG_OK; +} +*/ diff --git a/tests/ampctl_parse.h b/tests/ampctl_parse.h new file mode 100644 index 000000000..cbf765eba --- /dev/null +++ b/tests/ampctl_parse.h @@ -0,0 +1,50 @@ +/* + * ampctl_parse.h - (C) Stephane Fillod 2000-2008 + * Derived from rotctl_parse.h - (C) Michael Black 2019 + * + * This program test/control an amplifier using Hamlib. + * It takes commands in interactive mode as well as + * from command line options. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AMPCTL_PARSE_H +#define AMPCTL_PARSE_H + +#include +#include + +/* + * external prototype + */ + +int dumpcaps_amp(AMP *, FILE *); + + +/* + * Prototypes + */ +void usage_amp(FILE *); +void version(); +void list_models(); +int print_conf_list(const struct confparams *cfp, rig_ptr_t data); +int set_conf(AMP *my_amp, char *conf_parms); + +int ampctl_parse(AMP *my_amp, FILE *fin, FILE *fout, char *argv[], int argc); + +#endif /* AMPCTL_PARSE_H */ diff --git a/tests/ampctld.c b/tests/ampctld.c new file mode 100644 index 000000000..23d2f26bc --- /dev/null +++ b/tests/ampctld.c @@ -0,0 +1,715 @@ +/* + * ampctld.c - (C) Stephane Fillod 2000-2011 + * (C) Nate Bargmann 2010,2011,2012,2013 + * + * This program test/control an amplifier using Hamlib. + * It takes commands from network connection. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include /* See NOTES */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#elif HAVE_WS2TCPIP_H +# include +# include +# if defined(HAVE_WSPIAPI_H) +# include +# endif +#endif +#ifdef HAVE_NETDB_H +# include +#endif + +#ifdef HAVE_PTHREAD +# include +#endif + +#include +#include "misc.h" + +#include "ampctl_parse.h" + +struct handle_data +{ + AMP *amp; + int sock; + struct sockaddr_storage cli_addr; + socklen_t clilen; +}; + +void *handle_socket(void *arg); + +void usage(); + +/* + * Reminder: when adding long options, + * keep up to date SHORT_OPTIONS, usage()'s output and man page. thanks. + * NB: do NOT use -W since it's reserved by POSIX. + * TODO: add an option to read from a file + */ +#define SHORT_OPTIONS "m:r:s:C:t:T:LuvhVlZ" +static struct option long_options[] = +{ + {"model", 1, 0, 'm'}, + {"amp-file", 1, 0, 'r'}, + {"serial-speed", 1, 0, 's'}, + {"port", 1, 0, 't'}, + {"listen-addr", 1, 0, 'T'}, + {"list", 0, 0, 'l'}, + {"set-conf", 1, 0, 'C'}, + {"show-conf", 0, 0, 'L'}, + {"dump-caps", 0, 0, 'u'}, + {"debug-time-stamps", 0, 0, 'Z'}, + {"verbose", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0} +}; + +int interactive = 1; /* no cmd because of daemon */ +int prompt = 0 ; /* Daemon mode for rigparse return string */ + +const char *portno = "4533"; +const char *src_addr = NULL; /* INADDR_ANY */ + +char send_cmd_term = '\r'; /* send_cmd termination char */ + +#define MAXCONFLEN 128 + + +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, + // Default language + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL)) + { + + rig_debug(lvl, "%s: Network error %d: %s\n", msg, e, 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[]) +{ + AMP *my_amp; /* handle to amp (instance) */ + amp_model_t my_model = AMP_MODEL_DUMMY; + + int retcode; /* generic return code from functions */ + + int verbose = 0; + int show_conf = 0; + int dump_caps_opt = 0; + const char *amp_file = NULL; + int serial_rate = 0; + char conf_parms[MAXCONFLEN] = ""; + + struct addrinfo hints, *result, *saved_result; + int sock_listen; + int reuseaddr = 1; + int sockopt; + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + +#ifdef HAVE_PTHREAD + pthread_t thread; + pthread_attr_t attr; +#endif + struct handle_data *arg; + + while (1) + { + int c; + int option_index = 0; + + c = getopt_long(argc, argv, SHORT_OPTIONS, long_options, &option_index); + + if (c == -1) + { + break; + } + + switch (c) + { + case 'h': + usage(); + exit(0); + + case 'V': + version(); + exit(0); + + case 'm': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + my_model = atoi(optarg); + break; + + case 'r': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + amp_file = optarg; + break; + + case 's': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + serial_rate = atoi(optarg); + break; + + case 'C': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + if (*conf_parms != '\0') + { + strcat(conf_parms, ","); + } + + strncat(conf_parms, optarg, MAXCONFLEN - strlen(conf_parms)); + break; + + case 't': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + portno = optarg; + break; + + case 'T': + if (!optarg) + { + usage(); /* wrong arg count */ + exit(1); + } + + src_addr = optarg; + break; + + case 'v': + verbose++; + break; + + case 'L': + show_conf++; + break; + + case 'l': + list_models(); + exit(0); + + case 'u': + dump_caps_opt++; + break; + + case 'Z': + rig_set_debug_time_stamp(1); + break; + + default: + usage(); /* unknown option? */ + exit(1); + } + } + + rig_set_debug(verbose); + + rig_debug(RIG_DEBUG_VERBOSE, "ampctld, %s\n", hamlib_version); + rig_debug(RIG_DEBUG_VERBOSE, + "Report bugs to \n\n"); + + my_amp = amp_init(my_model); + + if (!my_amp) + { + fprintf(stderr, + "Unknown amp num %d, or initialization error.\n", + my_model); + + fprintf(stderr, "Please check with --list option.\n"); + exit(2); + } + +#if 0 + retcode = set_conf(my_amp, conf_parms); + + if (retcode != RIG_OK) + { + fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode)); + exit(2); + } + +#endif + + if (amp_file) + { + strncpy(my_amp->state.ampport.pathname, amp_file, FILPATHLEN - 1); + } + + /* FIXME: bound checking and port type == serial */ + if (serial_rate != 0) + { + my_amp->state.ampport.parm.serial.rate = serial_rate; + } + +#if 0 + + /* + * print out conf parameters + */ + if (show_conf) + { + amp_token_foreach(my_amp, print_conf_list, (rig_ptr_t)my_amp); + } + +#endif + + /* + * Print out conf parameters, and exits immediately as we may be + * interested only in only caps, and rig_open may fail. + */ + if (dump_caps_opt) + { + dumpcaps_amp(my_amp, stdout); + amp_cleanup(my_amp); /* if you care about memory */ + exit(0); + } + + retcode = amp_open(my_amp); + + if (retcode != RIG_OK) + { + fprintf(stderr, "amp_open: error = %s \n", rigerror(retcode)); + exit(2); + } + + if (verbose > 0) + { + printf("Opened amp model %d, '%s'\n", + my_amp->caps->amp_model, + my_amp->caps->model_name); + } + + rig_debug(RIG_DEBUG_VERBOSE, + "Backend version: %s, Status: %s\n", + my_amp->caps->version, + rig_strstatus(my_amp->caps->status)); + +#ifdef __MINGW32__ +# ifndef SO_OPENTYPE +# define SO_OPENTYPE 0x7008 +# endif +# ifndef SO_SYNCHRONOUS_NONALERT +# define SO_SYNCHRONOUS_NONALERT 0x20 +# endif +# ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +# endif + + WSADATA wsadata; + + if (WSAStartup(MAKEWORD(1, 1), &wsadata) == SOCKET_ERROR) + { + fprintf(stderr, "WSAStartup socket error\n"); + exit(1); + } + + sockopt = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, + SOL_SOCKET, + SO_OPENTYPE, + (char *)&sockopt, + sizeof(sockopt)); +#endif + + /* + * Prepare listening socket + */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* TCP socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + + retcode = getaddrinfo(src_addr, portno, &hints, &result); + + if (retcode != 0) + { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retcode)); + exit(2); + } + + saved_result = result; + + do + { + sock_listen = socket(result->ai_family, + result->ai_socktype, + result->ai_protocol); + + if (sock_listen < 0) + { + handle_error(RIG_DEBUG_ERR, "socket"); + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + + if (setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, + (char *)&reuseaddr, sizeof(reuseaddr)) < 0) + { + + handle_error(RIG_DEBUG_ERR, "setsockopt"); + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + +#ifdef IPV6_V6ONLY + + if (AF_INET6 == result->ai_family) + { + /* allow IPv4 mapped to IPv6 clients, MS & BSD default this + to 1 i.e. disallowed */ + sockopt = 0; + + if (setsockopt(sock_listen, + IPPROTO_IPV6, + IPV6_V6ONLY, + (char *)&sockopt, + sizeof(sockopt)) + < 0) + { + + handle_error(RIG_DEBUG_ERR, "setsockopt"); + freeaddrinfo(saved_result); /* No longer needed */ + exit(1); + } + } + +#endif + + if (0 == bind(sock_listen, result->ai_addr, result->ai_addrlen)) + { + break; + } + + handle_error(RIG_DEBUG_WARN, "binding failed (trying next interface)"); +#ifdef __MINGW32__ + closesocket(sock_listen); +#else + close(sock_listen); +#endif + } + while ((result = result->ai_next) != NULL); + + freeaddrinfo(saved_result); /* No longer needed */ + + if (NULL == result) + { + rig_debug(RIG_DEBUG_ERR, "bind error - no available interface\n"); + exit(1); + } + + if (listen(sock_listen, 4) < 0) + { + handle_error(RIG_DEBUG_ERR, "listening"); + exit(1); + } + +#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. */ +#if HAVE_SIGACTION + struct sigaction act; + 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"); + } + +#elif HAVE_SIGNAL + + if (SIG_ERR == signal(SIGPIPE, SIG_IGN)) + { + handle_error(RIG_DEBUG_ERR, "signal"); + } + +#endif +#endif + + /* + * main loop accepting connections + */ + do + { + arg = malloc(sizeof(struct handle_data)); + + if (!arg) + { + rig_debug(RIG_DEBUG_ERR, "malloc: %s\n", strerror(errno)); + exit(1); + } + + arg->amp = my_amp; + arg->clilen = sizeof(arg->cli_addr); + arg->sock = accept(sock_listen, + (struct sockaddr *) &arg->cli_addr, + &arg->clilen); + + if (arg->sock < 0) + { + handle_error(RIG_DEBUG_ERR, "accept"); + break; + } + + if ((retcode = getnameinfo((struct sockaddr const *)&arg->cli_addr, + arg->clilen, + host, + sizeof(host), + serv, + sizeof(serv), + NI_NOFQDN)) + < 0) + { + + rig_debug(RIG_DEBUG_WARN, + "Peer lookup error: %s", + gai_strerror(retcode)); + } + + rig_debug(RIG_DEBUG_VERBOSE, + "Connection opened from %s:%s\n", + host, + serv); + +#ifdef HAVE_PTHREAD + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + retcode = pthread_create(&thread, &attr, handle_socket, arg); + + if (retcode != 0) + { + rig_debug(RIG_DEBUG_ERR, "pthread_create: %s\n", strerror(retcode)); + break; + } + +#else + handle_socket(arg); +#endif + } + while (retcode == 0); + + amp_close(my_amp); /* close port */ + amp_cleanup(my_amp); /* if you care about memory */ + +#ifdef __MINGW32__ + WSACleanup(); +#endif + + return 0; +} + + +/* + * This is the function run by the threads + */ +void *handle_socket(void *arg) +{ + struct handle_data *handle_data_arg = (struct handle_data *)arg; + FILE *fsockin; + FILE *fsockout; + int retcode; + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + +#ifdef __MINGW32__ + int sock_osfhandle = _open_osfhandle(handle_data_arg->sock, _O_RDONLY); + + if (sock_osfhandle == -1) + { + rig_debug(RIG_DEBUG_ERR, "_open_osfhandle error: %s\n", strerror(errno)); + goto handle_exit; + } + + fsockin = _fdopen(sock_osfhandle, "rb"); +#else + fsockin = fdopen(handle_data_arg->sock, "rb"); +#endif + + if (!fsockin) + { + rig_debug(RIG_DEBUG_ERR, "fdopen in: %s\n", strerror(errno)); + goto handle_exit; + } + +#ifdef __MINGW32__ + fsockout = _fdopen(sock_osfhandle, "wb"); +#else + fsockout = fdopen(handle_data_arg->sock, "wb"); +#endif + + if (!fsockout) + { + rig_debug(RIG_DEBUG_ERR, "fdopen out: %s\n", strerror(errno)); + fclose(fsockin); + goto handle_exit; + } + + do + { + retcode = ampctl_parse(handle_data_arg->amp, fsockin, fsockout, NULL, 0); + + if (ferror(fsockin) || ferror(fsockout)) + { + retcode = 1; + } + } + while (retcode == 0 || retcode == 2); + + if ((retcode = getnameinfo((struct sockaddr const *)&handle_data_arg->cli_addr, + handle_data_arg->clilen, + host, + sizeof(host), + serv, + sizeof(serv), + NI_NOFQDN)) + < 0) + { + + rig_debug(RIG_DEBUG_WARN, + "Peer lookup error: %s", + gai_strerror(retcode)); + } + + rig_debug(RIG_DEBUG_VERBOSE, + "Connection closed from %s:%s\n", + host, + serv); + + fclose(fsockin); +#ifndef __MINGW32__ + fclose(fsockout); +#endif + +handle_exit: +#ifdef __MINGW32__ + closesocket(handle_data_arg->sock); +#else + close(handle_data_arg->sock); +#endif + free(arg); + +#ifdef HAVE_PTHREAD + pthread_exit(NULL); +#endif + return NULL; +} + + +void usage() +{ + printf("Usage: ampctld [OPTION]... [COMMAND]...\n" + "Daemon serving COMMANDs to a connected amplifier.\n\n"); + + printf( + " -m, --model=ID select amplifier model number. See model list\n" + " -r, --amp-file=DEVICE set device of the amplifier to operate on\n" + " -s, --serial-speed=BAUD set serial speed of the serial port\n" + " -t, --port=NUM set TCP listening port, default %s\n" + " -T, --listen-addr=IPADDR set listening IP address, default ANY\n" + " -C, --set-conf=PARM=VAL set config parameters\n" + " -L, --show-conf list all config parameters\n" + " -l, --list list all model numbers and exit\n" + " -u, --dump-caps dump capabilities and exit\n" + " -v, --verbose set verbose mode, cumulative\n" + " -Z, --debug-time-stamps enable time stamps for debug messages\n" + " -h, --help display this help and exit\n" + " -V, --version output version information and exit\n\n", + portno); + + usage_amp(stdout); + + printf("\nReport bugs to .\n"); +} diff --git a/tests/dumpcaps_amp.c b/tests/dumpcaps_amp.c new file mode 100644 index 000000000..0f2f4fe83 --- /dev/null +++ b/tests/dumpcaps_amp.c @@ -0,0 +1,155 @@ +/* + * dumpcaps_amp.c - Copyright (C) 2000-2012 Stephane Fillod + * This programs dumps the capabilities of a backend rig. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include "misc.h" + +#include "sprintflst.h" +#include "ampctl_parse.h" + + +/* + * the amp may be in amp_init state, but not openned + */ +int dumpcaps_amp(AMP *amp, FILE *fout) +{ + const struct amp_caps *caps; + int backend_warnings = 0; + static char prntbuf[1024]; + + if (!amp || !amp->caps) + { + return -RIG_EINVAL; + } + + caps = amp->caps; + + fprintf(fout, "Caps dump for model:\t%d\n", caps->amp_model); + fprintf(fout, "Model name:\t\t%s\n", caps->model_name); + fprintf(fout, "Mfg name:\t\t%s\n", caps->mfg_name); + fprintf(fout, "Backend version:\t%s\n", caps->version); + fprintf(fout, "Backend copyright:\t%s\n", caps->copyright); + fprintf(fout, "Backend status:\t\t%s\n", rig_strstatus(caps->status)); + fprintf(fout, "Amp type:\t\t"); + + switch (caps->amp_type & AMP_TYPE_MASK) + { + case AMP_TYPE_OTHER: + fprintf(fout, "Other\n"); + break; + + default: + fprintf(fout, "Unknown type=%d\n", caps->amp_type); + backend_warnings++; + } + + fprintf(fout, "Port type:\t\t"); + + switch (caps->port_type) + { + case RIG_PORT_SERIAL: + fprintf(fout, "RS-232\n"); + fprintf(fout, + "Serial speed:\t\t%d..%d bauds, %d%c%d%s\n", + caps->serial_rate_min, + caps->serial_rate_max, + caps->serial_data_bits, + caps->serial_parity == RIG_PARITY_NONE ? 'N' : + caps->serial_parity == RIG_PARITY_ODD ? 'O' : + caps->serial_parity == RIG_PARITY_EVEN ? 'E' : + caps->serial_parity == RIG_PARITY_MARK ? 'M' : 'S', + caps->serial_stop_bits, + caps->serial_handshake == RIG_HANDSHAKE_NONE ? "" : + (caps->serial_handshake == RIG_HANDSHAKE_XONXOFF ? " XONXOFF" : " CTS/RTS") + ); + break; + + case RIG_PORT_PARALLEL: + fprintf(fout, "Parallel\n"); + break; + + case RIG_PORT_DEVICE: + fprintf(fout, "Device driver\n"); + break; + + case RIG_PORT_USB: + fprintf(fout, "USB\n"); + break; + + case RIG_PORT_NETWORK: + fprintf(fout, "Network link\n"); + break; + + case RIG_PORT_UDP_NETWORK: + fprintf(fout, "UDP Network link\n"); + break; + + case RIG_PORT_NONE: + fprintf(fout, "None\n"); + break; + + default: + fprintf(fout, "Unknown\n"); + backend_warnings++; + } + + fprintf(fout, + "Write delay:\t\t%dmS, timeout %dmS, %d retr%s\n", + caps->write_delay, + caps->timeout, caps->retry, + (caps->retry == 1) ? "y" : "ies"); + + fprintf(fout, + "Post Write delay:\t%dmS\n", + caps->post_write_delay); + + fprintf(fout, "Has priv data:\t\t%c\n", caps->priv != NULL ? 'Y' : 'N'); + + sprintf_level_amp(prntbuf, caps->has_get_level); + fprintf(fout, "Get level: %s\n", prntbuf); + + /* + * Status is either 'Y'es, 'E'mulated, 'N'o + * + * TODO: keep me up-to-date with API call list! + */ + fprintf(fout, "Has Init:\t\t%c\n", caps->amp_init != NULL ? 'Y' : 'N'); + fprintf(fout, "Has Cleanup:\t\t%c\n", caps->amp_cleanup != NULL ? 'Y' : 'N'); + fprintf(fout, "Has Open:\t\t%c\n", caps->amp_open != NULL ? 'Y' : 'N'); + fprintf(fout, "Has Close:\t\t%c\n", caps->amp_close != NULL ? 'Y' : 'N'); + fprintf(fout, "Can set Conf:\t\t%c\n", caps->set_conf != NULL ? 'Y' : 'N'); + fprintf(fout, "Can get Conf:\t\t%c\n", caps->get_conf != NULL ? 'Y' : 'N'); + + fprintf(fout, "Can Reset:\t\t%c\n", caps->reset != NULL ? 'Y' : 'N'); + fprintf(fout, "Can get Info:\t\t%c\n", caps->get_info != NULL ? 'Y' : 'N'); + + fprintf(fout, "\nOverall backend warnings: %d\n", backend_warnings); + + return backend_warnings; +} diff --git a/tests/sprintflst.c b/tests/sprintflst.c index 256277d5f..53766eda3 100644 --- a/tests/sprintflst.c +++ b/tests/sprintflst.c @@ -33,6 +33,7 @@ #include #include +#include #include "sprintflst.h" #include "misc.h" @@ -187,6 +188,34 @@ int sprintf_level(char *str, setting_t level) return len; } +int sprintf_level_amp(char *str, setting_t level) +{ + int i, len = 0; + + *str = '\0'; + + if (level == AMP_LEVEL_NONE) + { + return 0; + } + + for (i = 0; i < RIG_SETTING_MAX; i++) + { + const char *ms = amp_strlevel(level & rig_idx2setting(i)); + + if (!ms || !ms[0]) + { + continue; /* unknown, FIXME! */ + } + + strcat(str, ms); + strcat(str, " "); + len += strlen(ms) + 1; + } + + return len; +} + int sprintf_level_ext(char *str, const struct confparams *extlevels) { diff --git a/tests/sprintflst.h b/tests/sprintflst.h index 4b110c09d..f0a0c95e4 100644 --- a/tests/sprintflst.h +++ b/tests/sprintflst.h @@ -32,6 +32,7 @@ extern int sprintf_mode(char *str, rmode_t); extern int sprintf_vfo(char *str, vfo_t); extern int sprintf_func(char *str, setting_t); extern int sprintf_level(char *str, setting_t); +extern int sprintf_level_amp(char *str, setting_t); extern int sprintf_level_ext(char *str, const struct confparams *); extern int sprintf_level_gran(char *str, setting_t, const gran_t gran[]); extern int sprintf_parm(char *str, setting_t);