diff --git a/pipelines/Cluster.json b/pipelines/Cluster.json index b9dc42940..927d1125f 100644 --- a/pipelines/Cluster.json +++ b/pipelines/Cluster.json @@ -49,6 +49,10 @@ [ 2, 0 + ], + [ + 3, + 0 ] ], "work": { @@ -73,7 +77,10 @@ "rs_i": 5, "rs_type": "rs239" } - } + }, + "products":{ + "cluster_instruments": {} + } } } } \ No newline at end of file diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index cffad67da..7951892ec 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -213,6 +213,12 @@ if(PLUGIN_DVB OR PLUGINS_ALL) add_subdirectory(dvb_support) endif() +option(PLUGIN_CLUSTER "Enable the CLUSTER support plugin" OFF) + +if(PLUGIN_CLUSTER OR PLUGINS_ALL) + add_subdirectory(cluster_support) +endif() + # add_subdirectory(sample) option(PLUGIN_GVAR_EXTENDED "Enable the GVAR Extended plugin" OFF) diff --git a/plugins/cluster_support/CMakeLists.txt b/plugins/cluster_support/CMakeLists.txt new file mode 100644 index 000000000..b2216152f --- /dev/null +++ b/plugins/cluster_support/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0.0) +project(cluster_support) + +file(GLOB_RECURSE cluster_support_CPPS *.cpp) +add_library(cluster_support SHARED ${cluster_support_CPPS}) +target_link_libraries(cluster_support PUBLIC satdump_core) +target_include_directories(cluster_support PUBLIC src) + +install(TARGETS cluster_support DESTINATION lib/satdump/plugins) \ No newline at end of file diff --git a/plugins/cluster_support/cluster/instruments/wbd_decoder.cpp b/plugins/cluster_support/cluster/instruments/wbd_decoder.cpp new file mode 100644 index 000000000..177f062ef --- /dev/null +++ b/plugins/cluster_support/cluster/instruments/wbd_decoder.cpp @@ -0,0 +1,50 @@ +#include "wbd_decoder.h" + +namespace cluster +{ + std::vector WBDdecoder::work(uint8_t *frm) + { + std::vector outputframes; + + if (frm[3] % 4 == 0) + { + if (wbdwip.payload.size() == 1090 * 4 - 8) + outputframes.push_back(wbdwip); + wbdwip = MajorFrame(); + } + else if (frm[3] % 4 == 1) + { + wbdwip.antennaselect = (frm[5] >> 4) & 0b11; // 0 Ez, 1 Bz, 2 By, 3 Ey + wbdwip.convfreq = (frm[5] >> 2) & 0b11; // conversion freq, 0, 125.454, 250.908, 501.816k + wbdwip.hgagc = frm[5] & 0b11; // Higher AGC select + wbdwip.commands = frm[5] >> 5 & 0b1; // Command Status (0=No cmds; 1=Cmds received) + wbdwip.OBDH = frm[5] >> 6 & 0b1; // OBDH Interface (0=Primary; 1=Redundant) + wbdwip.ADpower = frm[5] >> 4 & 0b1; // seems to be always on + wbdwip.VCXO = frm[5] >> 7; // locked unlocked, 0 is locked + } + else if (frm[3] % 4 == 2) + { + + wbdwip.AGC = frm[4] >> 5 & 0b1; // AGC MAN/AUTO 0 for Auto, 1 for Manual + wbdwip.gainlevel = frm[4] >> 1 & 0b1111; // Gain, 16steps as per 3.2.2.4-1 + wbdwip.antennaselect = (frm[5] >> 4) & 0b11; // 0 Ez, 1 Bz, 2 By, 3 Ey + wbdwip.convfreq = (frm[5] >> 2) & 0b11; // conversion freq, 0, 125.454, 250.908, 501.816k + wbdwip.hgagc = frm[5] & 0b11; // Higher AGC select + } + else if (frm[3] % 4 == 3) + { + wbdwip.commands = frm[5] >> 5 & 0b1; // Command Status (0=No cmds; 1=Cmds received) + wbdwip.OBDH = frm[5] >> 6 & 0b1; // OBDH Interface (0=Primary; 1=Redundant) + wbdwip.ADpower = frm[5] >> 4 & 0b1; // seems to be always on + wbdwip.VCXO = frm[5] >> 7; // locked unlocked, 0 is locked + wbdwip.outputmode = frm[4] >> 2 & 0b111; // sampling rate, bit depth, duty cycle as per 3.2.2.5-1 + wbdwip.lwagc = frm[4] & 0b11; // Lower AGC select + } + + for (int i = 6; i < 1096; i++) + if (i != 88 && i != 89) + wbdwip.payload.push_back(frm[i]); + + return outputframes; + } +} \ No newline at end of file diff --git a/plugins/cluster_support/cluster/instruments/wbd_decoder.h b/plugins/cluster_support/cluster/instruments/wbd_decoder.h new file mode 100644 index 000000000..966c9d12c --- /dev/null +++ b/plugins/cluster_support/cluster/instruments/wbd_decoder.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +namespace cluster +{ + struct MajorFrame + { + std::vector payload; + bool VCXO = false; + bool ADpower = false; + bool AGC = false; + bool OBDH = false; + bool commands = false; + int convfreq = 0; + int outputmode = 0; + int antennaselect = 0; + int WBDmodel = 0; + int gainlevel = 0; + int lwagc = 0; + int hgagc = 0; + }; + + class WBDdecoder + { + private: + MajorFrame wbdwip; + + public: + std::vector work(uint8_t *frm); + }; +} \ No newline at end of file diff --git a/plugins/cluster_support/cluster/module_cluster_instruments.cpp b/plugins/cluster_support/cluster/module_cluster_instruments.cpp new file mode 100644 index 000000000..d6c693e51 --- /dev/null +++ b/plugins/cluster_support/cluster/module_cluster_instruments.cpp @@ -0,0 +1,231 @@ +#include "module_cluster_instruments.h" +#include +#include "common/ccsds/ccsds_standard/vcdu.h" +#include "logger.h" +#include +#include "imgui/imgui.h" +#include "common/utils.h" +#include "common/ccsds/ccsds_standard/demuxer.h" +#include "products/products.h" +#include "products/dataset.h" +#include "common/simple_deframer.h" +#include "instruments/wbd_decoder.h" +#include "common/audio/audio_sink.h" +#include "common/dsp/io/wav_writer.h" + +namespace cluster +{ + namespace instruments + { + CLUSTERInstrumentsDecoderModule::CLUSTERInstrumentsDecoderModule(std::string input_file, std::string output_file_hint, nlohmann::json parameters) + : ProcessingModule(input_file, output_file_hint, parameters) + { + } + + void CLUSTERInstrumentsDecoderModule::process() + { + if (input_data_type == DATA_FILE) + filesize = getFilesize(d_input_file); + std::ifstream data_in; + if (input_data_type == DATA_FILE) + data_in = std::ifstream(d_input_file, std::ios::binary); + + logger->info("Using input frames " + d_input_file); + + time_t lastTime = 0; + uint8_t cadu[1279]; + + // Demuxers + ccsds::ccsds_standard::Demuxer demuxer_vcid1(1101, false); + + std::ofstream output(d_output_file_hint + "_wbd.frm", std::ios::binary); + std::ofstream output_cadu2(d_output_file_hint + "_cadu_bkp.cadu", std::ios::binary); + + def::SimpleDeframer wbddeframer(0xFAF334, 24, 8768, 0); + WBDdecoder wbddecode; + + // Audio stuff + int16_t audio_buffer[4352]; + bool enable_audio = false; + std::shared_ptr audio_sink; + if (input_data_type != DATA_FILE && audio::has_sink()) + { + enable_audio = true; + audio_sink = audio::get_default_sink(); + audio_sink->set_samplerate(27443); + audio_sink->start(); + } + + // Buffers to wav...for all the antennas + std::ofstream out_antenna_Ez(d_output_file_hint + "_Ez.wav", std::ios::binary); + int final_data_size_ant_Ez = 0; + dsp::WavWriter wave_writer_Ez(out_antenna_Ez); + wave_writer_Ez.write_header(27443, 1); + + std::ofstream out_antenna_Bx(d_output_file_hint + "_Bx.wav", std::ios::binary); + int final_data_size_ant_Bx = 0; + dsp::WavWriter wave_writer_Bx(out_antenna_Bx); + wave_writer_Bx.write_header(27443, 1); + + std::ofstream out_antenna_By(d_output_file_hint + "_By.wav", std::ios::binary); + int final_data_size_ant_By = 0; + dsp::WavWriter wave_writer_By(out_antenna_By); + wave_writer_By.write_header(27443, 1); + + std::ofstream out_antenna_Ey(d_output_file_hint + "_Ey.wav", std::ios::binary); + int final_data_size_ant_Ey = 0; + dsp::WavWriter wave_writer_Ey(out_antenna_Ey); + wave_writer_Ey.write_header(27443, 1); + + while (input_data_type == DATA_FILE ? !data_in.eof() : input_active.load()) + { + // Read buffer + if (input_data_type == DATA_FILE) + data_in.read((char *)&cadu, 1279); + else + input_fifo->read((uint8_t *)&cadu, 1279); + + // Save (for now) + output_cadu2.write((char *)cadu, 1279); + + // Parse this transport frame + ccsds::ccsds_standard::VCDU vcdu = ccsds::ccsds_standard::parseVCDU(cadu); + + if (vcdu.vcid == 5) // Parse WBD VCID + { + std::vector ccsdsFrames = demuxer_vcid1.work(cadu); + for (ccsds::CCSDSPacket &pkt : ccsdsFrames) + { + std::vector> minorframes = wbddeframer.work(pkt.payload.data(), pkt.payload.size()); + for (auto &frm : minorframes) + { + std::vector majorframes = wbddecode.work(frm.data()); + for (auto &majorframe : majorframes) + { + for (int i = 0; i < 4352; i++) + audio_buffer[i] = (int(majorframe.payload[i]) - 127) * 255; + + if (enable_audio) + audio_sink->push_samples(audio_buffer, 4352); + + if (majorframe.VCXO == 0) + { + output.write((char *)majorframe.payload.data(), majorframe.payload.size()); + if (majorframe.antennaselect == 0) + { + out_antenna_Ez.write((char *)audio_buffer, 4352 * sizeof(int16_t)); + final_data_size_ant_Ez += 4352 * sizeof(int16_t); + } + else if (majorframe.antennaselect == 1) + { + out_antenna_Bx.write((char *)audio_buffer, 4352 * sizeof(int16_t)); + final_data_size_ant_Bx += 4352 * sizeof(int16_t); + } + else if (majorframe.antennaselect == 2) + { + out_antenna_By.write((char *)audio_buffer, 4352 * sizeof(int16_t)); + final_data_size_ant_By += 4352 * sizeof(int16_t); + } + else if (majorframe.antennaselect == 3) + { + out_antenna_Ey.write((char *)audio_buffer, 4352 * sizeof(int16_t)); + final_data_size_ant_Ey += 4352 * sizeof(int16_t); + } + } + + param_antenna = majorframe.antennaselect; + param_convfrq = majorframe.convfreq; + param_band = majorframe.outputmode; + param_VCXO = majorframe.VCXO; + param_OBDH = majorframe.OBDH; + param_commands = majorframe.commands; + } + } + } + } + + progress = data_in.tellg(); + if (time(NULL) % 10 == 0 && lastTime != time(NULL)) + { + lastTime = time(NULL); + logger->info("Progress " + std::to_string(round(((double)progress / (double)filesize) * 1000.0) / 10.0) + "%%"); + } + } + + wave_writer_Ez.finish_header(final_data_size_ant_Ez); + wave_writer_Bx.finish_header(final_data_size_ant_Bx); + wave_writer_By.finish_header(final_data_size_ant_By); + wave_writer_Ey.finish_header(final_data_size_ant_Ey); + + if (input_data_type == DATA_FILE) + data_in.close(); + output_cadu2.close(); + } + + void CLUSTERInstrumentsDecoderModule::drawUI(bool window) + { + ImGui::Begin("Cluster Instruments Decoder", NULL, window ? 0 : NOWINDOW_FLAGS); + + ImGui::Text("Antenna :"); + ImGui::SameLine(); + if (param_antenna == 0) + ImGui::Text("Ez"); + else if (param_antenna == 1) + ImGui::Text("Bx"); + else if (param_antenna == 2) + ImGui::Text("By"); + else if (param_antenna == 3) + ImGui::Text("Ey"); + + ImGui::Text("Conversion Freq : "); + ImGui::SameLine(); + if (param_convfrq == 0) + ImGui::Text("Baseband"); + else if (param_convfrq == 1) + ImGui::Text("125.454 kHz"); + else if (param_convfrq == 2) + ImGui::Text("250.908 kHz"); + else if (param_convfrq == 3) + ImGui::Text("501.816 kHz"); + + // ImGui::Text("Freq Band:"); + // ImGui::SameLine(); + // ImGui::Text("%d", param_band); + + ImGui::Text("VCXO : "); + ImGui::SameLine(); + if (param_VCXO == 0) + ImGui::Text("Locked"); + else + ImGui::Text("Unlocked"); + + ImGui::Text("OBDH : "); + ImGui::SameLine(); + ImGui::Text("%d", param_OBDH); + + ImGui::Text("CMD : "); + ImGui::SameLine(); + ImGui::Text("%d", param_commands); + + if (input_data_type == DATA_FILE) + ImGui::ProgressBar((double)progress / (double)filesize, ImVec2(ImGui::GetWindowWidth() - 10, 20 * ui_scale)); + + ImGui::End(); + } + + std::string CLUSTERInstrumentsDecoderModule::getID() + { + return "cluster_instruments"; + } + + std::vector CLUSTERInstrumentsDecoderModule::getParameters() + { + return {}; + } + + std::shared_ptr CLUSTERInstrumentsDecoderModule::getInstance(std::string input_file, std::string output_file_hint, nlohmann::json parameters) + { + return std::make_shared(input_file, output_file_hint, parameters); + } + } // namespace amsu +} // namespace metop \ No newline at end of file diff --git a/plugins/cluster_support/cluster/module_cluster_instruments.h b/plugins/cluster_support/cluster/module_cluster_instruments.h new file mode 100644 index 000000000..401353e88 --- /dev/null +++ b/plugins/cluster_support/cluster/module_cluster_instruments.h @@ -0,0 +1,42 @@ +#pragma once + +#include "core/module.h" + +#include "instruments/wbd_decoder.h" + +namespace cluster +{ + namespace instruments + { + class CLUSTERInstrumentsDecoderModule : public ProcessingModule + { + protected: + std::atomic filesize; + std::atomic progress; + + // Readers + // cips::CIPSReader cips_readers[4]; + // totally not stolen AIM decoder..... + // Statuses + // instrument_status_t cips_status[4] = {DECODING, DECODING, DECODING, DECODING}; + + bool param_VCXO = false; + bool param_OBDH = false; + bool param_commands = false; + int param_antenna = 0; + int param_convfrq = 0; + int param_band = 0; + + public: + CLUSTERInstrumentsDecoderModule(std::string input_file, std::string output_file_hint, nlohmann::json parameters); + void process(); + void drawUI(bool window); + + public: + static std::string getID(); + virtual std::string getIDM() { return getID(); }; + static std::vector getParameters(); + static std::shared_ptr getInstance(std::string input_file, std::string output_file_hint, nlohmann::json parameters); + }; + } // namespace amsu +} // namespace metop \ No newline at end of file diff --git a/plugins/cluster_support/main.cpp b/plugins/cluster_support/main.cpp new file mode 100644 index 000000000..b4d13c099 --- /dev/null +++ b/plugins/cluster_support/main.cpp @@ -0,0 +1,25 @@ +#include "core/plugin.h" +#include "logger.h" +#include "core/module.h" + +#include "cluster/module_cluster_instruments.h" + +class CLUSTERSupport : public satdump::Plugin +{ +public: + std::string getID() + { + return "cluster_support"; + } + + void init() + { + satdump::eventBus->register_handler(registerPluginsHandler); + } + static void registerPluginsHandler(const RegisterModulesEvent &evt) + { + REGISTER_MODULE_EXTERNAL(evt.modules_registry, cluster::instruments::CLUSTERInstrumentsDecoderModule); + } +}; + +PLUGIN_LOADER(CLUSTERSupport) \ No newline at end of file