aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2017-07-03 17:20:35 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2017-07-03 17:20:35 +0200
commit4870d0c148eb49ffeb5482ae55d9cc4c9cc89786 (patch)
tree0009fbe559e7e38b1cb30b7095930cfce267a83c
parent777ef8b852aab3e95859b9042ddffb09818b28d2 (diff)
downloadetisnoop-4870d0c148eb49ffeb5482ae55d9cc4c9cc89786.tar.gz
etisnoop-4870d0c148eb49ffeb5482ae55d9cc4c9cc89786.tar.bz2
etisnoop-4870d0c148eb49ffeb5482ae55d9cc4c9cc89786.zip
Measure audio levels, add ensemble database WIP
-rw-r--r--Makefile.am1
-rw-r--r--src/dabplussnoop.cpp27
-rw-r--r--src/dabplussnoop.hpp27
-rw-r--r--src/ensembledatabase.hpp68
-rw-r--r--src/etianalyse.cpp48
-rw-r--r--src/etianalyse.hpp36
-rw-r--r--src/etisnoop.cpp28
-rw-r--r--src/faad_decoder.cpp98
-rw-r--r--src/faad_decoder.hpp19
-rw-r--r--src/figs.hpp4
-rw-r--r--src/utils.cpp8
-rw-r--r--src/utils.hpp3
12 files changed, 278 insertions, 89 deletions
diff --git a/Makefile.am b/Makefile.am
index 696575c..f66e7e5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,6 +16,7 @@ etisnoop_SOURCES = src/dabplussnoop.cpp src/dabplussnoop.hpp \
src/etianalyse.cpp src/etianalyse.hpp \
src/etisnoop.cpp \
src/faad_decoder.cpp src/faad_decoder.hpp \
+ src/ensembledatabase.hpp \
src/fig0_0.cpp \
src/fig0_10.cpp \
src/fig0_11.cpp \
diff --git a/src/dabplussnoop.cpp b/src/dabplussnoop.cpp
index f0badaf..228016d 100644
--- a/src/dabplussnoop.cpp
+++ b/src/dabplussnoop.cpp
@@ -63,6 +63,11 @@ void DabPlusSnoop::push(uint8_t* streamdata, size_t streamsize)
}
}
+audio_statistics_t DabPlusSnoop::get_audio_statistics(void) const
+{
+ return m_faad_decoder.get_audio_statistics();
+}
+
// Idea and some code taken from Xpadxpert
bool DabPlusSnoop::seek_valid_firecode()
{
@@ -305,9 +310,14 @@ bool DabPlusSnoop::analyse_au(vector<vector<uint8_t> >& aus)
return m_faad_decoder.decode(aus);
}
-DabPlusSnoop::~DabPlusSnoop()
+StreamSnoop::StreamSnoop(StreamSnoop&& other)
{
- m_faad_decoder.close();
+ dps = move(other.dps);
+ m_index = other.m_index;
+ m_raw_data_stream_fd = other.m_raw_data_stream_fd;
+ other.m_raw_data_stream_fd = nullptr;
+ m_dump_to_file = other.m_dump_to_file;
+ other.m_dump_to_file = false;
}
StreamSnoop::~StreamSnoop()
@@ -324,18 +334,25 @@ void StreamSnoop::push(uint8_t* streamdata, size_t streamsize)
}
// First dump to subchannel file (superframe+parity word)
- if (m_raw_data_stream_fd == NULL) {
+ if (m_dump_to_file and m_raw_data_stream_fd == nullptr) {
stringstream dump_filename;
dump_filename << "stream-" << m_index << ".msc";
m_raw_data_stream_fd = fopen(dump_filename.str().c_str(), "w");
- if (m_raw_data_stream_fd == NULL) {
+ if (m_raw_data_stream_fd == nullptr) {
perror("File open failed");
}
}
- fwrite(streamdata, streamsize, 1, m_raw_data_stream_fd);
+ if (m_raw_data_stream_fd) {
+ fwrite(streamdata, streamsize, 1, m_raw_data_stream_fd);
+ }
dps.push(streamdata, streamsize);
}
+
+audio_statistics_t StreamSnoop::get_audio_statistics(void) const
+{
+ return dps.get_audio_statistics();
+}
diff --git a/src/dabplussnoop.hpp b/src/dabplussnoop.hpp
index 8ba35d5..6322c9b 100644
--- a/src/dabplussnoop.hpp
+++ b/src/dabplussnoop.hpp
@@ -78,21 +78,12 @@ he_aac_super_frame(subchannel_index)
#include <vector>
#include "faad_decoder.hpp"
-#ifndef __DABPLUSSNOOP_H_
-#define __DABPLUSSNOOP_H_
+#pragma once
// DabPlusSnoop is responsible for decoding DAB+ audio
class DabPlusSnoop
{
public:
- DabPlusSnoop() :
- m_index(0),
- m_subchannel_index(0),
- m_data(0) {}
- ~DabPlusSnoop();
- DabPlusSnoop(const DabPlusSnoop& other) = delete;
- DabPlusSnoop& operator=(const DabPlusSnoop& other) = delete;
-
void set_subchannel_index(unsigned subchannel_index)
{
m_subchannel_index = subchannel_index;
@@ -105,6 +96,8 @@ class DabPlusSnoop
void push(uint8_t* streamdata, size_t streamsize);
+ audio_statistics_t get_audio_statistics(void) const;
+
private:
/* Data needed for FAAD */
FaadDecoder m_faad_decoder;
@@ -132,11 +125,14 @@ class DabPlusSnoop
class StreamSnoop
{
public:
- StreamSnoop() :
+ StreamSnoop(bool dump_to_file) :
dps(),
m_index(-1),
- m_raw_data_stream_fd(NULL) {}
+ m_raw_data_stream_fd(nullptr),
+ m_dump_to_file(dump_to_file) {}
~StreamSnoop();
+ StreamSnoop(StreamSnoop&& other);
+
StreamSnoop(const StreamSnoop& other) = delete;
StreamSnoop& operator=(const StreamSnoop& other) = delete;
@@ -153,13 +149,12 @@ class StreamSnoop
void push(uint8_t* streamdata, size_t streamsize);
+ audio_statistics_t get_audio_statistics(void) const;
+
private:
DabPlusSnoop dps;
int m_index;
-
FILE* m_raw_data_stream_fd;
+ bool m_dump_to_file;
};
-
-#endif
-
diff --git a/src/ensembledatabase.hpp b/src/ensembledatabase.hpp
new file mode 100644
index 0000000..ebe7a66
--- /dev/null
+++ b/src/ensembledatabase.hpp
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/)
+ Copyright (C) 2017 Matthias P. Braendli (http://www.opendigitalradio.org)
+ Copyright (C) 2015 Data Path
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Ensemble Database, gather data from the ensemble for the
+ statistics.
+
+ Authors:
+ Sergio Sagliocco <sergio.sagliocco@csp.it>
+ Matthias P. Braendli <matthias@mpb.li>
+ / | |- ')|) |-|_ _ (|,_ .| _ ,_ \
+ Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/
+
+*/
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string>
+#include <list>
+
+struct service_t {
+ uint32_t id;
+ std::string label;
+ std::string shortlabel;
+ // TODO PTy language announcement
+};
+
+struct subchannel_t {
+ uint8_t address;
+ uint8_t num_cu;
+ // TODO type bitrate protection
+};
+
+struct component_t {
+ uint32_t service_id;
+ uint8_t subchId;
+ /* TODO
+ uint8_t type;
+ uint8_t SCIdS;
+
+ uaType for audio
+ */
+};
+
+struct ensemble_t {
+ std::list<service_t> services;
+ std::list<subchannel_t> subchannels;
+ std::list<component_t> components;
+};
+
diff --git a/src/etianalyse.cpp b/src/etianalyse.cpp
index 9687755..9799fb5 100644
--- a/src/etianalyse.cpp
+++ b/src/etianalyse.cpp
@@ -16,9 +16,6 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
- etisnoop.cpp
- Parse ETI NI G.703 file
-
Authors:
Sergio Sagliocco <sergio.sagliocco@csp.it>
Matthias P. Braendli <matthias@mpb.li>
@@ -47,13 +44,6 @@ using namespace std;
// Signal handler flag
std::atomic<bool> quit(false);
-struct FIG0_13_shortAppInfo
-{
- uint16_t SId;
- uint8_t No:4;
- uint8_t SCIdS:4;
-} PACKED;
-
bool
eti_analyse_config_t::is_fig_to_be_printed(int type, int extension) const
{
@@ -94,7 +84,7 @@ static void print_fig_result(const fig_result_t& fig_result, const display_setti
}
-int eti_analyse(eti_analyse_config_t &config)
+void ETI_Analyser::eti_analyse()
{
uint8_t p[ETINIPACKETSIZE];
string desc;
@@ -129,8 +119,6 @@ int eti_analyse(eti_analyse_config_t &config)
printf("?\n");
}
- WatermarkDecoder wm_decoder;
-
if (config.analyse_fig_rates) {
rate_display_header(config.analyse_fig_rates_per_second);
}
@@ -346,9 +334,15 @@ int eti_analyse(eti_analyse_config_t &config)
sprintf(sdesc, "%d => %d kbit/s", stl[i], stl[i]*8/3);
printbuf("STL - Sub-channel Stream Length", 3, NULL, 0, sdesc);
+ if (config.statistics and config.streams_to_decode.count(i) == 0) {
+ config.streams_to_decode.emplace(std::piecewise_construct,
+ std::make_tuple(i),
+ std::make_tuple(false)); // do not dump to file
+ }
+
if (config.streams_to_decode.count(i) > 0) {
- config.streams_to_decode[i].set_subchannel_index(stl[i]/3);
- config.streams_to_decode[i].set_index(i);
+ config.streams_to_decode.at(i).set_subchannel_index(stl[i]/3);
+ config.streams_to_decode.at(i).set_index(i);
}
}
@@ -409,7 +403,7 @@ int eti_analyse(eti_analyse_config_t &config)
figlen = fig[0] & 0x1F;
sprintf(sdesc, "FIG %d [%d bytes]", figtype, figlen);
printbuf(sdesc, 3, fig+1, figlen);
- decodeFIG(config, figs, wm_decoder, fig+1, figlen, figtype, 4);
+ decodeFIG(config, figs, fig+1, figlen, figtype, 4);
fig += figlen + 1;
figcount += figlen + 1;
if (figcount >= 29)
@@ -458,7 +452,7 @@ int eti_analyse(eti_analyse_config_t &config)
}
if (config.streams_to_decode.count(i) > 0) {
- config.streams_to_decode[i].push(streamdata, stl[i]*8);
+ config.streams_to_decode.at(i).push(streamdata, stl[i]*8);
}
}
@@ -502,6 +496,18 @@ int eti_analyse(eti_analyse_config_t &config)
rate_display_analysis(false, config.analyse_fig_rates_per_second);
}
+ if (config.statistics) {
+ for (const auto& snoop : config.streams_to_decode) {
+ printf("Statistics for %d:\n", snoop.first);
+
+ const auto& stat = snoop.second.get_audio_statistics();
+ printf(" Avg L: %d dB\n", absolute_to_dB(stat.average_level_left));
+ printf(" Avg R: %d dB\n", absolute_to_dB(stat.average_level_right));
+ printf(" Peak L: %d dB\n", absolute_to_dB(stat.peak_level_left));
+ printf(" Peak R: %d dB\n", absolute_to_dB(stat.peak_level_right));
+ }
+ }
+
if (quit.load()) running = false;
}
@@ -515,15 +521,11 @@ int eti_analyse(eti_analyse_config_t &config)
}
figs_cleardb();
-
-
- return 0;
}
-void decodeFIG(
+void ETI_Analyser::decodeFIG(
const eti_analyse_config_t &config,
FIGalyser &figs,
- WatermarkDecoder &wm_decoder,
uint8_t* f,
uint8_t figlen,
uint16_t figtype,
@@ -534,7 +536,7 @@ void decodeFIG(
switch (figtype) {
case 0:
{
- fig0_common_t fig0(f, figlen, wm_decoder);
+ fig0_common_t fig0(f, figlen, ensemble, wm_decoder);
const display_settings_t disp(config.is_fig_to_be_printed(figtype, fig0.ext()), indent);
diff --git a/src/etianalyse.hpp b/src/etianalyse.hpp
index 40a19fe..7d02071 100644
--- a/src/etianalyse.hpp
+++ b/src/etianalyse.hpp
@@ -16,9 +16,6 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
- etisnoop.cpp
- Parse ETI NI G.703 file
-
Authors:
Sergio Sagliocco <sergio.sagliocco@csp.it>
Matthias P. Braendli <matthias@mpb.li>
@@ -43,6 +40,7 @@
#include "watermarkdecoder.hpp"
#include "repetitionrate.hpp"
#include "figalyser.hpp"
+#include "ensembledatabase.hpp"
extern std::atomic<bool> quit;
@@ -54,7 +52,8 @@ struct eti_analyse_config_t {
analyse_fic_carousel(false),
analyse_fig_rates(false),
analyse_fig_rates_per_second(false),
- decode_watermark(false) {}
+ decode_watermark(false),
+ statistics(false) {}
FILE* etifd;
bool ignore_error;
@@ -64,19 +63,30 @@ struct eti_analyse_config_t {
bool analyse_fig_rates;
bool analyse_fig_rates_per_second;
bool decode_watermark;
+ bool statistics;
bool is_fig_to_be_printed(int type, int extension) const;
};
+class ETI_Analyser {
+ public:
+ ETI_Analyser(eti_analyse_config_t &config) :
+ config(config) {}
+
+ void eti_analyse(void);
-int eti_analyse(eti_analyse_config_t &config);
+ private:
+ void decodeFIG(
+ const eti_analyse_config_t &config,
+ FIGalyser &figs,
+ uint8_t* f,
+ uint8_t figlen,
+ uint16_t figtype,
+ int indent);
-void decodeFIG(
- const eti_analyse_config_t &config,
- FIGalyser &figs,
- WatermarkDecoder &wm_decoder,
- uint8_t* f,
- uint8_t figlen,
- uint16_t figtype,
- int indent);
+ eti_analyse_config_t &config;
+
+ ensemble_t ensemble;
+ WatermarkDecoder wm_decoder;
+};
diff --git a/src/etisnoop.cpp b/src/etisnoop.cpp
index 194dfc0..4c0645f 100644
--- a/src/etisnoop.cpp
+++ b/src/etisnoop.cpp
@@ -57,18 +57,6 @@
using namespace std;
-// Function prototypes
-void decodeFIG(
- const eti_analyse_config_t &config,
- FIGalyser &figs,
- WatermarkDecoder &wm_decoder,
- uint8_t* figdata,
- uint8_t figlen,
- uint16_t figtype,
- int indent);
-
-int eti_analyse(eti_analyse_config_t &config);
-
const char *get_programme_type_str(size_t int_table_Id, size_t pty);
int sprintfMJD(char *dst, int mjd);
@@ -85,6 +73,7 @@ const struct option longopts[] = {
{"help", no_argument, 0, 'h'},
{"ignore-error", no_argument, 0, 'e'},
{"input-file", no_argument, 0, 'i'},
+ {"statistics", no_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{"decode-stream", required_argument, 0, 'd'},
{"filter-fig", required_argument, 0, 'F'},
@@ -100,10 +89,11 @@ void usage(void)
"\n"
" http://www.opendigitalradio.org\n"
"\n"
- "Usage: etisnoop [-v] [-f] [-w] [-i filename] [-d stream_index]\n"
+ "Usage: etisnoop [options] [-i filename]\n"
"\n"
" -v increase verbosity (can be given more than once)\n"
" -d N decode subchannel N into .msc file and if DAB+, decode to .wav file\n"
+ " -s statistic mode: decode all subchannels and measure audio level\n"
" -f analyse FIC carousel\n"
" -r analyse FIG rates in FIGs per second\n"
" -R analyse FIG rates in frames per FIG\n"
@@ -135,12 +125,14 @@ int main(int argc, char *argv[])
eti_analyse_config_t config;
while(ch != -1) {
- ch = getopt_long(argc, argv, "d:efF:hrRvwi:", longopts, &index);
+ ch = getopt_long(argc, argv, "d:efF:hrRsvwi:", longopts, &index);
switch (ch) {
case 'd':
{
int subchix = atoi(optarg);
- config.streams_to_decode.emplace(std::piecewise_construct, std::make_tuple(subchix), std::make_tuple());
+ config.streams_to_decode.emplace(std::piecewise_construct,
+ std::make_tuple(subchix),
+ std::make_tuple(true)); // dump to file
}
break;
case 'e':
@@ -180,6 +172,9 @@ int main(int argc, char *argv[])
config.analyse_fig_rates = true;
config.analyse_fig_rates_per_second = false;
break;
+ case 's':
+ config.statistics = true;
+ break;
case 'v':
set_verbosity(get_verbosity() + 1);
break;
@@ -208,7 +203,8 @@ int main(int argc, char *argv[])
}
config.etifd = etifd;
- eti_analyse(config);
+ ETI_Analyser eti_analyser(config);
+ eti_analyser.eti_analyse();
fclose(etifd);
}
diff --git a/src/faad_decoder.cpp b/src/faad_decoder.cpp
index 24c951a..ced9921 100644
--- a/src/faad_decoder.cpp
+++ b/src/faad_decoder.cpp
@@ -43,6 +43,34 @@ FaadDecoder::FaadDecoder() :
{
}
+FaadDecoder& FaadDecoder::operator=(FaadDecoder&& other)
+{
+ m_data_len = other.m_data_len;
+ m_fd = other.m_fd;
+ other.m_fd = nullptr;
+ m_initialised = other.m_initialised;
+ other.m_initialised = false;
+
+ return *this;
+}
+
+FaadDecoder::FaadDecoder(FaadDecoder&& other)
+{
+ m_data_len = other.m_data_len;
+ m_fd = other.m_fd;
+ other.m_fd = nullptr;
+ m_initialised = other.m_initialised;
+ other.m_initialised = false;
+}
+
+FaadDecoder::~FaadDecoder()
+{
+ if (m_fd) {
+ wavfile_close(m_fd);
+ }
+}
+
+
void FaadDecoder::open(string filename, bool ps_flag, bool aac_channel_mode,
bool dac_rate, bool sbr_flag, int mpeg_surround_config)
{
@@ -125,27 +153,71 @@ bool FaadDecoder::decode(vector<vector<uint8_t> > aus)
return false;
}
- if (m_fd == nullptr) {
+ if (m_fd == nullptr and not m_filename.empty()) {
stringstream ss;
ss << m_filename << ".wav";
m_fd = wavfile_open(ss.str().c_str(), m_sample_rate);
}
if (samples) {
+ if (m_channels != 1 and m_channels != 2) {
+ printf("Cannot handle %d channels\n", m_channels);
+ }
+
+ m_stats.peak_level_left = 0;
+ m_stats.peak_level_right = 0;
+
if (m_channels == 1) {
- int16_t *buffer = (int16_t *)alloca (2 * samples);
- size_t i;
- for (i = 0; i < samples; i ++) {
- buffer [2 * i] = ((int16_t *)outBuffer) [i];
- buffer [2 * i + 1] = buffer [2 * i];
+ int32_t sum_ampl = 0;
+ for (size_t i = 0; i < samples; i ++) {
+ const int16_t ampl = abs(outBuffer[i]);
+ if (m_stats.peak_level_left < ampl) {
+ m_stats.peak_level_left = ampl;
+ }
+
+ sum_ampl += ampl;
}
- wavfile_write(m_fd, buffer, 2*samples);
+ m_stats.average_level_left = sum_ampl / samples;
}
else if (m_channels == 2) {
- wavfile_write(m_fd, outBuffer, samples);
+ assert((samples % 2) == 0);
+
+ int32_t sum_left = 0;
+ int32_t sum_right = 0;
+
+ for (size_t i = 0; i < samples; i += 2) {
+ const int16_t ampl_left = abs(outBuffer[i]);
+ if (m_stats.peak_level_left < ampl_left) {
+ m_stats.peak_level_left = ampl_left;
+ }
+
+ sum_left += ampl_left;
+
+ const int16_t ampl_right = abs(outBuffer[i+1]);
+ if (m_stats.peak_level_right < ampl_right) {
+ m_stats.peak_level_right = ampl_right;
+ }
+
+ sum_right += ampl_right;
+ }
+
+ m_stats.average_level_left = sum_left / samples;
+ m_stats.average_level_right = sum_right / samples;
}
- else {
- printf("Cannot handle %d channels\n", m_channels);
+
+ if (m_fd) {
+ if (m_channels == 1) {
+ vector<int16_t> buffer(2*samples);
+ for (size_t i = 0; i < samples; i ++) {
+ buffer [2 * i] = ((int16_t *)outBuffer) [i];
+ buffer [2 * i + 1] = buffer [2 * i];
+ }
+
+ wavfile_write(m_fd, buffer.data(), 2*samples);
+ }
+ else if (m_channels == 2) {
+ wavfile_write(m_fd, outBuffer, samples);
+ }
}
}
@@ -153,11 +225,9 @@ bool FaadDecoder::decode(vector<vector<uint8_t> > aus)
return true;
}
-void FaadDecoder::close()
+audio_statistics_t FaadDecoder::get_audio_statistics(void) const
{
- if (m_fd) {
- wavfile_close(m_fd);
- }
+ return m_stats;
}
int FaadDecoder::get_aac_channel_configuration()
diff --git a/src/faad_decoder.hpp b/src/faad_decoder.hpp
index 470f905..e154619 100644
--- a/src/faad_decoder.hpp
+++ b/src/faad_decoder.hpp
@@ -61,24 +61,39 @@ class FaadHandle
NeAACDecHandle decoder;
};
+struct audio_statistics_t {
+ int16_t average_level_left;
+ int16_t average_level_right;
+
+ int16_t peak_level_left;
+ int16_t peak_level_right;
+};
+
class FaadDecoder
{
public:
FaadDecoder();
+ ~FaadDecoder();
+ FaadDecoder& operator=(FaadDecoder&& other);
+ FaadDecoder(FaadDecoder&& other);
+ FaadDecoder(const FaadDecoder&) = delete;
+ FaadDecoder& operator=(const FaadDecoder&) = delete;
void open(std::string filename, bool ps_flag, bool aac_channel_mode,
bool dac_rate, bool sbr_flag, int mpeg_surround_config);
- void close(void);
-
bool decode(std::vector<std::vector<uint8_t> > aus);
bool is_initialised(void) { return m_initialised; }
+ audio_statistics_t get_audio_statistics(void) const;
+
private:
int get_aac_channel_configuration();
size_t m_data_len;
+ audio_statistics_t m_stats;
+
std::string m_filename;
FILE* m_fd;
diff --git a/src/figs.hpp b/src/figs.hpp
index 403970d..8decea3 100644
--- a/src/figs.hpp
+++ b/src/figs.hpp
@@ -33,6 +33,7 @@
#include "utils.hpp"
#include "tables.hpp"
#include "watermarkdecoder.hpp"
+#include "ensembledatabase.hpp"
struct fig_result_t {
struct msg_info_t {
@@ -58,13 +59,16 @@ struct fig0_common_t {
fig0_common_t(
uint8_t* fig_data,
uint16_t fig_len,
+ ensemble_t &ens,
WatermarkDecoder &wm_dec) :
f(fig_data),
figlen(fig_len),
+ ensemble(ens),
wm_decoder(wm_dec) { }
uint8_t* f;
uint16_t figlen;
+ ensemble_t& ensemble;
WatermarkDecoder &wm_decoder;
uint16_t cn(void) { return (f[0] & 0x80) >> 7; }
diff --git a/src/utils.cpp b/src/utils.cpp
index 3e741c9..d0c1273 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -29,6 +29,8 @@
#include "utils.hpp"
#include <cstring>
+#include <cmath>
+#include <limits>
#include <stdarg.h>
using namespace std;
@@ -229,3 +231,9 @@ std::string pnum_to_str(uint16_t Programme_Number)
}
}
+int absolute_to_dB(int16_t value)
+{
+ const int16_t int16_max = std::numeric_limits<int16_t>::max();
+ return value ? round(20*log10((double)value / int16_max)) : -90;
+}
+
diff --git a/src/utils.hpp b/src/utils.hpp
index 890bd50..3ff9f13 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -82,3 +82,6 @@ int sprintfMJD(char *dst, int mjd);
// as the RDS "Programme Item Number (PIN)" feature (EN 62106).
std::string pnum_to_str(uint16_t Programme_Number);
+// Convert and absolute 16-bit int value to dB. If value is zero, returns
+// -90dB
+int absolute_to_dB(int16_t value);