From 50a14bb08fdcccc59fc1f7cf803cd6866ee369f3 Mon Sep 17 00:00:00 2001 From: Mike Black W9MDB Date: Fri, 20 Jan 2023 12:47:52 -0600 Subject: [PATCH] Add ACLog rig -- should work with WSJT_X and Fake It mode -- mode setting is a bit odd though might need None for mode in WSJT-X and manually set rig --- include/hamlib/riglist.h | 32 +- rigs/dummy/Makefile.am | 2 +- rigs/dummy/aclog.c | 994 +++++++++++++++++++++++++++++++++++++++ rigs/dummy/dummy.c | 1 + rigs/dummy/dummy.h | 1 + 5 files changed, 998 insertions(+), 32 deletions(-) create mode 100644 rigs/dummy/aclog.c diff --git a/include/hamlib/riglist.h b/include/hamlib/riglist.h index 9acb9a22f..6d730595d 100644 --- a/include/hamlib/riglist.h +++ b/include/hamlib/riglist.h @@ -71,7 +71,7 @@ #define RIG_MODEL_TRXMANAGER_RIG RIG_MAKE_MODEL(RIG_DUMMY, 5) #define RIG_MODEL_DUMMY_NOVFO RIG_MAKE_MODEL(RIG_DUMMY, 6) #define RIG_MODEL_TCI1X RIG_MAKE_MODEL(RIG_DUMMY, 7) - +#define RIG_MODEL_ACLOG RIG_MAKE_MODEL(RIG_DUMMY, 8) /* * Yaesu @@ -128,7 +128,6 @@ #define RIG_MODEL_FT990UNI RIG_MAKE_MODEL(RIG_YAESU, 48) #define RIG_MODEL_FT710 RIG_MAKE_MODEL(RIG_YAESU, 49) - /* * Kenwood */ @@ -274,7 +273,6 @@ #define RIG_MODEL_G90 RIG_MAKE_MODEL(RIG_ICOM, 88) /* Xiegu G90 */ #define RIG_MODEL_X5105 RIG_MAKE_MODEL(RIG_ICOM, 89) /* Xiegu X5105 -- G90 compatible */ - /* * Optoelectronics (CI-V) */ @@ -283,7 +281,6 @@ #define RIG_MODEL_OS535 RIG_MAKE_MODEL(RIG_ICOM, 52) #define RIG_MODEL_OS456 RIG_MAKE_MODEL(RIG_ICOM, 53) - /* * TenTec (CI-V) */ @@ -292,7 +289,6 @@ #define RIG_MODEL_PARAGON2 RIG_MAKE_MODEL(RIG_ICOM, 59) #define RIG_MODEL_DELTAII RIG_MAKE_MODEL(RIG_ICOM, 64) - /* * Icom PCR */ @@ -303,7 +299,6 @@ #define RIG_MODEL_PCR1500 RIG_MAKE_MODEL(RIG_PCR, 3) #define RIG_MODEL_PCR2500 RIG_MAKE_MODEL(RIG_PCR, 4) - /* * AOR */ @@ -326,7 +321,6 @@ #define RIG_MODEL_AR7030P RIG_MAKE_MODEL(RIG_AOR, 15) #define RIG_MODEL_SR2200 RIG_MAKE_MODEL(RIG_AOR, 16) - /* * JRC */ @@ -340,7 +334,6 @@ #define RIG_MODEL_NRD535 RIG_MAKE_MODEL(RIG_JRC, 6) #define RIG_MODEL_NRD545 RIG_MAKE_MODEL(RIG_JRC, 7) - /* * Radio Shack * Actually, they might be either Icom or Uniden. TBC --SF @@ -354,7 +347,6 @@ #define RIG_MODEL_RS2042 RIG_MAKE_MODEL(RIG_RADIOSHACK, 5) /* w/ OptoElectronics OS435 Board */ #define RIG_MODEL_RS2041 RIG_MAKE_MODEL(RIG_RADIOSHACK, 6) /* PRO-2041 */ - /* * Uniden */ @@ -373,7 +365,6 @@ #define RIG_MODEL_BCD996T RIG_MAKE_MODEL(RIG_UNIDEN, 11) #define RIG_MODEL_BC898 RIG_MAKE_MODEL(RIG_UNIDEN, 12) - /* * Drake */ @@ -383,7 +374,6 @@ #define RIG_MODEL_DKR8A RIG_MAKE_MODEL(RIG_DRAKE, 2) #define RIG_MODEL_DKR8B RIG_MAKE_MODEL(RIG_DRAKE, 3) - /* * Lowe */ @@ -394,7 +384,6 @@ #define RIG_MODEL_HF250 RIG_MAKE_MODEL(RIG_LOWE, 3) #define RIG_MODEL_HF235 RIG_MAKE_MODEL(RIG_LOWE, 4) - /* * Racal */ @@ -406,7 +395,6 @@ #define RIG_MODEL_RA3710 RIG_MAKE_MODEL(RIG_RACAL, 4) #define RIG_MODEL_RA3702 RIG_MAKE_MODEL(RIG_RACAL, 5) - /* * Watkins-Johnson */ @@ -417,7 +405,6 @@ #define RIG_MODEL_WJ8711 RIG_MAKE_MODEL(RIG_WJ, 3) #define RIG_MODEL_WJ8888 RIG_MAKE_MODEL(RIG_WJ, 4) - /* * Rohde & Schwarz--ek */ @@ -429,7 +416,6 @@ #define RIG_MODEL_EK895 RIG_MAKE_MODEL(RIG_EK, 4) #define RIG_MODEL_EK070 RIG_MAKE_MODEL(RIG_EK, 5) - /* * Skanti */ @@ -440,7 +426,6 @@ #define RIG_MODEL_TRP9000 RIG_MAKE_MODEL(RIG_SKANTI, 3) #define RIG_MODEL_TRP8255 RIG_MAKE_MODEL(RIG_SKANTI, 4) - /* * WiNRADiO/LinRADiO */ @@ -458,7 +443,6 @@ #define RIG_MODEL_G305 RIG_MAKE_MODEL(RIG_WINRADIO, 10) #define RIG_MODEL_G315 RIG_MAKE_MODEL(RIG_WINRADIO, 11) - /* * Ten Tec */ @@ -477,7 +461,6 @@ #define RIG_MODEL_RX331 RIG_MAKE_MODEL(RIG_TENTEC, 12) #define RIG_MODEL_TT599 RIG_MAKE_MODEL(RIG_TENTEC, 13) /* Eagle */ - /* * Alinco */ @@ -486,7 +469,6 @@ #define RIG_MODEL_DX77 RIG_MAKE_MODEL(RIG_ALINCO, 1) #define RIG_MODEL_DXSR8 RIG_MAKE_MODEL(RIG_ALINCO, 2) - /* * Kachina */ @@ -494,7 +476,6 @@ #define RIG_BACKEND_KACHINA "kachina" #define RIG_MODEL_505DSP RIG_MAKE_MODEL(RIG_KACHINA, 1) - /* * Gnuradio backend */ @@ -506,7 +487,6 @@ #define RIG_MODEL_GRAUDIOIQ RIG_MAKE_MODEL(RIG_GNURADIO, 4) /* I&Q stereo sound card source */ #define RIG_MODEL_USRP_G RIG_MAKE_MODEL(RIG_GNURADIO, 5) /* Universal Software Radio Peripheral */ - /* * Microtune tuners */ @@ -516,7 +496,6 @@ #define RIG_MODEL_MICROTUNE_4702 RIG_MAKE_MODEL(RIG_MICROTUNE, 2) /* Alan's */ #define RIG_MODEL_MICROTUNE_4707 RIG_MAKE_MODEL(RIG_MICROTUNE, 3) - /* * TAPR */ @@ -524,7 +503,6 @@ #define RIG_BACKEND_TAPR "tapr" #define RIG_MODEL_DSP10 RIG_MAKE_MODEL(RIG_TAPR, 1) - /* * Flex-radio */ @@ -535,7 +513,6 @@ #define RIG_MODEL_DTTSP RIG_MAKE_MODEL(RIG_FLEXRADIO, 3) #define RIG_MODEL_DTTSP_UDP RIG_MAKE_MODEL(RIG_FLEXRADIO, 4) - /* * VEB Funkwerk Köpenick RFT */ @@ -543,7 +520,6 @@ #define RIG_BACKEND_RFT "rft" #define RIG_MODEL_EKD500 RIG_MAKE_MODEL(RIG_RFT, 1) - /* * Various kits */ @@ -569,7 +545,6 @@ #define RIG_MODEL_FUNCUBEDONGLEPLUS RIG_MAKE_MODEL(RIG_KIT, 18) /* FunCUBE Dongle Pro+ */ #define RIG_MODEL_RSHFIQ RIG_MAKE_MODEL(RIG_KIT, 19) /* RS-HFIQ */ - /* * SW/FM/TV tuner cards supported by Video4Linux,*BSD, .. */ @@ -578,7 +553,6 @@ #define RIG_MODEL_V4L RIG_MAKE_MODEL(RIG_TUNER, 1) #define RIG_MODEL_V4L2 RIG_MAKE_MODEL(RIG_TUNER, 2) - /* * Rohde & Schwarz--rs */ @@ -589,7 +563,6 @@ #define RIG_MODEL_XK2100 RIG_MAKE_MODEL(RIG_RS, 3) #define RIG_MODEL_EK89X RIG_MAKE_MODEL(RIG_RS, 4) - /* * Phillips/Simoco PRM80 */ @@ -598,7 +571,6 @@ #define RIG_MODEL_PRM8060 RIG_MAKE_MODEL(RIG_PRM80, 1) #define RIG_MODEL_PRM8070 RIG_MAKE_MODEL(RIG_PRM80, 2) - /* * ADAT by HB9CBU * @@ -608,7 +580,6 @@ #define RIG_BACKEND_ADAT "adat" #define RIG_MODEL_ADT_200A RIG_MAKE_MODEL(RIG_ADAT, 1) - /* * ICOM Marine */ @@ -619,7 +590,6 @@ #define RIG_MODEL_IC_M710 RIG_MAKE_MODEL(RIG_ICMARINE, 3) #define RIG_MODEL_IC_M803 RIG_MAKE_MODEL(RIG_ICMARINE, 4) - /* * Dorji transceiver modules */ diff --git a/rigs/dummy/Makefile.am b/rigs/dummy/Makefile.am index 7a60bdcef..5a419d3f3 100644 --- a/rigs/dummy/Makefile.am +++ b/rigs/dummy/Makefile.am @@ -1,4 +1,4 @@ -DUMMYSRC = dummy_common.c dummy_common.h 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 tci1x.c +DUMMYSRC = dummy_common.c dummy_common.h 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 tci1x.c aclog.c noinst_LTLIBRARIES = libhamlib-dummy.la libhamlib_dummy_la_SOURCES = $(DUMMYSRC) diff --git a/rigs/dummy/aclog.c b/rigs/dummy/aclog.c new file mode 100644 index 000000000..d5a5a8288 --- /dev/null +++ b/rigs/dummy/aclog.c @@ -0,0 +1,994 @@ +/* +* Hamlib ACLog backend - main file +* Copyright (c) 2023 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 +#include + +#include +#include +#include +#include +#include +#include + +#include "dummy_common.h" + +#define DEBUG 1 +#define DEBUG_TRACE DEBUG_VERBOSE +#define TRUE 1 +#define FALSE 0 + + +#define MAXCMDLEN 8192 +#define MAXXMLLEN 8192 +#define MAXARGLEN 128 +#define MAXBANDWIDTHLEN 4096 + +#define DEFAULTPATH "127.0.0.1:1100" + +#define ACLOG_VFOS (RIG_VFO_A) + +#define ACLOG_MODES (RIG_MODE_AM | RIG_MODE_PKTAM | RIG_MODE_CW | RIG_MODE_CWR |\ + RIG_MODE_RTTY | RIG_MODE_RTTYR |\ + RIG_MODE_PKTLSB | RIG_MODE_PKTUSB |\ + RIG_MODE_SSB | RIG_MODE_LSB | RIG_MODE_USB |\ + RIG_MODE_FM | RIG_MODE_WFM | RIG_MODE_FMN | RIG_MODE_PKTFM |\ + RIG_MODE_C4FM) + +#define streq(s1,s2) (strcmp(s1,s2)==0) + +struct aclog_priv_data +{ + vfo_t curr_vfo; + char bandwidths[MAXBANDWIDTHLEN]; /* pipe delimited set returned from aclog */ + int nbandwidths; + char info[8192]; + ptt_t ptt; + split_t split; + rmode_t curr_modeA; + rmode_t curr_modeB; + freq_t curr_freqA; + freq_t curr_freqB; + pbwidth_t curr_widthA; + pbwidth_t curr_widthB; + int has_get_modeA; /* True if this function is available */ + int has_get_bwA; /* True if this function is available */ + int has_set_bwA; /* True if this function is available */ + float powermeter_scale; /* So we can scale power meter to 0-1 */ + value_t parms[RIG_SETTING_MAX]; + struct ext_list *ext_parms; +}; + +//Structure for mapping aclog dynmamic modes to hamlib modes +//aclog displays modes as the rig displays them +struct s_modeMap +{ + rmode_t mode_hamlib; + char *mode_aclog; +}; + +//ACLog will provide us the modes for the selected rig +//We will then put them in this struct +static struct s_modeMap modeMap[] = +{ + {RIG_MODE_USB, "|USB|"}, + {RIG_MODE_USB, "|SSB|"}, + {RIG_MODE_LSB, "|LSB|"}, + {RIG_MODE_PKTUSB, NULL}, + {RIG_MODE_PKTLSB, NULL}, + {RIG_MODE_AM, "|AM|"}, + {RIG_MODE_FM, "|FM|"}, + {RIG_MODE_FMN, NULL}, + {RIG_MODE_WFM, NULL}, + {RIG_MODE_CW, "|CW|"}, + {RIG_MODE_CWR, "|CWR|"}, + {RIG_MODE_RTTY, "|RTTY|"}, + {RIG_MODE_RTTYR, "|RTTYR|"}, + {RIG_MODE_C4FM, "|C4FM|"}, + {0, NULL} +}; + +/* +* check_vfo +* No assumptions +*/ +static int check_vfo(vfo_t vfo) +{ + switch (vfo) + { + case RIG_VFO_A: + break; + + case RIG_VFO_TX: + case RIG_VFO_B: + break; + + case RIG_VFO_CURR: + break; // will default to A in which_vfo + + default: + return (FALSE); + } + + return (TRUE); +} + +/* +* read_transaction +* Assumes rig!=NULL, xml!=NULL, xml_len>=MAXXMLLEN +*/ +static int read_transaction(RIG *rig, char *xml, int xml_len) +{ + int retval; + int retry; + char *delims; + char *terminator = "\r\n"; + struct rig_state *rs = &rig->state; + + ENTERFUNC; + + retry = 2; + delims = "\n"; + xml[0] = 0; + + do + { + char tmp_buf[MAXXMLLEN]; // plenty big for expected aclog responses hopefully + + if (retry < 2) + { + rig_debug(RIG_DEBUG_WARN, "%s: retry needed? retry=%d\n", __func__, retry); + } + + int len = read_string(&rs->rigport, (unsigned char *) tmp_buf, sizeof(tmp_buf), + delims, + strlen(delims), 0, 1); + rig_debug(RIG_DEBUG_TRACE, "%s: string='%s'\n", __func__, tmp_buf); + + // if our first response we should see the HTTP header + if (strlen(xml) == 0 && strstr(tmp_buf, "") == NULL) + { + rig_debug(RIG_DEBUG_ERR, "%s: Expected '', got '%s'\n", __func__, + tmp_buf); + continue; // we'll try again + } + + if (len > 0) { retry = 3; } + + if (len <= 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: read_string error=%d\n", __func__, len); + continue; + } + + if (strlen(xml) + strlen(tmp_buf) < xml_len - 1) + { + strncat(xml, tmp_buf, xml_len - 1); + } + else + { + rig_debug(RIG_DEBUG_ERR, + "%s: xml buffer overflow!!\nTrying to add len=%d\nTo len=%d\n", __func__, + (int)strlen(tmp_buf), (int)strlen(xml)); + RETURNFUNC(-RIG_EPROTO); + } + } + while (retry-- > 0 && strstr(xml, terminator) == NULL); + + if (retry == 0) + { + rig_debug(RIG_DEBUG_WARN, "%s: retry timeout\n", __func__); + RETURNFUNC(-RIG_ETIMEOUT); + } + + if (strstr(xml, terminator)) + { + rig_debug(RIG_DEBUG_TRACE, "%s: got %s\n", __func__, terminator); + retval = RIG_OK; + } + else + { + rig_debug(RIG_DEBUG_VERBOSE, "%s: did not get %s\n", __func__, terminator); + retval = -(101 + RIG_EPROTO); + } + + RETURNFUNC(retval); +} + +/* +* write_transaction +* Assumes rig!=NULL, xml!=NULL, xml_len=total size of xml for response +*/ +static int write_transaction(RIG *rig, char *xml, int xml_len) +{ + + int try = rig->caps->retry; + + int retval = -RIG_EPROTO; + + struct rig_state *rs = &rig->state; + + ENTERFUNC; + + // This shouldn't ever happen...but just in case + // We need to avoid an empty write as rigctld replies with blank line + if (xml_len == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: len==0??\n", __func__); + RETURNFUNC(retval); + } + + // appears we can lose sync if we don't clear things out + // shouldn't be anything for us now anyways + rig_flush(&rig->state.rigport); + + while (try-- >= 0 && retval != RIG_OK) + { + retval = write_block(&rs->rigport, (unsigned char *) xml, strlen(xml)); + + if (retval < 0) + { + RETURNFUNC(-RIG_EIO); + } + } + + RETURNFUNC(retval); +} + +static int aclog_transaction(RIG *rig, char *cmd, char *value, + int value_len) +{ + char xml[MAXXMLLEN]; + int retry = 3; + + ENTERFUNC; + ELAPSED1; + + set_transaction_active(rig); + + if (value) + { + value[0] = 0; + } + + do + { + int retval; + + if (retry != 3) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s: cmd=%s, retry=%d\n", __func__, cmd, retry); + } + + retval = write_transaction(rig, cmd, strlen(cmd)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: write_transaction error=%d\n", __func__, retval); + + // if we get RIG_EIO the socket has probably disappeared + // so bubble up the error so port can re re-opened + if (retval == -RIG_EIO) { set_transaction_inactive(rig); RETURNFUNC(retval); } + + hl_usleep(50 * 1000); // 50ms sleep if error + } + + if (value) + { + read_transaction(rig, xml, sizeof(xml)); // this might time out -- that's OK + } + + // we get an unknown response if function does not exist + if (strstr(xml, "UNKNOWN")) { set_transaction_inactive(rig); RETURNFUNC(RIG_ENAVAIL); } + + strncpy(value, xml, value_len); + + } + while (((value && strlen(value) == 0)) + && retry--); // we'll do retries if needed + + if (value && strlen(value) == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: no value returned\n", __func__); + set_transaction_inactive(rig); RETURNFUNC(RIG_EPROTO); + } + + ELAPSED2; + set_transaction_inactive(rig); + RETURNFUNC(RIG_OK); +} + +/* +* aclog_init +* Assumes rig!=NULL +*/ +static int aclog_init(RIG *rig) +{ + struct aclog_priv_data *priv; + + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s version %s\n", __func__, rig->caps->version); + + rig->state.priv = (struct aclog_priv_data *)calloc(1, sizeof( + struct aclog_priv_data)); + + if (!rig->state.priv) + { + RETURNFUNC(-RIG_ENOMEM); + } + + priv = rig->state.priv; + + memset(priv, 0, sizeof(struct aclog_priv_data)); + memset(priv->parms, 0, RIG_SETTING_MAX * sizeof(value_t)); + + /* + * set arbitrary initial status + */ + rig->state.current_vfo = RIG_VFO_A; + priv->split = 0; + priv->ptt = 0; + priv->curr_modeA = -1; + priv->curr_modeB = -1; + priv->curr_widthA = -1; + priv->curr_widthB = -1; + + if (!rig->caps) + { + RETURNFUNC(-RIG_EINVAL); + } + + strncpy(rig->state.rigport.pathname, DEFAULTPATH, + sizeof(rig->state.rigport.pathname)); + + RETURNFUNC(RIG_OK); +} + +/* +* modeMapGet +* Assumes mode!=NULL +* Return the string for ACLog for the given hamlib mode +*/ +static const char *modeMapGet(rmode_t modeHamlib) +{ + int i; + + rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__); + + for (i = 0; modeMap[i].mode_hamlib != 0; ++i) + { + if (modeMap[i].mode_aclog == NULL) { continue; } + + rig_debug(RIG_DEBUG_TRACE, + "%s: checking modeMap[%d]=%.0f to modeHamlib=%.0f, mode_aclog='%s'\n", __func__, + i, (double)modeMap[i].mode_hamlib, (double)modeHamlib, modeMap[i].mode_aclog); + + if (modeMap[i].mode_hamlib == modeHamlib && strlen(modeMap[i].mode_aclog) > 0) + { + rig_debug(RIG_DEBUG_TRACE, "%s matched mode=%.0f, returning '%s'\n", __func__, + (double)modeHamlib, modeMap[i].mode_aclog); + return (modeMap[i].mode_aclog); + } + } + + rig_debug(RIG_DEBUG_ERR, "%s: ACLog does not have mode: %s\n", __func__, + rig_strrmode(modeHamlib)); + return ("ERROR"); +} + +/* +* modeMapGetHamlib +* Assumes mode!=NULL +* Return the hamlib mode from the given ACLog string +*/ +static rmode_t modeMapGetHamlib(const char *modeACLog) +{ + int i; + char modeCheck[64]; + + SNPRINTF(modeCheck, sizeof(modeCheck), "|%s|", modeACLog); + + for (i = 0; modeMap[i].mode_hamlib != 0; ++i) + { + rig_debug(RIG_DEBUG_TRACE, "%s: find '%s' in '%s'\n", __func__, + modeCheck, modeMap[i].mode_aclog); + + if (modeMap[i].mode_aclog + && strcmp(modeMap[i].mode_aclog, modeCheck) == 0) + { + return (modeMap[i].mode_hamlib); + } + } + + rig_debug(RIG_DEBUG_TRACE, "%s: mode requested: %s, not in modeMap\n", __func__, + modeACLog); + return (RIG_MODE_NONE); +} + +/* +* aclog_get_freq +* Assumes rig!=NULL, rig->state.priv!=NULL, freq!=NULL +*/ +static int aclog_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) +{ + char value[MAXARGLEN]; + struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __func__, + rig_strvfo(vfo)); + + + if (check_vfo(vfo) == FALSE) + { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __func__, rig_strvfo(vfo)); + RETURNFUNC(-RIG_EINVAL); + } + + if (vfo == RIG_VFO_CURR) + { + vfo = rig->state.current_vfo; + rig_debug(RIG_DEBUG_TRACE, "%s: get_freq2 vfo=%s\n", + __func__, rig_strvfo(vfo)); + } + + char *cmd = "\r\n"; + int retval; + + retval = aclog_transaction(rig, cmd, value, sizeof(value)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: READBMF failed retval=%s\n", __func__, + rigerror(retval)); + RETURNFUNC(retval); + } + + char *p = strstr(value, ""); + *freq = 0; + + if (p) { sscanf(p, "%lf", freq); } + + *freq *= 1e6; // convert from MHz to Hz + + if (*freq == 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: freq==0??\nvalue=%s\n", __func__, + value); + RETURNFUNC(-RIG_EPROTO); + } + else + { + rig_debug(RIG_DEBUG_TRACE, "%s: freq=%.0f\n", __func__, *freq); + } + + if (vfo == RIG_VFO_A) + { + priv->curr_freqA = *freq; + } + else // future support in ACLOG maybe? + { + priv->curr_freqB = *freq; + } + + RETURNFUNC(RIG_OK); +} + +/* +* aclog_get_mode +* Assumes rig!=NULL, rig->state.priv!=NULL, mode!=NULL +*/ +static int aclog_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) +{ + int retval; + char value[MAXCMDLEN]; + char *cmdp; + struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s\n", __func__, + rig_strvfo(vfo)); + + if (check_vfo(vfo) == FALSE) + { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __func__, rig_strvfo(vfo)); + RETURNFUNC(-RIG_EINVAL); + } + + cmdp = "\r\n"; /* default to old way */ + + retval = aclog_transaction(rig, cmdp, value, sizeof(value)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: %s failed: %s\n", __func__, cmdp, + rigerror(retval)); + RETURNFUNC(retval); + } + + char *p = strstr(value, ""); + char modetmp[32]; + modetmp[0] = 0; + + if (p) + { + *mode = RIG_MODE_NONE; + int n = sscanf(p, "%[^<]", modetmp); + + if (n) { *mode = modeMapGetHamlib(modetmp); } + else + { + rig_debug(RIG_DEBUG_ERR, "%s: Unable to parse from '%s'\n", __func__, + value); + *mode = RIG_MODE_USB; // just default to USB if we fail parsing + } + } + + rig_debug(RIG_DEBUG_TRACE, "%s: mode='%s'\n", __func__, + rig_strrmode(*mode)); + + if (vfo == RIG_VFO_A) + { + priv->curr_modeA = *mode; + } + else + { + priv->curr_modeB = *mode; + } + + *width = 2400; // just default to 2400 for now + + RETURNFUNC(RIG_OK); +} +/* +* aclog_open +* Assumes rig!=NULL, rig->state.priv!=NULL +*/ +static int aclog_open(RIG *rig) +{ + int retval; + char value[MAXARGLEN]; + char *p; + + //;struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + ENTERFUNC; + rig_debug(RIG_DEBUG_VERBOSE, "%s version %s\n", __func__, rig->caps->version); + + retval = aclog_transaction(rig, "\r\n", value, + sizeof(value)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, + "%s: PROGRAM failed: %s", __func__, rigerror(retval)); + } + + rig_debug(RIG_DEBUG_TRACE, "%s: returned value=%s\n", __func__, value); + char version_pgm[64]; + sscanf(value, + "N3FJP's Amateur Contact Log%[^<]", + version_pgm); + rig_debug(RIG_DEBUG_VERBOSE, "%s: ACLog version=%s\n", __func__, version_pgm); + + double version_api = 0; + p = strstr(value, ""); + + if (p) { sscanf(strstr(value, ""), "%lf", &version_api); } + + rig_debug(RIG_DEBUG_VERBOSE, "%s ACLog API version %.1lf\n", __func__, + version_api); + + retval = aclog_transaction(rig, "\r\n", value, + sizeof(value)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: RIGENABLED failed,,,not fatal: %s\n", __func__, + rigerror(retval)); + } + + p = strstr(value, ""); + char transceiver[64]; + strcpy(transceiver, "Unknown"); + + if (p) { sscanf(p, "%[^<]", transceiver); } + + rig_debug(RIG_DEBUG_VERBOSE, "Transceiver=%s\n", transceiver); + + freq_t freq; + retval = aclog_get_freq(rig, RIG_VFO_CURR, &freq); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: aclog_get_freq not working!!\n", __func__); + RETURNFUNC(RIG_EPROTO); + } + + rig->state.current_vfo = RIG_VFO_A; + rig_debug(RIG_DEBUG_TRACE, "%s: currvfo=%s value=%s\n", __func__, + rig_strvfo(rig->state.current_vfo), value); + + RETURNFUNC(retval); +} + +/* +* aclog_close +* Assumes rig!=NULL +*/ +static int aclog_close(RIG *rig) +{ + ENTERFUNC; + + RETURNFUNC(RIG_OK); +} + +/* +* aclog_cleanup +* Assumes rig!=NULL, rig->state.priv!=NULL +*/ +static int aclog_cleanup(RIG *rig) +{ + struct aclog_priv_data *priv; + + rig_debug(RIG_DEBUG_TRACE, "%s\n", __func__); + + if (!rig) + { + RETURNFUNC2(-RIG_EINVAL); + } + + priv = (struct aclog_priv_data *)rig->state.priv; + + free(priv->ext_parms); + free(rig->state.priv); + + rig->state.priv = NULL; + + // we really don't need to free this up as it's only done once + // was causing problem when cleanup was followed by rig_open + // model_aclog was not getting refilled + // if we can figure out that one we can re-enable this +#if 0 + int i; + + for (i = 0; modeMap[i].mode_hamlib != 0; ++i) + { + if (modeMap[i].mode_aclog) + { + free(modeMap[i].mode_aclog); + modeMap[i].mode_aclog = NULL; + modeMap[i].mode_hamlib = 0; + } + + } + +#endif + + RETURNFUNC2(RIG_OK); +} + + +/* +* aclog_set_freq +* assumes rig!=NULL, rig->state.priv!=NULL +*/ +static int aclog_set_freq(RIG *rig, vfo_t vfo, freq_t freq) +{ + int retval; + char cmd[MAXARGLEN]; + char value[1024]; + + //struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + rig_debug(RIG_DEBUG_TRACE, "%s\n", __func__); + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s freq=%.0f\n", __func__, + rig_strvfo(vfo), freq); + + if (check_vfo(vfo) == FALSE) + { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __func__, rig_strvfo(vfo)); + RETURNFUNC2(-RIG_EINVAL); + } + + if (vfo == RIG_VFO_CURR) + { + vfo = rig->state.current_vfo; + } + + SNPRINTF(cmd, sizeof(cmd), + "%lfTRUE\r\n", + freq / 1e6); + + retval = aclog_transaction(rig, cmd, value, sizeof(value)); + + if (retval != RIG_OK) + { + RETURNFUNC2(retval); + } + + RETURNFUNC2(RIG_OK); +} + + +/* +* aclog_set_mode +* Assumes rig!=NULL +*/ +static int aclog_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) +{ + int retval; + char cmd[MAXCMDLEN]; + char *p; + char *pttmode; + char *ttmode; + struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s: vfo=%s mode=%s width=%d\n", + __func__, rig_strvfo(vfo), rig_strrmode(mode), (int)width); + + + // if ptt is on do not set mode + if (priv->ptt) + { + rig_debug(RIG_DEBUG_TRACE, "%s: returning because priv->ptt=%d\n", __func__, + (int)priv->ptt); + RETURNFUNC(RIG_OK); + } + + if (vfo == RIG_VFO_CURR) + { + vfo = rig->state.current_vfo; + } + + if (check_vfo(vfo) == FALSE) + { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __func__, rig_strvfo(vfo)); + RETURNFUNC(-RIG_EINVAL); + } + + if (priv->ptt) + { + rig_debug(RIG_DEBUG_VERBOSE, "%s set_mode call not made as PTT=1\n", __func__); + RETURNFUNC(RIG_OK); // just return OK and ignore this + } + + // Switch to VFOB if appropriate since we can't set mode directly + // MDB + rig_debug(RIG_DEBUG_TRACE, "%s: curr_vfo = %s\n", __func__, + rig_strvfo(rig->state.current_vfo)); + + // Set the mode + if (strstr(modeMapGet(mode), "ERROR") == NULL) + { + ttmode = strdup(modeMapGet(mode)); + } + else + { + rig_debug(RIG_DEBUG_ERR, "%s: modeMapGet failed on mode=%d\n", __func__, + (int)mode); + RETURNFUNC(-RIG_EINVAL); + } + + rig_debug(RIG_DEBUG_TRACE, "%s: got ttmode = %s\n", __func__, + ttmode == NULL ? "NULL" : ttmode); + + if (ttmode == NULL) + { + rig_debug(RIG_DEBUG_ERR, "%s: strdup failed\n", __func__); + RETURNFUNC(-RIG_EINTERNAL); + } + + pttmode = ttmode; + + if (ttmode[0] == '|') { pttmode = &ttmode[1]; } // remove first pipe symbol + + p = strchr(pttmode, '|'); + + if (p) { *p = 0; } // remove any other pipe + + SNPRINTF(cmd, sizeof(cmd), + "%s\r\n", pttmode); + free(ttmode); + + retval = aclog_transaction(rig, cmd, NULL, 0); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: failed: %s\n", __func__, + rigerror(retval)); + RETURNFUNC(retval); + } + + if (vfo == RIG_VFO_A) + { + priv->curr_modeA = mode; + priv->curr_widthA = width; + } + else + { + priv->curr_modeB = mode; + priv->curr_widthB = width; + } + + rig_debug(RIG_DEBUG_TRACE, + "%s: return modeA=%s, widthA=%d\n,modeB=%s, widthB=%d\n", __func__, + rig_strrmode(priv->curr_modeA), (int)priv->curr_widthA, + rig_strrmode(priv->curr_modeB), (int)priv->curr_widthB); + RETURNFUNC(RIG_OK); +} + + +/* +* aclog_get_vfo +* assumes rig!=NULL, vfo != NULL +*/ +static int aclog_get_vfo(RIG *rig, vfo_t *vfo) +{ + ENTERFUNC; + + *vfo = RIG_VFO_A; + + RETURNFUNC(RIG_OK); +} + +/* +* aclog_get_info +* assumes rig!=NULL +*/ +static const char *aclog_get_info(RIG *rig) +{ + struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + return (priv->info); +} + +static int aclog_power2mW(RIG *rig, unsigned int *mwpower, float power, + freq_t freq, rmode_t mode) +{ + struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s: passed power = %f\n", __func__, power); + rig_debug(RIG_DEBUG_TRACE, "%s: passed freq = %"PRIfreq" Hz\n", __func__, freq); + rig_debug(RIG_DEBUG_TRACE, "%s: passed mode = %s\n", __func__, + rig_strrmode(mode)); + + power *= priv->powermeter_scale; + *mwpower = (power * 100000); + + RETURNFUNC(RIG_OK); +} + +static int aclog_mW2power(RIG *rig, float *power, unsigned int mwpower, + freq_t freq, rmode_t mode) +{ + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s: passed mwpower = %u\n", __func__, mwpower); + rig_debug(RIG_DEBUG_TRACE, "%s: passed freq = %"PRIfreq" Hz\n", __func__, freq); + rig_debug(RIG_DEBUG_TRACE, "%s: passed mode = %s\n", __func__, + rig_strrmode(mode)); + + *power = ((float)mwpower / 100000); + + RETURNFUNC(RIG_OK); + +} + +/* +* aclog_set_ptt +* Assumes rig!=NULL +*/ +static int aclog_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) +{ + int retval; + char cmd[MAXCMDLEN]; + struct aclog_priv_data *priv = (struct aclog_priv_data *) rig->state.priv; + + ENTERFUNC; + rig_debug(RIG_DEBUG_TRACE, "%s: ptt=%d\n", __func__, ptt); + + + if (check_vfo(vfo) == FALSE) + { + rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n", + __func__, rig_strvfo(vfo)); + RETURNFUNC(-RIG_EINVAL); + } + + snprintf(cmd, sizeof(cmd), + ptt == RIG_PTT_ON ? "\r\n" : "\r\n"); + + retval = aclog_transaction(rig, cmd, NULL, 0); + + if (retval != RIG_OK) + { + RETURNFUNC(retval); + } + + priv->ptt = ptt; + + RETURNFUNC(RIG_OK); +} + + +const struct rig_caps aclog_caps = +{ + RIG_MODEL(RIG_MODEL_ACLOG), + .model_name = "ACLog", + .mfg_name = "N3FJP", + .version = "20230120.0", + .copyright = "LGPL", + .status = RIG_STATUS_STABLE, + .rig_type = RIG_TYPE_TRANSCEIVER, + //.targetable_vfo = RIG_TARGETABLE_FREQ | RIG_TARGETABLE_MODE, + .ptt_type = RIG_PTT_RIG, + .port_type = RIG_PORT_NETWORK, + .write_delay = 0, + .post_write_delay = 0, + .timeout = 1000, + .retry = 2, + + .filters = { + {RIG_MODE_ALL, RIG_FLT_ANY}, + RIG_FLT_END + }, + + .rx_range_list1 = {{ + .startf = kHz(1), .endf = GHz(10), .modes = ACLOG_MODES, + .low_power = -1, .high_power = -1, ACLOG_VFOS, RIG_ANT_1 + }, + RIG_FRNG_END, + }, + .tx_range_list1 = {RIG_FRNG_END,}, + .rx_range_list2 = {{ + .startf = kHz(1), .endf = GHz(10), .modes = ACLOG_MODES, + .low_power = -1, .high_power = -1, ACLOG_VFOS, RIG_ANT_1 + }, + RIG_FRNG_END, + }, + .tx_range_list2 = {RIG_FRNG_END,}, + .tuning_steps = { {ACLOG_MODES, 1}, {ACLOG_MODES, RIG_TS_ANY}, RIG_TS_END, }, + .priv = NULL, /* priv */ + + .rig_init = aclog_init, + .rig_open = aclog_open, + .rig_close = aclog_close, + .rig_cleanup = aclog_cleanup, + + .set_freq = aclog_set_freq, + .get_freq = aclog_get_freq, + .get_vfo = aclog_get_vfo, + .set_mode = aclog_set_mode, + .get_mode = aclog_get_mode, + .get_info = aclog_get_info, + .set_ptt = aclog_set_ptt, + //.get_ptt = aclog_get_ptt, + .power2mW = aclog_power2mW, + .mW2power = aclog_mW2power, + .hamlib_check_rig_caps = HAMLIB_CHECK_RIG_CAPS +}; diff --git a/rigs/dummy/dummy.c b/rigs/dummy/dummy.c index f85b040e1..c95390533 100644 --- a/rigs/dummy/dummy.c +++ b/rigs/dummy/dummy.c @@ -2675,6 +2675,7 @@ DECLARE_INITRIG_BACKEND(dummy) rig_register(&flrig_caps); rig_register(&trxmanager_caps); rig_register(&dummy_no_vfo_caps); + rig_register(&aclog_caps); // rig_register(&tci1x_caps); return RIG_OK; } diff --git a/rigs/dummy/dummy.h b/rigs/dummy/dummy.h index 3445f1597..dc844a201 100644 --- a/rigs/dummy/dummy.h +++ b/rigs/dummy/dummy.h @@ -45,6 +45,7 @@ extern struct rig_caps netrigctl_caps; extern const struct rig_caps flrig_caps; extern const struct rig_caps trxmanager_caps; extern const struct rig_caps tci1x_caps; +extern const struct rig_caps aclog_caps; int netrigctl_get_vfo_mode(RIG *);