Attempting to use an external library to handle bitstreams. Isn't going the grreatest.

This commit is contained in:
2024-10-11 15:41:46 -04:00
parent b9502ebe86
commit 170fdddcf0
30 changed files with 3239 additions and 334 deletions
+57 -35
View File
@@ -5,7 +5,7 @@
#include <stdexcept>
#include <vector>
#include "bitstream.h"
#include "bitstream/bitstream.h"
/**
* @class FECEncoder
@@ -30,26 +30,30 @@ public:
* @param data The input BitStream to be encoded.
* @return The encoded BitStream.
*/
BitStream encode(const BitStream& data) {
BitStream input_data(data);
BitStream output_data;
void encode(bitstream::growing_bit_writer<std::vector<uint8_t>>& output_data, bitstream::fixed_bit_reader& input_data) {
std::vector<uint8_t> intermediate_buffer;
bitstream::growing_bit_writer<std::vector<uint8_t>> intermediate_data(intermediate_buffer);
while (input_data.get_remaining_bits() > 0) {
uint32_t bit;
input_data.serialize_bits(bit, 1);
while (input_data.hasNext()) {
uint8_t bit = input_data.getNextBit();
// Shift the input bit into the shift register
shift_register = ((shift_register << 1) | bit) & 0x7F;
// Calculate T1 and T2 using the generator polynomials
uint8_t t1 = calculateT1();
uint8_t t2 = calculateT2();
uint32_t t1 = calculateT1();
uint32_t t2 = calculateT2();
// Append T1 and T2 to the encoded data
output_data.putBit(t1);
output_data.putBit(t2);
intermediate_data.serialize_bits(t1, 1);
intermediate_data.serialize_bits(t2, 1);
}
bitstream::fixed_bit_reader intermediate_reader(intermediate_buffer.data(), intermediate_data.get_num_bits_serialized());
// Apply repetition or puncturing based on baud rate and operation mode
return adjustRate(output_data);
return adjustRate(output_data, intermediate_reader);
}
private:
@@ -62,7 +66,9 @@ private:
* @return The calculated T1 bit.
*/
uint8_t calculateT1() {
return (shift_register >> 6) ^ ((shift_register >> 4) & 0x01) ^ ((shift_register >> 3) & 0x01) ^ ((shift_register >> 1) & 0x01) ^ (shift_register & 0x01);
return (shift_register >> 6) ^ ((shift_register >> 4) & 0x01) ^
((shift_register >> 3) & 0x01) ^ ((shift_register >> 1) & 0x01) ^
(shift_register & 0x01);
}
/**
@@ -70,7 +76,9 @@ private:
* @return The calculated T2 bit.
*/
uint8_t calculateT2() {
return (shift_register >> 6) ^ ((shift_register >> 5) & 0x01) ^ ((shift_register >> 4) & 0x01) ^ ((shift_register >> 1) & 0x01) ^ (shift_register & 0x01);
return (shift_register >> 6) ^ ((shift_register >> 5) & 0x01) ^
((shift_register >> 4) & 0x01) ^ ((shift_register >> 1) & 0x01) ^
(shift_register & 0x01);
}
/**
@@ -78,32 +86,46 @@ private:
* @param encoded_data The encoded BitStream to be adjusted.
* @return The adjusted BitStream.
*/
BitStream adjustRate(const BitStream& encoded_data) {
BitStream adjusted_data;
void adjustRate(bitstream::growing_bit_writer<std::vector<uint8_t>>& adjusted_data, bitstream::fixed_bit_reader& encoded_data) {
size_t repetition_factor = getRepetitionFactor();
if ((baud_rate == 300 || baud_rate == 150 || baud_rate == 75) && is_frequency_hopping) {
// Repetition for frequency-hopping operation at lower baud rates
size_t repetition_factor = (baud_rate == 300) ? 2 : (baud_rate == 150) ? 4 : 8;
for (size_t i = 0; i < encoded_data.getMaxBitIndex(); i += 2) {
for (size_t j = 0; j < repetition_factor; j++) {
adjusted_data.putBit(encoded_data.getBitVal(i));
adjusted_data.putBit(encoded_data.getBitVal(i + 1));
}
if (repetition_factor == 1) {
while (encoded_data.get_remaining_bits() > 0) {
uint32_t bit;
encoded_data.serialize_bits(bit, 1);
adjusted_data.serialize_bits(bit, 1);
}
} else if ((baud_rate == 300 || baud_rate == 150) && !is_frequency_hopping) {
// Repetition for fixed-frequency operation at lower baud rates
size_t repetition_factor = (baud_rate == 300) ? 2 : 4;
for (size_t i = 0; i < encoded_data.getMaxBitIndex(); i += 2) {
for (size_t j = 0; j < repetition_factor; j++) {
adjusted_data.putBit(encoded_data.getBitVal(i));
adjusted_data.putBit(encoded_data.getBitVal(i + 1));
}
}
} else {
adjusted_data = encoded_data;
return;
}
return adjusted_data;
while (encoded_data.get_remaining_bits() >= 2) {
uint32_t t1, t2;
encoded_data.serialize_bits(t1, 1);
encoded_data.serialize_bits(t2, 1);
for (size_t j = 0; j < repetition_factor; j++) {
adjusted_data.serialize_bits(t1, 1);
adjusted_data.serialize_bits(t2, 1);
}
}
}
size_t getRepetitionFactor() const {
if (is_frequency_hopping) {
switch (baud_rate) {
case 300: return 2;
case 150: return 4;
case 75: return 8;
default: return 1;
}
} else {
switch (baud_rate) {
case 300: return 2;
case 150: return 4;
default: return 1;
}
}
}
};
+63 -45
View File
@@ -6,7 +6,7 @@
#include <stdexcept>
#include <vector>
#include "bitstream.h"
#include "bitstream/bitstream.h"
/**
* @class Interleaver
@@ -34,30 +34,43 @@ public:
* @param input_data The input BitStream to be interleaved.
* @return A new BitStream containing the interleaved data.
*/
std::vector<uint8_t> interleaveStream(const BitStream& input_data) {
BitStream data = input_data;
BitStream interleaved_data;
std::vector<uint8_t> interleaveStream(bitstream::fixed_bit_reader& input_data) {
std::vector<uint8_t> interleaved_buffer;
bitstream::growing_bit_writer<std::vector<uint8_t>> interleaved_data(interleaved_buffer);
size_t chunk_size = rows * columns;
size_t input_index = 0;
while (input_index < data.getMaxBitIndex()) {
size_t end_index = std::min(input_index + chunk_size, data.getMaxBitIndex());
BitStream chunk = data.getSubStream(input_index, end_index);
if (chunk.getMaxBitIndex() > rows * columns) {
throw std::invalid_argument("Input data exceeds interleaver matrix size in loadChunk()");
std::vector<uint8_t> chunk_data((chunk_size + 7) / 8, 0);
while (input_data.get_remaining_bits() >= chunk_size) {
std::fill(chunk_data.begin(), chunk_data.end(), 0);
if (!input_data.serialize_bytes(chunk_data.data(), chunk_size)) {
throw std::runtime_error("Failed to serialize chunk from input data");
}
BitStream interleaved_chunk = interleaveChunk(chunk);
interleaved_data += interleaved_chunk;
input_index = end_index;
bitstream::fixed_bit_reader chunk_reader(chunk_data.data(), chunk_size);
interleaveChunk(interleaved_data, chunk_reader);
}
// Apply puncturing for 2400 bps in frequency-hopping mode (Rate 2/3)
if (baud_rate == 2400 && is_frequency_hopping) {
return applyPuncturing(interleaved_data);
std::vector<uint8_t> punctured_buffer;
bitstream::growing_bit_writer<std::vector<uint8_t>> punctured_writer(punctured_buffer);
bitstream::fixed_bit_reader interleaved_reader(interleaved_buffer.data(), interleaved_buffer.size() * 8);
applyPuncturing(punctured_writer, interleaved_reader);
interleaved_buffer = punctured_buffer;
}
std::vector<uint8_t> final_interleaved_data = groupSymbols(interleaved_data);
return final_interleaved_data;
bitstream::fixed_bit_reader final_reader(interleaved_buffer.data(), interleaved_buffer.size() * 8);
return groupSymbols(final_reader);
}
/**
@@ -65,7 +78,7 @@ public:
* @return The number of bits needed for a complete flush.
*/
size_t getFlushBits() const {
return rows * columns;
return (interleave_setting == 0) ? 0 : (rows * columns);
}
private:
@@ -84,21 +97,16 @@ private:
* @param input_data The input BitStream to be grouped into symbols.
* @return A vector of grouped symbols.
*/
std::vector<uint8_t> groupSymbols(BitStream& input_data) {
std::vector<uint8_t> groupSymbols(bitstream::fixed_bit_reader& input_data) {
std::vector<uint8_t> grouped_data;
size_t max_index = input_data.getMaxBitIndex();
size_t bits_per_symbol = (baud_rate == 2400) ? 3 : (baud_rate == 1200 || (baud_rate == 75 && !is_frequency_hopping)) ? 2 : 1;
size_t current_index = 0;
while (input_data.get_remaining_bits() >= bits_per_symbol) {
uint32_t symbol = 0;
while ((current_index + bits_per_symbol) < max_index) {
uint8_t symbol = 0;
input_data.serialize_bits(symbol, bits_per_symbol);
for (int i = 0; i < bits_per_symbol; i++) {
symbol = (symbol << 1) | input_data.getBitVal(current_index + i);
}
grouped_data.push_back(symbol);
current_index += bits_per_symbol;
grouped_data.push_back(static_cast<uint8_t>(symbol));
}
return grouped_data;
@@ -109,16 +117,16 @@ private:
* @param input_data The input BitStream chunk.
* @return A BitStream representing the interleaved chunk.
*/
BitStream interleaveChunk(const BitStream& input_data) {
void interleaveChunk(bitstream::growing_bit_writer<std::vector<uint8_t>>& interleaved_writer, bitstream::fixed_bit_reader& input_data) {
loadChunk(input_data);
return fetchChunk();
return fetchChunk(interleaved_writer);
}
/**
* @brief Loads bits from the input BitStream into the interleaver matrix.
* @param data The input BitStream to load.
*/
void loadChunk(const BitStream& data) {
void loadChunk(bitstream::fixed_bit_reader& data) {
size_t row = 0;
size_t col = 0;
size_t index = 0;
@@ -127,13 +135,19 @@ private:
// Load bits into the matrix
std::fill(matrix.begin(), matrix.end(), 0); // Clear previous data
while (index < data.getMaxBitIndex() && col < columns) {
while (data.get_remaining_bits() > 0 && col < columns) {
size_t matrix_idx = row * columns + col;
if (matrix_idx >= matrix.size()) {
throw std::out_of_range("Matrix index out of bounds in loadChunk()");
}
matrix[matrix_idx] = data.getBitVal(index++);
uint32_t bit = 0;
if (!data.serialize_bits(bit, 1)) {
throw std::runtime_error("Failed to read bit from chunk_reader in loadChunk()");
}
matrix[matrix_idx] = static_cast<uint8_t>(bit);
row = (row + row_increment) % rows;
if (row == 0) {
@@ -146,21 +160,23 @@ private:
* @brief Fetches bits from the interleaver matrix in the interleaved order.
* @return A BitStream containing the fetched interleaved data.
*/
BitStream fetchChunk() {
BitStream fetched_data;
void fetchChunk(bitstream::growing_bit_writer<std::vector<uint8_t>>& interleaved_writer) {
size_t row = 0;
size_t col = 0;
size_t column_decrement = (baud_rate == 75 && interleave_setting == 2) ? 7 : 17;
// Fetch bits from the matrix
while (fetched_data.getMaxBitIndex() < rows * columns) {
for (size_t i = 0; i < rows * columns; i++) {
size_t matrix_idx = row * columns + col;
if (matrix_idx >= matrix.size()) {
throw std::out_of_range("Matrix index out of bounds in fetchChunk()");
}
fetched_data.putBit(matrix[matrix_idx]);
uint32_t bit = static_cast<uint32_t>(matrix[matrix_idx]);
if (!interleaved_writer.serialize_bits(bit, 1)) {
throw std::runtime_error("Failed to write bit to interleaved_writer in fetchChunk()");
}
row++;
if (row == rows) {
@@ -170,8 +186,6 @@ private:
col = (col + columns - column_decrement) % columns;
}
}
return fetched_data;
}
@@ -210,14 +224,18 @@ private:
* @param interleaved_data The interleaved data to be punctured.
* @return A BitStream containing punctured data.
*/
BitStream applyPuncturing(const BitStream& interleaved_data) {
BitStream punctured_data;
for (size_t i = 0; i < interleaved_data.getMaxBitIndex(); i++) {
if ((i % 4) != 1) { // Skip every fourth bit (the second value of T2)
punctured_data.putBit(interleaved_data.getBitVal(i));
void applyPuncturing(bitstream::growing_bit_writer<std::vector<uint8_t>>& punctured_data, bitstream::fixed_bit_reader& interleaved_data) {
size_t bit_index = 0;
while (interleaved_data.get_remaining_bits() > 0) {
uint32_t bit = 0;
interleaved_data.serialize_bits(bit, 1);
if ((bit_index % 4) != 3) {
punctured_data.serialize_bits(bit, 1);
}
bit_index++;
}
return punctured_data;
}
};
+44 -34
View File
@@ -6,7 +6,7 @@
#include <memory>
#include <vector>
#include "bitstream.h"
#include "bitstream/bitstream.h"
#include "FECEncoder.h"
#include "Interleaver.h"
#include "MGDDecoder.h"
@@ -40,7 +40,7 @@ public:
* @param data The input data stream to be transmitted. The `is_voice` parameter controls whether the modem treats it as binary file data,
* or a binary stream from the MELPe (or other) voice codec.
*/
ModemController(const size_t _baud_rate, const bool _is_voice, const bool _is_frequency_hopping, const size_t _interleave_setting, BitStream _data)
ModemController(const size_t _baud_rate, const bool _is_voice, const bool _is_frequency_hopping, const size_t _interleave_setting)
: baud_rate(_baud_rate),
is_voice(_is_voice),
is_frequency_hopping(_is_frequency_hopping),
@@ -49,7 +49,6 @@ public:
scrambler(),
fec_encoder(baud_rate, is_frequency_hopping),
interleaver(baud_rate, interleave_setting, is_frequency_hopping),
input_data(std::move(_data)),
mgd_decoder(baud_rate, is_frequency_hopping),
modulator(baud_rate, 48000, 0.5, is_frequency_hopping) {}
@@ -58,36 +57,47 @@ public:
* @return The scrambled data ready for modulation.
* @note The modulated signal is generated internally but is intended to be handled externally.
*/
std::vector<int16_t> transmit() {
// Step 1: Append EOM Symbols
BitStream eom_appended_data = appendEOMSymbols(input_data);
std::vector<int16_t> transmit(bitstream::fixed_bit_reader& input_data) {
// Step 1: Append EOM Symbols using a uint32_t aligned output buffer
std::vector<uint8_t> output_buffer;
bitstream::growing_bit_writer<std::vector<uint8_t>> output_writer(output_buffer);
appendEOMSymbols(output_writer, input_data);
// Step 2: Handle Baud Rate Specific Encoding
std::vector<uint8_t> processed_data;
if (baud_rate == 4800) {
processed_data = splitTribitSymbols(eom_appended_data);
// For 4800 baud, perform tribit symbol splitting
bitstream::fixed_bit_reader eom_appended_reader(output_buffer.data(), output_writer.get_num_bits_serialized());
processed_data = splitTribitSymbols(eom_appended_reader);
} else {
// Step 2: FEC Encoding
BitStream fec_encoded_data = fec_encoder.encode(eom_appended_data);
// Step 3: FEC Encoding
bitstream::fixed_bit_reader eom_appended_reader(output_buffer.data(), output_writer.get_num_bits_serialized());
std::vector<uint8_t> fec_encoded_buffer;
bitstream::growing_bit_writer<std::vector<uint8_t>> fec_encoded_writer(fec_encoded_buffer);
fec_encoder.encode(fec_encoded_writer, eom_appended_reader);
// Step 3: Interleaving
processed_data = interleaver.interleaveStream(fec_encoded_data);
// Step 4: Interleaving
bitstream::fixed_bit_reader fec_encoded_reader(fec_encoded_buffer.data(), fec_encoded_writer.get_num_bits_serialized());
processed_data = interleaver.interleaveStream(fec_encoded_reader);
}
// Step 5: MGD Decoding
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 6: Symbol Formation (including sync preamble and scrambling)
std::vector<uint8_t> symbol_stream = symbol_formation.formSymbols(mgd_decoded_data);
// Step 7: Modulation
std::vector<int16_t> modulated_signal = modulator.modulate(symbol_stream);
return modulated_signal;
}
private:
size_t baud_rate; ///< The baud rate for the modem.
bool is_voice; ///< Indicates if the data being transmitted is voice.
bool is_frequency_hopping; ///< Indicates if frequency hopping is used.
BitStream input_data; ///< The input data stream.
size_t interleave_setting; ///< The interleave setting to be used.
size_t sample_rate;
@@ -106,38 +116,38 @@ private:
* the FEC encoder and interleaver matrices. The function calculates the number of flush bits required
* based on the FEC and interleaver settings.
*/
BitStream appendEOMSymbols(const BitStream& input_data) const {
BitStream eom_data = input_data;
void appendEOMSymbols(bitstream::growing_bit_writer<std::vector<uint8_t>>& output_data, bitstream::fixed_bit_reader& input_data) const {
while (input_data.get_num_bits_serialized() < input_data.get_total_bits()) {
uint32_t value;
uint32_t bits_to_read = std::min(32U, input_data.get_remaining_bits());
input_data.serialize_bits(value, bits_to_read);
output_data.serialize_bits(value, bits_to_read);
}
// Append the EOM sequence (4B65A5B2 in hexadecimal)
BitStream eom_sequence({0x4B, 0x65, 0xA5, 0xB2}, 32);
eom_data += eom_sequence;
uint32_t eom_sequence = 0x4B65A5B2;
output_data.serialize_bits(eom_sequence, 32);
// Append additional zeros to flush the FEC encoder and interleaver
size_t fec_flush_bits = 144; // FEC encoder flush bits
size_t interleave_flush_bits = interleaver.getFlushBits();
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)
total_flush_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);
eom_data += flush_bits;
return eom_data;
size_t current_bit_index = output_data.get_num_bits_serialized();
size_t alignment_bits_needed = (interleave_flush_bits - (current_bit_index + fec_flush_bits) % interleave_flush_bits) % interleave_flush_bits;
total_flush_bits += alignment_bits_needed;
}
std::vector<uint8_t> splitTribitSymbols(const BitStream& input_data) {
std::vector<uint8_t> splitTribitSymbols(bitstream::fixed_bit_reader& input_data) {
std::vector<uint8_t> return_vector;
size_t max_index = input_data.getMaxBitIndex();
size_t current_index = 0;
size_t total_bits = input_data.get_total_bits();
size_t num_bits_serialized = input_data.get_num_bits_serialized();
while (current_index + 2 < max_index) {
uint8_t symbol = 0;
for (int i = 0; i < 3; i++) {
symbol = (symbol << 1) | input_data.getBitVal(current_index + i);
}
return_vector.push_back(symbol);
current_index += 3;
while (num_bits_serialized + 3 <= total_bits) {
uint32_t symbol = 0;
input_data.serialize_bits(symbol, 3);
return_vector.push_back(static_cast<uint8_t>(symbol));
num_bits_serialized = input_data.get_num_bits_serialized();
}
return return_vector;