/* * Hamlib Kenwood backend - TM-V7 description * Copyright (c) 2004-2005 by Stephane Fillod * * $Id: tmv7.c,v 1.10 2005-02-24 22:35:24 fillods 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. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include /* String function definitions */ #include /* UNIX standard function definitions */ #include #include "kenwood.h" #include "th.h" #include "misc.h" #if 1 #define RIG_ASSERT(x) if (!(x)) { rig_debug(RIG_DEBUG_ERR, "Assertion failed on line %i\n",__LINE__); abort(); } #else #define RIG_ASSERT(x) #endif #define TMV7_FUNC_ALL (\ RIG_FUNC_TBURST \ ) #define TMV7_LEVEL_ALL (\ RIG_LEVEL_RAWSTR| \ RIG_LEVEL_SQL| \ RIG_LEVEL_AF| \ RIG_LEVEL_RFPOWER\ ) #define TMV7_CHANNEL_CAPS \ .freq=1,\ .tx_freq=1,\ .mode=1,\ .tuning_step=1,\ .rptr_shift=1,\ .ctcss_tone=1,\ .ctcss_sql=1,\ .channel_desc=1 #ifndef RIG_TONEMAX #define RIG_TONEMAX 38 #endif #define RIG_VFO_A_OP (RIG_OP_UP|RIG_OP_DOWN|RIG_OP_TO_VFO) #define ACKBUF_LEN 128 const struct kenwood_priv_caps tmv7_priv_caps = { .cmdtrm = EOM_TH, /* Command termination character */ }; #define EOM EOM_TH /* tmv7 procs */ static int tmv7_open(RIG *rig); static int tmv7_decode_event (RIG *rig); static int tmv7_set_vfo (RIG *rig, vfo_t vfo); static int tmv7_get_mode (RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width); static int tmv7_get_powerstat (RIG *rig, powerstat_t *status); static int tmv7_get_channel(RIG *rig, channel_t *chan); static int tmv7_set_channel(RIG *rig, const channel_t *chan); /* * tm-v7 rig capabilities. */ const struct rig_caps tmv7_caps = { .rig_model = RIG_MODEL_TMV7, .model_name = "TM-V7", .mfg_name = "Kenwood", .version = "0.2", .copyright = "LGPL", .status = RIG_STATUS_BETA, .rig_type = RIG_TYPE_MOBILE, .ptt_type = RIG_PTT_RIG, .dcd_type = RIG_DCD_RIG, .port_type = RIG_PORT_SERIAL, .serial_rate_min = 9600, .serial_rate_max = 9600, .serial_data_bits = 8, .serial_stop_bits = 1, .serial_parity = RIG_PARITY_NONE, .serial_handshake = RIG_HANDSHAKE_NONE, .write_delay = 1, .post_write_delay = 0, .timeout = 500, .retry = 3, .has_set_func = TMV7_FUNC_ALL, .has_get_level = TMV7_LEVEL_ALL, .has_set_level = RIG_LEVEL_SET(TMV7_LEVEL_ALL), .level_gran = { [LVL_RAWSTR] = { .min = { .i = 0 }, .max = { .i = 7 } }, [LVL_SQL] = { .min = { .i = 0 }, .max = { .i = 32 } }, [LVL_AF] = { .min = { .i = 0 }, .max = { .i = 32 } }, [LVL_RFPOWER] = { .min = { .i = 0 }, .max = { .i = 2 } }, }, .parm_gran = {}, .ctcss_list = kenwood38_ctcss_list, .dcs_list = NULL, .preamp = { RIG_DBLST_END, }, .attenuator = { RIG_DBLST_END, }, .max_rit = Hz(0), .max_xit = Hz(0), .max_ifshift = Hz(0), .vfo_ops = RIG_VFO_A_OP, .targetable_vfo = RIG_TARGETABLE_NONE, .transceive = RIG_TRN_RIG, .bank_qty = 0, .chan_desc_sz = 6, .chan_list = { { 1, 90 , RIG_MTYPE_MEM , {TMV7_CHANNEL_CAPS}}, /* normal MEM VHF */ { 101, 190, RIG_MTYPE_MEM , {TMV7_CHANNEL_CAPS}}, /* normal MEM UHF */ { 201,206, RIG_MTYPE_EDGE , {TMV7_CHANNEL_CAPS}}, /* L MEM */ { 211,216, RIG_MTYPE_EDGE , {TMV7_CHANNEL_CAPS}}, /* U MEM */ { 221,222, RIG_MTYPE_MEM , {TMV7_CHANNEL_CAPS}}, /* Call V/U */ RIG_CHAN_END, }, .rx_range_list1 = { {MHz(118),MHz(174),RIG_MODE_AM | RIG_MODE_FM,-1,-1,RIG_VFO_A}, {MHz(300),MHz(470),RIG_MODE_FM,-1,-1,RIG_VFO_B}, RIG_FRNG_END, }, /* rx range */ .tx_range_list1 = { {MHz(118),MHz(174), RIG_MODE_FM,W(5),W(50),RIG_VFO_A}, {MHz(300),MHz(470),RIG_MODE_FM,W(5),W(35),RIG_VFO_B}, RIG_FRNG_END, }, /* tx range */ .rx_range_list2 = { {MHz(118),MHz(174),RIG_MODE_AM | RIG_MODE_FM,-1,-1,RIG_VFO_A}, {MHz(300),MHz(470),RIG_MODE_FM,-1,-1,RIG_VFO_B}, RIG_FRNG_END, }, /* rx range */ .tx_range_list2 = { {MHz(118),MHz(174), RIG_MODE_FM,W(5),W(50),RIG_VFO_A}, {MHz(300),MHz(470),RIG_MODE_FM,W(5),W(35),RIG_VFO_B}, RIG_FRNG_END, }, /* tx range */ .tuning_steps = { {RIG_MODE_FM,kHz(5)}, {RIG_MODE_FM,kHz(6.25)}, {RIG_MODE_FM,kHz(10)}, {RIG_MODE_FM,kHz(12.5)}, {RIG_MODE_FM,kHz(15)}, {RIG_MODE_FM,kHz(25)}, {RIG_MODE_FM,kHz(50)}, RIG_TS_END, }, /* mode/filter list, remember: order matters! */ .filters = { RIG_FLT_END, }, .str_cal ={ 4, { {0, -60 }, {1, -30,}, {5,0}, {7,20}}}, /* rought guess */ .priv = (void *)&tmv7_priv_caps, .rig_open = tmv7_open, .rig_close = NULL, .set_freq = th_set_freq, .get_freq = th_get_freq, .get_mode = tmv7_get_mode, .set_vfo = tmv7_set_vfo, .get_vfo = th_get_vfo, .set_mem = th_set_mem, .get_mem = th_get_mem, .set_channel = tmv7_set_channel, .get_channel = tmv7_get_channel, .set_trn = th_set_trn, .get_trn = th_get_trn, .set_func = th_set_func, .get_func = th_get_func, .get_level = th_get_level, .set_level = th_set_level, .get_info = th_get_info, .get_powerstat = tmv7_get_powerstat, .vfo_op = th_vfo_op, .set_ptt = th_set_ptt, .get_dcd=th_get_dcd, .decode_event = tmv7_decode_event, }; /* --------------------------------------------------------------------- */ int tmv7_decode_event (RIG *rig) { char asyncbuf[ACKBUF_LEN]; int retval, asyncbuf_len = ACKBUF_LEN-1; rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __FUNCTION__); retval = kenwood_transaction(rig, NULL, 0, asyncbuf, &asyncbuf_len); if (retval != RIG_OK) return retval; rig_debug(RIG_DEBUG_TRACE, "%s: Decoding message\n", __FUNCTION__); if (asyncbuf[0] == 'B' && asyncbuf[1] == 'U' && asyncbuf[2] == 'F') { freq_t freq, offset; int step, shift, rev, tone, ctcss, tonefq, ctcssfq; retval = sscanf(asyncbuf, "BUF 0,%"SCNfreq",%d,%d,%d,%d,%d,,%d,,%d,%"SCNfreq, &freq, &step, &shift, &rev, &tone, &ctcss, &tonefq, &ctcssfq, &offset); if (retval != 11) { rig_debug(RIG_DEBUG_ERR, "%s: Unexpected BUF message '%s'\n", __FUNCTION__, asyncbuf); return -RIG_ERJCTED; } rig_debug(RIG_DEBUG_TRACE, "%s: Buffer (freq %"PRIfreq" Hz, mode %d)\n", __FUNCTION__, freq); /* Callback execution */ if (rig->callbacks.vfo_event) { rig->callbacks.vfo_event(rig, RIG_VFO_A, rig->callbacks.vfo_arg); } if (rig->callbacks.freq_event) { rig->callbacks.freq_event(rig, RIG_VFO_A, freq, rig->callbacks.freq_arg); } /* if (rig->callbacks.mode_event) { rig->callbacks.mode_event(rig, RIG_VFO_A, mode, RIG_PASSBAND_NORMAL, rig->callbacks.mode_arg); } */ /* --------------------------------------------------------------------- */ } else if (asyncbuf[0] == 'S' && asyncbuf[1] == 'M') { int lev; retval = sscanf(asyncbuf, "SM 0,%d", &lev); if (retval != 2) { rig_debug(RIG_DEBUG_ERR, "%s: Unexpected SM message '%s'\n", __FUNCTION__, asyncbuf); return -RIG_ERJCTED; } rig_debug(RIG_DEBUG_TRACE, "%s: Signal strength event - signal = %.3f\n", __FUNCTION__, (float)(lev / 5.0)); /* Callback execution */ #if STILLHAVETOADDCALLBACK if (rig->callbacks.strength_event) rig->callbacks.strength_event(rig, RIG_VFO_0,(float)(lev / 5.0), rig->callbacks.strength_arg); #endif /* --------------------------------------------------------------------- */ } else if (asyncbuf[0] == 'B' && asyncbuf[1] == 'Y') { int busy; retval = sscanf(asyncbuf, "BY 0,%d", &busy); if (retval != 2) { rig_debug(RIG_DEBUG_ERR, "%s: Unexpected BY message '%s'\n", __FUNCTION__, asyncbuf); return -RIG_ERJCTED; } rig_debug(RIG_DEBUG_TRACE, "%s: Busy event - status = '%s'\n", __FUNCTION__, (busy == 0) ? "OFF" : "ON" ); return -RIG_ENIMPL; /* This event does not have a callback. */ /* --------------------------------------------------------------------- */ } else if (asyncbuf[0] == 'V' && asyncbuf[1] == 'M' && asyncbuf[2] == 'C') { vfo_t bandmode; retval = sscanf(asyncbuf, "VMC 0,%d", &bandmode); if (retval != 1) { rig_debug(RIG_DEBUG_ERR, "%s: Unexpected VMC message '%s'\n", __FUNCTION__, asyncbuf); return -RIG_ERJCTED; } switch (bandmode) { case 0: bandmode = RIG_VFO_VFO; break; case 2: bandmode = RIG_VFO_MEM; break; /* case 3: bandmode = RIG_VFO_CALL; break; */ default: bandmode = RIG_VFO_CURR; break; } rig_debug(RIG_DEBUG_TRACE, "%s: Mode of Band event - %d\n", __FUNCTION__, bandmode); /* TODO: This event does not have a callback! */ return -RIG_ENIMPL; /* --------------------------------------------------------------------- */ } else { rig_debug(RIG_DEBUG_ERR, "%s: Unsupported transceive cmd '%s'\n", __FUNCTION__, asyncbuf); return -RIG_ENIMPL; } return RIG_OK; } /* --------------------------------------------------------------------- */ int tmv7_set_vfo (RIG *rig, vfo_t vfo) { char vfobuf[16], ackbuf[ACKBUF_LEN]; int retval,ack_len; rig_debug(RIG_DEBUG_TRACE, "%s: called %d\n", __FUNCTION__,vfo); switch (vfo) { case RIG_VFO_A: case RIG_VFO_VFO: sprintf(vfobuf, "VMC 0,0" EOM); break; case RIG_VFO_B: sprintf(vfobuf, "VMC 1,0" EOM); break; case RIG_VFO_MEM: sprintf(vfobuf, "BC" EOM); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, vfobuf, strlen(vfobuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; sprintf(vfobuf, "VMC %c,2" EOM,ackbuf[3]); break; default: rig_debug(RIG_DEBUG_ERR, "%s: Unsupported VFO %d\n", __FUNCTION__, vfo); return -RIG_EVFO; } ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, vfobuf, strlen(vfobuf), ackbuf, &ack_len); if (retval != RIG_OK) { rig_debug(RIG_DEBUG_ERR, "%s: bad return \n", __FUNCTION__); return retval; } rig_debug(RIG_DEBUG_TRACE, "%s: next %d\n", __FUNCTION__,vfo); switch (vfo) { case RIG_VFO_A: case RIG_VFO_VFO: sprintf(vfobuf, "BC 0,0" EOM); break; case RIG_VFO_B: sprintf(vfobuf, "BC 1,1" EOM); break; case RIG_VFO_MEM: return RIG_OK; default: return RIG_OK; } rig_debug(RIG_DEBUG_TRACE, "%s: next2\n", __FUNCTION__); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, vfobuf, strlen(vfobuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; return RIG_OK; } /* --------------------------------------------------------------------- */ int tmv7_get_mode (RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) { char ackbuf[ACKBUF_LEN]; int retval,ack_len=ACKBUF_LEN; int step; freq_t freq; rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __FUNCTION__); switch (vfo) { case RIG_VFO_CURR: break; case RIG_VFO_A: break; default: rig_debug(RIG_DEBUG_ERR, "%s: Unsupported VFO %d\n", __FUNCTION__, vfo); return -RIG_EVFO; } /* try to guess from frequency */ retval = kenwood_transaction(rig, "FQ"EOM, 3, ackbuf, &ack_len); if (retval != RIG_OK) return retval; sscanf(ackbuf,"FQ %"SCNfreq",%d",&freq,&step); if(freq channel_num<100) sprintf(req,"MR 0,0,%03d",chan->channel_num); else if(chan->channel_num<200) sprintf(req,"MR 1,0,%03d",chan->channel_num-100); else if(chan->channel_num<204) { sprintf(req,"MR 0,0,L%01d",chan->channel_num-200); sprintf(chan->channel_desc,"L%01d/V",chan->channel_num-200); } else if(chan->channel_num<211) { sprintf(req,"MR 1,0,L%01d",chan->channel_num-203); sprintf(chan->channel_desc,"L%01d/U",chan->channel_num-203); } else if(chan->channel_num<214) { sprintf(req,"MR 0,0,U%01d",chan->channel_num-210); sprintf(chan->channel_desc,"U%01d/V",chan->channel_num-210); } else if(chan->channel_num<220) { sprintf(req,"MR 1,0,U%01d",chan->channel_num-213); sprintf(chan->channel_desc,"U%01d/U",chan->channel_num-213); } else if(chan->channel_num<223) { if(chan->channel_num==221) { sprintf(req,"CR 0,0"); sprintf(chan->channel_desc,"Call V"); } if(chan->channel_num==222) { sprintf(req,"CR 1,0"); sprintf(chan->channel_desc,"Call U"); } } else return -RIG_EINVAL; sprintf(membuf,"%s"EOM,req); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, membuf, strlen(membuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; strcpy(scf,req); strcat(scf,",%"SCNfreq",%d,%d,%d,%d,0,%d,%d,000,%d,,0"); retval = sscanf(ackbuf, scf, &freq, &step, &shift, &rev, &tone, &ctcss, &tonefq, &ctcssfq); chan->freq=freq; chan->vfo=RIG_VFO_MEM; chan->tuning_step=rig->state.tuning_steps[step].ts; if(freq mode=RIG_MODE_AM; } else { chan->mode=RIG_MODE_FM; } switch(shift) { case 0 : chan->rptr_shift=RIG_RPT_SHIFT_NONE; break; case 1 : chan->rptr_shift=RIG_RPT_SHIFT_PLUS; break; case 2 : chan->rptr_shift=RIG_RPT_SHIFT_MINUS; break; } if(tone) chan->ctcss_tone=rig->caps->ctcss_list[tonefq==1?0:tonefq-2]; else chan->ctcss_tone=0; if(ctcss) chan->ctcss_sql=rig->caps->ctcss_list[ctcssfq==1?0:ctcssfq-2]; else chan->ctcss_sql=0; chan->tx_freq=RIG_FREQ_NONE; if(chan->channel_num<223 && shift==0) { req[5]='1'; sprintf(membuf,"%s"EOM,req); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, membuf, strlen(membuf), ackbuf, &ack_len); if (retval == RIG_OK) { strcpy(scf,req); strcat(scf,",%"SCNfreq",%d"); retval = sscanf(ackbuf, scf, &freq, &step); chan->tx_freq=freq; } } if(chan->channel_num<200) { if(chan->channel_num<100) sprintf(membuf,"MNA 0,%03d"EOM,chan->channel_num); else sprintf(membuf,"MNA 1,%03d"EOM,chan->channel_num-100); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, membuf, strlen(membuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; memcpy(chan->channel_desc,&ackbuf[10],7); } return RIG_OK; } /* --------------------------------------------------------------------- */ int tmv7_set_channel(RIG *rig, const channel_t *chan) { char membuf[ACKBUF_LEN],ackbuf[ACKBUF_LEN]; int retval,ack_len; char req[64]; long long freq; int chn, step, shift, tone, ctcss, tonefq, ctcssfq; chn=chan->channel_num; freq=(long long)chan->freq; for(step=0; rig->state.tuning_steps[step].ts!=0;step++) if(chan->tuning_step==rig->state.tuning_steps[step].ts) break; switch(chan->rptr_shift) { case RIG_RPT_SHIFT_NONE : shift=0; break; case RIG_RPT_SHIFT_PLUS: shift=1; break; case RIG_RPT_SHIFT_MINUS: shift=2; break; default: rig_debug(RIG_DEBUG_ERR, "%s: not supported shift\n", __FUNCTION__); return -RIG_EINVAL; } if(chan->ctcss_tone==0) { tone=0;tonefq=9; } else { tone=1; for (tonefq = 0; rig->caps->ctcss_list[tonefq] != 0 && tonefq < RIG_TONEMAX; tonefq++) { if (rig->caps->ctcss_list[tonefq] == chan->ctcss_tone) break; } tonefq=tonefq==0?1:tonefq+2; } if(chan->ctcss_sql==0) { ctcss=0;ctcssfq=9; } else { ctcss=1; for (ctcssfq = 0; rig->caps->ctcss_list[ctcssfq] != 0 && ctcssfq < RIG_TONEMAX; ctcssfq++) { if (rig->caps->ctcss_list[ctcssfq] == chan->ctcss_sql) break; } ctcssfq=ctcssfq==0?1:ctcssfq+2; } if(chan->channel_num<100) sprintf(req,"MW 0,0,%03d",chan->channel_num); else if(chan->channel_num<200) sprintf(req,"MW 1,0,%03d",chan->channel_num-100); else if(chan->channel_num<204) { sprintf(req,"MW 0,0,L%01d",chan->channel_num-200); } else if(chan->channel_num<211) { sprintf(req,"MW 1,0,L%01d",chan->channel_num-203); } else if(chan->channel_num<214) { sprintf(req,"MW 0,0,U%01d",chan->channel_num-210); } else if(chan->channel_num<220) { sprintf(req,"MW 1,0,U%01d",chan->channel_num-213); } else if(chan->channel_num<223) { if(chan->channel_num==221) { sprintf(req,"CW 0,0"); } if(chan->channel_num==222) { sprintf(req,"CW 1,0"); } } else return -RIG_EINVAL; if(chan->channel_num<221) sprintf(membuf, "%s,%011"PRIll",%01d,%01d,0,%01d,%01d,0,%02d,000,%02d,0,0"EOM, req,(long long)freq, step, shift, tone, ctcss, tonefq, ctcssfq); else sprintf(membuf, "%s,%011"PRIll",%01d,%01d,0,%01d,%01d,0,%02d,000,%02d,"EOM, req, (long long)freq, step, shift, tone, ctcss, tonefq, ctcssfq); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, membuf, strlen(membuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; if(chan->tx_freq!=RIG_FREQ_NONE) { req[5]='1'; sprintf(membuf, "%s,%011"PRIll",%01d"EOM, req,(long long)chan->tx_freq, step); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, membuf, strlen(membuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; } if(chan->channel_num<200) { if(chan->channel_num<100) sprintf(membuf,"MNA 0,%03d,%s"EOM,chan->channel_num,chan->channel_desc); else sprintf(membuf,"MNA 1,%03d,%s"EOM,chan->channel_num-100,chan->channel_desc); ack_len=ACKBUF_LEN; retval = kenwood_transaction(rig, membuf, strlen(membuf), ackbuf, &ack_len); if (retval != RIG_OK) return retval; } return RIG_OK; } /*-------------------------------------------------------------------- */ int tmv7_open(RIG *rig) { char ackbuf[ACKBUF_LEN],*strl,*stru; int retval,i,ack_len=ACKBUF_LEN; const freq_range_t frend=RIG_FRNG_END; /* just to be sure it's a TM-V7 */ retval = kenwood_transaction(rig, "ID"EOM, 3, ackbuf, &ack_len); if (retval != RIG_OK) return retval; if (ack_len<9 || strncmp(ackbuf,"ID TM-V7",8)) { rig_debug(RIG_DEBUG_ERR, "%s: Unexpected reply '%s'\n", __FUNCTION__, ackbuf); return -RIG_ERJCTED; } return RIG_OK; }