Rewrite, test, and document hamlib interface for PTT.

modified:   audio.h
	modified:   config.c
	modified:   direwolf.c
	modified:   man1/direwolf.1
	modified:   ptt.c
This commit is contained in:
WB2OSZ 2016-02-27 15:42:26 -05:00
parent c6bff39a30
commit 7125b5b22f
5 changed files with 169 additions and 149 deletions

View File

@ -182,6 +182,7 @@ struct audio_s {
ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB. */
char ptt_device[20]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */
/* Also used for HAMLIB. Could be host:port when model is 1. */
ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */
ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */
@ -195,7 +196,8 @@ struct audio_s {
int ptt_invert2; /* Invert the secondary output. */
#ifdef USE_HAMLIB
int ptt_rig; /* HAMLib rig. */
int ptt_model; /* HAMLIB model. -1 for AUTO. 2 for rigctld. Others are radio model. */
#endif
} octrl[NUM_OCTYPES];

121
config.c
View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -46,9 +46,6 @@
#include <gps.h> /* for DEFAULT_GPSD_PORT (2947) */
#endif
#ifdef USE_HAMLIB
#include <hamlib/rig.h>
#endif
#include "ax25_pad.h"
#include "textcolor.h"
@ -612,12 +609,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
int adevice;
int m;
#if USE_HAMLIB
RIG *rig;
int rigs = 0;
#endif
#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("config_init ( %s )\n", fname);
@ -1460,6 +1451,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
* xxx serial-port [-]rts-or-dtr [ [-]rts-or-dtr ]
* xxx GPIO [-]gpio-num
* xxx LPT [-]bit-num
* PTT RIG model port
* PTT RIG AUTO port
*
* When model is 2, port would host:port like 127.0.0.1:4532
* Otherwise, port would be a serial port like /dev/ttyS0
*
*
* Applies to most recent CHANNEL command.
*/
@ -1544,16 +1541,41 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
else if (strcasecmp(t, "RIG") == 0) {
#ifdef USE_HAMLIB
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB;
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing RIG number.\n", line);
dw_printf ("Config file line %d: Missing model number for hamlib.\n", line);
continue;
}
if (strcasecmp(t, "AUTO") == 0) {
p_audio_config->achan[channel].octrl[ot].ptt_model = -1;
}
else {
int n = atoi(t);
if (n < 1 || n > 9999) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Unreasonable model number %d for hamlib.\n", line, n);
continue;
}
p_audio_config->achan[channel].octrl[ot].ptt_model = n;
}
p_audio_config->achan[channel].octrl[ot].ptt_rig = atoi(t);
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing port for hamlib.\n", line);
continue;
}
strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device));
t = split(NULL,0);
if (t != NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: %s was not expected after model & port for hamlib.\n", line, t);
}
p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB;
#else
text_color_set(DW_COLOR_ERROR);
@ -1691,79 +1713,6 @@ void config_init (char *fname, struct audio_s *p_audio_config,
}
}
/*
* RIG - HAMLib rig configuration.
*
* xxx port model
*
* For example a Yeasu FT-817 on /dev/ttyUSB0:
* RIG /dev/ttyUSB0 120
*
* For example rigctld on localhost:
* RIG 127.0.0.1:4532 2
*/
else if (strcasecmp(t, "RIG") == 0) {
#ifdef USE_HAMLIB
int n;
hamlib_port_t port;
rig_model_t rig_model;
if (rigs == MAX_RIGS) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Maximum number of rigs reached.\n", line);
continue;
}
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing port, model[, key=value].\n",
line);
continue;
}
strncpy (port.pathname, t, FILPATHLEN - 1);
t = split(NULL,0);
if (t == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Missing model[, key=value]\n", line);
continue;
}
if (strcasecmp(t, "AUTO") == 0) {
rig_load_all_backends();
rig_model = rig_probe(&port);
}
else {
rig_model = atoi(t);
}
rig = rig_init(rig_model);
if (!rig) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Unknown rig %d, please check riglist.h.\n", line, rig_model);
continue;
}
strncpy (rig->state.rigport.pathname, port.pathname, FILPATHLEN - 1);
n = rig_open(rig);
if (n != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: Rig open error %d: %s\n", line, n, rigerror(n));
continue;
}
p_audio_config->rig[rigs++] = rig;
p_audio_config->rigs = rigs;
#else
text_color_set(DW_COLOR_ERROR);
dw_printf ("Config file line %d: RIG is only available when hamlib support is enabled.\n", line);
continue;
#endif
}
/*
* DWAIT - Extra delay for receiver squelch.

View File

@ -67,6 +67,10 @@
#include <netdb.h>
#endif
#if USE_HAMLIB
#include <hamlib/rig.h>
#endif
#define DIREWOLF_C 1
@ -185,7 +189,9 @@ int main (int argc, char *argv[])
int d_g_opt = 0; /* "-d g" option for GPS. Can be repeated for more detail. */
int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */
int d_i_opt = 0; /* "-d i" option for IGate. Repeat for more detail */
#if USE_HAMLIB
int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */
#endif
strlcpy(l_opt, "", sizeof(l_opt));
strlcpy(P_opt, "", sizeof(P_opt));
@ -230,8 +236,8 @@ int main (int argc, char *argv[])
text_color_init(t_opt);
text_color_set(DW_COLOR_INFO);
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "K", __DATE__);
dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "K", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
@ -453,6 +459,9 @@ int main (int argc, char *argv[])
case 'i': d_i_opt++; break;
#if AX25MEMDEBUG
case 'm': ax25memdebug_set(); break; // Track down memory leak. Not documented.
#endif
#if USE_HAMLIB
case 'h': d_h_opt++; break; // Hamlib verbose level.
#endif
default: break;
}
@ -527,6 +536,10 @@ int main (int argc, char *argv[])
* Possibly override some by command line options.
*/
#if USE_HAMLIB
rig_set_debug(d_h_opt);
#endif
symbols_init ();
config_init (config_file, &audio_config, &digi_config, &tt_config, &igate_config, &misc_config);
@ -1067,6 +1080,9 @@ static void usage (char **argv)
dw_printf (" t t = Tracker beacon.\n");
dw_printf (" o o = output controls such as PTT and DCD.\n");
dw_printf (" i i = IGate.\n");
#if USE_HAMLIB
dw_printf (" h h = hamlib increase verbose level.\n");
#endif
dw_printf (" -q Quiet (suppress output) options:\n");
dw_printf (" h h = Heard line with the audio level.\n");
dw_printf (" d d = Decoding of APRS packets.\n");

