/* 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; }