Skip to content

Commit

Permalink
Added fm decoding to mp3.
Browse files Browse the repository at this point in the history
  • Loading branch information
shajen authored and Borys Dyczko committed Aug 27, 2021
1 parent 64dca1b commit 8d67b78
Show file tree
Hide file tree
Showing 17 changed files with 352 additions and 110 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 1,4 @@
CMakeLists.txt.user
build
*.wav
todo.txt
9 changes: 5 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 23,9 @@ TARGET_LINK_LIBRARIES(auto-sdr
spdlog::spdlog
rtlsdr
Threads::Threads
fftw3
fftw3_threads
fftw3f
fftw3f_threads
liquid
sox
soxr
)

install(TARGETS auto-sdr DESTINATION)
13 changes: 13 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 1,13 @@
#!/bin/bash

TYPE=${1:-'Release'}
echo "build type: $TYPE"

rm -rf build
mkdir -p build
pushd build

cmake .. -DCMAKE_BUILD_TYPE=$TYPE
make -j4

popd
38 changes: 0 additions & 38 deletions sources/algorithms/fft.cpp

This file was deleted.

28 changes: 0 additions & 28 deletions sources/algorithms/fft.h

This file was deleted.

18 changes: 18 additions & 0 deletions sources/algorithms/spectrogram.cpp
Original file line number Diff line number Diff line change
@@ -0,0 1,18 @@
#include "spectrogram.h"

Spectrogram::Spectrogram(uint32_t size) : m_size(size), m_buffer(size), m_signals(size), m_spectrogram(spgramcf_create_default(size)) {}

Spectrogram::~Spectrogram() { spgramcf_destroy(m_spectrogram); }

const std::vector<Signal>& Spectrogram::psd(uint32_t centerFrequency, uint32_t bandwidth, std::vector<std::complex<float>>& buffer, uint32_t size) {
spgramcf_reset(m_spectrogram);
spgramcf_write(m_spectrogram, toLiquidComplext(buffer.data()), size);
spgramcf_get_psd(m_spectrogram, m_buffer.data());

for (int i = 0; i < m_size; i) {
m_signals[i].power = m_buffer[i];
m_signals[i].frequency.frequency = (centerFrequency - bandwidth / 2) static_cast<uint64_t>(i) * bandwidth / m_size;
}

return m_signals;
}
21 changes: 21 additions & 0 deletions sources/algorithms/spectrogram.h
Original file line number Diff line number Diff line change
@@ -0,0 1,21 @@
#pragma once

#include <config.h>
#include <liquid/liquid.h>
#include <utils.h>

#include <complex>
#include <vector>

class Spectrogram {
public:
Spectrogram(uint32_t size);
virtual ~Spectrogram();
const std::vector<Signal>& psd(uint32_t centerFrequency, uint32_t bandwidth, std::vector<std::complex<float>>& buffer, uint32_t size);

private:
const uint32_t m_size;
std::vector<float> m_buffer;
std::vector<Signal> m_signals;
spgramcf m_spectrogram;
};
10 changes: 6 additions & 4 deletions sources/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 7,14 @@

constexpr auto LOG_LEVEL = spdlog::level::info;
constexpr auto RANGE_SCANNING_TIME = std::chrono::milliseconds(100);
constexpr auto FFT_THREADS = 8;
constexpr auto NOISE_LEVEL = -7.0;
constexpr auto MAX_SILENCE_TIME = std::chrono::milliseconds(1000);
constexpr auto MIN_RECORDING_TIME = std::chrono::milliseconds(1000);
constexpr auto NOISE_LEVEL = -7.0;
constexpr auto MP3_SAMPLE_RATE = 48000;
const auto MP3_OUTPUT_DIRECTORY = std::string(getenv("HOME")) "/sdr_recordings/";

constexpr auto RTL_SDR_GAIN = std::optional<int>(328);
constexpr auto RTL_SDR_MAX_BUFFER_SIZE = 40960000;
constexpr auto RTL_SDR_PPM = 0;
constexpr auto RTL_SDR_GAIN = std::optional<int>(496);
constexpr auto RTL_SDR_MAX_BANDWIDTH = 2800000;

