MIL-STD-188-110C/include/encoder/ModemController.h

160 lines
7.4 KiB
C++

#ifndef MODEM_CONTROLLER_H
#define MODEM_CONTROLLER_H
#include <cstdint>
#include <limits>
#include <memory>
#include <vector>
#include "bitstream/bitstream.h"
#include "FECEncoder.h"
#include "Interleaver.h"
#include "MGDDecoder.h"
#include "PSKModulator.h"
#include "Scrambler.h"
#include "SymbolFormation.h"
/**
* @brief Clamps an integer value to the range of int16_t.
* @param x The value to be clamped.
* @return The clamped value.
*/
constexpr int16_t clamp(int16_t x) {
constexpr int16_t max_val = std::numeric_limits<int16_t>::max();
constexpr int16_t min_val = std::numeric_limits<int16_t>::min();
return (x > max_val) ? max_val : (x < min_val) ? min_val : x;
}
/**
* @class ModemController
* @brief Controls the modulation process for transmitting data using FEC encoding, interleaving, scrambling, and PSK modulation.
*/
class ModemController {
public:
/**
* @brief Constructs a ModemController object.
* @param baud_rate The baud rate for the modem.
* @param is_voice Indicates if the data being transmitted is voice.
* @param is_frequency_hopping Indicates if frequency hopping is used.
* @param interleave_setting The interleave setting to be used.
* @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)
: baud_rate(_baud_rate),
is_voice(_is_voice),
is_frequency_hopping(_is_frequency_hopping),
interleave_setting(_interleave_setting),
symbol_formation(baud_rate, interleave_setting, is_voice, is_frequency_hopping),
scrambler(),
fec_encoder(baud_rate, is_frequency_hopping),
interleaver(baud_rate, interleave_setting, is_frequency_hopping),
mgd_decoder(baud_rate, is_frequency_hopping),
modulator(baud_rate, 48000, 0.5, is_frequency_hopping) {}
/**
* @brief Transmits the input data by processing it through different phases like FEC encoding, interleaving, symbol formation, scrambling, and modulation.
* @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(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) {
// 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 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 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 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.
size_t interleave_setting; ///< The interleave setting to be used.
size_t sample_rate;
SymbolFormation symbol_formation; ///< Symbol formation instance to form symbols from data.
Scrambler scrambler; ///< Scrambler instance for scrambling the data.
FECEncoder fec_encoder; ///< FEC encoder instance for encoding the data.
Interleaver interleaver; ///< Interleaver instance for interleaving the data.
PSKModulator modulator; ///< PSK modulator instance for modulating the data.
MGDDecoder mgd_decoder; ///< MGD decoder
/**
* @brief Appends the EOM symbols to the input data and flushes the FEC encoder and interleaver.
* @param input_data The input data to which the EOM symbols are appended.
* @return The input data with EOM symbols and flush bits appended.
* @details The EOM sequence (4B65A5B2 in hexadecimal) is appended to the data, followed by enough zero bits to flush
* the FEC encoder and interleaver matrices. The function calculates the number of flush bits required
* based on the FEC and interleaver settings.
*/
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)
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);
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(bitstream::fixed_bit_reader& input_data) {
std::vector<uint8_t> return_vector;
size_t total_bits = input_data.get_total_bits();
size_t num_bits_serialized = input_data.get_num_bits_serialized();
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;
}
};
#endif