2015-07-27 01:17:23 +00:00
2015-07-27 00:35:07 +00:00
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
2017-10-19 00:50:59 +00:00
// Copyright (C) 2011, 2013, 2014, 2015, 2016, 2017 John Langner, WB2OSZ
2015-07-27 00:35:07 +00:00
//
// 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 : xmit . c
*
* Purpose : Transmit queued up packets when channel is clear .
*
* Description : Producers of packets to be transmitted call tq_append and then
* go merrily on their way , unconcerned about when the packet might
* actually get transmitted .
*
* This thread waits until the channel is clear and then removes
* packets from the queue and transmits them .
*
*
* Usage : ( 1 ) The main application calls xmit_init .
*
* This will initialize the transmit packet queue
* and create a thread to empty the queue when
* the channel is clear .
*
* ( 2 ) The application queues up packets by calling tq_append .
*
* Packets that are being digipeated should go in the
* high priority queue so they will go out first .
*
* Other packets should go into the lower priority queue .
*
* ( 3 ) xmit_thread removes packets from the queue and transmits
* them when other signals are not being heard .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2016-07-03 22:09:34 +00:00
# include "direwolf.h"
2015-07-27 00:35:07 +00:00
# include <stdio.h>
# include <unistd.h>
# include <stdlib.h>
# include <assert.h>
# include <string.h>
# include <math.h>
2015-07-27 01:17:23 +00:00
# include <errno.h>
2019-12-05 03:38:12 +00:00
# include <stddef.h>
2015-07-27 00:35:07 +00:00
# include "direwolf.h"
# include "ax25_pad.h"
# include "textcolor.h"
# include "audio.h"
# include "tq.h"
# include "xmit.h"
# include "hdlc_send.h"
# include "hdlc_rec.h"
# include "ptt.h"
2015-07-27 01:17:23 +00:00
# include "dtime_now.h"
2015-09-07 23:56:20 +00:00
# include "morse.h"
2017-01-01 16:49:55 +00:00
# include "dtmf.h"
2016-11-20 19:58:51 +00:00
# include "xid.h"
2018-08-05 15:20:27 +00:00
# include "dlq.h"
2020-04-14 01:07:10 +00:00
# include "server.h"
2015-07-27 00:35:07 +00:00
/*
* Parameters for transmission .
* Each channel can have different timing values .
*
* These are initialized once at application startup time
* and some can be changed later by commands from connected applications .
*/
2021-09-19 18:51:18 +00:00
static int xmit_slottime [ MAX_CHANS ] ; /* Slot time in 10 mS units for persistence algorithm. */
2015-07-27 00:35:07 +00:00
static int xmit_persist [ MAX_CHANS ] ; /* Sets probability for transmitting after each */
/* slot time delay. Transmit if a random number */
/* in range of 0 - 255 <= persist value. */
/* Otherwise wait another slot time and try again. */
static int xmit_txdelay [ MAX_CHANS ] ; /* After turning on the transmitter, */
/* send "flags" for txdelay * 10 mS. */
2015-09-07 23:56:20 +00:00
static int xmit_txtail [ MAX_CHANS ] ; /* Amount of time to keep transmitting after we */
2015-07-27 00:35:07 +00:00
/* are done sending the data. This is to avoid */
/* dropping PTT too soon and chopping off the end */
/* of the frame. Again 10 mS units. */
2017-10-19 00:50:59 +00:00
static int xmit_fulldup [ MAX_CHANS ] ; /* Full duplex if non-zero. */
2015-07-27 00:35:07 +00:00
static int xmit_bits_per_sec [ MAX_CHANS ] ; /* Data transmission rate. */
2018-08-05 15:20:27 +00:00
/* Often called baud rate which is equivalent for */
/* 1200 & 9600 cases but could be different with other */
2015-07-27 00:35:07 +00:00
/* modulation techniques. */
2015-07-27 01:05:48 +00:00
static int g_debug_xmit_packet ; /* print packet in hexadecimal form for debugging. */
2016-11-20 19:58:51 +00:00
// TODO: When this was first written, bits/sec was same as baud.
// Need to revisit this for PSK modes where they are not the same.
2018-08-05 15:20:27 +00:00
#if 0 // Added during 1.5 beta test
static int BITS_TO_MS ( int b , int ch ) {
int bits_per_symbol ;
switch ( save_audio_config_p - > achan [ ch ] . modem_type ) {
case MODEM_QPSK : bits_per_symbol = 2 ; break ;
case MODEM_8PSK : bits_per_symbol = 3 ; break ;
case default : bits_per_symbol = 1 ; break ;
}
return ( ( b * 1000 ) / ( xmit_bits_per_sec [ ( ch ) ] * bits_per_symbol ) ) ;
}
static int MS_TO_BITS ( int ms , int ch ) {
int bits_per_symbol ;
switch ( save_audio_config_p - > achan [ ch ] . modem_type ) {
case MODEM_QPSK : bits_per_symbol = 2 ; break ;
case MODEM_8PSK : bits_per_symbol = 3 ; break ;
case default : bits_per_symbol = 1 ; break ;
}
return ( ( ms * xmit_bits_per_sec [ ( ch ) ] * bits_per_symbol ) / 1000 ) ; TODO . . .
}
# else // OK for 1200, 9600 but wrong for PSK
2015-07-27 00:35:07 +00:00
# define BITS_TO_MS(b,ch) (((b)*1000) / xmit_bits_per_sec[(ch)])
# define MS_TO_BITS(ms,ch) (((ms)*xmit_bits_per_sec[(ch)]) / 1000)
2018-08-05 15:20:27 +00:00
# endif
2017-01-01 16:49:55 +00:00
# define MAXX(a,b) (((a)>(b)) ? (a) : (b))
2015-07-27 00:35:07 +00:00
2015-07-27 01:05:48 +00:00
# if __WIN32__
static unsigned __stdcall xmit_thread ( void * arg ) ;
# else
2015-07-27 00:35:07 +00:00
static void * xmit_thread ( void * arg ) ;
2015-07-27 01:05:48 +00:00
# endif
2015-07-27 01:17:23 +00:00
/*
* When an audio device is in stereo mode , we can have two
* different channels that want to transmit at the same time .
* We are not clever enough to multiplex them so use this
* so only one is activte at the same time .
*/
static dw_mutex_t audio_out_dev_mutex [ MAX_ADEVS ] ;
2017-10-19 00:50:59 +00:00
static int wait_for_clear_channel ( int channel , int slotttime , int persist , int fulldup ) ;
2016-11-20 19:58:51 +00:00
static void xmit_ax25_frames ( int c , int p , packet_t pp , int max_bundle ) ;
static int send_one_frame ( int c , int p , packet_t pp ) ;
2015-07-27 01:17:23 +00:00
static void xmit_speech ( int c , packet_t pp ) ;
2015-09-07 23:56:20 +00:00
static void xmit_morse ( int c , packet_t pp , int wpm ) ;
2017-01-01 16:49:55 +00:00
static void xmit_dtmf ( int c , packet_t pp , int speed ) ;
2015-07-27 00:35:07 +00:00
/*-------------------------------------------------------------------
*
* Name : xmit_init
*
* Purpose : Initialize the transmit process .
*
* Inputs : modem - Structure with modem and timing parameters .
*
*
* Outputs : Remember required information for future use .
*
* Description : Initialize the queue to be empty and set up other
* mechanisms for sharing it between different threads .
*
2015-07-27 01:17:23 +00:00
* Start up xmit_thread ( s ) to actually send the packets
2015-07-27 00:35:07 +00:00
* at the appropriate time .
*
2015-07-27 01:17:23 +00:00
* Version 1.2 : We now allow multiple audio devices with one or two channels each .
* Each audio channel has its own thread .
*
2015-07-27 00:35:07 +00:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-07-27 01:17:23 +00:00
static struct audio_s * save_audio_config_p ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:05:48 +00:00
void xmit_init ( struct audio_s * p_modem , int debug_xmit_packet )
2015-07-27 00:35:07 +00:00
{
int j ;
2015-07-27 01:17:23 +00:00
int ad ;
2015-07-27 00:35:07 +00:00
# if __WIN32__
2015-07-27 01:17:23 +00:00
HANDLE xmit_th [ MAX_CHANS ] ;
2015-07-27 00:35:07 +00:00
# else
//pthread_attr_t attr;
//struct sched_param sp;
2015-07-27 01:17:23 +00:00
pthread_t xmit_tid [ MAX_CHANS ] ;
2015-07-27 00:35:07 +00:00
# endif
2015-07-27 01:17:23 +00:00
//int e;
2015-07-27 00:35:07 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " xmit_init ( ... ) \n " ) ;
# endif
2015-07-27 01:17:23 +00:00
save_audio_config_p = p_modem ;
2015-07-27 01:05:48 +00:00
g_debug_xmit_packet = debug_xmit_packet ;
2015-07-27 00:35:07 +00:00
/*
* Push to Talk ( PTT ) control .
*/
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " xmit_init: about to call ptt_init \n " ) ;
# endif
ptt_init ( p_modem ) ;
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " xmit_init: back from ptt_init \n " ) ;
# endif
/*
* Save parameters for later use .
2015-07-27 01:17:23 +00:00
* TODO1 .2 : Any reason to use global config rather than making a copy ?
2015-07-27 00:35:07 +00:00
*/
for ( j = 0 ; j < MAX_CHANS ; j + + ) {
2015-07-27 01:17:23 +00:00
xmit_bits_per_sec [ j ] = p_modem - > achan [ j ] . baud ;
xmit_slottime [ j ] = p_modem - > achan [ j ] . slottime ;
xmit_persist [ j ] = p_modem - > achan [ j ] . persist ;
xmit_txdelay [ j ] = p_modem - > achan [ j ] . txdelay ;
xmit_txtail [ j ] = p_modem - > achan [ j ] . txtail ;
2017-10-19 00:50:59 +00:00
xmit_fulldup [ j ] = p_modem - > achan [ j ] . fulldup ;
2015-07-27 00:35:07 +00:00
}
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " xmit_init: about to call tq_init \n " ) ;
# endif
2015-07-27 01:17:23 +00:00
tq_init ( p_modem ) ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
for ( ad = 0 ; ad < MAX_ADEVS ; ad + + ) {
dw_mutex_init ( & ( audio_out_dev_mutex [ ad ] ) ) ;
}
2015-07-27 00:35:07 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
2015-07-27 01:17:23 +00:00
dw_printf ( " xmit_init: about to create threads \n " ) ;
2015-07-27 00:35:07 +00:00
# endif
//TODO: xmit thread should be higher priority to avoid
// underrun on the audio output device.
2015-07-27 01:17:23 +00:00
for ( j = 0 ; j < MAX_CHANS ; j + + ) {
2019-05-13 10:25:12 +00:00
if ( p_modem - > achan [ j ] . medium = = MEDIUM_RADIO ) {
2015-07-27 00:35:07 +00:00
# if __WIN32__
2019-12-01 01:20:13 +00:00
xmit_th [ j ] = ( HANDLE ) _beginthreadex ( NULL , 0 , xmit_thread , ( void * ) ( ptrdiff_t ) j , 0 , NULL ) ;
2015-07-27 01:17:23 +00:00
if ( xmit_th [ j ] = = NULL ) {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Could not create xmit thread %d \n " , j ) ;
return ;
}
2015-07-27 00:35:07 +00:00
# else
2015-07-27 01:17:23 +00:00
int e ;
2015-07-27 00:35:07 +00:00
#if 0
//TODO: not this simple. probably need FIFO policy.
2015-07-27 01:17:23 +00:00
pthread_attr_init ( & attr ) ;
e = pthread_attr_getschedparam ( & attr , & sp ) ;
if ( e ! = 0 ) {
text_color_set ( DW_COLOR_ERROR ) ;
perror ( " pthread_attr_getschedparam " ) ;
}
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Default scheduling priority = %d, min=%d, max=%d \n " ,
2015-07-27 00:35:07 +00:00
sp . sched_priority ,
sched_get_priority_min ( SCHED_OTHER ) ,
sched_get_priority_max ( SCHED_OTHER ) ) ;
2015-07-27 01:17:23 +00:00
sp . sched_priority - - ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
e = pthread_attr_setschedparam ( & attr , & sp ) ;
if ( e ! = 0 ) {
text_color_set ( DW_COLOR_ERROR ) ;
perror ( " pthread_attr_setschedparam " ) ;
}
2015-07-27 00:35:07 +00:00
2019-12-01 01:20:13 +00:00
e = pthread_create ( & ( xmit_tid [ j ] ) , & attr , xmit_thread , ( void * ) ( ptrdiff_t ) j ) ;
2015-07-27 01:17:23 +00:00
pthread_attr_destroy ( & attr ) ;
2015-07-27 00:35:07 +00:00
# else
2019-12-01 01:20:13 +00:00
e = pthread_create ( & ( xmit_tid [ j ] ) , NULL , xmit_thread , ( void * ) ( ptrdiff_t ) j ) ;
2015-07-27 00:35:07 +00:00
# endif
2015-07-27 01:17:23 +00:00
if ( e ! = 0 ) {
text_color_set ( DW_COLOR_ERROR ) ;
perror ( " Could not create xmit thread for audio device " ) ;
return ;
}
2015-07-27 00:35:07 +00:00
# endif
2015-07-27 01:17:23 +00:00
}
}
2015-07-27 00:35:07 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " xmit_init: finished \n " ) ;
# endif
} /* end tq_init */
/*-------------------------------------------------------------------
*
* Name : xmit_set_txdelay
* xmit_set_persist
* xmit_set_slottime
* xmit_set_txtail
2017-10-19 00:50:59 +00:00
* xmit_set_fulldup
2015-07-27 00:35:07 +00:00
*
*
* Purpose : The KISS protocol , and maybe others , can specify
* transmit timing parameters . If the application
* specifies these , they will override what was read
* from the configuration file .
*
* Inputs : channel - should be 0 or 1.
*
* value - time values are in 10 mSec units .
*
*
* Outputs : Remember required information for future use .
*
* Question : Should we have an option to enable or disable the
* application changing these values ?
*
* Bugs : No validity checking other than array subscript out of bounds .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void xmit_set_txdelay ( int channel , int value )
{
if ( channel > = 0 & & channel < MAX_CHANS ) {
xmit_txdelay [ channel ] = value ;
}
}
void xmit_set_persist ( int channel , int value )
{
if ( channel > = 0 & & channel < MAX_CHANS ) {
xmit_persist [ channel ] = value ;
}
}
void xmit_set_slottime ( int channel , int value )
{
if ( channel > = 0 & & channel < MAX_CHANS ) {
xmit_slottime [ channel ] = value ;
}
}
void xmit_set_txtail ( int channel , int value )
{
if ( channel > = 0 & & channel < MAX_CHANS ) {
xmit_txtail [ channel ] = value ;
}
}
2017-10-19 00:50:59 +00:00
void xmit_set_fulldup ( int channel , int value )
{
if ( channel > = 0 & & channel < MAX_CHANS ) {
xmit_fulldup [ channel ] = value ;
}
}
2016-11-20 19:58:51 +00:00
/*-------------------------------------------------------------------
*
* Name : frame_flavor
*
* Purpose : Separate frames into different flavors so we can decide
* which can be bundled into a single transmission and which should
* be sent separately .
*
* Inputs : pp - Packet object .
*
* Returns : Flavor , one of :
*
* FLAVOR_SPEECH - Destination address is SPEECH .
* FLAVOR_MORSE - Destination address is MORSE .
2017-01-01 16:49:55 +00:00
* FLAVOR_DTMF - Destination address is DTMF .
2016-11-20 19:58:51 +00:00
* FLAVOR_APRS_NEW - APRS original , i . e . not digipeating .
* FLAVOR_APRS_DIGI - APRS digipeating .
* FLAVOR_OTHER - Anything left over , i . e . connected mode .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2017-01-01 16:49:55 +00:00
typedef enum flavor_e { FLAVOR_APRS_NEW , FLAVOR_APRS_DIGI , FLAVOR_SPEECH , FLAVOR_MORSE , FLAVOR_DTMF , FLAVOR_OTHER } flavor_t ;
2016-11-20 19:58:51 +00:00
static flavor_t frame_flavor ( packet_t pp )
{
if ( ax25_is_aprs ( pp ) ) { // UI frame, PID 0xF0.
// It's unfortunate APRS did not use its own special PID.
char dest [ AX25_MAX_ADDR_LEN ] ;
ax25_get_addr_no_ssid ( pp , AX25_DESTINATION , dest ) ;
if ( strcmp ( dest , " SPEECH " ) = = 0 ) {
return ( FLAVOR_SPEECH ) ;
}
if ( strcmp ( dest , " MORSE " ) = = 0 ) {
return ( FLAVOR_MORSE ) ;
}
2017-01-01 16:49:55 +00:00
if ( strcmp ( dest , " DTMF " ) = = 0 ) {
return ( FLAVOR_DTMF ) ;
}
2016-11-20 19:58:51 +00:00
/* Is there at least one digipeater AND has first one been used? */
/* I could be the first in the list or later. Doesn't matter. */
if ( ax25_get_num_repeaters ( pp ) > = 1 & & ax25_get_h ( pp , AX25_REPEATER_1 ) ) {
return ( FLAVOR_APRS_DIGI ) ;
}
return ( FLAVOR_APRS_NEW ) ;
}
return ( FLAVOR_OTHER ) ;
} /* end frame_flavor */
2015-07-27 00:35:07 +00:00
/*-------------------------------------------------------------------
*
* Name : xmit_thread
*
2015-07-27 01:17:23 +00:00
* Purpose : Process transmit queue for one channel .
2015-07-27 00:35:07 +00:00
*
2015-07-27 01:17:23 +00:00
* Inputs : transmit packet queue .
2015-07-27 00:35:07 +00:00
*
* Outputs :
*
2015-07-27 01:17:23 +00:00
* Description : We have different timing rules for different types of
2015-07-27 00:35:07 +00:00
* packets so they are put into different queues .
*
* High Priority -
*
* Packets which are being digipeated go out first .
* Latest recommendations are to retransmit these
* immdediately ( after no one else is heard , of course )
* rather than waiting random times to avoid collisions .
* The KPC - 3 configuration option for this is " UIDWAIT OFF " . ( ? )
*
2016-11-20 19:58:51 +00:00
* AX .25 connected mode also has a couple cases
* where " expedited " frames are sent .
*
2015-07-27 00:35:07 +00:00
* Low Priority -
*
* Other packets are sent after a random wait time
* ( determined by PERSIST & SLOTTIME ) to help avoid
* collisions .
*
* If more than one audio channel is being used , a separate
* pair of transmit queues is used for each channel .
*
*
2015-07-27 01:17:23 +00:00
*
* Version 1.2 : Allow more than one audio device .
* each channel has its own thread .
* Add speech capability .
*
2016-11-20 19:58:51 +00:00
* Version 1.4 : Rearranged logic for bundling multiple frames into a single transmission .
*
2017-01-01 16:49:55 +00:00
* The rule is that Speech , Morse Code , DTMF , and APRS digipeated frames
2016-11-20 19:58:51 +00:00
* are all sent separately . The rest can be bundled .
*
2015-07-27 01:17:23 +00:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# if __WIN32__
static unsigned __stdcall xmit_thread ( void * arg )
# else
static void * xmit_thread ( void * arg )
# endif
{
2019-12-01 01:20:13 +00:00
int chan = ( int ) ( ptrdiff_t ) arg ; // channel number.
2015-07-27 01:17:23 +00:00
packet_t pp ;
2016-11-20 19:58:51 +00:00
int prio ;
2015-07-27 01:17:23 +00:00
int ok ;
while ( 1 ) {
2016-11-20 19:58:51 +00:00
tq_wait_while_empty ( chan ) ;
2015-07-27 01:17:23 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread, channel %d: woke up \n " , chan ) ;
2015-07-27 01:17:23 +00:00
# endif
2016-11-20 19:58:51 +00:00
// Does this extra loop offer any benefit?
while ( tq_peek ( chan , TQ_PRIO_0_HI ) ! = NULL | | tq_peek ( chan , TQ_PRIO_1_LO ) ! = NULL ) {
2015-07-27 01:17:23 +00:00
/*
* Wait for the channel to be clear .
2016-11-20 19:58:51 +00:00
* If there is something in the high priority queue , begin transmitting immediately .
* Otherwise , wait a random amount of time , in hopes of minimizing collisions .
2015-07-27 01:17:23 +00:00
*/
2017-10-19 00:50:59 +00:00
ok = wait_for_clear_channel ( chan , xmit_slottime [ chan ] , xmit_persist [ chan ] , xmit_fulldup [ chan ] ) ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
prio = TQ_PRIO_1_LO ;
pp = tq_remove ( chan , TQ_PRIO_0_HI ) ;
if ( pp ! = NULL ) {
prio = TQ_PRIO_0_HI ;
}
else {
pp = tq_remove ( chan , TQ_PRIO_1_LO ) ;
}
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " xmit_thread: tq_remove(chan=%d, prio=%d) returned %p \n " , chan , prio , pp ) ;
# endif
// Shouldn't have NULL here but be careful.
if ( pp ! = NULL ) {
if ( ok ) {
2015-07-27 01:17:23 +00:00
/*
* Channel is clear and we have lock on output device .
2015-09-07 23:56:20 +00:00
*
* If destination is " SPEECH " send info part to speech synthesizer .
* If destination is " MORSE " send as morse code .
2017-01-01 16:49:55 +00:00
* If destination is " DTMF " send as Touch Tones .
2015-07-27 01:17:23 +00:00
*/
2017-01-01 16:49:55 +00:00
int ssid , wpm , speed ;
2015-09-07 23:56:20 +00:00
2016-11-20 19:58:51 +00:00
switch ( frame_flavor ( pp ) ) {
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
case FLAVOR_SPEECH :
xmit_speech ( chan , pp ) ;
break ;
2015-09-07 23:56:20 +00:00
2016-11-20 19:58:51 +00:00
case FLAVOR_MORSE :
ssid = ax25_get_ssid ( pp , AX25_DESTINATION ) ;
wpm = ( ssid > 0 ) ? ( ssid * 2 ) : MORSE_DEFAULT_WPM ;
2015-09-07 23:56:20 +00:00
// This is a bit of a hack so we don't respond too quickly for APRStt.
// It will be sent in high priority queue while a beacon wouldn't.
// Add a little delay so user has time release PTT after sending #.
// This and default txdelay would give us a second.
2016-11-20 19:58:51 +00:00
if ( prio = = TQ_PRIO_0_HI ) {
2015-09-07 23:56:20 +00:00
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("APRStt morse xmit delay hack...\n");
SLEEP_MS ( 700 ) ;
}
2016-11-20 19:58:51 +00:00
xmit_morse ( chan , pp , wpm ) ;
break ;
2017-01-01 16:49:55 +00:00
case FLAVOR_DTMF :
speed = ax25_get_ssid ( pp , AX25_DESTINATION ) ;
if ( speed = = 0 ) speed = 5 ; // default half of maximum
if ( speed > 10 ) speed = 10 ;
xmit_dtmf ( chan , pp , speed ) ;
break ;
2016-11-20 19:58:51 +00:00
case FLAVOR_APRS_DIGI :
xmit_ax25_frames ( chan , prio , pp , 1 ) ; /* 1 means don't bundle */
2021-09-21 16:53:17 +00:00
// I don't know if this in some official specification
// somewhere, but it is generally agreed that APRS digipeaters
// should send only one frame at a time rather than
// bunding multiple frames into a single transmission.
// Discussion here: http://lists.tapr.org/pipermail/aprssig_lists.tapr.org/2021-September/049034.html
2016-11-20 19:58:51 +00:00
break ;
case FLAVOR_APRS_NEW :
case FLAVOR_OTHER :
default :
xmit_ax25_frames ( chan , prio , pp , 256 ) ;
break ;
}
2015-09-07 23:56:20 +00:00
2016-11-20 19:58:51 +00:00
// Corresponding lock is in wait_for_clear_channel.
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
dw_mutex_unlock ( & ( audio_out_dev_mutex [ ACHAN2ADEV ( chan ) ] ) ) ;
}
else {
2015-07-27 01:17:23 +00:00
/*
* Timeout waiting for clear channel .
* Discard the packet .
* Display with ERROR color rather than XMIT color .
*/
2016-11-20 19:58:51 +00:00
char stemp [ 1024 ] ; /* max size needed? */
int info_len ;
unsigned char * pinfo ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Waited too long for clear channel. Discarding packet below. \n " ) ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
ax25_format_addrs ( pp , stemp ) ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
info_len = ax25_get_info ( pp , & pinfo ) ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " [%d%c] " , chan , ( prio = = TQ_PRIO_0_HI ) ? ' H ' : ' L ' ) ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
dw_printf ( " %s " , stemp ) ; /* stations followed by : */
ax25_safe_print ( ( char * ) pinfo , info_len , ! ax25_is_aprs ( pp ) ) ;
dw_printf ( " \n " ) ;
ax25_delete ( pp ) ;
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
} /* wait for clear channel error. */
} /* Have pp */
} /* while queue not empty */
} /* while 1 */
2015-07-27 01:17:23 +00:00
return 0 ; /* unreachable but quiet the warning. */
} /* end xmit_thread */
/*-------------------------------------------------------------------
*
* Name : xmit_ax25_frames
*
* Purpose : After we have a clear channel , and possibly waited a random time ,
* we transmit one or more frames .
*
2016-11-20 19:58:51 +00:00
* Inputs : chan - Channel number .
2015-07-27 01:17:23 +00:00
*
2016-11-20 19:58:51 +00:00
* prio - Priority of the first frame .
* Subsequent frames could be different .
2015-07-27 01:17:23 +00:00
*
* pp - Packet object pointer .
* It will be deleted so caller should not try
* to reference it after this .
*
2016-11-20 19:58:51 +00:00
* max_bundle - Max number of frames to bundle into one transmission .
*
2015-07-27 01:17:23 +00:00
* Description : Turn on transmitter .
* Send flags for TXDELAY time .
* Send the first packet , given by pp .
2016-11-20 19:58:51 +00:00
* Possibly send more packets from either queue .
2015-07-27 01:17:23 +00:00
* Send flags for TXTAIL time .
* Turn off transmitter .
*
*
2016-11-20 19:58:51 +00:00
* How many frames in one transmission ? ( for APRS )
2015-07-27 00:35:07 +00:00
*
* Should we send multiple frames in one transmission if we
* have more than one sitting in the queue ? At first I was thinking
* this would help reduce channel congestion . I don ' t recall seeing
2016-11-20 19:58:51 +00:00
* anything in the APRS specifications allowing or disallowing multiple
2015-07-27 00:35:07 +00:00
* frames in one transmission . I can think of some scenarios
* where it might help . I can think of some where it would
* definitely be counter productive .
*
* What to others have to say about this topic ?
*
* " For what it is worth, the original APRSdos used a several second random
* generator each time any kind of packet was generated . . . This is to avoid
* bundling . Because bundling , though good for connected packet , is not good
* on APRS . Sometimes the digi begins digipeating the first packet in the
* bundle and steps all over the remainder of them . So best to make sure each
* packet is isolated in time from others . . . "
*
* Bob , WB4APR
*
*
* Version 0.9 : Earlier versions always sent one frame per transmission .
* This was fine for APRS but more and more people are now
* using this as a KISS TNC for connected protocols .
2016-11-20 19:58:51 +00:00
* Rather than having a configuration file item ,
2015-07-27 00:35:07 +00:00
* we try setting the maximum number automatically .
* 1 for digipeated frames , 7 for others .
*
2016-11-20 19:58:51 +00:00
* Version 1.4 : Lift the limit . We could theoretically have a window size up to 127.
* If another section pumps out that many quickly we shouldn ' t
* break it up here . Empty out both queues with some exceptions .
*
* Digipeated APRS , Speech , and Morse code should have
* their own separate transmissions .
* Everything else can be bundled together .
* Different priorities can share a single transmission .
* Once we have control of the channel , we might as well keep going .
* [ High ] Priority frames will always go to head of the line ,
*
2017-10-19 00:50:59 +00:00
* Version 1.5 : Add full duplex option .
*
2015-07-27 00:35:07 +00:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
static void xmit_ax25_frames ( int chan , int prio , packet_t pp , int max_bundle )
2015-07-27 00:35:07 +00:00
{
2015-07-27 01:17:23 +00:00
2015-07-27 00:35:07 +00:00
int pre_flags , post_flags ;
int num_bits ; /* Total number of bits in transmission */
/* including all flags and bit stuffing. */
int duration ; /* Transmission time in milliseconds. */
int already ;
int wait_more ;
2016-11-20 19:58:51 +00:00
int numframe = 0 ; /* Number of frames sent during this transmission. */
2015-07-27 00:35:07 +00:00
/*
* These are for timing of a transmission .
* All are in usual unix time ( seconds since 1 / 1 / 1970 ) but higher resolution
*/
double time_ptt ; /* Time when PTT is turned on. */
double time_now ; /* Current time. */
2015-07-27 01:17:23 +00:00
int nb ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
/*
2015-07-27 00:35:07 +00:00
* Turn on transmitter .
* Start sending leading flag bytes .
*/
2015-07-27 01:17:23 +00:00
time_ptt = dtime_now ( ) ;
2015-07-27 00:35:07 +00:00
2016-05-01 22:46:47 +00:00
// TODO: This was written assuming bits/sec = baud.
// Does it is need to be scaled differently for PSK?
2015-07-27 01:17:23 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, Turn on PTT now for channel %d. speed = %d \n " , dtime_now ( ) - time_ptt , chan , xmit_bits_per_sec [ chan ] ) ;
2015-07-27 01:17:23 +00:00
# endif
2016-11-20 19:58:51 +00:00
ptt_set ( OCTYPE_PTT , chan , 1 ) ;
2015-07-27 01:05:48 +00:00
2018-08-05 15:20:27 +00:00
// Inform data link state machine that we are now transmitting.
dlq_seize_confirm ( chan ) ; // C4.2. "This primitive indicates, to the Data-link State
// machine, that the transmission opportunity has arrived."
2016-11-20 19:58:51 +00:00
pre_flags = MS_TO_BITS ( xmit_txdelay [ chan ] * 10 , chan ) / 8 ;
2021-10-22 21:29:20 +00:00
num_bits = layer2_preamble_postamble ( chan , pre_flags , 0 , save_audio_config_p ) ;
2015-07-27 01:17:23 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, txdelay=%d [*10], pre_flags=%d, num_bits=%d \n " , dtime_now ( ) - time_ptt , xmit_txdelay [ chan ] , pre_flags , num_bits ) ;
double presleep = dtime_now ( ) ;
2015-07-27 01:17:23 +00:00
# endif
2015-07-27 01:05:48 +00:00
2018-08-05 15:20:27 +00:00
SLEEP_MS ( 10 ) ; // Give data link state machine a chance to
// to stuff more frames into the transmit queue,
// in response to dlq_seize_confirm, so
// we don't run off the end too soon.
2019-04-21 23:09:41 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
// How long did sleep last?
dw_printf ( " xmit_thread: t=%.3f, Should be 0.010 second after the above. \n " , dtime_now ( ) - time_ptt ) ;
double naptime = dtime_now ( ) - presleep ;
if ( naptime > 0.015 ) {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Sleep for 10 ms actually took %.3f second! \n " , naptime ) ;
}
# endif
2015-07-27 01:05:48 +00:00
2015-07-27 00:35:07 +00:00
/*
* Transmit the frame .
2016-11-20 19:58:51 +00:00
*/
nb = send_one_frame ( chan , prio , pp ) ;
2015-07-27 01:17:23 +00:00
num_bits + = nb ;
2016-11-20 19:58:51 +00:00
if ( nb > 0 ) numframe + + ;
2015-07-27 01:17:23 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, nb=%d, num_bits=%d, numframe=%d \n " , dtime_now ( ) - time_ptt , nb , num_bits , numframe ) ;
2015-07-27 01:17:23 +00:00
# endif
ax25_delete ( pp ) ;
2015-07-27 00:35:07 +00:00
/*
2016-11-20 19:58:51 +00:00
* See if we can bundle additional frames into this transmission .
2015-07-27 00:35:07 +00:00
*/
2016-11-20 19:58:51 +00:00
int done = 0 ;
while ( numframe < max_bundle & & ! done ) {
/*
* Peek at what is available .
* Don ' t remove from queue yet because it might not be eligible .
*/
prio = TQ_PRIO_1_LO ;
pp = tq_peek ( chan , TQ_PRIO_0_HI ) ;
if ( pp ! = NULL ) {
prio = TQ_PRIO_0_HI ;
}
else {
pp = tq_peek ( chan , TQ_PRIO_1_LO ) ;
}
if ( pp ! = NULL ) {
switch ( frame_flavor ( pp ) ) {
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
case FLAVOR_SPEECH :
case FLAVOR_MORSE :
2017-01-01 16:49:55 +00:00
case FLAVOR_DTMF :
2016-11-20 19:58:51 +00:00
case FLAVOR_APRS_DIGI :
default :
done = 1 ; // not eligible for bundling.
break ;
case FLAVOR_APRS_NEW :
case FLAVOR_OTHER :
pp = tq_remove ( chan , prio ) ;
2015-07-27 00:35:07 +00:00
# if DEBUG
2016-11-20 19:58:51 +00:00
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, tq_remove(chan=%d, prio=%d) returned %p \n " , dtime_now ( ) - time_ptt , chan , prio , pp ) ;
2015-07-27 00:35:07 +00:00
# endif
2015-07-27 01:05:48 +00:00
2016-11-20 19:58:51 +00:00
nb = send_one_frame ( chan , prio , pp ) ;
num_bits + = nb ;
if ( nb > 0 ) numframe + + ;
2015-07-27 01:17:23 +00:00
# if DEBUG
2016-11-20 19:58:51 +00:00
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, nb=%d, num_bits=%d, numframe=%d \n " , dtime_now ( ) - time_ptt , nb , num_bits , numframe ) ;
2015-07-27 01:17:23 +00:00
# endif
2016-11-20 19:58:51 +00:00
ax25_delete ( pp ) ;
break ;
}
}
else {
done = 1 ;
}
2015-07-27 01:17:23 +00:00
}
2015-07-27 00:35:07 +00:00
/*
2015-07-27 01:17:23 +00:00
* Need TXTAIL because we don ' t know exactly when the sound is done .
2015-07-27 00:35:07 +00:00
*/
2016-11-20 19:58:51 +00:00
post_flags = MS_TO_BITS ( xmit_txtail [ chan ] * 10 , chan ) / 8 ;
2021-10-22 21:29:20 +00:00
nb = layer2_preamble_postamble ( chan , post_flags , 1 , save_audio_config_p ) ;
2015-07-27 01:17:23 +00:00
num_bits + = nb ;
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, txtail=%d [*10], post_flags=%d, nb=%d, num_bits=%d \n " , dtime_now ( ) - time_ptt , xmit_txtail [ chan ] , post_flags , nb , num_bits ) ;
2015-07-27 01:17:23 +00:00
# endif
2015-07-27 00:35:07 +00:00
/*
2015-07-27 01:17:23 +00:00
* While demodulating is CPU intensive , generating the tones is not .
2016-11-20 19:58:51 +00:00
* Example : on the RPi model 1 , with 50 % of the CPU taken with two receive
2015-07-27 01:17:23 +00:00
* channels , a transmission of more than a second is generated in
* about 40 mS of elapsed real time .
*/
2016-11-20 19:58:51 +00:00
audio_wait ( ACHAN2ADEV ( chan ) ) ;
2015-07-27 01:17:23 +00:00
/*
* Ideally we should be here just about the time when the audio is ending .
* However , the innards of " audio_wait " are not satisfactory in all cases .
2015-07-27 00:35:07 +00:00
*
2015-07-27 01:17:23 +00:00
* Calculate how long the frame ( s ) should take in milliseconds .
2015-07-27 00:35:07 +00:00
*/
2015-07-27 01:17:23 +00:00
2016-11-20 19:58:51 +00:00
duration = BITS_TO_MS ( num_bits , chan ) ;
2015-07-27 01:17:23 +00:00
/*
* See how long it has been since PTT was turned on .
* Wait additional time if necessary .
*/
time_now = dtime_now ( ) ;
already = ( int ) ( ( time_now - time_ptt ) * 1000. ) ;
wait_more = duration - already ;
2015-07-27 00:35:07 +00:00
# if DEBUG
2015-07-27 01:17:23 +00:00
text_color_set ( DW_COLOR_DEBUG ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, xmit duration=%d, %d already elapsed since PTT, wait %d more \n " , dtime_now ( ) - time_ptt , duration , already , wait_more ) ;
2015-07-27 00:35:07 +00:00
# endif
2015-07-27 01:17:23 +00:00
if ( wait_more > 0 ) {
SLEEP_MS ( wait_more ) ;
}
else if ( wait_more < - 100 ) {
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
/* If we run over by 10 mSec or so, it's nothing to worry about. */
/* However, if PTT is still on about 1/10 sec after audio */
/* should be done, something is wrong. */
/* Looks like a bug with the RPi audio system. Never an issue with Ubuntu. */
/* This runs over randomly sometimes. TODO: investigate more fully sometime. */
# ifndef __arm__
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Transmit timing error: PTT is on %d mSec too long. \n " , - wait_more ) ;
# endif
}
2015-07-27 00:35:07 +00:00
/*
* Turn off transmitter .
*/
2015-07-27 01:17:23 +00:00
# if DEBUG
text_color_set ( DW_COLOR_DEBUG ) ;
time_now = dtime_now ( ) ;
2019-04-21 23:09:41 +00:00
dw_printf ( " xmit_thread: t=%.3f, Turn off PTT now. Actual time on was %d mS, vs. %d desired \n " , dtime_now ( ) - time_ptt , ( int ) ( ( time_now - time_ptt ) * 1000. ) , duration ) ;
2015-07-27 01:17:23 +00:00
# endif
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
ptt_set ( OCTYPE_PTT , chan , 0 ) ;
2015-07-27 01:17:23 +00:00
} /* end xmit_ax25_frames */
2015-09-07 23:56:20 +00:00
2016-11-20 19:58:51 +00:00
/*-------------------------------------------------------------------
*
* Name : send_one_frame
*
* Purpose : Send one AX .25 frame .
*
* Inputs : c - Channel number .
*
* p - Priority .
*
* pp - Packet object pointer . Caller will delete it .
*
* Returns : Number of bits transmitted .
*
* Description : Caller is responsible for activiating PTT , TXDELAY ,
* deciding how many frames can be in one transmission ,
* deactivating PTT .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int send_one_frame ( int c , int p , packet_t pp )
{
char stemp [ 1024 ] ; /* max size needed? */
int info_len ;
unsigned char * pinfo ;
int nb ;
if ( ax25_is_null_frame ( pp ) ) {
2018-08-05 15:20:27 +00:00
// Issue 132 - We could end up in a situation where:
// Transmitter is already on.
// Application wants to send a frame.
// dl_seize_request turns into this null frame.
// It was being ignored here so the data got stuck in the queue.
// I think the solution is to send back a seize confirm here.
// It shouldn't hurt if we send it redundantly.
// Added for 1.5 beta test 4.
dlq_seize_confirm ( c ) ; // C4.2. "This primitive indicates, to the Data-link State
// machine, that the transmission opportunity has arrived."
SLEEP_MS ( 10 ) ; // Give data link state machine a chance to
// to stuff more frames into the transmit queue,
// in response to dlq_seize_confirm, so
// we don't run off the end too soon.
2016-11-20 19:58:51 +00:00
return ( 0 ) ;
}
2018-01-04 02:04:33 +00:00
char ts [ 100 ] ; // optional time stamp.
if ( strlen ( save_audio_config_p - > timestamp_format ) > 0 ) {
char tstmp [ 100 ] ;
timestamp_user_format ( tstmp , sizeof ( tstmp ) , save_audio_config_p - > timestamp_format ) ;
strlcpy ( ts , " " , sizeof ( ts ) ) ; // space after channel.
strlcat ( ts , tstmp , sizeof ( ts ) ) ;
}
else {
strlcpy ( ts , " " , sizeof ( ts ) ) ;
}
2016-11-20 19:58:51 +00:00
ax25_format_addrs ( pp , stemp ) ;
info_len = ax25_get_info ( pp , & pinfo ) ;
text_color_set ( DW_COLOR_XMIT ) ;
2021-10-22 21:29:20 +00:00
#if 0 // FIXME - enable this?
2020-04-14 01:07:10 +00:00
dw_printf ( " [%d%c%s%s] " , c ,
p = = TQ_PRIO_0_HI ? ' H ' : ' L ' ,
2021-10-22 21:29:20 +00:00
save_audio_config_p - > achan [ c ] . fx25_strength ? " F " : " " ,
2020-04-14 01:07:10 +00:00
ts ) ;
# else
2018-01-04 02:04:33 +00:00
dw_printf ( " [%d%c%s] " , c , p = = TQ_PRIO_0_HI ? ' H ' : ' L ' , ts ) ;
2020-04-14 01:07:10 +00:00
# endif
2016-11-20 19:58:51 +00:00
dw_printf ( " %s " , stemp ) ; /* stations followed by : */
/* Demystify non-APRS. Use same format for received frames in direwolf.c. */
if ( ! ax25_is_aprs ( pp ) ) {
ax25_frame_type_t ftype ;
cmdres_t cr ;
char desc [ 80 ] ;
int pf ;
int nr ;
int ns ;
ftype = ax25_frame_type ( pp , & cr , desc , & pf , & nr , & ns ) ;
dw_printf ( " (%s) " , desc ) ;
if ( ftype = = frame_type_U_XID ) {
struct xid_param_s param ;
2018-01-04 02:04:33 +00:00
char info2text [ 150 ] ;
2016-11-20 19:58:51 +00:00
xid_parse ( pinfo , info_len , & param , info2text , sizeof ( info2text ) ) ;
dw_printf ( " %s \n " , info2text ) ;
}
else {
ax25_safe_print ( ( char * ) pinfo , info_len , ! ax25_is_aprs ( pp ) ) ;
dw_printf ( " \n " ) ;
}
}
else {
ax25_safe_print ( ( char * ) pinfo , info_len , ! ax25_is_aprs ( pp ) ) ;
dw_printf ( " \n " ) ;
}
( void ) ax25_check_addresses ( pp ) ;
/* Optional hex dump of packet. */
if ( g_debug_xmit_packet ) {
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " ------ \n " ) ;
ax25_hex_dump ( pp ) ;
dw_printf ( " ------ \n " ) ;
}
/*
* Transmit the frame .
*/
int send_invalid_fcs2 = 0 ;
if ( save_audio_config_p - > xmit_error_rate ! = 0 ) {
float r = ( float ) ( rand ( ) ) / ( float ) RAND_MAX ; // Random, 0.0 to 1.0
if ( save_audio_config_p - > xmit_error_rate / 100.0 > r ) {
send_invalid_fcs2 = 1 ;
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " Intentionally sending invalid CRC for frame above. Xmit Error rate = %d per cent. \n " , save_audio_config_p - > xmit_error_rate ) ;
}
}
2021-10-22 21:29:20 +00:00
nb = layer2_send_frame ( c , pp , send_invalid_fcs2 , save_audio_config_p ) ;
2020-04-14 01:07:10 +00:00
// Optionally send confirmation to AGW client app if monitoring enabled.
server_send_monitored ( c , pp , 1 ) ;
2016-11-20 19:58:51 +00:00
return ( nb ) ;
} /* end send_one_frame */
2015-07-27 01:17:23 +00:00
/*-------------------------------------------------------------------
*
2015-09-07 23:56:20 +00:00
* Name : xmit_speech
2015-07-27 01:17:23 +00:00
*
* Purpose : After we have a clear channel , and possibly waited a random time ,
2015-09-07 23:56:20 +00:00
* we transmit information part of frame as speech .
2015-07-27 01:17:23 +00:00
*
* Inputs : c - Channel number .
*
* pp - Packet object pointer .
* It will be deleted so caller should not try
* to reference it after this .
*
* Description : Turn on transmitter .
* Invoke the text - to - speech script .
* Turn off transmitter .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void xmit_speech ( int c , packet_t pp )
{
int info_len ;
unsigned char * pinfo ;
2015-07-27 00:35:07 +00:00
/*
2015-07-27 01:17:23 +00:00
* Print spoken packet . Prefix by channel .
2015-07-27 00:35:07 +00:00
*/
2018-01-04 02:04:33 +00:00
char ts [ 100 ] ; // optional time stamp.
if ( strlen ( save_audio_config_p - > timestamp_format ) > 0 ) {
char tstmp [ 100 ] ;
timestamp_user_format ( tstmp , sizeof ( tstmp ) , save_audio_config_p - > timestamp_format ) ;
strlcpy ( ts , " " , sizeof ( ts ) ) ; // space after channel.
strlcat ( ts , tstmp , sizeof ( ts ) ) ;
}
else {
strlcpy ( ts , " " , sizeof ( ts ) ) ;
}
2015-07-27 01:17:23 +00:00
info_len = ax25_get_info ( pp , & pinfo ) ;
2016-07-03 22:09:34 +00:00
( void ) info_len ;
2015-07-27 01:17:23 +00:00
text_color_set ( DW_COLOR_XMIT ) ;
2018-02-02 00:48:08 +00:00
dw_printf ( " [%d.speech%s] \" %s \" \n " , c , ts , pinfo ) ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
if ( strlen ( save_audio_config_p - > tts_script ) = = 0 ) {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Text-to-speech script has not been configured. \n " ) ;
ax25_delete ( pp ) ;
return ;
}
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
/*
* Turn on transmitter .
*/
ptt_set ( OCTYPE_PTT , c , 1 ) ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
/*
* Invoke the speech - to - text script .
*/
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
xmit_speak_it ( save_audio_config_p - > tts_script , c , ( char * ) pinfo ) ;
/*
* Turn off transmitter .
*/
ptt_set ( OCTYPE_PTT , c , 0 ) ;
ax25_delete ( pp ) ;
} /* end xmit_speech */
/* Broken out into separate function so configuration can validate it. */
/* Returns 0 for success. */
int xmit_speak_it ( char * script , int c , char * orig_msg )
{
int err ;
char msg [ 2000 ] ;
2019-04-21 23:09:41 +00:00
char cmd [ sizeof ( msg ) + 16 ] ;
char * p ;
2015-07-27 01:17:23 +00:00
/* Remove any quotes because it will mess up command line argument parsing. */
2015-11-08 01:57:02 +00:00
strlcpy ( msg , orig_msg , sizeof ( msg ) ) ;
2015-07-27 01:17:23 +00:00
for ( p = msg ; * p ! = ' \0 ' ; p + + ) {
if ( * p = = ' " ' ) * p = ' ' ;
2015-07-27 00:35:07 +00:00
}
2015-07-27 01:17:23 +00:00
# if __WIN32__
2015-11-08 01:57:02 +00:00
snprintf ( cmd , sizeof ( cmd ) , " %s %d \" %s \" >nul " , script , c , msg ) ;
2015-07-27 01:17:23 +00:00
# else
2015-11-08 01:57:02 +00:00
snprintf ( cmd , sizeof ( cmd ) , " %s %d \" %s \" " , script , c , msg ) ;
2015-07-27 01:17:23 +00:00
# endif
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
//text_color_set(DW_COLOR_DEBUG);
//dw_printf ("cmd=%s\n", cmd);
err = system ( cmd ) ;
if ( err ! = 0 ) {
char cwd [ 1000 ] ;
char path [ 3000 ] ;
char * ignore ;
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Failed to run text-to-speech script, %s \n " , script ) ;
ignore = getcwd ( cwd , sizeof ( cwd ) ) ;
2016-07-03 22:09:34 +00:00
( void ) ignore ;
2015-11-08 01:57:02 +00:00
strlcpy ( path , getenv ( " PATH " ) , sizeof ( path ) ) ;
2015-07-27 01:17:23 +00:00
dw_printf ( " CWD = %s \n " , cwd ) ;
dw_printf ( " PATH = %s \n " , path ) ;
}
return ( err ) ;
}
2015-07-27 00:35:07 +00:00
2015-09-07 23:56:20 +00:00
/*-------------------------------------------------------------------
*
* Name : xmit_morse
*
* Purpose : After we have a clear channel , and possibly waited a random time ,
* we transmit information part of frame as Morse code .
*
* Inputs : c - Channel number .
*
* pp - Packet object pointer .
* It will be deleted so caller should not try
* to reference it after this .
*
* wpm - Speed in words per minute .
*
* Description : Turn on transmitter .
* Send text as Morse code .
2017-01-01 16:49:55 +00:00
* A small amount of quiet padding will appear at start and end .
2015-09-07 23:56:20 +00:00
* Turn off transmitter .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void xmit_morse ( int c , packet_t pp , int wpm )
{
int info_len ;
unsigned char * pinfo ;
2017-01-01 16:49:55 +00:00
int length_ms , wait_ms ;
double start_ptt , wait_until , now ;
2015-09-07 23:56:20 +00:00
2018-01-04 02:04:33 +00:00
char ts [ 100 ] ; // optional time stamp.
if ( strlen ( save_audio_config_p - > timestamp_format ) > 0 ) {
char tstmp [ 100 ] ;
timestamp_user_format ( tstmp , sizeof ( tstmp ) , save_audio_config_p - > timestamp_format ) ;
strlcpy ( ts , " " , sizeof ( ts ) ) ; // space after channel.
strlcat ( ts , tstmp , sizeof ( ts ) ) ;
}
else {
strlcpy ( ts , " " , sizeof ( ts ) ) ;
}
2015-09-07 23:56:20 +00:00
info_len = ax25_get_info ( pp , & pinfo ) ;
2016-07-03 22:09:34 +00:00
( void ) info_len ;
2015-09-07 23:56:20 +00:00
text_color_set ( DW_COLOR_XMIT ) ;
2018-02-02 00:48:08 +00:00
dw_printf ( " [%d.morse%s] \" %s \" \n " , c , ts , pinfo ) ;
2015-09-07 23:56:20 +00:00
ptt_set ( OCTYPE_PTT , c , 1 ) ;
2017-01-01 16:49:55 +00:00
start_ptt = dtime_now ( ) ;
// make txdelay at least 300 and txtail at least 250 ms.
length_ms = morse_send ( c , ( char * ) pinfo , wpm , MAXX ( xmit_txdelay [ c ] * 10 , 300 ) , MAXX ( xmit_txtail [ c ] * 10 , 250 ) ) ;
2015-09-07 23:56:20 +00:00
2017-01-01 16:49:55 +00:00
// there is probably still sound queued up in the output buffers.
wait_until = start_ptt + length_ms * 0.001 ;
now = dtime_now ( ) ;
wait_ms = ( int ) ( ( wait_until - now ) * 1000 ) ;
if ( wait_ms > 0 ) {
SLEEP_MS ( wait_ms ) ;
}
2015-09-07 23:56:20 +00:00
ptt_set ( OCTYPE_PTT , c , 0 ) ;
ax25_delete ( pp ) ;
} /* end xmit_morse */
2017-01-01 16:49:55 +00:00
/*-------------------------------------------------------------------
*
* Name : xmit_dtmf
*
* Purpose : After we have a clear channel , and possibly waited a random time ,
* we transmit information part of frame as DTMF tones .
*
* Inputs : c - Channel number .
*
* pp - Packet object pointer .
* It will be deleted so caller should not try
* to reference it after this .
*
* speed - Button presses per second .
*
* Description : Turn on transmitter .
* Send text as touch tones .
* A small amount of quiet padding will appear at start and end .
* Turn off transmitter .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void xmit_dtmf ( int c , packet_t pp , int speed )
{
int info_len ;
unsigned char * pinfo ;
int length_ms , wait_ms ;
double start_ptt , wait_until , now ;
2018-01-04 02:04:33 +00:00
char ts [ 100 ] ; // optional time stamp.
if ( strlen ( save_audio_config_p - > timestamp_format ) > 0 ) {
char tstmp [ 100 ] ;
timestamp_user_format ( tstmp , sizeof ( tstmp ) , save_audio_config_p - > timestamp_format ) ;
strlcpy ( ts , " " , sizeof ( ts ) ) ; // space after channel.
strlcat ( ts , tstmp , sizeof ( ts ) ) ;
}
else {
strlcpy ( ts , " " , sizeof ( ts ) ) ;
}
2017-01-01 16:49:55 +00:00
info_len = ax25_get_info ( pp , & pinfo ) ;
( void ) info_len ;
text_color_set ( DW_COLOR_XMIT ) ;
2018-02-02 00:48:08 +00:00
dw_printf ( " [%d.dtmf%s] \" %s \" \n " , c , ts , pinfo ) ;
2017-01-01 16:49:55 +00:00
ptt_set ( OCTYPE_PTT , c , 1 ) ;
start_ptt = dtime_now ( ) ;
// make txdelay at least 300 and txtail at least 250 ms.
length_ms = dtmf_send ( c , ( char * ) pinfo , speed , MAXX ( xmit_txdelay [ c ] * 10 , 300 ) , MAXX ( xmit_txtail [ c ] * 10 , 250 ) ) ;
// there is probably still sound queued up in the output buffers.
wait_until = start_ptt + length_ms * 0.001 ;
now = dtime_now ( ) ;
wait_ms = ( int ) ( ( wait_until - now ) * 1000 ) ;
if ( wait_ms > 0 ) {
SLEEP_MS ( wait_ms ) ;
}
else {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Oops. CPU too slow to keep up with DTMF generation. \n " ) ;
}
ptt_set ( OCTYPE_PTT , c , 0 ) ;
ax25_delete ( pp ) ;
} /* end xmit_dtmf */
2015-07-27 00:35:07 +00:00
/*-------------------------------------------------------------------
*
* Name : wait_for_clear_channel
*
* Purpose : Wait for the radio channel to be clear and any
* additional time for collision avoidance .
*
2016-11-20 19:58:51 +00:00
* Inputs : chan - Radio channel number .
2015-07-27 00:35:07 +00:00
*
* slottime - Amount of time to wait for each iteration
* of the waiting algorithm . 10 mSec units .
*
2017-10-19 00:50:59 +00:00
* persist - Probability of transmitting .
*
* fulldup - Full duplex . Just start sending immediately .
2015-07-27 00:35:07 +00:00
*
* Returns : 1 for OK . 0 for timeout .
*
2016-11-20 19:58:51 +00:00
* Description : New in version 1.2 : also obtain a lock on audio out device .
2015-07-27 01:17:23 +00:00
*
2017-10-19 00:50:59 +00:00
* New in version 1.5 : full duplex .
* Just start transmitting rather than waiting for clear channel .
* This would only be appropriate when transmit and receive are
2021-09-19 18:51:18 +00:00
* using different radio frequencies . e . g . VHF up , UHF down satellite .
2017-10-19 00:50:59 +00:00
*
2015-07-27 00:35:07 +00:00
* Transmit delay algorithm :
*
* Wait for channel to be clear .
2016-11-20 19:58:51 +00:00
* If anything in high priority queue , bail out of the following .
2015-07-27 00:35:07 +00:00
*
* Wait slottime * 10 milliseconds .
* Generate an 8 bit random number in range of 0 - 255.
* If random number < = persist value , return .
* Otherwise repeat .
*
* Example :
*
* For typical values of slottime = 10 and persist = 63 ,
*
* Delay Probability
* - - - - - - - - - - - - - - - -
* 100 .25 = 25 %
* 200 .75 * .25 = 19 %
* 300 .75 * .75 * .25 = 14 %
* 400 .75 * .75 * .75 * .25 = 11 %
* 500 .75 * .75 * .75 * .75 * .25 = 8 %
* 600 .75 * .75 * .75 * .75 * .75 * .25 = 6 %
* 700 .75 * .75 * .75 * .75 * .75 * .75 * .25 = 4 %
* etc . . . .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Give up if we can't get a clear channel in a minute. */
2015-07-27 01:17:23 +00:00
/* That's a long time to wait for APRS. */
/* Might need to revisit some day for connected mode file transfers. */
2015-07-27 00:35:07 +00:00
# define WAIT_TIMEOUT_MS (60 * 1000)
# define WAIT_CHECK_EVERY_MS 10
2017-10-19 00:50:59 +00:00
static int wait_for_clear_channel ( int chan , int slottime , int persist , int fulldup )
2015-07-27 00:35:07 +00:00
{
2016-11-20 19:58:51 +00:00
int n = 0 ;
2015-07-27 01:17:23 +00:00
2017-10-19 00:50:59 +00:00
/*
* For dull duplex we skip the channel busy check and random wait .
* We still need to wait if operating in stereo and the other audio
* half is busy .
*/
if ( ! fulldup ) {
2015-07-27 01:17:23 +00:00
start_over_again :
2016-11-20 19:58:51 +00:00
while ( hdlc_rec_data_detect_any ( chan ) ) {
2015-07-27 00:35:07 +00:00
SLEEP_MS ( WAIT_CHECK_EVERY_MS ) ;
n + + ;
if ( n > ( WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS ) ) {
return 0 ;
}
}
2016-11-20 19:58:51 +00:00
//TODO: rethink dwait.
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
/*
* Added in version 1.2 - for transceivers that can ' t
* turn around fast enough when using squelch and VOX .
*/
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
if ( save_audio_config_p - > achan [ chan ] . dwait > 0 ) {
SLEEP_MS ( save_audio_config_p - > achan [ chan ] . dwait * 10 ) ;
2015-07-27 00:35:07 +00:00
}
2016-11-20 19:58:51 +00:00
if ( hdlc_rec_data_detect_any ( chan ) ) {
2015-07-27 01:17:23 +00:00
goto start_over_again ;
}
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
/*
* Wait random time .
* Proceed to transmit sooner if anything shows up in high priority queue .
*/
while ( tq_peek ( chan , TQ_PRIO_0_HI ) = = NULL ) {
int r ;
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
SLEEP_MS ( slottime * 10 ) ;
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
if ( hdlc_rec_data_detect_any ( chan ) ) {
goto start_over_again ;
2015-07-27 01:17:23 +00:00
}
2016-11-20 19:58:51 +00:00
r = rand ( ) & 0xff ;
if ( r < = persist ) {
break ;
}
2015-07-27 01:17:23 +00:00
}
2017-10-19 00:50:59 +00:00
}
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
/*
* This is to prevent two channels from transmitting at the same time
* thru a stereo audio device .
* We are not clever enough to combine two audio streams .
* They must go out one at a time .
* Documentation recommends using separate audio device for each channel rather than stereo .
* That also allows better use of multiple cores for receiving .
*/
// TODO: review this.
2015-07-27 00:35:07 +00:00
2016-11-20 19:58:51 +00:00
while ( ! dw_mutex_try_lock ( & ( audio_out_dev_mutex [ ACHAN2ADEV ( chan ) ] ) ) ) {
2015-07-27 01:17:23 +00:00
SLEEP_MS ( WAIT_CHECK_EVERY_MS ) ;
n + + ;
if ( n > ( WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS ) ) {
return 0 ;
}
2015-07-27 00:35:07 +00:00
}
2015-07-27 01:17:23 +00:00
return 1 ;
2015-07-27 00:35:07 +00:00
2015-07-27 01:17:23 +00:00
} /* end wait_for_clear_channel */
2015-07-27 00:35:07 +00:00
/* end xmit.c */