2017-01-04 13:21:49 +00:00
/* SDR processing
*
* ( C ) 2017 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/>.
*/
2017-08-30 15:42:49 +00:00
enum paging_signal ;
2017-01-07 09:33:13 +00:00
# include <stdio.h>
2017-01-04 13:21:49 +00:00
# include <stdlib.h>
# include <stdint.h>
# include <string.h>
2017-02-25 06:09:53 +00:00
# include <errno.h>
2017-01-04 13:21:49 +00:00
# include <math.h>
2017-08-16 16:24:57 +00:00
# define __USE_GNU
2017-07-24 08:09:05 +00:00
# include <pthread.h>
# include <unistd.h>
2017-11-18 07:06:06 +00:00
# include "../libsample/sample.h"
2017-11-16 18:18:42 +00:00
# include "../libfm/fm.h"
2019-12-05 16:24:30 +00:00
# include "../libam/am.h"
2017-11-13 19:00:52 +00:00
# include "../libtimer/timer.h"
2017-11-18 07:33:07 +00:00
# include "../libmobile/sender.h"
2017-08-30 15:42:49 +00:00
# include "sdr_config.h"
# include "sdr.h"
2017-01-04 13:21:49 +00:00
# ifdef HAVE_UHD
# include "uhd.h"
# endif
2017-02-18 12:51:26 +00:00
# ifdef HAVE_SOAPY
# include "soapy.h"
# endif
2017-11-18 07:58:57 +00:00
# include "../libdebug/debug.h"
2017-01-04 13:21:49 +00:00
2017-07-24 08:09:05 +00:00
/* enable to debug buffer handling */
//#define DEBUG_BUFFER
2017-09-24 13:59:24 +00:00
/* enable to test without oversampling filter */
//#define DISABLE_FILTER
2017-10-16 16:26:13 +00:00
/* usable bandwidth of IQ rate, because no filter is perfect */
# define USABLE_BANDWIDTH 0.75
2017-09-24 13:59:24 +00:00
2018-08-25 06:13:16 +00:00
/* limit the IQ level to prevent IIR filter from exceeding range of -1 .. 1 */
# define LIMIT_IQ_LEVEL 0.95
2017-11-17 21:51:18 +00:00
int sdr_rx_overflow = 0 ;
2017-08-30 15:42:49 +00:00
typedef struct sdr_thread {
int use ;
volatile int running , exit ; /* flags to control exit of threads */
int buffer_size ;
volatile float * buffer ;
float * buffer2 ;
volatile int in , out ; /* in and out pointers (atomic, so no locking required) */
int max_fill ; /* measure maximum buffer fill */
double max_fill_timer ; /* timer to display/reset maximum fill */
2017-09-24 13:59:24 +00:00
iir_filter_t lp [ 2 ] ; /* filter for upsample/downsample IQ data */
2017-08-30 15:42:49 +00:00
} sdr_thread_t ;
2017-01-04 13:21:49 +00:00
typedef struct sdr_chan {
2017-02-09 18:24:09 +00:00
double tx_frequency ; /* frequency used */
double rx_frequency ; /* frequency used */
2019-12-05 16:24:30 +00:00
int am ; /* use AM instead of FM */
fm_mod_t fm_mod ; /* modulator instance */
fm_demod_t fm_demod ; /* demodulator instance */
am_mod_t am_mod ; /* modulator instance */
am_demod_t am_demod ; /* demodulator instance */
2017-09-25 16:46:50 +00:00
dispmeasparam_t * dmp_rf_level ;
dispmeasparam_t * dmp_freq_offset ;
dispmeasparam_t * dmp_deviation ;
2017-01-04 13:21:49 +00:00
} sdr_chan_t ;
typedef struct sdr {
2017-08-30 15:42:49 +00:00
int threads ; /* use threads */
int oversample ; /* oversample IQ rate */
sdr_thread_t thread_read ,
thread_write ;
2017-02-09 18:24:09 +00:00
sdr_chan_t * chan ; /* settings for all channels */
int paging_channel ; /* if set, points to paging channel */
sdr_chan_t paging_chan ; /* settings for extra paging channel */
int channels ; /* number of frequencies */
double amplitude ; /* amplitude of each carrier */
2017-07-24 08:09:05 +00:00
int samplerate ; /* sample rate of audio data */
2017-08-30 15:42:49 +00:00
int latspl ; /* latency in audio samples */
2017-02-09 18:24:09 +00:00
wave_rec_t wave_rx_rec ;
wave_rec_t wave_tx_rec ;
wave_play_t wave_rx_play ;
2017-03-16 17:06:45 +00:00
wave_play_t wave_tx_play ;
2019-12-05 16:24:30 +00:00
float * modbuff ; /* buffer for transmodulation */
2017-08-27 08:49:19 +00:00
sample_t * modbuff_I ;
sample_t * modbuff_Q ;
2019-12-05 16:24:30 +00:00
sample_t * modbuff_carrier ;
2017-08-27 08:49:19 +00:00
sample_t * wavespl0 ; /* sample buffer for wave generation */
sample_t * wavespl1 ;
2017-01-04 13:21:49 +00:00
} sdr_t ;
2019-05-30 16:20:25 +00:00
static void show_spectrum ( const char * direction , double halfbandwidth , double center , double * frequency , double paging_frequency , int num )
{
char text [ 80 ] ;
int i , x ;
memset ( text , ' ' , 79 ) ;
text [ 79 ] = ' \0 ' ;
// FIXME: better solution
if ( num > 9 )
num = 9 ;
for ( i = 0 ; i < num ; i + + ) {
x = ( frequency [ i ] - center ) / halfbandwidth * 39.0 + 39.5 ;
if ( x > = 0 & & x < 79 )
text [ x ] = ' 1 ' + i ;
}
if ( paging_frequency ) {
x = ( paging_frequency - center ) / halfbandwidth * 39.0 + 39.5 ;
if ( x > = 0 & & x < 79 )
text [ x ] = ' P ' ;
}
PDEBUG ( DSDR , DEBUG_INFO , " %s Spectrum: \n %s \n ---------------------------------------+--------------------------------------- \n " , direction , text ) ;
for ( i = 0 ; i < num ; i + + )
PDEBUG ( DSDR , DEBUG_INFO , " Frequency %c = %.4f MHz \n " , ' 1 ' + i , frequency [ i ] / 1e6 ) ;
if ( paging_frequency )
PDEBUG ( DSDR , DEBUG_INFO , " Frequency P = %.4f MHz (Paging Frequency) \n " , paging_frequency / 1e6 ) ;
}
2019-12-05 16:24:30 +00:00
void * sdr_open ( const char __attribute__ ( ( __unused__ ) ) * audiodev , double * tx_frequency , double * rx_frequency , int * am , int channels , double paging_frequency , int samplerate , int latspl , double max_deviation , double max_modulation , double modulation_index )
2017-01-04 13:21:49 +00:00
{
sdr_t * sdr ;
2017-08-30 15:42:49 +00:00
int threads = 1 , oversample = 1 ; /* always use threads */
2017-01-29 06:25:12 +00:00
double bandwidth ;
2017-03-20 18:57:37 +00:00
double tx_center_frequency = 0.0 , rx_center_frequency = 0.0 ;
2017-01-04 13:21:49 +00:00
int rc ;
int c ;
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_DEBUG , " Open SDR device \n " ) ;
2017-08-30 15:42:49 +00:00
if ( sdr_config - > samplerate ! = samplerate ) {
if ( samplerate > sdr_config - > samplerate ) {
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " SDR sample rate must be greater than audio sample rate! \n " ) ;
2017-08-30 15:42:49 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " You selected an SDR rate of %d and an audio rate of %d. \n " , sdr_config - > samplerate , samplerate ) ;
2017-07-24 08:09:05 +00:00
return NULL ;
}
2017-08-30 15:42:49 +00:00
if ( ( sdr_config - > samplerate % samplerate ) ) {
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " SDR sample rate must be a multiple of audio sample rate! \n " ) ;
2017-08-30 15:42:49 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " You selected an SDR rate of %d and an audio rate of %d. \n " , sdr_config - > samplerate , samplerate ) ;
2017-07-24 08:09:05 +00:00
return NULL ;
}
2017-08-30 15:42:49 +00:00
oversample = sdr_config - > samplerate / samplerate ;
threads = 1 ;
2017-07-24 08:09:05 +00:00
}
2017-01-29 06:25:12 +00:00
bandwidth = 2.0 * ( max_deviation + max_modulation ) ;
2018-01-20 14:51:13 +00:00
if ( bandwidth )
PDEBUG ( DSDR , DEBUG_INFO , " Require bandwidth of each channel is 2 * (%.1f deviation + %.1f modulation) = %.1f KHz \n " , max_deviation / 1e3 , max_modulation / 1e3 , bandwidth / 1e3 ) ;
2017-01-29 06:25:12 +00:00
2017-01-04 13:21:49 +00:00
if ( channels < 1 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " No channel given, please fix! \n " ) ;
abort ( ) ;
}
sdr = calloc ( sizeof ( * sdr ) , 1 ) ;
if ( ! sdr ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
sdr - > channels = channels ;
2017-02-05 07:57:56 +00:00
sdr - > amplitude = 1.0 / ( double ) channels ;
2017-07-24 08:09:05 +00:00
sdr - > samplerate = samplerate ;
2017-08-30 15:42:49 +00:00
sdr - > latspl = latspl ;
sdr - > threads = threads ; /* always requried, because write may block */
sdr - > oversample = oversample ;
if ( threads ) {
memset ( & sdr - > thread_read , 0 , sizeof ( sdr - > thread_read ) ) ;
sdr - > thread_read . buffer_size = sdr - > latspl * 2 * sdr - > oversample + 2 ;
sdr - > thread_read . buffer = calloc ( sdr - > thread_read . buffer_size , sizeof ( * sdr - > thread_read . buffer ) ) ;
if ( ! sdr - > thread_read . buffer ) {
PDEBUG ( DSDR , DEBUG_ERROR , " No mem! \n " ) ;
2017-10-11 16:46:55 +00:00
goto error ;
2017-08-30 15:42:49 +00:00
}
sdr - > thread_read . buffer2 = calloc ( sdr - > thread_read . buffer_size , sizeof ( * sdr - > thread_read . buffer2 ) ) ;
if ( ! sdr - > thread_read . buffer2 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " No mem! \n " ) ;
2017-10-11 16:46:55 +00:00
goto error ;
2017-08-30 15:42:49 +00:00
}
sdr - > thread_read . in = sdr - > thread_read . out = 0 ;
2017-09-24 13:59:24 +00:00
if ( oversample > 1 ) {
iir_lowpass_init ( & sdr - > thread_read . lp [ 0 ] , samplerate / 2.0 , sdr_config - > samplerate , 2 ) ;
iir_lowpass_init ( & sdr - > thread_read . lp [ 1 ] , samplerate / 2.0 , sdr_config - > samplerate , 2 ) ;
}
2017-08-30 15:42:49 +00:00
memset ( & sdr - > thread_write , 0 , sizeof ( sdr - > thread_write ) ) ;
sdr - > thread_write . buffer_size = sdr - > latspl * 2 + 2 ;
sdr - > thread_write . buffer = calloc ( sdr - > thread_write . buffer_size , sizeof ( * sdr - > thread_write . buffer ) ) ;
if ( ! sdr - > thread_write . buffer ) {
PDEBUG ( DSDR , DEBUG_ERROR , " No mem! \n " ) ;
2017-10-11 16:46:55 +00:00
goto error ;
2017-08-30 15:42:49 +00:00
}
sdr - > thread_write . buffer2 = calloc ( sdr - > thread_write . buffer_size * sdr - > oversample , sizeof ( * sdr - > thread_write . buffer2 ) ) ;
if ( ! sdr - > thread_write . buffer2 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " No mem! \n " ) ;
2017-10-11 16:46:55 +00:00
goto error ;
2017-08-30 15:42:49 +00:00
}
sdr - > thread_write . in = sdr - > thread_write . out = 0 ;
2017-09-24 13:59:24 +00:00
if ( oversample > 1 ) {
iir_lowpass_init ( & sdr - > thread_write . lp [ 0 ] , samplerate / 2.0 , sdr_config - > samplerate , 2 ) ;
iir_lowpass_init ( & sdr - > thread_write . lp [ 1 ] , samplerate / 2.0 , sdr_config - > samplerate , 2 ) ;
}
2017-08-30 15:42:49 +00:00
}
2017-01-04 13:21:49 +00:00
2017-08-27 08:49:19 +00:00
/* alloc fm modulation buffers */
2017-08-30 15:42:49 +00:00
sdr - > modbuff = calloc ( sdr - > latspl * 2 , sizeof ( * sdr - > modbuff ) ) ;
2017-08-27 08:49:19 +00:00
if ( ! sdr - > modbuff ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2017-08-30 15:42:49 +00:00
sdr - > modbuff_I = calloc ( sdr - > latspl , sizeof ( * sdr - > modbuff_I ) ) ;
2017-08-27 08:49:19 +00:00
if ( ! sdr - > modbuff_I ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2017-08-30 15:42:49 +00:00
sdr - > modbuff_Q = calloc ( sdr - > latspl , sizeof ( * sdr - > modbuff_Q ) ) ;
2017-08-27 08:49:19 +00:00
if ( ! sdr - > modbuff_Q ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2019-12-05 16:24:30 +00:00
sdr - > modbuff_carrier = calloc ( sdr - > latspl , sizeof ( * sdr - > modbuff_carrier ) ) ;
if ( ! sdr - > modbuff_carrier ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2017-08-30 15:42:49 +00:00
sdr - > wavespl0 = calloc ( sdr - > latspl , sizeof ( * sdr - > wavespl0 ) ) ;
2017-08-27 08:49:19 +00:00
if ( ! sdr - > wavespl0 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2017-08-30 15:42:49 +00:00
sdr - > wavespl1 = calloc ( sdr - > latspl , sizeof ( * sdr - > wavespl1 ) ) ;
2017-08-27 08:49:19 +00:00
if ( ! sdr - > wavespl1 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2017-01-07 09:33:13 +00:00
/* special case where we use a paging frequency */
if ( paging_frequency ) {
/* add extra paging channel */
sdr - > paging_channel = channels ;
}
2017-01-04 13:21:49 +00:00
/* create list of channel states */
2017-01-07 09:33:13 +00:00
sdr - > chan = calloc ( sizeof ( * sdr - > chan ) , channels + ( sdr - > paging_channel ! = 0 ) ) ;
2017-01-04 13:21:49 +00:00
if ( ! sdr - > chan ) {
PDEBUG ( DSDR , DEBUG_ERROR , " NO MEM! \n " ) ;
goto error ;
}
2017-03-20 18:57:37 +00:00
if ( tx_frequency ) {
/* calculate required bandwidth (IQ rate) */
2019-05-30 16:20:25 +00:00
double tx_low_frequency = 0.0 , tx_high_frequency = 0.0 ;
2017-03-20 18:57:37 +00:00
for ( c = 0 ; c < channels ; c + + ) {
sdr - > chan [ c ] . tx_frequency = tx_frequency [ c ] ;
2019-05-30 16:20:25 +00:00
if ( c = = 0 | | sdr - > chan [ c ] . tx_frequency < tx_low_frequency )
2017-03-20 18:57:37 +00:00
tx_low_frequency = sdr - > chan [ c ] . tx_frequency ;
2019-05-30 16:20:25 +00:00
if ( c = = 0 | | sdr - > chan [ c ] . tx_frequency > tx_high_frequency )
2017-03-20 18:57:37 +00:00
tx_high_frequency = sdr - > chan [ c ] . tx_frequency ;
2017-01-13 06:31:15 +00:00
}
2017-03-20 18:57:37 +00:00
if ( sdr - > paging_channel ) {
2019-05-30 16:20:25 +00:00
sdr - > chan [ sdr - > paging_channel ] . tx_frequency = paging_frequency ;
2017-03-20 18:57:37 +00:00
if ( sdr - > chan [ sdr - > paging_channel ] . tx_frequency < tx_low_frequency )
tx_low_frequency = sdr - > chan [ sdr - > paging_channel ] . tx_frequency ;
if ( sdr - > chan [ sdr - > paging_channel ] . tx_frequency > tx_high_frequency )
tx_high_frequency = sdr - > chan [ sdr - > paging_channel ] . tx_frequency ;
2017-01-13 06:31:15 +00:00
}
2019-01-02 14:57:54 +00:00
tx_center_frequency = ( tx_high_frequency + tx_low_frequency ) / 2.0 ;
2019-05-30 16:20:25 +00:00
/* show spectrum */
show_spectrum ( " TX " , ( double ) samplerate / 2.0 , tx_center_frequency , tx_frequency , paging_frequency , channels ) ;
2017-03-20 18:57:37 +00:00
/* range of TX */
2019-05-30 16:20:25 +00:00
double low_side , high_side , range ;
low_side = ( tx_center_frequency - tx_low_frequency ) + bandwidth / 2.0 ;
high_side = ( tx_high_frequency - tx_center_frequency ) + bandwidth / 2.0 ;
range = ( ( low_side > high_side ) ? low_side : high_side ) * 2.0 ;
PDEBUG ( DSDR , DEBUG_INFO , " Total bandwidth (two side bands) for all TX Frequencies: %.0f Hz \n " , range ) ;
2017-10-16 16:26:13 +00:00
if ( range > samplerate * USABLE_BANDWIDTH ) {
PDEBUG ( DSDR , DEBUG_NOTICE , " ******************************************************************************* \n " ) ;
PDEBUG ( DSDR , DEBUG_NOTICE , " The required bandwidth of %.0f Hz exceeds %.0f%% of the sample rate. \n " , range , USABLE_BANDWIDTH * 100.0 ) ;
2017-03-20 18:57:37 +00:00
PDEBUG ( DSDR , DEBUG_NOTICE , " Please increase samplerate! \n " ) ;
2017-10-16 16:26:13 +00:00
PDEBUG ( DSDR , DEBUG_NOTICE , " ******************************************************************************* \n " ) ;
2017-03-16 17:06:45 +00:00
goto error ;
}
2017-03-20 18:57:37 +00:00
PDEBUG ( DSDR , DEBUG_INFO , " Using center frequency: TX %.6f MHz \n " , tx_center_frequency / 1e6 ) ;
/* set offsets to center frequency */
for ( c = 0 ; c < channels ; c + + ) {
double tx_offset ;
tx_offset = sdr - > chan [ c ] . tx_frequency - tx_center_frequency ;
PDEBUG ( DSDR , DEBUG_DEBUG , " Frequency #%d: TX offset: %.6f MHz \n " , c , tx_offset / 1e6 ) ;
2019-12-05 16:24:30 +00:00
sdr - > chan [ c ] . am = am [ c ] ;
if ( am [ c ] ) {
double gain , bias ;
gain = modulation_index / 2.0 ;
bias = 1.0 - gain ;
rc = am_mod_init ( & sdr - > chan [ c ] . am_mod , samplerate , tx_offset , sdr - > amplitude * gain , sdr - > amplitude * bias ) ;
} else
rc = fm_mod_init ( & sdr - > chan [ c ] . fm_mod , samplerate , tx_offset , sdr - > amplitude ) ;
2017-08-05 08:41:23 +00:00
if ( rc < 0 )
goto error ;
2017-03-20 18:57:37 +00:00
}
if ( sdr - > paging_channel ) {
double tx_offset ;
tx_offset = sdr - > chan [ sdr - > paging_channel ] . tx_frequency - tx_center_frequency ;
PDEBUG ( DSDR , DEBUG_DEBUG , " Paging Frequency: TX offset: %.6f MHz \n " , tx_offset / 1e6 ) ;
2019-12-05 16:24:30 +00:00
rc = fm_mod_init ( & sdr - > chan [ sdr - > paging_channel ] . fm_mod , samplerate , tx_offset , sdr - > amplitude ) ;
2017-08-05 08:41:23 +00:00
if ( rc < 0 )
goto error ;
2017-03-20 18:57:37 +00:00
}
/* show gain */
2017-08-30 15:42:49 +00:00
PDEBUG ( DSDR , DEBUG_INFO , " Using gain: TX %.1f dB \n " , sdr_config - > tx_gain ) ;
2017-03-20 18:57:37 +00:00
/* open wave */
2017-08-30 15:42:49 +00:00
if ( sdr_config - > write_iq_tx_wave ) {
rc = wave_create_record ( & sdr - > wave_tx_rec , sdr_config - > write_iq_tx_wave , samplerate , 2 , 1.0 ) ;
2017-03-20 18:57:37 +00:00
if ( rc < 0 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " Failed to create WAVE recoding instance! \n " ) ;
goto error ;
}
}
2017-08-30 15:42:49 +00:00
if ( sdr_config - > read_iq_tx_wave ) {
2018-01-20 14:51:13 +00:00
int two = 2 ;
rc = wave_create_playback ( & sdr - > wave_tx_play , sdr_config - > read_iq_tx_wave , & samplerate , & two , 1.0 ) ;
2017-03-20 18:57:37 +00:00
if ( rc < 0 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " Failed to create WAVE playback instance! \n " ) ;
goto error ;
}
}
2017-03-16 17:06:45 +00:00
}
2017-03-20 18:57:37 +00:00
if ( rx_frequency ) {
2019-01-02 14:57:54 +00:00
/* calculate required bandwidth (IQ rate) */
2019-05-30 16:20:25 +00:00
double rx_low_frequency = 0.0 , rx_high_frequency = 0.0 ;
2017-03-20 18:57:37 +00:00
for ( c = 0 ; c < channels ; c + + ) {
sdr - > chan [ c ] . rx_frequency = rx_frequency [ c ] ;
2019-05-30 16:20:25 +00:00
if ( c = = 0 | | sdr - > chan [ c ] . rx_frequency < rx_low_frequency )
2017-03-20 18:57:37 +00:00
rx_low_frequency = sdr - > chan [ c ] . rx_frequency ;
2019-05-30 16:20:25 +00:00
if ( c = = 0 | | sdr - > chan [ c ] . rx_frequency > rx_high_frequency )
2017-03-20 18:57:37 +00:00
rx_high_frequency = sdr - > chan [ c ] . rx_frequency ;
}
2019-01-02 14:57:54 +00:00
rx_center_frequency = ( rx_high_frequency + rx_low_frequency ) / 2.0 ;
2019-05-30 16:20:25 +00:00
/* prevent channel bandwidth from overlapping with the center frequency */
if ( channels = = 1 ) {
2019-08-06 18:56:44 +00:00
/* simple: just move off the center by two times half of the bandwidth */
rx_center_frequency - = 2.0 * bandwidth / 2.0 ;
2019-05-30 16:20:25 +00:00
/* Note: rx_low_frequency is kept at old center.
Calculation of ' low_side ' will become 0.
This is correct , since there is no bandwidth
below new center frequency .
*/
PDEBUG ( DSDR , DEBUG_INFO , " We shift center frequency %.0f KHz down (half bandwidth), to prevent channel from overlap with DC level. \n " , bandwidth / 2.0 / 1e3 ) ;
} else {
/* find two channels that are aside the center */
double low_dist , high_dist , dist ;
int low_c = - 1 , high_c = - 1 ;
2019-01-02 14:57:54 +00:00
for ( c = 0 ; c < channels ; c + + ) {
2019-05-30 16:20:25 +00:00
dist = fabs ( rx_center_frequency - sdr - > chan [ c ] . rx_frequency ) ;
if ( round ( sdr - > chan [ c ] . rx_frequency ) > = round ( rx_center_frequency ) ) {
if ( high_c < 0 | | dist < high_dist ) {
high_dist = dist ;
high_c = c ;
}
} else {
if ( low_c < 0 | | dist < low_dist ) {
low_dist = dist ;
low_c = c ;
}
}
2019-01-02 14:57:54 +00:00
}
2019-05-30 16:20:25 +00:00
/* new center = center of the two frequencies aside old center */
if ( low_c > = 0 & & high_c > = 0 ) {
rx_center_frequency =
( ( sdr - > chan [ low_c ] . rx_frequency ) +
( sdr - > chan [ high_c ] . rx_frequency ) ) / 2.0 ;
PDEBUG ( DSDR , DEBUG_INFO , " We move center freqeuency between the two channels in the middle, to prevent them from overlap with DC level. \n " ) ;
2019-01-02 14:57:54 +00:00
}
}
2019-05-30 16:20:25 +00:00
/* show spectrum */
show_spectrum ( " RX " , ( double ) samplerate / 2.0 , rx_center_frequency , rx_frequency , 0.0 , channels ) ;
2017-03-20 18:57:37 +00:00
/* range of RX */
2019-01-02 14:57:54 +00:00
double low_side , high_side , range ;
low_side = ( rx_center_frequency - rx_low_frequency ) + bandwidth / 2.0 ;
high_side = ( rx_high_frequency - rx_center_frequency ) + bandwidth / 2.0 ;
range = ( ( low_side > high_side ) ? low_side : high_side ) * 2.0 ;
PDEBUG ( DSDR , DEBUG_INFO , " Total bandwidth (two side bands) for all RX Frequencies: %.0f Hz \n " , range ) ;
2017-10-16 16:26:13 +00:00
if ( range > samplerate * USABLE_BANDWIDTH ) {
PDEBUG ( DSDR , DEBUG_NOTICE , " ******************************************************************************* \n " ) ;
PDEBUG ( DSDR , DEBUG_NOTICE , " The required bandwidth of %.0f Hz exceeds %.0f%% of the sample rate. \n " , range , USABLE_BANDWIDTH * 100.0 ) ;
PDEBUG ( DSDR , DEBUG_NOTICE , " Please increase samplerate! \n " ) ;
PDEBUG ( DSDR , DEBUG_NOTICE , " ******************************************************************************* \n " ) ;
2017-01-13 06:31:15 +00:00
goto error ;
}
2017-03-20 18:57:37 +00:00
PDEBUG ( DSDR , DEBUG_INFO , " Using center frequency: RX %.6f MHz \n " , rx_center_frequency / 1e6 ) ;
/* set offsets to center frequency */
for ( c = 0 ; c < channels ; c + + ) {
double rx_offset ;
rx_offset = sdr - > chan [ c ] . rx_frequency - rx_center_frequency ;
PDEBUG ( DSDR , DEBUG_DEBUG , " Frequency #%d: RX offset: %.6f MHz \n " , c , rx_offset / 1e6 ) ;
2019-12-05 16:24:30 +00:00
sdr - > chan [ c ] . am = am [ c ] ;
if ( am [ c ] )
rc = am_demod_init ( & sdr - > chan [ c ] . am_demod , samplerate , rx_offset , bandwidth , 1.0 / modulation_index ) ;
else
rc = fm_demod_init ( & sdr - > chan [ c ] . fm_demod , samplerate , rx_offset , bandwidth / 2.0 ) ;
2017-08-05 08:41:23 +00:00
if ( rc < 0 )
goto error ;
2017-03-20 18:57:37 +00:00
}
/* show gain */
2017-08-30 15:42:49 +00:00
PDEBUG ( DSDR , DEBUG_INFO , " Using gain: RX %.1f dB \n " , sdr_config - > rx_gain ) ;
2017-03-20 18:57:37 +00:00
/* open wave */
2017-08-30 15:42:49 +00:00
if ( sdr_config - > write_iq_rx_wave ) {
rc = wave_create_record ( & sdr - > wave_rx_rec , sdr_config - > write_iq_rx_wave , samplerate , 2 , 1.0 ) ;
2017-03-20 18:57:37 +00:00
if ( rc < 0 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " Failed to create WAVE recoding instance! \n " ) ;
goto error ;
}
}
2017-08-30 15:42:49 +00:00
if ( sdr_config - > read_iq_rx_wave ) {
2018-01-20 14:51:13 +00:00
int two = 2 ;
rc = wave_create_playback ( & sdr - > wave_rx_play , sdr_config - > read_iq_rx_wave , & samplerate , & two , 1.0 ) ;
2017-03-20 18:57:37 +00:00
if ( rc < 0 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " Failed to create WAVE playback instance! \n " ) ;
goto error ;
}
}
2017-09-25 16:46:50 +00:00
/* init measurements display */
for ( c = 0 ; c < channels ; c + + ) {
sender_t * sender = get_sender_by_empfangsfrequenz ( sdr - > chan [ c ] . rx_frequency ) ;
if ( ! sender )
continue ;
2018-01-21 08:30:00 +00:00
sdr - > chan [ c ] . dmp_rf_level = display_measurements_add ( & sender - > dispmeas , " RF Level " , " %.1f dB " , DISPLAY_MEAS_AVG , DISPLAY_MEAS_LEFT , - 96.0 , 0.0 , - INFINITY ) ;
2019-12-05 16:24:30 +00:00
if ( ! am [ c ] ) {
sdr - > chan [ c ] . dmp_freq_offset = display_measurements_add ( & sender - > dispmeas , " Freq. Offset " , " %+.2f KHz " , DISPLAY_MEAS_AVG , DISPLAY_MEAS_CENTER , - max_modulation / 1000.0 * 2.0 , max_modulation / 1000.0 * 2.0 , 0.0 ) ;
sdr - > chan [ c ] . dmp_deviation = display_measurements_add ( & sender - > dispmeas , " Deviation " , " %.2f KHz " , DISPLAY_MEAS_PEAK2PEAK , DISPLAY_MEAS_LEFT , 0.0 , max_deviation / 1000.0 * 1.5 , max_deviation / 1000.0 ) ;
}
2017-09-25 16:46:50 +00:00
}
2017-01-13 06:31:15 +00:00
}
2017-08-30 15:42:49 +00:00
if ( sdr_config - > swap_links ) {
2017-08-10 15:42:54 +00:00
double temp ;
PDEBUG ( DSDR , DEBUG_NOTICE , " Sapping RX and TX frequencies! \n " ) ;
temp = rx_center_frequency ;
rx_center_frequency = tx_center_frequency ;
tx_center_frequency = temp ;
}
2017-09-02 13:41:11 +00:00
display_iq_init ( samplerate ) ;
display_spectrum_init ( samplerate , rx_center_frequency ) ;
2017-12-04 13:12:11 +00:00
PDEBUG ( DSDR , DEBUG_INFO , " Using local oscillator offseet: %.0f Hz \n " , sdr_config - > lo_offset ) ;
2017-01-04 13:21:49 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd ) {
2018-06-24 09:36:56 +00:00
rc = uhd_open ( sdr_config - > channel , sdr_config - > device_args , sdr_config - > stream_args , sdr_config - > tune_args , sdr_config - > tx_antenna , sdr_config - > rx_antenna , sdr_config - > clock_source , tx_center_frequency , rx_center_frequency , sdr_config - > lo_offset , sdr_config - > samplerate , sdr_config - > tx_gain , sdr_config - > rx_gain , sdr_config - > bandwidth , sdr_config - > uhd_tx_timestamps ) ;
2017-02-18 12:51:26 +00:00
if ( rc )
goto error ;
}
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy ) {
2018-06-24 09:36:56 +00:00
rc = soapy_open ( sdr_config - > channel , sdr_config - > device_args , sdr_config - > stream_args , sdr_config - > tune_args , sdr_config - > tx_antenna , sdr_config - > rx_antenna , sdr_config - > clock_source , tx_center_frequency , rx_center_frequency , sdr_config - > lo_offset , sdr_config - > samplerate , sdr_config - > tx_gain , sdr_config - > rx_gain , sdr_config - > bandwidth ) ;
2017-02-18 12:51:26 +00:00
if ( rc )
goto error ;
}
2017-01-04 13:21:49 +00:00
# endif
return sdr ;
error :
sdr_close ( sdr ) ;
return NULL ;
}
2017-11-26 07:53:09 +00:00
double bias_I , bias_Q ; /* calculated bias */
int bias_count = - 1 ; /* number of calculations */
void calibrate_bias ( void )
{
bias_count = 0 ;
bias_I = 0.0 ;
bias_Q = 0.0 ;
}
static void sdr_bias ( float * buffer , int count )
{
int i ;
if ( bias_count < sdr_config - > samplerate ) {
for ( i = 0 ; i < count ; i + + ) {
bias_I + = * buffer + + ;
bias_Q + = * buffer + + ;
}
bias_count + = count ;
if ( bias_count > = sdr_config - > samplerate ) {
bias_I / = bias_count ;
bias_Q / = bias_count ;
PDEBUG ( DSDR , DEBUG_INFO , " DC bias calibration finished. \n " ) ;
}
} else {
for ( i = 0 ; i < count ; i + + ) {
* buffer + + - = bias_I ;
* buffer + + - = bias_Q ;
}
}
}
2017-08-30 15:42:49 +00:00
static void * sdr_write_child ( void * arg )
2017-07-24 08:09:05 +00:00
{
sdr_t * sdr = ( sdr_t * ) arg ;
int num ;
int fill , out ;
int s , ss , o ;
2017-08-30 15:42:49 +00:00
while ( sdr - > thread_write . running ) {
2017-07-24 08:09:05 +00:00
/* write to SDR */
2017-08-30 15:42:49 +00:00
fill = ( sdr - > thread_write . in - sdr - > thread_write . out + sdr - > thread_write . buffer_size ) % sdr - > thread_write . buffer_size ;
2017-07-24 08:09:05 +00:00
num = fill / 2 ;
if ( num ) {
# ifdef DEBUG_BUFFER
printf ( " Thread found %d samples in write buffer and forwards them to SDR. \n " , num ) ;
# endif
2017-08-30 15:42:49 +00:00
out = sdr - > thread_write . out ;
2017-07-24 08:09:05 +00:00
for ( s = 0 , ss = 0 ; s < num ; s + + ) {
2017-08-30 15:42:49 +00:00
for ( o = 0 ; o < sdr - > oversample ; o + + ) {
2018-08-25 06:13:16 +00:00
sdr - > thread_write . buffer2 [ ss + + ] = sdr - > thread_write . buffer [ out ] * LIMIT_IQ_LEVEL ;
sdr - > thread_write . buffer2 [ ss + + ] = sdr - > thread_write . buffer [ out + 1 ] * LIMIT_IQ_LEVEL ;
2017-07-24 08:09:05 +00:00
}
2017-08-30 15:42:49 +00:00
out = ( out + 2 ) % sdr - > thread_write . buffer_size ;
2017-07-24 08:09:05 +00:00
}
2017-09-24 13:59:24 +00:00
sdr - > thread_write . out = out ;
# ifndef DISABLE_FILTER
/* filter spectrum */
if ( sdr - > oversample > 1 ) {
iir_process_baseband ( & sdr - > thread_write . lp [ 0 ] , sdr - > thread_write . buffer2 , num * sdr - > oversample ) ;
iir_process_baseband ( & sdr - > thread_write . lp [ 1 ] , sdr - > thread_write . buffer2 + 1 , num * sdr - > oversample ) ;
}
# endif
2017-07-24 08:09:05 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
uhd_send ( sdr - > thread_write . buffer2 , num * sdr - > oversample ) ;
2017-07-24 08:09:05 +00:00
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
soapy_send ( sdr - > thread_write . buffer2 , num * sdr - > oversample ) ;
2017-07-24 08:09:05 +00:00
# endif
}
/* delay some time */
usleep ( 1000 ) ;
}
PDEBUG ( DSDR , DEBUG_DEBUG , " Thread received exit! \n " ) ;
2017-08-30 15:42:49 +00:00
sdr - > thread_write . exit = 1 ;
2017-07-24 08:09:05 +00:00
return NULL ;
}
2017-08-30 15:42:49 +00:00
static void * sdr_read_child ( void * arg )
2017-07-24 08:09:05 +00:00
{
2017-08-30 15:42:49 +00:00
sdr_t * sdr = ( sdr_t * ) arg ;
2017-07-24 08:09:05 +00:00
int num , count = 0 ;
int space , in ;
int s , ss ;
2017-08-30 15:42:49 +00:00
while ( sdr - > thread_read . running ) {
2017-07-24 08:09:05 +00:00
/* read from SDR */
2017-08-30 15:42:49 +00:00
space = ( sdr - > thread_read . out - sdr - > thread_read . in - 2 + sdr - > thread_read . buffer_size ) % sdr - > thread_read . buffer_size ;
2017-07-24 08:09:05 +00:00
num = space / 2 ;
if ( num ) {
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
count = uhd_receive ( sdr - > thread_read . buffer2 , num ) ;
2017-07-24 08:09:05 +00:00
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
count = soapy_receive ( sdr - > thread_read . buffer2 , num ) ;
2017-07-24 08:09:05 +00:00
# endif
2017-11-26 07:53:09 +00:00
if ( bias_count > = 0 )
sdr_bias ( sdr - > thread_read . buffer2 , count ) ;
2017-07-24 08:09:05 +00:00
if ( count > 0 ) {
# ifdef DEBUG_BUFFER
printf ( " Thread read %d samples from SDR and writes them to read buffer. \n " , count ) ;
2017-09-24 13:59:24 +00:00
# endif
# ifndef DISABLE_FILTER
/* filter spectrum */
if ( sdr - > oversample > 1 ) {
iir_process_baseband ( & sdr - > thread_read . lp [ 0 ] , sdr - > thread_read . buffer2 , count ) ;
iir_process_baseband ( & sdr - > thread_read . lp [ 1 ] , sdr - > thread_read . buffer2 + 1 , count ) ;
}
2017-07-24 08:09:05 +00:00
# endif
2017-08-30 15:42:49 +00:00
in = sdr - > thread_read . in ;
2017-07-24 08:09:05 +00:00
for ( s = 0 , ss = 0 ; s < count ; s + + ) {
2017-08-30 15:42:49 +00:00
sdr - > thread_read . buffer [ in + + ] = sdr - > thread_read . buffer2 [ ss + + ] ;
sdr - > thread_read . buffer [ in + + ] = sdr - > thread_read . buffer2 [ ss + + ] ;
in % = sdr - > thread_read . buffer_size ;
2017-07-24 08:09:05 +00:00
}
2017-08-30 15:42:49 +00:00
sdr - > thread_read . in = in ;
2017-07-24 08:09:05 +00:00
}
}
/* delay some time */
usleep ( 1000 ) ;
}
PDEBUG ( DSDR , DEBUG_DEBUG , " Thread received exit! \n " ) ;
2017-08-30 15:42:49 +00:00
sdr - > thread_read . exit = 1 ;
2017-07-24 08:09:05 +00:00
return NULL ;
}
2017-02-25 06:09:53 +00:00
/* start streaming */
2017-08-30 15:42:49 +00:00
int sdr_start ( void * inst )
2017-02-25 06:09:53 +00:00
{
2017-08-30 15:42:49 +00:00
sdr_t * sdr = ( sdr_t * ) inst ;
2017-07-24 08:09:05 +00:00
int rc = - EINVAL ;
2017-02-25 06:09:53 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
2017-07-24 08:09:05 +00:00
rc = uhd_start ( ) ;
2017-02-25 06:09:53 +00:00
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
2017-07-24 08:09:05 +00:00
rc = soapy_start ( ) ;
2017-02-25 06:09:53 +00:00
# endif
2017-07-24 08:09:05 +00:00
if ( rc < 0 )
return rc ;
2017-08-30 15:42:49 +00:00
if ( sdr - > threads ) {
2017-07-24 08:09:05 +00:00
int rc ;
pthread_t tid ;
2017-08-16 16:24:57 +00:00
char tname [ 64 ] ;
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_DEBUG , " Create threads! \n " ) ;
2017-08-30 15:42:49 +00:00
sdr - > thread_write . running = 1 ;
sdr - > thread_write . exit = 0 ;
2017-07-24 08:09:05 +00:00
rc = pthread_create ( & tid , NULL , sdr_write_child , inst ) ;
if ( rc < 0 ) {
2017-08-30 15:42:49 +00:00
sdr - > thread_write . running = 0 ;
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " Failed to create thread! \n " ) ;
return rc ;
}
2017-08-16 16:24:57 +00:00
pthread_getname_np ( tid , tname , sizeof ( tname ) ) ;
2019-09-28 13:49:04 +00:00
strncat ( tname , " -sdr_tx " , sizeof ( tname ) - 7 - 1 ) ;
2017-08-16 16:24:57 +00:00
tname [ sizeof ( tname ) - 1 ] = ' \0 ' ;
pthread_setname_np ( tid , tname ) ;
2017-08-30 15:42:49 +00:00
sdr - > thread_read . running = 1 ;
sdr - > thread_read . exit = 0 ;
2017-07-24 08:09:05 +00:00
rc = pthread_create ( & tid , NULL , sdr_read_child , inst ) ;
if ( rc < 0 ) {
2017-08-30 15:42:49 +00:00
sdr - > thread_read . running = 0 ;
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " Failed to create thread! \n " ) ;
return rc ;
}
2017-08-16 16:24:57 +00:00
pthread_getname_np ( tid , tname , sizeof ( tname ) ) ;
2019-09-28 13:49:04 +00:00
strncat ( tname , " -sdr_rx " , sizeof ( tname ) - 7 - 1 ) ;
2017-08-16 16:24:57 +00:00
tname [ sizeof ( tname ) - 1 ] = ' \0 ' ;
pthread_setname_np ( tid , tname ) ;
2017-07-24 08:09:05 +00:00
}
return 0 ;
2017-02-25 06:09:53 +00:00
}
2017-01-04 13:21:49 +00:00
void sdr_close ( void * inst )
{
sdr_t * sdr = ( sdr_t * ) inst ;
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_DEBUG , " Close SDR device \n " ) ;
2017-08-30 15:42:49 +00:00
if ( sdr - > threads ) {
if ( sdr - > thread_write . running ) {
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_DEBUG , " Thread sending exit! \n " ) ;
2017-08-30 15:42:49 +00:00
sdr - > thread_write . running = 0 ;
while ( sdr - > thread_write . exit = = 0 )
2017-07-24 08:09:05 +00:00
usleep ( 1000 ) ;
}
2017-08-30 15:42:49 +00:00
if ( sdr - > thread_read . running ) {
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_DEBUG , " Thread sending exit! \n " ) ;
2017-08-30 15:42:49 +00:00
sdr - > thread_read . running = 0 ;
while ( sdr - > thread_read . exit = = 0 )
2017-07-24 08:09:05 +00:00
usleep ( 1000 ) ;
}
}
2017-08-30 15:42:49 +00:00
if ( sdr - > thread_read . buffer )
free ( ( void * ) sdr - > thread_read . buffer ) ;
if ( sdr - > thread_read . buffer2 )
free ( ( void * ) sdr - > thread_read . buffer2 ) ;
if ( sdr - > thread_write . buffer )
free ( ( void * ) sdr - > thread_write . buffer ) ;
if ( sdr - > thread_write . buffer2 )
free ( ( void * ) sdr - > thread_write . buffer2 ) ;
2017-07-24 08:09:05 +00:00
2017-01-04 13:21:49 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
2017-02-18 12:51:26 +00:00
uhd_close ( ) ;
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
2017-02-18 12:51:26 +00:00
soapy_close ( ) ;
2017-01-04 13:21:49 +00:00
# endif
if ( sdr ) {
2017-08-27 08:49:19 +00:00
free ( sdr - > modbuff ) ;
free ( sdr - > modbuff_I ) ;
free ( sdr - > modbuff_Q ) ;
2019-12-05 16:24:30 +00:00
free ( sdr - > modbuff_carrier ) ;
2017-08-27 08:49:19 +00:00
free ( sdr - > wavespl0 ) ;
free ( sdr - > wavespl1 ) ;
2017-01-13 06:31:15 +00:00
wave_destroy_record ( & sdr - > wave_rx_rec ) ;
wave_destroy_record ( & sdr - > wave_tx_rec ) ;
wave_destroy_playback ( & sdr - > wave_rx_play ) ;
2017-03-16 17:06:45 +00:00
wave_destroy_playback ( & sdr - > wave_tx_play ) ;
2017-08-05 08:41:23 +00:00
if ( sdr - > chan ) {
int c ;
for ( c = 0 ; c < sdr - > channels ; c + + ) {
2019-12-05 16:24:30 +00:00
fm_mod_exit ( & sdr - > chan [ c ] . fm_mod ) ;
fm_demod_exit ( & sdr - > chan [ c ] . fm_demod ) ;
am_mod_exit ( & sdr - > chan [ c ] . am_mod ) ;
am_demod_exit ( & sdr - > chan [ c ] . am_demod ) ;
2017-08-05 08:41:23 +00:00
}
if ( sdr - > paging_channel )
2019-12-05 16:24:30 +00:00
fm_mod_exit ( & sdr - > chan [ sdr - > paging_channel ] . fm_mod ) ;
2017-08-05 08:41:23 +00:00
free ( sdr - > chan ) ;
}
2017-01-04 13:21:49 +00:00
free ( sdr ) ;
sdr = NULL ;
}
2018-01-21 08:30:00 +00:00
display_spectrum_exit ( ) ;
2017-01-04 13:21:49 +00:00
}
2017-08-19 10:27:05 +00:00
int sdr_write ( void * inst , sample_t * * samples , uint8_t * * power , int num , enum paging_signal __attribute__ ( ( unused ) ) * paging_signal , int * on , int channels )
2017-01-04 13:21:49 +00:00
{
sdr_t * sdr = ( sdr_t * ) inst ;
2017-08-27 08:49:19 +00:00
float * buff = NULL ;
2017-01-04 13:21:49 +00:00
int c , s , ss ;
2017-02-18 12:51:26 +00:00
int sent = 0 ;
2017-01-04 13:21:49 +00:00
2017-08-30 15:42:49 +00:00
if ( num > sdr - > latspl ) {
2017-08-27 08:49:19 +00:00
fprintf ( stderr , " exceeding maximum size given by sdr_latspl, please fix! \n " ) ;
abort ( ) ;
}
2017-03-20 19:07:11 +00:00
if ( channels ! = sdr - > channels & & channels ! = 0 ) {
2017-01-04 13:21:49 +00:00
PDEBUG ( DSDR , DEBUG_ERROR , " Invalid number of channels, please fix! \n " ) ;
abort ( ) ;
}
/* process all channels */
2017-03-20 19:07:11 +00:00
if ( channels ) {
2017-08-27 08:49:19 +00:00
buff = sdr - > modbuff ;
memset ( buff , 0 , sizeof ( * buff ) * num * 2 ) ;
2017-03-20 19:07:11 +00:00
for ( c = 0 ; c < channels ; c + + ) {
/* switch to paging channel, if requested */
if ( on [ c ] & & sdr - > paging_channel )
2019-12-05 16:24:30 +00:00
fm_modulate_complex ( & sdr - > chan [ sdr - > paging_channel ] . fm_mod , samples [ c ] , power [ c ] , num , buff ) ;
else if ( sdr - > chan [ c ] . am ) {
if ( power [ c ] [ 0 ] )
am_modulate_complex ( & sdr - > chan [ c ] . am_mod , samples [ c ] , num , buff ) ;
} else
fm_modulate_complex ( & sdr - > chan [ c ] . fm_mod , samples [ c ] , power [ c ] , num , buff ) ;
2017-03-20 19:07:11 +00:00
}
} else {
buff = ( float * ) samples ;
2017-01-04 13:21:49 +00:00
}
2017-01-13 06:31:15 +00:00
if ( sdr - > wave_tx_rec . fp ) {
2017-08-27 08:49:19 +00:00
sample_t * spl_list [ 2 ] = { sdr - > wavespl0 , sdr - > wavespl1 } ;
2017-01-13 06:31:15 +00:00
for ( s = 0 , ss = 0 ; s < num ; s + + ) {
2017-08-27 08:49:19 +00:00
spl_list [ 0 ] [ s ] = buff [ ss + + ] ;
spl_list [ 1 ] [ s ] = buff [ ss + + ] ;
2017-01-13 06:31:15 +00:00
}
wave_write ( & sdr - > wave_tx_rec , spl_list , num ) ;
}
2017-03-16 17:06:45 +00:00
if ( sdr - > wave_tx_play . fp ) {
2017-08-27 08:49:19 +00:00
sample_t * spl_list [ 2 ] = { sdr - > wavespl0 , sdr - > wavespl1 } ;
2017-03-16 17:06:45 +00:00
wave_read ( & sdr - > wave_tx_play , spl_list , num ) ;
for ( s = 0 , ss = 0 ; s < num ; s + + ) {
2017-08-27 08:49:19 +00:00
buff [ ss + + ] = spl_list [ 0 ] [ s ] ;
buff [ ss + + ] = spl_list [ 1 ] [ s ] ;
2017-03-16 17:06:45 +00:00
}
}
2017-01-13 06:31:15 +00:00
2017-08-30 15:42:49 +00:00
if ( sdr - > threads ) {
2017-07-24 08:09:05 +00:00
/* store data towards SDR in ring buffer */
2017-11-17 21:51:18 +00:00
int fill , space , in ;
2017-07-24 08:09:05 +00:00
2017-11-17 21:51:18 +00:00
fill = ( sdr - > thread_write . in - sdr - > thread_write . out + sdr - > thread_write . buffer_size ) % sdr - > thread_write . buffer_size ;
2017-08-30 15:42:49 +00:00
space = ( sdr - > thread_write . out - sdr - > thread_write . in - 2 + sdr - > thread_write . buffer_size ) % sdr - > thread_write . buffer_size ;
2017-11-17 21:51:18 +00:00
/* debug fill level */
if ( fill > sdr - > thread_write . max_fill )
sdr - > thread_write . max_fill = fill ;
if ( sdr - > thread_write . max_fill_timer = = 0.0 )
sdr - > thread_write . max_fill_timer = get_time ( ) ;
if ( get_time ( ) - sdr - > thread_write . max_fill_timer > 1.0 ) {
double delay ;
delay = ( double ) sdr - > thread_write . max_fill / 2.0 / ( double ) sdr - > samplerate ;
sdr - > thread_write . max_fill = 0 ;
sdr - > thread_write . max_fill_timer + = 1.0 ;
PDEBUG ( DSDR , DEBUG_DEBUG , " write delay = %.3f ms \n " , delay * 1000.0 ) ;
}
2017-07-24 08:09:05 +00:00
if ( space < num * 2 ) {
PDEBUG ( DSDR , DEBUG_ERROR , " Write SDR buffer overflow! \n " ) ;
num = space / 2 ;
}
# ifdef DEBUG_BUFFER
printf ( " Writing %d samples to write buffer. \n " , num ) ;
# endif
2017-08-30 15:42:49 +00:00
in = sdr - > thread_write . in ;
2017-07-24 08:09:05 +00:00
for ( s = 0 , ss = 0 ; s < num ; s + + ) {
2017-08-30 15:42:49 +00:00
sdr - > thread_write . buffer [ in + + ] = buff [ ss + + ] ;
sdr - > thread_write . buffer [ in + + ] = buff [ ss + + ] ;
in % = sdr - > thread_write . buffer_size ;
2017-07-24 08:09:05 +00:00
}
2017-08-30 15:42:49 +00:00
sdr - > thread_write . in = in ;
2017-07-24 08:09:05 +00:00
sent = num ;
} else {
2017-01-04 13:21:49 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
2017-07-24 08:09:05 +00:00
sent = uhd_send ( buff , num ) ;
2017-02-18 12:51:26 +00:00
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
2017-07-24 08:09:05 +00:00
sent = soapy_send ( buff , num ) ;
2017-01-04 13:21:49 +00:00
# endif
2017-07-24 08:09:05 +00:00
if ( sent < 0 )
return sent ;
}
2017-01-04 13:21:49 +00:00
return sent ;
}
2017-10-09 18:49:14 +00:00
int sdr_read ( void * inst , sample_t * * samples , int num , int channels , double * rf_level_db )
2017-01-04 13:21:49 +00:00
{
sdr_t * sdr = ( sdr_t * ) inst ;
2017-08-27 08:49:19 +00:00
float * buff = NULL ;
2017-02-18 12:51:26 +00:00
int count = 0 ;
2017-01-04 13:21:49 +00:00
int c , s , ss ;
2017-08-30 15:42:49 +00:00
if ( num > sdr - > latspl ) {
2017-08-27 08:49:19 +00:00
fprintf ( stderr , " exceeding maximum size given by sdr_latspl, please fix! \n " ) ;
abort ( ) ;
}
2017-03-20 19:07:11 +00:00
if ( channels ) {
2017-08-27 08:49:19 +00:00
buff = sdr - > modbuff ;
2017-03-20 19:07:11 +00:00
} else {
buff = ( float * ) samples ;
}
2017-08-30 15:42:49 +00:00
if ( sdr - > threads ) {
2017-07-24 08:09:05 +00:00
/* load data from SDR out of ring buffer */
int fill , out ;
2017-08-30 15:42:49 +00:00
fill = ( sdr - > thread_read . in - sdr - > thread_read . out + sdr - > thread_read . buffer_size ) % sdr - > thread_read . buffer_size ;
2017-11-17 21:51:18 +00:00
/* debug fill level */
2017-08-30 15:42:49 +00:00
if ( fill > sdr - > thread_read . max_fill )
sdr - > thread_read . max_fill = fill ;
if ( sdr - > thread_read . max_fill_timer = = 0.0 )
sdr - > thread_read . max_fill_timer = get_time ( ) ;
if ( get_time ( ) - sdr - > thread_read . max_fill_timer > 1.0 ) {
2017-07-24 08:09:05 +00:00
double delay ;
2017-08-30 15:42:49 +00:00
delay = ( double ) sdr - > thread_read . max_fill / 2.0 / ( double ) sdr_config - > samplerate ;
sdr - > thread_read . max_fill = 0 ;
sdr - > thread_read . max_fill_timer + = 1.0 ;
2017-07-24 08:09:05 +00:00
PDEBUG ( DSDR , DEBUG_DEBUG , " read delay = %.3f ms \n " , delay * 1000.0 ) ;
}
2017-11-17 21:51:18 +00:00
2017-08-30 15:42:49 +00:00
if ( fill / 2 / sdr - > oversample < num )
num = fill / 2 / sdr - > oversample ;
2017-07-24 08:09:05 +00:00
# ifdef DEBUG_BUFFER
printf ( " Reading %d samples from read buffer. \n " , num ) ;
# endif
2017-08-30 15:42:49 +00:00
out = sdr - > thread_read . out ;
2017-07-24 08:09:05 +00:00
for ( s = 0 , ss = 0 ; s < num ; s + + ) {
2017-08-30 15:42:49 +00:00
buff [ ss + + ] = sdr - > thread_read . buffer [ out ] ;
buff [ ss + + ] = sdr - > thread_read . buffer [ out + 1 ] ;
out = ( out + 2 * sdr - > oversample ) % sdr - > thread_read . buffer_size ;
2017-07-24 08:09:05 +00:00
}
2017-08-30 15:42:49 +00:00
sdr - > thread_read . out = out ;
2017-07-24 08:09:05 +00:00
count = num ;
} else {
2017-01-04 13:21:49 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
2017-07-24 08:09:05 +00:00
count = uhd_receive ( buff , num ) ;
2017-02-18 12:51:26 +00:00
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
2017-07-24 08:09:05 +00:00
count = soapy_receive ( buff , num ) ;
2017-01-04 13:21:49 +00:00
# endif
2017-11-26 07:53:09 +00:00
if ( bias_count > = 0 )
sdr_bias ( buff , count ) ;
2017-07-24 08:09:05 +00:00
if ( count < = 0 )
return count ;
2017-09-25 16:46:50 +00:00
}
2017-01-04 13:21:49 +00:00
2017-11-17 21:51:18 +00:00
if ( sdr_rx_overflow ) {
PDEBUG ( DSDR , DEBUG_ERROR , " SDR RX overflow! \n " ) ;
sdr_rx_overflow = 0 ;
}
2017-01-13 06:31:15 +00:00
if ( sdr - > wave_rx_rec . fp ) {
2017-08-27 08:49:19 +00:00
sample_t * spl_list [ 2 ] = { sdr - > wavespl0 , sdr - > wavespl1 } ;
2017-01-13 06:31:15 +00:00
for ( s = 0 , ss = 0 ; s < count ; s + + ) {
2017-08-27 08:49:19 +00:00
spl_list [ 0 ] [ s ] = buff [ ss + + ] ;
spl_list [ 1 ] [ s ] = buff [ ss + + ] ;
2017-01-13 06:31:15 +00:00
}
wave_write ( & sdr - > wave_rx_rec , spl_list , count ) ;
}
if ( sdr - > wave_rx_play . fp ) {
2017-08-27 08:49:19 +00:00
sample_t * spl_list [ 2 ] = { sdr - > wavespl0 , sdr - > wavespl1 } ;
2017-01-13 06:31:15 +00:00
wave_read ( & sdr - > wave_rx_play , spl_list , count ) ;
for ( s = 0 , ss = 0 ; s < count ; s + + ) {
2017-08-27 08:49:19 +00:00
buff [ ss + + ] = spl_list [ 0 ] [ s ] ;
buff [ ss + + ] = spl_list [ 1 ] [ s ] ;
2017-01-13 06:31:15 +00:00
}
}
2017-01-07 18:53:43 +00:00
display_iq ( buff , count ) ;
2017-02-05 07:54:56 +00:00
display_spectrum ( buff , count ) ;
2017-01-07 18:53:43 +00:00
2017-03-20 19:07:11 +00:00
if ( channels ) {
2017-09-25 16:46:50 +00:00
for ( c = 0 ; c < channels ; c + + ) {
2019-12-05 16:24:30 +00:00
if ( sdr - > chan [ c ] . am )
am_demodulate_complex ( & sdr - > chan [ c ] . am_demod , samples [ c ] , count , buff , sdr - > modbuff_I , sdr - > modbuff_Q , sdr - > modbuff_carrier ) ;
else
fm_demodulate_complex ( & sdr - > chan [ c ] . fm_demod , samples [ c ] , count , buff , sdr - > modbuff_I , sdr - > modbuff_Q ) ;
2017-09-25 16:46:50 +00:00
sender_t * sender = get_sender_by_empfangsfrequenz ( sdr - > chan [ c ] . rx_frequency ) ;
if ( ! sender | | ! count )
continue ;
double min , max , avg ;
avg = 0.0 ;
for ( s = 0 ; s < count ; s + + ) {
/* average the square length of vector */
avg + = sdr - > modbuff_I [ s ] * sdr - > modbuff_I [ s ] + sdr - > modbuff_Q [ s ] * sdr - > modbuff_Q [ s ] ;
}
avg = sqrt ( avg / ( double ) count ) ; /* RMS */
avg = log10 ( avg ) * 20 ;
display_measurements_update ( sdr - > chan [ c ] . dmp_rf_level , avg , 0.0 ) ;
2017-10-09 18:49:14 +00:00
rf_level_db [ c ] = avg ;
2019-12-05 16:24:30 +00:00
if ( ! sdr - > chan [ c ] . am ) {
min = 0.0 ;
max = 0.0 ;
avg = 0.0 ;
for ( s = 0 ; s < count ; s + + ) {
avg + = samples [ c ] [ s ] ;
if ( s = = 0 | | samples [ c ] [ s ] > max )
max = samples [ c ] [ s ] ;
if ( s = = 0 | | samples [ c ] [ s ] < min )
min = samples [ c ] [ s ] ;
}
avg / = ( double ) count ;
display_measurements_update ( sdr - > chan [ c ] . dmp_freq_offset , avg / 1000.0 , 0.0 ) ;
/* use half min and max, because we want the deviation above/below (+-) center frequency. */
display_measurements_update ( sdr - > chan [ c ] . dmp_deviation , min / 2.0 / 1000.0 , max / 2.0 / 1000.0 ) ;
2017-09-25 16:46:50 +00:00
}
}
2017-01-04 13:21:49 +00:00
}
return count ;
}
2017-07-24 08:09:05 +00:00
/* how much do we need to send (in audio sample duration) to get the target delay (latspl) */
2017-08-30 15:42:49 +00:00
int sdr_get_tosend ( void * inst , int latspl )
2017-01-04 13:21:49 +00:00
{
2017-08-30 15:42:49 +00:00
sdr_t * sdr = ( sdr_t * ) inst ;
2017-02-18 12:51:26 +00:00
int count = 0 ;
2017-01-04 13:21:49 +00:00
# ifdef HAVE_UHD
2017-08-30 15:42:49 +00:00
if ( sdr_config - > uhd )
count = uhd_get_tosend ( latspl * sdr - > oversample ) ;
2017-02-18 12:51:26 +00:00
# endif
# ifdef HAVE_SOAPY
2017-08-30 15:42:49 +00:00
if ( sdr_config - > soapy )
count = soapy_get_tosend ( latspl * sdr - > oversample ) ;
2017-01-04 13:21:49 +00:00
# endif
if ( count < 0 )
return count ;
2017-08-30 15:42:49 +00:00
count / = sdr - > oversample ;
2017-07-24 08:09:05 +00:00
2017-08-30 15:42:49 +00:00
if ( sdr - > threads ) {
2019-07-15 19:14:52 +00:00
/* subtract what we have in write buffer, because this is not jent sent to the SDR */
2017-07-24 08:09:05 +00:00
int fill ;
2017-08-30 15:42:49 +00:00
fill = ( sdr - > thread_write . in - sdr - > thread_write . out + sdr - > thread_write . buffer_size ) % sdr - > thread_write . buffer_size ;
2017-07-24 08:09:05 +00:00
count - = fill / 2 ;
if ( count < 0 )
count = 0 ;
}
2017-01-04 13:21:49 +00:00
return count ;
}