2018-01-20 15:03:47 +00:00
/* main function
*
* ( C ) 2018 by Andreas Eversberg < jolly @ eversberg . eu >
* All Rights Reserved
*
* 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 3 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/>.
*/
# include <stdio.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <math.h>
# include <errno.h>
# include <pthread.h>
# include "../libsample/sample.h"
# include "../libdebug/debug.h"
# include "../libsound/sound.h"
# include "../libclipper/clipper.h"
# include "radio.h"
# define CLIP_POINT 0.85
# define DC_CUTOFF 30.0 // Wikipedia: UKW-Rundfunk
# define STEREO_BW 15000.0
# define PILOT_FREQ 19000.0
# define PILOT_BW 5.0
2019-07-05 19:50:24 +00:00
int radio_init ( radio_t * radio , int latspl , int samplerate , const char * tx_wave_file , const char * rx_wave_file , const char * tx_audiodev , const char * rx_audiodev , enum modulation modulation , double bandwidth , double deviation , double modulation_index , double time_constant_us , double volume , int stereo , int rds , int rds2 )
2018-01-20 15:03:47 +00:00
{
int rc = - EINVAL ;
clipper_init ( CLIP_POINT ) ;
memset ( radio , 0 , sizeof ( * radio ) ) ;
radio - > latspl = latspl ;
2019-07-05 19:50:24 +00:00
radio - > volume = volume ;
2018-01-20 15:03:47 +00:00
radio - > stereo = stereo ;
radio - > rds = rds ;
radio - > rds2 = rds2 ;
radio - > tx_wave_file = tx_wave_file ;
radio - > modulation = modulation ;
radio - > signal_samplerate = samplerate ;
radio - > audio_bandwidth = bandwidth ;
switch ( radio - > modulation ) {
case MODULATION_FM :
radio - > fm_deviation = deviation ;
radio - > signal_bandwidth = deviation + bandwidth ;
if ( radio - > stereo ) {
radio - > signal_bandwidth = deviation + 53000.0 ;
radio - > audio_bandwidth = STEREO_BW ;
}
if ( radio - > rds )
radio - > signal_bandwidth = deviation + 60000.0 ;
if ( radio - > rds2 )
radio - > signal_bandwidth = deviation + 80000.0 ;
break ;
case MODULATION_AM_DSB :
case MODULATION_AM_USB :
case MODULATION_AM_LSB :
/* level is 1.0, which is full amplitude */
radio - > signal_bandwidth = bandwidth ;
break ;
case MODULATION_NONE :
PDEBUG ( DRADIO , DEBUG_ERROR , " Wrong modulation, plese fix! \n " ) ;
goto error ;
}
if ( tx_wave_file ) {
/* open wave file */
int _samplerate = 0 ;
radio - > tx_audio_channels = 0 ;
rc = wave_create_playback ( & radio - > wave_tx_play , tx_wave_file , & _samplerate , & radio - > tx_audio_channels , 1.0 ) ;
if ( rc < 0 ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to create WAVE playback instance! \n " ) ;
goto error ;
}
if ( radio - > tx_audio_channels ! = 1 & & radio - > tx_audio_channels ! = 2 )
{
PDEBUG ( DRADIO , DEBUG_ERROR , " WAVE file must have one or two channels! \n " ) ;
goto error ;
}
radio - > tx_audio_samplerate = _samplerate ;
radio - > tx_audio_mode = AUDIO_MODE_WAVEFILE ;
} else if ( tx_audiodev ) {
# ifdef HAVE_ALSA
/* open audio device */
radio - > tx_audio_samplerate = 48000 ;
radio - > tx_audio_channels = ( stereo ) ? 2 : 1 ;
2019-12-05 16:24:30 +00:00
radio - > tx_sound = sound_open ( tx_audiodev , NULL , NULL , NULL , radio - > tx_audio_channels , 0.0 , radio - > tx_audio_samplerate , radio - > latspl , 1.0 , 0.0 , 2.0 ) ;
2018-01-20 15:03:47 +00:00
if ( ! radio - > tx_sound ) {
rc = - EIO ;
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to open sound device! \n " ) ;
goto error ;
}
jitter_create ( & radio - > tx_dejitter [ 0 ] , radio - > tx_audio_samplerate / 5 ) ;
jitter_create ( & radio - > tx_dejitter [ 1 ] , radio - > tx_audio_samplerate / 5 ) ;
radio - > tx_audio_mode = AUDIO_MODE_AUDIODEV ;
# else
rc = - ENOTSUP ;
PDEBUG ( DRADIO , DEBUG_ERROR , " No sound card support compiled in! \n " ) ;
goto error ;
# endif
} else {
int i ;
double phase ;
/* use built-in sample sound */
radio - > tx_audio_samplerate = samplerate ;
radio - > tx_audio_channels = ( radio - > stereo ) ? 2 : 1 ;
radio - > testtone_length = radio - > tx_audio_samplerate ;
radio - > testtone [ 0 ] = calloc ( radio - > testtone_length * 2 , sizeof ( sample_t ) ) ;
if ( ! radio - > testtone [ 0 ] ) {
rc = - ENOMEM ;
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to allocate test sound buffer! \n " ) ;
goto error ;
}
radio - > testtone [ 1 ] = radio - > testtone [ 0 ] + radio - > testtone_length ;
/* generate tone */
phase = 2.0 * M_PI * 1000.0 / radio - > tx_audio_samplerate ;
if ( radio - > stereo ) {
for ( i = 0 ; i < radio - > testtone_length / 2 ; i + + ) {
radio - > testtone [ 0 ] [ i ] = sin ( i * phase ) ;
radio - > testtone [ 1 ] [ i ] = 0.0 ;
}
for ( ; i < radio - > testtone_length ; i + + ) {
radio - > testtone [ 0 ] [ i ] = 0.0 ;
radio - > testtone [ 1 ] [ i ] = sin ( i * phase ) ;
}
} else {
for ( i = 0 ; i < radio - > testtone_length ; i + + ) {
radio - > testtone [ 0 ] [ i ] = sin ( i * phase ) ;
}
}
radio - > tx_audio_mode = AUDIO_MODE_TESTTONE ;
}
if ( rx_wave_file ) {
/* open wave file */
2018-11-10 13:46:53 +00:00
radio - > rx_audio_samplerate = 48000 ;
2018-01-20 15:03:47 +00:00
radio - > rx_audio_channels = ( radio - > stereo ) ? 2 : 1 ;
rc = wave_create_record ( & radio - > wave_rx_rec , rx_wave_file , radio - > rx_audio_samplerate , radio - > rx_audio_channels , 1.0 ) ;
if ( rc < 0 ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to create WAVE record instance! \n " ) ;
goto error ;
}
radio - > rx_audio_mode = AUDIO_MODE_WAVEFILE ;
} else if ( rx_audiodev ) {
# ifdef HAVE_ALSA
/* open audio device */
radio - > rx_audio_samplerate = 48000 ;
radio - > rx_audio_channels = ( stereo ) ? 2 : 1 ;
/* check if we use same device */
if ( radio - > tx_sound & & ! strcmp ( tx_audiodev , rx_audiodev ) )
radio - > rx_sound = radio - > tx_sound ;
else
2019-12-05 16:24:30 +00:00
radio - > rx_sound = sound_open ( rx_audiodev , NULL , NULL , NULL , radio - > rx_audio_channels , 0.0 , radio - > rx_audio_samplerate , radio - > latspl , 1.0 , 0.0 , 2.0 ) ;
2018-01-20 15:03:47 +00:00
if ( ! radio - > rx_sound ) {
rc = - EIO ;
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to open sound device! \n " ) ;
goto error ;
}
jitter_create ( & radio - > rx_dejitter [ 0 ] , radio - > rx_audio_samplerate / 5 ) ;
jitter_create ( & radio - > rx_dejitter [ 1 ] , radio - > rx_audio_samplerate / 5 ) ;
radio - > rx_audio_mode = AUDIO_MODE_AUDIODEV ;
# else
rc = - ENOTSUP ;
PDEBUG ( DRADIO , DEBUG_ERROR , " No sound card support compiled in! \n " ) ;
goto error ;
# endif
2018-11-10 13:46:53 +00:00
#if 0
2018-08-25 08:22:55 +00:00
} else {
rc = - ENOTSUP ;
PDEBUG ( DRADIO , DEBUG_ERROR , " No RX audio sink is selected, try \" --audio-device default \" ! \n " ) ;
goto error ;
2018-11-10 13:46:53 +00:00
# endif
2018-01-20 15:03:47 +00:00
}
/* check if sample rate is too low */
if ( radio - > tx_audio_samplerate > radio - > signal_samplerate ) {
rc = - EINVAL ;
PDEBUG ( DRADIO , DEBUG_ERROR , " You have selected a signal processing sample rate of %.0f. Your audio sample rate is %.0f. \n " , radio - > signal_samplerate , radio - > tx_audio_samplerate ) ;
PDEBUG ( DRADIO , DEBUG_ERROR , " Please select a sample rate that is higher or equal the audio sample rate! \n " ) ;
goto error ;
}
if ( radio - > rx_audio_samplerate > radio - > signal_samplerate ) {
rc = - EINVAL ;
PDEBUG ( DRADIO , DEBUG_ERROR , " You have selected a signal processing sample rate of %.0f. Your audio sample rate is %.0f. \n " , radio - > signal_samplerate , radio - > rx_audio_samplerate ) ;
PDEBUG ( DRADIO , DEBUG_ERROR , " Please select a sample rate that is higher or equal the audio sample rate! \n " ) ;
goto error ;
}
if ( radio - > signal_samplerate < radio - > signal_bandwidth * 2 / 0.75 ) {
rc = - EINVAL ;
PDEBUG ( DRADIO , DEBUG_ERROR , " You have selected a signal processing sample rate of %.0f. Your signal's bandwidth %.0f. \n " , radio - > signal_samplerate , radio - > signal_bandwidth ) ;
PDEBUG ( DRADIO , DEBUG_ERROR , " Your signal processing sample rate must be at least one third greater than the signal's double bandwidth. Use at least %.0f. \n " , radio - > signal_bandwidth * 2.0 / 0.75 ) ;
goto error ;
}
iir_highpass_init ( & radio - > tx_dc_removal [ 0 ] , DC_CUTOFF , radio - > tx_audio_samplerate , 1 ) ;
iir_highpass_init ( & radio - > tx_dc_removal [ 1 ] , DC_CUTOFF , radio - > tx_audio_samplerate , 1 ) ;
/* stereo pilot tone phase */
radio - > pilot_phasestep = 2.0 * M_PI * PILOT_FREQ / radio - > signal_samplerate ;
/* stere decoding filters */
iir_lowpass_init ( & radio - > rx_lp_pilot_I , PILOT_BW , radio - > signal_samplerate , 2 ) ;
iir_lowpass_init ( & radio - > rx_lp_pilot_Q , PILOT_BW , radio - > signal_samplerate , 2 ) ;
iir_lowpass_init ( & radio - > rx_lp_sum , STEREO_BW , radio - > signal_samplerate , 2 ) ;
iir_lowpass_init ( & radio - > rx_lp_diff , STEREO_BW , radio - > signal_samplerate , 2 ) ;
/* init sample rate conversion, use complete bandwidth for resample filter */
rc = init_samplerate ( & radio - > tx_resampler [ 0 ] , radio - > tx_audio_samplerate , radio - > signal_samplerate , radio - > tx_audio_samplerate / 2.0 ) ;
if ( rc < 0 )
goto error ;
rc = init_samplerate ( & radio - > tx_resampler [ 1 ] , radio - > tx_audio_samplerate , radio - > signal_samplerate , radio - > tx_audio_samplerate / 2.0 ) ;
if ( rc < 0 )
goto error ;
rc = init_samplerate ( & radio - > rx_resampler [ 0 ] , radio - > rx_audio_samplerate , radio - > signal_samplerate , radio - > rx_audio_samplerate / 2.0 ) ;
if ( rc < 0 )
goto error ;
rc = init_samplerate ( & radio - > rx_resampler [ 1 ] , radio - > rx_audio_samplerate , radio - > signal_samplerate , radio - > rx_audio_samplerate / 2.0 ) ;
if ( rc < 0 )
goto error ;
/* init filters (using signal sample rate) */
switch ( radio - > modulation ) {
case MODULATION_FM :
2019-12-22 05:46:31 +00:00
if ( time_constant_us > 0.0 ) {
radio - > emphasis = 1 ;
/* time constant */
PDEBUG ( DRADIO , DEBUG_INFO , " Using emphasis cut-off at %.0f Hz. \n " , timeconstant2cutoff ( time_constant_us ) ) ;
rc = init_emphasis ( & radio - > fm_emphasis [ 0 ] , radio - > signal_samplerate , timeconstant2cutoff ( time_constant_us ) , DC_CUTOFF , radio - > audio_bandwidth ) ;
if ( rc < 0 )
goto error ;
rc = init_emphasis ( & radio - > fm_emphasis [ 1 ] , radio - > signal_samplerate , timeconstant2cutoff ( time_constant_us ) , DC_CUTOFF , radio - > audio_bandwidth ) ;
if ( rc < 0 )
goto error ;
}
2018-01-20 15:03:47 +00:00
rc = fm_mod_init ( & radio - > fm_mod , radio - > signal_samplerate , 0.0 , 1.0 ) ;
if ( rc < 0 )
goto error ;
rc = fm_demod_init ( & radio - > fm_demod , radio - > signal_samplerate , 0.0 , 2 * radio - > signal_bandwidth ) ;
if ( rc < 0 )
goto error ;
break ;
case MODULATION_AM_DSB :
iir_lowpass_init ( & radio - > tx_am_bw_limit , radio - > audio_bandwidth , radio - > signal_samplerate , 1 ) ;
/* modulation index 0.0 = no envelope, bias 1.0
* modulation index 1.0 = envelope + - 0.5 , bias 0.5
* modulation index 0.5 = envelope + - 0.25 , bias 0.75
*/
double gain = modulation_index / 2.0 ;
double bias = 1.0 - gain ;
rc = am_mod_init ( & radio - > am_mod , radio - > signal_samplerate , 0.0 , gain , bias ) ;
if ( rc < 0 )
goto error ;
rc = am_demod_init ( & radio - > am_demod , radio - > signal_samplerate , 0.0 , radio - > signal_bandwidth , 1.0 / modulation_index ) ;
if ( rc < 0 )
goto error ;
break ;
case MODULATION_AM_USB :
iir_lowpass_init ( & radio - > tx_am_bw_limit , radio - > audio_bandwidth , radio - > signal_samplerate , 1 ) ;
rc = am_mod_init ( & radio - > am_mod , radio - > signal_samplerate , 0.0 , 1.0 , 0.0 ) ;
if ( rc < 0 )
goto error ;
break ;
case MODULATION_AM_LSB :
iir_lowpass_init ( & radio - > tx_am_bw_limit , radio - > audio_bandwidth , radio - > signal_samplerate , 1 ) ;
rc = am_mod_init ( & radio - > am_mod , radio - > signal_samplerate , 0.0 , 1.0 , 0.0 ) ;
if ( rc < 0 )
goto error ;
break ;
default :
break ;
}
if ( radio - > tx_audio_mode )
PDEBUG ( DRADIO , DEBUG_INFO , " Bandwidth of audio source is %.0f Hz. \n " , radio - > tx_audio_samplerate / 2.0 ) ;
if ( radio - > rx_audio_mode )
PDEBUG ( DRADIO , DEBUG_INFO , " Bandwidth of audio sink is %.0f Hz. \n " , radio - > rx_audio_samplerate / 2.0 ) ;
PDEBUG ( DRADIO , DEBUG_INFO , " Bandwidth of audio signal is %.0f Hz. \n " , radio - > audio_bandwidth ) ;
PDEBUG ( DRADIO , DEBUG_INFO , " Bandwidth of modulated signal is %.0f Hz. \n " , radio - > signal_bandwidth ) ;
if ( radio - > tx_audio_mode )
PDEBUG ( DRADIO , DEBUG_INFO , " Sample rate of audio source is %.0f Hz. \n " , radio - > tx_audio_samplerate ) ;
if ( radio - > rx_audio_mode )
PDEBUG ( DRADIO , DEBUG_INFO , " Sample rate of audio sink is %.0f Hz. \n " , radio - > rx_audio_samplerate ) ;
PDEBUG ( DRADIO , DEBUG_INFO , " Sample rate of signal is %.0f Hz. \n " , radio - > signal_samplerate ) ;
/* one or two audio channels */
if ( radio - > tx_audio_channels ! = 1 & & radio - > tx_audio_channels ! = 2 )
{
PDEBUG ( DRADIO , DEBUG_ERROR , " Wrong number of audio channels, please fix! \n " ) ;
goto error ;
}
/* audio buffers: how many sample for audio (rounded down) */
int tx_size = ( int ) ( ( double ) latspl / radio - > tx_resampler [ 0 ] . factor ) ;
int rx_size = ( int ) ( ( double ) latspl / radio - > rx_resampler [ 0 ] . factor ) ;
if ( tx_size > rx_size )
radio - > audio_buffer_size = tx_size ;
else
radio - > audio_buffer_size = rx_size ;
radio - > audio_buffer = calloc ( radio - > audio_buffer_size * 2 , sizeof ( * radio - > audio_buffer ) ) ;
if ( ! radio - > audio_buffer ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " No memory!! \n " ) ;
rc = - ENOMEM ;
goto error ;
}
/* signal buffers */
radio - > signal_buffer_size = latspl ;
radio - > signal_buffer = calloc ( radio - > signal_buffer_size * 3 , sizeof ( * radio - > signal_buffer ) ) ;
radio - > signal_power_buffer = calloc ( radio - > signal_buffer_size , sizeof ( * radio - > signal_power_buffer ) ) ;
if ( ! radio - > signal_buffer | | ! radio - > signal_power_buffer ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " No memory!! \n " ) ;
rc = - ENOMEM ;
goto error ;
}
/* termporary I/Q/carrier buffers, used while demodulating */
radio - > I_buffer = calloc ( latspl , sizeof ( * radio - > I_buffer ) ) ;
radio - > Q_buffer = calloc ( latspl , sizeof ( * radio - > Q_buffer ) ) ;
radio - > carrier_buffer = calloc ( latspl , sizeof ( * radio - > carrier_buffer ) ) ;
if ( ! radio - > I_buffer | | ! radio - > Q_buffer | | ! radio - > carrier_buffer ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " No memory!! \n " ) ;
rc = - ENOMEM ;
goto error ;
}
return 0 ;
error :
radio_exit ( radio ) ;
return rc ;
}
void radio_exit ( radio_t * radio )
{
if ( radio - > audio_buffer ) {
free ( radio - > audio_buffer ) ;
radio - > audio_buffer = NULL ;
}
if ( radio - > signal_buffer ) {
free ( radio - > signal_buffer ) ;
radio - > signal_buffer = NULL ;
}
if ( radio - > signal_power_buffer ) {
free ( radio - > signal_power_buffer ) ;
radio - > signal_power_buffer = NULL ;
}
if ( radio - > I_buffer ) {
free ( radio - > I_buffer ) ;
radio - > I_buffer = NULL ;
}
if ( radio - > Q_buffer ) {
free ( radio - > Q_buffer ) ;
radio - > Q_buffer = NULL ;
}
if ( radio - > carrier_buffer ) {
free ( radio - > carrier_buffer ) ;
radio - > carrier_buffer = NULL ;
}
if ( radio - > tx_audio_mode = = AUDIO_MODE_WAVEFILE ) {
wave_destroy_playback ( & radio - > wave_tx_play ) ;
radio - > tx_audio_mode = AUDIO_MODE_NONE ;
}
if ( radio - > rx_audio_mode = = AUDIO_MODE_WAVEFILE ) {
wave_destroy_record ( & radio - > wave_rx_rec ) ;
radio - > rx_audio_mode = AUDIO_MODE_NONE ;
}
# ifdef HAVE_ALSA
if ( radio - > tx_sound ) {
sound_close ( radio - > tx_sound ) ;
/* if same device was used */
if ( radio - > tx_sound = = radio - > rx_sound )
radio - > rx_sound = NULL ;
radio - > tx_sound = NULL ;
radio - > tx_audio_mode = AUDIO_MODE_NONE ;
}
if ( radio - > rx_sound ) {
sound_close ( radio - > rx_sound ) ;
radio - > rx_sound = NULL ;
radio - > rx_audio_mode = AUDIO_MODE_NONE ;
}
# endif
jitter_destroy ( & radio - > tx_dejitter [ 0 ] ) ;
jitter_destroy ( & radio - > tx_dejitter [ 1 ] ) ;
jitter_destroy ( & radio - > rx_dejitter [ 0 ] ) ;
jitter_destroy ( & radio - > rx_dejitter [ 1 ] ) ;
if ( radio - > tx_audio_mode = = AUDIO_MODE_TESTTONE ) {
free ( radio - > testtone [ 0 ] ) ;
radio - > tx_audio_mode = AUDIO_MODE_NONE ;
}
if ( radio - > modulation = = MODULATION_FM )
fm_mod_exit ( & radio - > fm_mod ) ;
else
am_mod_exit ( & radio - > am_mod ) ;
}
int radio_start ( radio_t __attribute__ ( ( unused ) ) * radio )
{
int rc = 0 ;
# ifdef HAVE_ALSA
/* start rx sound */
if ( radio - > rx_sound )
rc = sound_start ( radio - > rx_sound ) ;
/* start tx sound, if different device */
if ( radio - > tx_sound & & radio - > tx_sound ! = radio - > rx_sound )
rc = sound_start ( radio - > tx_sound ) ;
# endif
return rc ;
}
int radio_tx ( radio_t * radio , float * baseband , int signal_num )
{
int i ;
int __attribute__ ( ( unused ) ) rc ;
int audio_num ;
sample_t * audio_samples [ 2 ] ;
sample_t * signal_samples [ 3 ] ;
uint8_t * signal_power ;
if ( signal_num > radio - > latspl ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " signal_num > latspl, please fix!. \n " ) ;
abort ( ) ;
}
/* audio buffers: how many sample for audio (rounded down) */
audio_num = ( int ) ( ( double ) signal_num / radio - > tx_resampler [ 0 ] . factor ) ;
if ( audio_num > radio - > audio_buffer_size ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " audio_num > audio_buffer_size, please fix!. \n " ) ;
abort ( ) ;
}
audio_samples [ 0 ] = radio - > audio_buffer ;
audio_samples [ 1 ] = radio - > audio_buffer + radio - > audio_buffer_size ;
/* signal buffers: a bit more samples to be safe */
signal_num = ( int ) ( ( double ) audio_num * radio - > tx_resampler [ 0 ] . factor + 0.5 ) + 10 ;
if ( signal_num > radio - > signal_buffer_size ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " signal_num > signal_buffer_size, please fix!. \n " ) ;
abort ( ) ;
}
signal_samples [ 0 ] = radio - > signal_buffer ;
signal_samples [ 1 ] = radio - > signal_buffer + radio - > signal_buffer_size ;
signal_samples [ 2 ] = radio - > signal_buffer + radio - > signal_buffer_size * 2 ;
signal_power = radio - > signal_power_buffer ;
/* get audio to be sent */
switch ( radio - > tx_audio_mode ) {
case AUDIO_MODE_WAVEFILE :
wave_read ( & radio - > wave_tx_play , audio_samples , audio_num ) ;
2019-07-05 19:50:24 +00:00
2018-01-20 15:03:47 +00:00
if ( ! radio - > wave_tx_play . left ) {
int rc ;
int _samplerate = 0 ;
wave_destroy_playback ( & radio - > wave_tx_play ) ;
rc = wave_create_playback ( & radio - > wave_tx_play , radio - > tx_wave_file , & _samplerate , & radio - > tx_audio_channels , 1.0 ) ;
if ( rc < 0 ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to re-open wave file. \n " ) ;
return rc ;
}
}
break ;
# ifdef HAVE_ALSA
case AUDIO_MODE_AUDIODEV :
rc = sound_read ( radio - > tx_sound , audio_samples , radio - > audio_buffer_size , radio - > tx_audio_channels , NULL ) ;
if ( rc < 0 ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to read from sound device (rc = %d)! \n " , audio_num ) ;
if ( rc = = - EPIPE )
PDEBUG ( DRADIO , DEBUG_ERROR , " Trying to recover. \n " ) ;
else
return 0 ;
}
jitter_save ( & radio - > tx_dejitter [ 0 ] , audio_samples [ 0 ] , rc ) ;
jitter_load ( & radio - > tx_dejitter [ 0 ] , audio_samples [ 0 ] , audio_num ) ;
if ( radio - > tx_audio_channels = = 2 ) {
jitter_save ( & radio - > tx_dejitter [ 1 ] , audio_samples [ 1 ] , rc ) ;
jitter_load ( & radio - > tx_dejitter [ 1 ] , audio_samples [ 1 ] , audio_num ) ;
}
break ;
# endif
case AUDIO_MODE_TESTTONE :
for ( i = 0 ; i < audio_num ; i + + ) {
audio_samples [ 0 ] [ i ] = radio - > testtone [ 0 ] [ radio - > testtone_pos ] ;
audio_samples [ 1 ] [ i ] = radio - > testtone [ 1 ] [ radio - > testtone_pos ] ;
radio - > testtone_pos = ( radio - > testtone_pos + 1 ) % radio - > testtone_length ;
}
break ;
default :
PDEBUG ( DRADIO , DEBUG_ERROR , " Wrong audio mode, plese fix! \n " ) ;
return - EINVAL ;
}
/* convert mono/stereo, generate differential signal */
if ( radio - > stereo & & radio - > tx_audio_channels = = 1 ) {
/* mono to stereo: sum is 90%, differential signal is 0 */
for ( i = 0 ; i < audio_num ; i + + ) {
audio_samples [ 0 ] [ i ] = 0.9 ;
audio_samples [ 1 ] [ i ] = 0.0 ;
}
}
if ( radio - > stereo & & radio - > tx_audio_channels = = 2 ) {
/* stereo: sum is 90%, diffential is 90% */
double left , right ;
for ( i = 0 ; i < audio_num ; i + + ) {
left = audio_samples [ 0 ] [ i ] ;
right = audio_samples [ 1 ] [ i ] ;
audio_samples [ 0 ] [ i ] = ( left + right ) * 0.45 ;
audio_samples [ 1 ] [ i ] = ( left - right ) * 0.45 ;
}
}
if ( ! radio - > stereo & & radio - > tx_audio_channels = = 2 ) {
/* stereo to mono: sum both channel */
for ( i = 0 ; i < audio_num ; i + + )
audio_samples [ 0 ] [ i ] = ( audio_samples [ 0 ] [ i ] + audio_samples [ 1 ] [ i ] ) / 2.0 ;
}
/* remove DC */
iir_process ( & radio - > tx_dc_removal [ 0 ] , audio_samples [ 0 ] , audio_num ) ;
if ( radio - > stereo )
iir_process ( & radio - > tx_dc_removal [ 1 ] , audio_samples [ 1 ] , audio_num ) ;
2019-07-05 19:50:24 +00:00
/* gain volume */
if ( radio - > volume ! = 1.0 ) {
for ( i = 0 ; i < audio_num ; i + + )
audio_samples [ 0 ] [ i ] * = radio - > volume ;
if ( radio - > stereo ) {
for ( i = 0 ; i < audio_num ; i + + )
audio_samples [ 1 ] [ i ] * = radio - > volume ;
}
}
2018-01-20 15:03:47 +00:00
/* upsample */
signal_num = samplerate_upsample ( & radio - > tx_resampler [ 0 ] , audio_samples [ 0 ] , audio_num , signal_samples [ 0 ] ) ;
if ( radio - > stereo )
samplerate_upsample ( & radio - > tx_resampler [ 1 ] , audio_samples [ 1 ] , audio_num , signal_samples [ 1 ] ) ;
/* prepare baseband */
memset ( baseband , 0 , sizeof ( float ) * 2 * signal_num ) ;
2019-12-05 20:57:23 +00:00
memset ( signal_power , 1 , signal_num ) ;
2018-01-20 15:03:47 +00:00
/* filter audio (remove DC, remove high frequencies, pre-emphasis)
* and modulate */
switch ( radio - > modulation ) {
case MODULATION_FM :
2019-12-22 05:46:31 +00:00
if ( radio - > emphasis )
pre_emphasis ( & radio - > fm_emphasis [ 0 ] , signal_samples [ 0 ] , signal_num ) ;
2018-01-20 15:03:47 +00:00
clipper_process ( signal_samples [ 0 ] , signal_num ) ;
if ( radio - > stereo ) {
2019-12-22 05:46:31 +00:00
if ( radio - > emphasis )
pre_emphasis ( & radio - > fm_emphasis [ 1 ] , signal_samples [ 1 ] , signal_num ) ;
2018-01-20 15:03:47 +00:00
clipper_process ( signal_samples [ 1 ] , signal_num ) ;
/* add pilot tone */
double phasestep = radio - > pilot_phasestep ;
double phase = radio - > tx_pilot_phase ;
for ( i = 0 ; i < signal_num ; i + + ) {
signal_samples [ 0 ] [ i ] + = sin ( phase ) * 0.1 ;
signal_samples [ 0 ] [ i ] + = signal_samples [ 1 ] [ i ] * sin ( phase * 2 ) ;
phase + = phasestep ;
if ( phase > = 2.0 * M_PI )
phase - = 2.0 * M_PI ;
}
radio - > tx_pilot_phase = phase ;
}
for ( i = 0 ; i < signal_num ; i + + )
signal_samples [ 0 ] [ i ] * = radio - > fm_deviation ;
fm_modulate_complex ( & radio - > fm_mod , signal_samples [ 0 ] , signal_power , signal_num , baseband ) ;
break ;
case MODULATION_AM_DSB :
/* also clip to prevent overshooting after audio filtering */
clipper_process ( signal_samples [ 0 ] , signal_num ) ;
iir_process ( & radio - > tx_am_bw_limit , signal_samples [ 0 ] , signal_num ) ;
2019-12-05 20:57:23 +00:00
am_modulate_complex ( & radio - > am_mod , signal_samples [ 0 ] , signal_power , signal_num , baseband ) ;
2018-01-20 15:03:47 +00:00
break ;
case MODULATION_AM_USB :
case MODULATION_AM_LSB :
/* also clip to prevent overshooting after audio filtering */
clipper_process ( signal_samples [ 0 ] , signal_num ) ;
iir_process ( & radio - > tx_am_bw_limit , signal_samples [ 0 ] , signal_num ) ;
2019-12-05 20:57:23 +00:00
am_modulate_complex ( & radio - > am_mod , signal_samples [ 0 ] , signal_power , signal_num , baseband ) ;
2018-01-20 15:03:47 +00:00
break ;
default :
break ;
}
return signal_num ;
}
int radio_rx ( radio_t * radio , float * baseband , int signal_num )
{
int i ;
int audio_num ;
sample_t * samples [ 3 ] ;
double p ;
if ( signal_num > radio - > latspl ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " signal_num > latspl, please fix!. \n " ) ;
abort ( ) ;
}
if ( signal_num > radio - > signal_buffer_size ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " signal_num > signal_buffer_size, please fix!. \n " ) ;
abort ( ) ;
}
samples [ 0 ] = radio - > signal_buffer ;
samples [ 1 ] = radio - > signal_buffer + radio - > signal_buffer_size ;
samples [ 2 ] = radio - > signal_buffer + radio - > signal_buffer_size * 2 ;
switch ( radio - > modulation ) {
case MODULATION_FM :
fm_demodulate_complex ( & radio - > fm_demod , samples [ 0 ] , signal_num , baseband , radio - > I_buffer , radio - > Q_buffer ) ;
for ( i = 0 ; i < signal_num ; i + + )
samples [ 0 ] [ i ] / = radio - > fm_deviation ;
if ( radio - > stereo ) {
/* filter pilot tone */
p = radio - > rx_pilot_phase ; /* don't increment in radio structure, will be done later */
for ( i = 0 ; i < signal_num ; i + + ) {
samples [ 1 ] [ i ] = samples [ 0 ] [ i ] * cos ( p ) ; /* I */
samples [ 2 ] [ i ] = samples [ 0 ] [ i ] * sin ( p ) ; /* Q */
p + = radio - > pilot_phasestep ;
if ( p > = 2.0 * M_PI )
p - = 2.0 * M_PI ;
}
iir_process ( & radio - > rx_lp_pilot_I , samples [ 1 ] , signal_num ) ;
iir_process ( & radio - > rx_lp_pilot_Q , samples [ 2 ] , signal_num ) ;
/* mix pilot tone (double phase) with differential signal */
for ( i = 0 ; i < signal_num ; i + + ) {
p = atan2 ( samples [ 2 ] [ i ] , samples [ 1 ] [ i ] ) ;
2019-07-15 19:14:52 +00:00
/* subtract measured phase difference (use double amplitude, because we filter later) */
2018-01-20 15:03:47 +00:00
samples [ 1 ] [ i ] = samples [ 0 ] [ i ] * sin ( ( radio - > rx_pilot_phase - p ) * 2.0 ) * 2.0 ;
radio - > rx_pilot_phase + = radio - > pilot_phasestep ;
if ( radio - > rx_pilot_phase > = 2.0 * M_PI )
radio - > rx_pilot_phase - = 2.0 * M_PI ;
}
/* filter to match bandwidth */
iir_process ( & radio - > rx_lp_sum , samples [ 0 ] , signal_num ) ;
iir_process ( & radio - > rx_lp_diff , samples [ 1 ] , signal_num ) ;
}
2019-12-22 05:46:31 +00:00
if ( radio - > emphasis ) {
dc_filter ( & radio - > fm_emphasis [ 0 ] , samples [ 0 ] , signal_num ) ;
de_emphasis ( & radio - > fm_emphasis [ 0 ] , samples [ 0 ] , signal_num ) ;
if ( radio - > stereo ) {
dc_filter ( & radio - > fm_emphasis [ 1 ] , samples [ 1 ] , signal_num ) ;
de_emphasis ( & radio - > fm_emphasis [ 1 ] , samples [ 1 ] , signal_num ) ;
}
2018-01-20 15:03:47 +00:00
}
break ;
case MODULATION_AM_DSB :
am_demodulate_complex ( & radio - > am_demod , samples [ 0 ] , signal_num , baseband , radio - > I_buffer , radio - > Q_buffer , radio - > carrier_buffer ) ;
break ;
case MODULATION_AM_USB :
case MODULATION_AM_LSB :
am_demodulate_complex ( & radio - > am_demod , samples [ 0 ] , signal_num , baseband , radio - > I_buffer , radio - > Q_buffer , radio - > carrier_buffer ) ;
break ;
default :
break ;
}
/* downsample */
audio_num = samplerate_downsample ( & radio - > rx_resampler [ 0 ] , samples [ 0 ] , signal_num ) ;
if ( radio - > stereo )
samplerate_downsample ( & radio - > rx_resampler [ 1 ] , samples [ 1 ] , signal_num ) ;
2019-07-05 19:50:24 +00:00
/* dampen volume */
if ( radio - > volume ! = 1.0 ) {
for ( i = 0 ; i < audio_num ; i + + )
samples [ 0 ] [ i ] / = radio - > volume ;
if ( radio - > stereo ) {
for ( i = 0 ; i < audio_num ; i + + )
samples [ 1 ] [ i ] / = radio - > volume ;
}
}
2018-01-20 15:03:47 +00:00
/* convert mono/stereo, (from differential signal) */
if ( radio - > stereo & & radio - > rx_audio_channels = = 1 ) {
/* stereo to mono */
for ( i = 0 ; i < audio_num ; i + + ) {
samples [ 0 ] [ i ] = ( samples [ 0 ] [ i ] + samples [ 1 ] [ i ] ) / 2.0 ;
}
}
if ( radio - > stereo & & radio - > rx_audio_channels = = 2 ) {
/* stereo from differential */
double sum , diff ;
for ( i = 0 ; i < audio_num ; i + + ) {
sum = samples [ 0 ] [ i ] ;
diff = samples [ 1 ] [ i ] ;
samples [ 0 ] [ i ] = sum + diff / 2.0 ;
samples [ 1 ] [ i ] = sum - diff / 2.0 ;
}
}
if ( ! radio - > stereo & & radio - > rx_audio_channels = = 2 ) {
/* mono to stereo: clone channel */
for ( i = 0 ; i < audio_num ; i + + )
samples [ 1 ] [ i ] = samples [ 0 ] [ i ] ;
}
/* store received audio */
switch ( radio - > rx_audio_mode ) {
case AUDIO_MODE_WAVEFILE :
wave_write ( & radio - > wave_rx_rec , samples , audio_num ) ;
break ;
# ifdef HAVE_ALSA
case AUDIO_MODE_AUDIODEV :
jitter_save ( & radio - > rx_dejitter [ 0 ] , samples [ 0 ] , audio_num ) ;
if ( radio - > rx_audio_channels = = 2 )
jitter_save ( & radio - > rx_dejitter [ 1 ] , samples [ 1 ] , audio_num ) ;
audio_num = sound_get_tosend ( radio - > rx_sound , radio - > signal_buffer_size ) ;
jitter_load ( & radio - > rx_dejitter [ 0 ] , samples [ 0 ] , audio_num ) ;
if ( radio - > rx_audio_channels = = 2 )
jitter_load ( & radio - > rx_dejitter [ 1 ] , samples [ 1 ] , audio_num ) ;
audio_num = sound_write ( radio - > rx_sound , samples , NULL , audio_num , NULL , NULL , radio - > rx_audio_channels ) ;
if ( audio_num < 0 ) {
PDEBUG ( DRADIO , DEBUG_ERROR , " Failed to write to sound device (rc = %d)! \n " , audio_num ) ;
if ( audio_num = = - EPIPE )
PDEBUG ( DRADIO , DEBUG_ERROR , " Trying to recover. \n " ) ;
else
return 0 ;
}
break ;
# endif
default :
PDEBUG ( DRADIO , DEBUG_ERROR , " Wrong audio mode, plese fix! \n " ) ;
return - EINVAL ;
}
return signal_num ;
}