/* 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 "IQSource/IQSource_SoapySDR.h" #include "Decoder/Decoder.h" #include "common/console_colors.h" #include "habitat/habitat_interface.h" #include "GLOBALS.h" #include "server.h" #include "common/git_repo_sha1.h" bool G_DO_EXIT = false; using namespace std; 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(); auto p_device = SoapySDR::Device::make(device); for(double sr : p_device->listSampleRates(SOAPY_SDR_RX, 0)) { if( abs(sr - 2e6) < sr_diff ) { sr_diff = abs(sr - 2e6); GLOBALS::get().sampling_rate_ = sr; } } } GLOBALS::get().p_iq_source_.reset(new habdec::IQSource_SoapySDR); if(!GLOBALS::get().p_iq_source_) { cout<setOption("SoapySDR_Kwargs", &device); if( !GLOBALS::get().p_iq_source_->init() ) { cout<setOption("gain_double", &gain); double biastee = GLOBALS::get().biast_; GLOBALS::get().p_iq_source_->setOption("biastee_double", &biastee); GLOBALS::get().p_iq_source_->setOption("sampling_rate_double", &GLOBALS::get().sampling_rate_); double usb_pack = GLOBALS::get().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_FEED_THREAD() { using namespace std; cout<<"Start DECODER_FEED_THREAD"<start()) { cout<isRunning()) { cout<setOption("ppm_double", &GLOBALS::get().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(!G_DO_EXIT) { auto _start = std::chrono::high_resolution_clock::now(); size_t count = p_iq_src->get( samples.data(), samples.size() ); 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().afc_) { if( 100 < abs(freq_corr) ) { GLOBALS::get().frequency_ += freq_corr; double f = GLOBALS::get().frequency_; p_iq_src->setOption("frequency_double", &f); DECODER.resetFrequencyCorrection(freq_corr); last_afc_time = std::chrono::high_resolution_clock::now(); // notify webgui { lock_guard _lock( GLOBALS::get().out_commands_mtx_ ); GLOBALS::get().out_commands_.emplace_back("cmd::set:frequency=" + to_string(f / 1e6)); } } } } 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 LOG_THREAD() { using namespace std; while(!G_DO_EXIT) { this_thread::sleep_for( chrono::duration(500) ); vector sentences; { lock_guard _lock( GLOBALS::get().senteces_to_log_mtx_ ); sentences = move( GLOBALS::get().senteces_to_log_ ); } for(auto& sentence : sentences) { if(GLOBALS::get().station_callsign_ != "") { string res = habdec::habitat::HabitatUploadSentence( sentence, GLOBALS::get().station_callsign_ ); if( res != "OK" ) cout< { char do_thousands_sep() const { return ','; } string do_grouping() const { return "\3"; } }; try{ std::cout.imbue( std::locale(locale(""), new thousand_separators) ); } catch(exception& e) { } cout<<"git version: "<setOption("frequency_double", &freq); if(GLOBALS::get().station_callsign_ == "") cout< _lock( GLOBALS::get().senteces_to_log_mtx_ ); GLOBALS::get().senteces_to_log_.emplace_back( callsign + "," + data + "*" + crc ); } { lock_guard _lock( GLOBALS::get().senteces_to_web_mtx_ ); GLOBALS::get().senteces_to_web_.emplace_back( callsign + "," + data + "*" + crc ); } GLOBALS::DumpToFile("./habdecWebsocketServer.opts"); }; // feed decoder with IQ samples std::thread* decoder_feed_thread = new std::thread(DECODER_FEED_THREAD); // HAB upload std::thread* log_thread = new std::thread(LOG_THREAD); // websocket server thread. this call is blocking RunCommandServer( GLOBALS::get().command_host_ , GLOBALS::get().command_port_ ); G_DO_EXIT = true; decoder_feed_thread->join(); log_thread->join(); return 0; }