struct ConfigFrequencyRange {
Expand Down
4 changes: 1 addition & 3 deletions sources/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 1,4 @@
#include <algorithms/fft.h>
#include <algorithms/spectrogram.h>
#include <config.h>
#include <scanners/rtl_sdr_scanner.h>
#include <signal.h>
Expand All @@ -11,8 11,6 @@ void handler(int) { isRunning = false; }
int main() {
spdlog::set_level(LOG_LEVEL);
spdlog::info("start auto SDR");
FFTMultiThreadInitializer fftMultiThreadInitializer = FFTMultiThreadInitializer();
std::ignore = fftMultiThreadInitializer;

std::vector<std::unique_ptr<RtlSdrScanner>> scanners;
for (int i = 0; i < RtlSdrScanner::devicesCount(); i) {
Expand Down
71 changes: 71 additions & 0 deletions sources/mp3_writer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 1,71 @@
#include "mp3_writer.h"

#include <config.h>

#include <filesystem>

std::string getPath(const Frequency& frequency) {
time_t rawtime = time(nullptr);
struct tm* tm = localtime(&rawtime);

char dir[4096];
sprintf(dir, "%s/d-d-d/", MP3_OUTPUT_DIRECTORY.c_str(), tm->tm_year 1900, tm->tm_mon 1, tm->tm_mday);
std::filesystem::create_directories(dir);

char filename[4096];
const auto f1 = frequency.frequency / 1000000;
const auto f2 = (frequency.frequency / 1000) % 1000;
const auto f3 = frequency.frequency % 1000;
sprintf(filename, "d:d:d =_d_d.mp3", tm->tm_hour, tm->tm_min, tm->tm_sec, f1, f2, f3);
return std::string(dir) std::string(filename);
}

sox_signalinfo_t config() {
sox_signalinfo_t c;
c.channels = 1;
c.rate = MP3_SAMPLE_RATE;
return c;
}

Mp3Writer::Mp3Writer(const Frequency& frequency, uint32_t sampleRate)
: m_path(getPath(frequency)),
m_sampleRate(sampleRate),
m_samples(0),
m_resampler(soxr_create(sampleRate, MP3_SAMPLE_RATE, 1, nullptr, nullptr, nullptr, nullptr)),
m_mp3Info(config()),
m_mp3File(sox_open_write(m_path.c_str(), &m_mp3Info, nullptr, nullptr, nullptr, nullptr)) {}

Mp3Writer::~Mp3Writer() {
soxr_delete(m_resampler);
sox_close(m_mp3File);
const auto duration = std::chrono::milliseconds(1000 * m_samples / m_sampleRate);
if (duration < MIN_RECORDING_TIME) {
spdlog::info("recording time: {:.2f} s, too short, removing", duration.count() / 1000.0);
std::filesystem::remove(m_path);
} else {
spdlog::info("recording time: {:.2f} s", duration.count() / 1000.0);
}
}

void Mp3Writer::appendSamples(const std::vector<float>& samples) {
m_samples = samples.size();
if (m_resamplerBuffer.size() < samples.size()) {
m_resamplerBuffer.resize(samples.size());
}
if (m_mp3Buffer.size() < samples.size()) {
m_mp3Buffer.resize(samples.size());
}
size_t read{0};
size_t write{0};
soxr_process(m_resampler, samples.data(), samples.size(), &read, m_resamplerBuffer.data(), m_resamplerBuffer.size(), &write);

if (read > 0 && write > 0) {
spdlog::debug("recording resampling, in rate/samples: {}/{}, out rate/samples: {}/{}", m_sampleRate, read, MP3_SAMPLE_RATE, write);
for (int i = 0; i < write; i) {
m_mp3Buffer[i] = m_resamplerBuffer[i] * 1000000000;
}
sox_write(m_mp3File, m_mp3Buffer.data(), write);
} else {
throw std::runtime_error("recording resampling error");
}
}
25 changes: 25 additions & 0 deletions sources/mp3_writer.h
Original file line number Diff line number Diff line change
@@ -0,0 1,25 @@
#pragma once

#include <sox.h>
#include <soxr.h>
#include <utils.h>

#include <string>

class Mp3Writer {
public:
Mp3Writer(const Frequency& frequency, uint32_t sampleRate);
~Mp3Writer();

void appendSamples(const std::vector<float>& samples);

private:
const std::string m_path;
const uint32_t m_sampleRate;
std::vector<float> m_resamplerBuffer;
std::vector<sox_sample_t> m_mp3Buffer;
uint64_t m_samples;
soxr_t m_resampler;
sox_signalinfo_t m_mp3Info;
sox_format_t* m_mp3File;
};
80 changes: 72 additions & 8 deletions sources/scanners/recorder.cpp
Original file line number Diff line number Diff line change
@@ -1,25 1,89 @@
#include "recorder.h"

#include <config.h>
#include <math.h>
#include <spdlog/spdlog.h>

Recorder::Recorder() : m_startDataTime(time()), m_lastActiveDataTime(time()), m_lastDataTime(time()) {}
#include <map>

constexpr auto DOWNSAMPLE = 10;
constexpr auto DOWNSAMPLE_FILTER_LENGTH = 10;

Recorder::Recorder(Signal signal, uint32_t centerFrequency, uint32_t bandwidth, uint32_t sampleRate, Spectrogram& Spectrogram)
: m_centerFrequency(centerFrequency),
m_bandwidth(bandwidth),
m_sampleRate(sampleRate),
m_spectrogram(Spectrogram),
m_fmDemodulator(freqdem_create(0.5f)),
m_Mp3Writer(signal.frequency, sampleRate / DOWNSAMPLE),
m_decimator(iirdecim_crcf_create_default(DOWNSAMPLE, DOWNSAMPLE_FILTER_LENGTH)),
m_startDataTime(time()),
m_lastActiveDataTime(time()),
m_lastDataTime(time()) {}

Recorder::~Recorder() {
const auto duration = (m_lastDataTime - m_startDataTime).count() / 1000.0;
const auto rawDataSize = m_samples.size() * sizeof(std::complex<float>) / 1024.0 / 1024.0;
spdlog::info("recording, time: {:.2f} s, best signals: {}, samples: {}, rawDataSize: {:.2f} MB", duration, m_bestSignals.size(), m_samples.size(), rawDataSize);
processSamples();
iirdecim_crcf_destroy(m_decimator);
freqdem_destroy(m_fmDemodulator);
spdlog::debug("close recording");
}

void Recorder::appendSamples(const Signal &bestSignal, bool active, std::vector<std::complex<float> > &buffer, const uint32_t samples) {
void Recorder::appendSamples(const Signal& bestSignal, bool active, std::vector<std::complex<float>>& buffer, const uint32_t samples) {
std::unique_lock<std::mutex> lock(m_mutex);
m_lastDataTime = time();
if (active) {
m_lastActiveDataTime = time();
m_bestSignals.push_back(bestSignal);
m_frequency[bestSignal.frequency.frequency] ;
for (const auto& noisedSample : m_noisedSamples) {
m_samples.push_back(noisedSample);
}
m_noisedSamples.clear();
for (int i = 0; i < samples; i) {
m_noisedSamples.push_back(buffer[i]);
}
} else {
for (int i = 0; i < samples; i) {
m_noisedSamples.push_back(buffer[i]);
}
}
for (int i = 0; i < samples; i) {
m_samples.push_back(buffer[i]);

if (m_sampleRate <= m_samples.size()) {
processSamples();
}
}

bool Recorder::isFinished() const { return m_lastActiveDataTime MAX_SILENCE_TIME < time(); }

void Recorder::processSamples() {
if (m_samples.size() <= 1 || m_frequency.empty()) {
return;
}
const auto duration = (m_lastDataTime - m_startDataTime).count() / 1000.0;
const auto bestFrequency = getBestFrequency();
spdlog::debug("processing partial recording, time: {:.2f} s, best {}", duration, bestFrequency.toString());
shift(m_samples, m_centerFrequency - bestFrequency.frequency, m_sampleRate, m_samples.size());

std::vector<std::complex<float>> downSamples;
if (m_lastSample.has_value()) {
downSamples.push_back(m_lastSample.value());
downSamples.resize(m_samples.size() / DOWNSAMPLE 1);
iirdecim_crcf_execute_block(m_decimator, reinterpret_cast<liquid_float_complex*>(m_samples.data()), m_samples.size() / DOWNSAMPLE, reinterpret_cast<liquid_float_complex*>(downSamples.data() 1));
} else {
downSamples.resize(m_samples.size() / DOWNSAMPLE);
iirdecim_crcf_execute_block(m_decimator, toLiquidComplext(m_samples.data()), m_samples.size() / DOWNSAMPLE, toLiquidComplext(downSamples.data()));
}

std::vector<float> fm;
fm.resize(downSamples.size() - 1);
freqdem_demodulate_block(m_fmDemodulator, toLiquidComplext(downSamples.data()), downSamples.size(), fm.data());
m_Mp3Writer.appendSamples(fm);

m_lastSample = downSamples.back();
m_samples.clear();
spdlog::debug("finish partial processing recording");
}

Frequency Recorder::getBestFrequency() const {
auto max = std::max_element(m_frequency.begin(), m_frequency.end(), [](const auto& x, const auto& y) { return x.second < y.second; });
return {max->first};
}
Loading

0 comments on commit 8d67b78

Please sign in to comment.