Logo Search packages:      
Sourcecode: hamlib version File versions

uniden_digital.c

/*
 *  Hamlib Uniden backend - uniden_digital backend
 *  Copyright (c) 2001-2008 by Stephane Fillod
 *
 *    $Id: uniden_digital.c,v 1.7 2008/10/22 04:11:28 roger-linux 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 <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.
 */

static const struct { rig_model_t model; const char *id; }
uniden_id_string_list[] = {
      { RIG_MODEL_BCD396T,  "BCD396T" },
      { RIG_MODEL_BCD996T,  "BCD99tT" },
      { RIG_MODEL_NONE, NULL },     /* end marker */
};

/* 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", __FUNCTION__, 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", __FUNCTION__, 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", __FUNCTION__, 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", __FUNCTION__, 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", __FUNCTION__, 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", __FUNCTION__, 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", __FUNCTION__, BUFSZ);
      rig_debug(RIG_DEBUG_VERBOSE, "%s: DEBUG info_len'%i'\n", __FUNCTION__, 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", __FUNCTION__, 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
}


Generated by  Doxygen 1.6.0   Back to index