/*
Copyright 2018 Michal Fratczak
This file is part of habdec.
habdec 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.
habdec 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 habdec. If not, see .
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include "IQVector.h"
#include "Decimator.h"
#include "FFT.h"
#include "FirFilter.h"
#include "filtercoef.h"
#include "FSK2_Demod.h"
#include "SymbolExtractor.h"
#include "RTTY.h"
#include "sentence_extract.h"
#include "Decoder.h"
#include "AFC.h"
#include "SpectrumInfo.h"
#include "print_habhub_sentence.h"
#include "CRC.h"
namespace habdec
{
template
class Decoder
{
// STEPS:
//
// multi stage Decimation
// FFT
// Auto Frequency Correct
// FIR Filtering
// Demodulation (Polar Discriminant)
// Symbol Extraction
// RTTY
public:
typedef TReal TValue;
typedef std::complex TComplex;
typedef std::vector TRVector;
typedef habdec::IQVector TIQVector;
typedef habdec::Decimator< std::complex, TReal > TDecimator;
typedef habdec::FirFilter< std::complex, TReal> TFIR;
// feed decoder
bool pushSamples(const TIQVector& i_stream);
// configure options
void lowpass_bw(float i_lowpass_bw);
float lowpass_bw() const;
void lowpass_trans(float i_lowpass_trans);
float lowpass_trans() const;
void baud(double i_baud);
double baud() const;
void rtty_bits(size_t i_ascii_bits);
size_t rtty_bits() const;
void rtty_stops(float i_ascii_stops);
float rtty_stops() const;
size_t setupDecimationStagesFactor(const size_t i_factor);
size_t setupDecimationStagesBW(const double i_max_sampling_rate);
// get result
std::string getRTTY();
std::string getLastSentence();
// get info
int getDecimationFactor() const;
double getInputSamplingRate() const;
double getDecimatedSamplingRate() const;
double getSymbolRate() const;
// get data for GUI
size_t getBinsCount() const;
const TIQVector getFFT() const;
const TRVector getDemodulated() const;
const TRVector getPowerSpectrum() const;
void getPeaks(int& pl, int& pr);
void getNoiseFloor(double& nf, double& nv);
double getShift() const;
double getFrequencyCorrection() const;
void resetFrequencyCorrection(double frequency_correction);
// template
SpectrumInfo getSpectrumInfo();
// run it
void process();
void operator()()
{
typedef std::chrono::nanoseconds TDur;
auto _start = std::chrono::high_resolution_clock::now();
process();
TDur _duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _start);
// std::cout<<"process() duration "<<_duration.count()<<" nS "< decimation_stages_;
int decimation_factor_ = 1;
// FFT
const size_t fft_bins_cnt_ = 1<<12;
FFT fft_; //TODO ! this is only float !
TIQVector freq_in_; // FFT of chunk size
TIQVector freq_out_; // FFT of chunk size
mutable std::mutex freq_out_mtx_;
// AFC
AFC afc_;
mutable std::mutex afc_mtx_;
// Low Pass FIR
float lowpass_bw_ = 0.05;
float lowpass_trans_ = 0.0025;
TFIR lowpass_fir_;
mutable std::mutex lowpass_fir_mtx_;
// Demodulation
TRVector demodulated_; // result of Polar Discriminant
mutable std::mutex demodulated_mtx_;
// Symbol Extraction from demodulated samples
SymbolExtractor symbol_extractor_;
// RTTY reconstruction from bits
RTTY rtty_;
std::string rtty_char_stream_; // result of rtty
std::string last_sentence_; // result of rtty
// size_t last_sentence_len_ = 0; // optimization for regexp run
// threading
mutable std::mutex process_mutex_; // mutex for main processing
bool live_print_ = true; // live print decoded characters
};
template
bool habdec::Decoder::pushSamples(const TIQVector& i_stream)
{
{
std::lock_guard _lock(iq_in_buffer_mtx_);
const size_t pre_insert_size = iq_in_buffer_.size();
iq_in_buffer_.resize( pre_insert_size + i_stream.size() );
memcpy( iq_in_buffer_.data() + pre_insert_size, i_stream.data(), i_stream.size() * sizeof(TComplex) );
}
if( !iq_in_buffer_.samplingRate() )
init( i_stream.samplingRate() );
return true;
}
template
void habdec::Decoder::init(const float i_sampling_rate)
{
iq_in_buffer_.samplingRate(i_sampling_rate);
setupDecimationStagesBW(10.1e6/256);
std::lock_guard lock(process_mutex_);
// FFT
freq_in_.reserve(fft_bins_cnt_);
freq_out_.reserve(fft_bins_cnt_);
}
template
void habdec::Decoder::lowpass_bw(float i_lowpass_bw)
{
std::lock_guard _lock(lowpass_fir_mtx_);
lowpass_bw_ = i_lowpass_bw;
lowpass_fir_.LP_BlackmanHarris(lowpass_bw_, lowpass_trans_);
}
template
float habdec::Decoder::lowpass_bw() const
{
return lowpass_bw_;
}
template
void habdec::Decoder::lowpass_trans(float i_lowpass_trans)
{
std::lock_guard _lock(lowpass_fir_mtx_);
lowpass_trans_ = i_lowpass_trans;
lowpass_fir_.LP_BlackmanHarris(lowpass_bw_, lowpass_trans_);
}
template
float habdec::Decoder::lowpass_trans() const
{
return lowpass_trans_;
}
template
size_t habdec::Decoder::setupDecimationStagesFactor(const size_t i_factor)
{
using namespace std;
if( i_factor<1 || i_factor>256 )
{
cout<<"Unsupported decimation factor: "< lock(process_mutex_);
decimation_stages_.clear();
decimation_factor_ = 1;
switch(i_factor)
{
case 256:
decimation_stages_.emplace_back( TDecimator(64, &d_256_r_64_kernel[0], d_256_r_64_len) );
decimation_stages_.emplace_back( TDecimator(4, &d_4_r_4_kernel[0], d_4_r_4_len) );
break;
case 128:
decimation_stages_.emplace_back( TDecimator(32, &d_128_r_32_kernel[0], d_128_r_32_len) );
decimation_stages_.emplace_back( TDecimator(4, &d_4_r_4_kernel[0], d_4_r_4_len) );
break;
case 64:
decimation_stages_.emplace_back( TDecimator(32, &d_64_r_32_kernel[0], d_64_r_32_len) );
decimation_stages_.emplace_back( TDecimator(2, &d_2_r_2_kernel[0], d_2_r_2_len) );
break;
case 32:
decimation_stages_.emplace_back( TDecimator(16, &d_32_r_16_kernel[0], d_32_r_16_len) );
decimation_stages_.emplace_back( TDecimator(2, &d_2_r_2_kernel[0], d_2_r_2_len) );
break;
case 16:
decimation_stages_.emplace_back( TDecimator(8, &d_16_r_8_kernel[0], d_16_r_8_len) );
decimation_stages_.emplace_back( TDecimator(2, &d_2_r_2_kernel[0], d_2_r_2_len) );
break;
case 8:
decimation_stages_.emplace_back( TDecimator(8, &d_8_r_8_kernel[0], d_8_r_8_len) );
break;
case 4:
decimation_stages_.emplace_back( TDecimator(4, &d_4_r_4_kernel[0], d_4_r_4_len) );
break;
case 2:
decimation_stages_.emplace_back( TDecimator(2, &d_2_r_2_kernel[0], d_2_r_2_len) );
break;
default:
cout<<"Unsupported decimation factor: "<= fft_bins_cnt_)
{
{
lock_guard _lock(freq_out_mtx_);
freq_out_.resize(fft_bins_cnt_);
fft_.setInput( (float*) freq_in_.data(), fft_bins_cnt_ );
fft_.setOutput( (float*) freq_out_.data() );
fft_();
}
freq_in_.clear();
}
const size_t batch_size = 256; // why does it have to be 256 ?
if(iq_samples_decimated_.size() < batch_size)
{
if(b_debug)
cout<<"Decoder::process() "<<"iq_samples_decimated_.size() < batch_size : "<(afc_mtx_, try_to_lock))
{
if(unique_lock(freq_out_mtx_, try_to_lock))
{
if(freq_out_.size() == fft_bins_cnt_)
afc_.setFftSamples(freq_out_);
afc_();
}
}
double frequency_correction = getFrequencyCorrection();
double shift_Hz = afc_.getShift();
double nf, nv;
afc_.getNoiseFloor(nf, nv);
// cout< 4*max_decimated_sampling_rate_)
{
iq_samples_temp_.clear();
iq_samples_decimated_.clear();
return;
}
// Low Pass FIR
//
const size_t samples_to_filter_cnt = iq_samples_decimated_.size() - iq_samples_decimated_.size() % batch_size;
iq_samples_filtered_.resize(samples_to_filter_cnt);
{
lock_guard _lock(lowpass_fir_mtx_);
lowpass_fir_.setInput (iq_samples_decimated_.data(), samples_to_filter_cnt);
lowpass_fir_.setOutput(iq_samples_filtered_.data());
lowpass_fir_.LP_BlackmanHarris(lowpass_bw_, lowpass_trans_);
lowpass_fir_();
iq_samples_filtered_.samplingRate( getDecimatedSamplingRate() );
}
iq_samples_decimated_.erase( iq_samples_decimated_.begin(), iq_samples_decimated_.begin()+samples_to_filter_cnt );
if(b_debug)
cout<<"Decoder::process() "<<"FIR taps : "< _lock(demodulated_mtx_);
demodulated_.resize( iq_samples_filtered_.size() );
FSK2_Demod(
iq_samples_filtered_.data(), iq_samples_filtered_.size(),
demodulated_.data() );
symbol_extractor_.samplingRate( getDecimatedSamplingRate() );
symbol_extractor_.pushSamples(demodulated_);
}
if(b_debug)
cout<<"Decoder::process() "<<"demodulated_.size() "< symbols = symbol_extractor_.get();
if(b_debug)
cout<<"Decoder::process() "<<"symbols.size() "< 1000)
rtty_char_stream_.erase( 0, rtty_char_stream_.rfind('$') );
// scan for sentence from time to time
if( rtty_char_stream_.size() > 20 /*last_sentence_.size()/2*/ )
{
bool b_scan_for_sentence = true;
while(b_scan_for_sentence)
{
map result = extractSentence(rtty_char_stream_);
if( result["success"] == "OK" )
{
rtty_char_stream_ = result["stream"]; // rest of stream after sentence
last_sentence_ = result["callsign"] + "," + result["data"] + "*" + result["crc"];
printHabhubSentence( result["callsign"], result["data"], result["crc"] );
if( success_callback_ &&
result["crc"] == CRC(result["callsign"] + "," + result["data"]) )
success_callback_( result["callsign"], result["data"], result["crc"] );
}
else
{
b_scan_for_sentence = false;
}
}
}
} // process()
template
std::string Decoder::getRTTY()
{
return rtty_char_stream_;
}
template
std::string Decoder::getLastSentence()
{
return last_sentence_;
}
template
void Decoder::baud(double i_baud)
{
std::lock_guard _lock(process_mutex_);
symbol_extractor_.symbolRate(i_baud);
}
template
double Decoder::baud() const
{
return symbol_extractor_.symbolRate();
}
template
void Decoder::rtty_bits(size_t i_ascii_bits)
{
std::lock_guard _lock(process_mutex_);
rtty_.ascii_bits(i_ascii_bits);
}
template
size_t Decoder::rtty_bits() const
{
return rtty_.ascii_bits();
}
template
void Decoder::rtty_stops(float i_ascii_stops)
{
std::lock_guard _lock(process_mutex_);
rtty_.ascii_stops(i_ascii_stops);
}
template
float Decoder::rtty_stops() const
{
return rtty_.ascii_stops();
}
template
int Decoder::getDecimationFactor() const
{
//std::lock_guard _lock(process_mutex_);
// this probably should be locked with process_mutex_
return decimation_factor_;
}
template
double Decoder::getInputSamplingRate() const
{
return iq_in_buffer_.samplingRate();
}
template
double Decoder::getDecimatedSamplingRate() const
{
return getInputSamplingRate() / getDecimationFactor();
}
template
double Decoder::getSymbolRate() const
{
return symbol_extractor_.symbolRate();
}
template
size_t Decoder::getBinsCount() const
{
return fft_bins_cnt_;
}
template
const typename Decoder::TIQVector Decoder::getFFT() const
{
std::lock_guard _lock(freq_out_mtx_);
return freq_out_;
}
template
const typename Decoder::TRVector Decoder::getDemodulated() const
{
std::lock_guard _lock(demodulated_mtx_);
return demodulated_;
}
template
const typename Decoder::TRVector Decoder::getPowerSpectrum() const
{
std::lock_guard _lock(afc_mtx_);
// std::this_thread::sleep_for( ( std::chrono::duration(1000) ));
return afc_.getPowerSpectrum();
}
template
void Decoder::getPeaks(int& pl, int& pr)
{
std::lock_guard _lock(afc_mtx_);
afc_.getPeaks(pl, pr);
}
template
void habdec::Decoder::getNoiseFloor(double& nf, double& nv)
{
std::lock_guard _lock(afc_mtx_);
afc_.getNoiseFloor(nf, nv);
}
template
double habdec::Decoder::getShift() const
{
return afc_.getShift();
}
template
double habdec::Decoder::getFrequencyCorrection() const
{
return afc_.getFrequencyCorrection();
}
template
void habdec::Decoder::resetFrequencyCorrection(double frequency_correction)
{
afc_.resetFrequencyCorrection(frequency_correction);
}
template
SpectrumInfo habdec::Decoder::getSpectrumInfo()
{
SpectrumInfo spectr_info;
spectr_info = getPowerSpectrum();
if(!spectr_info.size())
return spectr_info;
spectr_info.min_ = *std::min_element( spectr_info.cbegin(), spectr_info.cend() );
spectr_info.max_ = *std::max_element( spectr_info.cbegin(), spectr_info.cend() );
int pl, pr;
getPeaks(pl, pr);
spectr_info.peak_left_ = std::abs(pl);
spectr_info.peak_left_valid_ = pl>0;
spectr_info.peak_right_ = std::abs(pr);
spectr_info.peak_right_valid_ = pr>0;
getNoiseFloor(spectr_info.noise_floor_, spectr_info.noise_variance_);
spectr_info.sampling_rate_ = getDecimatedSamplingRate();
spectr_info.shift_ = getShift();
return spectr_info;
}
template
std::ostream& operator<<( std::ostream& output, const std::vector& v )
{
for(const auto& a : v)
output<