kopia lustrzana https://github.com/Hamlib/Hamlib
				
				
				
			
		
			
				
	
	
		
			498 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			498 wiersze
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
| /*
 | |
|  * hamlib - (C) Frank Singleton 2000 (javabear at users.sourceforge.net)
 | |
|  *              and the Hamlib Group (hamlib-developer at lists.sourceforge.net)
 | |
|  *
 | |
|  * newcat.c - (C) Nate Bargmann 2007 (n0nb at arrl.net)
 | |
|  *
 | |
|  * This shared library provides an API for communicating
 | |
|  * via serial interface to any newer Yaesu radio using the
 | |
|  * "new" text CAT interface.
 | |
|  *
 | |
|  * Models this code aims to support are FTDX-9000*, FT-2000,
 | |
|  * FT-950, FT-450.  Much testing remains.  -N0NB
 | |
|  *
 | |
|  *
 | |
|  * $Id: newcat.c,v 1.3 2007-12-01 22:09:52 n0nb Exp $
 | |
|  *
 | |
|  *
 | |
|  *  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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 | |
|  *
 | |
|  */
 | |
| 
 | |
| 
 | |
| #ifdef HAVE_CONFIG_H
 | |
| #include "config.h"
 | |
| #endif
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>  /* String function definitions */
 | |
| #include <unistd.h>  /* UNIX standard function definitions */
 | |
| 
 | |
| #include "hamlib/rig.h"
 | |
| #include "iofunc.h"
 | |
| #include "newcat.h"
 | |
| 
 | |
| /* global variables */
 | |
| static char cat_term = ';';             /* Yaesu command terminator */
 | |
| 
 | |
| /*
 | |
|  * future - private data
 | |
|  *
 | |
|  * FIXME: Does this need to be exposed to the application/frontend through
 | |
|  * rig_caps.priv?  I'm guessing not since it's private to the backend.  -N0NB
 | |
|  */
 | |
| 
 | |
