12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619 |
- /*
- * RTLSDR AM/NFM demodulator, mixer, streamer and recorder
- *
- * Copyright (c) 2014 Wong Man Hang <microtony@gmail.com>
- * Copyright (c) 2015-2021 Tomasz Lemiech <szpajder@gmail.com>
- *
- * This program 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.
- *
- * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "config.h"
- #if defined WITH_BCM_VC && !defined __arm__
- #error Broadcom VideoCore support can only be enabled on ARM builds
- #endif
- // From this point we may safely assume that WITH_BCM_VC implies __arm__
- #ifdef WITH_BCM_VC
- #include "hello_fft/gpu_fft.h"
- #include "hello_fft/mailbox.h"
- #endif /* WITH_BCM_VC */
- //#define GPIOX
- #include <fcntl.h>
- #include <lame/lame.h>
- #include <ogg/ogg.h>
- #include <pthread.h>
- #include <shout/shout.h>
- #include <stdint.h> // uint8_t
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <syslog.h>
- #include <unistd.h>
- #include <vorbis/vorbisenc.h>
- #include <algorithm>
- #include <cassert>
- #include <cerrno>
- #include <cmath>
- #include <csignal>
- #include <cstdarg>
- #include <cstdio>
- #include <cstdlib>
- #include <cstring>
- #include <ctime>
- #include <iostream>
- #include <libconfig.h++>
- #include "input-common.h"
- #include "logging.h"
- #include "rtl_airband.h"
- #include "squelch.h"
- #include <semaphore.h>
- #ifdef WITH_PROFILING
- #include "gperftools/profiler.h"
- #endif /* WITH_PROFILING */
- #include <gpiod.h>
- /*#include <ncurses.h>*/
- #include <iomanip>
- #include <fstream>
- #include <string>
- #include <chrono>
- #include "mqtt/async_client.h"
- #include <ncurses.h>
- #include <curl/curl.h>
- #include <queue>
- #include <jsoncpp/json/json.h>
- #include <nlohmann/json.hpp>
- using namespace std;
- using namespace libconfig;
- struct gpiod_chip *chip;
- struct gpiod_line *led_squ;
- struct gpiod_line *led_vox;
- struct gpiod_line *led_control;
- struct gpiod_line *relay1;
- struct gpiod_line *relay2;
- #define MAX_BUFFERS 10
- #define GPIOX
- using json = nlohmann::json;
- sem_t mutex_lock_main_thread, mutex_unlock_main_thread;
- sem_t mutex_lock_main_thread_mqtt, mutex_unlock_main_thread_mqtt;
- pthread_mutex_t lock_data;
- pthread_mutex_t lock_mqtt_data;
- device_t* devices;
- mixer_t* mixers;
- long int start_time;
- long int stop_time;
- int bel_start_on_time = 0;
- int bel_open_time = 0;
- int bel_prolong_time = 0;
- float bel_wave_out_level = 0;
- float bel_wave_in_level = 0;
- int bel_blink_time = 0;
- bool mqtt_activate=false;
- char* bel_mqtt_server;
- char* bel_mqtt_client_id;
- char* bel_mqtt_user_id;
- char* bel_mqtt_password;
- int squelch_status = 0;
- int audio_status = 0;
- int status_data = 0;
- int status_data_old = 0;
- int device_count, mixer_count;
- static int devices_running = 0;
- int tui = 0; // do not display textual user interface
- int shout_metadata_delay = 3;
- volatile int do_exit = 0;
- bool use_localtime = false;
- bool multiple_demod_threads = false;
- bool multiple_output_threads = false;
- bool log_scan_activity = false;
- char* stats_filepath = NULL;
- size_t fft_size_log = DEFAULT_FFT_SIZE_LOG;
- size_t fft_size = 1 << fft_size_log;
- #ifdef NFM
- float alpha = exp(-1.0f / (WAVE_RATE * 2e-4));
- enum fm_demod_algo { FM_FAST_ATAN2, FM_QUADRI_DEMOD };
- enum fm_demod_algo fm_demod = FM_FAST_ATAN2;
- #endif /* NFM */
- #ifdef DEBUG
- char* debug_path;
- #endif /* DEBUG */
- void sighandler(int sig) {
- log(LOG_NOTICE, "Got signal %d, exiting\n", sig);
- do_exit = 1;
- }
- template <typename T>
- class TSQueue {
- private:
- // Underlying queue
- std::queue<T> m_queue;
- // mutex for thread synchronization
- std::mutex m_mutex;
- // Condition variable for signaling
- std::condition_variable m_cond;
- public:
- // Pushes an element to the queue
- void push(T item)
- {
- // Acquire lock
- std::unique_lock<std::mutex> lock(m_mutex);
- // Add item
- m_queue.push(item);
- // Notify one thread that
- // is waiting
- m_cond.notify_one();
- }
- // Pops an element off the queue
- T pop()
- {
- // acquire lock
- std::unique_lock<std::mutex> lock(m_mutex);
- // wait until queue is not empty
- m_cond.wait(lock,
- [this]() { return !m_queue.empty(); });
- // retrieve item
- T item = m_queue.front();
- m_queue.pop();
- // return item
- return item;
- }
- int size() { return m_queue.size(); }
- };
- TSQueue<mqtt_msg_t> mqtt_queue;
- void* controller_thread(void* params) {
- device_t* dev = (device_t*)params;
- int i = 0;
- int consecutive_squelch_off = 0;
- int new_centerfreq = 0;
- struct timeval tv;
- if (dev->channels[0].freq_count < 2)
- return 0;
- while (!do_exit) {
- SLEEP(200);
- if (dev->channels[0].axcindicate == NO_SIGNAL) {
- if (consecutive_squelch_off < 10) {
- consecutive_squelch_off++;
- } else {
- i++;
- i %= dev->channels[0].freq_count;
- dev->channels[0].freq_idx = i;
- new_centerfreq = dev->channels[0].freqlist[i].frequency + 20 * (double)(dev->input->sample_rate / fft_size);
- if (input_set_centerfreq(dev->input, new_centerfreq) < 0) {
- break;
- }
- }
- } else {
- if (consecutive_squelch_off == 10) {
- if (log_scan_activity)
- log(LOG_INFO, "Activity on %7.3f MHz\n", dev->channels[0].freqlist[i].frequency / 1000000.0);
- if (i != dev->last_frequency) {
- // squelch has just opened on a new frequency - we might need to update outputs' metadata
- gettimeofday(&tv, NULL);
- tag_queue_put(dev, i, tv);
- dev->last_frequency = i;
- }
- }
- consecutive_squelch_off = 0;
- }
- }
- return 0;
- }
- void multiply(float ar, float aj, float br, float bj, float* cr, float* cj) {
- *cr = ar * br - aj * bj;
- *cj = aj * br + ar * bj;
- }
- class PublisherCallback : public virtual mqtt::callback
- {
- public:
- void connection_lost(const std::string& cause) override
- {
- std::cout << "Connection lost: " << cause << std::endl;
- }
- void delivery_complete(mqtt::delivery_token_ptr token) override
- {
- token=token;
- std::cout << "Message delivered" << std::endl;
- }
- void message_arrived (mqtt::const_message_ptr msg) override
- {
- std::cout << "Message arrived: " << msg->get_payload_str() << std::endl;
- mqtt_activate=true;
- }
- };
- void* mqtt_control_thread(void*){
- string address = bel_mqtt_server,
- clientID = bel_mqtt_client_id,
- username = bel_mqtt_user_id,
- password = bel_mqtt_password;
- const std::string TOPIC1("device/"+username+"/sq");
- const std::string TOPIC2("device/"+username+"/light");
- const int QOS = 1;
- //const int TIMEOUT = 10000;
- mqtt::async_client client(address, clientID,mqtt::create_options(MQTTVERSION_5));
- mqtt::connect_options connOpts;
- mqtt::ssl_options ssl_options;
- connOpts.set_keep_alive_interval(20);
- connOpts.set_clean_session(true);
- connOpts.set_user_name(username);
- connOpts.set_password(password);
- connOpts.set_ssl(ssl_options);
-
- connOpts.set_automatic_reconnect(10, 40);
- try
- {
- PublisherCallback callback;
- client.set_callback(callback);
- mqtt::token_ptr connectionToken = client.connect(connOpts);
- connectionToken->wait();
- mqtt::token_ptr subToken = client.subscribe(TOPIC2, QOS);
- subToken->wait();
- while (true)
- {
- mqtt_msg_t temp_mess;
- //printf("Size %i\n",mqtt_queue.size());
- temp_mess=mqtt_queue.pop();
- Json::Value event;
- event["type"]=temp_mess.type;
- if (temp_mess.type==1){
- event["sq"]=temp_mess.sq;
- event["freq"]=temp_mess.freq;
- event["name"]=temp_mess.name;
-
- event["mountpoint"]=temp_mess.mountpoint;
- }
- if (temp_mess.type==2){
- event["bel_status"]=temp_mess.bel_status;
- }
- if (temp_mess.type==2){
- event["type"]=temp_mess.type;
- event["status"]="connect";
- }
- Json::StreamWriterBuilder builder;
- if (client.is_connected()){
- mqtt::message_ptr pubMessage = mqtt::make_message(TOPIC1,Json::writeString(builder, event), QOS, false);
- client.publish(pubMessage)->wait();
- }
- }
- mqtt::token_ptr disconnectionToken = client.disconnect();
- disconnectionToken->wait();
- }
- catch (const mqtt::exception& ex)
- {
- std::cerr << "MQTT Exception: " << ex.what() << std::endl;
- return 0;
- }
- printf("HEJ3");
- return 0;
- }
- void* belysning_control_thread(void*){
- struct timespec ts;
- struct timeval tv;
- long int sq_start_time=0;
- long int sq_stop_time=0;
- bool sq_status = false;
- long int audio_start_time=0;
- bool active=false;
- char *str ;
- memset(&str, 0, sizeof(str));
- mqtt_msg_t mqtt_msg;
- mqtt_msg.sq = 0;
- mqtt_msg.freq = 0;
- mqtt_msg.name = str;
- mqtt_msg.audio = 0;
- mqtt_msg.mountpoint = str;
- mqtt_msg.bel_status = -1;
- mqtt_msg.type=2;
- int status_data_temp=0;
- gettimeofday(&tv, 0);
- start_time=tv.tv_sec;
- stop_time=tv.tv_sec+1;
- while (!do_exit) {
- status_data=status_data;
- int sem_try=0;
- bool activate=false;
- bool prolong=false;
- if (clock_gettime(CLOCK_REALTIME, &ts) == -1 && tui)
- printf("clock_gettime");
- ts.tv_sec +=1;
- sem_try = sem_timedwait(&mutex_lock_main_thread, &ts);
- if (sem_try == 0){
- status_data_temp=status_data;
- sem_post(&mutex_unlock_main_thread);
- if (sq_status == false && (status_data_temp & 1)){
- sq_start_time = tv.tv_sec;
- sq_status = true;
- }
- if (sq_status == true && !(status_data_temp & 1)){
- sq_stop_time = tv.tv_sec;
- sq_status = false;
- }
- //printf("ST %u, %u ",(status_data & 1),(status_data >> 1));
- if ( (status_data_temp >> 1) ){
- audio_start_time = tv.tv_sec;
- }
- prolong = true;
-
- }
- if (sq_status && sq_start_time < tv.tv_sec){
- prolong = true;
- }else{
- prolong = false;
- }
- //printf("DATA %u, %ld, %ld, %ld %u %ld\n",sq_status,sq_start_time,audio_start_time,tv.tv_sec,prolong,stop_time);
- //Check if we should start
- if (sq_status == true && sq_start_time+bel_open_time < tv.tv_sec && audio_start_time+bel_open_time < tv.tv_sec){
- sq_start_time=sq_start_time;
- sq_stop_time=sq_stop_time;
- activate=true;
- }
- gettimeofday(&tv, 0);
- //First time activation
- if ((active==false && activate==true) || mqtt_activate){
- start_time=tv.tv_sec;
- stop_time=tv.tv_sec+bel_start_on_time;
- mqtt_activate=false;
- active=true;
- mqtt_msg.bel_status = 1;
- mqtt_queue.push(mqtt_msg);
- if (tui)
- printf("Start\n");
- }
- // Prolong
- if (active==true && prolong==true){
-
- if (tv.tv_sec+bel_prolong_time >= stop_time){
- stop_time=tv.tv_sec+bel_prolong_time;
- if (tui)
- printf("Prolong %u\n",bel_prolong_time);
- mqtt_msg.bel_status = 2;
- mqtt_queue.push(mqtt_msg);
- prolong = false;
- }
- }
- if (stop_time<=tv.tv_sec && active){
- active=false;
- start_time=start_time;
- mqtt_msg.bel_status = 0;
- mqtt_queue.push(mqtt_msg);
- if (tui)
- printf("Stop\n");
- }
- #ifdef GPIOX
- if (active)
- if ((stop_time<tv.tv_sec+bel_blink_time) && tv.tv_sec % 2 && bel_blink_time ){
- gpiod_line_set_value(led_control, 0);
- gpiod_line_set_value(relay1, 1);
- gpiod_line_set_value(relay2, 1);
- }else{
- gpiod_line_set_value(led_control, 1);
- gpiod_line_set_value(relay1, 0);
- gpiod_line_set_value(relay2, 0);
- }
- else{
- gpiod_line_set_value(led_control, 0);
- gpiod_line_set_value(relay1, 1);
- gpiod_line_set_value(relay2, 1);
- }
- #endif
- }
- return 0;
- }
- #ifdef NFM
- float fast_atan2(float y, float x) {
- float yabs, angle;
- float pi4 = M_PI_4, pi34 = 3 * M_PI_4;
- if (x == 0.0f && y == 0.0f) {
- return 0;
- }
- yabs = y;
- if (yabs < 0.0f) {
- yabs = -yabs;
- }
- if (x >= 0.0f) {
- angle = pi4 - pi4 * (x - yabs) / (x + yabs);
- } else {
- angle = pi34 - pi4 * (x + yabs) / (yabs - x);
- }
- if (y < 0.0f) {
- return -angle;
- }
- return angle;
- }
- float polar_disc_fast(float ar, float aj, float br, float bj) {
- float cr, cj;
- multiply(ar, aj, br, -bj, &cr, &cj);
- return (float)(fast_atan2(cj, cr) * M_1_PI);
- }
- float fm_quadri_demod(float ar, float aj, float br, float bj) {
- return (float)((br * aj - ar * bj) / (ar * ar + aj * aj + 1.0f) * M_1_PI);
- }
- #endif /* NFM */
- class AFC {
- const status _prev_axcindicate;
- #ifdef WITH_BCM_VC
- float square(const GPU_FFT_COMPLEX* fft_results, size_t index) {
- return fft_results[index].re * fft_results[index].re + fft_results[index].im * fft_results[index].im;
- }
- #else
- float square(const fftwf_complex* fft_results, size_t index) {
- return fft_results[index][0] * fft_results[index][0] + fft_results[index][1] * fft_results[index][1];
- }
- #endif /* WITH_BCM_VC */
- template <class FFT_RESULTS, int STEP>
- size_t check(const FFT_RESULTS* fft_results, const size_t base, const float base_value, unsigned char afc) {
- float threshold = 0;
- size_t bin;
- for (bin = base;; bin += STEP) {
- if (STEP < 0) {
- if (bin < -STEP)
- break;
- } else if ((size_t)(bin + STEP) >= fft_size)
- break;
- const float value = square(fft_results, (size_t)(bin + STEP));
- if (value <= base_value)
- break;
- if (base == (size_t)bin) {
- threshold = (value - base_value) / (float)afc;
- } else {
- if ((value - base_value) < threshold)
- break;
- threshold += threshold / 10.0;
- }
- }
- return bin;
- }
- public:
- AFC(device_t* dev, int index) : _prev_axcindicate(dev->channels[index].axcindicate) {}
- template <class FFT_RESULTS>
- void finalize(device_t* dev, int index, const FFT_RESULTS* fft_results) {
- channel_t* channel = &dev->channels[index];
- if (channel->afc == 0)
- return;
- const char axcindicate = channel->axcindicate;
- if (axcindicate != NO_SIGNAL && _prev_axcindicate == NO_SIGNAL) {
- const size_t base = dev->base_bins[index];
- const float base_value = square(fft_results, base);
- size_t bin = check<FFT_RESULTS, -1>(fft_results, base, base_value, channel->afc);
- if (bin == base)
- bin = check<FFT_RESULTS, 1>(fft_results, base, base_value, channel->afc);
- if (dev->bins[index] != bin) {
- #ifdef AFC_LOGGING
- log(LOG_INFO, "AFC device=%d channel=%d: base=%zu prev=%zu now=%zu\n", dev->device, index, base, dev->bins[index], bin);
- #endif /* AFC_LOGGING */
- dev->bins[index] = bin;
- if (bin > base)
- channel->axcindicate = AFC_UP;
- else if (bin < base)
- channel->axcindicate = AFC_DOWN;
- }
- } else if (axcindicate == NO_SIGNAL && _prev_axcindicate != NO_SIGNAL)
- dev->bins[index] = dev->base_bins[index];
- }
- };
- void init_demod(demod_params_t* params, Signal* signal, int device_start, int device_end) {
- assert(params != NULL);
- assert(signal != NULL);
- params->mp3_signal = signal;
- params->device_start = device_start;
- params->device_end = device_end;
- #ifndef WITH_BCM_VC
- params->fftin = fftwf_alloc_complex(fft_size);
- params->fftout = fftwf_alloc_complex(fft_size);
- params->fft = fftwf_plan_dft_1d(fft_size, params->fftin, params->fftout, FFTW_FORWARD, FFTW_MEASURE);
- #endif /* WITH_BCM_VC */
- }
- void init_output(output_params_t* params, int device_start, int device_end, int mixer_start, int mixer_end) {
- assert(params != NULL);
- params->mp3_signal = new Signal;
- params->device_start = device_start;
- params->device_end = device_end;
- params->mixer_start = mixer_start;
- params->mixer_end = mixer_end;
- }
- int next_device(demod_params_t* params, int current) {
- current++;
- if (current < params->device_end) {
- return current;
- }
- return params->device_start;
- }
- void* demodulate(void* params) {
- assert(params != NULL);
- demod_params_t* demod_params = (demod_params_t*)params;
-
- debug_print("Starting demod thread, devices %d:%d, signal %p\n", demod_params->device_start, demod_params->device_end, demod_params->mp3_signal);
- // initialize fft engine
- #ifdef WITH_BCM_VC
- int mb = mbox_open();
- struct GPU_FFT* fft;
- int ret = gpu_fft_prepare(mb, fft_size_log, GPU_FFT_FWD, FFT_BATCH, &fft);
- switch (ret) {
- case -1:
- log(LOG_CRIT, "Unable to enable V3D. Please check your firmware is up to date.\n");
- error();
- break;
- case -2:
- log(LOG_CRIT, "log2_N=%d not supported. Try between 8 and 17.\n", fft_size_log);
- error();
- break;
- case -3:
- log(LOG_CRIT, "Out of memory. Try a smaller batch or increase GPU memory.\n");
- error();
- break;
- }
- #else
- fftwf_complex* fftin = demod_params->fftin;
- fftwf_complex* fftout = demod_params->fftout;
- #endif /* WITH_BCM_VC */
- float ALIGNED32 levels_u8[256], levels_s8[256];
- float* levels_ptr = NULL;
- for (int i = 0; i < 256; i++) {
- levels_u8[i] = (i - 127.5f) / 127.5f;
- }
- for (int16_t i = -127; i < 128; i++) {
- levels_s8[(uint8_t)i] = i / 128.0f;
- }
- // initialize fft window
- // blackman 7
- // the whole matrix is computed
- #ifdef WITH_BCM_VC
- float ALIGNED32 window[fft_size * 2];
- #else
- float ALIGNED32 window[fft_size];
- #endif /* WITH_BCM_VC */
- const double a0 = 0.27105140069342f;
- const double a1 = 0.43329793923448f;
- const double a2 = 0.21812299954311f;
- const double a3 = 0.06592544638803f;
- const double a4 = 0.01081174209837f;
- const double a5 = 0.00077658482522f;
- const double a6 = 0.00001388721735f;
- for (size_t i = 0; i < fft_size; i++) {
- double x = a0 - (a1 * cos((2.0 * M_PI * i) / (fft_size - 1))) + (a2 * cos((4.0 * M_PI * i) / (fft_size - 1))) - (a3 * cos((6.0 * M_PI * i) / (fft_size - 1))) +
- (a4 * cos((8.0 * M_PI * i) / (fft_size - 1))) - (a5 * cos((10.0 * M_PI * i) / (fft_size - 1))) + (a6 * cos((12.0 * M_PI * i) / (fft_size - 1)));
- #ifdef WITH_BCM_VC
- window[i * 2] = window[i * 2 + 1] = (float)x;
- #else
- window[i] = (float)x;
- #endif /* WITH_BCM_VC */
- }
- #ifdef DEBUG
- struct timeval ts, te;
- gettimeofday(&ts, NULL);
- #endif /* DEBUG */
- size_t available;
- int device_num = demod_params->device_start;
- while (true) {
- if (do_exit) {
- #ifdef WITH_BCM_VC
- log(LOG_INFO, "Freeing GPU memory\n");
- gpu_fft_release(fft);
- #endif /* WITH_BCM_VC */
- return NULL;
- }
- device_t* dev = devices + device_num;
- pthread_mutex_lock(&dev->input->buffer_lock);
- if (dev->input->bufe >= dev->input->bufs)
- available = dev->input->bufe - dev->input->bufs;
- else
- available = dev->input->buf_size - dev->input->bufs + dev->input->bufe;
- pthread_mutex_unlock(&dev->input->buffer_lock);
- if (devices_running == 0) {
- log(LOG_ERR, "All receivers failed, exiting\n");
- do_exit = 1;
- continue;
- }
- if (dev->input->state != INPUT_RUNNING) {
- if (dev->input->state == INPUT_FAILED) {
- dev->input->state = INPUT_DISABLED;
- disable_device_outputs(dev);
- devices_running--;
- }
- device_num = next_device(demod_params, device_num);
- continue;
- }
- // number of input bytes per output wave sample (x 2 for I and Q)
- size_t bps = 2 * dev->input->bytes_per_sample * (size_t)round((double)dev->input->sample_rate / (double)WAVE_RATE);
- if (available < bps * FFT_BATCH + fft_size * dev->input->bytes_per_sample * 2) {
- // move to next device
- device_num = next_device(demod_params, device_num);
- SLEEP(10);
- continue;
- }
- if (dev->input->sfmt == SFMT_S16) {
- float const scale = 1.0f / dev->input->fullscale;
- #ifdef WITH_BCM_VC
- struct GPU_FFT_COMPLEX* ptr = fft->in;
- for (size_t b = 0; b < FFT_BATCH; b++, ptr += fft->step) {
- short* buf2 = (short*)(dev->input->buffer + dev->input->bufs + b * bps);
- for (size_t i = 0; i < fft_size; i++, buf2 += 2) {
- ptr[i].re = scale * (float)buf2[0] * window[i * 2];
- ptr[i].im = scale * (float)buf2[1] * window[i * 2];
- }
- }
- #else
- short* buf2 = (short*)(dev->input->buffer + dev->input->bufs);
- for (size_t i = 0; i < fft_size; i++, buf2 += 2) {
- fftin[i][0] = scale * (float)buf2[0] * window[i];
- fftin[i][1] = scale * (float)buf2[1] * window[i];
- }
- #endif /* WITH_BCM_VC */
- } else if (dev->input->sfmt == SFMT_F32) {
- float const scale = 1.0f / dev->input->fullscale;
- #ifdef WITH_BCM_VC
- struct GPU_FFT_COMPLEX* ptr = fft->in;
- for (size_t b = 0; b < FFT_BATCH; b++, ptr += fft->step) {
- float* buf2 = (float*)(dev->input->buffer + dev->input->bufs + b * bps);
- for (size_t i = 0; i < fft_size; i++, buf2 += 2) {
- ptr[i].re = scale * buf2[0] * window[i * 2];
- ptr[i].im = scale * buf2[1] * window[i * 2];
- }
- }
- #else // WITH_BCM_VC
- float* buf2 = (float*)(dev->input->buffer + dev->input->bufs);
- for (size_t i = 0; i < fft_size; i++, buf2 += 2) {
- fftin[i][0] = scale * buf2[0] * window[i];
- fftin[i][1] = scale * buf2[1] * window[i];
- }
- #endif /* WITH_BCM_VC */
- } else { // S8 or U8
- levels_ptr = (dev->input->sfmt == SFMT_U8 ? levels_u8 : levels_s8);
- #ifdef WITH_BCM_VC
- sample_fft_arg sfa = {fft_size / 4, fft->in};
- for (size_t i = 0; i < FFT_BATCH; i++) {
- samplefft(&sfa, dev->input->buffer + dev->input->bufs + i * bps, window, levels_ptr);
- sfa.dest += fft->step;
- }
- #else
- unsigned char* buf2 = dev->input->buffer + dev->input->bufs;
- for (size_t i = 0; i < fft_size; i++, buf2 += 2) {
- fftin[i][0] = levels_ptr[buf2[0]] * window[i];
- fftin[i][1] = levels_ptr[buf2[1]] * window[i];
- }
- #endif /* WITH_BCM_VC */
- }
- #ifdef WITH_BCM_VC
- gpu_fft_execute(fft);
- #else
- fftwf_execute(demod_params->fft);
- #endif /* WITH_BCM_VC */
- #ifdef WITH_BCM_VC
- for (int i = 0; i < dev->channel_count; i++) {
- float* wavein = dev->channels[i].wavein + dev->waveend;
- __builtin_prefetch(wavein, 1);
- const int bin = dev->bins[i];
- const GPU_FFT_COMPLEX* fftout = fft->out + bin;
- for (int j = 0; j < FFT_BATCH; j++, ++wavein, fftout += fft->step)
- *wavein = sqrtf(fftout->im * fftout->im + fftout->re * fftout->re);
- }
- for (int j = 0; j < dev->channel_count; j++) {
- if (dev->channels[j].needs_raw_iq) {
- struct GPU_FFT_COMPLEX* ptr = fft->out;
- for (int job = 0; job < FFT_BATCH; job++) {
- dev->channels[j].iq_in[2 * (dev->waveend + job)] = ptr[dev->bins[j]].re;
- dev->channels[j].iq_in[2 * (dev->waveend + job) + 1] = ptr[dev->bins[j]].im;
- ptr += fft->step;
- }
- }
- }
- #else
- for (int j = 0; j < dev->channel_count; j++) {
- dev->channels[j].wavein[dev->waveend] = sqrtf(fftout[dev->bins[j]][0] * fftout[dev->bins[j]][0] + fftout[dev->bins[j]][1] * fftout[dev->bins[j]][1]);
- if (dev->channels[j].needs_raw_iq) {
- dev->channels[j].iq_in[2 * dev->waveend] = fftout[dev->bins[j]][0];
- dev->channels[j].iq_in[2 * dev->waveend + 1] = fftout[dev->bins[j]][1];
- }
- }
- #endif /* WITH_BCM_VC */
- dev->waveend += FFT_BATCH;
- if (dev->waveend >= WAVE_BATCH + AGC_EXTRA) {
- for (int i = 0; i < dev->channel_count; i++) {
- AFC afc(dev, i);
- channel_t* channel = dev->channels + i;
- freq_t* fparms = channel->freqlist + channel->freq_idx;
- // set to NO_SIGNAL, will be updated to SIGNAL based on squelch below
- channel->axcindicate = NO_SIGNAL;
- for (int j = AGC_EXTRA; j < WAVE_BATCH + AGC_EXTRA; j++) {
- float& real = channel->iq_in[2 * (j - AGC_EXTRA)];
- float& imag = channel->iq_in[2 * (j - AGC_EXTRA) + 1];
- fparms->squelch.process_raw_sample(channel->wavein[j]);
- // If squelch is open / opening and using I/Q, then cleanup the signal and possibly update squelch.
- if (fparms->squelch.should_filter_sample() && channel->needs_raw_iq) {
- // remove phase rotation introduced by FFT sliding window
- float swf, cwf, re_tmp, im_tmp;
- sincosf_lut(channel->dm_phi, &swf, &cwf);
- multiply(real, imag, cwf, -swf, &re_tmp, &im_tmp);
- channel->dm_phi += channel->dm_dphi;
- channel->dm_phi &= 0xffffff;
- // apply lowpass filter, will be a no-op if not configured
- fparms->lowpass_filter.apply(re_tmp, im_tmp);
- // update I/Q and wave
- real = re_tmp;
- imag = im_tmp;
- channel->wavein[j] = sqrt(real * real + imag * imag);
- // update squelch post-cleanup
- if (fparms->lowpass_filter.enabled()) {
- fparms->squelch.process_filtered_sample(channel->wavein[j]);
- }
- }
- if (fparms->modulation == MOD_AM) {
- // if squelch is just opening then bootstrip agcavgfast with prior values of wavein
- if (fparms->squelch.first_open_sample()) {
- for (int k = j - AGC_EXTRA; k < j; k++) {
- if (channel->wavein[k] >= fparms->squelch.squelch_level()) {
- fparms->agcavgfast = fparms->agcavgfast * 0.9f + channel->wavein[k] * 0.1f;
- }
- }
- }
- // if squelch is just closing then fade out the prior samples of waveout
- else if (fparms->squelch.last_open_sample()) {
- for (int k = j - AGC_EXTRA + 1; k < j; k++) {
- channel->waveout[k] = channel->waveout[k - 1] * 0.94f;
- }
- }
- }
- float& waveout = channel->waveout[j];
- // If squelch sees power then do modulation-specific processing
- if (fparms->squelch.should_process_audio()) {
- if (fparms->modulation == MOD_AM) {
- if (channel->wavein[j] > fparms->squelch.squelch_level()) {
- fparms->agcavgfast = fparms->agcavgfast * 0.995f + channel->wavein[j] * 0.005f;
- }
- waveout = (channel->wavein[j - AGC_EXTRA] - fparms->agcavgfast) / (fparms->agcavgfast * 1.5f);
- if (abs(waveout) > 0.8f) {
- waveout *= 0.85f;
- fparms->agcavgfast *= 1.15f;
- }
-
- if ((abs(waveout)>bel_wave_out_level && channel->wavein[j]>bel_wave_in_level) ){
- #ifdef GPIOX
- gpiod_line_set_value(led_vox, 1);
- #endif
- audio_status=1;
- }else{
- #ifdef GPIOX
- gpiod_line_set_value(led_vox, 0);
- #endif
- audio_status=0;
- }
- squelch_status = channel->freqlist[channel->freq_idx].oldstatus & 1;
- int temp_status = squelch_status + (audio_status << 1);
- if (channel->freqlist[channel->freq_idx].oldstatus !=temp_status && (fparms->banbel)){
- pthread_mutex_lock(&lock_data);
- //fprintf(stderr,"HEJ %i,%i,%f,%f\n",channel->freqlist[channel->freq_idx].oldstatus ,temp_status,abs(waveout),channel->wavein[j]);
- status_data = temp_status;
- if (sem_post (&mutex_lock_main_thread) == -1) {
- perror ("sem_post: mutex_lock_main_thread"); exit (1);
- }
- if (sem_wait(&mutex_unlock_main_thread) == -1){
- perror ("sem_post: mutex_unlock_main_thread"); exit (1);
- }
- //channel->freqlist[channel->freq_idx].oldstatus = temp_status;;
- pthread_mutex_unlock(&lock_data);
-
- }
-
- }
- #ifdef NFM
- else if (fparms->modulation == MOD_NFM) {
- // FM demod
- if (fm_demod == FM_FAST_ATAN2) {
- waveout = polar_disc_fast(real, imag, channel->pr, channel->pj);
- } else if (fm_demod == FM_QUADRI_DEMOD) {
- waveout = fm_quadri_demod(real, imag, channel->pr, channel->pj);
- }
- channel->pr = real;
- channel->pj = imag;
- // de-emphasis IIR + DC blocking
- fparms->agcavgfast = fparms->agcavgfast * 0.995f + waveout * 0.005f;
- waveout -= fparms->agcavgfast;
- waveout = waveout * (1.0f - channel->alpha) + channel->prev_waveout * channel->alpha;
- // save off waveout before notch and ampfactor
- channel->prev_waveout = waveout;
- }
- #endif /* NFM */
- // process audio sample for CTCSS, will be no-op if not configured
- fparms->squelch.process_audio_sample(waveout);
- }
- // If squelch is still open then save samples to output
- if (fparms->squelch.is_open()) {
- // apply the notch filter, will be a no-op if not configured
- fparms->notch_filter.apply(waveout);
- // apply the ampfactor
- waveout *= fparms->ampfactor;
- // make sure the value is between +/- 1 (requirement for libmp3lame)
- if (isnan(waveout)) {
- waveout = 0.0;
- } else if (waveout > 1.0) {
- waveout = 1.0;
- } else if (waveout < -1.0) {
- waveout = -1.0;
- }
- channel->axcindicate = SIGNAL;
- if (channel->has_iq_outputs) {
- channel->iq_out[2 * (j - AGC_EXTRA)] = real;
- channel->iq_out[2 * (j - AGC_EXTRA) + 1] = imag;
- }
- // Squelch is closed
- } else {
- waveout = 0;
- if (channel->has_iq_outputs) {
- channel->iq_out[2 * (j - AGC_EXTRA)] = 0;
- channel->iq_out[2 * (j - AGC_EXTRA) + 1] = 0;
- }
- }
- }
- memmove(channel->wavein, channel->wavein + WAVE_BATCH, (dev->waveend - WAVE_BATCH) * sizeof(float));
- if (channel->needs_raw_iq) {
- memmove(channel->iq_in, channel->iq_in + 2 * WAVE_BATCH, (dev->waveend - WAVE_BATCH) * sizeof(float) * 2);
- }
- #ifdef WITH_BCM_VC
- afc.finalize(dev, i, fft->out);
- #else
- afc.finalize(dev, i, demod_params->fftout);
- #endif /* WITH_BCM_VC */
- if (tui || 1==1) {
- char symbol = fparms->squelch.signal_outside_filter() ? '~' : (char)channel->axcindicate;
- if (dev->mode == R_SCAN) {
- //GOTOXY(0, device_num * 17 + dev->row + 3);
- //printf("%4.0f/%3.0f%c %7.3f ", level_to_dBFS(fparms->squelch.signal_level()), level_to_dBFS(fparms->squelch.noise_level()), symbol,
- // (dev->channels[0].freqlist[channel->freq_idx].frequency / 1000000.0));
- } else {
- //GOTOXY(i * 10, device_num * 17 + dev->row + 3);
- //printf("%4.0f/%3.0f%c ", level_to_dBFS(fparms->squelch.signal_level()), level_to_dBFS(fparms->squelch.noise_level()), symbol);
- }
- //BANBEL
-
- if (symbol=='*'){
- #ifdef GPIOX
- gpiod_line_set_value(led_squ, 1);
- #endif
- squelch_status = 1;
- }else{
- #ifdef GPIOX
- gpiod_line_set_value(led_squ, 0);
- #endif
- squelch_status = 0;
- }
- int temp_status = squelch_status + (audio_status << 1);
- if (channel->freqlist[channel->freq_idx].oldstatus != temp_status){
- //fprintf(stderr,"BABEL %i %i\n",fparms->banbel,temp_status);
- if (fparms->banbel){
- pthread_mutex_lock(&lock_data);
- status_data = temp_status;
- if (sem_post (&mutex_lock_main_thread) == -1) {
- perror ("sem_post: mutex_lock_main_thread"); exit (1);
- }
-
- if (sem_wait(&mutex_unlock_main_thread) == -1){
- perror ("sem_post: mutex_unlock_main_thread"); exit (1);
- }
- pthread_mutex_unlock(&lock_data);
- }
-
- if ((temp_status & 1) != (channel->freqlist[channel->freq_idx].oldstatus & 1)) {
- mqtt_msg_t mqtt_msg;
- mqtt_msg.audio =(audio_status << 1);
- mqtt_msg.sq = squelch_status;
- mqtt_msg.freq = channel->freqlist->frequency;
- mqtt_msg.name = fparms->label;
- mqtt_msg.mountpoint = fparms->mountpoint;
- mqtt_msg.bel_status = -1;
- mqtt_msg.type=1;
- mqtt_queue.push(mqtt_msg);
- }
- channel->freqlist[channel->freq_idx].oldstatus = temp_status;
- //Here for mqtt
- }
- fflush(stdout);
- }
- if (channel->axcindicate != NO_SIGNAL) {
- channel->freqlist[channel->freq_idx].active_counter++;
- }
- }
- if (dev->waveavail == 1) {
- debug_print("devices[%d]: output channel overrun\n", device_num);
- dev->output_overrun_count++;
- } else {
- dev->waveavail = 1;
- }
- dev->waveend -= WAVE_BATCH;
- #ifdef DEBUG
- gettimeofday(&te, NULL);
- debug_bulk_print("waveavail %lu.%lu %lu\n", te.tv_sec, (unsigned long)te.tv_usec, (te.tv_sec - ts.tv_sec) * 1000000UL + te.tv_usec - ts.tv_usec);
- ts.tv_sec = te.tv_sec;
- ts.tv_usec = te.tv_usec;
- #endif /* DEBUG */
- demod_params->mp3_signal->send();
- dev->row++;
- if (dev->row == 12) {
- dev->row = 0;
- }
- }
- dev->input->bufs = (dev->input->bufs + bps * FFT_BATCH) % dev->input->buf_size;
- device_num = next_device(demod_params, device_num);
- }
- }
- void usage() {
- cout << "Usage: rtl_airband [options] [-c <config_file_path>]\n\
- \t-h\t\t\tDisplay this help text\n\
- \t-f\t\t\tRun in foreground, display textual waterfalls\n\
- \t-F\t\t\tRun in foreground, do not display waterfalls (for running as a systemd service)\n";
- #ifdef NFM
- cout << "\t-Q\t\t\tUse quadri correlator for FM demodulation (default is atan2)\n";
- #endif /* NFM */
- #ifdef DEBUG
- cout << "\t-d <file>\t\tLog debugging information to <file> (default is " << DEBUG_PATH << ")\n";
- #endif /* DEBUG */
- cout << "\t-e\t\t\tPrint messages to standard error (disables syslog logging)\n";
- cout << "\t-c <config_file_path>\tUse non-default configuration file\n\t\t\t\t(default: " << CFGFILE << ")\n\
- \t-v\t\t\tDisplay version and exit\n";
- exit(EXIT_SUCCESS);
- }
- static int count_devices_running() {
- int ret = 0;
- for (int i = 0; i < device_count; i++) {
- if (devices[i].input->state == INPUT_RUNNING) {
- ret++;
- }
- }
- return ret;
- }
- int main(int argc, char* argv[]) {
- #ifdef WITH_PROFILING
- ProfilerStart("rtl_airband.prof");
- #endif /* WITH_PROFILING */
- // initialization
- sem_init (&mutex_lock_main_thread, 1, 0);
- sem_init (&mutex_unlock_main_thread, 1, 0);
- if (pthread_mutex_init(&lock_data, NULL) != 0) {
- printf("\n mutex init has failed\n");
- return 1;
- }
- if (pthread_mutex_init(&lock_mqtt_data, NULL) != 0) {
- printf("\n mutex init has failed\n");
- return 1;
- }
- printf("SEM and locks OPEN\n");
- #ifdef GPIOX
- char const *chipname = "gpiochip0";
- unsigned int gpio16 = 16;
- unsigned int gpio20 = 20;
- unsigned int gpio26 = 26;
- unsigned int gpio24 = 24;
- unsigned int gpio25 = 25;
- chip = gpiod_chip_open_by_name(chipname);
- if (!chip) {
- perror("gpiod_chip_open_by_name");
- return 1;
- }
- led_squ = gpiod_chip_get_line(chip, gpio26);
- led_vox = gpiod_chip_get_line(chip, gpio20);
- led_control = gpiod_chip_get_line(chip, gpio16);
- relay1 = gpiod_chip_get_line(chip, gpio25);
- relay2 = gpiod_chip_get_line(chip, gpio24);
- #endif
- #ifdef GPIOX
- gpiod_line_request_output(led_squ, "Consumer1", 0);
- gpiod_line_set_value(led_squ, 0);
- gpiod_line_request_output(led_vox, "Consumer2", 0);
- gpiod_line_set_value(led_vox, 0);
- gpiod_line_request_output(led_control, "Consumer3", 0);
- gpiod_line_set_value(led_control, 0);
- gpiod_line_request_output(relay1, "Consumer4", 0);
- gpiod_line_set_value(relay1, 1);
- gpiod_line_request_output(relay2, "Consumer5", 0);
- gpiod_line_set_value(relay2, 1);
- #endif
- #pragma GCC diagnostic ignored "-Wwrite-strings"
- char* cfgfile = CFGFILE;
- char* pidfile = PIDFILE;
- #pragma GCC diagnostic warning "-Wwrite-strings"
- int opt;
- char optstring[16] = "efFhvc:";
- #ifdef NFM
- strcat(optstring, "Q");
- #endif /* NFM */
- #ifdef DEBUG
- strcat(optstring, "d:");
- #endif /* DEBUG */
- int foreground = 0; // daemonize
- int do_syslog = 1;
- while ((opt = getopt(argc, argv, optstring)) != -1) {
- switch (opt) {
- #ifdef NFM
- case 'Q':
- fm_demod = FM_QUADRI_DEMOD;
- break;
- #endif /* NFM */
- #ifdef DEBUG
- case 'd':
- debug_path = strdup(optarg);
- break;
- #endif /* DEBUG */
- case 'e':
- do_syslog = 0;
- break;
- case 'f':
- foreground = 1;
- tui = 1;
- break;
- case 'F':
- foreground = 1;
- tui = 0;
- break;
- case 'c':
- cfgfile = optarg;
- break;
- case 'v':
- cout << "RTLSDR-Airband version " << RTL_AIRBAND_VERSION << "\n";
- exit(EXIT_SUCCESS);
- case 'h':
- default:
- usage();
- break;
- }
- }
- #ifdef DEBUG
- if (!debug_path)
- debug_path = strdup(DEBUG_PATH);
- init_debug(debug_path);
- #endif /* DEBUG */
- // If executing other than as root, GPU memory gets alloc'd and the
- // 'permission denied' message on /dev/mem kills rtl_airband without
- // releasing GPU memory.
- #ifdef WITH_BCM_VC
- // should probably do this check in other circumstances also.
- if (0 != getuid()) {
- cerr << "FFT library requires that rtl_airband be executed as root\n";
- exit(1);
- }
- #endif /* WITH_BCM_VC */
- // read config
- try {
- Config config;
- config.readFile(cfgfile);
- Setting& root = config.getRoot();
- if (root.exists("pidfile"))
- pidfile = strdup(root["pidfile"]);
- if (root.exists("fft_size")) {
- int fsize = (int)(root["fft_size"]);
- fft_size_log = 0;
- for (size_t i = MIN_FFT_SIZE_LOG; i <= MAX_FFT_SIZE_LOG; i++) {
- if (fsize == 1 << i) {
- fft_size = (size_t)fsize;
- fft_size_log = i;
- break;
- }
- }
- if (fft_size_log == 0) {
- cerr << "Configuration error: invalid fft_size value (must be a power of two in range " << (1 << MIN_FFT_SIZE_LOG) << "-" << (1 << MAX_FFT_SIZE_LOG) << ")\n";
- error();
- }
- }
- if (root.exists("shout_metadata_delay"))
- shout_metadata_delay = (int)(root["shout_metadata_delay"]);
- if (shout_metadata_delay < 0 || shout_metadata_delay > 2 * TAG_QUEUE_LEN) {
- cerr << "Configuration error: shout_metadata_delay is out of allowed range (0-" << 2 * TAG_QUEUE_LEN << ")\n";
- error();
- }
- if (root.exists("localtime") && (bool)root["localtime"] == true)
- use_localtime = true;
- if (root.exists("multiple_demod_threads") && (bool)root["multiple_demod_threads"] == true) {
- #ifdef WITH_BCM_VC
- cerr << "Using multiple_demod_threads not supported with BCM VideoCore for FFT\n";
- exit(1);
- #endif /* WITH_BCM_VC */
- multiple_demod_threads = true;
- }
- if (root.exists("multiple_output_threads") && (bool)root["multiple_output_threads"] == true) {
- multiple_output_threads = true;
- }
- if (root.exists("log_scan_activity") && (bool)root["log_scan_activity"] == true)
- log_scan_activity = true;
- if (root.exists("stats_filepath"))
- stats_filepath = strdup(root["stats_filepath"]);
- #ifdef NFM
- if (root.exists("tau"))
- alpha = ((int)root["tau"] == 0 ? 0.0f : exp(-1.0f / (WAVE_RATE * 1e-6 * (int)root["tau"])));
- #endif /* NFM */
- if (root.exists("bel_start_on_time"))
- bel_start_on_time=(int)(root["bel_start_on_time"]);
- if (root.exists("bel_open_time"))
- bel_open_time=(int)(root["bel_open_time"]);
- if (root.exists("bel_blink_time"))
- bel_blink_time=(int)(root["bel_blink_time"]);
- if (root.exists("bel_wave_out_level"))
- bel_wave_out_level=(float)(root["bel_wave_out_level"]);
- if (root.exists("bel_wave_in_level"))
- bel_wave_in_level=(float)(root["bel_wave_in_level"]);
- if (root.exists("bel_prolong_time"))
- bel_prolong_time=(int)(root["bel_prolong_time"]);
- //MQTT
- if (root.exists("bel_mqtt_server"))
- bel_mqtt_server = strdup(root["bel_mqtt_server"]);
- if (root.exists("bel_mqtt_client_id"))
- bel_mqtt_client_id = strdup(root["bel_mqtt_client_id"]);
- if (root.exists("bel_mqtt_user_id"))
- bel_mqtt_user_id= strdup(root["bel_mqtt_user_id"]);
- if (root.exists("bel_mqtt_server"))
- bel_mqtt_password= strdup(root["bel_mqtt_password"]);
- Setting& devs = config.lookup("devices");
- device_count = devs.getLength();
- if (device_count < 1) {
- cerr << "Configuration error: no devices defined\n";
- error();
- }
- struct sigaction sigact, pipeact;
- memset(&sigact, 0, sizeof(sigact));
- memset(&pipeact, 0, sizeof(pipeact));
- pipeact.sa_handler = SIG_IGN;
- sigact.sa_handler = &sighandler;
- sigaction(SIGPIPE, &pipeact, NULL);
- sigaction(SIGHUP, &sigact, NULL);
- sigaction(SIGINT, &sigact, NULL);
- sigaction(SIGQUIT, &sigact, NULL);
- sigaction(SIGTERM, &sigact, NULL);
- devices = (device_t*)XCALLOC(device_count, sizeof(device_t));
- shout_init();
- if (do_syslog) {
- openlog("rtl_airband", LOG_PID, LOG_DAEMON);
- log_destination = SYSLOG;
- } else if (foreground) {
- log_destination = STDERR;
- } else {
- log_destination = NONE;
- }
- if (root.exists("mixers")) {
- Setting& mx = config.lookup("mixers");
- mixers = (mixer_t*)XCALLOC(mx.getLength(), sizeof(struct mixer_t));
- if ((mixer_count = parse_mixers(mx)) > 0) {
- mixers = (mixer_t*)XREALLOC(mixers, mixer_count * sizeof(struct mixer_t));
- } else {
- free(mixers);
- }
- } else {
- mixer_count = 0;
- }
- uint32_t devs_enabled = parse_devices(devs);
- if (devs_enabled < 1) {
- cerr << "Configuration error: no devices defined\n";
- error();
- }
- device_count = devs_enabled;
- debug_print("mixer_count=%d\n", mixer_count);
- #ifdef DEBUG
- for (int z = 0; z < mixer_count; z++) {
- mixer_t* m = &mixers[z];
- debug_print("mixer[%d]: name=%s, input_count=%d, output_count=%d\n", z, m->name, m->input_count, m->channel.output_count);
- }
- #endif /* DEBUG */
- } catch (const FileIOException& e) {
- cerr << "Cannot read configuration file " << cfgfile << "\n";
- error();
- } catch (const ParseException& e) {
- cerr << "Error while parsing configuration file " << cfgfile << " line " << e.getLine() << ": " << e.getError() << "\n";
- error();
- } catch (const SettingNotFoundException& e) {
- cerr << "Configuration error: mandatory parameter missing: " << e.getPath() << "\n";
- error();
- } catch (const SettingTypeException& e) {
- cerr << "Configuration error: invalid parameter type: " << e.getPath() << "\n";
- error();
- } catch (const ConfigException& e) {
- cerr << "Unhandled config exception\n";
- error();
- }
- log(LOG_INFO, "RTLSDR-Airband version %s starting\n", RTL_AIRBAND_VERSION);
- if (!foreground) {
- int pid1, pid2;
- if ((pid1 = fork()) == -1) {
- cerr << "Cannot fork child process: " << strerror(errno) << "\n";
- error();
- }
- if (pid1) {
- waitpid(-1, NULL, 0);
- return (0);
- } else {
- if ((pid2 = fork()) == -1) {
- cerr << "Cannot fork child process: " << strerror(errno) << "\n";
- error();
- }
- if (pid2) {
- return (0);
- } else {
- int nullfd, dupfd;
- if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
- log(LOG_CRIT, "Cannot open /dev/null: %s\n", strerror(errno));
- error();
- }
- for (dupfd = 0; dupfd <= 2; dupfd++) {
- if (dup2(nullfd, dupfd) == -1) {
- log(LOG_CRIT, "dup2(): %s\n", strerror(errno));
- error();
- }
- }
- if (nullfd > 2)
- close(nullfd);
- FILE* f = fopen(pidfile, "w");
- if (f == NULL) {
- log(LOG_WARNING, "Cannot write pidfile: %s\n", strerror(errno));
- } else {
- fprintf(f, "%ld\n", (long)getpid());
- fclose(f);
- }
- }
- }
- }
- for (int i = 0; i < mixer_count; i++) {
- if (mixers[i].enabled == false) {
- continue; // no inputs connected = no need to initialize output
- }
- channel_t* channel = &mixers[i].channel;
- if (channel->need_mp3) {
- channel->lame = airlame_init(mixers[i].channel.mode, mixers[i].channel.highpass, mixers[i].channel.lowpass);
- channel->lamebuf = (unsigned char*)malloc(sizeof(unsigned char) * LAMEBUF_SIZE);
- }
- for (int k = 0; k < channel->output_count; k++) {
- output_t* output = channel->outputs + k;
- if (output->type == O_ICECAST) {
- shout_setup((icecast_data*)(output->data), channel->mode);
- } else if (output->type == O_UDP_STREAM) {
- udp_stream_data* sdata = (udp_stream_data*)(output->data);
- if (!udp_stream_init(sdata, channel->mode, (size_t)WAVE_BATCH * sizeof(float))) {
- cerr << "Failed to initialize mixer " << i << " output " << k << " - aborting\n";
- error();
- }
- #ifdef WITH_PULSEAUDIO
- } else if (output->type == O_PULSE) {
- pulse_init();
- pulse_setup((pulse_data*)(output->data), channel->mode);
- #endif /* WITH_PULSEAUDIO */
- }
- }
- }
- for (int i = 0; i < device_count; i++) {
- device_t* dev = devices + i;
- for (int j = 0; j < dev->channel_count; j++) {
- channel_t* channel = dev->channels + j;
- // If the channel has icecast or MP3 file output, we will attempt to
- // initialize a separate LAME context for MP3 encoding.
- if (channel->need_mp3) {
- channel->lame = airlame_init(channel->mode, channel->highpass, channel->lowpass);
- channel->lamebuf = (unsigned char*)malloc(sizeof(unsigned char) * LAMEBUF_SIZE);
- }
- for (int k = 0; k < channel->output_count; k++) {
- output_t* output = channel->outputs + k;
- if (output->type == O_ICECAST) {
- shout_setup((icecast_data*)(output->data), channel->mode);
- } else if (output->type == O_UDP_STREAM) {
- udp_stream_data* sdata = (udp_stream_data*)(output->data);
- if (!udp_stream_init(sdata, channel->mode, (size_t)WAVE_BATCH * sizeof(float))) {
- cerr << "Failed to initialize device " << i << " channel " << j << " output " << k << " - aborting\n";
- error();
- }
- #ifdef WITH_PULSEAUDIO
- } else if (output->type == O_PULSE) {
- pulse_init();
- pulse_setup((pulse_data*)(output->data), channel->mode);
- #endif /* WITH_PULSEAUDIO */
- }
- }
- }
- if (input_init(dev->input) != 0 || dev->input->state != INPUT_INITIALIZED) {
- if (errno != 0) {
- cerr << "Failed to initialize input device " << i << ": " << strerror(errno) << " - aborting\n";
- } else {
- cerr << "Failed to initialize input device " << i << " - aborting\n";
- }
- error();
- }
- if (input_start(dev->input) != 0) {
- cerr << "Failed to start input on device " << i << ": " << strerror(errno) << " - aborting\n";
- error();
- }
- if (dev->mode == R_SCAN) {
- // FIXME: set errno
- if (pthread_mutex_init(&dev->tag_queue_lock, NULL) != 0) {
- cerr << "Failed to initialize mutex - aborting\n";
- error();
- }
- // FIXME: not needed when freq_count == 1?
- pthread_create(&dev->controller_thread, NULL, &controller_thread, dev);
- }
- }
- int timeout_device = 50; // 5 seconds
- while ((devices_running = count_devices_running()) != device_count && timeout_device > 0) {
- SLEEP(100);
- timeout_device--;
- }
- if ((devices_running = count_devices_running()) != device_count) {
- log(LOG_ERR, "%d device(s) failed to initialize - aborting\n", device_count - devices_running);
- error();
- }
- if (tui && 1==0) { //PMPMPM
- printf("\e[1;1H\e[2J");
- GOTOXY(0, 0);
- printf(" ");
- for (int i = 0; i < device_count; i++) {
- GOTOXY(0, i * 17 + 1);
- for (int j = 0; j < devices[i].channel_count; j++) {
- printf(" %7.3f ", devices[i].channels[j].freqlist[devices[i].channels[j].freq_idx].frequency / 1000000.0);
- }
- if (i != device_count - 1) {
- GOTOXY(0, i * 17 + 16);
- printf("-------------------------------------------------------------------------------");
- }
- }
- }
- THREAD belysning_control_check;
- THREAD mqtt_control_check;
- pthread_create(&belysning_control_check, NULL, &belysning_control_thread, NULL);
- pthread_create(&mqtt_control_check, NULL, &mqtt_control_thread, NULL);
- mqtt_msg_t mqtt_msg;
- mqtt_msg.type=3;
- mqtt_queue.push(mqtt_msg);
- THREAD output_check;
- pthread_create(&output_check, NULL, &output_check_thread, NULL);
- int demod_thread_count = multiple_demod_threads ? device_count : 1;
- demod_params_t* demod_params = (demod_params_t*)XCALLOC(demod_thread_count, sizeof(demod_params_t));
- THREAD* demod_threads = (THREAD*)XCALLOC(demod_thread_count, sizeof(THREAD));
- int output_thread_count = 1;
- if (multiple_output_threads) {
- output_thread_count = demod_thread_count;
- if (mixer_count > 0) {
- output_thread_count++;
- }
- }
- output_params_t* output_params = (output_params_t*)XCALLOC(output_thread_count, sizeof(output_params_t));
- THREAD* output_threads = (THREAD*)XCALLOC(output_thread_count, sizeof(THREAD));
- // Setup the output and demod threads
- if (multiple_output_threads == false) {
- init_output(&output_params[0], 0, device_count, 0, mixer_count);
- if (multiple_demod_threads == false) {
- init_demod(&demod_params[0], output_params[0].mp3_signal, 0, device_count);
- } else {
- for (int i = 0; i < demod_thread_count; i++) {
- init_demod(&demod_params[i], output_params[0].mp3_signal, i, i + 1);
- }
- }
- } else {
- if (multiple_demod_threads == false) {
- init_output(&output_params[0], 0, device_count, 0, 0);
- init_demod(&demod_params[0], output_params[0].mp3_signal, 0, device_count);
- } else {
- for (int i = 0; i < device_count; i++) {
- init_output(&output_params[i], i, i + 1, 0, 0);
- init_demod(&demod_params[i], output_params[i].mp3_signal, i, i + 1);
- }
- }
- if (mixer_count > 0) {
- init_output(&output_params[output_thread_count - 1], 0, 0, 0, mixer_count);
- }
- }
- // Startup the output threads
- for (int i = 0; i < output_thread_count; i++) {
- pthread_create(&output_threads[i], NULL, &output_thread, &output_params[i]);
- }
- // Startup the mixer thread (if there is one) using the signal for the last output thread
- THREAD mixer;
- if (mixer_count > 0) {
- pthread_create(&mixer, NULL, &mixer_thread, output_params[output_thread_count - 1].mp3_signal);
- }
- #ifdef WITH_PULSEAUDIO
- pulse_start();
- #endif /* WITH_PULSEAUDIO */
- sincosf_lut_init();
- // Startup the demod threads
- for (int i = 0; i < demod_thread_count; i++) {
- pthread_create(&demod_threads[i], NULL, &demodulate, &demod_params[i]);
- }
- // Wait for demod threads to exit
- for (int i = 0; i < demod_thread_count; i++) {
- pthread_join(demod_threads[i], NULL);
- }
- log(LOG_INFO, "Cleaning up\n");
- for (int i = 0; i < device_count; i++) {
- if (devices[i].mode == R_SCAN)
- pthread_join(devices[i].controller_thread, NULL);
- if (input_stop(devices[i].input) != 0 || devices[i].input->state != INPUT_STOPPED) {
- if (errno != 0) {
- log(LOG_ERR, "Failed do stop device #%d: %s\n", i, strerror(errno));
- } else {
- log(LOG_ERR, "Failed do stop device #%d\n", i);
- }
- }
- }
- log(LOG_INFO, "Input threads closed\n");
- for (int i = 0; i < device_count; i++) {
- device_t* dev = devices + i;
- disable_device_outputs(dev);
- }
- if (mixer_count > 0) {
- log(LOG_INFO, "Closing mixer thread\n");
- pthread_join(mixer, NULL);
- }
- log(LOG_INFO, "Closing output thread(s)\n");
- for (int i = 0; i < output_thread_count; i++) {
- output_params[i].mp3_signal->send();
- pthread_join(output_threads[i], NULL);
- }
- for (int i = 0; i < device_count; i++) {
- device_t* dev = devices + i;
- for (int j = 0; j < dev->channel_count; j++) {
- channel_t* channel = dev->channels + j;
- if (channel->need_mp3 && channel->lame) {
- lame_close(channel->lame);
- }
- }
- }
- log(LOG_INFO, "Closing bel thread\n");
- pthread_join(belysning_control_check, NULL);
- log(LOG_INFO, "Closing mqtt thread\n");
- pthread_kill(mqtt_control_check,SIGALRM);
- // pthread_join(mqtt_control_check, NULL);
- log(LOG_INFO, "Closing xxx thread\n");
- close_debug();
- #ifdef WITH_PROFILING
- ProfilerStop();
- #endif /* WITH_PROFILING */
- return 0;
- }
|