/*
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 .
*/
#include "program_options.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "IQSource/IQSource_SoapySDR.h"
#include "Decoder/Decoder.h"
#include "common/console_colors.h"
#include "habitat/habitat_interface.h"
#include "GLOBALS.h"
#include "ws_server.h"
#include "common/git_repo_sha1.h"
using namespace std;
std::vector split_string(const std::string& text, char sep)
{
std::vector tokens;
std::size_t start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos)
{
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
return tokens;
}
void SentenceToPosition(const std::string i_snt,
float &lat, float &lon, float &alt,
const std::string coord_format_lat, const std::string coord_format_lon
)
{
// "CALLSIGN,123,15:41:24,44.32800,-74.14427,00491,0,0,12,30.7,0.0,0.001,20.2,958*6BC9"
std::vector tokens = split_string(i_snt, ',');
lat = std::stof(tokens[3]);
lon = std::stof(tokens[4]);
alt = std::stof(tokens[5]);
if(coord_format_lat == "ddmm.mmmm")
{
const float degs = trunc(lat / 100);
const float mins = lat - 100.0f * degs;
lat = degs + mins / 60.0f;
}
if(coord_format_lon == "ddmm.mmmm")
{
const float degs = trunc(lon / 100);
const float mins = lon - 100.0f * degs;
lon = degs + mins / 60.0f;
}
}
void PrintDevicesList(const SoapySDR::KwargsList& device_list)
{
int DEV_NUM = 0;
cout< sampling_rates = p_device->listSampleRates(SOAPY_SDR_RX, 0);
sort(sampling_rates.begin(), sampling_rates.end());
cout<<"\tSampling Rates:"<= int(device_list.size()) )
{
cout<::max();
for(double sr : p_device->listSampleRates(SOAPY_SDR_RX, 0))
{
if( abs(sr - user_sr) < sr_diff )
{
sr_diff = abs(sr - user_sr);
GLOBALS::get().par_.sampling_rate_ = sr;
}
}
cout<setOption("SoapySDR_Kwargs", &device);
if( !GLOBALS::get().p_iq_source_->init() )
{
cout<setOption("gain_double", &gain);
double biastee = GLOBALS::get().par_.biast_;
GLOBALS::get().p_iq_source_->setOption("biastee_double", &biastee);
GLOBALS::get().p_iq_source_->setOption("sampling_rate_double", &GLOBALS::get().par_.sampling_rate_);
double usb_pack = GLOBALS::get().par_.usb_pack_;
GLOBALS::get().p_iq_source_->setOption("usb_pack_double", &usb_pack);
// this works only if device is running. not yet here
// GLOBALS::get().p_iq_source_->setOption("ppm_double", &GLOBALS::get().ppm_);
o_device = device;
return true;
}
void DECODER_THREAD()
{
using namespace std;
cout<<"Start DECODER_THREAD"<start())
{
cout<isRunning())
{
cout<setOption("ppm_double", &GLOBALS::get().par_.ppm_);
//////
//
typedef std::chrono::nanoseconds TDur;
auto& DECODER = GLOBALS::get().decoder_;
habdec::IQVector samples;
samples.resize(256*256);
samples.samplingRate( p_iq_src->samplingRate() );
while(1)
{
auto _start = std::chrono::high_resolution_clock::now();
size_t count = p_iq_src->get( samples.data(), samples.size() );
if(count)
samples.resize(count);
DECODER.pushSamples(samples);
DECODER(); // DECODE !
// AFC - don't do this too often. Too unstable, needs more work.
static auto last_afc_time = std::chrono::high_resolution_clock::now();
if( std::chrono::duration_cast< std::chrono::seconds >
(std::chrono::high_resolution_clock::now() - last_afc_time).count() > 5
)
{
double freq_corr = DECODER.getFrequencyCorrection();
if(GLOBALS::get().par_.afc_)
{
if( 100 < abs(freq_corr) )
{
GLOBALS::get().par_.frequency_ += freq_corr;
double f = GLOBALS::get().par_.frequency_;
p_iq_src->setOption("frequency_double", &f);
DECODER.resetFrequencyCorrection(freq_corr);
last_afc_time = std::chrono::high_resolution_clock::now();
}
}
}
TDur _duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - _start);
// accumulate demod samples to display more
{
std::lock_guard _lock(GLOBALS::get().demod_accumulated_mtx_);
const size_t max_sz = DECODER.getDecimatedSamplingRate() /
DECODER.getSymbolRate() * 50; // 50 symbols
auto demod = DECODER.getDemodulated();
auto& demod_acc = GLOBALS::get().demod_accumulated_;
demod_acc.insert( demod_acc.end(), demod.cbegin(), demod.cend() );
// remove leading values
if( demod_acc.size() > max_sz)
demod_acc.erase( demod_acc.begin(), demod_acc.begin() + demod_acc.size() - max_sz );
}
}
}
void SentenceCallback(std::string callsign, std::string data, std::string crc, std::shared_ptr p_ws)
{
using namespace std;
using Ms = std::chrono::milliseconds;
const string sentence = callsign + "," + data + "*" + crc;
const int sentence_number = stoi( data.substr(0, data.find(',')) );
// notify all websocket clients
if(p_ws)
{
auto p_sentence_msg = std::make_shared();
p_sentence_msg->to_all_clients_ = true;
p_sentence_msg->data_stream_<<"cmd::info:sentence="<sessions_send(p_sentence_msg);
}
// register in globals
if( GLOBALS::get().sentences_map_mtx_.try_lock_for(Ms(1000)) )
{
GLOBALS::get().sentences_map_[sentence_number] = sentence;
GLOBALS::get().stats_.num_ok_ = GLOBALS::get().sentences_map_.size();
GLOBALS::get().sentences_map_mtx_.unlock();
}
// save opts
try {
GLOBALS::DumpToFile("./habdecWebsocketServer.opts");
}
catch(const exception& e) {
cout<<"Failed saving options "<
{
char do_thousands_sep() const { return ','; }
string do_grouping() const { return "\3"; }
};
try{
cout.imbue( locale(locale(""), new thousand_separators) );
}
catch(exception& e) {
}
cout<<"git version: "<(3000) ));
continue;
}
else
{
cout<setOption("frequency_double", &freq);
if(GLOBALS::get().par_.station_callsign_ == "")
cout< p_ws_server = make_shared(
GLOBALS::get().par_.command_host_ , GLOBALS::get().par_.command_port_);
DECODER.sentence_callback_ =
[p_ws_server](string callsign, string data, string crc)
{
async(launch::async, SentenceCallback, callsign, data, crc, p_ws_server);
};
DECODER.character_callback_ =
[p_ws_server](string rtty_characters)
{
shared_ptr p_msg = make_shared();
p_msg->data_stream_<<"cmd::info:liveprint="<sessions_send(p_msg);
};
// START THREADS
//
unordered_set threads;
// start websocket
threads.emplace( new thread(
[p_ws_server]() { (*p_ws_server)(); }
) );
// start decoder
threads.emplace( new thread(
DECODER_THREAD
) );
for(auto t : threads)
t->join();
return 0;
}