| struct newcat_priv_data {
 | |
|     unsigned int        read_update_delay;              /* depends on pacing value */
 | |
|     vfo_t               current_vfo;                    /* active VFO from last cmd */
 | |
|     char                cmd_str[NEWCAT_DATA_LEN];       /* command string buffer */
 | |
|     char                ret_data[NEWCAT_DATA_LEN];      /* returned data--max value, most are less */
 | |
|     unsigned char       current_mem;                    /* private memory channel number */
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * ************************************
 | |
|  *
 | |
|  * Hamlib API functions
 | |
|  *
 | |
|  * ************************************
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * rig_init
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int newcat_init(RIG *rig) {
 | |
|     struct newcat_priv_data *priv;
 | |
|   
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
|   
 | |
|     priv = (struct newcat_priv_data *)malloc(sizeof(struct newcat_priv_data));
 | |
|     if (!priv)                                  /* whoops! memory shortage! */
 | |
|         return -RIG_ENOMEM;
 | |
|   
 | |
|     /* TODO: read pacing from preferences */
 | |
| //    priv->pacing = NEWCAT_PACING_DEFAULT_VALUE; /* set pacing to minimum for now */
 | |
|     priv->read_update_delay = NEWCAT_DEFAULT_READ_TIMEOUT; /* set update timeout to safe value */
 | |
|     priv->current_vfo =  RIG_VFO_MAIN;          /* default to whatever */
 | |
|     rig->state.priv = (void *)priv;
 | |
|   
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_cleanup
 | |
|  *
 | |
|  * the serial port is closed by the frontend
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int newcat_cleanup(RIG *rig) {
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
|   
 | |
|     if (rig->state.priv)
 | |
|         free(rig->state.priv);
 | |
|     rig->state.priv = NULL;
 | |
| 
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_open
 | |
|  *
 | |
|  * New CAT does not support pacing
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int newcat_open(RIG *rig) {
 | |
|     struct rig_state *rig_s;
 | |
| //    struct newcat_priv_data *priv;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
| 
 | |
| //    priv = (struct newcat_priv_data *)rig->state.priv;
 | |
|     rig_s = &rig->state;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: write_delay = %i msec\n",
 | |
|               __func__, rig_s->rigport.write_delay);
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: post_write_delay = %i msec\n",
 | |
|               __func__, rig_s->rigport.post_write_delay);
 | |
| 
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_close
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| int newcat_close(RIG *rig) {
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
| 
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_set_freq
 | |
|  *
 | |
|  * Set frequency for a given VFO
 | |
|  *
 | |
|  * If vfo is set to RIG_VFO_CUR then vfo from priv_data is used.
 | |
|  * If vfo differs from stored value then VFO will be set to the
 | |
|  * passed vfo.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int newcat_set_freq(RIG *rig, vfo_t vfo, freq_t freq) {
 | |
|     const struct rig_caps *caps;
 | |
|     struct newcat_priv_data *priv;
 | |
|     struct rig_state *state;
 | |
|     char c;
 | |
|     int err, len;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
| 
 | |
|     priv = (struct newcat_priv_data *)rig->state.priv;
 | |
|     caps = rig->caps;
 | |
|     state = &rig->state;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: passed vfo = 0x%02x\n", __func__, vfo);
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: passed freq = %"PRIfreq" Hz\n", __func__, freq);
 | |
| 
 | |
|     /* additional debugging */
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: R2 minimum freq = %"PRIfreq" Hz\n", __func__, caps->rx_range_list2[0].start);
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: R2 maximum freq = %"PRIfreq" Hz\n", __func__, caps->rx_range_list2[0].end);
 | |
| 
 | |
|     if (freq < caps->rx_range_list1[0].start || freq > caps->rx_range_list1[0].end ||
 | |
|         freq < caps->rx_range_list2[0].start || freq > caps->rx_range_list2[0].end)
 | |
|         return -RIG_EINVAL;
 | |
| 
 | |
|     if (vfo == RIG_VFO_CURR) {
 | |
|         vfo = priv->current_vfo;        /* from previous vfo cmd */
 | |
|         rig_debug(RIG_DEBUG_TRACE, "%s: priv->current_vfo = 0x%02x\n", __func__, vfo);
 | |
|     } else if (vfo != priv->current_vfo) {
 | |
|         /* force a VFO change if requested vfo value differs from stored value */
 | |
|         err = newcat_set_vfo(rig, vfo);
 | |
|         if (err != RIG_OK)
 | |
|             return err;
 | |
|     }
 | |
| 
 | |
|     switch (vfo) {
 | |
|     case RIG_VFO_A:
 | |
|         c = 'A';
 | |
|         break;
 | |
|     case RIG_VFO_B:
 | |
|         c = 'B';
 | |
|         break;
 | |
|     default:
 | |
|         return -RIG_ENIMPL;             /* Only VFO_A or VFO_B are valid */
 | |
|     }
 | |
| 
 | |
|     /* CAT command/terminator plus variable length frequency
 | |
|      * string length plus '\0' string terminator
 | |
|      */
 | |
|     len = snprintf(NULL, 0, "F%c%d%c", c, (int)freq, cat_term) + 1;
 | |
|     if (len < 0)
 | |
|         return -RIG_EINTERNAL;          /* bad news */
 | |
| 
 | |
|     /* Build the command string */
 | |
|     snprintf(priv->cmd_str, len, "F%c%d%c", c, (int)freq, cat_term);
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: cmd_str = %s\n", __func__, priv->cmd_str);
 | |
| 
 | |
|     err = write_block(&state->rigport, priv->cmd_str, strlen(priv->cmd_str));
 | |
|     if (err != RIG_OK)
 | |
|         return err;
 | |
| 
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_get_freq
 | |
|  *
 | |
|  * Return Freq for a given VFO
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int newcat_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) {
 | |
|     struct newcat_priv_data *priv;
 | |
|     struct rig_state *state;
 | |
|     char c;
 | |
|     int err;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: passed vfo = 0x%02x\n", __func__, vfo);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
|   
 | |
|     priv = (struct newcat_priv_data *)rig->state.priv;
 | |
|     state = &rig->state;
 | |
| 
 | |
|     if (vfo == RIG_VFO_CURR) {
 | |
|         err = newcat_get_vfo(rig, &priv->current_vfo);
 | |
|         if (err != RIG_OK)
 | |
|             return err;
 | |
| 
 | |
|         vfo = priv->current_vfo;    /* from previous get_vfo cmd */
 | |
|         rig_debug(RIG_DEBUG_TRACE,
 | |
|                   "%s: priv->current_vfo = 0x%02x\n", __func__, vfo);
 | |
|     }
 | |
| 
 | |
|     switch(vfo) {
 | |
|     case RIG_VFO_A:
 | |
|     case RIG_VFO_VFO:
 | |
|         c = 'A';
 | |
|         break;
 | |
|     case RIG_VFO_B:
 | |
|         c = 'B';
 | |
|         break;
 | |
| //    case RIG_VFO_MEM:
 | |
| //    case RIG_VFO_MAIN:
 | |
| //        break;
 | |
|     default:
 | |
|         return -RIG_EINVAL;         /* sorry, unsupported VFO */
 | |
|     }
 | |
| 
 | |
|     /* Build the command string */
 | |
|     snprintf(priv->cmd_str, sizeof(priv->cmd_str), "F%c%c", c, cat_term);
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "cmd_str = %s\n", priv->cmd_str);
 | |
| 
 | |
|     /* get freq */
 | |
|     err = write_block(&state->rigport, priv->cmd_str, strlen(priv->cmd_str));
 | |
|     if (err != RIG_OK)
 | |
|         return err;
 | |
| 
 | |
|     err = read_string(&state->rigport, priv->ret_data, sizeof(priv->ret_data),
 | |
|                       &cat_term, sizeof(cat_term));
 | |
|     if (err < 0)
 | |
|         return err;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: read count = %d, ret_data = %s\n",
 | |
|               __func__, err, priv->ret_data);
 | |
| 
 | |
|     /* Check that command termination is correct */
 | |
|     if (strchr(&cat_term, priv->ret_data[strlen(priv->ret_data) - 1]) == NULL) {
 | |
| 
 | |
|         rig_debug(RIG_DEBUG_ERR, "%s: Command is not correctly terminated '%s'\n",
 | |
|                   __func__, priv->ret_data);
 | |
| 
 | |
|         return -RIG_EPROTO;
 | |
|     }
 | |
| 
 | |
|     /* convert the read frequency string into freq_t and store in *freq */
 | |
|     sscanf(priv->ret_data+2, "%"SCNfreq, freq);
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE,
 | |
|               "%s: freq = %"PRIfreq" Hz for vfo 0x%02x\n", __func__, freq, vfo);
 | |
| 
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_set_vfo
 | |
|  *
 | |
|  * set vfo and store requested vfo for later RIG_VFO_CURR
 | |
|  * requests.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int newcat_set_vfo(RIG *rig, vfo_t vfo) {
 | |
|     struct newcat_priv_data *priv;
 | |
|     struct rig_state *state;
 | |
|     char c;
 | |
|     int err;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: passed vfo = 0x%02x\n", __func__, vfo);
 | |
|   
 | |
|     priv = (struct newcat_priv_data *)rig->state.priv;
 | |
|     state = &rig->state;
 | |
| 
 | |
|     if (vfo == RIG_VFO_CURR) {
 | |
|         vfo = priv->current_vfo;    /* from previous vfo cmd */
 | |
|         rig_debug(RIG_DEBUG_TRACE,
 | |
|                   "%s: priv->current_vfo = 0x%02x\n", __func__, vfo);
 | |
|     }
 | |
| 
 | |
|     /* FIXME: Include support for RIG_VFO_MAIN, RIG_VFO_MEM */
 | |
|     switch(vfo) {
 | |
|     case RIG_VFO_A:
 | |
|         priv->current_vfo = vfo;    /* update active VFO */
 | |
|         c = '0';
 | |
|         break;
 | |
|     case RIG_VFO_B:
 | |
|         priv->current_vfo = vfo;
 | |
|         c = '1';
 | |
|         break;
 | |
| //    case RIG_VFO_MEM:
 | |
|         /* reset to memory channel stored by previous get_vfo
 | |
|          * The recall mem channel command uses 0x01 though 0x20
 | |
|          */
 | |
| //        err = newcat_send_dynamic_cmd(rig, FT450_NATIVE_RECALL_MEM,
 | |
| //                                     (priv->current_mem + 1), 0, 0, 0);
 | |
| //        if (err != RIG_OK)
 | |
| //            return err;
 | |
| 
 | |
| //        priv->current_vfo = vfo;
 | |
| 
 | |
| //        rig_debug(RIG_DEBUG_TRACE, "%s: set mem channel = 0x%02x\n",
 | |
| //                  __func__, priv->current_mem);
 | |
| //        return RIG_OK;
 | |
|     default:
 | |
|         return -RIG_ENIMPL;         /* sorry, VFO not implemented */
 | |
|     }
 | |
| 
 | |
|     /* Build the command string */
 | |
|     snprintf(priv->cmd_str, sizeof(priv->cmd_str), "VS%c%c", c, cat_term);
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "cmd_str = %s\n", priv->cmd_str);
 | |
| 
 | |
|     err = write_block(&state->rigport, priv->cmd_str, strlen(priv->cmd_str));
 | |
|     if (err != RIG_OK)
 | |
|         return err;
 | |
| 
 | |
|     return RIG_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * rig_get_vfo
 | |
|  *
 | |
|  * get current RX vfo/mem and store requested vfo for
 | |
|  * later RIG_VFO_CURR requests plus pass the tested vfo/mem
 | |
|  * back to the frontend.
 | |
|  *
 | |
|  * TODO: determine memory status if possible
 | |
|  */
 | |
| 
 | |
| int newcat_get_vfo(RIG *rig, vfo_t *vfo) {
 | |
|     struct newcat_priv_data *priv;
 | |
|     struct rig_state *state;
 | |
|     char c;
 | |
|     int err;
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
 | |
| 
 | |
|     if (!rig)
 | |
|         return -RIG_EINVAL;
 | |
|   
 | |
|     priv = (struct newcat_priv_data *)rig->state.priv;
 | |
|     state = &rig->state;
 | |
| 
 | |
|     /* Build the command string */
 | |
|     snprintf(priv->cmd_str, sizeof(priv->cmd_str), "VS;");
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: cmd_str = %s\n", __func__, priv->cmd_str);
 | |
| 
 | |
|     /* Get VFO */
 | |
|     err = write_block(&state->rigport, priv->cmd_str, strlen(priv->cmd_str));
 | |
|     if (err != RIG_OK)
 | |
|         return err;
 | |
| 
 | |
|     err = read_string(&state->rigport, priv->ret_data, sizeof(priv->ret_data),
 | |
|                       &cat_term, sizeof(cat_term));
 | |
|     if (err < 0)
 | |
|         return err;
 | |
| 
 | |
|     /* Check that command termination is correct */
 | |
|     if (strchr(&cat_term, priv->ret_data[strlen(priv->ret_data) - 1]) == NULL) {
 | |
|         rig_debug(RIG_DEBUG_ERR, "%s: Command is not correctly terminated '%s'\n",
 | |
|                   __func__, priv->ret_data);
 | |
| 
 | |
|         return -RIG_EPROTO;
 | |
|     }
 | |
| 
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: read count = %d, ret_data = %s, VFO value = %c\n",
 | |
|               __func__, err, priv->ret_data, priv->ret_data[2]);
 | |
| 
 | |
|     /*
 | |
|      * The current VFO value is a digit ('0' or '1' ('A' or 'B' respectively))
 | |
|      * embedded at ret_data[2] in the read string.
 | |
|      */
 | |
|     c = priv->ret_data[2];
 | |
| 
 | |
|     switch (c) {
 | |
|     case '0':
 | |
|         *vfo = RIG_VFO_A;
 | |
|         priv->current_vfo = RIG_VFO_A;
 | |
|         break;
 | |
|     case '1':
 | |
|         *vfo = RIG_VFO_B;
 | |
|         priv->current_vfo = RIG_VFO_B;
 | |
|         break;
 | |
|     default:
 | |
| //        switch (stat_mem) {
 | |
| //        case SF_MT:
 | |
| //        case SF_MR:
 | |
| //            *vfo = RIG_VFO_MEM;
 | |
| //            priv->current_vfo = RIG_VFO_MEM;
 | |
| 
 | |
|             /*
 | |
|              * Per Hamlib policy capture and store memory channel number
 | |
|              * for future set_vfo command.
 | |
|              */
 | |
| //            err = newcat_get_update_data(rig, FT450_NATIVE_MEM_CHNL,
 | |
| //                                        FT450_MEM_CHNL_LENGTH);
 | |
| //            if (err != RIG_OK)
 | |
| //                return err;
 | |
| 
 | |
| //            priv->current_mem = priv->update_data[FT450_SUMO_MEM_CHANNEL];
 | |
| 
 | |
| //            rig_debug(RIG_DEBUG_TRACE, "%s: stored mem channel = 0x%02x\n",
 | |
| //                      __func__, priv->current_mem);
 | |
| //            break;
 | |
| //        default:                      /* Oops! */
 | |
| //            return -RIG_EINVAL;         /* sorry, wrong current VFO */
 | |
| //        }
 | |
|         return -RIG_EINVAL;         /* sorry, wrong current VFO */
 | |
|     }
 | |
|     rig_debug(RIG_DEBUG_TRACE, "%s: set vfo = 0x%02x\n", __func__, *vfo);
 | |
| 
 | |
|     return RIG_OK;
 | |
| 
 | |
| }
 | |
| 
 |