From 8e2342018ad2215ebd273fa4ad69d04fed514d2c Mon Sep 17 00:00:00 2001
From: charlie-foxtrot <13514783+charlie-foxtrot@users.noreply.github.com>
Date: Tue, 6 Feb 2024 19:23:06 -0800
Subject: [PATCH 1/9] add clang formatting workflow
---
.clang-format | 5 +
.devcontainer/Dockerfile.ubuntu-20.04 | 3 +-
.devcontainer/Dockerfile.ubuntu-latest | 3 +-
.devcontainer/devcontainer.json | 3 +-
.github/workflows/code_formatting.yml | 16 ++
src/ctcss.cpp | 212 ++++++++++++-------------
6 files changed, 127 insertions(+), 115 deletions(-)
create mode 100644 .clang-format
create mode 100644 .github/workflows/code_formatting.yml
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..90fb419
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,5 @@
+---
+BasedOnStyle: Chromium
+IndentWidth: 4
+ObjCBlockIndentWidth: 4
+ColumnLimit: 200
\ No newline at end of file
diff --git a/.devcontainer/Dockerfile.ubuntu-20.04 b/.devcontainer/Dockerfile.ubuntu-20.04
index eecf022..59d94a9 100644
--- a/.devcontainer/Dockerfile.ubuntu-20.04
+++ b/.devcontainer/Dockerfile.ubuntu-20.04
@@ -10,7 +10,8 @@ RUN DEBIAN_FRONTEND=noninteractive \
tzdata\
git \
sudo \
- gdb
+ gdb \
+ clang-format
WORKDIR /app
COPY .github/install_dependencies /app/
diff --git a/.devcontainer/Dockerfile.ubuntu-latest b/.devcontainer/Dockerfile.ubuntu-latest
index 29b1524..3a60736 100644
--- a/.devcontainer/Dockerfile.ubuntu-latest
+++ b/.devcontainer/Dockerfile.ubuntu-latest
@@ -10,7 +10,8 @@ RUN DEBIAN_FRONTEND=noninteractive \
tzdata\
git \
sudo \
- gdb
+ gdb \
+ clang-format
WORKDIR /app
COPY .github/install_dependencies /app/
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 580cc00..5faa346 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -22,7 +22,8 @@
"twxs.cmake",
"streetsidesoftware.code-spell-checker",
"ms-azuretools.vscode-docker",
- "GitHub.vscode-github-actions"
+ "GitHub.vscode-github-actions",
+ "xaver.clang-format"
]
}
},
diff --git a/.github/workflows/code_formatting.yml b/.github/workflows/code_formatting.yml
new file mode 100644
index 0000000..328c883
--- /dev/null
+++ b/.github/workflows/code_formatting.yml
@@ -0,0 +1,16 @@
+name: Code Formatting
+
+on:
+ pull_request:
+ schedule:
+ - cron: '39 13 * * *' # run daily
+
+jobs:
+ code_formatting:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: RafikFarhad/clang-format-github-action@v3
+ with:
+ sources: "src/**/*.h,src/**/*.cpp"
+ style: "file"
diff --git a/src/ctcss.cpp b/src/ctcss.cpp
index dee5e57..7a3cdbd 100644
--- a/src/ctcss.cpp
+++ b/src/ctcss.cpp
@@ -17,10 +17,10 @@
* along with this program. If not, see .
*/
-#include // M_PI
-#include // sort
+#include // M_PI
+#include // sort
-#include "logging.h" // debug_print()
+#include "logging.h" // debug_print()
#include "ctcss.h"
@@ -28,157 +28,145 @@ using namespace std;
// Implementation of https://www.embedded.com/detecting-ctcss-tones-with-goertzels-algorithm/
// also https://www.embedded.com/the-goertzel-algorithm/
-ToneDetector::ToneDetector(float tone_freq, float sample_rate, int window_size)
-{
- tone_freq_ = tone_freq;
- magnitude_ = 0.0;
-
- window_size_ = window_size;
-
- int k = (0.5 + window_size * tone_freq / sample_rate);
- float omega = (2.0 * M_PI * k) / window_size;
- coeff_ = 2.0 * cos(omega);
-
- reset();
+ToneDetector::ToneDetector(float tone_freq, float sample_rate, int window_size) {
+ tone_freq_ = tone_freq;
+ magnitude_ = 0.0;
+
+ window_size_ = window_size;
+
+ int k = (0.5 + window_size * tone_freq / sample_rate);
+ float omega = (2.0 * M_PI * k) / window_size;
+ coeff_ = 2.0 * cos(omega);
+
+ reset();
}
-void ToneDetector::process_sample(const float &sample) {
- q0_ = coeff_ * q1_ - q2_ + sample;
- q2_ = q1_;
- q1_ = q0_;
+void ToneDetector::process_sample(const float& sample) {
+ q0_ = coeff_ * q1_ - q2_ + sample;
+ q2_ = q1_;
+ q1_ = q0_;
- count_++;
- if (count_ == window_size_) {
- magnitude_ = q1_*q1_ + q2_*q2_ - q1_*q2_*coeff_;
- count_ = 0;
- }
+ count_++;
+ if (count_ == window_size_) {
+ magnitude_ = q1_ * q1_ + q2_ * q2_ - q1_ * q2_ * coeff_;
+ count_ = 0;
+ }
}
void ToneDetector::reset(void) {
- count_ = 0;
- q0_ = q1_ = q2_ = 0.0;
+ count_ = 0;
+ q0_ = q1_ = q2_ = 0.0;
}
+bool ToneDetectorSet::add(const float& tone_freq, const float& sample_rate, int window_size) {
+ ToneDetector new_tone = ToneDetector(tone_freq, sample_rate, window_size);
+ for (const auto tone : tones_) {
+ if (new_tone.coefficient() == tone.coefficient()) {
+ debug_print("Skipping tone %f, too close to other tones\n", tone_freq);
+ return false;
+ }
+ }
-bool ToneDetectorSet::add(const float & tone_freq, const float & sample_rate, int window_size) {
- ToneDetector new_tone = ToneDetector(tone_freq, sample_rate, window_size);
-
- for (const auto tone : tones_) {
- if (new_tone.coefficient() == tone.coefficient()) {
- debug_print("Skipping tone %f, too close to other tones\n", tone_freq);
- return false;
- }
- }
-
- tones_.push_back(new_tone);
- return true;
+ tones_.push_back(new_tone);
+ return true;
}
-void ToneDetectorSet::process_sample(const float &sample) {
- for (vector::iterator it = tones_.begin(); it != tones_.end(); ++it) {
- it->process_sample(sample);
- }
+void ToneDetectorSet::process_sample(const float& sample) {
+ for (vector::iterator it = tones_.begin(); it != tones_.end(); ++it) {
+ it->process_sample(sample);
+ }
}
void ToneDetectorSet::reset(void) {
- for (vector::iterator it = tones_.begin(); it != tones_.end(); ++it) {
- it->reset();
- }
+ for (vector::iterator it = tones_.begin(); it != tones_.end(); ++it) {
+ it->reset();
+ }
}
-float ToneDetectorSet::sorted_powers(vector &powers) {
- powers.clear();
+float ToneDetectorSet::sorted_powers(vector& powers) {
+ powers.clear();
- float total_power = 0.0;
- for (size_t i = 0; i < tones_.size(); ++i) {
- powers.push_back({tones_[i].relative_power(), tones_[i].freq()});
- total_power += tones_[i].relative_power();
- }
+ float total_power = 0.0;
+ for (size_t i = 0; i < tones_.size(); ++i) {
+ powers.push_back({tones_[i].relative_power(), tones_[i].freq()});
+ total_power += tones_[i].relative_power();
+ }
- sort(powers.begin(), powers.end(), [](PowerIndex a, PowerIndex b) {
- return a.power > b.power;
- });
-
- return total_power / tones_.size();
-}
+ sort(powers.begin(), powers.end(), [](PowerIndex a, PowerIndex b) { return a.power > b.power; });
-vector CTCSS::standard_tones = {
- 67.0, 69.3, 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5, 94.8, 97.4, 100.0, 103.5, 107.2,
- 110.9, 114.8, 118.8, 123.0, 127.3, 131.8, 136.5, 141.3, 146.2, 150.0, 151.4, 156.7, 159.8,
- 162.2, 165.5, 167.9, 171.3, 173.8, 177.3, 179.9, 183.5, 186.2, 189.9, 192.8, 196.6, 199.5,
- 203.5, 206.5, 210.7, 218.1, 225.7, 229.1, 233.6, 241.8, 250.3, 254.1
-};
+ return total_power / tones_.size();
+}
-CTCSS::CTCSS(const float & ctcss_freq, const float & sample_rate, int window_size)
- : enabled_(true), ctcss_freq_(ctcss_freq), window_size_(window_size), found_count_(0), not_found_count_(0) {
+vector CTCSS::standard_tones = {67.0, 69.3, 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5, 94.8, 97.4, 100.0, 103.5, 107.2, 110.9, 114.8,
+ 118.8, 123.0, 127.3, 131.8, 136.5, 141.3, 146.2, 150.0, 151.4, 156.7, 159.8, 162.2, 165.5, 167.9, 171.3, 173.8, 177.3,
+ 179.9, 183.5, 186.2, 189.9, 192.8, 196.6, 199.5, 203.5, 206.5, 210.7, 218.1, 225.7, 229.1, 233.6, 241.8, 250.3, 254.1};
+CTCSS::CTCSS(const float& ctcss_freq, const float& sample_rate, int window_size) : enabled_(true), ctcss_freq_(ctcss_freq), window_size_(window_size), found_count_(0), not_found_count_(0) {
debug_print("Adding CTCSS detector for %f Hz with a sample rate of %f and window %d\n", ctcss_freq, sample_rate, window_size_);
- // Add the target CTCSS frequency first followed by the other "standard tones", except those
- // within +/- 5 Hz
- powers_.add(ctcss_freq, sample_rate, window_size_);
-
- for (const auto tone : standard_tones) {
- if (abs(ctcss_freq - tone) < 5) {
- debug_print("Skipping tone %f, too close to other tones\n", tone);
- continue;
- }
- powers_.add(tone, sample_rate, window_size_);
- }
-
+ // Add the target CTCSS frequency first followed by the other "standard tones", except those
+ // within +/- 5 Hz
+ powers_.add(ctcss_freq, sample_rate, window_size_);
+
+ for (const auto tone : standard_tones) {
+ if (abs(ctcss_freq - tone) < 5) {
+ debug_print("Skipping tone %f, too close to other tones\n", tone);
+ continue;
+ }
+ powers_.add(tone, sample_rate, window_size_);
+ }
+
// clear all values to start NOTE: has_tone_ will be true until the first window count of samples are processed
reset();
}
+void CTCSS::process_audio_sample(const float& sample) {
+ if (!enabled_) {
+ return;
+ }
+
+ powers_.process_sample(sample);
+
+ sample_count_++;
+ if (sample_count_ < window_size_) {
+ return;
+ }
-void CTCSS::process_audio_sample(const float &sample) {
- if (!enabled_) {
- return;
- }
-
- powers_.process_sample(sample);
-
- sample_count_++;
- if (sample_count_ < window_size_) {
- return;
- }
-
enough_samples_ = true;
- // if this is sample fills out the window then check if one of the "strongest"
- // tones is the CTCSS tone we are looking for. NOTE: there can be multiple "strongest"
- // tones based on floating point math
- vector tone_powers;
- float avg_power = powers_.sorted_powers(tone_powers);
- float ctcss_tone_power = 0.0;
- for( const auto i:tone_powers) {
- if (i.freq == ctcss_freq_) {
- ctcss_tone_power = i.power;
- break;
- }
- }
- if (ctcss_tone_power == tone_powers[0].power && ctcss_tone_power > avg_power) {
+ // if this is sample fills out the window then check if one of the "strongest"
+ // tones is the CTCSS tone we are looking for. NOTE: there can be multiple "strongest"
+ // tones based on floating point math
+ vector tone_powers;
+ float avg_power = powers_.sorted_powers(tone_powers);
+ float ctcss_tone_power = 0.0;
+ for (const auto i : tone_powers) {
+ if (i.freq == ctcss_freq_) {
+ ctcss_tone_power = i.power;
+ break;
+ }
+ }
+ if (ctcss_tone_power == tone_powers[0].power && ctcss_tone_power > avg_power) {
debug_print("CTCSS tone of %f Hz detected\n", ctcss_freq_);
has_tone_ = true;
found_count_++;
} else {
- debug_print("CTCSS tone of %f Hz not detected - highest power was %f Hz at %f vs %f\n",
- ctcss_freq_, tone_powers[0].freq, tone_powers[0].power, ctcss_tone_power);
+ debug_print("CTCSS tone of %f Hz not detected - highest power was %f Hz at %f vs %f\n", ctcss_freq_, tone_powers[0].freq, tone_powers[0].power, ctcss_tone_power);
has_tone_ = false;
not_found_count_++;
}
// reset everything for the next window's worth of samples
- powers_.reset();
- sample_count_ = 0;
+ powers_.reset();
+ sample_count_ = 0;
}
void CTCSS::reset(void) {
- if (enabled_) {
- powers_.reset();
+ if (enabled_) {
+ powers_.reset();
enough_samples_ = false;
- sample_count_ = 0;
- has_tone_ = false;
- }
+ sample_count_ = 0;
+ has_tone_ = false;
+ }
}
From 365c0e0758b65cf2874e0c8736844e4124562f7b Mon Sep 17 00:00:00 2001
From: charlie-foxtrot <13514783+charlie-foxtrot@users.noreply.github.com>
Date: Tue, 6 Feb 2024 19:39:49 -0800
Subject: [PATCH 2/9] fix sources
---
.github/workflows/code_formatting.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/code_formatting.yml b/.github/workflows/code_formatting.yml
index 328c883..e5a447e 100644
--- a/.github/workflows/code_formatting.yml
+++ b/.github/workflows/code_formatting.yml
@@ -9,8 +9,8 @@ jobs:
code_formatting:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- uses: RafikFarhad/clang-format-github-action@v3
with:
- sources: "src/**/*.h,src/**/*.cpp"
+ sources: "src/*.h,src/*.cpp"
style: "file"
From e89dc4367d60230f7fdfef0519c775303c4e65d5 Mon Sep 17 00:00:00 2001
From: charlie-foxtrot <13514783+charlie-foxtrot@users.noreply.github.com>
Date: Tue, 6 Feb 2024 21:27:56 -0800
Subject: [PATCH 3/9] get files
---
.github/workflows/code_formatting.yml | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/code_formatting.yml b/.github/workflows/code_formatting.yml
index e5a447e..bcd8d68 100644
--- a/.github/workflows/code_formatting.yml
+++ b/.github/workflows/code_formatting.yml
@@ -9,8 +9,18 @@ jobs:
code_formatting:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - uses: RafikFarhad/clang-format-github-action@v3
- with:
- sources: "src/*.h,src/*.cpp"
- style: "file"
+
+ - name: Runner Info
+ run: printenv | sort
+
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: working dir
+ run: find .
+
+ - name: clang-format
+ uses: RafikFarhad/clang-format-github-action@v3
+ with:
+ sources: "src/*.h,src/*.cpp"
+ style: "file"
From 21091473b0aac3ea0ef8f8517eb77dcdd95585b8 Mon Sep 17 00:00:00 2001
From: charlie-foxtrot <13514783+charlie-foxtrot@users.noreply.github.com>
Date: Tue, 6 Feb 2024 22:00:47 -0800
Subject: [PATCH 4/9] add script to reformat, change workflow to run script
---
.devcontainer/Dockerfile.ubuntu-20.04 | 2 +-
.devcontainer/Dockerfile.ubuntu-latest | 2 +-
.github/workflows/code_formatting.yml | 13 ++++++-------
scripts/reformat_code | 3 +++
4 files changed, 11 insertions(+), 9 deletions(-)
create mode 100755 scripts/reformat_code
diff --git a/.devcontainer/Dockerfile.ubuntu-20.04 b/.devcontainer/Dockerfile.ubuntu-20.04
index 59d94a9..a0bcff7 100644
--- a/.devcontainer/Dockerfile.ubuntu-20.04
+++ b/.devcontainer/Dockerfile.ubuntu-20.04
@@ -11,7 +11,7 @@ RUN DEBIAN_FRONTEND=noninteractive \
git \
sudo \
gdb \
- clang-format
+ clang-format-14
WORKDIR /app
COPY .github/install_dependencies /app/
diff --git a/.devcontainer/Dockerfile.ubuntu-latest b/.devcontainer/Dockerfile.ubuntu-latest
index 3a60736..5515542 100644
--- a/.devcontainer/Dockerfile.ubuntu-latest
+++ b/.devcontainer/Dockerfile.ubuntu-latest
@@ -11,7 +11,7 @@ RUN DEBIAN_FRONTEND=noninteractive \
git \
sudo \
gdb \
- clang-format
+ clang-format-14
WORKDIR /app
COPY .github/install_dependencies /app/
diff --git a/.github/workflows/code_formatting.yml b/.github/workflows/code_formatting.yml
index bcd8d68..861e6e1 100644
--- a/.github/workflows/code_formatting.yml
+++ b/.github/workflows/code_formatting.yml
@@ -16,11 +16,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - name: working dir
- run: find .
+ - name: Install Clang Format
+ run: sudo apt-get install clang-format-14
- - name: clang-format
- uses: RafikFarhad/clang-format-github-action@v3
- with:
- sources: "src/*.h,src/*.cpp"
- style: "file"
+ - name: Run Clang Format
+ run: |
+ ./scripts/reformat_code
+ git diff --exit-code
diff --git a/scripts/reformat_code b/scripts/reformat_code
new file mode 100755
index 0000000..3687fd3
--- /dev/null
+++ b/scripts/reformat_code
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+find src/*.h src/*.cpp | xargs clang-format-14 -i
From 0da955f5a997bfe623730b28051a527fa5df8d33 Mon Sep 17 00:00:00 2001
From: charlie-foxtrot <13514783+charlie-foxtrot@users.noreply.github.com>
Date: Tue, 6 Feb 2024 22:02:39 -0800
Subject: [PATCH 5/9] apply formatting
---
src/config.cpp | 1651 ++++++++++++++--------------
src/ctcss.h | 129 ++-
src/filters.cpp | 216 ++--
src/filters.h | 71 +-
src/generate_signal.cpp | 70 +-
src/generate_signal.h | 26 +-
src/helper_functions.cpp | 30 +-
src/helper_functions.h | 12 +-
src/input-common.cpp | 186 ++--
src/input-common.h | 63 +-
src/input-file.cpp | 287 +++--
src/input-file.h | 10 +-
src/input-helpers.cpp | 63 +-
src/input-helpers.h | 4 +-
src/input-mirisdr.cpp | 388 ++++---
src/input-mirisdr.h | 14 +-
src/input-rtlsdr.cpp | 417 ++++---
src/input-rtlsdr.h | 15 +-
src/input-soapysdr.cpp | 639 ++++++-----
src/input-soapysdr.h | 22 +-
src/logging.cpp | 65 +-
src/logging.h | 30 +-
src/mixer.cpp | 362 +++----
src/output.cpp | 1687 ++++++++++++++---------------
src/pulse.cpp | 385 ++++---
src/rtl_airband.cpp | 1912 ++++++++++++++++-----------------
src/rtl_airband.h | 428 ++++----
src/squelch.cpp | 945 ++++++++--------
src/squelch.h | 196 ++--
src/test_base_class.cpp | 185 ++--
src/test_base_class.h | 11 +-
src/test_ctcss.cpp | 243 ++---
src/test_filters.cpp | 29 +-
src/test_generate_signal.cpp | 444 ++++----
src/test_helper_functions.cpp | 204 ++--
src/test_squelch.cpp | 468 ++++----
src/udp_stream.cpp | 144 +--
src/util.cpp | 230 ++--
38 files changed, 6037 insertions(+), 6244 deletions(-)
diff --git a/src/config.cpp b/src/config.cpp
index 7e451c1..d4f042a 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -18,894 +18,869 @@
* along with this program. If not, see .
*/
-#include
-#include
-#include
-#include
#include
-#include // uint32_t
+#include // uint32_t
#include
+#include
+#include
+#include
+#include
#include
-#include "input-common.h" // input_t
+#include "input-common.h" // input_t
#include "rtl_airband.h"
using namespace std;
-static int parse_outputs(libconfig::Setting &outs, channel_t *channel, int i, int j, bool parsing_mixers) {
- int oo = 0;
- for(int o = 0; o < channel->output_count; o++) {
- if(outs[o].exists("disable") && (bool)outs[o]["disable"] == true) {
- continue;
- }
- if(!strncmp(outs[o]["type"], "icecast", 7)) {
- channel->outputs[oo].data = XCALLOC(1, sizeof(struct icecast_data));
- channel->outputs[oo].type = O_ICECAST;
- icecast_data *idata = (icecast_data *)(channel->outputs[oo].data);
- idata->hostname = strdup(outs[o]["server"]);
- idata->port = outs[o]["port"];
- idata->mountpoint = strdup(outs[o]["mountpoint"]);
- idata->username = strdup(outs[o]["username"]);
- idata->password = strdup(outs[o]["password"]);
- if(outs[o].exists("name"))
- idata->name = strdup(outs[o]["name"]);
- if(outs[o].exists("genre"))
- idata->genre = strdup(outs[o]["genre"]);
- if(outs[o].exists("description"))
- idata->description = strdup(outs[o]["description"]);
- if(outs[o].exists("send_scan_freq_tags"))
- idata->send_scan_freq_tags = (bool)outs[o]["send_scan_freq_tags"];
- else
- idata->send_scan_freq_tags = 0;
+static int parse_outputs(libconfig::Setting& outs, channel_t* channel, int i, int j, bool parsing_mixers) {
+ int oo = 0;
+ for (int o = 0; o < channel->output_count; o++) {
+ if (outs[o].exists("disable") && (bool)outs[o]["disable"] == true) {
+ continue;
+ }
+ if (!strncmp(outs[o]["type"], "icecast", 7)) {
+ channel->outputs[oo].data = XCALLOC(1, sizeof(struct icecast_data));
+ channel->outputs[oo].type = O_ICECAST;
+ icecast_data* idata = (icecast_data*)(channel->outputs[oo].data);
+ idata->hostname = strdup(outs[o]["server"]);
+ idata->port = outs[o]["port"];
+ idata->mountpoint = strdup(outs[o]["mountpoint"]);
+ idata->username = strdup(outs[o]["username"]);
+ idata->password = strdup(outs[o]["password"]);
+ if (outs[o].exists("name"))
+ idata->name = strdup(outs[o]["name"]);
+ if (outs[o].exists("genre"))
+ idata->genre = strdup(outs[o]["genre"]);
+ if (outs[o].exists("description"))
+ idata->description = strdup(outs[o]["description"]);
+ if (outs[o].exists("send_scan_freq_tags"))
+ idata->send_scan_freq_tags = (bool)outs[o]["send_scan_freq_tags"];
+ else
+ idata->send_scan_freq_tags = 0;
#ifdef LIBSHOUT_HAS_TLS
- if(outs[o].exists("tls")) {
- if(outs[o]["tls"].getType() == libconfig::Setting::TypeString) {
- if(!strcmp(outs[o]["tls"], "auto")) {
- idata->tls_mode = SHOUT_TLS_AUTO;
- } else if(!strcmp(outs[o]["tls"], "auto_no_plain")) {
- idata->tls_mode = SHOUT_TLS_AUTO_NO_PLAIN;
- } else if(!strcmp(outs[o]["tls"], "transport")) {
- idata->tls_mode = SHOUT_TLS_RFC2818;
- } else if(!strcmp(outs[o]["tls"], "upgrade")) {
- idata->tls_mode = SHOUT_TLS_RFC2817;
- } else if(!strcmp(outs[o]["tls"], "disabled")) {
- idata->tls_mode = SHOUT_TLS_DISABLED;
- } else {
- if (parsing_mixers) {
- cerr<<"Configuration error: mixers.["<tls_mode = SHOUT_TLS_DISABLED;
- }
+ if (outs[o].exists("tls")) {
+ if (outs[o]["tls"].getType() == libconfig::Setting::TypeString) {
+ if (!strcmp(outs[o]["tls"], "auto")) {
+ idata->tls_mode = SHOUT_TLS_AUTO;
+ } else if (!strcmp(outs[o]["tls"], "auto_no_plain")) {
+ idata->tls_mode = SHOUT_TLS_AUTO_NO_PLAIN;
+ } else if (!strcmp(outs[o]["tls"], "transport")) {
+ idata->tls_mode = SHOUT_TLS_RFC2818;
+ } else if (!strcmp(outs[o]["tls"], "upgrade")) {
+ idata->tls_mode = SHOUT_TLS_RFC2817;
+ } else if (!strcmp(outs[o]["tls"], "disabled")) {
+ idata->tls_mode = SHOUT_TLS_DISABLED;
+ } else {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: ";
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: ";
+ }
+ cerr << "invalid value for tls; must be one of: auto, auto_no_plain, transport, upgrade, disabled\n";
+ error();
+ }
+ } else {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: ";
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: ";
+ }
+ cerr << "tls value must be a string\n";
+ error();
+ }
+ } else {
+ idata->tls_mode = SHOUT_TLS_DISABLED;
+ }
#endif /* LIBSHOUT_HAS_TLS */
- channel->need_mp3 = 1;
- } else if(!strncmp(outs[o]["type"], "file", 4)) {
- channel->outputs[oo].data = XCALLOC(1, sizeof(struct file_data));
- channel->outputs[oo].type = O_FILE;
- file_data *fdata = (file_data *)(channel->outputs[oo].data);
-
- fdata->type = O_FILE;
- if (!outs[o].exists("directory") || !outs[o].exists("filename_template")) {
- if(parsing_mixers) {
- cerr << "Configuration error: mixers.["<basedir = outs[o]["directory"].c_str();
- fdata->basename = outs[o]["filename_template"].c_str();
- fdata->dated_subdirectories = outs[o].exists("dated_subdirectories") ?
- (bool)(outs[o]["dated_subdirectories"]) : false;
- fdata->suffix = ".mp3";
-
- fdata->continuous = outs[o].exists("continuous") ?
- (bool)(outs[o]["continuous"]) : false;
- fdata->append = (!outs[o].exists("append")) || (bool)(outs[o]["append"]);
- fdata->split_on_transmission = outs[o].exists("split_on_transmission") ?
- (bool)(outs[o]["split_on_transmission"]) : false;
- fdata->include_freq = outs[o].exists("include_freq") ?
- (bool)(outs[o]["include_freq"]) : false;
- channel->need_mp3 = 1;
-
- if(fdata->split_on_transmission) {
- if (parsing_mixers) {
- cerr<<"Configuration error: mixers.["<continuous) {
- cerr<<"Configuration error: devices.["<outputs[oo].data = XCALLOC(1, sizeof(struct file_data));
- channel->outputs[oo].type = O_RAWFILE;
- file_data *fdata = (file_data *)(channel->outputs[oo].data);
-
- fdata->type = O_RAWFILE;
- if (!outs[o].exists("directory") || !outs[o].exists("filename_template")) {
- cerr<<"Configuration error: devices.["<basedir = outs[o]["directory"].c_str();
- fdata->basename = outs[o]["filename_template"].c_str();
- fdata->dated_subdirectories = outs[o].exists("dated_subdirectories") ?
- (bool)(outs[o]["dated_subdirectories"]) : false;
- fdata->suffix = ".cf32";
-
- fdata->continuous = outs[o].exists("continuous") ?
- (bool)(outs[o]["continuous"]) : false;
- fdata->append = (!outs[o].exists("append")) || (bool)(outs[o]["append"]);
- fdata->split_on_transmission = outs[o].exists("split_on_transmission") ?
- (bool)(outs[o]["split_on_transmission"]) : false;
- fdata->include_freq = outs[o].exists("include_freq") ?
- (bool)(outs[o]["include_freq"]) : false;
- channel->needs_raw_iq = channel->has_iq_outputs = 1;
-
- if(fdata->continuous && fdata->split_on_transmission) {
- cerr<<"Configuration error: devices.["<outputs[oo].data = XCALLOC(1, sizeof(struct mixer_data));
- channel->outputs[oo].type = O_MIXER;
- mixer_data *mdata = (mixer_data *)(channel->outputs[oo].data);
- const char *name = (const char *)outs[o]["name"];
- if((mdata->mixer = getmixerbyname(name)) == NULL) {
- cerr<<"Configuration error: devices.["< 1.0f) {
- cerr<<"Configuration error: devices.["<\n";
- error();
- }
- if((mdata->input = mixer_connect_input(mdata->mixer, ampfactor, balance)) < 0) {
- cerr<<"Configuration error: devices.["<input, ampfactor, balance);
- } else if(!strncmp(outs[o]["type"], "udp_stream", 6)) {
- channel->outputs[oo].data = XCALLOC(1, sizeof(struct udp_stream_data));
- channel->outputs[oo].type = O_UDP_STREAM;
-
- udp_stream_data *sdata = (udp_stream_data *)channel->outputs[oo].data;
-
- sdata->continuous = outs[o].exists("continuous") ? (bool)(outs[o]["continuous"]) : false;
-
- if (outs[o].exists("dest_address")) {
- sdata->dest_address = strdup(outs[o]["dest_address"]);
- } else {
- if (parsing_mixers) {
- cerr << "Configuration error: mixers.["<dest_port = strdup(buffer);
- } else {
- sdata->dest_port = strdup(outs[o]["dest_port"]);
- }
- } else {
- if (parsing_mixers) {
- cerr << "Configuration error: mixers.["<need_mp3 = 1;
+ } else if (!strncmp(outs[o]["type"], "file", 4)) {
+ channel->outputs[oo].data = XCALLOC(1, sizeof(struct file_data));
+ channel->outputs[oo].type = O_FILE;
+ file_data* fdata = (file_data*)(channel->outputs[oo].data);
+
+ fdata->type = O_FILE;
+ if (!outs[o].exists("directory") || !outs[o].exists("filename_template")) {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: ";
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: ";
+ }
+ cerr << "both directory and filename_template required for file\n";
+ error();
+ }
+ fdata->basedir = outs[o]["directory"].c_str();
+ fdata->basename = outs[o]["filename_template"].c_str();
+ fdata->dated_subdirectories = outs[o].exists("dated_subdirectories") ? (bool)(outs[o]["dated_subdirectories"]) : false;
+ fdata->suffix = ".mp3";
+
+ fdata->continuous = outs[o].exists("continuous") ? (bool)(outs[o]["continuous"]) : false;
+ fdata->append = (!outs[o].exists("append")) || (bool)(outs[o]["append"]);
+ fdata->split_on_transmission = outs[o].exists("split_on_transmission") ? (bool)(outs[o]["split_on_transmission"]) : false;
+ fdata->include_freq = outs[o].exists("include_freq") ? (bool)(outs[o]["include_freq"]) : false;
+ channel->need_mp3 = 1;
+
+ if (fdata->split_on_transmission) {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: split_on_transmission is not allowed for mixers\n";
+ error();
+ }
+ if (fdata->continuous) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: can't have both continuous and split_on_transmission\n";
+ error();
+ }
+ }
+
+ } else if (!strncmp(outs[o]["type"], "rawfile", 7)) {
+ if (parsing_mixers) { // rawfile outputs not allowed for mixers
+ cerr << "Configuration error: mixers.[" << i << "] outputs[" << o << "]: rawfile output is not allowed for mixers\n";
+ error();
+ }
+ channel->outputs[oo].data = XCALLOC(1, sizeof(struct file_data));
+ channel->outputs[oo].type = O_RAWFILE;
+ file_data* fdata = (file_data*)(channel->outputs[oo].data);
+
+ fdata->type = O_RAWFILE;
+ if (!outs[o].exists("directory") || !outs[o].exists("filename_template")) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: both directory and filename_template required for file\n";
+ error();
+ }
+
+ fdata->basedir = outs[o]["directory"].c_str();
+ fdata->basename = outs[o]["filename_template"].c_str();
+ fdata->dated_subdirectories = outs[o].exists("dated_subdirectories") ? (bool)(outs[o]["dated_subdirectories"]) : false;
+ fdata->suffix = ".cf32";
+
+ fdata->continuous = outs[o].exists("continuous") ? (bool)(outs[o]["continuous"]) : false;
+ fdata->append = (!outs[o].exists("append")) || (bool)(outs[o]["append"]);
+ fdata->split_on_transmission = outs[o].exists("split_on_transmission") ? (bool)(outs[o]["split_on_transmission"]) : false;
+ fdata->include_freq = outs[o].exists("include_freq") ? (bool)(outs[o]["include_freq"]) : false;
+ channel->needs_raw_iq = channel->has_iq_outputs = 1;
+
+ if (fdata->continuous && fdata->split_on_transmission) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: can't have both continuous and split_on_transmission\n";
+ error();
+ }
+ } else if (!strncmp(outs[o]["type"], "mixer", 5)) {
+ if (parsing_mixers) { // mixer outputs not allowed for mixers
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: mixer output is not allowed for mixers\n";
+ error();
+ }
+ channel->outputs[oo].data = XCALLOC(1, sizeof(struct mixer_data));
+ channel->outputs[oo].type = O_MIXER;
+ mixer_data* mdata = (mixer_data*)(channel->outputs[oo].data);
+ const char* name = (const char*)outs[o]["name"];
+ if ((mdata->mixer = getmixerbyname(name)) == NULL) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: unknown mixer \"" << name << "\"\n";
+ error();
+ }
+ float ampfactor = outs[o].exists("ampfactor") ? (float)outs[o]["ampfactor"] : 1.0f;
+ float balance = outs[o].exists("balance") ? (float)outs[o]["balance"] : 0.0f;
+ if (balance < -1.0f || balance > 1.0f) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: balance out of allowed range <-1.0;1.0>\n";
+ error();
+ }
+ if ((mdata->input = mixer_connect_input(mdata->mixer, ampfactor, balance)) < 0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o
+ << "]: "
+ "could not connect to mixer "
+ << name << ": " << mixer_get_error() << "\n";
+ error();
+ }
+ debug_print("dev[%d].chan[%d].out[%d] connected to mixer %s as input %d (ampfactor=%.1f balance=%.1f)\n", i, j, o, name, mdata->input, ampfactor, balance);
+ } else if (!strncmp(outs[o]["type"], "udp_stream", 6)) {
+ channel->outputs[oo].data = XCALLOC(1, sizeof(struct udp_stream_data));
+ channel->outputs[oo].type = O_UDP_STREAM;
+
+ udp_stream_data* sdata = (udp_stream_data*)channel->outputs[oo].data;
+
+ sdata->continuous = outs[o].exists("continuous") ? (bool)(outs[o]["continuous"]) : false;
+
+ if (outs[o].exists("dest_address")) {
+ sdata->dest_address = strdup(outs[o]["dest_address"]);
+ } else {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: ";
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: ";
+ }
+ cerr << "missing dest_address\n";
+ error();
+ }
+
+ if (outs[o].exists("dest_port")) {
+ if (outs[o]["dest_port"].getType() == libconfig::Setting::TypeInt) {
+ char buffer[12];
+ sprintf(buffer, "%d", (int)outs[o]["dest_port"]);
+ sdata->dest_port = strdup(buffer);
+ } else {
+ sdata->dest_port = strdup(outs[o]["dest_port"]);
+ }
+ } else {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: ";
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: ";
+ }
+ cerr << "missing dest_port\n";
+ error();
+ }
#ifdef WITH_PULSEAUDIO
- } else if(!strncmp(outs[o]["type"], "pulse", 5)) {
- channel->outputs[oo].data = XCALLOC(1, sizeof(struct pulse_data));
- channel->outputs[oo].type = O_PULSE;
-
- pulse_data *pdata = (pulse_data *)(channel->outputs[oo].data);
- pdata->continuous = outs[o].exists("continuous") ?
- (bool)(outs[o]["continuous"]) : false;
- pdata->server = outs[o].exists("server") ? strdup(outs[o]["server"]) : NULL;
- pdata->name = outs[o].exists("name") ? strdup(outs[o]["name"]) : "rtl_airband";
- pdata->sink = outs[o].exists("sink") ? strdup(outs[o]["sink"]) : NULL;
-
- if (outs[o].exists("stream_name")) {
- pdata->stream_name = strdup(outs[o]["stream_name"]);
- } else {
- if(parsing_mixers) {
- cerr<<"Configuration error: mixers.["<freqlist[0].frequency / 1000000.0f);
- pdata->stream_name = strdup(buf);
- }
+ } else if (!strncmp(outs[o]["type"], "pulse", 5)) {
+ channel->outputs[oo].data = XCALLOC(1, sizeof(struct pulse_data));
+ channel->outputs[oo].type = O_PULSE;
+
+ pulse_data* pdata = (pulse_data*)(channel->outputs[oo].data);
+ pdata->continuous = outs[o].exists("continuous") ? (bool)(outs[o]["continuous"]) : false;
+ pdata->server = outs[o].exists("server") ? strdup(outs[o]["server"]) : NULL;
+ pdata->name = outs[o].exists("name") ? strdup(outs[o]["name"]) : "rtl_airband";
+ pdata->sink = outs[o].exists("sink") ? strdup(outs[o]["sink"]) : NULL;
+
+ if (outs[o].exists("stream_name")) {
+ pdata->stream_name = strdup(outs[o]["stream_name"]);
+ } else {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: PulseAudio outputs of mixers must have stream_name defined\n";
+ error();
+ }
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%.3f MHz", (float)channel->freqlist[0].frequency / 1000000.0f);
+ pdata->stream_name = strdup(buf);
+ }
#endif /* WITH_PULSEAUDIO */
- } else {
- if(parsing_mixers) {
- cerr << "Configuration error: mixers.["<outputs[oo].enabled = true;
- channel->outputs[oo].active = false;
- oo++;
- }
- return oo;
+ } else {
+ if (parsing_mixers) {
+ cerr << "Configuration error: mixers.[" << i << "] outputs.[" << o << "]: ";
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] outputs.[" << o << "]: ";
+ }
+ cerr << "unknown output type\n";
+ error();
+ }
+ channel->outputs[oo].enabled = true;
+ channel->outputs[oo].active = false;
+ oo++;
+ }
+ return oo;
}
-static struct freq_t *mk_freqlist( int n )
-{
- if(n < 1) {
- cerr<<"mk_freqlist: invalid list length " << n << "\n";
- error();
- }
- struct freq_t *fl = (struct freq_t *)XCALLOC(n, sizeof(struct freq_t));
- for(int i = 0; i < n; i++) {
- fl[i].frequency = 0;
- fl[i].label = NULL;
- fl[i].agcavgfast = 0.5f;
- fl[i].ampfactor = 1.0f;
- fl[i].squelch = Squelch();
- fl[i].active_counter = 0;
- fl[i].modulation = MOD_AM;
- }
- return fl;
+static struct freq_t* mk_freqlist(int n) {
+ if (n < 1) {
+ cerr << "mk_freqlist: invalid list length " << n << "\n";
+ error();
+ }
+ struct freq_t* fl = (struct freq_t*)XCALLOC(n, sizeof(struct freq_t));
+ for (int i = 0; i < n; i++) {
+ fl[i].frequency = 0;
+ fl[i].label = NULL;
+ fl[i].agcavgfast = 0.5f;
+ fl[i].ampfactor = 1.0f;
+ fl[i].squelch = Squelch();
+ fl[i].active_counter = 0;
+ fl[i].modulation = MOD_AM;
+ }
+ return fl;
}
static void warn_if_freq_not_in_range(int devidx, int chanidx, int freq, int centerfreq, int sample_rate) {
- static const float soft_bw_threshold = 0.9f;
- float bw_limit = (float)sample_rate / 2.f * soft_bw_threshold;
- if((float)abs(freq - centerfreq) >= bw_limit) {
- log(LOG_WARNING,
- "Warning: dev[%d].channel[%d]: frequency %.3f MHz is outside of SDR operating bandwidth (%.3f-%.3f MHz)\n",
- devidx, chanidx, (double)freq / 1e6,
- (double)(centerfreq - bw_limit) / 1e6,
- (double)(centerfreq + bw_limit) / 1e6);
- }
+ static const float soft_bw_threshold = 0.9f;
+ float bw_limit = (float)sample_rate / 2.f * soft_bw_threshold;
+ if ((float)abs(freq - centerfreq) >= bw_limit) {
+ log(LOG_WARNING, "Warning: dev[%d].channel[%d]: frequency %.3f MHz is outside of SDR operating bandwidth (%.3f-%.3f MHz)\n", devidx, chanidx, (double)freq / 1e6,
+ (double)(centerfreq - bw_limit) / 1e6, (double)(centerfreq + bw_limit) / 1e6);
+ }
}
static int parse_anynum2int(libconfig::Setting& f) {
- int ret = 0;
- if(f.getType() == libconfig::Setting::TypeInt) {
- ret = (int)f;
- } else if(f.getType() == libconfig::Setting::TypeFloat) {
- ret = (int)((double)f * 1e6);
- } else if(f.getType() == libconfig::Setting::TypeString) {
- char *s = strdup((char const *)f);
- ret = (int)atofs(s);
- free(s);
- }
- return ret;
+ int ret = 0;
+ if (f.getType() == libconfig::Setting::TypeInt) {
+ ret = (int)f;
+ } else if (f.getType() == libconfig::Setting::TypeFloat) {
+ ret = (int)((double)f * 1e6);
+ } else if (f.getType() == libconfig::Setting::TypeString) {
+ char* s = strdup((char const*)f);
+ ret = (int)atofs(s);
+ free(s);
+ }
+ return ret;
}
-static int parse_channels(libconfig::Setting &chans, device_t *dev, int i) {
- int jj = 0;
- for (int j = 0; j < chans.getLength(); j++) {
- if(chans[j].exists("disable") && (bool)chans[j]["disable"] == true) {
- continue;
- }
- channel_t* channel = dev->channels + jj;
- for (int k = 0; k < AGC_EXTRA; k++) {
- channel->wavein[k] = 20;
- channel->waveout[k] = 0.5;
- }
- channel->axcindicate = NO_SIGNAL;
- channel->mode = MM_MONO;
- channel->need_mp3 = 0;
- channel->freq_count = 1;
- channel->freq_idx = 0;
- channel->highpass = chans[j].exists("highpass") ? (int)chans[j]["highpass"] : 100;
- channel->lowpass = chans[j].exists("lowpass") ? (int)chans[j]["lowpass"] : 2500;
- channel->lame = NULL;
- channel->lamebuf = NULL;
+static int parse_channels(libconfig::Setting& chans, device_t* dev, int i) {
+ int jj = 0;
+ for (int j = 0; j < chans.getLength(); j++) {
+ if (chans[j].exists("disable") && (bool)chans[j]["disable"] == true) {
+ continue;
+ }
+ channel_t* channel = dev->channels + jj;
+ for (int k = 0; k < AGC_EXTRA; k++) {
+ channel->wavein[k] = 20;
+ channel->waveout[k] = 0.5;
+ }
+ channel->axcindicate = NO_SIGNAL;
+ channel->mode = MM_MONO;
+ channel->need_mp3 = 0;
+ channel->freq_count = 1;
+ channel->freq_idx = 0;
+ channel->highpass = chans[j].exists("highpass") ? (int)chans[j]["highpass"] : 100;
+ channel->lowpass = chans[j].exists("lowpass") ? (int)chans[j]["lowpass"] : 2500;
+ channel->lame = NULL;
+ channel->lamebuf = NULL;
#ifdef NFM
- channel->pr = 0;
- channel->pj = 0;
- channel->prev_waveout = 0.5;
- channel->alpha = dev->alpha;
+ channel->pr = 0;
+ channel->pj = 0;
+ channel->prev_waveout = 0.5;
+ channel->alpha = dev->alpha;
#endif /* NFM */
- // Make sure lowpass / highpass aren't flipped.
- // If lowpass is enabled (greater than zero) it must be larger than highpass
- if (channel->lowpass > 0 && channel->lowpass < channel->highpass) {
- cerr << "Configuration error: devices.["<lowpass << ") must be greater than or equal to highpass (" << channel->highpass << ")\n";
- error();
- }
+ // Make sure lowpass / highpass aren't flipped.
+ // If lowpass is enabled (greater than zero) it must be larger than highpass
+ if (channel->lowpass > 0 && channel->lowpass < channel->highpass) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: lowpass (" << channel->lowpass << ") must be greater than or equal to highpass (" << channel->highpass << ")\n";
+ error();
+ }
- modulations channel_modulation = MOD_AM;
- if(chans[j].exists("modulation")) {
+ modulations channel_modulation = MOD_AM;
+ if (chans[j].exists("modulation")) {
#ifdef NFM
- if(strncmp(chans[j]["modulation"], "nfm", 3) == 0) {
- channel_modulation = MOD_NFM;
- } else
+ if (strncmp(chans[j]["modulation"], "nfm", 3) == 0) {
+ channel_modulation = MOD_NFM;
+ } else
#endif /* NFM */
- if(strncmp(chans[j]["modulation"], "am", 2) != 0) {
- cerr<<"Configuration error: devices.["<afc = chans[j].exists("afc") ? (unsigned char) (unsigned int)chans[j]["afc"] : 0;
- if(dev->mode == R_MULTICHANNEL) {
- channel->freqlist = mk_freqlist( 1 );
- channel->freqlist[0].frequency = parse_anynum2int(chans[j]["freq"]);
- warn_if_freq_not_in_range(i, j, channel->freqlist[0].frequency, dev->input->centerfreq, dev->input->sample_rate);
- if (chans[j].exists("label"))
- {
- channel->freqlist[0].label = strdup(chans[j]["label"]);
- }
- channel->freqlist[0].modulation = channel_modulation;
- } else { /* R_SCAN */
- channel->freq_count = chans[j]["freqs"].getLength();
- if(channel->freq_count < 1) {
- cerr<<"Configuration error: devices.["<freqlist = mk_freqlist( channel->freq_count );
- if(chans[j].exists("labels") && chans[j]["labels"].getLength() < channel->freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(chans[j].exists("squelch_threshold") && libconfig::Setting::TypeList == chans[j]["squelch_threshold"].getType() && chans[j]["squelch_threshold"].getLength() < channel->freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(chans[j].exists("squelch_snr_threshold") && libconfig::Setting::TypeList == chans[j]["squelch_snr_threshold"].getType() && chans[j]["squelch_snr_threshold"].getLength() < channel->freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(chans[j].exists("notch") && libconfig::Setting::TypeList == chans[j]["notch"].getType() && chans[j]["notch"].getLength() < channel->freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(chans[j].exists("notch_q") && libconfig::Setting::TypeList == chans[j]["notch_q"].getType() && chans[j]["notch_q"].getLength() < channel->freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(chans[j].exists("ctcss") && libconfig::Setting::TypeList == chans[j]["ctcss"].getType() && chans[j]["ctcss"].getLength() < channel->freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(chans[j].exists("modulation") && chans[j].exists("modulations")) {
- cerr<<"Configuration error: devices.["<freq_count) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
-
- for(int f = 0; ffreq_count; f++) {
- channel->freqlist[f].frequency = parse_anynum2int((chans[j]["freqs"][f]));
- if(chans[j].exists("labels")) {
- channel->freqlist[f].label = strdup(chans[j]["labels"][f]);
- }
- if(chans[j].exists("modulations")) {
+ if (strncmp(chans[j]["modulation"], "am", 2) != 0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: unknown modulation\n";
+ error();
+ }
+ }
+ channel->afc = chans[j].exists("afc") ? (unsigned char)(unsigned int)chans[j]["afc"] : 0;
+ if (dev->mode == R_MULTICHANNEL) {
+ channel->freqlist = mk_freqlist(1);
+ channel->freqlist[0].frequency = parse_anynum2int(chans[j]["freq"]);
+ warn_if_freq_not_in_range(i, j, channel->freqlist[0].frequency, dev->input->centerfreq, dev->input->sample_rate);
+ if (chans[j].exists("label")) {
+ channel->freqlist[0].label = strdup(chans[j]["label"]);
+ }
+ channel->freqlist[0].modulation = channel_modulation;
+ } else { /* R_SCAN */
+ channel->freq_count = chans[j]["freqs"].getLength();
+ if (channel->freq_count < 1) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: freqs should be a list with at least one element\n";
+ error();
+ }
+ channel->freqlist = mk_freqlist(channel->freq_count);
+ if (chans[j].exists("labels") && chans[j]["labels"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: labels should be a list with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ if (chans[j].exists("squelch_threshold") && libconfig::Setting::TypeList == chans[j]["squelch_threshold"].getType() && chans[j]["squelch_threshold"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: squelch_threshold should be an int or a list of ints with at least " << channel->freq_count
+ << " elements\n";
+ error();
+ }
+ if (chans[j].exists("squelch_snr_threshold") && libconfig::Setting::TypeList == chans[j]["squelch_snr_threshold"].getType() &&
+ chans[j]["squelch_snr_threshold"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j
+ << "]: squelch_snr_threshold should be an int, a float or a list of "
+ "ints or floats with at least "
+ << channel->freq_count << " elements\n";
+ error();
+ }
+ if (chans[j].exists("notch") && libconfig::Setting::TypeList == chans[j]["notch"].getType() && chans[j]["notch"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: notch should be an float or a list of floats with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ if (chans[j].exists("notch_q") && libconfig::Setting::TypeList == chans[j]["notch_q"].getType() && chans[j]["notch_q"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: notch_q should be a float or a list of floats with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ if (chans[j].exists("ctcss") && libconfig::Setting::TypeList == chans[j]["ctcss"].getType() && chans[j]["ctcss"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: ctcss should be an float or a list of floats with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ if (chans[j].exists("modulation") && chans[j].exists("modulations")) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: can't set both modulation and modulations\n";
+ error();
+ }
+ if (chans[j].exists("modulations") && chans[j]["modulations"].getLength() < channel->freq_count) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: modulations should be a list with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+
+ for (int f = 0; f < channel->freq_count; f++) {
+ channel->freqlist[f].frequency = parse_anynum2int((chans[j]["freqs"][f]));
+ if (chans[j].exists("labels")) {
+ channel->freqlist[f].label = strdup(chans[j]["labels"][f]);
+ }
+ if (chans[j].exists("modulations")) {
#ifdef NFM
- if(strncmp(chans[j]["modulations"][f], "nfm", 3) == 0) {
- channel->freqlist[f].modulation = MOD_NFM;
- } else
+ if (strncmp(chans[j]["modulations"][f], "nfm", 3) == 0) {
+ channel->freqlist[f].modulation = MOD_NFM;
+ } else
#endif /* NFM */
- if(strncmp(chans[j]["modulations"][f], "am", 2) == 0) {
- channel->freqlist[f].modulation = MOD_AM;
- } else {
- cerr<<"Configuration error: devices.["<freqlist[f].modulation = channel_modulation;
- }
- }
-// Set initial frequency for scanning
-// We tune 20 FFT bins higher to avoid DC spike
- dev->input->centerfreq = channel->freqlist[0].frequency + 20 * (double)(dev->input->sample_rate / fft_size);
- }
- if(chans[j].exists("squelch")) {
- cerr << "Warning: 'squelch' no longer supported and will be ignored, use 'squelch_threshold' or 'squelch_snr_threshold' instead\n";
- }
- if(chans[j].exists("squelch_threshold") && chans[j].exists("squelch_snr_threshold")) {
- cerr << "Warning: Both 'squelch_threshold' and 'squelch_snr_threshold' are set and may conflict\n";
- }
- if(chans[j].exists("squelch_threshold")) {
- // Value is dBFS, zero disables manual threshold (ie use auto squelch), negative is valid, positive is invalid
- if(libconfig::Setting::TypeList == chans[j]["squelch_threshold"].getType()) {
- // New-style array of per-frequency squelch settings
- for(int f = 0; ffreq_count; f++) {
- int threshold_dBFS = (int)chans[j]["squelch_threshold"][f];
- if (threshold_dBFS > 0) {
- cerr << "Configuration error: devices.["<freqlist[f].squelch.set_squelch_level_threshold(0);
- } else {
- channel->freqlist[f].squelch.set_squelch_level_threshold(dBFS_to_level(threshold_dBFS));
- }
- }
- } else if(libconfig::Setting::TypeInt == chans[j]["squelch_threshold"].getType()) {
- // Legacy (single squelch for all frequencies)
- int threshold_dBFS = (int)chans[j]["squelch_threshold"];
- float level;
- if (threshold_dBFS > 0) {
- cerr << "Configuration error: devices.["<freq_count; f++) {
- channel->freqlist[f].squelch.set_squelch_level_threshold(level);
- }
- } else {
- cerr << "Invalid value for squelch_threshold (should be int or list - use parentheses)\n";
- error();
- }
- }
- if(chans[j].exists("squelch_snr_threshold")) {
- // Value is SNR in dB, zero disables squelch (ie always open), -1 uses default value, positive is valid, other negative values are invalid
- if(libconfig::Setting::TypeList == chans[j]["squelch_snr_threshold"].getType()) {
- // New-style array of per-frequency squelch settings
- for(int f = 0; ffreq_count; f++) {
- float snr = 0.f;
- if (libconfig::Setting::TypeFloat == chans[j]["squelch_snr_threshold"][f].getType()) {
- snr = (float)chans[j]["squelch_snr_threshold"][f];
- } else if (libconfig::Setting::TypeInt == chans[j]["squelch_snr_threshold"][f].getType()) {
- snr = (int)chans[j]["squelch_snr_threshold"][f];
- } else {
- cerr << "Configuration error: devices.["<freqlist[f].squelch.set_squelch_snr_threshold(snr);
- }
- }
- } else if(libconfig::Setting::TypeFloat == chans[j]["squelch_snr_threshold"].getType() || libconfig::Setting::TypeInt == chans[j]["squelch_snr_threshold"].getType()) {
- // Legacy (single squelch for all frequencies)
- float snr = (libconfig::Setting::TypeFloat == chans[j]["squelch_snr_threshold"].getType()) ? (float)chans[j]["squelch_snr_threshold"] : (int)chans[j]["squelch_snr_threshold"];
-
- if (snr == -1.0) {
- continue; // "disable" so use the default without error message
- } else if (snr < 0) {
- cerr << "Configuration error: devices.["<freq_count; f++) {
- channel->freqlist[f].squelch.set_squelch_snr_threshold(snr);
- }
- } else {
- cerr << "Invalid value for squelch_snr_threshold (should be float, int, or list of int/float - use parentheses)\n";
- error();
- }
- }
- if(chans[j].exists("notch")) {
- static const float default_q = 10.0;
-
- if(chans[j].exists("notch_q") && chans[j]["notch"].getType() != chans[j]["notch_q"].getType()) {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- if(libconfig::Setting::TypeList == chans[j]["notch"].getType()) {
- for(int f = 0; ffreq_count; f++) {
- float freq = (float)chans[j]["notch"][f];
- float q = chans[j].exists("notch_q") ? (float)chans[j]["notch_q"][f] : default_q;
-
- if (q == 0.0) {
- q = default_q;
- } else if (q <= 0.0) {
- cerr<<"Configuration error: devices.["<freqlist[f].notch_filter = NotchFilter(freq, WAVE_RATE, q);
- }
- }
- } else if(libconfig::Setting::TypeFloat == chans[j]["notch"].getType() ) {
- float freq = (float)chans[j]["notch"];
- float q = chans[j].exists("notch_q") ? (float)chans[j]["notch_q"] : default_q;
- if (q <= 0.0) {
- cerr<<"Configuration error: devices.["<freq_count; f++) {
- if(freq == 0) {
- continue; // "disable" is default so ignore without error message
- } else if(freq < 0) {
- cerr << "devices.["<freqlist[f].notch_filter = NotchFilter(freq, WAVE_RATE, q);
- }
- }
- } else {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- }
- if(chans[j].exists("ctcss")) {
- if(libconfig::Setting::TypeList == chans[j]["ctcss"].getType()) {
- for(int f = 0; ffreq_count; f++) {
- float freq = (float)chans[j]["ctcss"][f];
-
- if(freq == 0) {
- continue; // "disable" for this channel in list
- } else if(freq < 0) {
- cerr << "devices.["<freqlist[f].squelch.set_ctcss_freq(freq, WAVE_RATE);
- }
- }
- } else if(libconfig::Setting::TypeFloat == chans[j]["ctcss"].getType() ) {
- float freq = (float)chans[j]["ctcss"];
- for(int f = 0; ffreq_count; f++) {
- if(freq <= 0) {
- cerr << "devices.["<freqlist[f].squelch.set_ctcss_freq(freq, WAVE_RATE);
- }
- }
- } else {
- cerr<<"Configuration error: devices.["<freq_count<<" elements\n";
- error();
- }
- }
- if(chans[j].exists("bandwidth")) {
- channel->needs_raw_iq = 1;
-
- if(libconfig::Setting::TypeList == chans[j]["bandwidth"].getType()) {
- for(int f = 0; ffreq_count; f++) {
- int bandwidth = parse_anynum2int(chans[j]["bandwidth"][f]);
-
- if(bandwidth == 0) {
- continue; // "disable" for this channel in list
- } else if(bandwidth < 0) {
- cerr << "devices.["<freqlist[f].lowpass_filter = LowpassFilter((float)bandwidth/2, WAVE_RATE);
- }
- }
- } else {
- int bandwidth = parse_anynum2int(chans[j]["bandwidth"]);
- if(bandwidth == 0) {
- continue; // "disable" is default so ignore without error message
- } else if(bandwidth < 0) {
- cerr << "devices.["<freq_count; f++) {
- channel->freqlist[f].lowpass_filter = LowpassFilter((float)bandwidth/2, WAVE_RATE);
- }
- }
- }
- }
- if(chans[j].exists("ampfactor")) {
- if(libconfig::Setting::TypeList == chans[j]["ampfactor"].getType()) {
- for(int f = 0; f < channel->freq_count; f++) {
- float ampfactor = (float)chans[j]["ampfactor"][f];
-
- if(ampfactor < 0) {
- cerr << "devices.["<freqlist[f].ampfactor = ampfactor;
- }
- } else {
- float ampfactor = (float)chans[j]["ampfactor"];
-
- if(ampfactor < 0) {
- cerr << "devices.["<freq_count; f++) {
- channel->freqlist[f].ampfactor = ampfactor;
- }
- }
- }
+ if (strncmp(chans[j]["modulations"][f], "am", 2) == 0) {
+ channel->freqlist[f].modulation = MOD_AM;
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] modulations.[" << f << "]: unknown modulation\n";
+ error();
+ }
+ } else {
+ channel->freqlist[f].modulation = channel_modulation;
+ }
+ }
+ // Set initial frequency for scanning
+ // We tune 20 FFT bins higher to avoid DC spike
+ dev->input->centerfreq = channel->freqlist[0].frequency + 20 * (double)(dev->input->sample_rate / fft_size);
+ }
+ if (chans[j].exists("squelch")) {
+ cerr << "Warning: 'squelch' no longer supported and will be ignored, use 'squelch_threshold' or 'squelch_snr_threshold' instead\n";
+ }
+ if (chans[j].exists("squelch_threshold") && chans[j].exists("squelch_snr_threshold")) {
+ cerr << "Warning: Both 'squelch_threshold' and 'squelch_snr_threshold' are set and may conflict\n";
+ }
+ if (chans[j].exists("squelch_threshold")) {
+ // Value is dBFS, zero disables manual threshold (ie use auto squelch), negative is valid, positive is invalid
+ if (libconfig::Setting::TypeList == chans[j]["squelch_threshold"].getType()) {
+ // New-style array of per-frequency squelch settings
+ for (int f = 0; f < channel->freq_count; f++) {
+ int threshold_dBFS = (int)chans[j]["squelch_threshold"][f];
+ if (threshold_dBFS > 0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: squelch_threshold must be less than or equal to 0\n";
+ error();
+ } else if (threshold_dBFS == 0) {
+ channel->freqlist[f].squelch.set_squelch_level_threshold(0);
+ } else {
+ channel->freqlist[f].squelch.set_squelch_level_threshold(dBFS_to_level(threshold_dBFS));
+ }
+ }
+ } else if (libconfig::Setting::TypeInt == chans[j]["squelch_threshold"].getType()) {
+ // Legacy (single squelch for all frequencies)
+ int threshold_dBFS = (int)chans[j]["squelch_threshold"];
+ float level;
+ if (threshold_dBFS > 0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: squelch_threshold must be less than or equal to 0\n";
+ error();
+ } else if (threshold_dBFS == 0) {
+ level = 0;
+ } else {
+ level = dBFS_to_level(threshold_dBFS);
+ }
+
+ for (int f = 0; f < channel->freq_count; f++) {
+ channel->freqlist[f].squelch.set_squelch_level_threshold(level);
+ }
+ } else {
+ cerr << "Invalid value for squelch_threshold (should be int or list - use parentheses)\n";
+ error();
+ }
+ }
+ if (chans[j].exists("squelch_snr_threshold")) {
+ // Value is SNR in dB, zero disables squelch (ie always open), -1 uses default value, positive is valid, other negative values are invalid
+ if (libconfig::Setting::TypeList == chans[j]["squelch_snr_threshold"].getType()) {
+ // New-style array of per-frequency squelch settings
+ for (int f = 0; f < channel->freq_count; f++) {
+ float snr = 0.f;
+ if (libconfig::Setting::TypeFloat == chans[j]["squelch_snr_threshold"][f].getType()) {
+ snr = (float)chans[j]["squelch_snr_threshold"][f];
+ } else if (libconfig::Setting::TypeInt == chans[j]["squelch_snr_threshold"][f].getType()) {
+ snr = (int)chans[j]["squelch_snr_threshold"][f];
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: squelch_snr_threshold list must be of int or float\n";
+ error();
+ }
+
+ if (snr == -1.0) {
+ continue; // "disable" for this channel in list
+ } else if (snr < 0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: squelch_snr_threshold must be greater than or equal to 0\n";
+ error();
+ } else {
+ channel->freqlist[f].squelch.set_squelch_snr_threshold(snr);
+ }
+ }
+ } else if (libconfig::Setting::TypeFloat == chans[j]["squelch_snr_threshold"].getType() || libconfig::Setting::TypeInt == chans[j]["squelch_snr_threshold"].getType()) {
+ // Legacy (single squelch for all frequencies)
+ float snr = (libconfig::Setting::TypeFloat == chans[j]["squelch_snr_threshold"].getType()) ? (float)chans[j]["squelch_snr_threshold"] : (int)chans[j]["squelch_snr_threshold"];
+
+ if (snr == -1.0) {
+ continue; // "disable" so use the default without error message
+ } else if (snr < 0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: squelch_snr_threshold must be greater than or equal to 0\n";
+ error();
+ }
+
+ for (int f = 0; f < channel->freq_count; f++) {
+ channel->freqlist[f].squelch.set_squelch_snr_threshold(snr);
+ }
+ } else {
+ cerr << "Invalid value for squelch_snr_threshold (should be float, int, or list of int/float - use parentheses)\n";
+ error();
+ }
+ }
+ if (chans[j].exists("notch")) {
+ static const float default_q = 10.0;
+
+ if (chans[j].exists("notch_q") && chans[j]["notch"].getType() != chans[j]["notch_q"].getType()) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: notch_q (if set) must be the same type as notch - "
+ << "float or a list of floats with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ if (libconfig::Setting::TypeList == chans[j]["notch"].getType()) {
+ for (int f = 0; f < channel->freq_count; f++) {
+ float freq = (float)chans[j]["notch"][f];
+ float q = chans[j].exists("notch_q") ? (float)chans[j]["notch_q"][f] : default_q;
+
+ if (q == 0.0) {
+ q = default_q;
+ } else if (q <= 0.0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "] freq.[" << f << "]: invalid value for notch_q: " << q << " (must be greater than 0.0)\n";
+ error();
+ }
+
+ if (freq == 0) {
+ continue; // "disable" for this channel in list
+ } else if (freq < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "] freq.[" << f << "]: invalid value for notch: " << freq << ", ignoring\n";
+ } else {
+ channel->freqlist[f].notch_filter = NotchFilter(freq, WAVE_RATE, q);
+ }
+ }
+ } else if (libconfig::Setting::TypeFloat == chans[j]["notch"].getType()) {
+ float freq = (float)chans[j]["notch"];
+ float q = chans[j].exists("notch_q") ? (float)chans[j]["notch_q"] : default_q;
+ if (q <= 0.0) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: invalid value for notch_q: " << q << " (must be greater than 0.0)\n";
+ error();
+ }
+ for (int f = 0; f < channel->freq_count; f++) {
+ if (freq == 0) {
+ continue; // "disable" is default so ignore without error message
+ } else if (freq < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "]: notch value '" << freq << "' invalid, ignoring\n";
+ } else {
+ channel->freqlist[f].notch_filter = NotchFilter(freq, WAVE_RATE, q);
+ }
+ }
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: notch should be an float or a list of floats with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ }
+ if (chans[j].exists("ctcss")) {
+ if (libconfig::Setting::TypeList == chans[j]["ctcss"].getType()) {
+ for (int f = 0; f < channel->freq_count; f++) {
+ float freq = (float)chans[j]["ctcss"][f];
+
+ if (freq == 0) {
+ continue; // "disable" for this channel in list
+ } else if (freq < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "] freq.[" << f << "]: invalid value for ctcss: " << freq << ", ignoring\n";
+ } else {
+ channel->freqlist[f].squelch.set_ctcss_freq(freq, WAVE_RATE);
+ }
+ }
+ } else if (libconfig::Setting::TypeFloat == chans[j]["ctcss"].getType()) {
+ float freq = (float)chans[j]["ctcss"];
+ for (int f = 0; f < channel->freq_count; f++) {
+ if (freq <= 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "]: ctcss value '" << freq << "' invalid, ignoring\n";
+ } else {
+ channel->freqlist[f].squelch.set_ctcss_freq(freq, WAVE_RATE);
+ }
+ }
+ } else {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: ctcss should be an float or a list of floats with at least " << channel->freq_count << " elements\n";
+ error();
+ }
+ }
+ if (chans[j].exists("bandwidth")) {
+ channel->needs_raw_iq = 1;
+
+ if (libconfig::Setting::TypeList == chans[j]["bandwidth"].getType()) {
+ for (int f = 0; f < channel->freq_count; f++) {
+ int bandwidth = parse_anynum2int(chans[j]["bandwidth"][f]);
+
+ if (bandwidth == 0) {
+ continue; // "disable" for this channel in list
+ } else if (bandwidth < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "] freq.[" << f << "]: bandwidth value '" << bandwidth << "' invalid, ignoring\n";
+ } else {
+ channel->freqlist[f].lowpass_filter = LowpassFilter((float)bandwidth / 2, WAVE_RATE);
+ }
+ }
+ } else {
+ int bandwidth = parse_anynum2int(chans[j]["bandwidth"]);
+ if (bandwidth == 0) {
+ continue; // "disable" is default so ignore without error message
+ } else if (bandwidth < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "]: bandwidth value '" << bandwidth << "' invalid, ignoring\n";
+ } else {
+ for (int f = 0; f < channel->freq_count; f++) {
+ channel->freqlist[f].lowpass_filter = LowpassFilter((float)bandwidth / 2, WAVE_RATE);
+ }
+ }
+ }
+ }
+ if (chans[j].exists("ampfactor")) {
+ if (libconfig::Setting::TypeList == chans[j]["ampfactor"].getType()) {
+ for (int f = 0; f < channel->freq_count; f++) {
+ float ampfactor = (float)chans[j]["ampfactor"][f];
+
+ if (ampfactor < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "] freq.[" << f << "]: ampfactor '" << ampfactor << "' must not be negative\n";
+ error();
+ }
+
+ channel->freqlist[f].ampfactor = ampfactor;
+ }
+ } else {
+ float ampfactor = (float)chans[j]["ampfactor"];
+
+ if (ampfactor < 0) {
+ cerr << "devices.[" << i << "] channels.[" << j << "]: ampfactor '" << ampfactor << "' must not be negative\n";
+ error();
+ }
+
+ for (int f = 0; f < channel->freq_count; f++) {
+ channel->freqlist[f].ampfactor = ampfactor;
+ }
+ }
+ }
#ifdef NFM
- if(chans[j].exists("tau")) {
- channel->alpha = ((int)chans[j]["tau"] == 0 ? 0.0f : exp(-1.0f/(WAVE_RATE * 1e-6 * (int)chans[j]["tau"])));
- }
+ if (chans[j].exists("tau")) {
+ channel->alpha = ((int)chans[j]["tau"] == 0 ? 0.0f : exp(-1.0f / (WAVE_RATE * 1e-6 * (int)chans[j]["tau"])));
+ }
#endif /* NFM */
- libconfig::Setting &outputs = chans[j]["outputs"];
- channel->output_count = outputs.getLength();
- if(channel->output_count < 1) {
- cerr<<"Configuration error: devices.["<outputs = (output_t *)XCALLOC(channel->output_count, sizeof(struct output_t));
- int outputs_enabled = parse_outputs(outputs, channel, i, j, false);
- if(outputs_enabled < 1) {
- cerr<<"Configuration error: devices.["<outputs = (output_t *)XREALLOC(channel->outputs, outputs_enabled * sizeof(struct output_t));
- channel->output_count = outputs_enabled;
-
- dev->base_bins[jj] = dev->bins[jj] = (size_t)ceil(
- (channel->freqlist[0].frequency + dev->input->sample_rate - dev->input->centerfreq)
- / (double)(dev->input->sample_rate / fft_size) - 1.0
- ) % fft_size;
- debug_print("bins[%d]: %zu\n", jj, dev->bins[jj]);
+ libconfig::Setting& outputs = chans[j]["outputs"];
+ channel->output_count = outputs.getLength();
+ if (channel->output_count < 1) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: no outputs defined\n";
+ error();
+ }
+ channel->outputs = (output_t*)XCALLOC(channel->output_count, sizeof(struct output_t));
+ int outputs_enabled = parse_outputs(outputs, channel, i, j, false);
+ if (outputs_enabled < 1) {
+ cerr << "Configuration error: devices.[" << i << "] channels.[" << j << "]: no outputs defined\n";
+ error();
+ }
+ channel->outputs = (output_t*)XREALLOC(channel->outputs, outputs_enabled * sizeof(struct output_t));
+ channel->output_count = outputs_enabled;
+
+ dev->base_bins[jj] = dev->bins[jj] =
+ (size_t)ceil((channel->freqlist[0].frequency + dev->input->sample_rate - dev->input->centerfreq) / (double)(dev->input->sample_rate / fft_size) - 1.0) % fft_size;
+ debug_print("bins[%d]: %zu\n", jj, dev->bins[jj]);
#ifdef NFM
- for(int f = 0; f < channel->freq_count; f++) {
- if(channel->freqlist[f].modulation == MOD_NFM) {
- channel->needs_raw_iq = 1;
- break;
- }
- }
+ for (int f = 0; f < channel->freq_count; f++) {
+ if (channel->freqlist[f].modulation == MOD_NFM) {
+ channel->needs_raw_iq = 1;
+ break;
+ }
+ }
#endif /* NFM */
- if(channel->needs_raw_iq) {
-// Downmixing is done only for NFM and raw IQ outputs. It's not critical to have some residual
-// freq offset in AM, as it doesn't affect sound quality significantly.
- double dm_dphi = (double)(channel->freqlist[0].frequency - dev->input->centerfreq); // downmix freq in Hz
-
-// In general, sample_rate is not required to be an integer multiple of WAVE_RATE.
-// However the FFT window may only slide by an integer number of input samples. A non-zero rounding error
-// introduces additional phase rotation which we have to compensate in order to shift the channel of interest
-// to the center of the spectrum of the output I/Q stream. This is important for correct NFM demodulation.
-// The error value (in Hz):
-// - has an absolute value 0..WAVE_RATE/2
-// - is linear with the error introduced by rounding the value of sample_rate/WAVE_RATE to the nearest integer
-// (range of -0.5..0.5)
-// - is linear with the distance between center frequency and the channel frequency, normalized to 0..1
- double decimation_factor = ((double)dev->input->sample_rate / (double)WAVE_RATE);
- double dm_dphi_correction = (double)WAVE_RATE / 2.0;
- dm_dphi_correction *= (decimation_factor - round(decimation_factor));
- dm_dphi_correction *= (double)(channel->freqlist[0].frequency - dev->input->centerfreq) /
- ((double)dev->input->sample_rate/2.0);
-
- debug_print("dev[%d].chan[%d]: dm_dphi: %f Hz dm_dphi_correction: %f Hz\n",
- i, jj, dm_dphi, dm_dphi_correction);
- dm_dphi -= dm_dphi_correction;
- debug_print("dev[%d].chan[%d]: dm_dphi_corrected: %f Hz\n", i, jj, dm_dphi);
-// Normalize
- dm_dphi /= (double)WAVE_RATE;
-// Unalias it, to prevent overflow of int during cast
- dm_dphi -= trunc(dm_dphi);
- debug_print("dev[%d].chan[%d]: dm_dphi_normalized=%f\n", i, jj, dm_dphi);
-// Translate this to uint32_t range 0x00000000-0x00ffffff
- dm_dphi *= 256.0 * 65536.0;
-// Cast it to signed int first, because casting negative float to uint is not portable
- channel->dm_dphi = (uint32_t)((int)dm_dphi);
- debug_print("dev[%d].chan[%d]: dm_dphi_scaled=%f cast=0x%x\n", i, jj, dm_dphi, channel->dm_dphi);
- channel->dm_phi = 0.f;
- }
+ if (channel->needs_raw_iq) {
+ // Downmixing is done only for NFM and raw IQ outputs. It's not critical to have some residual
+ // freq offset in AM, as it doesn't affect sound quality significantly.
+ double dm_dphi = (double)(channel->freqlist[0].frequency - dev->input->centerfreq); // downmix freq in Hz
+
+ // In general, sample_rate is not required to be an integer multiple of WAVE_RATE.
+ // However the FFT window may only slide by an integer number of input samples. A non-zero rounding error
+ // introduces additional phase rotation which we have to compensate in order to shift the channel of interest
+ // to the center of the spectrum of the output I/Q stream. This is important for correct NFM demodulation.
+ // The error value (in Hz):
+ // - has an absolute value 0..WAVE_RATE/2
+ // - is linear with the error introduced by rounding the value of sample_rate/WAVE_RATE to the nearest integer
+ // (range of -0.5..0.5)
+ // - is linear with the distance between center frequency and the channel frequency, normalized to 0..1
+ double decimation_factor = ((double)dev->input->sample_rate / (double)WAVE_RATE);
+ double dm_dphi_correction = (double)WAVE_RATE / 2.0;
+ dm_dphi_correction *= (decimation_factor - round(decimation_factor));
+ dm_dphi_correction *= (double)(channel->freqlist[0].frequency - dev->input->centerfreq) / ((double)dev->input->sample_rate / 2.0);
+
+ debug_print("dev[%d].chan[%d]: dm_dphi: %f Hz dm_dphi_correction: %f Hz\n", i, jj, dm_dphi, dm_dphi_correction);
+ dm_dphi -= dm_dphi_correction;
+ debug_print("dev[%d].chan[%d]: dm_dphi_corrected: %f Hz\n", i, jj, dm_dphi);
+ // Normalize
+ dm_dphi /= (double)WAVE_RATE;
+ // Unalias it, to prevent overflow of int during cast
+ dm_dphi -= trunc(dm_dphi);
+ debug_print("dev[%d].chan[%d]: dm_dphi_normalized=%f\n", i, jj, dm_dphi);
+ // Translate this to uint32_t range 0x00000000-0x00ffffff
+ dm_dphi *= 256.0 * 65536.0;
+ // Cast it to signed int first, because casting negative float to uint is not portable
+ channel->dm_dphi = (uint32_t)((int)dm_dphi);
+ debug_print("dev[%d].chan[%d]: dm_dphi_scaled=%f cast=0x%x\n", i, jj, dm_dphi, channel->dm_dphi);
+ channel->dm_phi = 0.f;
+ }
#ifdef DEBUG_SQUELCH
- // Setup squelch debug file, if enabled
- char tmp_filepath[1024];
- for(int f = 0; f < channel->freq_count; f++) {
- snprintf(tmp_filepath, sizeof(tmp_filepath), "./squelch_debug-%d-%d.dat", j, f);
- channel->freqlist[f].squelch.set_debug_file(tmp_filepath);
- }
+ // Setup squelch debug file, if enabled
+ char tmp_filepath[1024];
+ for (int f = 0; f < channel->freq_count; f++) {
+ snprintf(tmp_filepath, sizeof(tmp_filepath), "./squelch_debug-%d-%d.dat", j, f);
+ channel->freqlist[f].squelch.set_debug_file(tmp_filepath);
+ }
#endif /* DEBUG_SQUELCH */
- jj++;
- }
- return jj;
+ jj++;
+ }
+ return jj;
}
-int parse_devices(libconfig::Setting &devs) {
- int devcnt = 0;
- for (int i = 0; i < devs.getLength(); i++) {
- if(devs[i].exists("disable") && (bool)devs[i]["disable"] == true) continue;
- device_t* dev = devices + devcnt;
- if(devs[i].exists("type")) {
- dev->input = input_new(devs[i]["type"]);
- if(dev->input == NULL) {
- cerr<<"Configuration error: devices.["<input = input_new(devs[i]["type"]);
+ if (dev->input == NULL) {
+ cerr << "Configuration error: devices.[" << i << "]: unsupported device type\n";
+ error();
+ }
+ } else {
#ifdef WITH_RTLSDR
- cerr<<"Warning: devices.["<input = input_new("rtlsdr");
+ cerr << "Warning: devices.[" << i << "]: assuming device type \"rtlsdr\", please set \"type\" in the device section.\n";
+ dev->input = input_new("rtlsdr");
#else
- cerr<<"Configuration error: devices.["<input != NULL);
- if(devs[i].exists("sample_rate")) {
- int sample_rate = parse_anynum2int(devs[i]["sample_rate"]);
- if(sample_rate < WAVE_RATE) {
- cerr<<"Configuration error: devices.["<input->sample_rate = sample_rate;
- }
- if(devs[i].exists("mode")) {
- if(!strncmp(devs[i]["mode"], "multichannel", 12)) {
- dev->mode = R_MULTICHANNEL;
- } else if(!strncmp(devs[i]["mode"], "scan", 4)) {
- dev->mode = R_SCAN;
- } else {
- cerr<<"Configuration error: devices.["<mode = R_MULTICHANNEL;
- }
- if(dev->mode == R_MULTICHANNEL) {
- dev->input->centerfreq = parse_anynum2int(devs[i]["centerfreq"]);
- } // centerfreq for R_SCAN will be set by parse_channels() after frequency list has been read
+ }
+ assert(dev->input != NULL);
+ if (devs[i].exists("sample_rate")) {
+ int sample_rate = parse_anynum2int(devs[i]["sample_rate"]);
+ if (sample_rate < WAVE_RATE) {
+ cerr << "Configuration error: devices.[" << i << "]: sample_rate must be greater than " << WAVE_RATE << "\n";
+ error();
+ }
+ dev->input->sample_rate = sample_rate;
+ }
+ if (devs[i].exists("mode")) {
+ if (!strncmp(devs[i]["mode"], "multichannel", 12)) {
+ dev->mode = R_MULTICHANNEL;
+ } else if (!strncmp(devs[i]["mode"], "scan", 4)) {
+ dev->mode = R_SCAN;
+ } else {
+ cerr << "Configuration error: devices.[" << i << "]: invalid mode (must be one of: \"scan\", \"multichannel\")\n";
+ error();
+ }
+ } else {
+ dev->mode = R_MULTICHANNEL;
+ }
+ if (dev->mode == R_MULTICHANNEL) {
+ dev->input->centerfreq = parse_anynum2int(devs[i]["centerfreq"]);
+ } // centerfreq for R_SCAN will be set by parse_channels() after frequency list has been read
#ifdef NFM
- if(devs[i].exists("tau")) {
- dev->alpha = ((int)devs[i]["tau"] == 0 ? 0.0f : exp(-1.0f/(WAVE_RATE * 1e-6 * (int)devs[i]["tau"])));
- } else {
- dev->alpha = alpha;
- }
+ if (devs[i].exists("tau")) {
+ dev->alpha = ((int)devs[i]["tau"] == 0 ? 0.0f : exp(-1.0f / (WAVE_RATE * 1e-6 * (int)devs[i]["tau"])));
+ } else {
+ dev->alpha = alpha;
+ }
#endif /* NFM */
-// Parse hardware-dependent configuration parameters
- if(input_parse_config(dev->input, devs[i]) < 0) {
- // FIXME: get and display error string from input_parse_config
- // Right now it exits the program on failure.
- }
-// Some basic sanity checks for crucial parameters which have to be set
-// (or can be modified) by the input driver
- assert(dev->input->sfmt != SFMT_UNDEF);
- assert(dev->input->fullscale > 0);
- assert(dev->input->bytes_per_sample > 0);
- assert(dev->input->sample_rate > WAVE_RATE);
-
-// For the input buffer size use a base value and round it up to the nearest multiple
-// of FFT_BATCH blocks of input samples.
-// ceil is required here because sample rate is not guaranteed to be an integer multiple of WAVE_RATE.
- size_t fft_batch_len = FFT_BATCH * (2 * dev->input->bytes_per_sample *
- (size_t)ceil((double)dev->input->sample_rate / (double)WAVE_RATE));
- dev->input->buf_size = MIN_BUF_SIZE;
- if(dev->input->buf_size % fft_batch_len != 0)
- dev->input->buf_size += fft_batch_len - dev->input->buf_size % fft_batch_len;
- debug_print("dev->input->buf_size: %zu\n", dev->input->buf_size);
- dev->input->buffer = (unsigned char *)XCALLOC(sizeof(unsigned char),
- dev->input->buf_size + 2 * dev->input->bytes_per_sample * fft_size);
- dev->input->bufs = dev->input->bufe = 0;
- dev->input->overflow_count = 0;
- dev->output_overrun_count = 0;
- dev->waveend = dev->waveavail = dev->row = dev->tq_head = dev->tq_tail = 0;
- dev->last_frequency = -1;
-
- libconfig::Setting &chans = devs[i]["channels"];
- if(chans.getLength() < 1) {
- cerr<<"Configuration error: devices.["<channels = (channel_t *)XCALLOC(chans.getLength(), sizeof(channel_t));
- dev->bins = (size_t *)XCALLOC(chans.getLength(), sizeof(size_t));
- dev->base_bins = (size_t *)XCALLOC(chans.getLength(), sizeof(size_t));
- dev->channel_count = 0;
- int channel_count = parse_channels(chans, dev, i);
- if(channel_count < 1) {
- cerr<<"Configuration error: devices.["<mode == R_SCAN && channel_count > 1) {
- cerr<<"Configuration error: devices.["<channels = (channel_t *)XREALLOC(dev->channels, channel_count * sizeof(channel_t));
- dev->bins = (size_t *)XREALLOC(dev->bins, channel_count * sizeof(size_t));
- dev->base_bins = (size_t *)XREALLOC(dev->base_bins, channel_count * sizeof(size_t));
- dev->channel_count = channel_count;
- devcnt++;
- }
- return devcnt;
+ // Parse hardware-dependent configuration parameters
+ if (input_parse_config(dev->input, devs[i]) < 0) {
+ // FIXME: get and display error string from input_parse_config
+ // Right now it exits the program on failure.
+ }
+ // Some basic sanity checks for crucial parameters which have to be set
+ // (or can be modified) by the input driver
+ assert(dev->input->sfmt != SFMT_UNDEF);
+ assert(dev->input->fullscale > 0);
+ assert(dev->input->bytes_per_sample > 0);
+ assert(dev->input->sample_rate > WAVE_RATE);
+
+ // For the input buffer size use a base value and round it up to the nearest multiple
+ // of FFT_BATCH blocks of input samples.
+ // ceil is required here because sample rate is not guaranteed to be an integer multiple of WAVE_RATE.
+ size_t fft_batch_len = FFT_BATCH * (2 * dev->input->bytes_per_sample * (size_t)ceil((double)dev->input->sample_rate / (double)WAVE_RATE));
+ dev->input->buf_size = MIN_BUF_SIZE;
+ if (dev->input->buf_size % fft_batch_len != 0)
+ dev->input->buf_size += fft_batch_len - dev->input->buf_size % fft_batch_len;
+ debug_print("dev->input->buf_size: %zu\n", dev->input->buf_size);
+ dev->input->buffer = (unsigned char*)XCALLOC(sizeof(unsigned char), dev->input->buf_size + 2 * dev->input->bytes_per_sample * fft_size);
+ dev->input->bufs = dev->input->bufe = 0;
+ dev->input->overflow_count = 0;
+ dev->output_overrun_count = 0;
+ dev->waveend = dev->waveavail = dev->row = dev->tq_head = dev->tq_tail = 0;
+ dev->last_frequency = -1;
+
+ libconfig::Setting& chans = devs[i]["channels"];
+ if (chans.getLength() < 1) {
+ cerr << "Configuration error: devices.[" << i << "]: no channels configured\n";
+ error();
+ }
+ dev->channels = (channel_t*)XCALLOC(chans.getLength(), sizeof(channel_t));
+ dev->bins = (size_t*)XCALLOC(chans.getLength(), sizeof(size_t));
+ dev->base_bins = (size_t*)XCALLOC(chans.getLength(), sizeof(size_t));
+ dev->channel_count = 0;
+ int channel_count = parse_channels(chans, dev, i);
+ if (channel_count < 1) {
+ cerr << "Configuration error: devices.[" << i << "]: no channels enabled\n";
+ error();
+ }
+ if (dev->mode == R_SCAN && channel_count > 1) {
+ cerr << "Configuration error: devices.[" << i << "]: only one channel is allowed in scan mode\n";
+ error();
+ }
+ dev->channels = (channel_t*)XREALLOC(dev->channels, channel_count * sizeof(channel_t));
+ dev->bins = (size_t*)XREALLOC(dev->bins, channel_count * sizeof(size_t));
+ dev->base_bins = (size_t*)XREALLOC(dev->base_bins, channel_count * sizeof(size_t));
+ dev->channel_count = channel_count;
+ devcnt++;
+ }
+ return devcnt;
}
-int parse_mixers(libconfig::Setting &mx) {
- const char *name;
- int mm = 0;
- for(int i = 0; i < mx.getLength(); i++) {
- if(mx[i].exists("disable") && (bool)mx[i]["disable"] == true) continue;
- if((name = mx[i].getName()) == NULL) {
- cerr<<"Configuration error: mixers.["<name = strdup(name);
- mixer->enabled = false;
- mixer->interval = MIX_DIVISOR;
- mixer->output_overrun_count = 0;
- mixer->input_count = 0;
- mixer->inputs = NULL;
- mixer->inputs_todo = NULL;
- mixer->input_mask = NULL;
- channel_t *channel = &mixer->channel;
- channel->highpass = mx[i].exists("highpass") ? (int)mx[i]["highpass"] : 100;
- channel->lowpass = mx[i].exists("lowpass") ? (int)mx[i]["lowpass"] : 2500;
- channel->mode = MM_MONO;
-
- // Make sure lowpass / highpass aren't flipped.
- // If lowpass is enabled (greater than zero) it must be larger than highpass
- if (channel->lowpass > 0 && channel->lowpass < channel->highpass) {
- cerr << "Configuration error: mixers.[" << i <<"]: lowpass (" << channel->lowpass << ") must be greater than or equal to highpass (" << channel->highpass << ")\n";
- error();
- }
-
- libconfig::Setting &outputs = mx[i]["outputs"];
- channel->output_count = outputs.getLength();
- if(channel->output_count < 1) {
- cerr<<"Configuration error: mixers.["<outputs = (output_t *)XCALLOC(channel->output_count, sizeof(struct output_t));
- int outputs_enabled = parse_outputs(outputs, channel, i, 0, true);
- if(outputs_enabled < 1) {
- cerr<<"Configuration error: mixers.["<outputs = (output_t *)XREALLOC(channel->outputs, outputs_enabled * sizeof(struct output_t));
- channel->output_count = outputs_enabled;
- mm++;
- }
- return mm;
+int parse_mixers(libconfig::Setting& mx) {
+ const char* name;
+ int mm = 0;
+ for (int i = 0; i < mx.getLength(); i++) {
+ if (mx[i].exists("disable") && (bool)mx[i]["disable"] == true)
+ continue;
+ if ((name = mx[i].getName()) == NULL) {
+ cerr << "Configuration error: mixers.[" << i << "]: undefined mixer name\n";
+ error();
+ }
+ debug_print("mm=%d name=%s\n", mm, name);
+ mixer_t* mixer = &mixers[mm];
+ mixer->name = strdup(name);
+ mixer->enabled = false;
+ mixer->interval = MIX_DIVISOR;
+ mixer->output_overrun_count = 0;
+ mixer->input_count = 0;
+ mixer->inputs = NULL;
+ mixer->inputs_todo = NULL;
+ mixer->input_mask = NULL;
+ channel_t* channel = &mixer->channel;
+ channel->highpass = mx[i].exists("highpass") ? (int)mx[i]["highpass"] : 100;
+ channel->lowpass = mx[i].exists("lowpass") ? (int)mx[i]["lowpass"] : 2500;
+ channel->mode = MM_MONO;
+
+ // Make sure lowpass / highpass aren't flipped.
+ // If lowpass is enabled (greater than zero) it must be larger than highpass
+ if (channel->lowpass > 0 && channel->lowpass < channel->highpass) {
+ cerr << "Configuration error: mixers.[" << i << "]: lowpass (" << channel->lowpass << ") must be greater than or equal to highpass (" << channel->highpass << ")\n";
+ error();
+ }
+
+ libconfig::Setting& outputs = mx[i]["outputs"];
+ channel->output_count = outputs.getLength();
+ if (channel->output_count < 1) {
+ cerr << "Configuration error: mixers.[" << i << "]: no outputs defined\n";
+ error();
+ }
+ channel->outputs = (output_t*)XCALLOC(channel->output_count, sizeof(struct output_t));
+ int outputs_enabled = parse_outputs(outputs, channel, i, 0, true);
+ if (outputs_enabled < 1) {
+ cerr << "Configuration error: mixers.[" << i << "]: no outputs defined\n";
+ error();
+ }
+ channel->outputs = (output_t*)XREALLOC(channel->outputs, outputs_enabled * sizeof(struct output_t));
+ channel->output_count = outputs_enabled;
+ mm++;
+ }
+ return mm;
}
// vim: ts=4
diff --git a/src/ctcss.h b/src/ctcss.h
index 432a44d..94d080c 100644
--- a/src/ctcss.h
+++ b/src/ctcss.h
@@ -20,82 +20,79 @@
#ifndef _CTCSS_H
#define _CTCSS_H 1
+#include // size_t
#include
-#include // size_t
class ToneDetector {
-public:
- ToneDetector(float tone_freq, float sample_freq, int window_size);
- void process_sample(const float &sample);
- void reset(void);
-
- const float & relative_power(void) const { return magnitude_; }
- const float & freq(void) const { return tone_freq_; }
- const float & coefficient (void) const { return coeff_; }
-
-private:
- float tone_freq_;
- float magnitude_;
-
- int window_size_;
- float coeff_;
-
- int count_;
- float q0_;
- float q1_;
- float q2_;
+ public:
+ ToneDetector(float tone_freq, float sample_freq, int window_size);
+ void process_sample(const float& sample);
+ void reset(void);
+
+ const float& relative_power(void) const { return magnitude_; }
+ const float& freq(void) const { return tone_freq_; }
+ const float& coefficient(void) const { return coeff_; }
+
+ private:
+ float tone_freq_;
+ float magnitude_;
+
+ int window_size_;
+ float coeff_;
+
+ int count_;
+ float q0_;
+ float q1_;
+ float q2_;
};
-
class ToneDetectorSet {
-public:
- struct PowerIndex {
- float power;
- float freq;
- };
-
- ToneDetectorSet() {}
-
- bool add(const float & tone_freq, const float & sample_freq, int window_size);
- void process_sample(const float &sample);
- void reset(void);
-
- float sorted_powers(std::vector &powers);
-
-private:
- std::vector tones_;
-};
+ public:
+ struct PowerIndex {
+ float power;
+ float freq;
+ };
+
+ ToneDetectorSet() {}
+ bool add(const float& tone_freq, const float& sample_freq, int window_size);
+ void process_sample(const float& sample);
+ void reset(void);
+
+ float sorted_powers(std::vector& powers);
+
+ private:
+ std::vector tones_;
+};
class CTCSS {
-public:
- CTCSS(void) : enabled_(false), found_count_(0), not_found_count_(0) {}
- CTCSS(const float & ctcss_freq, const float & sample_rate, int window_size);
- void process_audio_sample(const float &sample);
- void reset(void);
-
- const size_t & found_count(void) const { return found_count_; }
- const size_t & not_found_count(void) const { return not_found_count_; }
-
- bool is_enabled(void) const { return enabled_; }
- bool enough_samples(void) const { return enough_samples_; }
- bool has_tone(void) const { return !enabled_ || has_tone_; }
-
- static std::vector standard_tones;
-
-private:
- bool enabled_;
- float ctcss_freq_;
- int window_size_;
- size_t found_count_;
- size_t not_found_count_;
-
- ToneDetectorSet powers_;
-
- bool enough_samples_;
- int sample_count_;
- bool has_tone_;
+ public:
+ CTCSS(void) : enabled_(false), found_count_(0), not_found_count_(0) {}
+ CTCSS(const float& ctcss_freq, const float& sample_rate, int window_size);
+ void process_audio_sample(const float& sample);
+ void reset(void);
+
+ const size_t& found_count(void) const { return found_count_; }
+ const size_t& not_found_count(void) const { return not_found_count_; }
+
+ bool is_enabled(void) const { return enabled_; }
+ bool enough_samples(void) const { return enough_samples_; }
+ bool has_tone(void) const { return !enabled_ || has_tone_; }
+
+ static std::vector standard_tones;
+
+ private:
+ bool enabled_;
+ float ctcss_freq_;
+ int window_size_;
+ size_t found_count_;
+ size_t not_found_count_;
+
+ ToneDetectorSet powers_;
+ bool enough_samples_;
+ int sample_count_;
+ bool has_tone_;
};
#endif /* _CTCSS_H */
diff --git a/src/filters.cpp b/src/filters.cpp
index 426e171..be7a926 100644
--- a/src/filters.cpp
+++ b/src/filters.cpp
@@ -17,161 +17,147 @@
* along with this program. If not, see .
*/
-#include "logging.h" // debug_print()
+#include "logging.h" // debug_print()
#include "filters.h"
using namespace std;
// Default constructor is no filter
-NotchFilter::NotchFilter(void) : enabled_(false) {
-}
+NotchFilter::NotchFilter(void) : enabled_(false) {}
// Notch Filter based on https://www.dsprelated.com/showcode/173.php
-NotchFilter::NotchFilter(float notch_freq, float sample_freq, float q): enabled_(true), x{0.0}, y{0.0} {
- if (notch_freq <= 0.0) {
- debug_print("Invalid frequency %f Hz, disabling notch filter\n", notch_freq);
- enabled_ = false;
- return;
- }
+NotchFilter::NotchFilter(float notch_freq, float sample_freq, float q) : enabled_(true), x{0.0}, y{0.0} {
+ if (notch_freq <= 0.0) {
+ debug_print("Invalid frequency %f Hz, disabling notch filter\n", notch_freq);
+ enabled_ = false;
+ return;
+ }
- debug_print("Adding notch filter for %f Hz with parameters {%f, %f}\n", notch_freq, sample_freq, q);
+ debug_print("Adding notch filter for %f Hz with parameters {%f, %f}\n", notch_freq, sample_freq, q);
- float wo = 2*M_PI*(notch_freq/sample_freq);
+ float wo = 2 * M_PI * (notch_freq / sample_freq);
- e = 1/(1 + tan(wo/(q*2)));
- p = cos(wo);
- d[0] = e;
- d[1] = 2*e*p;
- d[2] = (2*e-1);
+ e = 1 / (1 + tan(wo / (q * 2)));
+ p = cos(wo);
+ d[0] = e;
+ d[1] = 2 * e * p;
+ d[2] = (2 * e - 1);
- debug_print("wo:%f e:%f p:%f d:{%f,%f,%f}\n", wo, e, p, d[0], d[1], d[2]);
+ debug_print("wo:%f e:%f p:%f d:{%f,%f,%f}\n", wo, e, p, d[0], d[1], d[2]);
}
-void NotchFilter::apply(float &value) {
- if (!enabled_) {
- return;
- }
+void NotchFilter::apply(float& value) {
+ if (!enabled_) {
+ return;
+ }
- x[0] = x[1];
- x[1] = x[2];
- x[2] = value;
+ x[0] = x[1];
+ x[1] = x[2];
+ x[2] = value;
- y[0] = y[1];
- y[1] = y[2];
- y[2] = d[0]*x[2] - d[1]*x[1] + d[0]*x[0] + d[1]*y[1] - d[2]*y[0];
+ y[0] = y[1];
+ y[1] = y[2];
+ y[2] = d[0] * x[2] - d[1] * x[1] + d[0] * x[0] + d[1] * y[1] - d[2] * y[0];
- value = y[2];
+ value = y[2];
}
// Default constructor is no filter
-LowpassFilter::LowpassFilter(void) : enabled_(false) {
-}
+LowpassFilter::LowpassFilter(void) : enabled_(false) {}
// 2nd order lowpass Bessel filter, based entirely on a simplification of https://www-users.cs.york.ac.uk/~fisher/mkfilter/
LowpassFilter::LowpassFilter(float freq, float sample_freq) : enabled_(true) {
- if (freq <= 0.0) {
- debug_print("Invalid frequency %f Hz, disabling lowpass filter\n", freq);
- enabled_ = false;
- return;
- }
-
- debug_print("Adding lowpass filter at %f Hz with a sample rate of %f\n", freq, sample_freq);
-
- double raw_alpha = (double)freq/sample_freq;
- double warped_alpha = tan(M_PI * raw_alpha) / M_PI;
-
- complex zeros[2] = {-1.0, -1.0};
- complex poles[2];
- poles[0] = blt(M_PI * 2 * warped_alpha * complex(-1.10160133059e+00, 6.36009824757e-01));
- poles[1] = blt(M_PI * 2 * warped_alpha * conj(complex(-1.10160133059e+00, 6.36009824757e-01)));
-
- complex topcoeffs[3];
- complex botcoeffs[3];
- expand(zeros, 2, topcoeffs);
- expand(poles, 2, botcoeffs);
- complex gain_complex = evaluate(topcoeffs, 2, botcoeffs, 2, 1.0);
- gain = hypot(gain_complex.imag(), gain_complex.real());
-
- for (int i = 0; i <= 2; i++)
-{
- ycoeffs[i] = -(botcoeffs[i].real() / botcoeffs[2].real());
- }
-
- debug_print("gain: %f, ycoeffs: {%f, %f}\n", gain, ycoeffs[0], ycoeffs[1]);
+ if (freq <= 0.0) {
+ debug_print("Invalid frequency %f Hz, disabling lowpass filter\n", freq);
+ enabled_ = false;
+ return;
+ }
+
+ debug_print("Adding lowpass filter at %f Hz with a sample rate of %f\n", freq, sample_freq);
+
+ double raw_alpha = (double)freq / sample_freq;
+ double warped_alpha = tan(M_PI * raw_alpha) / M_PI;
+
+ complex zeros[2] = {-1.0, -1.0};
+ complex poles[2];
+ poles[0] = blt(M_PI * 2 * warped_alpha * complex(-1.10160133059e+00, 6.36009824757e-01));
+ poles[1] = blt(M_PI * 2 * warped_alpha * conj(complex(-1.10160133059e+00, 6.36009824757e-01)));
+
+ complex topcoeffs[3];
+ complex botcoeffs[3];
+ expand(zeros, 2, topcoeffs);
+ expand(poles, 2, botcoeffs);
+ complex gain_complex = evaluate(topcoeffs, 2, botcoeffs, 2, 1.0);
+ gain = hypot(gain_complex.imag(), gain_complex.real());
+
+ for (int i = 0; i <= 2; i++) {
+ ycoeffs[i] = -(botcoeffs[i].real() / botcoeffs[2].real());
+ }
+
+ debug_print("gain: %f, ycoeffs: {%f, %f}\n", gain, ycoeffs[0], ycoeffs[1]);
}
-complex LowpassFilter::blt(complex pz)
-{
- return (2.0 + pz) / (2.0 - pz);
+complex LowpassFilter::blt(complex pz) {
+ return (2.0 + pz) / (2.0 - pz);
}
/* evaluate response, substituting for z */
-complex LowpassFilter::evaluate(complex topco[], int nz, complex botco[], int np, complex z)
-{
- return eval(topco, nz, z) / eval(botco, np, z);
+complex LowpassFilter::evaluate(complex topco[], int nz, complex botco[], int np, complex z) {
+ return eval(topco, nz, z) / eval(botco, np, z);
}
/* evaluate polynomial in z, substituting for z */
-complex LowpassFilter::eval(complex coeffs[], int npz, complex z)
-{
- complex sum (0.0);
- for (int i = npz; i >= 0; i--) {
- sum = (sum * z) + coeffs[i];
- }
- return sum;
+complex LowpassFilter::eval(complex coeffs[], int npz, complex z) {
+ complex sum(0.0);
+ for (int i = npz; i >= 0; i--) {
+ sum = (sum * z) + coeffs[i];
+ }
+ return sum;
}
/* compute product of poles or zeros as a polynomial of z */
-void LowpassFilter::expand(complex pz[], int npz, complex coeffs[])
-{
- coeffs[0] = 1.0;
- for (int i = 0; i < npz; i++)
- {
- coeffs[i+1] = 0.0;
- }
- for (int i = 0; i < npz; i++)
- {
- multin(pz[i], npz, coeffs);
- }
- /* check computed coeffs of z^k are all real */
- for (int i = 0; i < npz+1; i++)
- {
- if (fabs(coeffs[i].imag()) > 1e-10)
- {
- log(LOG_ERR, "coeff of z^%d is not real; poles/zeros are not complex conjugates\n", i);
- error();
- }
- }
+void LowpassFilter::expand(complex pz[], int npz, complex coeffs[]) {
+ coeffs[0] = 1.0;
+ for (int i = 0; i < npz; i++) {
+ coeffs[i + 1] = 0.0;
+ }
+ for (int i = 0; i < npz; i++) {
+ multin(pz[i], npz, coeffs);
+ }
+ /* check computed coeffs of z^k are all real */
+ for (int i = 0; i < npz + 1; i++) {
+ if (fabs(coeffs[i].imag()) > 1e-10) {
+ log(LOG_ERR, "coeff of z^%d is not real; poles/zeros are not complex conjugates\n", i);
+ error();
+ }
+ }
}
-void LowpassFilter::multin(complex w, int npz, complex coeffs[])
-{
- /* multiply factor (z-w) into coeffs */
- complex nw = -w;
- for (int i = npz; i >= 1; i--)
- {
- coeffs[i] = (nw * coeffs[i]) + coeffs[i-1];
- }
- coeffs[0] = nw * coeffs[0];
+void LowpassFilter::multin(complex w, int npz, complex coeffs[]) {
+ /* multiply factor (z-w) into coeffs */
+ complex nw = -w;
+ for (int i = npz; i >= 1; i--) {
+ coeffs[i] = (nw * coeffs[i]) + coeffs[i - 1];
+ }
+ coeffs[0] = nw * coeffs[0];
}
-void LowpassFilter::apply(float &r, float &j) {
- if (!enabled_) {
- return;
- }
+void LowpassFilter::apply(float& r, float& j) {
+ if (!enabled_) {
+ return;
+ }
- complex input(r, j);
+ complex input(r, j);
- xv[0] = xv[1];
- xv[1] = xv[2];
- xv[2] = input / gain;
+ xv[0] = xv[1];
+ xv[1] = xv[2];
+ xv[2] = input / gain;
- yv[0] = yv[1];
- yv[1] = yv[2];
- yv[2] = (xv[0] + xv[2]) + (2.0f * xv[1]) + (ycoeffs[0] * yv[0]) + (ycoeffs[1] * yv[1]);
+ yv[0] = yv[1];
+ yv[1] = yv[2];
+ yv[2] = (xv[0] + xv[2]) + (2.0f * xv[1]) + (ycoeffs[0] * yv[0]) + (ycoeffs[1] * yv[1]);
- r = yv[2].real();
- j = yv[2].imag();
+ r = yv[2].real();
+ j = yv[2].imag();
}
-
diff --git a/src/filters.h b/src/filters.h
index db2e41d..209ff0d 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -22,45 +22,42 @@
#include
-class NotchFilter
-{
-public:
- NotchFilter(void);
- NotchFilter(float notch_freq, float sample_freq, float q);
- void apply(float &value);
- bool enabled(void) { return enabled_; }
-
-private:
- bool enabled_;
- float e;
- float p;
- float d[3];
- float x[3];
- float y[3];
+class NotchFilter {
+ public:
+ NotchFilter(void);
+ NotchFilter(float notch_freq, float sample_freq, float q);
+ void apply(float& value);
+ bool enabled(void) { return enabled_; }
+
+ private:
+ bool enabled_;
+ float e;
+ float p;
+ float d[3];
+ float x[3];
+ float y[3];
};
-class LowpassFilter
-{
-public:
- LowpassFilter(void);
- LowpassFilter(float freq, float sample_freq);
- void apply(float &r, float &j);
- bool enabled(void) const {return enabled_;}
-
-private:
- static std::complex blt(std::complex pz);
- static void expand(std::complex pz[], int npz, std::complex coeffs[]);
- static void multin(std::complex w, int npz, std::complex coeffs[]);
- static std::complex evaluate(std::complex topco[], int nz, std::complex botco[], int np, std::complex z);
- static std::complex eval(std::complex coeffs[], int npz, std::complex z);
-
- bool enabled_;
- float ycoeffs[3];
- float gain;
-
- std::complex xv[3];
- std::complex yv[3];
+class LowpassFilter {
+ public:
+ LowpassFilter(void);
+ LowpassFilter(float freq, float sample_freq);
+ void apply(float& r, float& j);
+ bool enabled(void) const { return enabled_; }
+
+ private:
+ static std::complex blt(std::complex pz);
+ static void expand(std::complex pz[], int npz, std::complex coeffs[]);
+ static void multin(std::complex w, int npz, std::complex coeffs[]);
+ static std::complex evaluate(std::complex topco[], int nz, std::complex botco[], int np, std::complex z);
+ static std::complex eval(std::complex coeffs[], int npz, std::complex z);
+
+ bool enabled_;
+ float ycoeffs[3];
+ float gain;
+
+ std::complex xv[3];
+ std::complex yv[3];
};
#endif /* _FILTERS_H */
-
diff --git a/src/generate_signal.cpp b/src/generate_signal.cpp
index 38aab7d..f59e0f7 100644
--- a/src/generate_signal.cpp
+++ b/src/generate_signal.cpp
@@ -27,68 +27,60 @@ float Tone::WEAK = 0.05;
float Tone::NORMAL = 0.2;
float Tone::STRONG = 0.4;
-Tone::Tone(int sample_rate, const float &freq, const float &l) : sample_rate_(sample_rate), freq_(freq), ampl_(ampl), sample_count_(0)
-{
-}
+Tone::Tone(int sample_rate, const float& freq, const float& ampl) : sample_rate_(sample_rate), freq_(freq), ampl_(ampl), sample_count_(0) {}
-float Tone::get_sample(void)
-{
- sample_count_++;
- return ampl_ * sin(2 * M_PI * sample_count_ * freq_ / sample_rate_);
+float Tone::get_sample(void) {
+ sample_count_++;
+ return ampl_ * sin(2 * M_PI * sample_count_ * freq_ / sample_rate_);
}
float Noise::WEAK = 0.05;
float Noise::NORMAL = 0.2;
float Noise::STRONG = 0.5;
-Noise::Noise(const float &l) : ampl_(ampl) {
-
- // create a seeded generator
- std::random_device r;
- std::seed_seq s{r(), r(), r(), r(), r(), r(), r(), r()};
- generator = std::mt19937(s);
+Noise::Noise(const float& ampl) : ampl_(ampl) {
+ // create a seeded generator
+ std::random_device r;
+ std::seed_seq s{r(), r(), r(), r(), r(), r(), r(), r()};
+ generator = std::mt19937(s);
- // centered at 0.0, standard deviation of 0.1
- distribution = normal_distribution (0.0, 0.1);
+ // centered at 0.0, standard deviation of 0.1
+ distribution = normal_distribution(0.0, 0.1);
}
float Noise::get_sample(void) {
return ampl_ * distribution(generator);
}
-GenerateSignal::GenerateSignal(int sample_rate) : sample_rate_(sample_rate)
-{
-}
+GenerateSignal::GenerateSignal(int sample_rate) : sample_rate_(sample_rate) {}
-void GenerateSignal::add_tone(const float &freq, const float &l) {
- tones_.push_back(Tone(sample_rate_, freq, ampl));
+void GenerateSignal::add_tone(const float& freq, const float& ampl) {
+ tones_.push_back(Tone(sample_rate_, freq, ampl));
}
-void GenerateSignal::add_noise(const float &l) {
- noises_.push_back(Noise(ampl));
+void GenerateSignal::add_noise(const float& ampl) {
+ noises_.push_back(Noise(ampl));
}
float GenerateSignal::get_sample(void) {
- float value = 0.0;
+ float value = 0.0;
- for (vector::iterator tone = tones_.begin() ; tone != tones_.end(); ++tone) {
- value += tone->get_sample();
- }
+ for (vector::iterator tone = tones_.begin(); tone != tones_.end(); ++tone) {
+ value += tone->get_sample();
+ }
- for (vector::iterator noise = noises_.begin() ; noise != noises_.end(); ++noise) {
- value += noise->get_sample();
- }
+ for (vector::iterator noise = noises_.begin(); noise != noises_.end(); ++noise) {
+ value += noise->get_sample();
+ }
- return value;
+ return value;
}
+void GenerateSignal::write_file(const string& filepath, const float& seconds) {
+ FILE* fp = fopen(filepath.c_str(), "wb");
-void GenerateSignal::write_file(const string &filepath, const float &seconds) {
-
- FILE* fp = fopen(filepath.c_str(), "wb");
-
- for (int i = 0 ; i < sample_rate_ * seconds; ++i) {
- float sample = get_sample();
- fwrite(&sample, sizeof(float), 1, fp);
- }
- fclose(fp);
+ for (int i = 0; i < sample_rate_ * seconds; ++i) {
+ float sample = get_sample();
+ fwrite(&sample, sizeof(float), 1, fp);
+ }
+ fclose(fp);
}
diff --git a/src/generate_signal.h b/src/generate_signal.h
index d6e9aef..c644906 100644
--- a/src/generate_signal.h
+++ b/src/generate_signal.h
@@ -20,20 +20,20 @@
#ifndef _GENERATE_SIGNAL_H
#define _GENERATE_SIGNAL_H
-#include
-#include
#include
+#include
+#include
class Tone {
-public:
+ public:
static float WEAK;
static float NORMAL;
static float STRONG;
- Tone(int sample_rate, const float &freq, const float &l);
+ Tone(int sample_rate, const float& freq, const float& ampl);
float get_sample(void);
-private:
+ private:
int sample_rate_;
float freq_;
float ampl_;
@@ -41,32 +41,32 @@ class Tone {
};
class Noise {
-public:
+ public:
static float WEAK;
static float NORMAL;
static float STRONG;
- Noise(const float &l);
+ Noise(const float& ampl);
float get_sample(void);
-private:
+ private:
float ampl_;
std::mt19937 generator;
std::normal_distribution distribution;
};
class GenerateSignal {
-public:
+ public:
GenerateSignal(int sample_rate);
- void add_tone(const float &freq, const float &l);
- void add_noise(const float &l);
+ void add_tone(const float& freq, const float& ampl);
+ void add_noise(const float& ampl);
float get_sample(void);
- void write_file(const std::string &filepath, const float &seconds);
+ void write_file(const std::string& filepath, const float& seconds);
-private:
+ private:
int sample_rate_;
std::vector tones_;
std::vector noises_;
diff --git a/src/helper_functions.cpp b/src/helper_functions.cpp
index 21e8b86..e782b71 100644
--- a/src/helper_functions.cpp
+++ b/src/helper_functions.cpp
@@ -17,26 +17,26 @@
* along with this program. If not, see .
*/
-#include // size_t
-#include // strerror
-#include // struct stat, S_ISDIR
+#include // struct stat, S_ISDIR
+#include // size_t
+#include // strerror
-#include "logging.h"
#include "helper_functions.h"
+#include "logging.h"
using namespace std;
-bool dir_exists(const string &dir_path) {
- struct stat st;
- return (stat(dir_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
+bool dir_exists(const string& dir_path) {
+ struct stat st;
+ return (stat(dir_path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
}
-bool file_exists(const string &file_path) {
- struct stat st;
- return (stat(file_path.c_str(), &st) == 0 && S_ISREG(st.st_mode));
+bool file_exists(const string& file_path) {
+ struct stat st;
+ return (stat(file_path.c_str(), &st) == 0 && S_ISREG(st.st_mode));
}
-bool make_dir(const string &dir_path) {
+bool make_dir(const string& dir_path) {
if (dir_exists(dir_path)) {
return true;
}
@@ -48,8 +48,7 @@ bool make_dir(const string &dir_path) {
return true;
}
-bool make_subdirs(const string &basedir, const string &subdirs) {
-
+bool make_subdirs(const string& basedir, const string& subdirs) {
// if final directory exists then nothing to do
const string delim = "/";
const string final_path = basedir + delim + subdirs;
@@ -64,15 +63,14 @@ bool make_subdirs(const string &basedir, const string &subdirs) {
if (!make_dir(basedir + delim + subdirs.substr(0, index))) {
return false;
}
- index = subdirs.find_first_of(delim, index+1);
+ index = subdirs.find_first_of(delim, index + 1);
}
make_dir(final_path);
return dir_exists(final_path);
}
-string make_dated_subdirs(const string &basedir, const struct tm *time) {
-
+string make_dated_subdirs(const string& basedir, const struct tm* time) {
// use the time to build the date subdirectories
char date_path[11];
strftime(date_path, sizeof(date_path), "%Y/%m/%d", time);
diff --git a/src/helper_functions.h b/src/helper_functions.h
index 728fd9e..9db4b08 100644
--- a/src/helper_functions.h
+++ b/src/helper_functions.h
@@ -20,13 +20,13 @@
#ifndef _HELPER_FUNCTIONS_H
#define _HELPER_FUNCTIONS_H
-#include // struct tm
+#include // struct tm
#include
-bool dir_exists(const std::string &dir_path);
-bool file_exists(const std::string &file_path);
-bool make_dir(const std::string &dir_path);
-bool make_subdirs(const std::string &basedir, const std::string &subdirs);
-std::string make_dated_subdirs(const std::string &basedir, const struct tm *time);
+bool dir_exists(const std::string& dir_path);
+bool file_exists(const std::string& file_path);
+bool make_dir(const std::string& dir_path);
+bool make_subdirs(const std::string& basedir, const std::string& subdirs);
+std::string make_dated_subdirs(const std::string& basedir, const struct tm* time);
#endif /* _HELPER_FUNCTIONS_H */
diff --git a/src/input-common.cpp b/src/input-common.cpp
index 157fc3b..20255fc 100644
--- a/src/input-common.cpp
+++ b/src/input-common.cpp
@@ -18,116 +18,114 @@
* along with this program. If not, see .
*/
-
-#include
+#include "input-common.h"
#include
-#include // dlopen, dlsym
+#include // dlopen, dlsym
#include
#include
-#include // asprintf
-#include // free
+#include // asprintf
+#include // free
#include
-#include "input-common.h"
+#include
using namespace std;
-typedef input_t *(*input_new_func_t)(void);
+typedef input_t* (*input_new_func_t)(void);
-input_t *input_new(char const * const type) {
- assert(type != NULL);
- void *dlhandle = dlopen(NULL, RTLD_NOW);
- assert(dlhandle != NULL);
- char *fname = NULL;
- int chars_written = asprintf(&fname, "%s_input_new", type);
- if(chars_written <= 0) {
- return NULL;
- }
- input_new_func_t fptr = (input_new_func_t)dlsym(dlhandle, fname);
- free(fname);
- if(fptr == NULL) {
- return NULL;
- }
- input_t *input = (*fptr)();
- assert(input->init != NULL);
- assert(input->run_rx_thread != NULL);
- assert(input->set_centerfreq != NULL);
- return input;
+input_t* input_new(char const* const type) {
+ assert(type != NULL);
+ void* dlhandle = dlopen(NULL, RTLD_NOW);
+ assert(dlhandle != NULL);
+ char* fname = NULL;
+ int chars_written = asprintf(&fname, "%s_input_new", type);
+ if (chars_written <= 0) {
+ return NULL;
+ }
+ input_new_func_t fptr = (input_new_func_t)dlsym(dlhandle, fname);
+ free(fname);
+ if (fptr == NULL) {
+ return NULL;
+ }
+ input_t* input = (*fptr)();
+ assert(input->init != NULL);
+ assert(input->run_rx_thread != NULL);
+ assert(input->set_centerfreq != NULL);
+ return input;
}
-int input_init(input_t * const input) {
- assert(input != NULL);
- input_state_t new_state = INPUT_FAILED; // fail-safe default
- errno = 0;
- int ret = input->init(input);
- if(ret < 0) {
- ret = -1;
- } else if((ret = pthread_mutex_init(&input->buffer_lock, NULL)) != 0) {
- errno = ret;
- ret = -1;
- } else {
- new_state = INPUT_INITIALIZED;
- ret = 0;
- }
- input->state = new_state;
- return ret;
+int input_init(input_t* const input) {
+ assert(input != NULL);
+ input_state_t new_state = INPUT_FAILED; // fail-safe default
+ errno = 0;
+ int ret = input->init(input);
+ if (ret < 0) {
+ ret = -1;
+ } else if ((ret = pthread_mutex_init(&input->buffer_lock, NULL)) != 0) {
+ errno = ret;
+ ret = -1;
+ } else {
+ new_state = INPUT_INITIALIZED;
+ ret = 0;
+ }
+ input->state = new_state;
+ return ret;
}
-int input_start(input_t * const input) {
- assert(input != NULL);
- assert(input->dev_data != NULL);
- assert(input->state == INPUT_INITIALIZED);
- int err = pthread_create(&input->rx_thread, NULL, input->run_rx_thread, (void *)input);
- if(err != 0) {
- errno = err;
- return -1;
- }
- return 0;
+int input_start(input_t* const input) {
+ assert(input != NULL);
+ assert(input->dev_data != NULL);
+ assert(input->state == INPUT_INITIALIZED);
+ int err = pthread_create(&input->rx_thread, NULL, input->run_rx_thread, (void*)input);
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+ return 0;
}
-int input_parse_config(input_t * const input, libconfig::Setting &cfg) {
- assert(input != NULL);
- if(input->parse_config != NULL) {
- return input->parse_config(input, cfg);
- } else {
-// Very simple inputs (like stdin) might not necessarily have any configuration
-// variables, so it's legal not to have parse_config defined.
- return 0;
- }
+int input_parse_config(input_t* const input, libconfig::Setting& cfg) {
+ assert(input != NULL);
+ if (input->parse_config != NULL) {
+ return input->parse_config(input, cfg);
+ } else {
+ // Very simple inputs (like stdin) might not necessarily have any configuration
+ // variables, so it's legal not to have parse_config defined.
+ return 0;
+ }
}
-int input_stop(input_t * const input) {
- assert(input != NULL);
- assert(input->dev_data != NULL);
- int err = 0;
- errno = 0;
- if(input->state == INPUT_RUNNING && input->stop != NULL) {
- err = input->stop(input);
- if(err != 0) {
- input->state = INPUT_FAILED;
- return -1;
- }
- }
- input->state = INPUT_STOPPED;
- err = pthread_join(input->rx_thread, NULL);
- if(err != 0) {
- errno = err;
- return -1;
- }
- return 0;
+int input_stop(input_t* const input) {
+ assert(input != NULL);
+ assert(input->dev_data != NULL);
+ int err = 0;
+ errno = 0;
+ if (input->state == INPUT_RUNNING && input->stop != NULL) {
+ err = input->stop(input);
+ if (err != 0) {
+ input->state = INPUT_FAILED;
+ return -1;
+ }
+ }
+ input->state = INPUT_STOPPED;
+ err = pthread_join(input->rx_thread, NULL);
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+ return 0;
}
-int input_set_centerfreq(input_t * const input, int const centerfreq) {
- assert(input != NULL);
- assert(input->dev_data != NULL);
- if(input->state != INPUT_RUNNING) {
- return -1;
- }
- int ret = input->set_centerfreq(input, centerfreq);
- if(ret != 0) {
- input->state = INPUT_FAILED;
- return -1;
- }
- input->centerfreq = centerfreq;
- return 0;
+int input_set_centerfreq(input_t* const input, int const centerfreq) {
+ assert(input != NULL);
+ assert(input->dev_data != NULL);
+ if (input->state != INPUT_RUNNING) {
+ return -1;
+ }
+ int ret = input->set_centerfreq(input, centerfreq);
+ if (ret != 0) {
+ input->state = INPUT_FAILED;
+ return -1;
+ }
+ input->centerfreq = centerfreq;
+ return 0;
}
-
diff --git a/src/input-common.h b/src/input-common.h
index 18bc55b..ccf926a 100644
--- a/src/input-common.h
+++ b/src/input-common.h
@@ -28,52 +28,39 @@
#define MODULE_EXPORT extern "C"
#endif /* __GNUC__ */
-typedef enum {
- SFMT_UNDEF = 0,
- SFMT_U8,
- SFMT_S8,
- SFMT_S16,
- SFMT_F32
-} sample_format_t;
+typedef enum { SFMT_UNDEF = 0, SFMT_U8, SFMT_S8, SFMT_S16, SFMT_F32 } sample_format_t;
#define SAMPLE_FORMAT_CNT 5
-typedef enum {
- INPUT_UNKNOWN = 0,
- INPUT_INITIALIZED,
- INPUT_RUNNING,
- INPUT_FAILED,
- INPUT_STOPPED,
- INPUT_DISABLED
-} input_state_t;
+typedef enum { INPUT_UNKNOWN = 0, INPUT_INITIALIZED, INPUT_RUNNING, INPUT_FAILED, INPUT_STOPPED, INPUT_DISABLED } input_state_t;
#define INPUT_STATE_CNT 6
typedef struct input_t input_t;
struct input_t {
- unsigned char *buffer;
- void *dev_data;
- size_t buf_size, bufs, bufe;
- size_t overflow_count;
- input_state_t state;
- sample_format_t sfmt;
- float fullscale;
- int bytes_per_sample;
- int sample_rate;
- int centerfreq;
- int (*parse_config)(input_t * const input, libconfig::Setting &cfg);
- int (*init)(input_t * const input);
- void *(*run_rx_thread)(void *input_ptr); // to be launched via pthread_create()
- int (*set_centerfreq)(input_t * const input, int const centerfreq);
- int (*stop)(input_t * const input) ;
- pthread_t rx_thread;
- pthread_mutex_t buffer_lock;
+ unsigned char* buffer;
+ void* dev_data;
+ size_t buf_size, bufs, bufe;
+ size_t overflow_count;
+ input_state_t state;
+ sample_format_t sfmt;
+ float fullscale;
+ int bytes_per_sample;
+ int sample_rate;
+ int centerfreq;
+ int (*parse_config)(input_t* const input, libconfig::Setting& cfg);
+ int (*init)(input_t* const input);
+ void* (*run_rx_thread)(void* input_ptr); // to be launched via pthread_create()
+ int (*set_centerfreq)(input_t* const input, int const centerfreq);
+ int (*stop)(input_t* const input);
+ pthread_t rx_thread;
+ pthread_mutex_t buffer_lock;
};
-input_t *input_new(char const * const type);
-int input_init(input_t * const input);
-int input_parse_config(input_t * const input, libconfig::Setting &cfg);
-int input_start(input_t * const input);
-int input_set_centerfreq(input_t * const input, int const centerfreq);
-int input_stop(input_t * const input);
+input_t* input_new(char const* const type);
+int input_init(input_t* const input);
+int input_parse_config(input_t* const input, libconfig::Setting& cfg);
+int input_start(input_t* const input);
+int input_set_centerfreq(input_t* const input, int const centerfreq);
+int input_stop(input_t* const input);
#endif /* _INPUT_COMMON_H */
diff --git a/src/input-file.cpp b/src/input-file.cpp
index f9f7516..9277b56 100644
--- a/src/input-file.cpp
+++ b/src/input-file.cpp
@@ -18,167 +18,166 @@
* along with this program. If not, see .
*/
-#include
+#include "input-file.h" // file_dev_data_t
#include
-#include // SCHAR_MAX
+#include // SCHAR_MAX
+#include
#include
-#include // FIXME: get rid of this
-#include // usleep
-#include // Setting
-#include "input-common.h" // input_t, sample_format_t, input_state_t, MODULE_EXPORT
-#include "input-helpers.h" // circbuffer_append
-#include "input-file.h" // file_dev_data_t
-#include "rtl_airband.h" // do_exit, fft_size, debug_print, XCALLOC, error()
+#include // FIXME: get rid of this
+#include // usleep
+#include // Setting
+#include "input-common.h" // input_t, sample_format_t, input_state_t, MODULE_EXPORT
+#include "input-helpers.h" // circbuffer_append
+#include "rtl_airband.h" // do_exit, fft_size, debug_print, XCALLOC, error()
using namespace std;
-int file_parse_config(input_t * const input, libconfig::Setting &cfg) {
- assert(input != NULL);
- file_dev_data_t *dev_data = (file_dev_data_t *)input->dev_data;
- assert(dev_data != NULL);
-
- if(cfg.exists("filepath")) {
- dev_data->filepath = strdup(cfg["filepath"]);
- } else {
- cerr << "File configuration error: no 'filepath' given\n";
- error();
- }
-
- if (cfg.exists("speedup_factor")) {
- if (cfg["speedup_factor"].getType() == libconfig::Setting::TypeInt) {
- dev_data->speedup_factor = (int)cfg["speedup_factor"];
- } else if (cfg["speedup_factor"].getType() == libconfig::Setting::TypeFloat) {
- dev_data->speedup_factor = (float)cfg["speedup_factor"];
- } else {
- cerr << "File configuration error: 'speedup_factor' must be a float or int if set\n";
- error();
- }
- if (dev_data->speedup_factor <= 0.0) {
- cerr << "File configuration error: 'speedup_factor' must be >= 0.0\n";
- error();
- }
- } else {
- dev_data->speedup_factor = 4;
- }
-
- return 0;
+int file_parse_config(input_t* const input, libconfig::Setting& cfg) {
+ assert(input != NULL);
+ file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
+ assert(dev_data != NULL);
+
+ if (cfg.exists("filepath")) {
+ dev_data->filepath = strdup(cfg["filepath"]);
+ } else {
+ cerr << "File configuration error: no 'filepath' given\n";
+ error();
+ }
+
+ if (cfg.exists("speedup_factor")) {
+ if (cfg["speedup_factor"].getType() == libconfig::Setting::TypeInt) {
+ dev_data->speedup_factor = (int)cfg["speedup_factor"];
+ } else if (cfg["speedup_factor"].getType() == libconfig::Setting::TypeFloat) {
+ dev_data->speedup_factor = (float)cfg["speedup_factor"];
+ } else {
+ cerr << "File configuration error: 'speedup_factor' must be a float or int if set\n";
+ error();
+ }
+ if (dev_data->speedup_factor <= 0.0) {
+ cerr << "File configuration error: 'speedup_factor' must be >= 0.0\n";
+ error();
+ }
+ } else {
+ dev_data->speedup_factor = 4;
+ }
+
+ return 0;
}
-int file_init(input_t * const input) {
- assert(input != NULL);
- file_dev_data_t *dev_data = (file_dev_data_t *)input->dev_data;
- assert(dev_data != NULL);
+int file_init(input_t* const input) {
+ assert(input != NULL);
+ file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
+ assert(dev_data != NULL);
- dev_data->input_file = fopen(dev_data->filepath, "rb");
- if(!dev_data->input_file) {
- cerr << "File input failed to open '" << dev_data->filepath << "' - " << strerror(errno) << endl;
- error();
- }
+ dev_data->input_file = fopen(dev_data->filepath, "rb");
+ if (!dev_data->input_file) {
+ cerr << "File input failed to open '" << dev_data->filepath << "' - " << strerror(errno) << endl;
+ error();
+ }
- log(LOG_INFO, "File input %s initialized\n", dev_data->filepath);
- return 0;
+ log(LOG_INFO, "File input %s initialized\n", dev_data->filepath);
+ return 0;
}
-void *file_rx_thread(void *ctx) {
- input_t *input = (input_t *)ctx;
- assert(input != NULL);
- assert(input->sample_rate != 0);
- file_dev_data_t *dev_data = (file_dev_data_t *)input->dev_data;
- assert(dev_data != NULL);
- assert(dev_data->input_file != NULL);
- assert(dev_data->speedup_factor != 0.0);
-
- size_t buf_len = (input->buf_size/2) - 1;
- unsigned char *buf = (unsigned char *)XCALLOC(1, buf_len);
-
- float time_per_byte_ms = 1000 / (input->sample_rate * input->bytes_per_sample * 2 * dev_data->speedup_factor);
-
- log(LOG_DEBUG, "sample_rate: %d, bytes_per_sample: %d, speedup_factor: %f, time_per_byte_ms: %f\n",
- input->sample_rate, input->bytes_per_sample, dev_data->speedup_factor, time_per_byte_ms);
-
- input->state = INPUT_RUNNING;
-
- while(true) {
- if(do_exit) {
- break;
- }
- if(feof(dev_data->input_file)) {
- log(LOG_INFO, "File '%s': hit end of file at %d, disabling\n", dev_data->filepath, ftell(dev_data->input_file));
- input->state = INPUT_FAILED;
- break;
- }
- if(ferror(dev_data->input_file)) {
- log(LOG_ERR, "File '%s': read error (%d), disabling\n", dev_data->filepath, ferror(dev_data->input_file));
- input->state = INPUT_FAILED;
- break;
- }
-
- timeval start;
- gettimeofday(&start, NULL);
-
- size_t space_left;
- pthread_mutex_lock(&input->buffer_lock);
- if (input->bufe >= input->bufs) {
- space_left = input->bufs + (input->buf_size - input->bufe);
- } else {
- space_left = input->bufs - input->bufe;
- }
- pthread_mutex_unlock(&input->buffer_lock);
-
- if (space_left > buf_len) {
- size_t len = fread(buf, sizeof(unsigned char), buf_len, dev_data->input_file);
- circbuffer_append(input, buf, len);
-
- timeval end;
- gettimeofday(&end, NULL);
-
- int time_taken_ms = delta_sec(&start, &end) * 1000;
- int sleep_time_ms = len * time_per_byte_ms - time_taken_ms;
-
- if(sleep_time_ms > 0) {
- SLEEP(sleep_time_ms);
- }
- } else {
- SLEEP(10);
- }
- }
-
- free(buf);
- return 0;
+void* file_rx_thread(void* ctx) {
+ input_t* input = (input_t*)ctx;
+ assert(input != NULL);
+ assert(input->sample_rate != 0);
+ file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
+ assert(dev_data != NULL);
+ assert(dev_data->input_file != NULL);
+ assert(dev_data->speedup_factor != 0.0);
+
+ size_t buf_len = (input->buf_size / 2) - 1;
+ unsigned char* buf = (unsigned char*)XCALLOC(1, buf_len);
+
+ float time_per_byte_ms = 1000 / (input->sample_rate * input->bytes_per_sample * 2 * dev_data->speedup_factor);
+
+ log(LOG_DEBUG, "sample_rate: %d, bytes_per_sample: %d, speedup_factor: %f, time_per_byte_ms: %f\n", input->sample_rate, input->bytes_per_sample, dev_data->speedup_factor, time_per_byte_ms);
+
+ input->state = INPUT_RUNNING;
+
+ while (true) {
+ if (do_exit) {
+ break;
+ }
+ if (feof(dev_data->input_file)) {
+ log(LOG_INFO, "File '%s': hit end of file at %d, disabling\n", dev_data->filepath, ftell(dev_data->input_file));
+ input->state = INPUT_FAILED;
+ break;
+ }
+ if (ferror(dev_data->input_file)) {
+ log(LOG_ERR, "File '%s': read error (%d), disabling\n", dev_data->filepath, ferror(dev_data->input_file));
+ input->state = INPUT_FAILED;
+ break;
+ }
+
+ timeval start;
+ gettimeofday(&start, NULL);
+
+ size_t space_left;
+ pthread_mutex_lock(&input->buffer_lock);
+ if (input->bufe >= input->bufs) {
+ space_left = input->bufs + (input->buf_size - input->bufe);
+ } else {
+ space_left = input->bufs - input->bufe;
+ }
+ pthread_mutex_unlock(&input->buffer_lock);
+
+ if (space_left > buf_len) {
+ size_t len = fread(buf, sizeof(unsigned char), buf_len, dev_data->input_file);
+ circbuffer_append(input, buf, len);
+
+ timeval end;
+ gettimeofday(&end, NULL);
+
+ int time_taken_ms = delta_sec(&start, &end) * 1000;
+ int sleep_time_ms = len * time_per_byte_ms - time_taken_ms;
+
+ if (sleep_time_ms > 0) {
+ SLEEP(sleep_time_ms);
+ }
+ } else {
+ SLEEP(10);
+ }
+ }
+
+ free(buf);
+ return 0;
}
-int file_set_centerfreq(input_t * const /*input*/, int const /*centerfreq*/) {
- return 0;
+int file_set_centerfreq(input_t* const /*input*/, int const /*centerfreq*/) {
+ return 0;
}
-int file_stop(input_t * const input) {
- assert(input != NULL);
- file_dev_data_t *dev_data = (file_dev_data_t *)input->dev_data;
- assert(dev_data != NULL);
- fclose(dev_data->input_file);
- dev_data->input_file = NULL;
- return 0;
+int file_stop(input_t* const input) {
+ assert(input != NULL);
+ file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
+ assert(dev_data != NULL);
+ fclose(dev_data->input_file);
+ dev_data->input_file = NULL;
+ return 0;
}
-MODULE_EXPORT input_t *file_input_new() {
- file_dev_data_t *dev_data = (file_dev_data_t *)XCALLOC(1, sizeof(file_dev_data_t));
- dev_data->input_file = NULL;
- dev_data->speedup_factor = 0.0;
-
- input_t *input = (input_t *)XCALLOC(1, sizeof(input_t));
- input->dev_data = dev_data;
- input->state = INPUT_UNKNOWN;
- input->sfmt = SFMT_U8;
- input->fullscale = (float)SCHAR_MAX - 0.5f;
- input->bytes_per_sample = sizeof(unsigned char);
- input->sample_rate = 0;
- input->parse_config = &file_parse_config;
- input->init = &file_init;
- input->run_rx_thread = &file_rx_thread;
- input->set_centerfreq = &file_set_centerfreq;
- input->stop = &file_stop;
-
- return input;
+MODULE_EXPORT input_t* file_input_new() {
+ file_dev_data_t* dev_data = (file_dev_data_t*)XCALLOC(1, sizeof(file_dev_data_t));
+ dev_data->input_file = NULL;
+ dev_data->speedup_factor = 0.0;
+
+ input_t* input = (input_t*)XCALLOC(1, sizeof(input_t));
+ input->dev_data = dev_data;
+ input->state = INPUT_UNKNOWN;
+ input->sfmt = SFMT_U8;
+ input->fullscale = (float)SCHAR_MAX - 0.5f;
+ input->bytes_per_sample = sizeof(unsigned char);
+ input->sample_rate = 0;
+ input->parse_config = &file_parse_config;
+ input->init = &file_init;
+ input->run_rx_thread = &file_rx_thread;
+ input->set_centerfreq = &file_set_centerfreq;
+ input->stop = &file_stop;
+
+ return input;
}
// vim: ts=4
diff --git a/src/input-file.h b/src/input-file.h
index c0ed8cc..3d0b033 100644
--- a/src/input-file.h
+++ b/src/input-file.h
@@ -18,14 +18,14 @@
* along with this program. If not, see .
*/
-#include
-#include
#include
+#include
+#include
typedef struct {
- char *filepath;
- FILE *input_file;
- float speedup_factor;
+ char* filepath;
+ FILE* input_file;
+ float speedup_factor;
} file_dev_data_t;
// vim: ts=4
diff --git a/src/input-helpers.cpp b/src/input-helpers.cpp
index 0b0757e..dda3eb7 100644
--- a/src/input-helpers.cpp
+++ b/src/input-helpers.cpp
@@ -18,11 +18,11 @@
* along with this program. If not, see .
*/
-#include // cerr
-#include // memcpy
-#include // pthread_mutex_lock, unlock
-#include "input-common.h" // input_t
-#include "rtl_airband.h" // debug_print
+#include // pthread_mutex_lock, unlock
+#include // memcpy
+#include // cerr
+#include "input-common.h" // input_t
+#include "rtl_airband.h" // debug_print
/* Write input data into circular buffer input->buffer.
* In general, input->buffer_size is not an exact multiple of len,
@@ -34,35 +34,32 @@
* so that the signal windowing function could handle the whole FFT batch
* without wrapping.
*/
-void circbuffer_append(input_t * const input, unsigned char *buf, size_t len) {
- if(len == 0) return;
- pthread_mutex_lock(&input->buffer_lock);
- size_t space_left = input->buf_size - input->bufe;
- if(space_left >= len) {
- memcpy(input->buffer + input->bufe, buf, len);
- if(input->bufe == 0) {
- memcpy(input->buffer + input->buf_size, input->buffer,
- std::min(len, 2 * input->bytes_per_sample * fft_size));
- debug_print("tail_len=%zu bytes\n",
- std::min(len, 2 * input->bytes_per_sample * fft_size));
- }
- } else {
- memcpy(input->buffer + input->bufe, buf, space_left);
- memcpy(input->buffer, buf + space_left, len - space_left);
- memcpy(input->buffer + input->buf_size, input->buffer,
- std::min(len - space_left, 2 * input->bytes_per_sample * fft_size));
- debug_print("buf wrap: space_left=%zu len=%zu bufe=%zu wrap_len=%zu tail_len=%zu\n",
- space_left, len, input->bufe, len - space_left,
- std::min(len - space_left, 2 * input->bytes_per_sample * fft_size));
- }
+void circbuffer_append(input_t* const input, unsigned char* buf, size_t len) {
+ if (len == 0)
+ return;
+ pthread_mutex_lock(&input->buffer_lock);
+ size_t space_left = input->buf_size - input->bufe;
+ if (space_left >= len) {
+ memcpy(input->buffer + input->bufe, buf, len);
+ if (input->bufe == 0) {
+ memcpy(input->buffer + input->buf_size, input->buffer, std::min(len, 2 * input->bytes_per_sample * fft_size));
+ debug_print("tail_len=%zu bytes\n", std::min(len, 2 * input->bytes_per_sample * fft_size));
+ }
+ } else {
+ memcpy(input->buffer + input->bufe, buf, space_left);
+ memcpy(input->buffer, buf + space_left, len - space_left);
+ memcpy(input->buffer + input->buf_size, input->buffer, std::min(len - space_left, 2 * input->bytes_per_sample * fft_size));
+ debug_print("buf wrap: space_left=%zu len=%zu bufe=%zu wrap_len=%zu tail_len=%zu\n", space_left, len, input->bufe, len - space_left,
+ std::min(len - space_left, 2 * input->bytes_per_sample * fft_size));
+ }
- size_t old_end = input->bufe;
- input->bufe = (input->bufe + len) % input->buf_size;
- if(old_end < input->bufs && input->bufe >= input->bufs) {
- std::cerr << "Warning: buffer overflow\n";
- input->overflow_count++;
- }
- pthread_mutex_unlock(&input->buffer_lock);
+ size_t old_end = input->bufe;
+ input->bufe = (input->bufe + len) % input->buf_size;
+ if (old_end < input->bufs && input->bufe >= input->bufs) {
+ std::cerr << "Warning: buffer overflow\n";
+ input->overflow_count++;
+ }
+ pthread_mutex_unlock(&input->buffer_lock);
}
// vim: ts=4
diff --git a/src/input-helpers.h b/src/input-helpers.h
index aa07769..966d734 100644
--- a/src/input-helpers.h
+++ b/src/input-helpers.h
@@ -18,7 +18,7 @@
* along with this program. If not, see .
*/
-#include "input-common.h" // input_t
+#include "input-common.h" // input_t
// input-helpers.cpp
-void circbuffer_append(input_t * const input, unsigned char *buf, size_t len);
+void circbuffer_append(input_t* const input, unsigned char* buf, size_t len);
diff --git a/src/input-mirisdr.cpp b/src/input-mirisdr.cpp
index e1c0a0e..99f171f 100644
--- a/src/input-mirisdr.cpp
+++ b/src/input-mirisdr.cpp
@@ -18,224 +18,222 @@
* along with this program. If not, see .
*/
+#include "input-mirisdr.h" // mirisdr_dev_data_t
#include
-#include
-#include // SCHAR_MAX
+#include // SCHAR_MAX
+#include
+#include // uint32_t
#include
-#include // uint32_t
#include
#include
-#include // FIXME: get rid of this
-#include // Setting
-#include
-#include "input-common.h" // input_t, sample_format_t, input_state_t, MODULE_EXPORT
-#include "input-helpers.h" // circbuffer_append
-#include "input-mirisdr.h" // mirisdr_dev_data_t
-#include "rtl_airband.h" // do_exit, fft_size, debug_print, XCALLOC, error()
+#include // FIXME: get rid of this
+#include
+#include // Setting
+#include "input-common.h" // input_t, sample_format_t, input_state_t, MODULE_EXPORT
+#include "input-helpers.h" // circbuffer_append
+#include "rtl_airband.h" // do_exit, fft_size, debug_print, XCALLOC, error()
using namespace std;
-static void mirisdr_callback(unsigned char *buf, uint32_t len, void *ctx) {
- if(do_exit) return;
- input_t *input = (input_t *)ctx;
- circbuffer_append(input, buf, (size_t)len);
+static void mirisdr_callback(unsigned char* buf, uint32_t len, void* ctx) {
+ if (do_exit)
+ return;
+ input_t* input = (input_t*)ctx;
+ circbuffer_append(input, buf, (size_t)len);
}
/* based on librtlsdr-keenerd, (c) Kyle Keen */
-static bool mirisdr_nearest_gain(mirisdr_dev_t *dev, int target_gain, int *nearest) {
- assert(nearest != NULL);
- int i, r, err1, err2, count;
- int *gains;
- r = mirisdr_set_tuner_gain_mode(dev, 1);
- if (r < 0) {
- return false;
- }
- count = mirisdr_get_tuner_gains(dev, NULL);
- if (count <= 0) {
- return false;
- }
- gains = (int *)XCALLOC(count, sizeof(int));
- count = mirisdr_get_tuner_gains(dev, gains);
- *nearest = gains[0];
- for (i = 0; i < count; i++) {
- err1 = abs(target_gain - *nearest);
- err2 = abs(target_gain - gains[i]);
- if (err2 < err1) {
- *nearest = gains[i];
- }
- }
- free(gains);
- return true;
+static bool mirisdr_nearest_gain(mirisdr_dev_t* dev, int target_gain, int* nearest) {
+ assert(nearest != NULL);
+ int i, r, err1, err2, count;
+ int* gains;
+ r = mirisdr_set_tuner_gain_mode(dev, 1);
+ if (r < 0) {
+ return false;
+ }
+ count = mirisdr_get_tuner_gains(dev, NULL);
+ if (count <= 0) {
+ return false;
+ }
+ gains = (int*)XCALLOC(count, sizeof(int));
+ count = mirisdr_get_tuner_gains(dev, gains);
+ *nearest = gains[0];
+ for (i = 0; i < count; i++) {
+ err1 = abs(target_gain - *nearest);
+ err2 = abs(target_gain - gains[i]);
+ if (err2 < err1) {
+ *nearest = gains[i];
+ }
+ }
+ free(gains);
+ return true;
}
-static int mirisdr_find_device_by_serial(char const * const s) {
- char vendor[256] = {0}, product[256] = {0}, serial[256] = {0};
- int count = mirisdr_get_device_count();
- if(count < 1) {
- return -1;
- }
- for(int i = 0; i < count; i++) {
- mirisdr_get_device_usb_strings(i, vendor, product, serial);
- if (strcmp(s, serial) != 0) {
- continue;
- }
- return i;
- }
- return -1;
+static int mirisdr_find_device_by_serial(char const* const s) {
+ char vendor[256] = {0}, product[256] = {0}, serial[256] = {0};
+ int count = mirisdr_get_device_count();
+ if (count < 1) {
+ return -1;
+ }
+ for (int i = 0; i < count; i++) {
+ mirisdr_get_device_usb_strings(i, vendor, product, serial);
+ if (strcmp(s, serial) != 0) {
+ continue;
+ }
+ return i;
+ }
+ return -1;
}
-int mirisdr_init(input_t * const input) {
- mirisdr_dev_data_t *dev_data = (mirisdr_dev_data_t *)input->dev_data;
- if(dev_data->serial != NULL) {
- dev_data->index = mirisdr_find_device_by_serial(dev_data->serial);
- if(dev_data->index < 0) {
- cerr<<"MiriSDR device with serial number "<serial<<" not found\n";
- error();
- }
- }
-
- dev_data->dev = NULL;
- mirisdr_open(&dev_data->dev, dev_data->index);
- if(NULL == dev_data->dev) {
- log(LOG_ERR, "Failed to open mirisdr device #%d.\n", dev_data->index);
- error();
- }
-
- char transfer_str[] = "BULK";
- char sample_format_str[] = "504_S8";
-
- mirisdr_dev_t *miri = dev_data->dev;
- int r = mirisdr_set_transfer(miri, transfer_str);
- if (r < 0) {
- log(LOG_ERR, "Failed to set bulk transfer mode for MiriSDR device #%d: error %d\n", dev_data->index, r);
- error();
- }
- r = mirisdr_set_sample_rate(miri, input->sample_rate);
- if (r < 0) {
- log(LOG_ERR, "Failed to set sample rate for device #%d. Error %d.\n", dev_data->index, r);
- }
-
- r = mirisdr_set_center_freq(miri, input->centerfreq - dev_data->correction);
- if(r < 0) {
- log(LOG_ERR, "Failed to set center freq for device #%d. Error %d.\n", dev_data->index, r);
- }
-
- int ngain = 0;
- if(mirisdr_nearest_gain(miri, dev_data->gain, &ngain) != true) {
- log(LOG_ERR, "Failed to read supported gain list for device #%d\n", dev_data->index);
- error();
- }
- r = mirisdr_set_tuner_gain_mode(miri, 1);
- r |= mirisdr_set_tuner_gain(miri, ngain);
- if (r < 0) {
- log(LOG_ERR, "Failed to set gain to %d for device #%d: error %d\n",
- ngain, dev_data->index, r);
- } else {
- log(LOG_INFO, "Device #%d: gain set to %d dB\n", dev_data->index,
- mirisdr_get_tuner_gain(miri));
- }
- r = mirisdr_set_sample_format(miri, sample_format_str);
- if (r < 0) {
- log(LOG_ERR, "Failed to set sample format for device #%d: error %d\n", dev_data->index, r);
- error();
- }
- mirisdr_reset_buffer(miri);
- log(LOG_INFO, "MiriSDR device %d initialized\n", dev_data->index);
- return 0;
+int mirisdr_init(input_t* const input) {
+ mirisdr_dev_data_t* dev_data = (mirisdr_dev_data_t*)input->dev_data;
+ if (dev_data->serial != NULL) {
+ dev_data->index = mirisdr_find_device_by_serial(dev_data->serial);
+ if (dev_data->index < 0) {
+ cerr << "MiriSDR device with serial number " << dev_data->serial << " not found\n";
+ error();
+ }
+ }
+
+ dev_data->dev = NULL;
+ mirisdr_open(&dev_data->dev, dev_data->index);
+ if (NULL == dev_data->dev) {
+ log(LOG_ERR, "Failed to open mirisdr device #%d.\n", dev_data->index);
+ error();
+ }
+
+ char transfer_str[] = "BULK";
+ char sample_format_str[] = "504_S8";
+
+ mirisdr_dev_t* miri = dev_data->dev;
+ int r = mirisdr_set_transfer(miri, transfer_str);
+ if (r < 0) {
+ log(LOG_ERR, "Failed to set bulk transfer mode for MiriSDR device #%d: error %d\n", dev_data->index, r);
+ error();
+ }
+ r = mirisdr_set_sample_rate(miri, input->sample_rate);
+ if (r < 0) {
+ log(LOG_ERR, "Failed to set sample rate for device #%d. Error %d.\n", dev_data->index, r);
+ }
+
+ r = mirisdr_set_center_freq(miri, input->centerfreq - dev_data->correction);
+ if (r < 0) {
+ log(LOG_ERR, "Failed to set center freq for device #%d. Error %d.\n", dev_data->index, r);
+ }
+
+ int ngain = 0;
+ if (mirisdr_nearest_gain(miri, dev_data->gain, &ngain) != true) {
+ log(LOG_ERR, "Failed to read supported gain list for device #%d\n", dev_data->index);
+ error();
+ }
+ r = mirisdr_set_tuner_gain_mode(miri, 1);
+ r |= mirisdr_set_tuner_gain(miri, ngain);
+ if (r < 0) {
+ log(LOG_ERR, "Failed to set gain to %d for device #%d: error %d\n", ngain, dev_data->index, r);
+ } else {
+ log(LOG_INFO, "Device #%d: gain set to %d dB\n", dev_data->index, mirisdr_get_tuner_gain(miri));
+ }
+ r = mirisdr_set_sample_format(miri, sample_format_str);
+ if (r < 0) {
+ log(LOG_ERR, "Failed to set sample format for device #%d: error %d\n", dev_data->index, r);
+ error();
+ }
+ mirisdr_reset_buffer(miri);
+ log(LOG_INFO, "MiriSDR device %d initialized\n", dev_data->index);
+ return 0;
}
-void *mirisdr_rx_thread(void *ctx) {
- input_t *input = (input_t *)ctx;
- mirisdr_dev_data_t *dev_data = (mirisdr_dev_data_t *)input->dev_data;
- assert(dev_data->dev != NULL);
-
- input->state = INPUT_RUNNING;
- if(mirisdr_read_async(dev_data->dev, mirisdr_callback, ctx, dev_data->bufcnt, MIRISDR_BUFSIZE) < 0) {
- log(LOG_ERR, "MiriSDR device #%d: async read failed, disabling\n", dev_data->index);
- input->state = INPUT_FAILED;
- }
- return 0;
+void* mirisdr_rx_thread(void* ctx) {
+ input_t* input = (input_t*)ctx;
+ mirisdr_dev_data_t* dev_data = (mirisdr_dev_data_t*)input->dev_data;
+ assert(dev_data->dev != NULL);
+
+ input->state = INPUT_RUNNING;
+ if (mirisdr_read_async(dev_data->dev, mirisdr_callback, ctx, dev_data->bufcnt, MIRISDR_BUFSIZE) < 0) {
+ log(LOG_ERR, "MiriSDR device #%d: async read failed, disabling\n", dev_data->index);
+ input->state = INPUT_FAILED;
+ }
+ return 0;
}
-int mirisdr_stop(input_t * const input) {
- mirisdr_dev_data_t *dev_data = (mirisdr_dev_data_t *)input->dev_data;
- assert(dev_data->dev != NULL);
+int mirisdr_stop(input_t* const input) {
+ mirisdr_dev_data_t* dev_data = (mirisdr_dev_data_t*)input->dev_data;
+ assert(dev_data->dev != NULL);
- if(mirisdr_cancel_async(dev_data->dev) < 0) {
- return -1;
- }
- return 0;
+ if (mirisdr_cancel_async(dev_data->dev) < 0) {
+ return -1;
+ }
+ return 0;
}
-int mirisdr_set_centerfreq(input_t * const input, int const centerfreq) {
- mirisdr_dev_data_t *dev_data = (mirisdr_dev_data_t *)input->dev_data;
- assert(dev_data->dev != NULL);
-
- int r = mirisdr_set_center_freq(dev_data->dev, centerfreq - dev_data->correction);
- if(r < 0) {
- log(LOG_ERR, "Failed to set centerfreq for MiriSDR device #%d: error %d\n",
- dev_data->index, r);
- return -1;
- }
- return 0;
+int mirisdr_set_centerfreq(input_t* const input, int const centerfreq) {
+ mirisdr_dev_data_t* dev_data = (mirisdr_dev_data_t*)input->dev_data;
+ assert(dev_data->dev != NULL);
+
+ int r = mirisdr_set_center_freq(dev_data->dev, centerfreq - dev_data->correction);
+ if (r < 0) {
+ log(LOG_ERR, "Failed to set centerfreq for MiriSDR device #%d: error %d\n", dev_data->index, r);
+ return -1;
+ }
+ return 0;
}
-int mirisdr_parse_config(input_t * const input, libconfig::Setting &cfg) {
- mirisdr_dev_data_t *dev_data = (mirisdr_dev_data_t *)input->dev_data;
- if(cfg.exists("serial")) {
- dev_data->serial = strdup(cfg["serial"]);
- } else if(cfg.exists("index")) {
- dev_data->index = (int)cfg["index"];
- } else {
- cerr<<"MiriSDR configuration error: no index and no serial number given\n";
- error();
- }
- if(cfg.exists("gain")) {
- dev_data->gain = (int)cfg["gain"];
- } else {
- cerr<<"MiriSDR configuration error: gain is not configured\n";
- error();
- }
- if(cfg.exists("correction")) {
- dev_data->correction = (int)cfg["correction"];
- }
- if(cfg.exists("num_buffers")) {
- dev_data->bufcnt = (int)(cfg["num_buffers"]);
- if(dev_data->bufcnt < 1) {
- cerr<<"MiriSDR configuration error: num_buffers must be greater than 0\n";
- error();
- }
- }
- return 0;
+int mirisdr_parse_config(input_t* const input, libconfig::Setting& cfg) {
+ mirisdr_dev_data_t* dev_data = (mirisdr_dev_data_t*)input->dev_data;
+ if (cfg.exists("serial")) {
+ dev_data->serial = strdup(cfg["serial"]);
+ } else if (cfg.exists("index")) {
+ dev_data->index = (int)cfg["index"];
+ } else {
+ cerr << "MiriSDR configuration error: no index and no serial number given\n";
+ error();
+ }
+ if (cfg.exists("gain")) {
+ dev_data->gain = (int)cfg["gain"];
+ } else {
+ cerr << "MiriSDR configuration error: gain is not configured\n";
+ error();
+ }
+ if (cfg.exists("correction")) {
+ dev_data->correction = (int)cfg["correction"];
+ }
+ if (cfg.exists("num_buffers")) {
+ dev_data->bufcnt = (int)(cfg["num_buffers"]);
+ if (dev_data->bufcnt < 1) {
+ cerr << "MiriSDR configuration error: num_buffers must be greater than 0\n";
+ error();
+ }
+ }
+ return 0;
}
-MODULE_EXPORT input_t *mirisdr_input_new() {
- mirisdr_dev_data_t *dev_data = (mirisdr_dev_data_t *)XCALLOC(1, sizeof(mirisdr_dev_data_t));
- dev_data->index = -1; // invalid default receiver index
- dev_data->gain = -1; // invalid default gain value
- dev_data->bufcnt = MIRISDR_DEFAULT_LIBUSB_BUFFER_COUNT;
-/* return &( input_t ){
- .dev_data = dev_data,
- .state = INPUT_UNKNOWN,
- .sfmt = SFMT_U8,
- .sample_rate = MIRISDR_DEFAULT_SAMPLE_RATE,
- .parse_config = &mirisdr_parse_config,
- .init = &mirisdr_init,
- .run_rx_thread = &mirisdr_rx_thread,
- .set_centerfreq = &mirisdr_set_centerfreq,
- .stop = &mirisdr_stop
- }; */
- input_t *input = (input_t *)XCALLOC(1, sizeof(input_t));
- input->dev_data = dev_data;
- input->state = INPUT_UNKNOWN;
- input->sfmt = SFMT_S8;
- input->fullscale = (float)SCHAR_MAX - 0.5f;
- input->bytes_per_sample = sizeof(char);
- input->sample_rate = MIRISDR_DEFAULT_SAMPLE_RATE;
- input->parse_config = &mirisdr_parse_config;
- input->init = &mirisdr_init;
- input->run_rx_thread = &mirisdr_rx_thread;
- input->set_centerfreq = &mirisdr_set_centerfreq;
- input->stop = &mirisdr_stop;
- return input;
+MODULE_EXPORT input_t* mirisdr_input_new() {
+ mirisdr_dev_data_t* dev_data = (mirisdr_dev_data_t*)XCALLOC(1, sizeof(mirisdr_dev_data_t));
+ dev_data->index = -1; // invalid default receiver index
+ dev_data->gain = -1; // invalid default gain value
+ dev_data->bufcnt = MIRISDR_DEFAULT_LIBUSB_BUFFER_COUNT;
+ /* return &( input_t ){
+ .dev_data = dev_data,
+ .state = INPUT_UNKNOWN,
+ .sfmt = SFMT_U8,
+ .sample_rate = MIRISDR_DEFAULT_SAMPLE_RATE,
+ .parse_config = &mirisdr_parse_config,
+ .init = &mirisdr_init,
+ .run_rx_thread = &mirisdr_rx_thread,
+ .set_centerfreq = &mirisdr_set_centerfreq,
+ .stop = &mirisdr_stop
+ }; */
+ input_t* input = (input_t*)XCALLOC(1, sizeof(input_t));
+ input->dev_data = dev_data;
+ input->state = INPUT_UNKNOWN;
+ input->sfmt = SFMT_S8;
+ input->fullscale = (float)SCHAR_MAX - 0.5f;
+ input->bytes_per_sample = sizeof(char);
+ input->sample_rate = MIRISDR_DEFAULT_SAMPLE_RATE;
+ input->parse_config = &mirisdr_parse_config;
+ input->init = &mirisdr_init;
+ input->run_rx_thread = &mirisdr_rx_thread;
+ input->set_centerfreq = &mirisdr_set_centerfreq;
+ input->stop = &mirisdr_stop;
+ return input;
}
diff --git a/src/input-mirisdr.h b/src/input-mirisdr.h
index b12feed..f6df057 100644
--- a/src/input-mirisdr.h
+++ b/src/input-mirisdr.h
@@ -17,16 +17,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#include // mirisdr_dev_t
+#include // mirisdr_dev_t
#define MIRISDR_BUFSIZE 320000
#define MIRISDR_DEFAULT_LIBUSB_BUFFER_COUNT 10
#define MIRISDR_DEFAULT_SAMPLE_RATE 2560000
typedef struct {
- mirisdr_dev_t *dev; // pointer to libmirisdr device struct
- char *serial; // dongle serial number
- int index; // dongle index
- int correction; // correction in Hertz (PPM correction is not supported by libmirisdr)
- int gain; // gain in dB
- int bufcnt; // libusb buffer count
+ mirisdr_dev_t* dev; // pointer to libmirisdr device struct
+ char* serial; // dongle serial number
+ int index; // dongle index
+ int correction; // correction in Hertz (PPM correction is not supported by libmirisdr)
+ int gain; // gain in dB
+ int bufcnt; // libusb buffer count
} mirisdr_dev_data_t;
diff --git a/src/input-rtlsdr.cpp b/src/input-rtlsdr.cpp
index 7a926b2..1752982 100644
--- a/src/input-rtlsdr.cpp
+++ b/src/input-rtlsdr.cpp
@@ -18,240 +18,237 @@
* along with this program. If not, see .
*/
+#include "input-rtlsdr.h" // rtlsdr_dev_data_t
#include
-#include
-#include // SCHAR_MAX
+#include