Corrected probe data formation, added 4800 bps case in interleaver and EOM generator

This commit is contained in:
RecklessAndFeckless 2024-10-11 20:46:34 -04:00
parent b9502ebe86
commit 9f2d79617e
5 changed files with 81 additions and 68 deletions

View File

@ -180,7 +180,10 @@ private:
* @brief Sets the matrix dimensions based on baud rate and interleave setting. * @brief Sets the matrix dimensions based on baud rate and interleave setting.
*/ */
void setMatrixDimensions() { void setMatrixDimensions() {
if (baud_rate == 2400) { if (baud_rate == 4800) {
rows = 0;
columns = 0;
} else if (baud_rate == 2400) {
rows = 40; rows = 40;
columns = (interleave_setting == 2) ? 576 : 72; columns = (interleave_setting == 2) ? 576 : 72;
} else if (baud_rate == 1200) { } else if (baud_rate == 1200) {

View File

@ -51,7 +51,7 @@ public:
interleaver(baud_rate, interleave_setting, is_frequency_hopping), interleaver(baud_rate, interleave_setting, is_frequency_hopping),
input_data(std::move(_data)), input_data(std::move(_data)),
mgd_decoder(baud_rate, is_frequency_hopping), mgd_decoder(baud_rate, is_frequency_hopping),
modulator(baud_rate, 48000, 0.5, is_frequency_hopping) {} modulator(baud_rate, 48000, is_frequency_hopping) {}
/** /**
* @brief Transmits the input data by processing it through different phases like FEC encoding, interleaving, symbol formation, scrambling, and modulation. * @brief Transmits the input data by processing it through different phases like FEC encoding, interleaving, symbol formation, scrambling, and modulation.
@ -72,12 +72,13 @@ public:
// Step 3: Interleaving // Step 3: Interleaving
processed_data = interleaver.interleaveStream(fec_encoded_data); processed_data = interleaver.interleaveStream(fec_encoded_data);
} }
// Step 4: MGD Decoding
std::vector<uint8_t> mgd_decoded_data = mgd_decoder.mgdDecode(processed_data); std::vector<uint8_t> mgd_decoded_data = mgd_decoder.mgdDecode(processed_data);
// Step 4: Symbol Formation. This function injects the sync preamble symbols. Scrambling is built-in. // Step 5: Symbol Formation. This function injects the sync preamble symbols. Scrambling is handled internally.
std::vector<uint8_t> symbol_stream = symbol_formation.formSymbols(mgd_decoded_data); std::vector<uint8_t> symbol_stream = symbol_formation.formSymbols(mgd_decoded_data);
// Step 6. Modulation. The symbols are applied via 2400-bps 8-PSK modulation, with a 48 KHz sample rate.
std::vector<int16_t> modulated_signal = modulator.modulate(symbol_stream); std::vector<int16_t> modulated_signal = modulator.modulate(symbol_stream);
return modulated_signal; return modulated_signal;
@ -116,9 +117,10 @@ private:
size_t fec_flush_bits = 144; // FEC encoder flush bits size_t fec_flush_bits = 144; // FEC encoder flush bits
size_t interleave_flush_bits = interleaver.getFlushBits(); size_t interleave_flush_bits = interleaver.getFlushBits();
size_t total_flush_bits = fec_flush_bits + ((interleave_setting == 0) ? 0 : interleave_flush_bits); size_t total_flush_bits = fec_flush_bits + ((interleave_setting == 0) ? 0 : interleave_flush_bits);
while ((eom_data.getMaxBitIndex() + total_flush_bits) % interleave_flush_bits) if (interleave_flush_bits > 0) {
total_flush_bits++; while ((eom_data.getMaxBitIndex() + total_flush_bits) % interleave_flush_bits)
total_flush_bits++;
}
size_t total_bytes = (total_flush_bits + 7) / 8; // Round up to ensure we have enough bytes to handle all bits. size_t total_bytes = (total_flush_bits + 7) / 8; // Round up to ensure we have enough bytes to handle all bits.
BitStream flush_bits(std::vector<uint8_t>(total_bytes, 0), total_flush_bits); BitStream flush_bits(std::vector<uint8_t>(total_bytes, 0), total_flush_bits);
eom_data += flush_bits; eom_data += flush_bits;

View File

@ -21,16 +21,10 @@ std::vector<uint8_t> baud75_normal_3 = {0, 4, 4, 0};
class SymbolFormation { class SymbolFormation {
public: public:
SymbolFormation(size_t baud_rate, size_t interleave_setting, bool is_voice, bool is_frequency_hopping) : interleave_setting(interleave_setting), baud_rate(baud_rate), is_voice(is_voice), is_frequency_hopping(is_frequency_hopping) {} SymbolFormation(size_t baud_rate, size_t interleave_setting, bool is_voice, bool is_frequency_hopping) : interleave_setting(interleave_setting), baud_rate(baud_rate), is_voice(is_voice), is_frequency_hopping(is_frequency_hopping) {
std::vector<uint8_t> formSymbols(std::vector<uint8_t>& symbol_data) {
// Generate and scramble the sync preamble
std::vector<uint8_t> sync_preamble = generateSyncPreamble();
sync_preamble = scrambler.scrambleSyncPreamble(sync_preamble);
// Determine the block sizes // Determine the block sizes
size_t unknown_data_block_size = (baud_rate >= 2400) ? 32 : 20; unknown_data_block_size = (baud_rate >= 2400) ? 32 : 20;
size_t interleaver_block_size; known_data_block_size = (baud_rate >= 2400) ? 16 : 20;
if (baud_rate == 2400) { if (baud_rate == 2400) {
interleaver_block_size = (interleave_setting == 2) ? (40 * 576) : (40 * 72); interleaver_block_size = (interleave_setting == 2) ? (40 * 576) : (40 * 72);
@ -42,8 +36,17 @@ class SymbolFormation {
interleaver_block_size = (interleave_setting == 2) ? (20 * 36) : (10 * 9); interleaver_block_size = (interleave_setting == 2) ? (20 * 36) : (10 * 9);
} }
total_frames = interleaver_block_size / (unknown_data_block_size + known_data_block_size);
}
std::vector<uint8_t> formSymbols(std::vector<uint8_t>& symbol_data) {
// Generate and scramble the sync preamble
std::vector<uint8_t> sync_preamble = generateSyncPreamble();
sync_preamble = scrambler.scrambleSyncPreamble(sync_preamble);
size_t set_count = 0; size_t set_count = 0;
size_t symbol_count = 0; size_t symbol_count = 0;
size_t current_frame = 0;
std::vector<uint8_t> data_stream; std::vector<uint8_t> data_stream;
size_t current_index = 0; size_t current_index = 0;
@ -71,9 +74,9 @@ class SymbolFormation {
// Insert probe data if we are at an interleaver block boundary // Insert probe data if we are at an interleaver block boundary
if (baud_rate > 75) { if (baud_rate > 75) {
bool is_at_boundary = (symbol_count % interleaver_block_size) == 0; std::vector<uint8_t> probe_data = generateProbeData(current_frame, total_frames);
std::vector<uint8_t> probe_data = generateProbeData(!is_at_boundary);
data_stream.insert(data_stream.end(), probe_data.begin(), probe_data.end()); data_stream.insert(data_stream.end(), probe_data.begin(), probe_data.end());
current_frame = (current_frame + 1) % total_frames;
} }
} }
@ -93,6 +96,10 @@ class SymbolFormation {
int interleave_setting; int interleave_setting;
bool is_voice; bool is_voice;
bool is_frequency_hopping; bool is_frequency_hopping;
size_t interleaver_block_size;
size_t unknown_data_block_size;
size_t known_data_block_size;
size_t total_frames;
Scrambler scrambler = Scrambler(); Scrambler scrambler = Scrambler();
std::vector<uint8_t> mapChannelSymbolToTribitPattern(uint8_t symbol, bool repeat_twice = false) { std::vector<uint8_t> mapChannelSymbolToTribitPattern(uint8_t symbol, bool repeat_twice = false) {
@ -212,59 +219,60 @@ class SymbolFormation {
return preamble; return preamble;
} }
std::vector<uint8_t> generateProbeData(bool is_inside_block) { std::vector<uint8_t> generateProbeData(size_t current_frame, size_t total_frames) {
std::vector<uint8_t> probe_data; std::vector<uint8_t> probe_data;
// Determine interleaver block size based on baud rate and interleave setting // Set the known symbol patterns for D1 and D2 based on Table XI
size_t interleaver_block_size; uint8_t D1, D2;
if (baud_rate == 2400) { if (baud_rate == 4800) {
interleaver_block_size = (interleave_setting == 2) ? (40 * 576) : (40 * 72); D1 = 7; D2 = 6;
} else if (baud_rate == 2400 && is_voice) {
D1 = 7; D2 = 7;
} else if (baud_rate == 2400) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 4;
} else if (baud_rate == 1200) { } else if (baud_rate == 1200) {
interleaver_block_size = (interleave_setting == 2) ? (40 * 288) : (40 * 36); D1 = (interleave_setting <= 1) ? 6 : 4;
} else if ((baud_rate >= 150) || (baud_rate == 75 && is_frequency_hopping)) { D2 = 5;
interleaver_block_size = (interleave_setting == 2) ? (40 * 144) : (40 * 18); } else if (baud_rate == 600) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 6;
} else if (baud_rate == 300) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 7;
} else if (baud_rate == 150) {
D1 = (interleave_setting <= 1) ? 7 : 5;
D2 = 4;
} else if (baud_rate == 75) {
D1 = (interleave_setting <= 1) ? 7 : 5;
D2 = 5;
} else { } else {
interleaver_block_size = (interleave_setting == 2) ? (20 * 36) : (10 * 9); throw std::invalid_argument("Invalid baud rate for generateProbeData");
} }
// If we are inside an interleaver block, the probe data is filled with zeros // If the current frame is not the last two frames, set probe data to zeros
if (is_inside_block) { if (current_frame < total_frames - 2) {
probe_data.resize(interleaver_block_size, 0x00); probe_data.resize(known_data_block_size, 0x00);
} else { }
// Set the known symbol patterns for D1 and D2 based on Table XI // If the current frame is the second-to-last frame, set probe data to D1 pattern
uint8_t D1, D2; else if (current_frame == total_frames - 2) {
if (baud_rate == 4800) {
D1 = 7; D2 = 6;
} else if (baud_rate == 2400 && is_voice) {
D1 = 7; D2 = 7;
} else if (baud_rate == 2400) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 4;
} else if (baud_rate == 1200) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 5;
} else if (baud_rate == 600) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 6;
} else if (baud_rate == 300) {
D1 = (interleave_setting <= 1) ? 6 : 4;
D2 = 7;
} else if (baud_rate == 150) {
D1 = (interleave_setting <= 1) ? 7 : 5;
D2 = 4;
} else if (baud_rate == 75) {
D1 = (interleave_setting <= 1) ? 7 : 5;
D2 = 5;
} else {
throw std::invalid_argument("Invalid baud rate for generateProbeData");
}
// Generate the known symbol patterns D1 and D2, repeated twice
std::vector<uint8_t> d1_pattern = mapChannelSymbolToTribitPattern(D1, true); std::vector<uint8_t> d1_pattern = mapChannelSymbolToTribitPattern(D1, true);
std::vector<uint8_t> d2_pattern = mapChannelSymbolToTribitPattern(D2, true);
probe_data.insert(probe_data.end(), d1_pattern.begin(), d1_pattern.end()); probe_data.insert(probe_data.end(), d1_pattern.begin(), d1_pattern.end());
// Fill the remaining symbols with zeros if necessary
if (probe_data.size() < known_data_block_size) {
probe_data.resize(known_data_block_size, 0x00);
}
}
// If the current frame is the last frame, set probe data to D2 pattern
else if (current_frame == total_frames - 1) {
std::vector<uint8_t> d2_pattern = mapChannelSymbolToTribitPattern(D2, true);
probe_data.insert(probe_data.end(), d2_pattern.begin(), d2_pattern.end()); probe_data.insert(probe_data.end(), d2_pattern.begin(), d2_pattern.end());
// Fill the remaining symbols with zeros if necessary
if (probe_data.size() < known_data_block_size) {
probe_data.resize(known_data_block_size, 0x00);
}
} }
return probe_data; return probe_data;

View File

@ -10,7 +10,7 @@
class PSKModulator { class PSKModulator {
public: public:
PSKModulator(double baud_rate, double sample_rate, double energy_per_bit, bool is_frequency_hopping) PSKModulator(double baud_rate, double sample_rate, bool is_frequency_hopping)
: sample_rate(sample_rate), carrier_freq(1800), phase(0.0) { : sample_rate(sample_rate), carrier_freq(1800), phase(0.0) {
initializeSymbolMap(); initializeSymbolMap();
symbol_rate = 2400; // Fixed symbol rate as per specification (2400 symbols per second) symbol_rate = 2400; // Fixed symbol rate as per specification (2400 symbols per second)

View File

@ -13,18 +13,18 @@ int main() {
std::vector<uint8_t> sample_data(sample_string.begin(), sample_string.end()); std::vector<uint8_t> sample_data(sample_string.begin(), sample_string.end());
// Convert sample data to a BitStream object // Convert sample data to a BitStream object
BitStream bitstream(sample_data, sample_data.size() * 8); BitStream input_data(sample_data, sample_data.size() * 8);
// Configuration for modem // Configuration for modem
size_t baud_rate = 150; size_t baud_rate = 2400;
bool is_voice = false; // False indicates data mode bool is_voice = false; // False indicates data mode
bool is_frequency_hopping = false; // Fixed frequency operation bool is_frequency_hopping = false; // Fixed frequency operation
size_t interleave_setting = 2; // Short interleave size_t interleave_setting = 1; // Short interleave
// Create ModemController instance // Create ModemController instance
ModemController modem(baud_rate, is_voice, is_frequency_hopping, interleave_setting, bitstream); ModemController modem(baud_rate, is_voice, is_frequency_hopping, interleave_setting, input_data);
const char* file_name = "modulated_signal_150bps_longinterleave.wav"; const char* file_name = "modulated_signal_2400bps_voice.wav";
// Perform transmit operation to generate modulated signal // Perform transmit operation to generate modulated signal
std::vector<int16_t> modulated_signal = modem.transmit(); std::vector<int16_t> modulated_signal = modem.transmit();