~mrp/aerodramus

44b33c4f79b5acc5a4086c5fd7885d20bfc9aae8 — Mark Penner 2 months ago 49ad2d2
add function for calculating rms over time (sliding window)
3 files changed, 43 insertions(+), 15 deletions(-)

M autotests/lib/aerodramus.test.cpp
M src/lib/aerodramus.cpp
M src/lib/aerodramus.h
M autotests/lib/aerodramus.test.cpp => autotests/lib/aerodramus.test.cpp +18 -4
@@ 1,4 1,4 @@
// SPDX-FileCopyrightText: 2023 Mark Penner <mrpenner@mailbox.org>
// SPDX-FileCopyrightText: 2024 Mark Penner <mrpenner@mailbox.org>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "../../src/lib/aerodramus.h"


@@ 16,6 16,7 @@ private:
private Q_SLOTS:
    void test_generate_signal();
    void test_rms();
    void test_rms_sliding_window();
    void test_SoundFile();
    void test_dB_from_point_source();
    void test_watts_needed_to_point_source();


@@ 32,10 33,23 @@ void TestLibAerodramus::test_generate_signal()

void TestLibAerodramus::test_rms()
{
    QCOMPARE(Aerodramus::rms({1.0, -1.0, 1.0}), 1);
    QCOMPARE(Aerodramus::rms({-123456789.987, 987654321.123, -456321789.321}), 632174082.679749);
    std::vector<double> data{1.0, -1.0, 1.0};
    QCOMPARE(Aerodramus::rms(data), 1);
    data = {-123456789.987, 987654321.123, -456321789.321};
    QCOMPARE(Aerodramus::rms(data), 632174082.679749);
    data = Aerodramus::generate_signal(200, 20000, 20, 44100);
    double expected{std::round(100.0 / std::numbers::sqrt2)}; // factor of 100 and round because the extra precision isn't relevant for testing
    QCOMPARE(std::round(Aerodramus::rms(Aerodramus::generate_signal(200, 20000, 20, 44100)) * 100), expected);
    QCOMPARE(std::round(Aerodramus::rms(data) * 100), expected);
}

void TestLibAerodramus::test_rms_sliding_window()
{
    std::vector<double> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    auto result = Aerodramus::rms_sliding_window(data, 8);
    double expected{5.049752469181};
    QCOMPARE(result.at(0), expected);
    expected = 5.958187643906;
    QCOMPARE(result.at(1), expected);
}

void TestLibAerodramus::test_SoundFile()

M src/lib/aerodramus.cpp => src/lib/aerodramus.cpp +1 -9
@@ 1,4 1,4 @@
// SPDX-FileCopyrightText: 2023 Mark Penner <mrpenner@mailbox.org>
// SPDX-FileCopyrightText: 2024 Mark Penner <mrpenner@mailbox.org>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "aerodramus.h"


@@ 65,14 65,6 @@ std::vector<double> Aerodramus::generate_signal(double start_freq, double end_fr
    return data;
}

double Aerodramus::rms(std::vector<double> const &data)
{
    double summed_squares = std::transform_reduce(data.cbegin(), data.cend(), 0.0L, std::plus{}, [](auto x) {
        return x * x;
    });
    return std::sqrt(summed_squares / data.size());
}

double Aerodramus::dB_from_point_source(double sensitivity, double distance, double watts)
{
    double dB_loss{20 * std::log10(1 / distance)};

M src/lib/aerodramus.h => src/lib/aerodramus.h +24 -2
@@ 1,9 1,12 @@
// SPDX-FileCopyrightText: 2023 Mark Penner <mrpenner@mailbox.org>
// SPDX-FileCopyrightText: 2024 Mark Penner <mrpenner@mailbox.org>
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef AERODRAMUS_H
#define AERODRAMUS_H

#include <cmath>
#include <numeric>
#include <ranges>
#include <string>
#include <vector>



@@ 23,9 26,28 @@ SoundFile read(std::string const &path);
bool write(SoundFile const &soundfile);

std::vector<double> generate_signal(double start_freq, double end_freq, double seconds, long sample_rate);
double rms(std::vector<double> const &data);
double dB_from_point_source(double sensitivity, double distance, double watts);
double watts_needed_to_point_source(double sensitivity, double distance, double dB);
std::vector<int> zero_cross_histogram(const std::vector<float> &samples, int bins);

template<std::ranges::sized_range R>
double rms(R data)
{
    double summed_squares = std::transform_reduce(data.cbegin(), data.cend(), 0.0L, std::plus{}, [](auto x) {
        return x * x;
    });
    return std::sqrt(summed_squares / data.size());
}

template<std::ranges::sized_range R>
std::vector<double> rms_sliding_window(R data, uint window_size)
{
    std::vector<double> result{};
    for (auto window : data | std::views::slide(window_size)) {
        result.push_back(Aerodramus::rms(window));
    }
    return result;
}

}; // namespace Aerodramus
#endif