satdump/plugins/kanopus_support/kanopus/module_kanopusv_decoder.cpp
2026-02-19 13:49:57 +01:00

286 lines
9.9 KiB
C++

#include "module_kanopusv_decoder.h"
#include "common/codings/differential/qpsk_diff.h"
#include "common/codings/soft_reader.h"
#include "common/repack_bits_byte.h"
#include "logger.h"
#include "nlohmann/json_utils.h"
#include "utils/binary.h"
#include <cstdint>
#define BUFFER_SIZE (1024 * 8)
using namespace satdump;
namespace kanopus
{
KanopusVDecoderModule::KanopusVDecoderModule(std::string input_file, std::string output_file_hint, nlohmann::json parameters)
: satdump::pipeline::base::FileStreamToFileStreamModule(input_file, output_file_hint, parameters)
{
soft_buffer = new int8_t[BUFFER_SIZE];
prediff_bits_buffer = new uint8_t[BUFFER_SIZE];
bits_buffer = new uint8_t[BUFFER_SIZE];
frameshift_buffer = new uint64_t[384];
hard_symbs = getValueOrDefault(d_parameters["hard_symbols"], false);
dump_diff = getValueOrDefault(d_parameters["dump_diff"], false);
dump_stream = getValueOrDefault(d_parameters["dump_stream"], false);
if (dump_diff || dump_stream)
fsfsm_file_ext = ".bin";
else
fsfsm_file_ext = ".frm";
}
std::vector<ModuleDataType> KanopusVDecoderModule::getInputTypes() { return {DATA_FILE, DATA_STREAM}; }
std::vector<ModuleDataType> KanopusVDecoderModule::getOutputTypes() { return {DATA_FILE}; }
KanopusVDecoderModule::~KanopusVDecoderModule()
{
delete[] soft_buffer;
delete[] prediff_bits_buffer;
delete[] bits_buffer;
delete[] frameshift_buffer;
}
void KanopusVDecoderModule::process()
{
diff::QPSKDiff diff;
RepackBitsByte repack;
while (should_run())
{
if (!hard_symbs)
{
// Read a buffer
read_data((uint8_t *)soft_buffer, BUFFER_SIZE);
// Repack to bits fo the diff
for (int i = 0; i < BUFFER_SIZE / 2; i++)
prediff_bits_buffer[i] = (soft_buffer[i * 2 + 0] >= 0) << 1 | (soft_buffer[i * 2 + 1] >= 0);
}
else
{
read_data((uint8_t *)soft_buffer, BUFFER_SIZE / 8);
// We need only half...
for (int i = 0; i < BUFFER_SIZE / 8; i++)
{
prediff_bits_buffer[i * 4 + 0] = (((uint8_t *)soft_buffer)[i] >> 6) & 0b11;
prediff_bits_buffer[i * 4 + 1] = (((uint8_t *)soft_buffer)[i] >> 4) & 0b11;
prediff_bits_buffer[i * 4 + 2] = (((uint8_t *)soft_buffer)[i] >> 2) & 0b11;
prediff_bits_buffer[i * 4 + 3] = (((uint8_t *)soft_buffer)[i] >> 0) & 0b11;
}
}
// Perform
diff.work(prediff_bits_buffer, BUFFER_SIZE / 2, bits_buffer);
// Invert 2nd branch
for (int i = 0; i < BUFFER_SIZE / 2; i++)
{
bits_buffer[i * 2 + 0] = bits_buffer[i * 2 + 0];
bits_buffer[i * 2 + 1] = (~bits_buffer[i * 2 + 1]) & 1;
}
// Optionally, dump data already
if (dump_diff)
{
int o = repack.work(bits_buffer, BUFFER_SIZE, bits_buffer);
write_data(bits_buffer, o);
continue;
}
// Next, start synchronizing
process_bits(bits_buffer, BUFFER_SIZE);
}
logger->info("Decoding finished");
cleanup();
}
inline uint8_t get_bit_in_buffer(uint64_t *buf, uint32_t pos) { return (buf[pos / 64] >> (63 - (pos % 64))) & 1; } // TODOREWORK put into utils
void KanopusVDecoderModule::write_frame()
{
// PN Sequence
uint8_t derand[48] = {0x55, 0xE2, 0xA4, 0x6A, 0x30, 0xA7, 0xDA, 0x16, 0x24, 0x86, 0x39, 0x07, 0x0D, 0x1D, 0xEC, 0x64, 0xF0, 0x3D, 0xD7, 0x66, 0x98, 0x08, 0x15, 0x94,
0xA0, 0xBA, 0x5B, 0x22, 0x3E, 0x67, 0x40, 0x1B, 0x54, 0x3A, 0xB7, 0x2B, 0x9E, 0xB0, 0x4B, 0xD3, 0xB6, 0xF3, 0x8D, 0xF1, 0xE5, 0xC4, 0x27, 0x36};
// Deinterleave
uint8_t deint_buffer[8][384];
for (int i = 0; i < 3072 * 8; i++)
deint_buffer[i % 8][i / 64] = deint_buffer[i % 8][i / 64] << 1 | get_bit_in_buffer(frameshift_buffer, i);
// write_data(deint_buffer[0], 384);
// Derand everything
for (int f = 0; f < 8; f++)
for (int i = 0; i < 8; i++)
for (int ii = 0; ii < 48; ii++)
deint_buffer[f][i * 48 + ii] ^= derand[ii];
// Write, re-interleaving into a full stream
uint8_t final[384 * 8 - 16];
for (int i = 0, y = 0; i < 8; i++)
{
for (int ii = 0; ii < 48; ii++)
{
for (int f : {1, 0, 3, 2, 5, 4, 7, 6})
if (f < 2 && ii == 0)
;
else
final[y++] = deint_buffer[f][i * 48 + ii];
}
}
frame_count++;
if (dump_stream)
{
write_data(final, 3056);
return;
}
// Finally sync PMSS Frames & write
auto pmss_frames = pmss_deframer.work(final, 3056);
for (auto &f : pmss_frames)
{
write_data(f.data(), 15680 / 8);
pmss_frame_count++;
}
}
void KanopusVDecoderModule::process_bits(uint8_t *bits, int cnt)
{
const uint8_t SYNC_0 = 0b00000100;
const uint8_t SYNC_1 = 0b10110011;
const uint8_t SYNC_2 = 0b11100011;
const uint8_t SYNC_3 = 0b01110101;
// SYNC_4 Counter
// SYNC_5 No idea, some marker?
// SYNC_6 Changes
// SYNC_7 Counter
for (int c = 0; c < cnt; c++)
{
// Shift into sync buffer. uint64_t to optimize things
for (int i = 0; i < (384 - 1); i++)
frameshift_buffer[i] = (frameshift_buffer[i] << 1) | (frameshift_buffer[i + 1] >> 63);
frameshift_buffer[(384 - 1)] = (frameshift_buffer[(384 - 1)] << 1) | bits[c];
// Skip to next frame as needed
if (skip > 0)
{
skip--;
continue;
}
// Extract what should be sync bytes
uint8_t sync_bytes[2][4];
for (int f = 0; f < 2; f++)
{
for (int s = 0; s < 4; s++)
{
sync_bytes[f][s] = get_bit_in_buffer(frameshift_buffer, ((s * 384) + 0) * 8 + f) << 7 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 1) * 8 + f) << 6 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 2) * 8 + f) << 5 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 3) * 8 + f) << 4 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 4) * 8 + f) << 3 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 5) * 8 + f) << 2 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 6) * 8 + f) << 1 | //
get_bit_in_buffer(frameshift_buffer, ((s * 384) + 7) * 8 + f);
}
}
// Diff them & count
uint8_t final_diff = count_ones(sync_bytes[0][0] ^ SYNC_0) + count_ones(sync_bytes[1][0] ^ SYNC_0) + //
count_ones(sync_bytes[0][1] ^ SYNC_1) + count_ones(sync_bytes[1][1] ^ SYNC_1) + //
count_ones(sync_bytes[0][2] ^ SYNC_2) + count_ones(sync_bytes[1][2] ^ SYNC_2) + //
count_ones(sync_bytes[0][3] ^ SYNC_3) + count_ones(sync_bytes[1][3] ^ SYNC_3);
if (!synced)
{
for (int i = 0; i < 8; i++)
{
if (final_diff == 0)
{
synced = true;
write_frame();
skip = 3072 * 8 - 1;
bad_sync = 0;
break;
}
}
}
else
{
if (final_diff < 10)
{
write_frame();
skip = 3072 * 8 - 1;
bad_sync = 0;
}
else
bad_sync++;
if (bad_sync > 10)
synced = false;
}
}
}
nlohmann::json KanopusVDecoderModule::getModuleStats()
{
auto v = satdump::pipeline::base::FileStreamToFileStreamModule::getModuleStats();
v["synced"] = synced;
return v;
}
void KanopusVDecoderModule::drawUI(bool window)
{
ImGui::Begin("Kanopus-V Decoder", NULL, window ? 0 : NOWINDOW_FLAGS);
ImGui::Button("Deframer", {200 * ui_scale, 20 * ui_scale});
{
ImGui::Text("State : ");
ImGui::SameLine();
if (!synced)
ImGui::TextColored(style::theme.red, "NOSYNC");
else
ImGui::TextColored(style::theme.green, "SYNCED");
ImGui::Text("Frames : ");
ImGui::SameLine();
ImGui::TextColored(style::theme.green, UITO_C_STR(frame_count));
}
ImGui::Button("Payload", {200 * ui_scale, 20 * ui_scale});
{
ImGui::Text("PMSS : ");
ImGui::SameLine();
ImGui::TextColored(style::theme.green, UITO_C_STR(pmss_frame_count));
}
drawProgressBar();
ImGui::End();
}
std::string KanopusVDecoderModule::getID() { return "kanopusv_decoder"; }
std::shared_ptr<ProcessingModule> KanopusVDecoderModule::getInstance(std::string input_file, std::string output_file_hint, nlohmann::json parameters)
{
return std::make_shared<KanopusVDecoderModule>(input_file, output_file_hint, parameters);
}
} // namespace kanopus