diff --git a/include/modulation/PSKModulator.h b/include/modulation/PSKModulator.h index 9ce3cf1..f8e6697 100644 --- a/include/modulation/PSKModulator.h +++ b/include/modulation/PSKModulator.h @@ -22,8 +22,9 @@ static constexpr double SCALE_FACTOR = 32767.0; class PSKModulator { public: PSKModulator(const double _sample_rate, const bool _is_frequency_hopping, const size_t _num_taps) - : sample_rate(validateSampleRate(_sample_rate)), gain(1.0/sqrt(2.0)), is_frequency_hopping(_is_frequency_hopping), samples_per_symbol(static_cast(sample_rate / SYMBOL_RATE)), phase_detector(symbolMap), srrc_filter(48, _sample_rate, SYMBOL_RATE, ROLLOFF_FACTOR) { + : sample_rate(validateSampleRate(_sample_rate)), gain(1.0/sqrt(2.0)), is_frequency_hopping(_is_frequency_hopping), samples_per_symbol(static_cast(sample_rate / SYMBOL_RATE)), srrc_filter(48, _sample_rate, SYMBOL_RATE, ROLLOFF_FACTOR) { initializeSymbolMap(); + phase_detector = PhaseDetector(symbolMap); } std::vector modulate(const std::vector& symbols) { @@ -78,7 +79,7 @@ public: std::vector demodulate(const std::vector passband_signal) { // Carrier recovery. initialize the Costas loop. - CostasLoop costas_loop(sample_rate, symbolMap); + CostasLoop costas_loop(CARRIER_FREQ, sample_rate, symbolMap, 5.0); // Convert passband signal to doubles. std::vector normalized_passband(passband_signal.size()); @@ -91,8 +92,17 @@ public: // Phase detection and symbol formation std::vector baseband_symbols; - for (size_t i = 0; i < baseband_IQ.size(); i++) { - baseband_symbols.emplace_back(phase_detector.getSymbol(baseband_IQ[i])); + size_t samples_per_symbol = sample_rate / SYMBOL_RATE; + + for (size_t i = 0; i < baseband_IQ.size(); i += samples_per_symbol) { + std::complex symbol_avg(0.0, 0.0); + for (size_t j = 0; j < samples_per_symbol; ++j) { + symbol_avg += baseband_IQ[i + j]; + } + symbol_avg /= static_cast(samples_per_symbol); + + // Detect symbol from averaged signal + baseband_symbols.emplace_back(phase_detector.getSymbol(symbol_avg)); } return baseband_symbols; diff --git a/include/utils/costasloop.h b/include/utils/costasloop.h index 3be7334..aeff5b7 100644 --- a/include/utils/costasloop.h +++ b/include/utils/costasloop.h @@ -7,6 +7,7 @@ class PhaseDetector { public: + PhaseDetector() {} PhaseDetector(const std::vector>& _symbolMap) : symbolMap(_symbolMap) {} uint8_t getSymbol(const std::complex& input) { @@ -38,8 +39,8 @@ private: class CostasLoop { public: - CostasLoop(const double _sample_rate, const std::vector>& _symbolMap) - : sample_rate(_sample_rate), k_factor(-5 / _sample_rate), + CostasLoop(const double _carrier_freq, const double _sample_rate, const std::vector>& _symbolMap, const double _vco_gain) + : carrier_freq(_carrier_freq), sample_rate(_sample_rate), vco_gain(_vco_gain), k_factor(-1 / (_sample_rate * _vco_gain)), prev_in_iir(0), prev_out_iir(0), prev_in_vco(0), feedback(1.0, 0.0), error_total(0), out_iir_total(0), in_vco_total(0), srrc_filter(SRRCFilter(48, _sample_rate, 2400, 0.35)) {} @@ -80,15 +81,19 @@ public: prev_in_vco = in_vco; // Generate feedback signal for next iteration - double feedback_real = std::cos(k_factor * in_vco); - double feedback_imag = -std::sin(k_factor * in_vco); + double feedback_real = std::cos(current_phase); + double feedback_imag = -std::sin(current_phase); feedback = std::complex(feedback_real, feedback_imag); + + current_phase += 2 * M_PI * carrier_freq / sample_rate + k_factor * in_vco; + if (current_phase > 2 * M_PI) current_phase -= 2 * M_PI; } return output_signal; } private: + double carrier_freq; double sample_rate; double k_factor; double prev_in_iir; @@ -99,6 +104,7 @@ private: double out_iir_total; double in_vco_total; SRRCFilter srrc_filter; + double vco_gain; std::complex limiter(const std::complex& sample) const { double limited_I = std::clamp(sample.real(), -1.0, 1.0); diff --git a/main.cpp b/main.cpp index a75b919..f9ba598 100644 --- a/main.cpp +++ b/main.cpp @@ -6,6 +6,7 @@ #include #include "ModemController.h" +#include "PSKModulator.h" int main() { // Sample test data @@ -16,37 +17,40 @@ int main() { BitStream input_data(sample_data, sample_data.size() * 8); // Configuration for modem - size_t baud_rate = 300; + size_t baud_rate = 150; bool is_voice = false; // False indicates data mode bool is_frequency_hopping = false; // Fixed frequency operation size_t interleave_setting = 1; // Short interleave // Create ModemController instance ModemController modem(baud_rate, is_voice, is_frequency_hopping, interleave_setting, input_data); + PSKModulator modulator(48000, is_frequency_hopping, 48); - const char* file_name = "modulated_signal_600bps_shortinterleave.wav"; + const char* file_name = "modulated_signal_75bps_shortinterleave.wav"; // Perform transmit operation to generate modulated signal std::vector modulated_signal = modem.transmit(); - // Output modulated signal to a WAV file using libsndfile - SF_INFO sfinfo; - sfinfo.channels = 1; - sfinfo.samplerate = 48000; - sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + std::vector demodulated_symbols = modulator.demodulate(modulated_signal); - SNDFILE* sndfile = sf_open(file_name, SFM_WRITE, &sfinfo); - if (sndfile == nullptr) { - std::cerr << "Unable to open WAV file for writing modulated signal: " << sf_strerror(sndfile) << "\n"; - return 1; - } - - sf_write_short(sndfile, modulated_signal.data(), modulated_signal.size()); - sf_close(sndfile); - std::cout << "Modulated signal written to " << file_name << '\n'; - - // Success message - std::cout << "Modem test completed successfully.\n"; + //// Output modulated signal to a WAV file using libsndfile + //SF_INFO sfinfo; + //sfinfo.channels = 1; + //sfinfo.samplerate = 48000; + //sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; +// + //SNDFILE* sndfile = sf_open(file_name, SFM_WRITE, &sfinfo); + //if (sndfile == nullptr) { + // std::cerr << "Unable to open WAV file for writing modulated signal: " << sf_strerror(sndfile) << "\n"; + // return 1; + //} +// + //sf_write_short(sndfile, modulated_signal.data(), modulated_signal.size()); + //sf_close(sndfile); + //std::cout << "Modulated signal written to " << file_name << '\n'; +// + //// Success message + //std::cout << "Modem test completed successfully.\n"; return 0; } \ No newline at end of file diff --git a/tests/PSKModulatorTests.cpp b/tests/PSKModulatorTests.cpp index bcbffe0..cfce492 100644 --- a/tests/PSKModulatorTests.cpp +++ b/tests/PSKModulatorTests.cpp @@ -26,14 +26,38 @@ TEST_F(PSKModulatorTest, ModulationOutputLength) { TEST_F(PSKModulatorTest, DemodulationOutput) { auto passband_signal = modulator.modulate(symbols); + + // Debug: Print modulated passband signal + std::cout << "Modulated Passband Signal: "; + for (const auto& sample : passband_signal) { + std::cout << sample << " "; + } + std::cout << std::endl; + auto decoded_symbols = modulator.demodulate(passband_signal); + // Debug: Print decoded symbols + std::cout << "Decoded Symbols: "; + for (const auto& symbol : decoded_symbols) { + std::cout << (int)symbol << " "; + } + std::cout << std::endl; + + // Debug: Print expected symbols + std::cout << "Expected Symbols: "; + for (const auto& symbol : symbols) { + std::cout << (int)symbol << " "; + } + std::cout << std::endl; + ASSERT_EQ(symbols.size(), decoded_symbols.size()); + for (size_t i = 0; i < symbols.size(); i++) { - EXPECT_EQ(symbols[i], decoded_symbols[i]); + EXPECT_EQ(symbols[i], decoded_symbols[i]) << " at index " << i; } } + TEST_F(PSKModulatorTest, InvalidSymbolInput) { std::vector invalid_symbols = {0, 8, 9};