/* * Hamlib Kenwood backend - TS2000 description * Copyright (c) 2000-2002 by Stephane Fillod * * $Id: ts2k.c,v 1.5 2002-06-30 10:17:03 dedmons Exp $ * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * This code is has been substatiantially altered from the original * author's. Also, many new functions have been added and are * (C) Copyrighted 2002 by Dale E. Edmons (KD7ENI). The license * is unchanged, and fitness disclaimers still apply. */ /* * Copied kenwood.c to end of this file. When I change a function for the * ts2000 I will already have the kenwood version with the function prefix * changed to ts2k. Unique functions will be pointed to in this file, * whereas the unmodified version will be in kenwood.c. This simplifies * things during development and unused ts2k_() functions should go away * as soon as possible. * * Dale KD7ENI */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "kenwood.h" #include "ts2k.h" /* * I just read in kenwood.c and will modify the functions here. This way, * kenwood functions that actually work on other rigs won't be broken by * my hacks. Anything that works for all can be sent back over to kenwood.c * * Note: due to my compulsive laziness, I often abbreviate Kenwood TS-2000 * as simply ts2k, especially for code! * * Dale kd7eni */ /* * Hamlib Kenwood backend - main file * Copyright (c) 2000-2002 by Stephane Fillod * * $Id: ts2k.c,v 1.5 2002-06-30 10:17:03 dedmons Exp $ */ #include #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include /* POSIX terminal control definitions */ #include #include #include #include /* * modes in use by the "MD" command */ #define MD_NONE '0' #define MD_LSB '1' #define MD_USB '2' #define MD_CW '3' #define MD_FM '4' #define MD_AM '5' #define MD_FSK '6' #define MD_CWR '7' #define MD_FSKR '9' // Added the following two lists --Dale, kd7eni // FIXME: RIG_MODE_[FSKR|CWR] undefined in rig.h const int ts2k_mode_list[] = { RIG_MODE_NONE, RIG_MODE_LSB, RIG_MODE_USB, RIG_MODE_CW, RIG_MODE_FM, RIG_MODE_AM, RIG_MODE_RTTY, RIG_MODE_CW, RIG_MODE_RTTY }; long int ts2k_steps[2][10] = { {1000, 2500, 5000, 10000, 0, 0, 0, 0, 0, 0}, // ssb, cw, fsk {5000, 6250, 10000, 12500, 15000, 20000, 25000, 30000, 50000, 100000} // am/fm }; struct ts2k_id { rig_model_t model; int id; }; struct ts2k_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 ts2k_id ts2k_id_list[] = { {RIG_MODEL_R5000, 5}, {RIG_MODEL_TS870S, 15}, {RIG_MODEL_TS570D, 17}, {RIG_MODEL_TS570S, 18}, {RIG_MODEL_TS2000, 19}, /* correct --kd7eni */ {RIG_MODEL_NONE, UNKNOWN_ID}, /* end marker */ }; static const struct ts2k_id_string ts2k_id_string_list[] = { {RIG_MODEL_THD7A, "TH-D7"}, {RIG_MODEL_THD7AG, "TH-D7G"}, {RIG_MODEL_THF6A, "TH-F6"}, {RIG_MODEL_THF7E, "TH-F7"}, {RIG_MODEL_NONE, NULL}, /* end marker */ }; /* * 38 CTCSS sub-audible tones (17500 invalid for ctcss --kd7eni) */ const int ts2k_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, // 17500, /* Note: 17500 is not available as ctcss, only tone. --kd7eni */ 0, }; #define cmd_trm(rig) ((struct ts2k_priv_caps *)(rig)->caps->priv)->cmdtrm #define ta_quit rs->hold_decode = 0; return retval /** * kenwood_transaction * Assumes rig!=NULL rig->state!=NULL rig->caps!=NULL * * cmdstr - Command to be sent to the rig. Cmdstr can also be NULL, indicating * that only a reply is needed (nothing will be send). * data - Buffer for reply string. Can be NULL, indicating that no reply is * 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. */ /* FIXME: cmd_len appears to change and needs set every invocation? --Dale */ int ts2k_transaction(RIG * rig, const char *cmdstr, int cmd_len, char *data, size_t * datasize) { // return kenwood_transaction(rig, cmdstr, cmd_len, data, datasize); struct rig_state *rs; int retval; const char *cmdtrm = EOM_KEN; /* Default Command/Reply termination char */ int retry_read = 0; char *errtxt; #define MAX_RETRY_READ 5 rs = &rig->state; rs->hold_decode = 1; serial_flush(&rs->rigport); cmdtrm = cmd_trm(rig); if (cmdstr != NULL) { // rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 1) sending '%s'\n\n", cmdstr); retval = write_block(&rs->rigport, cmdstr, strlen(cmdstr)); if (retval != RIG_OK) { ta_quit; } #undef TH_ADD_CMDTRM #ifdef TH_ADD_CMDTRM // rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 2) sending '%s'\n\n", cmdtrm); retval = write_block(&rs->rigport, cmdtrm, strlen(cmdtrm)); if (retval != RIG_OK) { ta_quit; } #endif } /* FIXME: Everything below this line wants to be completely rewritten!!!! */ if (data == NULL || datasize <= 0) { rig->state.hold_decode = 0; return RIG_OK; /* don't want a reply */ } transaction_read: /* FIXME : TS-2000 gets alot of 'timedout' on read_string()! */ //rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 3a) reading %u bytes...\n", *datasize); retval = read_string(&rs->rigport, data, *datasize, cmdtrm, strlen(cmdtrm)); //rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 3b) read '%s', retval=%u\n\n", data, retval); *datasize = retval; if (retval > 0) { //rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 3b) read cmd '%s', retval=%u\n\n", data, retval); retval = RIG_OK; goto transaction_check; } /* Check that command termination is correct */ if (!strchr(cmdtrm, data[strlen(data)])) { if (retry_read++ < MAX_RETRY_READ) goto transaction_read; rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Command is not correctly terminated '%s'\n", data); retval = -RIG_EPROTO; ta_quit; } /* Errors */ if (strlen(data) == 2 && data[0] == 'E') { switch (data[0]) { case 'E': rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Communication Error for '%s'\n", cmdstr); break; case 'O': rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Communication Error for '%s'\n", cmdstr); break; case '?': rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Communication Error for '%s'\n", cmdstr); break; default: rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Hamlib Error for '%s'\n", cmdstr); break; } retval = -RIG_ERJCTED; ta_quit; } #undef CONFIG_STRIP_CMDTRM #ifdef CONFIG_STRIP_CMDTRM if (strlen(data) > 0) data[strlen(data) - 1] = '\0'; /* not very elegant, but should work. */ else data[0] = '\0'; #endif /* * Check that received the correct reply. The first two characters * should be the same as command. */ transaction_check: if (cmdstr && (toupper(data[0]) != toupper(cmdstr[0]) || toupper(data[1]) != toupper(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. */ if (retry_read++ < MAX_RETRY_READ) goto transaction_read; rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Unexpected reply '%s'\n", data); retval = -RIG_EPROTO; { ta_quit; } } retval = RIG_OK; transaction_quit: { ta_quit; } } /* * kenwood_set_vfo * Assumes rig!=NULL * * status: VFOA, VFOB, VFOC, Main, Sub, * MEMA, MEMC, CALLA, CALLC * VFO_AB, VFO_BA, ... * They all work! --Dale */ int ts2k_set_vfo(RIG * rig, vfo_t vfo) { unsigned char cmdbuf[10]; int ptt, ctrl, v,cmd_len, retval; static int sat_on; // temporary! to be removed! char vfo_function; // trivial case, but needs checked if enabled /*// if( (vfo == RIG_CTRL_MODE(RIG_CTRL_MAIN,RIG_VFO_ALL)) // || (vfo == RIG_CTRL_MODE(RIG_CTRL_SUB,RIG_VFO_ALL)) ) { // rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \ // ": Geez, you can't set *all* VFO's!\n"); // return -RIG_EINVAL; // } */ cmd_len = 10; ptt = ctrl = v = 0; // be optimistic (and ensure initialization) retval = RIG_OK; // Main/Sub Active Transceiver switch (vfo) { case RIG_VFO_A: case RIG_VFO_B: case RIG_VFO_AB: // split case RIG_VFO_BA: case RIG_CTRL_SAT: // FIXME: Not even close to correct case RIG_VFO_MAIN: case RIG_VFO_MEM_A: case RIG_VFO_CALL_A: ctrl = TS2K_CTRL_ON_MAIN; // FIXME : these are independent! ptt = TS2K_PTT_ON_MAIN; break; case RIG_VFO_C: case RIG_VFO_SUB: case RIG_VFO_MEM_C: case RIG_VFO_CALL_C: ctrl = TS2K_CTRL_ON_SUB; ptt = TS2K_PTT_ON_SUB; break; default: break; } // set now so "ft...;" and "fr...;" don't fail retval = ts2k_set_ctrl(rig, ptt, ctrl); if (retval != RIG_OK) return -RIG_EINVAL; // check if we need to skip the remainder v = (vfo == RIG_VFO_SUB) || (vfo == RIG_VFO_MAIN) || (vfo == RIG_VFO_CURR) || (vfo == RIG_VFO_VFO) // || (vfo == RIG_VFO_ALL) // yea, I know /* bit mask checks */ || (vfo & RIG_CTRL_SAT) // "fr...;", "ft...;" won't do! ; rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \ ": starting check.... vfo = 0x%X, v=%d\n", vfo, v); if (!v) { // start check // FIXME: this is a speed-up kludge but won't *always* work! if(sat_on) { retval = ts2k_sat_off(rig, vfo); // we gotta do it. if(retval != RIG_OK) return retval; sat_on = 0; } // RX Active Tuning switch (vfo) { case RIG_VFO_AB: // TX is opposite case RIG_VFO_A: case RIG_VFO_C: vfo_function = '0'; break; case RIG_VFO_BA: // TX is opposite case RIG_VFO_B: vfo_function = '1'; break; case RIG_VFO_MEM_A: case RIG_VFO_MEM_C: vfo_function = '2'; break; case RIG_VFO_CALL_A: case RIG_VFO_CALL_C: vfo_function = '3'; break; default: rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": unsupported VFO %u\n", vfo); return -RIG_EINVAL; break; } // ack_len is tmp cmd_len = sprintf(cmdbuf, "fr%c%s", vfo_function, cmd_trm(rig)); /* set RX VFO */ retval = ts2k_transaction(rig, cmdbuf, cmd_len, NULL, NULL); if (retval != RIG_OK) return -RIG_EINVAL; // TX Active tuning switch (vfo) { case RIG_VFO_A: case RIG_VFO_C: case RIG_VFO_BA: // opposite of above vfo_function = '0'; break; case RIG_VFO_AB: // opposite of above case RIG_VFO_B: vfo_function = '1'; break; case RIG_VFO_MEM_A: case RIG_VFO_MEM_C: // FIXME: need to handle vfo/mem split vfo_function = '2'; break; case RIG_VFO_CALL_A: case RIG_VFO_CALL_C: vfo_function = '3'; break; default: rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": unsupported VFO %u\n", vfo); return -RIG_EINVAL; } /* set TX VFO */ cmdbuf[1] = 't'; // removing this causes split to not function!!!! cmd_len = sprintf(cmdbuf, "ft%c%s", vfo_function, cmd_trm(rig)); retval = ts2k_transaction(rig, cmdbuf, cmd_len, NULL, NULL); if (retval != RIG_OK) return retval; } else { // Check further for special modes not using "fr...;", "ft...;" if(vfo & RIG_CTRL_SAT) { // test the SAT bit retval = ts2k_sat_on(rig, vfo); if (retval != RIG_OK) return retval; sat_on = 1; } else { rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \ ": VFO not changed, only PTT/CTRL\n"); } } /* * FIXME: some items like scan, satellite need checked and turned off here. * I've got a simple kludge to turn SAT *off* but it wants to be done * here. It's a bit expensive though and I'm trying to find a better * way to do SAT as well as others. ts2k_sat_off() reads the current, * sets it off, then writes it back so the user selected stuff don't * change unexpectedly. Now, SAT won't get turned off if first turned * on via the front panel. MEM and SCAN have similar quirks. * --Dale */ return retval; } /* we just turn SAT on here. It'll take some doing to run it! */ int ts2k_sat_on(RIG *rig, vfo_t vfo) { char cmd[20], ack[20]; int cmdlen, acklen; acklen = 20; if(!(vfo & RIG_CTRL_SAT)) return -RIG_EINTERNAL; // All right. Who called us!? // cmdlen = sprintf(cmd, "sa%07u;", 0); // Initial string to modify acklen = ts2k_transaction(rig, "sa;", 3, ack, &acklen); // Sat mode ON ack[2] = '1'; // Everything below is *nice*, this is *required* goto STest; // testing /* cmd is already full of '0's, but we set them again explicitly */ // SAT_VFO or SAT_MEM? if(vfo & RIG_CTRL_MEM) ack[8] = '1'; // sat mem ch 0-9 else ack[8] = '0'; // sat vfo /* Main or Sub as uplink? */ // Note: if both are set, Main is still uplink if(vfo & RIG_CTRL_MAIN) { ack[4] = '0'; // sat main=uplink } else if(vfo & RIG_CTRL_SUB) { ack[4] = '1'; // sat sub=uplink } // FIXME: Add Sat Trace here! // Trace REV if(vfo & RIG_CTRL_REV) ack[7] = '1'; // sat trace REV else ack[7] = '0'; // CTRL to main or sub? if ((vfo & RIG_VFO_CTRL) && (vfo & RIG_CTRL_SUB)) ack[5] = '1'; // sat CTRL on sub else ack[5] = '0'; // sat CTRL on main STest: rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \ ": sat = %s, vfo = 0x%X\n", cmd, vfo); // of coure, this is *required* too! return ts2k_transaction(rig, ack, acklen, NULL, NULL); } int ts2k_sat_off(RIG *rig, vfo_t vfo) { char cmd[20], ack[20]; int cmdlen, acklen, retval; acklen = 10; cmdlen = sprintf(cmd, "sa;"); retval = ts2k_transaction(rig, cmd, cmdlen, ack, &acklen); if(retval != RIG_OK) return retval; ack[2] = '0'; cmdlen = 20; return ts2k_transaction(rig, ack, acklen, NULL, NULL); } /* * kenwood_get_vfo * Assumes rig!=NULL, !vfo * * status: works perfect for implemented modes! --Dale * code's getting a little ugly. okay, real ugly. */ int ts2k_get_vfo(RIG * rig, vfo_t * vfo) { char vfobuf[50], r_vfo; char *ctrl_ptt; int vfo_len, retval, tmp; /* * FIXME: if FR != FT && rcvr==main, the mode = split! --kd7eni */ // Check which receiver so VFO_C may be detected (PTT/CTRL) ctrl_ptt = ts2k_get_ctrl(rig); if (ctrl_ptt == NULL) return -RIG_EINVAL; // rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: PTT/CTRL is %s\n", ctrl_ptt); /* query RX VFO */ vfo_len = 50; rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": sending fr; cmd/checking SAT. Expect TIMEDOUT if in SAT mode!\n"); retval = ts2k_transaction(rig, "fr;", 3, vfobuf, &vfo_len); /* "fr;" fails in satellite mode; interesting... */ if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": kenwood/ts2k.c\n" "FIXME: ts2k.c,\tThis is timeout cannot be prevented.\n"); tmp = retval; retval = ts2k_transaction(rig, "sa;", 3, vfobuf, &vfo_len); if (retval == RIG_OK) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": SAT=%s\n", vfobuf); if(vfobuf[2] == '1') { /* yes, we're in satellite mode! */ *vfo = RIG_CTRL_SAT; // FIXME: set the rest! /* TODO: write get_sat() and let it do the work */ return RIG_OK; } } return tmp; // return original "fr;" error! } // rig_debug(RIG_DEBUG_ERR, __FUNCTION__": checking fr; cmd.\n"); r_vfo = vfobuf[2]; //if (vfo_len != 4 || vfobuf[1] != 'R') { if (vfobuf[1] != 'R') { rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": unexpected answer %s, " "len=%u\n", vfobuf, vfo_len); return -RIG_ERJCTED; } rig_debug(RIG_DEBUG_ERR, __FUNCTION__": sending ft; cmd.\n"); vfo_len = 50; retval = ts2k_transaction(rig, "ft;", 3, vfobuf, &vfo_len); if (retval != RIG_OK) return retval; rig_debug(RIG_DEBUG_ERR, __FUNCTION__": checking ft; cmd.\n"); if (vfobuf[2] == r_vfo) { // check most common first rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: Non-Split.\n"); /* TODO: replace 0,1,2,.. constants by defines */ /* FIXME: return based on RIG_PTT_ON_???? or RIG_CTRL_ON_???? * may be different. We need to specify actual status. * Right now we pretend things are simpler. --kd7eni */ switch (vfobuf[2]) { case '0': if (ctrl_ptt[3] == '0') // we use CTRL as Active Transceiver *vfo = RIG_VFO_A; else if (ctrl_ptt[3] == '1') *vfo = RIG_VFO_C; else { // "There be errors here!" rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: VFO1 on erroneous xcvr %c\n", vfobuf[2]); return -RIG_EPROTO; } break; case '1': // only valid on Main--no checks required. *vfo = RIG_VFO_B; break; case '2': if (ctrl_ptt[3] == '0') // we use CTRL as Active Transceiver. *vfo = RIG_VFO_MEM_A; else if (ctrl_ptt[3] == '1') *vfo = RIG_VFO_MEM_C; else return -RIG_EPROTO; break; case '3': if (ctrl_ptt[3] == '0') // we use CTRL as current *vfo = RIG_VFO_CALL_A; else if (ctrl_ptt[3] == '1') // we use CTRL as current *vfo = RIG_VFO_CALL_C; else return -RIG_EPROTO; break; default: // Different or newer rig types... rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: unsupported VFO %c\n", vfobuf[2]); return -RIG_EPROTO; } // end switch } else { // end rx == tx; start split checks. rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: Split.\n"); if (r_vfo == '0' && vfobuf[2] == '1') *vfo = RIG_VFO_AB; else if (r_vfo == '1' && vfobuf[2] == '0') *vfo = RIG_VFO_BA; else { // FIXME: need vfo <--> mem split rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ":FIXME: vfo<->mem split! -kd7eni!\n"); return -RIG_EPROTO; } } return RIG_OK; } /* * kenwood_set_freq * Assumes rig!=NULL * * status: correctly sets FA, FB, FC --Dale */ int ts2k_set_freq(RIG * rig, vfo_t vfo, freq_t freq) { unsigned char freqbuf[16]; int freq_len, ack_len = 0, retval; char vfo_letter; /* * better FIXME: vfo==RIG_VFO_CURR */ if (vfo == RIG_VFO_CURR) { retval = ts2k_get_vfo(rig, &vfo); if (retval != RIG_OK) return retval; } switch (vfo) { 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, "ts2k_set_freq: unsupported VFO %u\n", vfo); return -RIG_EINVAL; } freq_len = sprintf(freqbuf, "F%c%011Lu;", vfo_letter, freq); ack_len = 14; retval = ts2k_transaction(rig, freqbuf, freq_len, NULL, NULL); return retval; } /* * kenwood_get_freq * Assumes rig!=NULL, freq!=NULL */ int ts2k_get_freq(RIG * rig, vfo_t vfo, freq_t * freq) { unsigned char freqbuf[50]; unsigned char cmdbuf[4]; int cmd_len, freq_len, retval; char vfo_letter; /* * FIXME: need to handle RIG_VFO_MEM, etc... */ if (vfo == RIG_VFO_CURR) { retval = ts2k_get_vfo(rig, &vfo); if (retval != RIG_OK) return retval; } switch (vfo) { 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, __FUNCTION__": unsupported VFO %u\n", vfo); return -RIG_EPROTO; } cmd_len = sprintf(cmdbuf, "f%c%s", vfo_letter, cmd_trm(rig)); freq_len = 15; retval = ts2k_transaction(rig, cmdbuf, cmd_len, freqbuf, &freq_len); //rig_debug(RIG_DEBUG_ERR,"__FUNCTION__: received %s\n", cmdbuf); if (retval != RIG_OK) return retval; //if (freq_len != 14 || freqbuf[0] != 'F') { if (freqbuf[0] != 'F') { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": unexpected answer '%s', " "len=%u\n", freqbuf, freq_len); return -RIG_ERJCTED; } sscanf(freqbuf + 2, "%llu", freq); return RIG_OK; } /* * kenwood_set_mode * Assumes rig!=NULL */ int ts2k_set_mode(RIG * rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { unsigned char mdbuf[16], ackbuf[16]; int mdbuf_len, ack_len = 0, kmode, retval; switch (mode) { case RIG_MODE_CW: kmode = MD_CW; break; case RIG_MODE_USB: kmode = MD_USB; break; case RIG_MODE_LSB: kmode = MD_LSB; break; case RIG_MODE_FM: kmode = MD_FM; break; case RIG_MODE_AM: kmode = MD_AM; break; case RIG_MODE_RTTY: kmode = MD_FSK; break; default: rig_debug(RIG_DEBUG_ERR, "ts2k_set_mode: " "unsupported mode %u\n", mode); return -RIG_EINVAL; } mdbuf_len = sprintf(mdbuf, "MD%c;", kmode); ack_len = 16; rig_debug(RIG_DEBUG_ERR, "ts2k_set_mode: sending %s\n", mdbuf); retval = ts2k_transaction(rig, mdbuf, mdbuf_len, NULL, NULL); return retval; } /* * kenwood_get_mode * Assumes rig!=NULL, mode!=NULL */ int ts2k_get_mode(RIG * rig, vfo_t vfo, rmode_t * mode, pbwidth_t * width) { unsigned char modebuf[50]; int mode_len, retval; mode_len = 50; retval = ts2k_transaction(rig, "MD;", 3, modebuf, &mode_len); if (retval != RIG_OK) return retval; if (mode_len != 4 || modebuf[1] != 'D') { rig_debug(RIG_DEBUG_ERR, "ts2k_get_mode: unexpected answer, len=%u\n", mode_len); return -RIG_ERJCTED; } *width = RIG_PASSBAND_NORMAL; /* FIXME */ switch (modebuf[2]) { case MD_CW: *mode = RIG_MODE_CW; break; case MD_USB: *mode = RIG_MODE_USB; break; case MD_LSB: *mode = RIG_MODE_LSB; break; case MD_FM: *mode = RIG_MODE_FM; break; case MD_AM: *mode = RIG_MODE_AM; break; case MD_FSK: *mode = RIG_MODE_RTTY; break; #ifdef RIG_MODE_CWR case MD_CWR: *mode = RIG_MODE_CWR; break; #endif #ifdef RIG_MODE_RTTYR case MD_FSKR: *mode = RIG_MODE_RTTY; break; #endif case MD_NONE: *mode = RIG_MODE_NONE; break; default: rig_debug(RIG_DEBUG_ERR, "ts2k_get_mode: " "unsupported mode '%c'\n", modebuf[2]); return -RIG_EINVAL; } return RIG_OK; } /* added a whole buch of stuff --Dale */ int ts2k_set_level(RIG * rig, vfo_t vfo, setting_t level, value_t val) { unsigned char levelbuf[16], ackbuf[16], ctrl; char *dc; // to be replaced. // char dc[10]; // use this when ts2k_get_ctrl() is fixed. // int dc_len; // use this when ts2k_get_ctrl() is fixed. int level_len, ack_len = 0, retval, reply; int ts2k_val; if (RIG_LEVEL_IS_FLOAT(level)) ts2k_val = val.f * 255; else ts2k_val = val.i; /* Several commands need to know status of CTRL */ // dc_len = 10; // these are to be used when ts2k_get_ctrl() is changed. // if( ts2k_get_ctrl(rig, dc, 10) != RIG_OK) // return -RIG_EINVAL; // we shouldn't really change dc; its a static and will get changed. dc = ts2k_get_ctrl(rig); // will get fixed // m = dc[2]; s = dc[3]; // set defaults // FIXME: Just handles simple stuff RIG_VFO_CURR,VFOA,VFOB,VFOC; *may* work as intended. ctrl = '0'; // Assume Main if(vfo & RIG_CTRL_SUB) // Change only if sub (bitwise, not logical) ctrl = '1'; // assume no replies reply = 0; switch (level) { case RIG_LEVEL_RFPOWER: level_len = sprintf(levelbuf, "pc%03d;", ts2k_val); break; case RIG_LEVEL_AF: // I call this volume. :) level_len = sprintf(levelbuf, "ag%c%03u;", ctrl, ts2k_val); break; case RIG_LEVEL_RF: level_len = sprintf(levelbuf, "rg%03d;", ts2k_val); break; case RIG_LEVEL_SQL: // fixed: uses main/sub --Dale level_len = sprintf(levelbuf, "sq%c%03u;", ctrl, ts2k_val); break; /* I added the following levels --Dale */ case RIG_LEVEL_PREAMP: level_len = sprintf(levelbuf, "pa%c;", (ts2k_val==0)? '0' : '1'); break; case RIG_LEVEL_ATT: if(ts2k_val<1 || ts2k_val>2) // only 1 or 2, 0=read return -RIG_EINVAL; level_len = sprintf(levelbuf, "an%01u;", ts2k_val); break; case RIG_LEVEL_NR: if(ts2k_val<0 || ts2k_val>2) // only 1 or 2, 0=read return -RIG_EINVAL; level_len = sprintf(levelbuf, "nr%01u;", ts2k_val); break; /* FIXME: FM mic gain is low/mid/high; cmd="ex0410000n;" 0=low --Dale */ case RIG_LEVEL_MICGAIN: level_len = sprintf(levelbuf, "mg%03u;", (ts2k_val>100)? 100 : ts2k_val); break; /* no rig error on invalid values */ case RIG_LEVEL_KEYSPD: if(ts2k_val<10 || ts2k_val>60) // only 1 or 2, 0=read return -RIG_EINVAL; level_len = sprintf(levelbuf, "ks%03u;", ts2k_val); break; case RIG_LEVEL_NOTCHF: // actually, this is autonotch--same thing? level_len = sprintf(levelbuf, "al%03u;", (ts2k_val>4)? 4 : ts2k_val); break; case RIG_LEVEL_AGC: level_len = sprintf(levelbuf, "gt%03u;", (ts2k_val>20)? 20 : ts2k_val); break; case RIG_LEVEL_BKINDL: // 0-1000ms in 50ms steps; this'll be good 'nuff. level_len = sprintf(levelbuf, "sd%04u;",(int)((float) ts2k_val*1000.0/255.0)); break; case RIG_LEVEL_VOXGAIN: level_len = sprintf(levelbuf, "vg%03u;",(ts2k_val>9)? 9 : ts2k_val); // alternate // level_len = sprintf(levelbuf, "vg%03u;",(int)((float) ts2k_val*9.0/255.0)); break; // case RIG_LEVEL_VOX: // header file declares these the same case RIG_LEVEL_VOXDELAY: level_len = sprintf(levelbuf, "vg%03u;",(int)((float) ts2k_val*3000.0/255.0)); break; // vox on/off // level_len = sprintf(levelbuf, "vx%c;", (ts2k_val==0)? '0' : '1'); // break; /* Check unsupported just so we can complain if one is found. :) */ case RIG_LEVEL_APF: /* readonly */ case RIG_LEVEL_SWR: case RIG_LEVEL_ALC: case RIG_LEVEL_STRENGTH: return -RIG_EINTERNAL; // and complain loud! /* not currently implemented */ case RIG_LEVEL_METER: // readonly + needs rechecked. case RIG_LEVEL_PBT_IN: // nnn case RIG_LEVEL_PBT_OUT: // NNN; "plnnnNNN;" case RIG_LEVEL_IF: // hmmmm.... case RIG_LEVEL_CWPITCH: // "ex0310000n;" and a fair bit of programming... case RIG_LEVEL_COMP: // available?, processor's not the same is it? case RIG_LEVEL_BALANCE: // AF R/L balance? See "ex01600002;" for similar(?) case RIG_LEVEL_SQLSTAT: // need more info; avail? return -RIG_ENIMPL; default: rig_debug(RIG_DEBUG_ERR, "Unsupported set_level %u", level); return -RIG_EINVAL; } // The following might make a nifty macro --Dale ack_len = reply? 16 : 0; // This'd work by itself wouldn't it? --Dale retval = ts2k_transaction(rig, levelbuf, level_len, reply? ackbuf : NULL, reply? &ack_len : NULL); if (retval != RIG_OK) return retval; return RIG_OK; } /* * assumes f!=NULL */ static int get_ts2k_level(RIG * rig, const char *cmd, int cmd_len, float *f) { unsigned char lvlbuf[50]; int lvl_len, retval; int lvl; lvl_len = 50; retval = ts2k_transaction(rig, cmd, cmd_len, lvlbuf, &lvl_len); if (retval != RIG_OK) return retval; if (lvl_len != 6) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_level: wrong answer len=%u\n", lvl_len); return -RIG_ERJCTED; } /* * 000..255 */ sscanf(lvlbuf + 2, "%u", &lvl); *f = (float) lvl / 255.0; return RIG_OK; }; /* * kenwood_get_level * Assumes rig!=NULL, val!=NULL */ int ts2k_get_level(RIG * rig, vfo_t vfo, setting_t level, value_t * val) { unsigned char lvlbuf[50]; int lvl_len, retval; int lvl; int i; lvl_len = 50; /* Optimize: * sort the switch cases with the most frequent first */ switch (level) { case RIG_LEVEL_STRENGTH: // fixme: "sm0;" VFO must be specified! --kd7eni retval = ts2k_transaction(rig, "SM;", 3, lvlbuf, &lvl_len); if (retval != RIG_OK) return retval; if (lvl_len != 7 || lvlbuf[1] != 'M') { rig_debug(RIG_DEBUG_ERR, "ts2k_get_level: " "wrong answer len=%u\n", lvl_len); return -RIG_ERJCTED; } /* * Frontend expects: * -54 = S0 * 0 = S9 */ sscanf(lvlbuf + 2, "%u", &val->i); val->i = (val->i * 4) - 54; break; case RIG_LEVEL_SQLSTAT: return -RIG_ENIMPL; /* get_dcd ? */ case RIG_LEVEL_PREAMP: return -RIG_ENIMPL; case RIG_LEVEL_ATT: retval = ts2k_transaction(rig, "RA;", 3, lvlbuf, &lvl_len); if (retval != RIG_OK) return retval; if (lvl_len != 5) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_level: " "unexpected answer len=%u\n", lvl_len); return -RIG_ERJCTED; } sscanf(lvlbuf + 2, "%u", &lvl); if (lvl == 0) { val->i = 0; } else { for (i = 0; i < lvl && i < MAXDBLSTSIZ; i++) if (rig->state.attenuator[i] == 0) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_level: " "unexpected att level %u\n", lvl); return -RIG_EPROTO; } if (i != lvl) return -RIG_EINTERNAL; val->i = rig->state.attenuator[i - 1]; } break; case RIG_LEVEL_RFPOWER: return get_ts2k_level(rig, "PC;", 3, &val->f); case RIG_LEVEL_AF: return get_ts2k_level(rig, "AG;", 3, &val->f); case RIG_LEVEL_RF: return get_ts2k_level(rig, "RG;", 3, &val->f); case RIG_LEVEL_SQL: return get_ts2k_level(rig, "SQ;", 3, &val->f); case RIG_LEVEL_MICGAIN: return get_ts2k_level(rig, "MG;", 3, &val->f); case RIG_LEVEL_AGC: return get_ts2k_level(rig, "GT;", 3, &val->f); 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_CWPITCH: case RIG_LEVEL_KEYSPD: 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 %u", level); return -RIG_EINVAL; } return RIG_OK; } int ts2k_set_func(RIG * rig, vfo_t vfo, setting_t func, int status) { unsigned char fctbuf[16], ackbuf[16]; int fct_len, ack_len = 16; /* Optimize: * sort the switch cases with the most frequent first */ switch (func) { case RIG_FUNC_NB: fct_len = sprintf(fctbuf, "NB%c;", status == RIG_FUNC_NB ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_ABM: fct_len = sprintf(fctbuf, "AM%c;", status == RIG_FUNC_ABM ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_COMP: fct_len = sprintf(fctbuf, "PR%c;", status == RIG_FUNC_COMP ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_TONE: fct_len = sprintf(fctbuf, "TO%c;", status == RIG_FUNC_TONE ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_TSQL: // fixme: see ts2000.doc and follow the proceedure! --kdeni // rigbug! fct_len = sprintf(fctbuf, "CT%c;", status == RIG_FUNC_TSQL ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, ackbuf, &ack_len); case RIG_FUNC_VOX: fct_len = sprintf(fctbuf, "VX%c;", status == RIG_FUNC_VOX ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_NR: fct_len = sprintf(fctbuf, "NR%c;", status == RIG_FUNC_NR ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_BC: fct_len = sprintf(fctbuf, "BC%c;", status == RIG_FUNC_BC ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_ANF: fct_len = sprintf(fctbuf, "NT%c;", status == RIG_FUNC_ANF ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); case RIG_FUNC_LOCK: fct_len = sprintf(fctbuf, "LK%c0;", status == RIG_FUNC_LOCK ? '0' : '1'); return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL); default: rig_debug(RIG_DEBUG_ERR, "Unsupported set_func %#x", func); return -RIG_EINVAL; } return RIG_OK; } /* * assumes status!=NULL * works for any 'format 1' command */ static int get_ts2k_func(RIG * rig, const char *cmd, int cmd_len, int *status) { unsigned char fctbuf[50]; int fct_len, retval; fct_len = 50; retval = ts2k_transaction(rig, cmd, cmd_len, fctbuf, &fct_len); if (retval != RIG_OK) return retval; if (fct_len != 4) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": wrong answer len=%u\n", fct_len); return -RIG_ERJCTED; } *status = fctbuf[2] == '0' ? 0 : 1; return RIG_OK; }; /* * kenwood_get_func * Assumes rig!=NULL, val!=NULL */ int ts2k_get_func(RIG * rig, vfo_t vfo, setting_t func, int *status) { unsigned char fctbuf[50]; int fct_len, retval; fct_len = 50; /* Optimize: * sort the switch cases with the most frequent first */ switch (func) { case RIG_FUNC_FAGC: retval = ts2k_transaction(rig, "GT;", 3, fctbuf, &fct_len); if (retval != RIG_OK) return retval; if (fct_len != 6) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_func: " "wrong answer len=%u\n", fct_len); return -RIG_ERJCTED; } *status = fctbuf[4] != '4' ? 1 : 0; break; case RIG_FUNC_NB: return get_ts2k_func(rig, "NB;", 3, status); case RIG_FUNC_ABM: return get_ts2k_func(rig, "AM;", 3, status); case RIG_FUNC_COMP: return get_ts2k_func(rig, "PR;", 3, status); case RIG_FUNC_TONE: return get_ts2k_func(rig, "TO;", 3, status); case RIG_FUNC_TSQL: return get_ts2k_func(rig, "CT;", 3, status); case RIG_FUNC_VOX: return get_ts2k_func(rig, "VX;", 3, status); case RIG_FUNC_NR: return get_ts2k_func(rig, "NR;", 3, status); /* FIXME on TS2000 */ case RIG_FUNC_BC: return get_ts2k_func(rig, "BC;", 3, status); case RIG_FUNC_ANF: return get_ts2k_func(rig, "NT;", 3, status); case RIG_FUNC_LOCK: return get_ts2k_func(rig, "LK;", 3, status); default: rig_debug(RIG_DEBUG_ERR, "Unsupported get_func %#x", func); return -RIG_EINVAL; } return RIG_OK; } /* * kenwood_set_ctcss_tone * Assumes rig!=NULL, rig->caps->ctcss_list != NULL * * Warning! This is untested stuff! May work at least on TS-870S * Please owners report to me , thanks. --SF * * TODO: TS-2000 uses CN/CT * ex057 menu is AutoPower off for TS-2000 --kd7eni */ int ts2k_set_ctcss_tone(RIG * rig, vfo_t vfo, tone_t tone) { return ts2k_set_Tones(rig, vfo, tone, (char)'c'); } int ts2k_set_tone(RIG * rig, vfo_t vfo, tone_t tone) { return ts2k_set_Tones(rig, vfo, tone, (char)'t'); } int ts2k_set_Tones(RIG * rig, vfo_t vfo, tone_t tone, const char ct) { const struct rig_caps *caps; unsigned char tonebuf[16], ackbuf[16]; int tone_len, ack_len = 0; int i; caps = rig->caps; /* TODO: replace 200 by something like RIGTONEMAX */ for (i = 0; ts2k_ctcss_list[i] != 0 && i < 38; i++) { if ((ts2k_ctcss_list[i] >= tone) && (ts2k_ctcss_list[i-1] < tone)) // at least get close break; } if (ts2k_ctcss_list[i-1] == tone) i--; if (ts2k_ctcss_list[i] > tone) return -RIG_EINVAL; tone_len = sprintf(tonebuf, "%cn%02u;", ct, i+1); ack_len = 16; return ts2k_transaction(rig, tonebuf, tone_len, NULL, NULL); rig_debug(RIG_DEBUG_ERR, __FUNCTION__": sent %s", tonebuf); } /* * kenwood_get_ctcss_tone * Assumes rig!=NULL, rig->state.priv!=NULL */ int ts2k_get_ctcss_tone(RIG * rig, vfo_t vfo, tone_t * tone) { return ts2k_get_Tones(rig, vfo, tone, "cn;"); } int ts2k_get_tone(RIG * rig, vfo_t vfo, tone_t * tone) { return ts2k_get_Tones(rig, vfo, tone, "tn;"); } int ts2k_get_Tones(RIG * rig, vfo_t vfo, tone_t * tone, const char *ct) { const struct rig_caps *caps; unsigned char tonebuf[10]; int tone_len, i, retval; unsigned int tone_idx; caps = rig->caps; tone_len = 10; retval = ts2k_transaction(rig, ct, 3, tonebuf, &tone_len); if (retval != RIG_OK) return retval; if (tone_len != 5) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": unexpected reply " "'%s', len=%u\n", tonebuf, tone_len); return -RIG_ERJCTED; } sscanf(tonebuf + 2, "%u", (int *) &tone_idx); if (tone_idx == 0) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": Unexpected Tone " "no (%04d)\n", tone_idx); return -RIG_EPROTO; } /* check this tone exists. That's better than nothing. */ for (i = 0; i < tone_idx; i++) { if (caps->ctcss_list[i] == 0) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": Tone NG " "(%04d)\n", tone_idx); return -RIG_EPROTO; } } *tone = caps->ctcss_list[tone_idx - 1]; return RIG_OK; } /* * kenwood_get_ptt * Assumes rig!=NULL, ptt!=NULL */ int ts2k_get_ptt(RIG * rig, vfo_t vfo, ptt_t * ptt) { unsigned char infobuf[50]; int info_len, retval; info_len = 50; retval = ts2k_transaction(rig, "IF;", 3, infobuf, &info_len); if (retval != RIG_OK) return retval; if (info_len != 38 || infobuf[1] != 'F') { rig_debug(RIG_DEBUG_ERR, "ts2k_get_ptt: wrong answer len=%u\n", info_len); return -RIG_ERJCTED; } *ptt = infobuf[28] == '0' ? RIG_PTT_OFF : RIG_PTT_ON; return RIG_OK; } /* * kenwood_set_ptt * Assumes rig!=NULL */ int ts2k_set_ptt(RIG * rig, vfo_t vfo, ptt_t ptt) { unsigned char ackbuf[16]; int ack_len = 16; return ts2k_transaction(rig, ptt == RIG_PTT_ON ? "TX;" : "RX;", 3, NULL, NULL); } /* * kenwood_get_dcd * Assumes rig!=NULL, dcd!=NULL */ int ts2k_get_dcd(RIG * rig, vfo_t vfo, dcd_t * dcd) { unsigned char busybuf[50]; int busy_len, retval; busy_len = 50; retval = ts2k_transaction(rig, "BY;", 3, busybuf, &busy_len); if (retval != RIG_OK) return retval; if (busy_len != 4) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_dcd: wrong answer len=%u\n", busy_len); return -RIG_ERJCTED; } *dcd = (busybuf[2] == 0x01) ? RIG_DCD_ON : RIG_DCD_OFF; return RIG_OK; } /* * kenwood_set_trn * Assumes rig!=NULL */ int ts2k_set_trn(RIG * rig, int trn) { unsigned char trnbuf[16], ackbuf[16]; int trn_len, ack_len = 16; /* changed to TS-2000 --D.E. kd7eni */ trn_len = sprintf(trnbuf, "AI%c;", trn == RIG_TRN_RIG ? '2' : '0'); return ts2k_transaction(rig, trnbuf, trn_len, NULL, NULL); // No reply on "ai2;"--how quaint! } /* * kenwood_get_trn * Assumes rig!=NULL, trn!=NULL */ int ts2k_get_trn(RIG * rig, int *trn) { unsigned char trnbuf[50]; int trn_len, retval; trn_len = 50; retval = ts2k_transaction(rig, "AI;", 3, trnbuf, &trn_len); if (retval != RIG_OK) return retval; if (trn_len != 4) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_trn: wrong answer" "len=%u\n", trn_len); return -RIG_ERJCTED; } *trn = trnbuf[2] != '0' ? RIG_TRN_RIG : RIG_TRN_OFF; return RIG_OK; } /* * kenwood_set_powerstat * Assumes rig!=NULL */ int ts2k_set_powerstat(RIG * rig, powerstat_t status) { unsigned char pwrbuf[16], ackbuf[16]; int pwr_len, ack_len = 16; pwr_len = sprintf(pwrbuf, "PS%c;", status == RIG_POWER_ON ? '1' : '0'); return ts2k_transaction(rig, pwrbuf, pwr_len, ackbuf, &ack_len); } /* * kenwood_get_powerstat * Assumes rig!=NULL, trn!=NULL */ int ts2k_get_powerstat(RIG * rig, powerstat_t * status) { unsigned char pwrbuf[50]; int pwr_len = 50, retval; // No reply when powered off, 1=power on. Geez! retval = ts2k_transaction(rig, "PS;", 3, pwrbuf, &pwr_len); if (retval != RIG_OK) return retval; if (pwr_len != 4) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_powerstat: wrong answer " "len=%u\n", pwr_len); return -RIG_ERJCTED; } *status = pwrbuf[2] == '0' ? RIG_POWER_OFF : RIG_POWER_ON; return RIG_OK; } /* * kenwood_reset * Assumes rig!=NULL */ int ts2k_reset(RIG * rig, reset_t reset) { unsigned char rstbuf[16], ackbuf[16]; int rst_len, ack_len = 16; char rst; switch (reset) { case RIG_RESET_VFO: rst = '1'; break; case RIG_RESET_MASTER: rst = '2'; break; default: rig_debug(RIG_DEBUG_ERR, "ts2k_reset: unsupported reset %u\n", reset); return -RIG_EINVAL; } rst_len = sprintf(rstbuf, "SR%c;", rst); // Largely untested! ;) --kd7eni return ts2k_transaction(rig, rstbuf, rst_len, ackbuf, &ack_len); } /* * kenwood_send_morse * Assumes rig!=NULL */ int ts2k_send_morse(RIG * rig, vfo_t vfo, const char *msg) { unsigned char morsebuf[30], ackbuf[16]; int morse_len, ack_len = 0; 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. */ buff_len = msg_len > 24 ? 24 : msg_len; strcpy(morsebuf, "KY "); strncat(morsebuf, p, buff_len); strcat(morsebuf, ";"); morse_len = 4 + buff_len; ack_len = 16; retval = ts2k_transaction(rig, morsebuf, morse_len, ackbuf, &ack_len); if (retval != RIG_OK) return retval; msg_len -= buff_len; p += buff_len; } return RIG_OK; } /* * kenwood_vfo_op * Assumes rig!=NULL */ int ts2k_vfo_op(RIG * rig, vfo_t vfo, vfo_op_t op) { unsigned char *cmd, ackbuf[16]; int ack_len = 0; switch (op) { case RIG_OP_UP: cmd = "UP;"; break; case RIG_OP_DOWN: cmd = "DN;"; break; case RIG_OP_BAND_UP: cmd = "BD;"; break; case RIG_OP_BAND_DOWN: cmd = "BU;"; break; default: rig_debug(RIG_DEBUG_ERR, "ts2k_vfo_op: unsupported op %#x\n", op); return -RIG_EINVAL; } ack_len = 16; return ts2k_transaction(rig, cmd, 3, NULL, NULL); } /* * kenwood_set_mem * Assumes rig!=NULL */ int ts2k_set_mem(RIG * rig, vfo_t vfo, int ch) { unsigned char membuf[16], ackbuf[16]; int mem_len, ack_len = 16; /* * "MCbmm;" * where b is the bank number, mm the memory number. * b can be a space ( *not*! manual wrong. --kd7eni */ mem_len = sprintf(membuf, "MC%03d;", ch); return ts2k_transaction(rig, membuf, mem_len, NULL, NULL); } /* * kenwood_get_mem * Assumes rig!=NULL */ int ts2k_get_mem(RIG * rig, vfo_t vfo, int *ch) { unsigned char membuf[50]; int retval, mem_len; /* * "MCbmm;" * where b is the bank number, mm the memory number. * b can be a space (*not* --kd7eni) */ mem_len = 10; retval = ts2k_transaction(rig, "MC;", 3, membuf, &mem_len); if (retval != RIG_OK) return retval; if (mem_len != 6) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_mem: wrong answer " "len=%u\n", mem_len); return -RIG_ERJCTED; } membuf[5] = '\0'; *ch = atoi(membuf + 2); return RIG_OK; } /* * kenwood_get_info * supposed to work only for TS2000... * Assumes rig!=NULL */ const char *ts2k_get_info(RIG * rig) { unsigned char firmbuf[50]; int firm_len, retval; firm_len = 50; retval = ts2k_transaction(rig, "TY;", 3, firmbuf, &firm_len); if (retval != RIG_OK) return NULL; if (firm_len != 6) { rig_debug(RIG_DEBUG_ERR, "ts2k_get_info: wrong answer len=%u\n", firm_len); 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 /* * probe_kenwood */ rig_model_t probe_ts2k(port_t * port) { unsigned char idbuf[IDBUFSZ]; int id_len, i, k_id; int retval; if (!port) return RIG_MODEL_NONE; port->write_delay = port->post_write_delay = 0; port->timeout = 50; port->retry = 1; 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, EOM_KEN EOM_TH, 2); close(port->fd); if (retval != RIG_OK) 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_ts2k: protocol error," " expected %u, received %u: %s\n", 6, id_len, idbuf); return RIG_MODEL_NONE; } /* first, try ID string */ for (i = 0; ts2k_id_string_list[i].model != RIG_MODEL_NONE; i++) { if (!strncmp(ts2k_id_string_list[i].id, idbuf + 2, 16)) { rig_debug(RIG_DEBUG_VERBOSE, "probe_ts2k: " "found %s\n", idbuf + 2); return ts2k_id_string_list[i].model; } } /* then, try ID numbers */ k_id = atoi(idbuf + 2); for (i = 0; ts2k_id_list[i].model != RIG_MODEL_NONE; i++) { if (ts2k_id_list[i].id == k_id) { rig_debug(RIG_DEBUG_VERBOSE, "probe_ts2k: " "found %03d\n", k_id); return ts2k_id_list[i].model; } } /* * not found in known table.... * update ts2k_id_list[]! */ rig_debug(RIG_DEBUG_WARN, "probe_ts2k: found unknown device " "with ID %03d, please report to Hamlib " "developers.\n", k_id); return RIG_MODEL_NONE; } /* kenwood_init * * Basically, it sets up *priv * REM: serial port is already open (rig->state.rigport.fd) */ int ts2k_init(RIG * rig) { const struct rig_caps *caps; const struct ts2k_priv_caps *priv_caps; rig_debug(RIG_DEBUG_TRACE, __FUNCTION__ ": called\n"); if (!rig || !rig->caps) return -RIG_EINVAL; caps = rig->caps; if (!caps->priv) return -RIG_ECONF; priv_caps = (const struct ts2k_priv_caps *) caps->priv; #if 0 /* No private data for Kenwood backends */ priv = (struct ts2k_priv_data *) malloc(sizeof(struct ts2k_priv_data)); if (!priv) { /* whoops! memory shortage! */ return -RIG_ENOMEM; } rig->state.priv = (void *) priv; /* Assign default values */ priv->dummy = -1; // placeholder for real entries. #endif return RIG_OK; } /* kenwood_cleanup * the serial port is closed by the frontend */ int ts2k_cleanup(RIG * rig) { rig_debug(RIG_DEBUG_TRACE, __FUNCTION__ ": called\n"); if (!rig) return -RIG_EINVAL; if (rig->state.priv) free(rig->state.priv); rig->state.priv = NULL; return RIG_OK; } /* * initrigs_kenwood is called by rig_backend_load */ int initrigs_ts2k(void *be_handle) { rig_debug(RIG_DEBUG_VERBOSE, "ts2k: _init called\n"); rig_register(&ts950sdx_caps); rig_register(&ts50s_caps); rig_register(&ts450s_caps); rig_register(&ts570d_caps); rig_register(&ts570s_caps); rig_register(&ts790_caps); rig_register(&ts850_caps); rig_register(&ts870s_caps); rig_register(&ts2000_caps); rig_register(&thd7a_caps); rig_register(&thf7e_caps); return RIG_OK; } /***************************************************************************** Added the following functions. (C) Copyright 2002 by Dale E. Edmons. All rights Reserved. License: Identical to all other Hamlib code. *****************************************************************************/ #define CHKERR(c) if((c) != RIG_OK) return c #define STUFF(c) int retval, acklen=(c); char ack[c] //#define STUFF(c) static int retval, acklen=(c); static char ack[c] // The following two are expensive but convenient! int ncpy(char *tmp, char *src, int cnt) { strncpy(tmp, src, cnt); tmp[cnt] = '\0'; return RIG_OK; } int int_n(char *tmp, char *src, const int cnt) { ncpy(tmp, src, cnt); return atoi(tmp); } /* * ts2k_get_ctrl() ts2000 transceiver check. Tests and returns the value of the current * PTT/CTRL (using "dc;") for main and sub transceivers. The settings are: * * PTT __ __ CTRL '0' = main; '1' = sub * \ / * "dc00;" PTT && CTRL both on main * "dc01;" PTT on main; CTRL on sub * "dc10;" PTT on sub; CTRL both on main * "dc11;" PTT && CTRL both on sub */ //int ts2k_get_ctrl(RIG * rig, char *dc_buf, int dc_len) // use this when static removed. char *ts2k_get_ctrl(RIG * rig) { // FIXME: I guess this shouldn't be static for re-entrancy --Dale static char dc_buf[16]; static int dc_len; static int retval; dc_len = 16; // rig_debug(RIG_DEBUG_VERBOSE, "ts2k_get_ctrl: getting PTT/CTRL bytes\n"); retval = ts2k_transaction(rig, "dc;", 3, dc_buf, &dc_len); if (retval != RIG_OK) { rig_debug(RIG_DEBUG_VERBOSE, "ts2k_get_ctrl:error: retval=%u, dc_buf=%s\n", retval, dc_buf); return NULL; } // rig_debug(RIG_DEBUG_VERBOSE, "ts2k_get_ctrl: returning %u PTT/CTRL bytes\n", dc_len); // return RIG_OK; // use this when static variable removed and all other code changed. return dc_buf; } /* NOTE: PTT/CTRL enable is set only if ptt != 0 (or ctrl) */ int ts2k_set_ctrl(RIG * rig, int ptt, int ctrl) { STUFF(10); char *buf; int buflen = 10; buf = ts2k_get_ctrl(rig); if(buf==NULL) { rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__ \ ": returned NULL!\n"); return -RIG_EINVAL; } rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__": curr='%s'," "ptt=%u, ctrl=%u\n", buf, ptt, ctrl); if (ptt != 0) buf[2] = (TS2K_PTT_ON_SUB == ptt) ? '1' : '0'; if (ctrl != 0) buf[3] = (TS2K_CTRL_ON_SUB == ctrl) ? '1' : '0'; buf[4] = ';'; buf[5] = '\0'; // just for printing debug acklen=10; retval = ts2k_transaction(rig, buf, 5, NULL, NULL); CHKERR(retval); return RIG_OK; } /* * FIXME: simple ascii to integer converter--expensive! * Prevents trashing original string, otherwise * just drop a (char)0 where you need it. */ int ts2k_get_int(char *src, int cnt) { static char buf[20]; strncpy(buf, src, cnt); buf[cnt] = (char) 0; return atoi(buf); } int ts2k_get_rit(RIG * rig, vfo_t vfo, shortfreq_t * rit) { int retval, acklen; char ack[40]; char *ctrl; ctrl = ts2k_get_ctrl(rig); if (ctrl == NULL) // do we have valid "dc;" ? return -RIG_EINVAL; // don't know proper errors yet! // sub shows main's rit! We return 0 if subreceiver. if (ctrl[3] == '1') { *rit = 0; return RIG_OK; // not really, but it's paid for... } retval = ts2k_transaction(rig, "if;", 3, ack, &acklen); CHKERR(retval); ack[23] = (char) 0; *rit = atoi(&ack[17]); return RIG_OK; } /* * ts2k_get_xit() We just call ts2k_get_rit() * On the ts2k, they're the same. The rig * acts this way. */ int ts2k_get_xit(RIG * rig, vfo_t vfo, shortfreq_t * rit) { return ts2k_get_rit(rig, vfo, rit); } int ts2k_set_rit(RIG * rig, vfo_t vfo, shortfreq_t rit) { char buf[40], c; int retval, i, len; // Clear current rit/xit. retval = ts2k_transaction(rig, "rc;", 3, NULL, NULL); CHKERR(retval); // Execute up/down request. if (rit > 0) { c = 'u'; i = rit; } else { c = 'd'; i = -rit; } len = sprintf(buf, "r%c%6d;", c, i); return ts2k_transaction(rig, buf, len, NULL, NULL); } int ts2k_set_xit(RIG * rig, vfo_t vfo, shortfreq_t rit) { return ts2k_set_rit(rig, vfo, rit); } int ts2k_get_ts(RIG * rig, vfo_t vfo, shortfreq_t * ts) { STUFF(10); int m, s; retval = ts2k_transaction(rig, "ST;", 5, ack, &acklen); CHKERR(retval); ack[4] = '\0'; s = atoi(&ack[2]); rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__": received: '%s', %u\n", ack, s); retval = ts2k_transaction(rig, "MD;", 5, ack, &acklen); CHKERR(retval); // fm or am mode selects 1 m = (ack[2] == '4' || ack[2] == '5') ? 1 : 0; rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__": received: '%s', %u\n", ack, m); *ts = ts2k_steps[m][s]; return RIG_OK; } // FIXME: should get nearest, not easiest. :) /* * status: working. fixed timeout. --Dale */ int ts2k_set_ts(RIG * rig, vfo_t vfo, shortfreq_t ts) { STUFF(10); char st[10]; int m, s, k; long int aver, diff[2]; retval = ts2k_transaction(rig, "md;", 5, ack, &acklen); CHKERR(retval); // fm or am mode selects 1 m = (ack[2] == '4' || ack[2] == '5') ? 1 : 0; // fm or am selects 10 step freq. else 4. k = ( m )? 10 : 4; s=0; if( ts < ts2k_steps[m][0] ) { k=s; } if( ts > ts2k_steps[m][9] ) { s=9; } for ( s=1 ; s diff[1] ) { // closer to [s] break; } else { // closer to [s-1] s--; break; } } } // m is tmp now m = sprintf(st, "st0%1u;", s); // no reply! retval = ts2k_transaction(rig, st, m, NULL, NULL); CHKERR(retval); return RIG_OK; } /* Rig truncates in kHz(50) steps, so we don't. */ int ts2k_get_rptr_offs(RIG * rig, vfo_t vfo, shortfreq_t * rptr_offs) { STUFF(20); retval = ts2k_transaction(rig, "of;", 3, ack, &acklen); CHKERR(retval); if (ack[0] != 'O' || ack[1] != 'F') return -RIG_EINVAL; if (acklen != 12) return -RIG_EINVAL; ack[11] = '\0'; *rptr_offs = atoi(&ack[2]); return RIG_OK; } /* Rig truncates in kHz(50) steps, so we don't. */ int ts2k_set_rptr_offs(RIG * rig, vfo_t vfo, shortfreq_t rptr_offs) { STUFF(20); char buf[20]; int buflen = 20, i; i = sprintf(buf, "of%09u;", (unsigned int) rptr_offs); retval = ts2k_transaction(rig, buf, i, NULL, NULL); CHKERR(retval); return RIG_OK; } /* * status: working, leaves vfo intact. */ int ts2k_get_rptr_shift(RIG * rig, vfo_t vfo, rptr_shift_t * rptr_shift) { STUFF(20); vfo_t vfo_tmp; // FIXME: I don't know if I should change back to currVFO, but I do. retval = ts2k_get_vfo(rig, &vfo_tmp); CHKERR(retval); retval = ts2k_transaction(rig, "os;", 3, ack, &acklen); CHKERR(retval); retval = ts2k_vfo_ctrl(rig, vfo); if (ack[0] != 'O' || ack[1] != 'S') return -RIG_EINVAL; //if(acklen != 4) return -RIG_EINVAL; switch (ack[2]) { case '0': *rptr_shift = RIG_RPT_SHIFT_NONE; break; case '1': *rptr_shift = RIG_RPT_SHIFT_MINUS; break; case '2': *rptr_shift = RIG_RPT_SHIFT_PLUS; break; case '3': *rptr_shift = RIG_RPT_SHIFT_1750; break; default: return -RIG_EINVAL; break; } // FIXME: I don't know if I should change back to currVFO, but I do. retval = ts2k_set_vfo(rig, vfo_tmp); CHKERR(retval); return RIG_OK; } /* * ts2k_set_rptr_shift() * * status: doesn't check VFO yet --Dale */ int ts2k_set_rptr_shift(RIG * rig, vfo_t vfo, rptr_shift_t rptr_shift) { STUFF(10); char c; switch (rptr_shift) { case RIG_RPT_SHIFT_NONE: c = '0'; break; case RIG_RPT_SHIFT_PLUS: c = '1'; break; case RIG_RPT_SHIFT_MINUS: c = '2'; break; case RIG_RPT_SHIFT_1750: // FIXME: invalid for mine! (non-Etype) c = '3'; break; default: return -RIG_EINVAL; break; } acklen = sprintf(ack, "os%c;", c); retval = ts2k_transaction(rig, ack, 4, NULL, NULL); CHKERR(retval); return RIG_OK; } int ts2k_get_split(RIG * rig, vfo_t vfo, split_t * split) { STUFF(10); char ack2[10]; int ack2len = 10; retval = ts2k_transaction(rig, "fr;", 3, ack, &acklen); CHKERR(retval); retval = ts2k_transaction(rig, "ft;", 3, ack2, &ack2len); CHKERR(retval); if (ack[2] != ack2[2]) *split = RIG_SPLIT_ON; else *split = RIG_SPLIT_OFF; return RIG_OK; } /* no VFO check, and no mem. Yet. */ int ts2k_set_split(RIG * rig, vfo_t vfo, split_t split) { STUFF(10); char ack2[10]; int ack2len = 10; retval = ts2k_transaction(rig, "fr;", 3, ack, &acklen); CHKERR(retval); retval = ts2k_transaction(rig, "ft;", 3, ack2, &ack2len); CHKERR(retval); if (split == RIG_SPLIT_ON) { // RX/TX on different vfo's if (ack[2] == '0') ack2[2] = '1'; else if (ack[2] == '1') ack2[2] = '0'; /* FIXME: mem split. mem/vfo split must enable menu 6a * with the "ex...;" or mem/vfo won't work. A split * memory operates simply by storing a memory with the * freq with RX != TX then recalling it. Don't know if * if two separate memories can be used (e.g. 100/101) */ else return -RIG_EINVAL; } else { // RX/TX on same vfo (or mem etc...) ack2[2] = ack[2]; } // now, just send back the rig's strings :) retval = ts2k_transaction(rig, ack, acklen, NULL, NULL); CHKERR(retval); retval = ts2k_transaction(rig, ack2, ack2len, NULL, NULL); CHKERR(retval); return RIG_OK; } int ts2k_get_split_freq(RIG * rig, vfo_t vfo, freq_t * tx_freq) { // FIXME : This makes too many assumptions--I'll fix it later...d PTT/CTRL bytes\n", dc_len);*//* The following are functions I've added for the TS-2000. (C) 2002 D. Edmons and all that. *//*-------------------------------------------------------------------------------------------*/ return ts2k_get_freq(rig, vfo, tx_freq); } int ts2k_set_split_freq(RIG * rig, vfo_t vfo, freq_t tx_freq) { // FIXME : This makes too many assumptions--I'll fix it later...d PTT/CTRL bytes\n", dc_len);*//* The following are functions I've added for the TS-2000. (C) 2002 D. Edmons and all that. *//*-------------------------------------------------------------------------------------------*/ return ts2k_set_freq(rig, vfo, tx_freq); } /* ts2k_get_channel() * * status: working! reading unset memory is an error. this * is the rig. vfo_t is not needed for regular memory * and we don't check it now. when we start to access * other memory we'll split into multiple functions * each dedicated to the unique type (e.g. quick/tmp). * * memory range not checked. * * Note: the manual erroneously states bank should be ' ' if * memory is <100. The "correct" way is to *never* * send a ' ' or you'll get a "?;" response. Always * set memory with (e.g.) "mc1020;". --kd7eni */ int ts2k_get_channel(RIG * rig, channel_t *chan) { // channel_t tch; // needed? char rxtx, mrtxt[2][60], mrcmd[15], ack[60], tmp[20]; int i, check, retval, curr_mem, mrtxt_len, mrcmd_len, ack_len; vfo_t curr_vfo; #ifdef _USEVFO vfo_t v; v = vfo; // check the memory bit if( !(v & RIG_VFO_MEM) ) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ": Non Memory VFO='%u', v=%u\n", vfo, v); return -RIG_EINVAL; // not a channel! } // get needed info if rig's mem pointers used if( ( vfo == RIG_VFO_MEM_A || vfo == RIG_VFO_MEM_C ) ) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": using rig's ptr\n"); retval = ts2k_get_vfo(rig, &curr_vfo); CHKERR(retval); retval = ts2k_get_mem(rig, curr_vfo, &curr_mem); CHKERR(retval); chan->channel_num = curr_mem; } #endif mrtxt_len = ack_len = 60; mrcmd_len=15; if(chan==NULL) return -RIG_EINVAL; // send request for both rx mem and tx mem for(i=0; i<2; i++) { // 0=rx; 1=tx mrcmd_len = sprintf(mrcmd, "mr%01d%03u;", i, chan->channel_num); ack_len = 60; // must be reset inside loop! retval = ts2k_transaction(rig, mrcmd, mrcmd_len, ack, &ack_len); CHKERR(retval); rig_debug(RIG_DEBUG_ERR, __FUNCTION__": read \n\t'%s'\n", ack); ack[50] = '\0'; // May be too far, but helps. // watch out. |= and != look the same! // Perform checks on data. check = (ack[0] != 'M'); check |= (ack[1] != 'R'); check |= (ack[2] != ((i == 0)? '0' : '1')); check |= (chan->channel_num != int_n(tmp, &ack[3], 3)); if( check ) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__ ":check failed!\n"); return -RIG_EINVAL; // correct error type? } // all is well, save string strncpy( &mrtxt[i][0], ack, 52); } // FIXME: handle mem split // final check on data. (if RX!=TX mem split, or limit!) if( strncmp( &mrtxt[0][3], &mrtxt[1][3], 41-3) != 0 ) { rig_debug(RIG_DEBUG_ERR, "\n"__FUNCTION__ ": MEM split and band limits not yet supported!\n"); return -RIG_EINVAL; // FIXME: sending proper error? } // FIXME: 1) Since chan is not an array, we fudge and only do TX! // even if split!!!!!!!! // FIXME: 2) we only handle regular memories, not everything // FIXME: 3) I store only data ts2k actually saves in memory // some values are the ts2k value, not the hamlib value! // (keep same order as channel struct to ease debugging!) // chan->channel_num = ; // already set? // The following may be used to indicate we're reading limits (290-299). // At any rate, it's currently unused. chan->bank_num = 0; // I merge the two--do not use! --Dale chan->lock = int_n(tmp, &mrtxt[0][18], 1); chan->freq = int_n(tmp, &mrtxt[0][06], 11); chan->mode = ts2k_mode_list[ int_n(tmp, &mrtxt[0][17], 1) ]; if(chan->mode == RIG_MODE_AM || chan->tx_mode == RIG_MODE_FM) i = 1; else i = 0; chan->width = 0; chan->tx_freq = int_n(tmp, &mrtxt[1][06], 11); chan->tx_mode = ts2k_mode_list[ int_n(tmp, &mrtxt[1][17], 1)]; chan->tx_width = 0; chan->split = 0; chan->rptr_shift = int_n(tmp, &mrtxt[1][28], 1); chan->rptr_offs = int_n(tmp, &mrtxt[1][29], 9); if(chan->tx_mode == RIG_MODE_AM || chan->tx_mode == RIG_MODE_FM) i = 1; else i = 0; chan->tuning_step = ts2k_steps[i][ int_n(tmp,& mrtxt[1][38], 2) ]; chan->rit = 0; chan->xit = 0; chan->funcs = 0; for( i=0; ilevels[i].f = 0.0; // I'd figured this out. else chan->levels[i].f = 0.0; } chan->ctcss_tone = ts2k_ctcss_list[ int_n(tmp, &mrtxt[1][22], 2) + 1 ]; chan->ctcss_sql = int_n(tmp, &mrtxt[1][19], 1); chan->dcs_code = ts2k_dcs_list[ int_n(tmp, &mrtxt[1][24], 3) ]; chan->dcs_sql = int_n(tmp, &mrtxt[1][19], 1); chan->scan_group = int_n(tmp, &mrtxt[1][40], 1); // chan->flags = curr_vfo; // n/a // FIXME : The following may have trailing garbage strncpy( chan->channel_desc, &mrtxt[1][41], 8); chan->channel_desc[8] = '\0'; #ifdef _USEVFO // if curr mem is changed at top, this'll restore it if( ( vfo == RIG_VFO_MEM_A || vfo == RIG_VFO_MEM_C ) ) { } rig_debug(RIG_DEBUG_ERR, __FUNCTION__": restoring mem=%i\n", curr_mem); retval = ts2k_set_mem(rig, curr_vfo, curr_mem); CHKERR(retval); #endif return RIG_OK; } /* * ts2k_set_channel() Here I read a channel_t and memory data. I'm * assuming this is correct. Anyway, this is a useful function. * The only quirk is that the TS-2000 saves both TX as well * as RX data separately. This function can easily be modified * to write as if there were 600 memories but that would lead * to confusion and the rig certainly don't operate that way. * I hope to channel_t *chan changed to channel_t *chan[2]. * (As well as vfo_t too.) Much thanks to Stephane for already * having the most important stuff working from the start * (mostly anyway). * * From here on out I'm gonna try to use hamlib-1.1.0.pdf * for the design document and hopefully I'll know which way * to go when things aren't the way they should be. We'll * see how things go. * --Dale kd7e */ int ts2k_set_channel(RIG * rig, const channel_t *chan) { char mrtxt[2][60], mrcmd[10], ack[60]; int retval, i, j, mr_len[2], ack_len; // the following are the actual memory data to be written. unsigned int p1, // RX/TX (bool) p2p3, // this is not a bug--I combine bank/channel p4, // freq p5, // mode p6, // lockout p7, // tone, ctcss, dcs p8, // tone # p9, // CTCSS # p10, // DCS code p11, // revers status p12, // repeater shift p13, // offset freq p14, // step size p15; // memory group (0-9) char *p16; // 8 char + 1 null byte ack_len = 10; /* * Write everthing in order. We only set things that * the rig actually puts in memory. This way, a set * followed by a get will match exactly. Otherwise, * we'll have to fudge and won't know when we have * a valid save. (FIXME: delete all this useless * comment stuff after the code is working. --Dale) */ /* FIXME: we are required to have RX/TX match */ if(chan->freq != chan->tx_freq) return -RIG_EINVAL; // should be 'unimplemented' for(i=0; i<2; i++) { p1 = (unsigned int) i; // 0=RX, 1=TX p2p3 = (unsigned int) chan->channel_num; // we'll compare 'em later if( i == 0 ) p4 = (unsigned int) chan->freq; else p4 = (unsigned int) chan->tx_freq; for(j=0; j<(sizeof(ts2k_mode_list)/sizeof(int)); j++) { if(chan->mode == ts2k_mode_list[j]) break; } p5 = (unsigned int) j; // FIXME: either not found, or last! p6 = (unsigned int) chan->lock; p7 = 0; // FIXME: to lazy to sort this out right now p8 = 0; // " " " " " p9 = 0; // " " " " " p10 = 0; // " " " " " p11 = 0; // " " " " " p12 = 0; // " " " " " p13 = 0; // " " " " " p14 = 0; // " " " " " p15 = (unsigned int) chan->scan_group; p16 = &(chan->channel_desc[0]); mr_len[i] = sprintf( &(mrtxt[i][0]), "mr%1u%3u%11u%1u%1u%1u%2u%2u%3u%1u%1u%9u%2u%1u%s;", p1, p2p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16 ); // yikes! retval = ts2k_transaction(rig, &mrtxt[i][0], mr_len[i], ack, &ack_len); CHKERR(retval); // FIXME: now readback the string and make sure it worked! } return retval; } /* * ts2k_vfo_ctrl() set PTT/CTRL based on VFO arg * * (Taken from my revision of ts2k_set_vfo()) * * status: VFOA, VFOB, VFOC, Main, Sub, * MEMA, MEMC, CALLA, CALLC * VFO_AB, VFO_BA, ... * They all work! --Dale */ int ts2k_vfo_ctrl(RIG * rig, vfo_t vfo) { int ptt, ctrl; int retval; ptt = ctrl = 0; // Main/Sub Active Transceiver switch (vfo) { case RIG_VFO_A: case RIG_VFO_B: case RIG_VFO_AB: // split case RIG_VFO_BA: case RIG_CTRL_SAT: // Should be PTT on main CTRL on sub (?) case RIG_VFO_MAIN: case RIG_VFO_MEM_A: case RIG_VFO_CALL_A: ctrl = TS2K_CTRL_ON_MAIN; // FIXME : these are independent! ptt = TS2K_PTT_ON_MAIN; break; case RIG_VFO_C: case RIG_VFO_SUB: case RIG_VFO_MEM_C: case RIG_VFO_CALL_C: ctrl = TS2K_CTRL_ON_SUB; ptt = TS2K_PTT_ON_SUB; break; default: break; } // set PTT/CTRL retval = ts2k_set_ctrl(rig, ptt, ctrl); if (retval != RIG_OK) return -RIG_EINVAL; return retval; } /* * status: ok, no vfo checks */ int ts2k_get_dcs_code(RIG * rig, vfo_t vfo, tone_t *code) { char ack[10], tmp[10]; int retval, acklen, i; acklen = 10; retval = ts2k_transaction(rig, "qc;", 6, ack, &acklen); CHKERR(retval); i = int_n(tmp, &ack[2], 3); *code = ts2k_dcs_list[i]; return RIG_OK; } /* * status: ok, no vfo checks */ int ts2k_set_dcs_code(RIG * rig, vfo_t vfo, tone_t code) { char ack[10], cmd[10], tmp[10]; int retval, acklen, cmdlen, i; // we only allow exact matches here i=0; while(code != ts2k_dcs_list[i]) { if(ts2k_dcs_list[i] == 0) return -RIG_EINVAL; i++; } cmdlen = sprintf(cmd, "qc%03u;", i); return ts2k_transaction(rig, cmd, cmdlen, NULL, NULL); } /* * status: new, all guesses and assumptions! * know nothing about txwidth (which one?) * or tx = rx + txwidth? */ int ts2k_set_split_mode(RIG * rig, vfo_t vfo, rmode_t txmode, pbwidth_t txwidth) { vfo_t vtmp; switch(vfo) { case RIG_VFO_AB: vtmp = RIG_VFO_B; break; case RIG_VFO_BA: vtmp = RIG_VFO_A; break; default: return -RIG_EINVAL; } return ts2k_set_mode(rig, vtmp, txmode, txwidth); } int ts2k_get_split_mode(RIG *rig, vfo_t vfo, rmode_t *txmode, pbwidth_t *txwidth) { vfo_t vtmp; switch(vfo) { case RIG_VFO_AB: vtmp = RIG_VFO_B; break; case RIG_VFO_BA: vtmp = RIG_VFO_A; break; default: return -RIG_EINVAL; } return ts2k_get_mode(rig, vtmp, txmode, txwidth); } /* * status: new */ int ts2k_scan(RIG *rig, vfo_t vfo, scan_t scan, int ch) { int retval; vfo_t v; rig_debug(RIG_DEBUG_ERR, __FUNCTION__": starting...\n"); if(vfo==RIG_VFO_CURR) { retval = ts2k_get_vfo(rig, &v); CHKERR(retval); } else v = vfo; // hopefully, this'll work. rig does nothing if already in scan! retval = ts2k_scan_off(rig); CHKERR(retval); rig_debug(RIG_DEBUG_ERR, __FUNCTION__": got VFO = %x\n", v); // set proper vfo first (already done?) switch(v) { case RIG_VFO_MEM: // Currently selected Main/Sub case RIG_VFO_MEM_A: // Main case RIG_VFO_MEM_C: // Sub // FIXME: we should set the group and fall through /* nobreak */ // case RIG_VFO_VFO: // Currently selected Main/Sub case RIG_VFO_A: // Main case RIG_VFO_B: // Main case RIG_VFO_C: // Sub retval = ts2k_set_vfo(rig, v); // already set? CHKERR(retval); break; case RIG_VFO_CALL_A: // case RIG_VFO_CALL_C: default: rig_debug(RIG_DEBUG_ERR, __FUNCTION__": vfo 'defaulted'\n"); return -RIG_ENIMPL; // unimplemented, but valid scan } rig_debug(RIG_DEBUG_ERR, __FUNCTION__": VFO set!\n"); retval = ts2k_scan_off(rig); CHKERR(retval); switch(scan) { case RIG_SCAN_STOP: return ts2k_scan_off(rig); break; case RIG_SCAN_PROG: if(ch<290) return -RIG_EINVAL; retval = ts2k_set_mem(rig, vfo, ch); CHKERR(retval); /* nobreak */ case RIG_SCAN_MEM: /* nobreak */ case RIG_SCAN_VFO: return ts2k_scan_on(rig, '1'); // Look Ma, I'm scanning! break; case RIG_SCAN_SLCT: /* nobreak */ case RIG_SCAN_PRIO: /* nobreak */ default: rig_debug(RIG_DEBUG_ERR, __FUNCTION__": scan 'defaulted'\n"); return -RIG_ENIMPL; // unimplemented, but valid scan } } int ts2k_scan_on(RIG *rig, char sc) { char cmd[]="sc0;", ack[10]; int retval, cmdlen, acklen, i; rig_debug(RIG_DEBUG_ERR, __FUNCTION__": turning scan on\n"); cmd[2] = sc; retval = ts2k_transaction(rig, cmd, 4, NULL, NULL); CHKERR(retval); rig_debug(RIG_DEBUG_ERR, __FUNCTION__": turned scan on\n"); // force reply when turning scan on if(sc != '0') { cmd[2] = ';'; cmd[3] = '\0'; retval = ts2k_transaction(rig, cmd, 3, ack, &acklen); CHKERR(retval); if(ack[2] != sc) return -RIG_EINVAL; } return RIG_OK; } int ts2k_scan_off(RIG *rig) { rig_debug(RIG_DEBUG_ERR, __FUNCTION__": turning scan off\n"); return ts2k_scan_on(rig, '0'); } int ts2k_get_parm(RIG *rig, setting_t parm, value_t *val) { int retval, acklen, cmdlen; char ack[30], cmd[30]; switch( parm ) { case RIG_PARM_BEEP: cmdlen = sprintf(cmd, "ex0120000;"); break; case RIG_PARM_BACKLIGHT: cmdlen = sprintf(cmd, "ex0000000;"); break; case RIG_PARM_KEYLIGHT: cmdlen = sprintf(cmd, "ex0010000;"); break; case RIG_PARM_APO: cmdlen = sprintf(cmd, "ex0570000;"); break; case RIG_PARM_ANN: return -RIG_ENIMPL; case RIG_PARM_TIME: return -RIG_ENAVAIL; default: return -RIG_EINTERNAL; } acklen = 30; retval = ts2k_transaction(rig, cmd, cmdlen, ack, &acklen); if(retval == RIG_OK) { switch( parm ) { case RIG_PARM_BEEP: val->i = (int)(ack[9] - '0'); break; case RIG_PARM_BACKLIGHT: case RIG_PARM_KEYLIGHT: val->f = (float)(ack[9] - '0'); break; case RIG_PARM_APO: val->i = (int) int_n(cmd, &ack[9], 2); // cmd is TMP now break; default: return -RIG_EINTERNAL; } } return retval; } int ts2k_set_parm(RIG *rig, setting_t parm, value_t val) { char cmd[30]; int retval, cmdlen, i; rig_debug(RIG_DEBUG_ERR, __FUNCTION__": val.i = %u\n", val.i); rig_debug(RIG_DEBUG_ERR, __FUNCTION__": val.f = %f\n", val.f); switch( parm ) { case RIG_PARM_APO: cmdlen = sprintf(cmd, "ex0570000%01u;", (val.i>180)? 3: val.i/60); break; case RIG_PARM_BEEP: cmdlen = sprintf(cmd, "ex0120000%01u;", (val.i>9)? 9: val.i); break; case RIG_PARM_BACKLIGHT: cmdlen = sprintf(cmd, "ex0000000%01u;", (int) ((val.f>1.0)? 4.0 : val.f*4.0) ); break; case RIG_PARM_KEYLIGHT: cmdlen = sprintf(cmd, "ex0010000%01u;", (val.i==0)? 0: 1); break; case RIG_PARM_ANN: return -RIG_ENIMPL; case RIG_PARM_TIME: case RIG_PARM_BAT: return -RIG_ENAVAIL; default: return -RIG_EINTERNAL; } retval = ts2k_transaction(rig, cmd, cmdlen, NULL, NULL); return retval; } #undef CHKERR #undef STUFF // End