/* * Hamlib Kenwood backend - main file * Copyright (c) 2000-2011 by Stephane Fillod * Copyright (C) 2009 Alessandro Zummo * * * 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 #include /* String function definitions */ #include /* UNIX standard function definitions */ #include #include #include "hamlib/rig.h" #include "serial.h" #include "misc.h" #include "register.h" #include "cal.h" #include "kenwood.h" struct kenwood_id { rig_model_t model; int id; }; struct kenwood_id_string { rig_model_t model; const char *id; }; #define UNKNOWN_ID -1 /* * Identification number as returned by "ID;" * Please, if the model number of your rig is listed as UNKNOWN_ID, * send the value to for inclusion. Thanks --SF * * TODO: sort this list with most frequent rigs first. */ static const struct kenwood_id kenwood_id_list[] = { { RIG_MODEL_TS940, 1 }, { RIG_MODEL_TS811, 2 }, { RIG_MODEL_TS711, 3 }, { RIG_MODEL_TS440, 4 }, { RIG_MODEL_R5000, 5 }, { RIG_MODEL_TS790, 7 }, { RIG_MODEL_TS950SDX, 8 }, /* reported as RIG_MODEL_TS950SD originally */ { RIG_MODEL_TS850, 9 }, { RIG_MODEL_TS450S, 10 }, { RIG_MODEL_TS690S, 11 }, { RIG_MODEL_TS950SDX, 12 }, /* Actual value from TS-950SDX */ { RIG_MODEL_TS870S, 15 }, { RIG_MODEL_TRC80, 16 }, { RIG_MODEL_TS570D, 17 }, /* Elecraft K2|K3 also returns 17 */ { RIG_MODEL_TS570S, 18 }, { RIG_MODEL_TS2000, 19 }, { RIG_MODEL_TS480, 20 }, { RIG_MODEL_TS590S, 21 }, /* TBC */ { RIG_MODEL_NONE, UNKNOWN_ID }, /* end marker */ }; /* XXX numeric ids have been tested only with the TS-450 */ static const struct kenwood_id_string kenwood_id_string_list[] = { { RIG_MODEL_TS940, "001" }, { RIG_MODEL_TS811, "002" }, { RIG_MODEL_TS711, "003" }, { RIG_MODEL_TS440, "004" }, { RIG_MODEL_R5000, "005" }, { RIG_MODEL_TS790, "007" }, { RIG_MODEL_TS950SDX, "008" }, /* reported as RIG_MODEL_TS950SD originally */ { RIG_MODEL_TS850, "009" }, { RIG_MODEL_TS450S, "010" }, { RIG_MODEL_TS690S, "011" }, { RIG_MODEL_TS950SDX, "012" }, /* Actual value from TS-950SDX */ { RIG_MODEL_TS870S, "015" }, { RIG_MODEL_TS570D, "017" }, /* Elecraft K2|K3 also returns 17 */ { RIG_MODEL_TS570S, "018" }, { RIG_MODEL_TS2000, "019" }, { RIG_MODEL_TS480, "020" }, { RIG_MODEL_TS590S, "021" }, /* TBC */ { RIG_MODEL_THD7A, "TH-D7" }, { RIG_MODEL_THD7AG, "TH-D7G" }, { RIG_MODEL_TMD700, "TM-D700" }, { RIG_MODEL_TMD710, "TM-D710" }, { RIG_MODEL_THD72A, "TH-D72" }, { RIG_MODEL_TMV7, "TM-V7" }, { RIG_MODEL_TMV71, "TM-V71" }, { RIG_MODEL_THF6A, "TH-F6" }, { RIG_MODEL_THF7E, "TH-F7" }, { RIG_MODEL_THG71, "TH-G71" }, { RIG_MODEL_NONE, NULL }, /* end marker */ }; rmode_t kenwood_mode_table[KENWOOD_MODE_TABLE_MAX] = { [0] = RIG_MODE_NONE, [1] = RIG_MODE_LSB, [2] = RIG_MODE_USB, [3] = RIG_MODE_CW, [4] = RIG_MODE_FM, [5] = RIG_MODE_AM, [6] = RIG_MODE_RTTY, [7] = RIG_MODE_CWR, [8] = RIG_MODE_NONE, /* TUNE mode */ [9] = RIG_MODE_RTTYR }; /* * 38 CTCSS sub-audible tones */ const tone_t kenwood38_ctcss_list[] = { 670, 719, 744, 770, 797, 825, 854, 885, 915, 948, 974, 1000, 1035, 1072, 1109, 1148, 1188, 1230, 1273, 1318, 1365, 1413, 1462, 1514, 1567, 1622, 1679, 1738, 1799, 1862, 1928, 2035, 2107, 2181, 2257, 2336, 2418, 2503, 0, }; /* * 42 CTCSS sub-audible tones */ const tone_t kenwood42_ctcss_list[] = { 670, 693, 719, 744, 770, 797, 825, 854, 885, 915, 948, 974, 1000, 1035, 1072, 1109, 1148, 1188, 1230, 1273, 1318, 1365, 1413, 1462, 1514, 1567, 1622, 1679, 1738, 1799, 1862, 1928, 2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, 2541, 0, }; /* Token definitions for .cfgparams in rig_caps * * See enum rig_conf_e and struct confparams in rig.h */ const struct confparams kenwood_cfg_params[] = { { TOK_FINE, "fine", "Fine", "Fine step mode", NULL, RIG_CONF_CHECKBUTTON, { } }, { TOK_VOICE, "voice", "Voice", "Voice recall", NULL, RIG_CONF_BUTTON, { } }, { TOK_XIT, "xit", "XIT", "XIT", NULL, RIG_CONF_CHECKBUTTON, { } }, { TOK_RIT, "rit", "RIT", "RIT", NULL, RIG_CONF_CHECKBUTTON, { } }, { RIG_CONF_END, NULL, } }; /* * Synchronize with the radio. It's easy to fall out of sync when * issuing an invalid command, or if the radio was left in an * inconsistent state by a previous session. */ void kenwood_sync(RIG *rig) { char buf[256]; struct rig_state *rs; rig_debug(RIG_DEBUG_VERBOSE, "%s called", __func__); rs = &rig->state; while (1) { write_block(&rs->rigport, "ID;", 3); bzero (buf, sizeof(buf)); read_string (&rs->rigport, buf, 255, ";", 1); if (strlen(buf) == 6 && strncmp (buf, "ID", 2) == 0) { break; } } while (1) { read_string (&rs->rigport, buf, 255, ";", 1); if (strncmp (buf, "ID", 2) != 0) break; } rig_debug(RIG_DEBUG_VERBOSE, "%s done", __func__); return; } /** * kenwood_transaction * Assumes rig!=NULL rig->state!=NULL rig->caps!=NULL * * Parameters: * cmdstr: Command to be sent to the rig. cmdstr can also be NULL, * indicating that only a reply is needed (nothing will be sent). * cmd_len: Not used * data: Buffer for reply string. Can be NULL, indicating that no reply * is needed and will return with RIG_OK after command was sent. * datasize: in: Size of buffer. It is the caller's responsibily to provide * a large enough buffer for all possible replies for a command. * out: Location where to store number of bytes read. * * returns: * RIG_OK - if no error occured. * RIG_EIO - if an I/O error occured while sending/receiving data. * RIG_ETIMEOUT - if timeout expires without any characters received. * RIG_REJECTED - if a negative acknowledge was received or command not * recognized by rig. */ int kenwood_transaction(RIG *rig, const char *cmdstr, int cmd_len, char *data, size_t *datasize) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !datasize) return -RIG_EINVAL; struct kenwood_priv_caps *caps = kenwood_caps(rig); struct rig_state *rs; int retval; char cmdtrm[2]; /* Default Command/Reply termination char */ int retry_read = 0; rs = &rig->state; rs->hold_decode = 1; rig_debug(RIG_DEBUG_TRACE, "%s: cmdstr = %s\n", __func__, cmdstr); cmdtrm[0] = caps->cmdtrm; cmdtrm[1] = '\0'; transaction_write: serial_flush(&rs->rigport); if (cmdstr) { char *cmd; int len = strlen(cmdstr); cmd = malloc(len + 2); if (cmd == NULL) { retval = -RIG_ENOMEM; goto transaction_quit; } memcpy(cmd, cmdstr, len); /* XXX the if is temporary, until all invocations are fixed */ if (cmdstr[len - 1] != ';' && cmdstr[len - 1] != '\r') { cmd[len] = caps->cmdtrm; len++; } retval = write_block(&rs->rigport, cmd, len); free(cmd); if (retval != RIG_OK) goto transaction_quit; } if (data == NULL || *datasize <= 0) { rig->state.hold_decode = 0; return RIG_OK; /* don't want a reply */ } memset(data,0,*datasize); retval = read_string(&rs->rigport, data, *datasize, cmdtrm, strlen(cmdtrm)); if (retval < 0) { kenwood_sync (rig); if (retry_read++ < rig->state.rigport.retry) goto transaction_write; goto transaction_quit; } /* Check that command termination is correct */ if (strchr(cmdtrm, data[strlen(data)-1])==NULL) { kenwood_sync (rig); rig_debug(RIG_DEBUG_ERR, "%s: Command is not correctly terminated '%s'\n", __func__, data); if (retry_read++ < rig->state.rigport.retry) goto transaction_write; retval = -RIG_EPROTO; goto transaction_quit; } if (strlen(data) == 2) { switch (data[0]) { case 'N': /* Command recognised by rig but invalid data entered. */ rig_debug(RIG_DEBUG_VERBOSE, "%s: NegAck for '%s'\n", __func__, cmdstr); retval = -RIG_ENAVAIL; goto transaction_quit; case 'O': /* Too many characters sent without a carriage return */ rig_debug(RIG_DEBUG_VERBOSE, "%s: Overflow for '%s'\n", __func__, cmdstr); if (retry_read++ < rig->state.rigport.retry) goto transaction_write; retval = -RIG_EPROTO; goto transaction_quit; case 'E': /* Communication error */ rig_debug(RIG_DEBUG_VERBOSE, "%s: Communication error for '%s'\n", __func__, cmdstr); kenwood_sync (rig); if (retry_read++ < rig->state.rigport.retry) goto transaction_write; retval = -RIG_EIO; goto transaction_quit; case '?': /* Command not understood by rig */ rig_debug(RIG_DEBUG_ERR, "%s: Unknown command '%s'\n", __func__, cmdstr); kenwood_sync (rig); if (retry_read++ < rig->state.rigport.retry) goto transaction_write; retval = -RIG_ERJCTED; goto transaction_quit; } } /* always give back a null terminated string without * the command terminator. */ if (strlen(data) > 0) data[strlen(data) - 1] = '\0'; /* not elegant, but works */ else data[0] = '\0'; /* * Check that we received the correct reply. The first two characters * should be the same as command. */ if (cmdstr && (data[0] != cmdstr[0] || data[1] != cmdstr[1])) { /* * TODO: When RIG_TRN is enabled, we can pass the string * to the decoder for callback. That way we don't ignore * any commands. */ rig_debug(RIG_DEBUG_ERR, "%s: wrong reply %c%c for command %c%c\n", __func__, data[0], data[1], cmdstr[0], cmdstr[1]); kenwood_sync (rig); if (retry_read++ < rig->state.rigport.retry) goto transaction_write; retval = -RIG_EPROTO; goto transaction_quit; } *datasize = retval; retval = RIG_OK; transaction_quit: rs->hold_decode = 0; return retval; } /** * kenwood_safe_transaction * A wrapper for kenwood_transaction to check returned data against * expected length, * * Parameters: * cmd Same as kenwood_transaction() cmdstr * buf Same as kenwwod_transaction() data * buf_size Same as kenwood_transaction() datasize * expected Value of expected string length * * Returns: * RIG_OK - if no error occured. * RIG_EPROTO if returned string and expected are not equal * Error from kenwood_transaction() if any * */ int kenwood_safe_transaction(RIG *rig, const char *cmd, char *buf, size_t buf_size, size_t expected) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !buf) return -RIG_EINVAL; int err; if (expected == 0) buf_size = 0; err = kenwood_transaction(rig, cmd, strlen(cmd), buf, &buf_size); if (err != RIG_OK) return err; if (buf_size != expected) { rig_debug(RIG_DEBUG_ERR, "%s: wrong answer; len for cmd %s: " "expected = %d, got %d\n", __func__, cmd, expected, buf_size); return -RIG_EPROTO; } return RIG_OK; } rmode_t kenwood2rmode(unsigned char mode, const rmode_t mode_table[]) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (mode >= KENWOOD_MODE_TABLE_MAX) return RIG_MODE_NONE; return mode_table[mode]; } char rmode2kenwood(rmode_t mode, const rmode_t mode_table[]) { int i; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); for(i = 0; i < KENWOOD_MODE_TABLE_MAX; i++) { if (mode_table[i] == mode) return i; } return -1; } int kenwood_init(RIG *rig) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; struct kenwood_priv_data *priv; struct kenwood_priv_caps *caps = kenwood_caps(rig); priv = malloc(sizeof(struct kenwood_priv_data)); if (priv == NULL) return -RIG_ENOMEM; memset(priv, 0x00, sizeof(struct kenwood_priv_data)); priv->split = RIG_SPLIT_OFF; rig->state.priv = priv; /* default mode_table */ if (caps->mode_table == NULL) caps->mode_table = kenwood_mode_table; /* default if_len */ if (caps->if_len == 0) caps->if_len = 38; rig_debug(RIG_DEBUG_TRACE, "%s: if_len = %d\n", __func__, caps->if_len); return RIG_OK; } int kenwood_cleanup(RIG *rig) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; free(rig->state.priv); rig->state.priv = NULL; return RIG_OK; } int kenwood_open(RIG *rig) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; int err, i; char *idptr; char id[KENWOOD_MAX_BUF_LEN]; kenwood_sync(rig); /* get id in buffer, will be null terminated */ err = kenwood_get_id(rig, id); if (err != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s: cannot get identification\n", __func__); return err; } /* id is something like 'IDXXX' or 'ID XXX' */ if (strlen(id) < 5) { rig_debug(RIG_DEBUG_ERR, "%s: unknown id type (%s)\n", __func__, id); return -RIG_EPROTO; } /* check for a white space and skip it */ idptr = &id[2]; if (*idptr == ' ') idptr++; /* compare id string */ for (i = 0; kenwood_id_string_list[i].model != RIG_MODEL_NONE; i++) { if (strcmp(kenwood_id_string_list[i].id, idptr) != 0) continue; /* found matching id, verify driver */ rig_debug(RIG_DEBUG_TRACE, "%s: found match %s\n", __func__, kenwood_id_string_list[i].id); if (kenwood_id_string_list[i].model == rig->caps->rig_model) return RIG_OK; /* driver mismatch */ rig_debug(RIG_DEBUG_ERR, "%s: wrong driver selected (%d instead of %d)\n", __func__, rig->caps->rig_model, kenwood_id_string_list[i].model); return -RIG_EINVAL; } rig_debug(RIG_DEBUG_ERR, "%s: your rig (%s) is unknown\n", __func__, id); return -RIG_EPROTO; } /* ID * Reads transceiver ID number * * caller must give a buffer of KENWOOD_MAX_BUF_LEN size * */ int kenwood_get_id(RIG *rig, char *buf) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; size_t size = KENWOOD_MAX_BUF_LEN; return kenwood_transaction(rig, "ID", 2, buf, &size); } /* IF * Retrieves the transceiver status * */ static int kenwood_get_if(RIG *rig) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; struct kenwood_priv_caps *caps = kenwood_caps(rig); return kenwood_safe_transaction(rig, "IF", priv->info, KENWOOD_MAX_BUF_LEN, caps->if_len); } /* FR FT * Sets the RX/TX VFO or M.CH mode of the transceiver, does not set split * VFO, but leaves it unchanged if in split VFO mode. * */ int kenwood_set_vfo(RIG *rig, vfo_t vfo) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; char cmdbuf[6]; int retval; char vfo_function; switch (vfo) { case RIG_VFO_VFO: case RIG_VFO_A: vfo_function = '0'; break; case RIG_VFO_B: vfo_function = '1'; break; case RIG_VFO_MEM: vfo_function = '2'; break; case RIG_VFO_CURR: return RIG_OK; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %d\n", __func__, vfo); return -RIG_EINVAL; } sprintf(cmdbuf, "FR%c", vfo_function); /* set RX VFO */ retval = kenwood_simple_cmd(rig, cmdbuf); if (retval != RIG_OK) return retval; /* If split mode on, the don't change TxVFO */ if (priv->split != RIG_SPLIT_OFF) return RIG_OK; /* set TX VFO */ cmdbuf[1] = 'T'; return kenwood_simple_cmd(rig, cmdbuf); } /* FR FT * Sets the split RX/TX VFO or M.CH mode of the transceiver. * */ int kenwood_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t txvfo) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; char cmdbuf[6]; int retval; unsigned char vfo_function; if(vfo != RIG_VFO_CURR) { switch (vfo) { case RIG_VFO_VFO: case RIG_VFO_A: vfo_function = '0'; break; case RIG_VFO_B: vfo_function = '1'; break; case RIG_VFO_MEM: vfo_function = '2'; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %d\n", __func__, vfo); return -RIG_EINVAL; } /* set RX VFO */ sprintf(cmdbuf, "FR%c", vfo_function); retval = kenwood_simple_cmd(rig, cmdbuf); if (retval != RIG_OK) return retval; } /* Split off means Rx and Tx are the same */ if (split == RIG_SPLIT_OFF) { txvfo = vfo; if (txvfo == RIG_VFO_CURR) { retval = rig_get_vfo(rig, &txvfo); if (retval != RIG_OK) return retval; } } switch (txvfo) { case RIG_VFO_VFO: case RIG_VFO_A: vfo_function = '0'; break; case RIG_VFO_B: vfo_function = '1'; break; case RIG_VFO_MEM: vfo_function = '2'; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %d\n", __func__, txvfo); return -RIG_EINVAL; } /* set TX VFO */ sprintf(cmdbuf, "FT%c", vfo_function); retval = kenwood_simple_cmd(rig, cmdbuf); if (retval != RIG_OK) return retval; /* Remember whether split is on, for kenwood_set_vfo */ priv->split = split; return RIG_OK; } /* IF * Gets split VFO status from kenwood_get_if() * */ int kenwood_get_split_vfo_if(RIG *rig, vfo_t rxvfo, split_t *split, vfo_t *txvfo) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !split || !txvfo) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; int retval; retval = kenwood_get_if(rig); if (retval != RIG_OK) return retval; switch (priv->info[32]) { case '0': *split = RIG_SPLIT_OFF; break; case '1': *split = RIG_SPLIT_ON; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported split %c\n", __func__, priv->info[32]); return -RIG_EPROTO; } /* Remember whether split is on, for kenwood_set_vfo */ priv->split = *split; /* TODO: find where is the txvfo.. */ return RIG_OK; } /* * kenwood_get_vfo_if using byte 31 of the IF information field */ int kenwood_get_vfo_if(RIG *rig, vfo_t *vfo) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !vfo) return -RIG_EINVAL; int retval; struct kenwood_priv_data *priv = rig->state.priv; retval = kenwood_get_if(rig); if (retval != RIG_OK) return retval; switch (priv->info[30]) { case '0': *vfo = RIG_VFO_A; break; case '1': *vfo = RIG_VFO_B; break; case '2': *vfo = RIG_VFO_MEM; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %c\n", __func__, priv->info[30]); return -RIG_EPROTO; } return RIG_OK; } /* * kenwood_set_freq */ int kenwood_set_freq(RIG *rig, vfo_t vfo, freq_t freq) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char freqbuf[16]; unsigned char vfo_letter; vfo_t tvfo; tvfo = (vfo==RIG_VFO_CURR || vfo==RIG_VFO_VFO) ? rig->state.current_vfo : vfo; switch (tvfo) { case RIG_VFO_A: vfo_letter = 'A'; break; case RIG_VFO_B: vfo_letter = 'B'; break; case RIG_VFO_C: vfo_letter = 'C'; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %d\n", __func__, vfo); return -RIG_EINVAL; } sprintf(freqbuf, "F%c%011ld", vfo_letter, (long)freq); return kenwood_simple_cmd(rig, freqbuf); } int kenwood_get_freq_if(RIG *rig, vfo_t vfo, freq_t *freq) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !freq) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; char freqbuf[50]; int retval; retval = kenwood_get_if(rig); if (retval != RIG_OK) return retval; memcpy(freqbuf, priv->info, 15); freqbuf[14] = '\0'; sscanf(freqbuf + 2, "%"SCNfreq, freq); return RIG_OK; } /* * kenwood_get_freq */ int kenwood_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !freq) return -RIG_EINVAL; char freqbuf[50]; char cmdbuf[4]; int retval; unsigned char vfo_letter; vfo_t tvfo; tvfo = (vfo == RIG_VFO_CURR || vfo==RIG_VFO_VFO) ? rig->state.current_vfo : vfo; /* memory frequency cannot be read with an Fx command, use IF */ if (tvfo == RIG_VFO_MEM) { return kenwood_get_freq_if(rig, vfo, freq); } switch (tvfo) { case RIG_VFO_A: vfo_letter = 'A'; break; case RIG_VFO_B: vfo_letter = 'B'; break; case RIG_VFO_C: vfo_letter = 'C'; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %d\n", __func__, vfo); return -RIG_EINVAL; } sprintf(cmdbuf, "F%c", vfo_letter); retval = kenwood_safe_transaction(rig, cmdbuf, freqbuf, 50, 14); if (retval != RIG_OK) return retval; sscanf(freqbuf+2, "%"SCNfreq, freq); return RIG_OK; } int kenwood_get_rit(RIG *rig, vfo_t vfo, shortfreq_t *rit) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !rit) return -RIG_EINVAL; int retval; char buf[6]; struct kenwood_priv_data *priv = rig->state.priv; retval = kenwood_get_if(rig); if (retval != RIG_OK) return retval; memcpy(buf, &priv->info[18], 5); buf[5] = '\0'; *rit = atoi(buf); return RIG_OK; } /* * rit can only move up/down by 10 Hz, so we use a loop... */ int kenwood_set_rit(RIG * rig, vfo_t vfo, shortfreq_t rit) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char buf[4]; int retval, i; if (rit == 0) return kenwood_simple_cmd(rig, "RC"); sprintf(buf, "R%c", (rit > 0) ? 'U' : 'D'); retval = kenwood_simple_cmd(rig, "RC"); if (retval != RIG_OK) return retval; for (i = 0; i < abs(rint(rit/10)); i++) retval = kenwood_simple_cmd(rig, buf); return retval; } /* * rit and xit are the same */ int kenwood_get_xit(RIG *rig, vfo_t vfo, shortfreq_t *rit) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !rit) return -RIG_EINVAL; return kenwood_get_rit(rig, vfo, rit); } int kenwood_set_xit(RIG *rig, vfo_t vfo, shortfreq_t rit) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; return kenwood_set_rit(rig, vfo, rit); } int kenwood_scan(RIG *rig, vfo_t vfo, scan_t scan, int ch) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; return kenwood_simple_cmd(rig, scan == RIG_SCAN_STOP ? "SC0" : "SC1"); } /* * 000 No select * 002 FM Wide * 003 FM Narrow * 005 AM * 007 SSB * 009 CW * 010 CW NARROW */ /* XXX revise */ static int kenwood_set_filter(RIG *rig, pbwidth_t width) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char *cmd; if (width <= Hz(250)) cmd = "FL010009"; else if(width <= Hz(500)) cmd = "FL002009"; else if(width <= kHz(2.7)) cmd = "FL007007"; else if(width <= kHz(6)) cmd = "FL005005"; else cmd = "FL002010"; return kenwood_simple_cmd(rig, cmd); } /* * kenwood_set_mode */ int kenwood_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; struct kenwood_priv_caps *caps = kenwood_caps(rig); char buf[6]; char kmode; int err; kmode = rmode2kenwood(mode, caps->mode_table); if (kmode < 0 ) { rig_debug(RIG_DEBUG_WARN, "%s: unsupported mode '%s'\n", __func__, rig_strrmode(mode)); return -RIG_EINVAL; } if (kmode != 0) { sprintf(buf, "MD%c", '0' + kmode); kenwood_simple_cmd(rig, buf); } if (rig->caps->rig_model == RIG_MODEL_TS450S || rig->caps->rig_model == RIG_MODEL_TS690S || rig->caps->rig_model == RIG_MODEL_TS850 || rig->caps->rig_model == RIG_MODEL_TS950SDX) { err = kenwood_set_filter(rig, width); /* non fatal */ } return RIG_OK; } static int kenwood_get_filter(RIG *rig, pbwidth_t *width) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !width) return -RIG_EINVAL; int err, f, f1, f2; char buf[10]; err = kenwood_safe_transaction(rig, "FL", buf, sizeof(buf), 9); if (err != RIG_OK) return err; buf[8] = '\0'; f2 = atoi(&buf[5]); buf[5] = '\0'; f1 = atoi(&buf[2]); if (f2 > f1) f = f2; else f = f1; switch (f) { case 2: *width = kHz(12); break; case 3: case 5: *width = kHz(6); break; case 7: *width = kHz(2.7); break; case 9: *width = Hz(500); break; case 10: *width = Hz(250); break; } return RIG_OK; } /* * kenwood_get_mode */ int kenwood_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !mode || !width) return -RIG_EINVAL; struct kenwood_priv_caps *caps = kenwood_caps(rig); char modebuf[10]; int retval; retval = kenwood_safe_transaction(rig, "MD", modebuf, 6, 4); if (retval != RIG_OK) return retval; *mode = kenwood2rmode(modebuf[2] - '0', caps->mode_table); /* XXX ? */ *width = rig_passband_normal(rig, *mode); return RIG_OK; } /* This is used when the radio does not support MD; for mode reading */ int kenwood_get_mode_if(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !mode || !width) return -RIG_EINVAL; int err; struct kenwood_priv_caps *caps = kenwood_caps(rig); struct kenwood_priv_data *priv = rig->state.priv; err = kenwood_get_if(rig); if (err != RIG_OK) return err; *mode = kenwood2rmode(priv->info[29] - '0', caps->mode_table); *width = rig_passband_normal(rig, *mode); if (rig->caps->rig_model == RIG_MODEL_TS450S || rig->caps->rig_model == RIG_MODEL_TS690S || rig->caps->rig_model == RIG_MODEL_TS850 || rig->caps->rig_model == RIG_MODEL_TS950SDX) { err = kenwood_get_filter(rig, width); /* non fatal */ } return RIG_OK; } int kenwood_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char levelbuf[16]; int i, kenwood_val; if (RIG_LEVEL_IS_FLOAT(level)) kenwood_val = val.f * 255; else kenwood_val = val.i; switch (level) { case RIG_LEVEL_RFPOWER: /* XXX check level range */ sprintf(levelbuf, "PC%03d", kenwood_val); break; case RIG_LEVEL_AF: sprintf(levelbuf, "AG%03d", kenwood_val); break; case RIG_LEVEL_RF: /* XXX check level range */ sprintf(levelbuf, "RG%03d", kenwood_val); break; case RIG_LEVEL_SQL: sprintf(levelbuf, "SQ%03d", kenwood_val); break; case RIG_LEVEL_AGC: if (kenwood_val > 3) kenwood_val = 3; /* 0.. 255 */ sprintf(levelbuf, "GT%03d", 84*kenwood_val); break; case RIG_LEVEL_ATT: /* set the attenuator if a correct value is entered */ if (val.i == 0) sprintf(levelbuf, "RA00"); else { for (i=0; istate.attenuator[i]; i++) { if (val.i == rig->state.attenuator[i]) { sprintf(levelbuf, "RA%02d", i+1); break; } } if (val.i != rig->state.attenuator[i]) return -RIG_EINVAL; } break; case RIG_LEVEL_PREAMP: /* set the preamp if a correct value is entered */ if (val.i == 0) sprintf(levelbuf, "PA0"); else { for (i=0; istate.preamp[i]; i++) { if (val.i == rig->state.preamp[i]) { sprintf(levelbuf, "PA%01d", i+1); break; } } if (val.i != rig->state.preamp[i]) return -RIG_EINVAL; } break; case RIG_LEVEL_SLOPE_HIGH: if(val.i>20 || val.i < 0) return -RIG_EINVAL; sprintf(levelbuf, "SH%02d",(val.i)); break; case RIG_LEVEL_SLOPE_LOW: if(val.i>20 || val.i < 0) return -RIG_EINVAL; sprintf(levelbuf, "SL%02d",(val.i)); break; case RIG_LEVEL_CWPITCH: if(val.i > 1000 || val.i < 400) return -RIG_EINVAL; sprintf(levelbuf, "PT%02d", (val.i / 50) - 8); break; case RIG_LEVEL_KEYSPD: if(val.i > 50 || val.i < 5) return -RIG_EINVAL; sprintf(levelbuf, "KS%03d", val.i); break; default: rig_debug(RIG_DEBUG_ERR, "Unsupported set_level %d", level); return -RIG_EINVAL; } return kenwood_simple_cmd(rig, levelbuf); } int get_kenwood_level(RIG *rig, const char *cmd, int cmd_len, float *f) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !cmd || !f) return -RIG_EINVAL; char lvlbuf[10]; int retval; int lvl; retval = kenwood_safe_transaction(rig, cmd, lvlbuf, 10, 6); if (retval != RIG_OK) return retval; /* 000..255 */ sscanf(lvlbuf+2, "%d", &lvl); *f = (float)lvl/255.0; return RIG_OK; }; /* * kenwood_get_level */ int kenwood_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !val) return -RIG_EINVAL; char lvlbuf[50]; int retval; int lvl; int i, ret, agclevel; size_t lvl_len; switch (level) { case RIG_LEVEL_RAWSTR: retval = kenwood_safe_transaction(rig, "SM", lvlbuf, 10, 7); if (retval != RIG_OK) return retval; /* XXX atoi ? */ sscanf(lvlbuf+2, "%d", &val->i); /* rawstr */ break; case RIG_LEVEL_STRENGTH: retval = kenwood_safe_transaction(rig, "SM", lvlbuf, 10, 7); if (retval != RIG_OK) return retval; sscanf(lvlbuf+2, "%d", &val->i); /* rawstr */ if (rig->caps->str_cal.size) val->i = (int) rig_raw2val(val->i, &rig->caps->str_cal); else val->i = (val->i * 4) - 54; break; case RIG_LEVEL_ATT: retval = kenwood_safe_transaction(rig, "RA", lvlbuf, 50, 5); if (retval != RIG_OK) return retval; sscanf(lvlbuf+2, "%d", &lvl); if (lvl == 0) { val->i = 0; } else { for (i=0; istate.attenuator[i] == 0) { rig_debug(RIG_DEBUG_ERR, "%s: " "unexpected att level %d\n", __func__, lvl); return -RIG_EPROTO; } } if (i != lvl) return -RIG_EINTERNAL; val->i = rig->state.attenuator[i-1]; } break; case RIG_LEVEL_PREAMP: retval = kenwood_safe_transaction(rig, "PA", lvlbuf, 50, 4); if (retval != RIG_OK) return retval; if (lvlbuf[2] == '0') val->i = 0; else if (isdigit(lvlbuf[2])) { lvl = lvlbuf[2]-'0'; for (i=0; istate.preamp[i] == 0) { rig_debug(RIG_DEBUG_ERR,"%s: " "unexpected preamp level %d\n", __func__, lvl); return -RIG_EPROTO; } } if (i != lvl) return -RIG_EINTERNAL; val->i = rig->state.preamp[i-1]; } else { rig_debug(RIG_DEBUG_ERR,"%s: " "unexpected preamp char '%c'\n", __func__, lvlbuf[2]); return -RIG_EPROTO; } break; case RIG_LEVEL_RFPOWER: return get_kenwood_level(rig, "PC", 3, &val->f); case RIG_LEVEL_AF: return get_kenwood_level(rig, "AG", 3, &val->f); case RIG_LEVEL_RF: return get_kenwood_level(rig, "RG", 3, &val->f); case RIG_LEVEL_SQL: return get_kenwood_level(rig, "SQ", 3, &val->f); case RIG_LEVEL_MICGAIN: return get_kenwood_level(rig, "MG", 3, &val->f); case RIG_LEVEL_AGC: ret = get_kenwood_level(rig, "GT", 3, &val->f); agclevel = 255 * val->f; if (agclevel == 0) val->i = 0; else if (agclevel < 85) val->i = 1; else if (agclevel < 170) val->i = 2; else if (agclevel <= 255) val->i = 3; return ret; case RIG_LEVEL_SLOPE_LOW: lvl_len = 50; retval = kenwood_transaction (rig, "SL", 3, lvlbuf, &lvl_len); if (retval != RIG_OK) return retval; lvlbuf[4]='\0'; val->i=atoi(&lvlbuf[2]); break; case RIG_LEVEL_SLOPE_HIGH: lvl_len = 50; retval = kenwood_transaction (rig, "SH", 3, lvlbuf, &lvl_len); if (retval != RIG_OK) return retval; lvlbuf[4]='\0'; val->i=atoi(&lvlbuf[2]); break; case RIG_LEVEL_CWPITCH: retval = kenwood_safe_transaction(rig, "PT", lvlbuf, 50, 5); if (retval != RIG_OK) return retval; sscanf(lvlbuf+2, "%d", &val->i); val->i = (val->i * 1000) + 1000; /* 00 - 08 */ break; case RIG_LEVEL_KEYSPD: retval = kenwood_safe_transaction(rig, "KS", lvlbuf, 50, 6); if (retval != RIG_OK) return retval; sscanf(lvlbuf+2, "%d", &val->i); break; case RIG_LEVEL_IF: case RIG_LEVEL_APF: case RIG_LEVEL_NR: case RIG_LEVEL_PBT_IN: case RIG_LEVEL_PBT_OUT: case RIG_LEVEL_NOTCHF: case RIG_LEVEL_COMP: case RIG_LEVEL_BKINDL: case RIG_LEVEL_BALANCE: return -RIG_ENIMPL; default: rig_debug(RIG_DEBUG_ERR, "Unsupported get_level %d", level); return -RIG_EINVAL; } return RIG_OK; } int kenwood_set_func(RIG *rig, vfo_t vfo, setting_t func, int status) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char buf[6]; /* longest cmd is GTxxx */ switch (func) { case RIG_FUNC_NB: sprintf(buf, "NB%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_ABM: sprintf(buf, "AM%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_COMP: sprintf(buf, "PR%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_TONE: sprintf(buf, "TO%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_TSQL: sprintf(buf, "CT%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_VOX: sprintf(buf, "VX%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_FAGC: sprintf(buf, "GT00%c", (status == 0) ? '4' : '2'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_NR: sprintf(buf, "NR%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_BC: sprintf(buf, "BC%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_ANF: sprintf(buf, "NT%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_LOCK: sprintf(buf, "LK%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case RIG_FUNC_AIP: sprintf(buf, "MX%c", (status == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); default: rig_debug(RIG_DEBUG_ERR, "Unsupported set_func %#x", func); return -RIG_EINVAL; } return -RIG_EINVAL; } /* * works for any 'format 1' command * answer is always 4 bytes: two byte command id, status and terminator */ static int get_kenwood_func(RIG *rig, const char *cmd, int *status) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !cmd || !status) return -RIG_EINVAL; int retval; char buf[10]; retval = kenwood_safe_transaction(rig, cmd, buf, 10, 4); if (retval != RIG_OK) return retval; *status = buf[2] == '0' ? 0 : 1; return RIG_OK; }; /* * kenwood_get_func */ int kenwood_get_func(RIG *rig, vfo_t vfo, setting_t func, int *status) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !status) return -RIG_EINVAL; char fctbuf[20]; int retval; switch (func) { case RIG_FUNC_FAGC: retval = kenwood_safe_transaction(rig, "GT", fctbuf, 20, 6); if (retval != RIG_OK) return retval; *status = fctbuf[4] != '4' ? 1 : 0; return RIG_OK; case RIG_FUNC_NB: return get_kenwood_func(rig, "NB", status); case RIG_FUNC_ABM: return get_kenwood_func(rig, "AM", status); case RIG_FUNC_COMP: return get_kenwood_func(rig, "PR", status); case RIG_FUNC_TONE: return get_kenwood_func(rig, "TO", status); case RIG_FUNC_TSQL: return get_kenwood_func(rig, "CT", status); case RIG_FUNC_VOX: return get_kenwood_func(rig, "VX", status); case RIG_FUNC_NR: return get_kenwood_func(rig, "NR", status); /* FIXME on TS2000 */ case RIG_FUNC_BC: return get_kenwood_func(rig, "BC", status); case RIG_FUNC_ANF: return get_kenwood_func(rig, "NT", status); case RIG_FUNC_LOCK: return get_kenwood_func(rig, "LK", status); case RIG_FUNC_AIP: return get_kenwood_func(rig, "MX", status); default: rig_debug(RIG_DEBUG_ERR, "Unsupported get_func %#x", func); return -RIG_EINVAL; } return -RIG_EINVAL; } /* * kenwood_set_ctcss_tone * Assumes rig->caps->ctcss_list != NULL * * Warning! This is untested stuff! May work at least on TS-870S * Please owners report to me , thanks. --SF */ int kenwood_set_ctcss_tone(RIG *rig, vfo_t vfo, tone_t tone) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; const struct rig_caps *caps; char tonebuf[16]; int i; caps = rig->caps; /* TODO: replace 200 by something like RIGTONEMAX */ for (i = 0; caps->ctcss_list[i] != 0 && i<200; i++) { if (caps->ctcss_list[i] == tone) break; } if (caps->ctcss_list[i] != tone) return -RIG_EINVAL; /* TODO: replace menu no 57 by a define */ sprintf(tonebuf, "EX%03d%04d", 57, i+1); return kenwood_simple_cmd(rig, tonebuf); } int kenwood_set_ctcss_tone_tn(RIG *rig, vfo_t vfo, tone_t tone) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; const struct rig_caps *caps = rig->caps; char buf[6]; int i; /* XXX 40 is a fixed constant */ for (i = 0; caps->ctcss_list[i] != 0 && i < 40; i++) { if (tone == caps->ctcss_list[i]) break; } if (tone != caps->ctcss_list[i]) return -RIG_EINVAL; sprintf(buf, "TN%02d", i + 1); return kenwood_simple_cmd(rig, buf); } /* * kenwood_get_ctcss_tone * Assumes rig->state.priv != NULL */ int kenwood_get_ctcss_tone(RIG *rig, vfo_t vfo, tone_t *tone) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !tone) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; const struct rig_caps *caps; char tonebuf[3]; int i, retval; unsigned int tone_idx; caps = rig->caps; retval = kenwood_get_if(rig); if (retval != RIG_OK) return retval; memcpy(tonebuf, &priv->info[34], 2); tonebuf[2] = '\0'; tone_idx = atoi(tonebuf); if (tone_idx == 0) { rig_debug(RIG_DEBUG_ERR, "%s: CTCSS tone is zero (%s)\n", __func__, tonebuf); return -RIG_EPROTO; } /* check this tone exists. That's better than nothing. */ for (i = 0; ictcss_list[i] == 0) { rig_debug(RIG_DEBUG_ERR, "%s: CTCSS NG (%04d)\n", __func__, tone_idx); return -RIG_EPROTO; } } *tone = caps->ctcss_list[tone_idx-1]; return RIG_OK; } int kenwood_set_ctcss_sql(RIG *rig, vfo_t vfo, tone_t tone) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; const struct rig_caps *caps = rig->caps; char buf[6]; int i; for (i = 0; caps->ctcss_list[i] != 0 && i < 40; i++) { if (tone == caps->ctcss_list[i]) break; } if (tone != caps->ctcss_list[i]) return -RIG_EINVAL; sprintf(buf, "CN%02d", i + 1); return kenwood_simple_cmd(rig, buf); } int kenwood_get_ctcss_sql(RIG *rig, vfo_t vfo, tone_t *tone) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !tone) return -RIG_EINVAL; const struct rig_caps *caps; char tonebuf[6]; int i, retval; unsigned int tone_idx; caps = rig->caps; retval = kenwood_safe_transaction(rig, "CT", tonebuf, 6, 5); if (retval != RIG_OK) return retval; tone_idx = atoi(tonebuf+2); if (tone_idx == 0) { rig_debug(RIG_DEBUG_ERR, "%s: CTCSS is zero (%s)\n", __func__, tonebuf); return -RIG_EPROTO; } /* check this tone exists. That's better than nothing. */ for (i = 0; ictcss_list[i] == 0) { rig_debug(RIG_DEBUG_ERR, "%s: CTCSS NG (%04d)\n", __func__, tone_idx); return -RIG_EPROTO; } } *tone = caps->ctcss_list[tone_idx-1]; return RIG_OK; } /* * set the aerial/antenna to use */ int kenwood_set_ant(RIG * rig, vfo_t vfo, ant_t ant) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; const char *cmd; switch (ant) { case RIG_ANT_1: cmd = "AN1"; break; case RIG_ANT_2: cmd = "AN2"; break; case RIG_ANT_3: cmd = "AN3"; break; case RIG_ANT_4: cmd = "AN4"; break; default: return -RIG_EINVAL; } return kenwood_simple_transaction(rig, cmd, 4); } int kenwood_set_ant_no_ack(RIG * rig, vfo_t vfo, ant_t ant) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; const char *cmd; switch (ant) { case RIG_ANT_1: cmd = "AN1"; break; case RIG_ANT_2: cmd = "AN2"; break; case RIG_ANT_3: cmd = "AN3"; break; case RIG_ANT_4: cmd = "AN4"; break; default: return -RIG_EINVAL; } return kenwood_simple_cmd(rig, cmd); } /* * get the aerial/antenna in use */ int kenwood_get_ant (RIG *rig, vfo_t vfo, ant_t *ant) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !ant) return -RIG_EINVAL; char ackbuf[6]; int retval; retval = kenwood_safe_transaction(rig, "AN", ackbuf, 6, 4); if (retval != RIG_OK) return retval; if (ackbuf[2] < '1' || ackbuf[2] > '9') return -RIG_EPROTO; *ant = RIG_ANT_N(ackbuf[2]-'1'); /* XXX check that the returned antenna is valid for the current rig */ return RIG_OK; } /* * kenwood_get_ptt */ int kenwood_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !ptt) return -RIG_EINVAL; struct kenwood_priv_data *priv = rig->state.priv; int retval; retval = kenwood_get_if(rig); if (retval != RIG_OK) return retval; *ptt = priv->info[28] == '0' ? RIG_PTT_OFF : RIG_PTT_ON; return RIG_OK; } int kenwood_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) { const char *ptt_cmd; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; switch (ptt) { case RIG_PTT_ON: ptt_cmd = "TX"; break; case RIG_PTT_ON_MIC: ptt_cmd = "TX0"; break; case RIG_PTT_ON_DATA: ptt_cmd = "TX1"; break; case RIG_PTT_OFF: ptt_cmd = "RX"; break; default: return -RIG_EINVAL; } return kenwood_simple_cmd(rig, ptt_cmd); } int kenwood_set_ptt_safe(RIG *rig, vfo_t vfo, ptt_t ptt) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; int err; ptt_t current_ptt; err = kenwood_get_ptt(rig, vfo, ¤t_ptt); if (err != RIG_OK) return err; if (current_ptt == ptt) return RIG_OK; return kenwood_simple_cmd(rig, (ptt == RIG_PTT_ON) ? "TX" : "RX"); } /* * kenwood_get_dcd */ int kenwood_get_dcd(RIG *rig, vfo_t vfo, dcd_t *dcd) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !dcd) return -RIG_EINVAL; char busybuf[10]; int retval; retval = kenwood_safe_transaction(rig, "BY", busybuf, 10, 4); if (retval != RIG_OK) return retval; *dcd = (busybuf[2] == '1') ? RIG_DCD_ON : RIG_DCD_OFF; return RIG_OK; } /* * kenwood_set_trn */ int kenwood_set_trn(RIG *rig, int trn) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; return kenwood_simple_transaction(rig, (trn == RIG_TRN_RIG) ? "AI1" : "AI0", 4); } /* * kenwood_get_trn */ int kenwood_get_trn(RIG *rig, int *trn) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !trn) return -RIG_EINVAL; char trnbuf[6]; int retval; retval = kenwood_safe_transaction(rig, "AI", trnbuf, 6, 4); if (retval != RIG_OK) return retval; *trn = trnbuf[2] != '0' ? RIG_TRN_RIG : RIG_TRN_OFF; return RIG_OK; } /* * kenwood_set_powerstat */ int kenwood_set_powerstat(RIG *rig, powerstat_t status) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; return kenwood_simple_transaction(rig, (status == RIG_POWER_ON) ? "PS1" : "PS0", 4); } /* * kenwood_get_powerstat */ int kenwood_get_powerstat(RIG *rig, powerstat_t *status) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !status) return -RIG_EINVAL; char pwrbuf[6]; int retval; retval = kenwood_safe_transaction(rig, "PS", pwrbuf, 6, 4); if (retval != RIG_OK) return retval; *status = pwrbuf[2] == '0' ? RIG_POWER_OFF : RIG_POWER_ON; return RIG_OK; } /* * kenwood_reset */ int kenwood_reset(RIG *rig, reset_t reset) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char rstbuf[6]; char rst; switch(reset) { case RIG_RESET_VFO: rst = '1'; break; case RIG_RESET_MASTER: rst = '2'; break; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported reset %d\n", __func__, reset); return -RIG_EINVAL; } sprintf(rstbuf, "SR%c", rst); /* this command has no answer */ return kenwood_simple_cmd(rig, rstbuf); } /* * kenwood_send_morse */ int kenwood_send_morse(RIG *rig, vfo_t vfo, const char *msg) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !msg) return -RIG_EINVAL; char morsebuf[30], m2[30]; int msg_len, buff_len, retval; const char *p; p = msg; msg_len = strlen(msg); while(msg_len > 0) { /* * TODO: check with "KY" if char buffer is available. * if not, sleep. * * Make the total message segments 28 characters * in length because Kenwood demands it. * Spaces fill in the message end. */ buff_len = msg_len > 24 ? 24 : msg_len; strncpy(m2, p, 24); m2[24] = '\0'; /* the command must consist of 28 bytes */ sprintf(morsebuf, "KY %-24s", m2); retval = kenwood_simple_cmd(rig, morsebuf); if (retval != RIG_OK) return retval; msg_len -= buff_len; p += buff_len; } return RIG_OK; } /* * kenwood_vfo_op */ int kenwood_vfo_op(RIG *rig, vfo_t vfo, vfo_op_t op) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; switch(op) { case RIG_OP_UP: return kenwood_simple_cmd(rig, "UP"); case RIG_OP_DOWN: return kenwood_simple_cmd(rig, "DN"); case RIG_OP_BAND_UP: return kenwood_simple_cmd(rig, "BU"); case RIG_OP_BAND_DOWN: return kenwood_simple_cmd(rig, "BD"); default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported op %#x\n", __func__, op); return -RIG_EINVAL; } } /* * kenwood_set_mem */ int kenwood_set_mem(RIG *rig, vfo_t vfo, int ch) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char buf[8]; /* * "MCbmm;" * where b is the bank number, mm the memory number. * b can be a space */ sprintf(buf, "MC %02d", ch); return kenwood_simple_cmd(rig, buf); } /* * kenwood_get_mem */ int kenwood_get_mem(RIG *rig, vfo_t vfo, int *ch) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !ch) return -RIG_EINVAL; char membuf[10]; int retval; /* * "MCbmm;" * where b is the bank number, mm the memory number. * b can be a space */ retval = kenwood_safe_transaction(rig, "MC", membuf, 10, 6); if (retval != RIG_OK) return retval; *ch = atoi(membuf+2); return RIG_OK; } int kenwood_get_mem_if(RIG *rig, vfo_t vfo, int *ch) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !ch) return -RIG_EINVAL; int err; char buf[4]; struct kenwood_priv_data *priv = rig->state.priv; err = kenwood_get_if(rig); if (err != RIG_OK) return err; memcpy(buf, &priv->info[26], 2); buf[2] = '\0'; *ch = atoi(buf); return RIG_OK; } int kenwood_get_channel(RIG *rig, channel_t *chan) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !chan) return -RIG_EINVAL; int err; char buf[26]; char cmd[8]; struct kenwood_priv_caps *caps = kenwood_caps(rig); /* put channel num in the command string */ sprintf(cmd, "MR0 %02d", chan->channel_num); err = kenwood_safe_transaction(rig, cmd, buf, 26, 24); if (err != RIG_OK) return err; memset(chan, 0x00, sizeof(channel_t)); chan->vfo = RIG_VFO_VFO; /* MR0 1700005890000510 ; * MRs ccfffffffffffMLTtt ; */ /* parse from right to left */ /* XXX based on the available documentation, there is no command * to read out the filters of a given memory channel. The rig, however, * stores this information. */ if (buf[19] == '0' || buf[19] == ' ') chan->ctcss_tone = 0; else { buf[22] = '\0'; if (rig->caps->ctcss_list) chan->ctcss_tone = rig->caps->ctcss_list[atoi(&buf[20])]; } /* memory lockout */ if (buf[18] == '1') chan->flags |= RIG_CHFLAG_SKIP; chan->mode = kenwood2rmode(buf[17] - '0', caps->mode_table); buf[17] = '\0'; chan->freq = atoi(&buf[6]); if (chan->freq == RIG_FREQ_NONE) return -RIG_ENAVAIL; buf[6] = '\0'; chan->channel_num = atoi(&buf[4]); /* split freq */ cmd[2] = '1'; err = kenwood_safe_transaction(rig, cmd, buf, 26, 24); if (err != RIG_OK) return err; chan->tx_mode = kenwood2rmode(buf[17] - '0', caps->mode_table); buf[17] = '\0'; chan->tx_freq = atoi(&buf[6]); if (chan->freq == chan->tx_freq) { chan->tx_freq = RIG_FREQ_NONE; chan->tx_mode = RIG_MODE_NONE; chan->split = RIG_SPLIT_OFF; } else chan->split = RIG_SPLIT_ON; return RIG_OK; } int kenwood_set_channel(RIG *rig, const channel_t *chan) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !chan) return -RIG_EINVAL; char buf[26]; char mode, tx_mode = 0; int err; int tone = 0; struct kenwood_priv_caps *caps = kenwood_caps(rig); mode = rmode2kenwood(chan->mode, caps->mode_table); if (mode < 0 ) { rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode '%s'\n", __func__, rig_strrmode(chan->mode)); return -RIG_EINVAL; } if (chan->split == RIG_SPLIT_ON) { tx_mode = rmode2kenwood(chan->tx_mode, caps->mode_table); if (tx_mode < 0 ) { rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode '%s'\n", __func__, rig_strrmode(chan->tx_mode)); return -RIG_EINVAL; } } /* find tone */ if (chan->ctcss_tone) { for (tone = 0; rig->caps->ctcss_list[tone] != 0; tone++) { if (chan->ctcss_tone == rig->caps->ctcss_list[tone]) break; } if (chan->ctcss_tone != rig->caps->ctcss_list[tone]) tone = 0; } sprintf(buf, "MW0 %02d%011d%c%c%c%02d ", /* note the space at the end */ chan->channel_num, (int) chan->freq, '0' + mode, (chan->flags & RIG_CHFLAG_SKIP) ? '1' : '0', chan->ctcss_tone ? '1' : '0', chan->ctcss_tone ? (tone + 1) : 0); err = kenwood_simple_cmd(rig, buf); if (err != RIG_OK) return err; sprintf(buf, "MW1 %02d%011d%c%c%c%02d ", chan->channel_num, (chan->split == RIG_SPLIT_ON) ? ((int) chan->tx_freq) : 0, (chan->split == RIG_SPLIT_ON) ? ('0' + tx_mode) : '0', (chan->flags & RIG_CHFLAG_SKIP) ? '1' : '0', chan->ctcss_tone ? '1' : '0', chan->ctcss_tone ? (tone + 1) : 0); return kenwood_simple_cmd(rig, buf); } int kenwood_set_ext_parm(RIG *rig, token_t token, value_t val) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return -RIG_EINVAL; char buf[4]; switch (token) { case TOK_VOICE: return kenwood_simple_cmd(rig, "VR"); case TOK_FINE: sprintf(buf, "FS%c", (val.i == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case TOK_XIT: sprintf(buf, "XT%c", (val.i == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); case TOK_RIT: sprintf(buf, "RT%c", (val.i == 0) ? '0' : '1'); return kenwood_simple_cmd(rig, buf); } return -RIG_EINVAL; } int kenwood_get_ext_parm(RIG *rig, token_t token, value_t *val) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig || !val) return -RIG_EINVAL; int err; struct kenwood_priv_data *priv = rig->state.priv; switch (token) { case TOK_FINE: return get_kenwood_func(rig, "FS", &val->i); case TOK_XIT: err = kenwood_get_if(rig); if (err != RIG_OK) return err; val->i = (priv->info[24] == '1') ? 1 : 0; return RIG_OK; case TOK_RIT: err = kenwood_get_if(rig); if (err != RIG_OK) return err; val->i = (priv->info[23] == '1') ? 1 : 0; return RIG_OK; } return -RIG_ENIMPL; } /* * kenwood_get_info * supposed to work only for TS2000... */ const char* kenwood_get_info(RIG *rig) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rig) return "*rig == NULL"; char firmbuf[10]; int retval; retval = kenwood_safe_transaction(rig, "TY", firmbuf, 10, 6); if (retval != RIG_OK) return NULL; switch (firmbuf[4]) { case '0': return "Firmware: Overseas type"; case '1': return "Firmware: Japanese 100W type"; case '2': return "Firmware: Japanese 20W type"; default: return "Firmware: unknown"; } } #define IDBUFSZ 16 /* * proberigs_kenwood * * Notes: * There's only one rig possible per port. * * rig_model_t probeallrigs_kenwood(port_t *port, rig_probe_func_t cfunc, rig_ptr_t data) */ DECLARE_PROBERIG_BACKEND(kenwood) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); char idbuf[IDBUFSZ]; int id_len=-1, i, k_id; int retval=-1; int rates[] = { 115200, 57600, 38400, 19200, 9600, 4800, 1200, 0 }; /* possible baud rates */ int rates_idx; if (!port) return RIG_MODEL_NONE; if (port->type.rig != RIG_PORT_SERIAL) return RIG_MODEL_NONE; port->write_delay = port->post_write_delay = 0; port->parm.serial.stop_bits = 2; port->retry = 1; /* * try for all different baud rates */ for (rates_idx = 0; rates[rates_idx]; rates_idx++) { port->parm.serial.rate = rates[rates_idx]; port->timeout = 2*1000/rates[rates_idx] + 50; retval = serial_open(port); if (retval != RIG_OK) return RIG_MODEL_NONE; retval = write_block(port, "ID;", 3); id_len = read_string(port, idbuf, IDBUFSZ, ";\r", 2); close(port->fd); if (retval != RIG_OK || id_len < 0) continue; } if (retval != RIG_OK || id_len < 0 || !strcmp(idbuf, "ID;")) return RIG_MODEL_NONE; /* * reply should be something like 'IDxxx;' */ if (id_len != 5 || id_len != 6) { idbuf[7] = '\0'; rig_debug(RIG_DEBUG_VERBOSE, "probe_kenwood: protocol error, " " expected %d, received %d: %s\n", 6, id_len, idbuf); return RIG_MODEL_NONE; } /* first, try ID string */ for (i=0; kenwood_id_string_list[i].model != RIG_MODEL_NONE; i++) { if (!strncmp(kenwood_id_string_list[i].id, idbuf+2, 16)) { rig_debug(RIG_DEBUG_VERBOSE, "probe_kenwood: " "found %s\n", idbuf+2); if (cfunc) (*cfunc)(port, kenwood_id_string_list[i].model, data); return kenwood_id_string_list[i].model; } } /* then, try ID numbers */ k_id = atoi(idbuf+2); /* * Elecraft K2 returns same ID as TS570 */ if (k_id == 17) { retval = serial_open(port); if (retval != RIG_OK) return RIG_MODEL_NONE; retval = write_block(port, "K2;", 3); id_len = read_string(port, idbuf, IDBUFSZ, ";\r", 2); close(port->fd); if (retval != RIG_OK) return RIG_MODEL_NONE; /* * reply should be something like 'K2n;' */ if (id_len == 4 || !strcmp(idbuf, "K2")) { rig_debug(RIG_DEBUG_VERBOSE, "probe_kenwood: found K2\n"); if (cfunc) (*cfunc)(port, RIG_MODEL_K2, data); return RIG_MODEL_K2; } } for (i=0; kenwood_id_list[i].model != RIG_MODEL_NONE; i++) { if (kenwood_id_list[i].id == k_id) { rig_debug(RIG_DEBUG_VERBOSE, "probe_kenwood: " "found %03d\n", k_id); if (cfunc) (*cfunc)(port, kenwood_id_list[i].model, data); return kenwood_id_list[i].model; } } /* * not found in known table.... * update kenwood_id_list[]! */ rig_debug(RIG_DEBUG_WARN, "probe_kenwood: found unknown device " "with ID %03d, please report to Hamlib " "developers.\n", k_id); return RIG_MODEL_NONE; } /* * initrigs_kenwood is called by rig_backend_load */ DECLARE_INITRIG_BACKEND(kenwood) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); // rig_debug(RIG_DEBUG_VERBOSE, "kenwood: _init called\n"); rig_register(&ts950sdx_caps); rig_register(&ts50s_caps); rig_register(&ts140_caps); rig_register(&ts450s_caps); rig_register(&ts570d_caps); rig_register(&ts570s_caps); rig_register(&ts680s_caps); rig_register(&ts690s_caps); rig_register(&ts790_caps); rig_register(&ts850_caps); rig_register(&ts870s_caps); rig_register(&ts930_caps); rig_register(&ts2000_caps); rig_register(&trc80_caps); rig_register(&k2_caps); rig_register(&k3_caps); rig_register(&ts440_caps); rig_register(&ts940_caps); rig_register(&ts711_caps); rig_register(&ts811_caps); rig_register(&r5000_caps); rig_register(&tmd700_caps); rig_register(&thd7a_caps); rig_register(&thd72a_caps); rig_register(&thf7e_caps); rig_register(&thg71_caps); rig_register(&tmv7_caps); rig_register(&tmd710_caps); rig_register(&ts590_caps); rig_register(&ts590_caps); rig_register(&ts480_caps); rig_register(&thf6a_caps); rig_register(&transfox_caps); return RIG_OK; }