2017-01-02 21:07:43 -05:00
// Copyright (c) Charles J. Cliffe
// SPDX-License-Identifier: GPL-2.0+
2015-09-07 20:46:03 -04:00
# include "FFTDataDistributor.h"
2017-02-04 10:32:35 +01:00
# include <algorithm>
2017-02-09 19:12:12 +01:00
# include <ThreadBlockingQueue.h>
2015-09-07 20:46:03 -04:00
2017-05-25 09:32:27 +02:00
//50 ms
# define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
2016-01-28 18:11:53 -05:00
FFTDataDistributor : : FFTDataDistributor ( ) : outputBuffers ( " FFTDataDistributorBuffers " ) , fftSize ( DEFAULT_FFT_SIZE ) , linesPerSecond ( DEFAULT_WATERFALL_LPS ) , lineRateAccum ( 0.0 ) {
2017-01-28 14:57:27 +01:00
2015-09-07 20:46:03 -04:00
}
2017-02-04 10:32:35 +01:00
void FFTDataDistributor : : setFFTSize ( unsigned int size ) {
fftSize . store ( size ) ;
2015-09-07 20:46:03 -04:00
}
2016-02-04 18:05:33 -05:00
void FFTDataDistributor : : setLinesPerSecond ( unsigned int lines ) {
2015-09-07 20:46:03 -04:00
this - > linesPerSecond = lines ;
}
2021-04-04 22:18:19 -04:00
unsigned int FFTDataDistributor : : getLinesPerSecond ( ) const {
2015-09-07 20:46:03 -04:00
return this - > linesPerSecond ;
}
void FFTDataDistributor : : process ( ) {
2016-07-05 21:43:45 +02:00
2015-09-09 23:46:05 -04:00
while ( ! input - > empty ( ) ) {
2015-09-07 20:46:03 -04:00
if ( ! isAnyOutputEmpty ( ) ) {
return ;
}
2017-05-24 12:24:36 +02:00
DemodulatorThreadIQDataPtr inp ;
2017-05-25 09:32:27 +02:00
if ( ! input - > pop ( inp , HEARTBEAT_CHECK_PERIOD_MICROS ) ) {
continue ;
}
2015-09-07 20:46:03 -04:00
if ( inp ) {
2017-02-04 10:32:35 +01:00
//Settings have changed, set new values and dump all previous samples stored in inputBuffer:
2015-09-07 20:46:03 -04:00
if ( inputBuffer . sampleRate ! = inp - > sampleRate | | inputBuffer . frequency ! = inp - > frequency ) {
2017-02-04 10:32:35 +01:00
//bufferMax must be at least fftSize (+ margin), else the waterfall get frozen, because no longer updated.
bufferMax = std : : max ( ( size_t ) ( inp - > sampleRate * FFT_DISTRIBUTOR_BUFFER_IN_SECONDS ) , ( size_t ) ( 1.2 * fftSize . load ( ) ) ) ;
2015-09-29 22:34:34 -04:00
// std::cout << "Buffer Max: " << bufferMax << std::endl;
bufferOffset = 0 ;
2017-02-04 10:32:35 +01:00
bufferedItems = 0 ;
2015-09-07 20:46:03 -04:00
inputBuffer . sampleRate = inp - > sampleRate ;
inputBuffer . frequency = inp - > frequency ;
2015-09-29 22:34:34 -04:00
inputBuffer . data . resize ( bufferMax ) ;
2015-09-07 20:46:03 -04:00
}
2017-02-04 10:32:35 +01:00
//adjust (bufferMax ; inputBuffer.data) in case of FFT size change only.
if ( bufferMax < ( size_t ) ( 1.2 * fftSize . load ( ) ) ) {
bufferMax = ( size_t ) ( 1.2 * fftSize . load ( ) ) ;
inputBuffer . data . resize ( bufferMax ) ;
}
size_t nbSamplesToAdd = inp - > data . size ( ) ;
//No room left in inputBuffer.data to accept inp->data.size() more samples.
//so make room by sliding left of bufferOffset, which is fine because
//those samples has already been processed.
2015-09-29 22:34:34 -04:00
if ( ( bufferOffset + bufferedItems + inp - > data . size ( ) ) > bufferMax ) {
2015-09-30 02:07:00 -04:00
memmove ( & inputBuffer . data [ 0 ] , & inputBuffer . data [ bufferOffset ] , bufferedItems * sizeof ( liquid_float_complex ) ) ;
bufferOffset = 0 ;
2017-02-04 10:32:35 +01:00
//if there are too much samples, we may even overflow !
2021-12-29 19:54:03 +01:00
//as a fallback strategy, drop the last incoming new samples not fitting in inputBuffer.data.
2017-02-04 10:32:35 +01:00
if ( bufferedItems + inp - > data . size ( ) > bufferMax ) {
//clamp nbSamplesToAdd
nbSamplesToAdd = bufferMax - bufferedItems ;
std : : cout < < " FFTDataDistributor::process() incoming samples overflow, dropping the last " < < ( inp - > data . size ( ) - nbSamplesToAdd ) < < " input samples... " < < std : : endl ;
}
2015-09-29 22:34:34 -04:00
}
2017-02-04 10:32:35 +01:00
//store nbSamplesToAdd incoming samples.
memcpy ( & inputBuffer . data [ bufferOffset + bufferedItems ] , & inp - > data [ 0 ] , nbSamplesToAdd * sizeof ( liquid_float_complex ) ) ;
bufferedItems + = nbSamplesToAdd ;
//
2017-05-24 12:24:36 +02:00
2015-09-07 20:46:03 -04:00
} else {
2017-02-04 10:32:35 +01:00
//empty inp, wait for another.
2015-09-09 23:46:05 -04:00
continue ;
2015-09-07 20:46:03 -04:00
}
// number of seconds contained in input
2015-09-29 22:34:34 -04:00
double inputTime = ( double ) bufferedItems / ( double ) inputBuffer . sampleRate ;
2015-09-07 20:46:03 -04:00
// number of lines in input
2015-09-29 22:34:34 -04:00
double inputLines = ( double ) bufferedItems / ( double ) fftSize ;
2015-09-07 20:46:03 -04:00
2017-02-04 10:32:35 +01:00
// ratio required to achieve the desired rate:
// it means we can achieive 'lineRateStep' times the target linesPerSecond.
// < 1 means we cannot reach it by lack of samples.
2015-09-07 20:46:03 -04:00
double lineRateStep = ( ( double ) linesPerSecond * inputTime ) / ( double ) inputLines ;
2017-02-04 10:32:35 +01:00
//we have enough samples to FFT at least one 'line' of 'fftSize' frequencies for display:
2015-09-29 22:34:34 -04:00
if ( bufferedItems > = fftSize ) {
2017-01-28 14:57:27 +01:00
size_t numProcessed = 0 ;
2015-09-29 22:34:34 -04:00
if ( lineRateAccum + ( lineRateStep * ( ( double ) bufferedItems / ( double ) fftSize ) ) < 1.0 ) {
2015-09-09 23:29:38 -04:00
// move along, nothing to see here..
2015-09-29 22:34:34 -04:00
lineRateAccum + = ( lineRateStep * ( ( double ) bufferedItems / ( double ) fftSize ) ) ;
numProcessed = bufferedItems ;
2015-09-09 23:29:38 -04:00
} else {
2017-01-28 14:57:27 +01:00
for ( size_t i = 0 , iMax = bufferedItems ; i < iMax ; i + = fftSize ) {
2015-09-07 20:46:03 -04:00
if ( ( i + fftSize ) > iMax ) {
break ;
}
lineRateAccum + = lineRateStep ;
if ( lineRateAccum > = 1.0 ) {
2017-02-04 10:32:35 +01:00
//each i represents a FFT computation
2017-05-24 12:24:36 +02:00
DemodulatorThreadIQDataPtr outp = outputBuffers . getBuffer ( ) ;
2015-09-07 20:46:03 -04:00
outp - > frequency = inputBuffer . frequency ;
outp - > sampleRate = inputBuffer . sampleRate ;
2017-02-04 10:32:35 +01:00
outp - > data . assign ( inputBuffer . data . begin ( ) + bufferOffset + i ,
inputBuffer . data . begin ( ) + bufferOffset + i + fftSize ) ;
2017-02-09 19:12:12 +01:00
//authorize distribute with losses
distribute ( outp , NON_BLOCKING_TIMEOUT ) ;
2015-09-07 20:46:03 -04:00
while ( lineRateAccum > = 1.0 ) {
lineRateAccum - = 1.0 ;
}
}
numProcessed + = fftSize ;
2017-02-04 10:32:35 +01:00
} //end for
2015-09-09 23:29:38 -04:00
}
2017-02-04 10:32:35 +01:00
//advance bufferOffset read pointer,
//reduce size of bufferedItems.
2015-09-07 20:46:03 -04:00
if ( numProcessed ) {
2015-09-29 22:34:34 -04:00
bufferedItems - = numProcessed ;
bufferOffset + = numProcessed ;
}
2017-02-04 10:32:35 +01:00
//clamp to zero the number of remaining items.
2015-09-29 22:34:34 -04:00
if ( bufferedItems < = 0 ) {
bufferedItems = 0 ;
bufferOffset = 0 ;
}
2017-02-04 10:32:35 +01:00
} //end if bufferedItems >= fftSize
} //en while
2015-09-07 20:46:03 -04:00
}