Hamlib/uniden/uniden_digital.c

433 wiersze
12 KiB
C

/*
* Hamlib Uniden backend - uniden_digital backend
* Copyright (c) 2001-2008 by Stephane Fillod
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <math.h>
#include "hamlib/rig.h"
#include "serial.h"
#include "misc.h"
#include "uniden_digital.h"
/*
* Uniden Digital backend should work for:
* BCD996T as well as the BCD396T. Some protocols available for the
* BCD996T may not be available for the BCD396T or modified to work.
*
* Protocol information can be found here:
* http://www.uniden.com/files/BCD396T_Protocol.pdf
* http://www.uniden.com/files/BCD996T_Protocol.pdf
*
* There are undocumented commands such as firmware_dump and
* firmware_load. These commands are defined within DSctl code.
*
* There are two methods of retrieving the next memory location
* (aka frequency bank). Use either the "Get Next Location" or
* use the address returned from one of the commands. If you decide
* the latter method, the order is slightly confusing but, I have it
* well documented within DSctl. The latter method is also as much
* as 30% faster then using the Uniden software or "Get Next
* Location" command.
*/
#if 0
// deactivated temporarily because uniden_id_string_list is nowhere used
static const struct { rig_model_t model; const char *id; }
uniden_id_string_list[] =
{
{ RIG_MODEL_BCD396T, "BCD396T" },
{ RIG_MODEL_BCD996T, "BCD996T" },
{ RIG_MODEL_NONE, NULL }, /* end marker */
};
#endif
/* EOM is not consistant with this BCD996T!
* Some commands return newline while others carriage return.
* Some commands return nothing special for End of Line char. */
#define EOM "\r" /* end of message */
/* I'm still getting clipped output from buffers being too small elsewhere! */
#define BUFSZ 256 /* Wild guess, 64 was too small. */
/**
* uniden_transaction
* uniden_digital_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).
* replystr - Reply prefix to be expected. Replystr can also be NULL, indicating
* that the prefix is either the cmdstr prefix or OK.
* 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.
*/
int
uniden_digital_transaction(RIG *rig, const char *cmdstr, int cmd_len,
const char *replystr,
char *data, size_t *datasize)
{
struct rig_state *rs;
int retval;
int retry_read = 0;
char replybuf[BUFSZ];
size_t reply_len = BUFSZ;
rs = &rig->state;
rs->hold_decode = 1;
transaction_write:
serial_flush(&rs->rigport);
if (cmdstr)
{
retval = write_block(&rs->rigport, cmdstr, strlen(cmdstr));
if (retval != RIG_OK)
{
goto transaction_quit;
}
}
/* Always read the reply to known if it went OK */
if (!data)
{
data = replybuf;
}
if (!datasize)
{
datasize = &reply_len;
}
memset(data, 0, *datasize);
retval = read_string(&rs->rigport, data, *datasize, EOM, strlen(EOM));
if (retval < 0)
{
if (retry_read++ < rig->state.rigport.retry)
{
goto transaction_write;
}
goto transaction_quit;
}
else
{
*datasize = retval;
}
/* Check that command termination is correct
* FIXME: Sometimes the BCD996T DOES NOT return a
* consistant carriage return or newline.
* ie: STS command will not return either "\r" or "\n"! */
/*if (strchr(EOM, data[strlen(data)-1])==NULL) {
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 (strcmp(data, "OK"EOM))
{
/* everything is fine */
retval = RIG_OK;
goto transaction_quit;
}
/* Any syntax returning NG indicates a VALID Command but not entered
* in the right mode or using the correct parameters. ERR indicates
* an INVALID Command.
*/
if (strcmp(data, "NG"EOM))
{
/* Invalid command */
rig_debug(RIG_DEBUG_VERBOSE,
"%s: Command Format Error / Value Error for '%s'\n", __func__, cmdstr);
retval = -RIG_EPROTO;
goto transaction_quit;
}
if (strcmp(data, "ERR"EOM))
{
/* Command format error */
rig_debug(RIG_DEBUG_VERBOSE,
"%s: The Command is Invalid at this Time for '%s'\n", __func__, cmdstr);
retval = -RIG_EINVAL;
goto transaction_quit;
}
if (strcmp(data, "FER"EOM))
{
/* Framing error */
rig_debug(RIG_DEBUG_VERBOSE, "%s: Framing Error for '%s'\n", __func__, cmdstr);
retval = -RIG_EINVAL;
goto transaction_quit;
}
if (strcmp(data, "ORER"EOM))
{
/* Overrun error */
rig_debug(RIG_DEBUG_VERBOSE, "%s: Overrun Error for '%s'\n", __func__, cmdstr);
retval = -RIG_EINVAL;
goto transaction_quit;
}
#define CONFIG_STRIP_CMDTRM 1
#ifdef CONFIG_STRIP_CMDTRM
if (strlen(data) > 0)
{
data[strlen(data) - 1] = '\0'; /* not very elegant, but should work. */
}
else
{
data[0] = '\0';
}
#endif
/* Special case for SQuelch */
/*if (!memcmp(cmdstr,"SQ",2) && (replystr[0] == '-' || replystr[0] == '+')) {
retval = RIG_OK;
goto transaction_quit;
}*/
/* Command prefix if no replystr supplied */
if (!replystr)
{
replystr = cmdstr;
}
/*
* Check that received the correct reply. The first two characters
* should be the same as command.
*/
if (replystr && replystr[0] && (data[0] != replystr[0] ||
(replystr[1] && data[1] != replystr[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: Unexpected reply '%s'\n", __func__, data);
if (retry_read++ < rig->state.rigport.retry)
{
goto transaction_write;
}
retval = -RIG_EPROTO;
goto transaction_quit;
}
retval = RIG_OK;
transaction_quit:
rs->hold_decode = 0;
return retval;
}
/*
* uniden_get_info
* Assumes rig!=NULL
*/
const char *uniden_digital_get_info(RIG *rig)
{
static char infobuf[BUFSZ];
size_t info_len = BUFSZ / 2, mdlinfo_len = BUFSZ / 2;
int ret;
/* GET CURRENT STATUS -- STS */
ret = uniden_digital_transaction(rig, "STS" EOM, 3, NULL, infobuf, &info_len);
/* NOTE FOR ME: Check Buffer Size with what we got returned in info_len.
* Don't know the max length of return on these units, so check frequently!
* Use five v's (-vvvvv) to activate output. */
rig_debug(RIG_DEBUG_VERBOSE, "%s: DEBUG BUFSZ'%i'\n", __func__, BUFSZ);
rig_debug(RIG_DEBUG_VERBOSE, "%s: DEBUG info_len'%i'\n", __func__,
(int)info_len);
if (ret != RIG_OK)
{
return NULL;
}
/* Example output:
* STS,011000, XXX ,,Fa
*
* STS command returns 3 lines including system, truck, freq info
*
* XXX indicates the BCD996T returns some non-printable ascii chars
* within it's comma separated fields. See pg 30-32 of BCD996T_Protocol.pdf.
* These chars cause abnomalies on stdout! */
/* FIXME: Strip or replace non-printable chars return from STS command!
* (Below is a snip from DSctl utils.c file)
*
* For example, search each character. For each sequence of chars found, replace with normal printable ascii [KEY]
* if (strstr(bcd_chars, "\213") != 0)
* strncat(tmp_line, "[FUNCTION KEY] ", 15);
*
* if (strstr(bcd_chars, "\215\216\217\220") != 0)
* strncat(tmp_line, "[HOLD] ", 7); */
if (info_len < 4)
{
return NULL;
}
if (info_len >= BUFSZ)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: DEBUG Max BUFSZ Reached: info_len = '%i'\n",
__func__, (int)info_len);
info_len = BUFSZ - 1;
}
infobuf[info_len] = '\0';
/* VR not on every rig <- This doesn't belong here for the newer BCD* units*/
/* VR1.00 */
/*ret = uniden_digital_transaction (rig, "VR" EOM, 2, NULL, infobuf+info_len, &vrinfo_len);
if (ret == RIG_OK)
{*/
/* overwrite "VR" */
/* FIXME: need to filter \r or it screws w/ stdout */
/*infobuf[info_len] = '\n';
infobuf[info_len+1] = ' ';
}
else
{
infobuf[info_len] = '\0';
}*/
/* GET MODEL INFO -- MDL */
ret = uniden_digital_transaction(rig, "MDL" EOM, 3, NULL, infobuf + info_len,
&mdlinfo_len);
if (ret == RIG_OK)
{
infobuf[info_len] = '\n';
infobuf[info_len + 1] = ' ';
}
else
{
infobuf[info_len] = '\0';
}
/* GET FIRMWARE VERSION -- VER */
ret = uniden_digital_transaction(rig, "VER" EOM, 3, NULL, infobuf + info_len,
&mdlinfo_len);
if (ret == RIG_OK)
{
infobuf[info_len] = '\n';
infobuf[info_len + 1] = ' ';
}
else
{
infobuf[info_len] = '\0';
}
/* skip beginning "STS, " */
/* FIXME: What about clipping the above two other MDL & VER Commands? */
return infobuf + 4;
}
/*
* skeleton
*/
int uniden_digital_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
#if 0
char freqbuf[BUFSZ];
size_t freq_len = BUFSZ;
/* freq in hundreds of Hz */
freq /= 100;
/* exactly 8 digits */
freq_len = sprintf(freqbuf, "RF%08u" EOM, (unsigned)freq);
return uniden_transaction(rig, freqbuf, freq_len, NULL, NULL, NULL);
#else
return -RIG_ENIMPL;
#endif
}
/*
* skeleton
*/
int uniden_digital_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
#if 0
char freqbuf[BUFSZ];
size_t freq_len = BUFSZ;
int ret;
ret = uniden_transaction(rig, "RF" EOM, 3, NULL, freqbuf, &freq_len);
if (ret != RIG_OK)
{
return ret;
}
if (freq_len < 10)
{
return -RIG_EPROTO;
}
sscanf(freqbuf + 2, "%"SCNfreq, freq);
/* returned freq in hundreds of Hz */
*freq *= 100;
return RIG_OK;
#else
return -RIG_ENIMPL;
#endif
}