View File

@ -84,6 +84,8 @@ t = Tracker beacon.
o = Output controls such as PTT and DCD.
.P
i = IGate
.P
h = Hamlib verbose level. Repeat for more.
.RE
.RE
.PD

169
ptt.c
View File

@ -1,7 +1,7 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2013, 2014, 2015 John Langner, WB2OSZ
// Copyright (C) 2011, 2013, 2014, 2015, 2016 John Langner, WB2OSZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -44,6 +44,8 @@
* Version 1.2: More than two radio channels.
* Generalize for additional signals besides PTT.
*
* Version 1.3: HAMLIB support.
*
* References: http://www.robbayer.com/files/serial-win.pdf
*
* https://www.kernel.org/doc/Documentation/gpio.txt
@ -53,7 +55,7 @@
/*
Idea for future enhancement:
A couple people have asked about support for the DMK URI.
A growing number of people have been asking about support for the DMK URI.
This uses a C-Media CM108/CM119 with one interesting addition, a GPIO
pin is used to drive PTT. Here is some related information.
@ -84,6 +86,12 @@
https://github.com/signal11/hidapi/blob/master/libusb/hid.c
http://stackoverflow.com/questions/899008/howto-write-to-the-gpio-pin-of-the-cm108-chip-in-linux
https://www.kernel.org/doc/Documentation/hid/hidraw.txt
In version 1.3, we add HAMLIB support which should be able to do this.
(Linux only & haven't verified that it actually works yet!)
Might want to have CM108 GPIO support built in, someday, for simpler building & configuration.
Maybe even for Windows. ;-)
*/
#include <stdio.h>
@ -104,6 +112,10 @@
#include <unistd.h>
#include <errno.h>
#ifdef USE_HAMLIB
#include <hamlib/rig.h>
#endif
/* So we can have more common code for fd. */
typedef int HANDLE;
#define INVALID_HANDLE_VALUE (-1)
@ -221,6 +233,7 @@ void export_gpio(int gpio, int invert, int direction)
err = system (stemp);
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/value", gpio);
err = system (stemp);
(void)err;
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", gpio);
@ -297,10 +310,11 @@ void export_gpio(int gpio, int invert, int direction)
* PTT_METHOD_SERIAL - serial (com) port.
* PTT_METHOD_GPIO - general purpose I/O.
* PTT_METHOD_LPT - Parallel printer port.
* PTT_METHOD_HAMLIB - HAMLib rig control.
* PTT_METHOD_HAMLIB - HAMLib rig control.
*
* ptt_device Name of serial port device.
* e.g. COM1 or /dev/ttyS0.
* HAMLIB can also use hostaddr:port.
*
* ptt_line RTS or DTR when using serial port.
*
@ -314,6 +328,11 @@ void export_gpio(int gpio, int invert, int direction)
* ptt_invert Invert the signal.
* Normally higher voltage means transmit or LED on.
*
* ptt_model Only for HAMLIB.
* 2 to communicate with rigctld.
* >= 3 for specific radio model.
* -1 guess at what is out there. (AUTO option in config file.)
*
* Outputs: Remember required information for future use.
*
* Description:
@ -328,7 +347,9 @@ static HANDLE ptt_fd[MAX_CHANS][NUM_OCTYPES];
/* Serial port handle or fd. */
/* Could be the same for two channels */
/* if using both RTS and DTR. */
#if USE_HAMLIB
static RIG *rig[MAX_CHANS][NUM_OCTYPES];
#endif
static char otnames[NUM_OCTYPES][8];
@ -359,7 +380,9 @@ void ptt_init (struct audio_s *audio_config_p)
for (ot = 0; ot < NUM_OCTYPES; ot++) {
ptt_fd[ch][ot] = INVALID_HANDLE_VALUE;
#if USE_HAMLIB
rig[ch][ot] = NULL;
#endif
if (ptt_debug_level >= 2) {
text_color_set(DW_COLOR_DEBUG);
@ -464,7 +487,7 @@ void ptt_init (struct audio_s *audio_config_p)
audio_config_p->achan[ch].octrl[ot].ptt_device, ch);
#if __WIN32__
#else
dw_printf ("%s\n", strerror(errno));
dw_printf ("%s\n", strerror(e));
#endif
/* Don't try using it later if device open failed. */
@ -491,7 +514,7 @@ void ptt_init (struct audio_s *audio_config_p)
#else
/*
* Does any of them use GPIO or HAMLIB?
* Does any of them use GPIO?
*/
using_gpio = 0;
@ -637,7 +660,7 @@ void ptt_init (struct audio_s *audio_config_p)
text_color_set(DW_COLOR_ERROR);
dw_printf ("ERROR - Can't open /dev/port for parallel printer port PTT control.\n");
dw_printf ("%s\n", strerror(errno));
dw_printf ("%s\n", strerror(e));
dw_printf ("You probably don't have adequate permissions to access I/O ports.\n");
dw_printf ("Either run direwolf as root or change these permissions:\n");
dw_printf (" sudo chmod go+rw /dev/port\n");
@ -667,31 +690,61 @@ void ptt_init (struct audio_s *audio_config_p)
#ifdef USE_HAMLIB
for (ch = 0; ch < MAX_CHANS; ch++) {
if (save_audio_config_p->achan[ch].valid) {
int ot, retcode;
RIG *rig;
freq_t freq;
int ot;
for (ot = 0; ot < NUM_OCTYPES; ot++) {
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) {
if (audio_config_p->achan[ch].octrl[ot].ptt_rig - 1 >= audio_config_p->rigs) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error: RIG %d not available.\n", audio_config_p->achan[ch].octrl[ot].ptt_rig);
audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
}
if (ot == OCTYPE_PTT) {
/* For "AUTO" model, try to guess what is out there. */
if (audio_config_p->achan[ch].octrl[ot].ptt_model == -1) {
hamlib_port_t hport;
memset (&hport, 0, sizeof(hport));
strlcpy (hport.pathname, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(hport.pathname));
rig_load_all_backends();
audio_config_p->achan[ch].octrl[ot].ptt_model = rig_probe(&hport);
if (audio_config_p->achan[ch].octrl[ot].ptt_model == RIG_MODEL_NONE) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't guess rig model number for AUTO option. Run \"rigctl --list\" for a list of model numbers.\n");
continue;
}
text_color_set(DW_COLOR_INFO);
dw_printf ("Hamlib AUTO option detected rig model %d. Run \"rigctl --list\" for a list of model numbers.\n",
audio_config_p->achan[ch].octrl[ot].ptt_model);
}
rig[ch][ot] = rig_init(audio_config_p->achan[ch].octrl[ot].ptt_model);
if (rig[ch][ot] == NULL) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Unknown rig model %d for hamlib. Run \"rigctl --list\" for a list of model numbers.\n",
audio_config_p->achan[ch].octrl[ot].ptt_model);
continue;
}
strlcpy (rig[ch][ot]->state.rigport.pathname, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(rig[ch][ot]->state.rigport.pathname));
int err = rig_open(rig[ch][ot]);
if (err != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Hamlib Rig open error %d: %s\n", err, rigerror(err));
rig_cleanup (rig[ch][ot]);
rig[ch][ot] = NULL;
continue;
}
/* Successful. Later code should check for rig[ch][ot] not NULL. */
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("HAMLIB can only be used for PTT. Not DCD or other output.\n");
}
}
}
}
}
rig = audio_config_p->rig[audio_config_p->achan[ch].octrl[ot].ptt_rig];
retcode = rig_get_freq(rig, RIG_VFO_CURR, &freq);
if (retcode == RIG_OK) {
text_color_set(DW_COLOR_INFO);
dw_printf ("RIG tuned on %"PRIfreq"\n", freq);
} else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("RIG rig_get_freq error %s, PTT probably will not work\n", rigerror(retcode));
}
}
}
}
}
#endif
@ -863,7 +916,7 @@ void ptt_set (int ot, int chan, int ptt_signal)
ptt_fd[chan][ot] != INVALID_HANDLE_VALUE) {
char lpt_data;
ssize_t n;
//ssize_t n;
lseek (ptt_fd[chan][ot], (off_t)LPT_IO_ADDR, SEEK_SET);
if (read (ptt_fd[chan][ot], &lpt_data, (size_t)1) != 1) {
@ -897,16 +950,22 @@ void ptt_set (int ot, int chan, int ptt_signal)
*/
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) {
int retcode;
RIG *rig = save_audio_config_p->rig[save_audio_config_p->achan[chan].octrl[ot].ptt_rig];
if ((retcode = rig_set_ptt(rig, RIG_VFO_CURR, ptt ? RIG_PTT_ON : RIG_PTT_OFF)) != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error sending rig_set_ptt command for channel %d %s\n", chan, otnames[ot]);
dw_printf ("%s\n", rigerror(retcode));
}
}
if (rig[chan][ot] != NULL) {
int retcode = rig_set_ptt(rig[chan][ot], RIG_VFO_CURR, ptt ? RIG_PTT_ON : RIG_PTT_OFF);
if (retcode != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error sending rig_set_ptt command for channel %d %s\n", chan, otnames[ot]);
dw_printf ("%s\n", rigerror(retcode));
}
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't use rig_set_ptt for channel %d %s because rig_open failed.\n", chan, otnames[ot]);
}
}
#endif
@ -1016,28 +1075,20 @@ void ptt_term (void)
}
#ifdef USE_HAMLIB
for (n = 0; n < save_audio_config_p->rigs; n++) {
RIG *rig = save_audio_config_p->rig[n];
int retcode;
if ((retcode = rig_set_ptt(rig, RIG_VFO_CURR, RIG_PTT_OFF)) != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error sending rig_set_ptt command for rig %d\n", n);
dw_printf ("%s\n", rigerror(retcode));
}
for (n = 0; n < MAX_CHANS; n++) {
if (save_audio_config_p->achan[n].valid) {
int ot;
for (ot = 0; ot < NUM_OCTYPES; ot++) {
if (rig[n][ot] != NULL) {
if ((retcode = rig_close(rig)) != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error sending rig_close command for rig %d\n", n);
dw_printf ("%s\n", rigerror(retcode));
}
if ((retcode = rig_cleanup(rig)) != RIG_OK) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error sending rig_cleanup command for rig %d\n", n);
dw_printf ("%s\n", rigerror(retcode));
}
}
rig_close(rig[n][ot]);
rig_cleanup(rig[n][ot]);
rig[n][ot] = NULL;
}
}
}
}
#endif
}