diff --git a/Makefile b/Makefile index 0ae53948..be2aa07a 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ # equal to some value. Your mileage my vary. win := $(shell uname | grep CYGWIN) +ifeq ($(win),) + win := $(shell uname | grep MINGW) +endif dar := $(shell uname | grep Darwin) ifneq ($(win),) diff --git a/Makefile.linux b/Makefile.linux index be9bd593..5960a297 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -232,7 +232,7 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \ fcs_calc.o ax25_pad.o \ decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ - gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ + gen_tone.o audio.o audio_ptt.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \ dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o \ diff --git a/Makefile.win b/Makefile.win index 3a9d9f6f..524c7137 100644 --- a/Makefile.win +++ b/Makefile.win @@ -70,7 +70,7 @@ direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_9600.o hd hdlc_rec2.o multi_modem.o redecode.o rdq.o rrbb.o dlq.o \ fcs_calc.o ax25_pad.o \ decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ - gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \ + gen_tone.o morse.o audio_win.o audio_ptt_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o nmea.o serial_port.o log.o telemetry.o \ dwgps.o dwgpsnmea.o dtime_now.o \ diff --git a/audio.c b/audio.c index 84aa854a..823aaf99 100644 --- a/audio.c +++ b/audio.c @@ -692,7 +692,6 @@ static int set_oss_params (int a, int fd, struct audio_s *pa) char message[100]; int ossbuf_size_in_bytes; - err = ioctl (fd, SNDCTL_DSP_CHANNELS, &(pa->adev[a].num_channels)); if (err == -1) { text_color_set(DW_COLOR_ERROR); @@ -1249,6 +1248,7 @@ int audio_flush (int a) } if (k < len) { /* presumably full but didn't block. */ + dw_printf("problem\n"); usleep (10000); } ptr += k; diff --git a/audio.h b/audio.h index 674c27ed..6e2e93cf 100644 --- a/audio.h +++ b/audio.h @@ -30,10 +30,17 @@ enum ptt_method_e { PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ - PTT_METHOD_HAMLIB }; /* HAMLib, Linux only. */ + PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ + PTT_METHOD_AUDIO }; /* Audio channel. */ typedef enum ptt_method_e ptt_method_t; +enum ptt_audio_state_e { + PTT_AUDIO_STATE_STOP, + PTT_AUDIO_STATE_START, + PTT_AUDIO_STATE_CLOSE }; +typedef enum ptt_audio_state_e ptt_audio_state_t; + enum ptt_line_e { PTT_LINE_NONE = 0, PTT_LINE_RTS = 1, PTT_LINE_DTR = 2 }; // Important: 0 for neither. typedef enum ptt_line_e ptt_line_t; @@ -195,6 +202,18 @@ struct audio_s { int ptt_invert; /* Invert the output. */ int ptt_invert2; /* Invert the secondary output. */ + int ptt_channel; /* Channel number for audio PTT. */ + int ptt_frequency; /* Audio frequency for audio PTT. */ +#if __WIN32__ + HANDLE ptt_start; /* Handle for event that starts ptt tone. */ + HANDLE ptt_stop; /* Handle for event that stops ptt tone. */ + HANDLE ptt_close; /* Handle for event that closes ptt. */ +#else + pthread_mutex_t ptt_mutex; /* Mutex for controlling ptt tone state. */ + pthread_cond_t ptt_condition; /* Condition for controlling ptt tone state. */ + ptt_audio_state_t ptt_state; /* State of ptt tone. */ +#endif + #ifdef USE_HAMLIB int ptt_model; /* HAMLIB model. -1 for AUTO. 2 for rigctld. Others are radio model. */ diff --git a/audio_ptt.c b/audio_ptt.c new file mode 100644 index 00000000..5188ac9a --- /dev/null +++ b/audio_ptt.c @@ -0,0 +1,254 @@ +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// Copyright (C) 2017 Andrew Walker, VA7YAA +// +// 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 +// 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +/*------------------------------------------------------------------ + * + * Module: audio_ptt.c + * + * Purpose: Interface to audio device commonly called a "sound card" for + * historical reasons. + * + * This version uses the native Windows sound interface. + * + *---------------------------------------------------------------*/ + +#if __WIN32__ +#else +#include <limits.h> +#include <math.h> +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#if USE_ALSA +#include <alsa/asoundlib.h> +#else +#include <errno.h> +#ifdef __OpenBSD__ +#include <soundcard.h> +#else +#include <sys/soundcard.h> +#endif +#endif + +#include "direwolf.h" +#include "audio.h" +#include "audio_stats.h" +#include "textcolor.h" +#include "ptt.h" +#include "audio_ptt.h" + +#if USE_ALSA +static int set_alsa_params (int a, snd_pcm_t *handle, struct audio_s *pa, char *name, char *dir); +//static void alsa_select_device (char *pick_dev, int direction, char *result); +#else +static int set_oss_params (int a, int fd, struct audio_s *pa); +#endif + +static struct audio_s *save_audio_config_p; + +static void * ptt_thread (void *arg); + +int start_ptt_thread (struct audio_s *pa, int ch) +{ + pthread_t tid = 0; + int e; + + save_audio_config_p = pa; + + e = pthread_create (&tid, NULL, ptt_thread, (void*)(long)ch); + + return tid; +} + +static void * ptt_thread (void *arg) +{ + int ch = (int)(long)arg; // channel number. + int channel = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel; + int freq = save_audio_config_p->achan[channel].octrl[OCTYPE_PTT].ptt_frequency; + int a = ACHAN2ADEV( channel ); + + if( save_audio_config_p->adev[a].defined ) { +#if USE_ALSA + snd_pcm_t *handle; + int err; + + err = snd_pcm_open (&handle, save_audio_config_p->adev[a].adevice_out, SND_PCM_STREAM_PLAYBACK, 0); + if (err == 0) { + snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; + + err = snd_pcm_set_params (handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, + save_audio_config_p->adev[a].num_channels, + save_audio_config_p->adev[a].samples_per_sec, 1, 500000); + if (err == 0) { + short* pnData; + short sample; + int nSamples = (int)( ( (double)save_audio_config_p->adev[a].samples_per_sec / (double)freq ) * ceil( (double)freq / 5.0 ) ); + int nBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof(short); + int i; + int j; + + pnData = (short*)malloc (nBufferLength); + + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)save_audio_config_p->adev[a].samples_per_sec ) * 2.0 * M_PI ) ); + + for (j = 0; j < save_audio_config_p->adev[a].num_channels; j++) { + if (channel == ADEVFIRSTCHAN( a ) + j) { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = sample; + } else { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = 0; + } + } + } + + while (1) { + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_audio_state_t ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + + if (ptt_state == PTT_AUDIO_STATE_STOP) { + snd_pcm_drop (handle); + + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + pthread_cond_wait (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_condition, &save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + + if (ptt_state == PTT_AUDIO_STATE_START) { + snd_pcm_prepare (handle); + } + } + + if (ptt_state == PTT_AUDIO_STATE_START) { + snd_pcm_writei (handle, pnData, nSamples); + } + else if (ptt_state == PTT_AUDIO_STATE_CLOSE) { + snd_pcm_drop (handle); + + break; + } + } + + free (pnData); + } else { + dw_printf("Failed to configure ALSA device. PTT tone will not be enabled.\n"); + } + snd_pcm_close (handle); + } else { + dw_printf("Failed to open ALSA device. PTT tone will not be enabled.\n"); + } +#else + int oss_audio_device_fd; + + oss_audio_device_fd = open (save_audio_config_p->adev[a].adevice_out, O_WRONLY); + if (oss_audio_device_fd >= 0) { + int devcaps; + int num_channels; + int samples_per_sec; + int bits_per_sample; + int err; + + num_channels = save_audio_config_p->adev[a].num_channels; + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_CHANNELS, &num_channels); + + if (err != -1) { + samples_per_sec = save_audio_config_p->adev[a].samples_per_sec; + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_SPEED, &samples_per_sec); + } + + if (err != -1) { + bits_per_sample = save_audio_config_p->adev[a].bits_per_sample; + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_SETFMT, &bits_per_sample); + } + + if (err != -1) { + err = ioctl (oss_audio_device_fd, SNDCTL_DSP_GETCAPS, &devcaps); + } + + if (err != -1) { + short* pnData; + short sample; + int nBufferLength; + int nSamples; + int written; + int i; + int j; + + nSamples = (int)( ( (double)samples_per_sec / (double)freq ) * ceil( (double)freq / 5.0 ) ); + nBufferLength = num_channels * nSamples * sizeof(short); + pnData = (short*)malloc (nBufferLength); + + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i * freq / (double)samples_per_sec ) * 2.0 * M_PI ) ); + + for (j = 0; j < num_channels; j++) { + if (channel == ADEVFIRSTCHAN( a ) + j) { + pnData[i*num_channels + j] = sample; + } else { + pnData[i*num_channels + j] = 0; + } + } + } + + while (1) { + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_audio_state_t ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + + if (ptt_state == PTT_AUDIO_STATE_STOP) { + ioctl (oss_audio_device_fd, SNDCTL_DSP_RESET, NULL); + + pthread_mutex_lock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + pthread_cond_wait (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_condition, &save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + ptt_state = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_state; + pthread_mutex_unlock (&save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_mutex); + } + + if (ptt_state == PTT_AUDIO_STATE_START) { + written = write (oss_audio_device_fd, pnData, nBufferLength); + } + else if (ptt_state == PTT_AUDIO_STATE_CLOSE) { + ioctl (oss_audio_device_fd, SNDCTL_DSP_RESET, NULL); + + break; + } + } + + free (pnData); + } else { + dw_printf("Failed to configure OSS device. PTT tone will not be enabled.\n"); + } + close (oss_audio_device_fd); + } else { + dw_printf("Failed to open OSS device. PTT tone will not be enabled.\n"); + } +#endif + } +} + +#endif + diff --git a/audio_ptt.h b/audio_ptt.h new file mode 100644 index 00000000..d880b5fc --- /dev/null +++ b/audio_ptt.h @@ -0,0 +1,11 @@ + +#ifndef AUDIO_PTT_H +#define AUDIO_PTT_H 1 + +#if __WIN32__ +extern HANDLE start_ptt_thread ( struct audio_s *pa, int ch ); +#else +extern int start_ptt_thread ( struct audio_s *pa, int ch ); +#endif + +#endif \ No newline at end of file diff --git a/audio_ptt_win.c b/audio_ptt_win.c new file mode 100644 index 00000000..7ccdd378 --- /dev/null +++ b/audio_ptt_win.c @@ -0,0 +1,173 @@ + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 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 +// 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +/*------------------------------------------------------------------ + * + * Module: audio_ptt_win.c + * + * Purpose: Interface to audio device commonly called a "sound card" for + * historical reasons. + * + * This version uses the native Windows sound interface. + * + *---------------------------------------------------------------*/ + +#if __WIN32__ +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> +#include <io.h> +#include <fcntl.h> +#include <math.h> +#include <limits.h> + +#include <windows.h> +#include <mmsystem.h> + +#ifndef WAVE_FORMAT_96M16 +#define WAVE_FORMAT_96M16 0x40000 +#define WAVE_FORMAT_96S16 0x80000 +#endif + +#include "direwolf.h" +#include "audio.h" +#include "audio_stats.h" +#include "textcolor.h" +#include "ptt.h" +#include "demod.h" /* for alevel_t & demod_get_audio_level() */ +#include "audio_ptt.h" + +static struct audio_s *save_audio_config_p; + +static unsigned __stdcall ptt_thread ( void *arg ); + +HANDLE start_ptt_thread (struct audio_s *pa , int ch) +{ + save_audio_config_p = pa; + + return (HANDLE)_beginthreadex (NULL, 0, ptt_thread, (void*)(long)ch, 0, NULL); +} + +unsigned __stdcall ptt_thread (void *arg) +{ + WAVEFORMATEX wf; + HWAVEOUT hWaveOut; + int ch = (int)(long)arg; // channel number. + int channel = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel; + int freq = save_audio_config_p->achan[channel].octrl[OCTYPE_PTT].ptt_frequency; + int a = ACHAN2ADEV( channel ); + int err; + + if (save_audio_config_p->adev[a].defined) { + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = save_audio_config_p->adev[a].num_channels; + wf.nSamplesPerSec = save_audio_config_p->adev[a].samples_per_sec; + wf.wBitsPerSample = save_audio_config_p->adev[a].bits_per_sample; + wf.nBlockAlign = ( wf.wBitsPerSample / 8 ) * wf.nChannels; + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.cbSize = 0; + + /* + * Open the audio output device. + * Soundcard is only possibility at this time. + */ + + err = waveOutOpen (&hWaveOut, atoi( save_audio_config_p->adev[a].adevice_out ), &wf, (DWORD_PTR)NULL, 0, CALLBACK_NULL); + if (err == MMSYSERR_NOERROR) { + WAVEHDR waveHeader; + short* pnData; + short sample; + int nSamples = save_audio_config_p->adev[a].samples_per_sec / freq; + int i; + int j; + + waveHeader.dwBufferLength = save_audio_config_p->adev[a].num_channels * nSamples * sizeof( short ); + waveHeader.lpData = malloc (waveHeader.dwBufferLength); + waveHeader.dwUser = 0; + waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; + waveHeader.dwLoops = 0xFFFF; + + pnData = (short*)waveHeader.lpData; + + for (i = 0; i < nSamples; i++) { + sample = (short)( (double)SHRT_MAX * sin( ( (double)i / (double)nSamples ) * 2.0 * M_PI ) ); + + for (j = 0; j < save_audio_config_p->adev[a].num_channels; j++) { + if (channel == ADEVFIRSTCHAN( a ) + j) { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = sample; + } else { + pnData[i*save_audio_config_p->adev[a].num_channels + j] = 0; + } + } + } + + err = waveOutPrepareHeader (hWaveOut, &waveHeader, sizeof( WAVEHDR )); + if (err == MMSYSERR_NOERROR) { + HANDLE handles[3]; + DWORD dwWait; + + handles[0] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_start; + handles[1] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_stop; + handles[2] = save_audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_close; + + while (1) + { + dwWait = WaitForMultipleObjects (3, handles, FALSE, INFINITE); + + if (dwWait == WAIT_OBJECT_0 + 0) { + // + // ptt_set on + // + + waveOutWrite (hWaveOut, &waveHeader, sizeof( WAVEHDR )); + } + else if (dwWait == WAIT_OBJECT_0 + 1) { + // + // ptt_set off + // + + waveOutReset (hWaveOut); + } + else if( dwWait == WAIT_OBJECT_0 + 2 ) { + // + // close + // + + waveOutReset (hWaveOut); + waveOutUnprepareHeader (hWaveOut, &waveHeader, sizeof( WAVEHDR )); + + break; + } + } + } + + waveOutClose (hWaveOut); + + free (waveHeader.lpData); + } + } + + return 0; +} +#endif diff --git a/config.c b/config.c index 8090c859..166c4380 100644 --- a/config.c +++ b/config.c @@ -666,7 +666,16 @@ void config_init (char *fname, struct audio_s *p_audio_config, p_audio_config->achan[channel].octrl[ot].ptt_lpt_bit = 0; p_audio_config->achan[channel].octrl[ot].ptt_invert = 0; p_audio_config->achan[channel].octrl[ot].ptt_invert2 = 0; - } + p_audio_config->achan[channel].octrl[ot].ptt_channel = 0; + p_audio_config->achan[channel].octrl[ot].ptt_frequency = PTT_FREQ_DEFAULT; +#ifdef __WIN32__ + p_audio_config->achan[channel].octrl[ot].ptt_start = NULL; + p_audio_config->achan[channel].octrl[ot].ptt_stop = NULL; + p_audio_config->achan[channel].octrl[ot].ptt_close = NULL; +#else + p_audio_config->achan[channel].octrl[ot].ptt_state = PTT_AUDIO_STATE_STOP; +#endif + } p_audio_config->achan[channel].dwait = DEFAULT_DWAIT; p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; @@ -1582,7 +1591,57 @@ void config_init (char *fname, struct audio_s *p_audio_config, dw_printf ("Config file line %d: %s with RIG is only available when hamlib support is enabled.\n", line, otname); #endif } - else { + else if (strcasecmp(t, "CHANNEL") == 0) { + t = split(NULL, 0); + if (t == NULL) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ("Config file line %d: Missing channel number for %s.\n", line, otname); + continue; + } + + int channel_ptt = atoi(t); + + if (channel_ptt < 0 || channel_ptt >= MAX_CHANS) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ( "Config file line %d: Invalid PTT channel number for %s.\n", line, otname ); + continue; + } + + if (channel == channel_ptt) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ( "Config file line %d: PTT channel number must not be the same as the channel number itself.\n", line ); + continue; + } + + int freq_ptt = PTT_FREQ_DEFAULT; + + t = split(NULL, 0); + if (t != NULL) { + freq_ptt = atoi(t); + + if (freq_ptt < PTT_FREQ_MIN || freq_ptt > PTT_FREQ_MAX) { + text_color_set( DW_COLOR_ERROR ); + dw_printf ("Config file line %d: Invalid value %d for PTT frequency. Using default of %d.\n", + line, freq_ptt, PTT_FREQ_DEFAULT ); + + freq_ptt = PTT_FREQ_DEFAULT; + } + } + + p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_AUDIO; + p_audio_config->achan[channel].octrl[ot].ptt_channel = channel_ptt; + p_audio_config->achan[channel].octrl[ot].ptt_frequency = freq_ptt; +#ifdef __WIN32__ + p_audio_config->achan[channel].octrl[ot].ptt_start = CreateEvent (NULL, FALSE, FALSE, NULL); + p_audio_config->achan[channel].octrl[ot].ptt_stop = CreateEvent (NULL, FALSE, FALSE, NULL); + p_audio_config->achan[channel].octrl[ot].ptt_close = CreateEvent (NULL, FALSE, FALSE, NULL); +#else + p_audio_config->achan[channel].octrl[ot].ptt_state = PTT_AUDIO_STATE_STOP; + pthread_mutex_init(&p_audio_config->achan[channel].octrl[ot].ptt_mutex, 0); + pthread_cond_init(&p_audio_config->achan[channel].octrl[ot].ptt_condition, 0); +#endif + } + else { /* serial port case. */ diff --git a/direwolf.h b/direwolf.h index 958e0932..347f4eb2 100644 --- a/direwolf.h +++ b/direwolf.h @@ -38,6 +38,14 @@ #define MAX_RIGS MAX_CHANS #endif +/* + * PTT frequency settings + */ + +#define PTT_FREQ_MIN 50 +#define PTT_FREQ_MAX 20000 +#define PTT_FREQ_DEFAULT 1000 + /* * Get audio device number for given channel. * and first channel for given device. diff --git a/direwolf.txt b/direwolf.txt index c8577395..e45d9c17 100644 --- a/direwolf.txt +++ b/direwolf.txt @@ -234,10 +234,16 @@ C# Both can be used for interfaces that want them driven with opposite polarity. C# L# COM1 can be used instead of /dev/ttyS0, COM2 for /dev/ttyS1, and so on. L# +C# With a suitable interface circuit an audio channel can also be used to +C# trigger the PTT control. The device should be specified as CHANNEL, +C# followed by the channel number and (optionally) the desired frequency, +C# in Hz, of the audio tone to be used. The frequency must be between +C# 50 and 20000 Hz and defaults to 1000 Hz. C C#PTT COM1 RTS C#PTT COM1 RTS -DTR L#PTT /dev/ttyUSB0 RTS +C#PTT CHANNEL 1 1000 C L# L# On Linux, you can also use general purpose I/O pins if diff --git a/ptt.c b/ptt.c index 749ada61..5ccd71bc 100644 --- a/ptt.c +++ b/ptt.c @@ -126,6 +126,7 @@ typedef int HANDLE; #include "textcolor.h" #include "audio.h" #include "ptt.h" +#include "audio_ptt.h" #if __WIN32__ @@ -357,6 +358,13 @@ void ptt_init (struct audio_s *audio_config_p) { int ch; HANDLE fd = INVALID_HANDLE_VALUE; + +#if __WIN32__ + HANDLE audio_ptt_th[MAX_CHANS]; +#else + pthread_t audio_ptt_tid[MAX_CHANS]; +#endif + #if __WIN32__ #else int using_gpio; @@ -386,7 +394,7 @@ void ptt_init (struct audio_s *audio_config_p) if (ptt_debug_level >= 2) { text_color_set(DW_COLOR_DEBUG); - dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n", + dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d, channel=%d, freq=%d\n", ch, otnames[ot], audio_config_p->achan[ch].octrl[ot].ptt_method, @@ -394,7 +402,9 @@ void ptt_init (struct audio_s *audio_config_p) audio_config_p->achan[ch].octrl[ot].ptt_line, audio_config_p->achan[ch].octrl[ot].ptt_gpio, audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit, - audio_config_p->achan[ch].octrl[ot].ptt_invert); + audio_config_p->achan[ch].octrl[ot].ptt_invert, + audio_config_p->achan[ch].octrl[ot].ptt_channel, + audio_config_p->achan[ch].octrl[ot].ptt_frequency ); } } } @@ -501,6 +511,7 @@ void ptt_init (struct audio_s *audio_config_p) ptt_set (ot, ch, 0); } /* if serial method. */ + } /* for each output type. */ } /* if channel valid. */ } /* For each channel. */ @@ -747,6 +758,33 @@ void ptt_init (struct audio_s *audio_config_p) #endif + /* + * Set up audio channel. + */ + + for (ch = 0; ch<MAX_CHANS; ch++) { + if (audio_config_p->achan[ch].valid) { + if (audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_AUDIO) { +#if __WIN32__ + audio_ptt_th[ch] = start_ptt_thread (audio_config_p, ch); + if (audio_ptt_th[ch] == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not create audio_ptt thread on channel %d for PTT of channel %d.\n", + audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel, ch); + return; + } +#else + audio_ptt_tid[ch] = start_ptt_thread (audio_config_p, ch); + if (audio_ptt_tid[ch] == 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Could not create audio_ptt thread on channel %d for PTT of channel %d.\n", + audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_channel, ch ); + return; + } +#endif + } + } + } /* Why doesn't it transmit? Probably forgot to specify PTT option. */ @@ -968,7 +1006,32 @@ void ptt_set (int ot, int chan, int ptt_signal) } #endif + /* + * Using audio channel? + */ + if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_AUDIO) { + if (ptt_signal) { +#ifdef __WIN32__ + SetEvent (save_audio_config_p->achan[chan].octrl[ot].ptt_start); +#else + pthread_mutex_lock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); + save_audio_config_p->achan[chan].octrl[ot].ptt_state = PTT_AUDIO_STATE_START; + pthread_cond_signal (&save_audio_config_p->achan[chan].octrl[ot].ptt_condition); + pthread_mutex_unlock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); +#endif + } + else + { +#ifdef __WIN32__ + SetEvent (save_audio_config_p->achan[chan].octrl[ot].ptt_stop); +#else + pthread_mutex_lock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); + save_audio_config_p->achan[chan].octrl[ot].ptt_state = PTT_AUDIO_STATE_STOP; + pthread_mutex_unlock (&save_audio_config_p->achan[chan].octrl[ot].ptt_mutex); +#endif + } + } } /* end ptt_set */ /*------------------------------------------------------------------- @@ -1056,7 +1119,7 @@ void ptt_term (void) ptt_set (ot, n, 0); } } - } + } for (n = 0; n < MAX_CHANS; n++) { if (save_audio_config_p->achan[n].valid) { @@ -1074,6 +1137,21 @@ void ptt_term (void) } } + for (n = 0; n < MAX_CHANS; n++) { + if (save_audio_config_p->achan[n].valid) { + if (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_AUDIO) { +#ifdef __WIN32__ + SetEvent (save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_close) ; +#else + pthread_mutex_lock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); + save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_state = PTT_AUDIO_STATE_CLOSE; + pthread_cond_signal (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_condition); + pthread_mutex_unlock (&save_audio_config_p->achan[n].octrl[OCTYPE_PTT].ptt_mutex); +#endif + } + } + } + #ifdef USE_HAMLIB for (n = 0; n < MAX_CHANS; n++) {