SDRPlusPlus/meteor_demodulator/src/main.cpp

211 lines
6.4 KiB
C++
Raw Normal View History

2021-04-01 16:54:16 +02:00
#include <imgui.h>
#include <watcher.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <options.h>
#include <dsp/pll.h>
#include <dsp/stream.h>
#include <dsp/demodulator.h>
#include <dsp/window.h>
#include <dsp/resampling.h>
#include <dsp/processing.h>
#include <dsp/routing.h>
#include <dsp/sink.h>
#include <gui/widgets/folder_select.h>
#include <gui/widgets/constellation_diagram.h>
#include <fstream>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO {
/* Name: */ "meteor_demodulator",
/* Description: */ "Meteor demodulator for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
std::string genFileName(std::string prefix, std::string suffix) {
time_t now = time(0);
tm *ltm = localtime(&now);
char buf[1024];
sprintf(buf, "%s_%02d-%02d-%02d_%02d-%02d-%02d%s", prefix.c_str(), ltm->tm_hour, ltm->tm_min, ltm->tm_sec, ltm->tm_mday, ltm->tm_mon + 1, ltm->tm_year + 1900, suffix.c_str());
return buf;
}
#define INPUT_SAMPLE_RATE 150000
class MeteorDemodulatorModule : public ModuleManager::Instance {
public:
MeteorDemodulatorModule(std::string name) : folderSelect("%ROOT%/recordings") {
this->name = name;
writeBuffer = new int8_t[STREAM_BUFFER_SIZE];
2021-04-17 22:37:50 +02:00
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 150000, INPUT_SAMPLE_RATE, 150000, 150000, true);
2021-04-01 16:54:16 +02:00
demod.init(vfo->output, INPUT_SAMPLE_RATE, 72000.0f, 32, 0.6f, 0.1f, 0.005f);
split.init(demod.out);
split.bindStream(&symSinkStream);
split.bindStream(&sinkStream);
reshape.init(&symSinkStream, 1024, (72000 / 30) - 1024);
symSink.init(&reshape.out, symSinkHandler, this);
sink.init(&sinkStream, sinkHandler, this);
demod.start();
split.start();
reshape.start();
symSink.start();
sink.start();
gui::menu.registerEntry(name, menuHandler, this, this);
}
~MeteorDemodulatorModule() {
}
void enable() {
2021-04-17 22:37:50 +02:00
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 150000, INPUT_SAMPLE_RATE, 150000, 150000, true);
2021-04-01 16:54:16 +02:00
demod.setInput(vfo->output);
demod.start();
split.start();
reshape.start();
symSink.start();
sink.start();
enabled = true;
}
void disable() {
demod.stop();
split.stop();
reshape.stop();
symSink.stop();
sink.stop();
sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static void menuHandler(void* ctx) {
MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
if (!_this->enabled) { style::beginDisabled(); }
ImGui::SetNextItemWidth(menuWidth);
_this->constDiagram.draw();
_this->folderSelect.render("##meteor-recorder-" + _this->name);
if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::beginDisabled(); }
if (_this->recording) {
if (ImGui::Button(CONCAT("Stop##_recorder_rec_", _this->name), ImVec2(menuWidth, 0))) {
std::lock_guard<std::mutex> lck(_this->recMtx);
_this->recording = false;
_this->recFile.close();
_this->dataWritten = 0;
}
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %.2fMB", (float)_this->dataWritten / 1000000.0f);
}
else {
if (ImGui::Button(CONCAT("Record##_recorder_rec_", _this->name), ImVec2(menuWidth, 0))) {
std::lock_guard<std::mutex> lck(_this->recMtx);
_this->dataWritten = 0;
std::string filename = genFileName(_this->folderSelect.expandString(_this->folderSelect.path) + "/meteor", ".s");
_this->recFile = std::ofstream(filename, std::ios::binary);
if (_this->recFile.is_open()) {
spdlog::info("Recording to '{0}'", filename);
_this->recording = true;
}
else {
spdlog::error("Could not open file for recording!");
}
}
ImGui::Text("Idle --.--MB");
}
if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::endDisabled(); }
if (!_this->enabled) { style::endDisabled(); }
}
static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) {
MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
dsp::complex_t* buf = _this->constDiagram.aquireBuffer();
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
_this->constDiagram.releaseBuffer();
}
static void sinkHandler(dsp::complex_t* data, int count, void* ctx) {
MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
std::lock_guard<std::mutex> lck(_this->recMtx);
if (!_this->recording) { return; }
for (int i = 0; i < count; i++) {
2021-04-01 20:57:03 +02:00
_this->writeBuffer[(2 * i)] = std::clamp<int>(data[i].re * 84.0f, -127, 127);
_this->writeBuffer[(2 * i) + 1] = std::clamp<int>(data[i].im * 84.0f, -127, 127);
2021-04-01 16:54:16 +02:00
}
_this->recFile.write((char*)_this->writeBuffer, count * 2);
_this->dataWritten += count * 2;
}
std::string name;
bool enabled = true;
// DSP Chain
VFOManager::VFO* vfo;
dsp::PSKDemod<4, false> demod;
dsp::Splitter<dsp::complex_t> split;
dsp::stream<dsp::complex_t> symSinkStream;
dsp::stream<dsp::complex_t> sinkStream;
dsp::Reshaper<dsp::complex_t> reshape;
dsp::HandlerSink<dsp::complex_t> symSink;
dsp::HandlerSink<dsp::complex_t> sink;
ImGui::ConstellationDiagram constDiagram;
FolderSelect folderSelect;
std::mutex recMtx;
bool recording = false;
uint64_t dataWritten = 0;
std::ofstream recFile;
int8_t* writeBuffer;
};
MOD_EXPORT void _INIT_() {
// Nothing
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new MeteorDemodulatorModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (MeteorDemodulatorModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing either
}