|
- // Copyright 2013 Olivier Gillet.
- //
- // Author: Olivier Gillet (ol.gillet@gmail.com)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- // See http://creativecommons.org/licenses/MIT/ for more information.
-
- #include "stm_audio_bootloader/qpsk/demodulator.h"
-
- #include <cstring>
-
- #define abs(x) (x < 0 ? -x : x)
-
- namespace stm_audio_bootloader {
-
- const uint16_t kObservationWindow = 4096;
- const uint32_t kPowerThreshold = 30;
- const uint16_t kNumSkippedZeroSymbols = 32;
- const uint16_t kNumLockedTemplateSymbols = 4;
-
- int16_t lut_sine[] = {
- 0, 50, 100, 150, 200, 250, 300, 350, 399,
- 448, 497, 546, 594, 642, 689, 737, 783, 829,
- 875, 920, 965, 1009, 1052, 1095, 1137, 1179, 1219,
- 1259, 1299, 1337, 1375, 1412, 1448, 1483, 1517, 1550,
- 1583, 1614, 1644, 1674, 1702, 1730, 1756, 1781, 1806,
- 1829, 1851, 1872, 1892, 1910, 1928, 1944, 1959, 1973,
- 1986, 1998, 2008, 2017, 2025, 2032, 2038, 2042, 2045,
- 2047, 2048, 2047, 2045, 2042, 2038, 2032, 2025, 2017,
- 2008, 1998, 1986, 1973, 1959, 1944, 1928, 1910, 1892,
- 1872, 1851, 1829, 1806, 1781, 1756, 1730, 1702, 1674,
- 1644, 1614, 1583, 1550, 1517, 1483, 1448, 1412, 1375,
- 1337, 1299, 1259, 1219, 1179, 1137, 1095, 1052, 1009,
- 965, 920, 875, 829, 783, 737, 689, 642, 594,
- 546, 497, 448, 399, 350, 300, 250, 200, 150,
- 100, 50, 0, -50, -100, -150, -200, -250, -300,
- -350, -399, -448, -497, -546, -594, -642, -689, -737,
- -783, -829, -875, -920, -965, -1009, -1052, -1095, -1137,
- -1179, -1219, -1259, -1299, -1337, -1375, -1412, -1448, -1483,
- -1517, -1550, -1583, -1614, -1644, -1674, -1702, -1730, -1756,
- -1781, -1806, -1829, -1851, -1872, -1892, -1910, -1928, -1944,
- -1959, -1973, -1986, -1998, -2008, -2017, -2025, -2032, -2038,
- -2042, -2045, -2047, -2048, -2047, -2045, -2042, -2038, -2032,
- -2025, -2017, -2008, -1998, -1986, -1973, -1959, -1944, -1928,
- -1910, -1892, -1872, -1851, -1829, -1806, -1781, -1756, -1730,
- -1702, -1674, -1644, -1614, -1583, -1550, -1517, -1483, -1448,
- -1412, -1375, -1337, -1299, -1259, -1219, -1179, -1137, -1095,
- -1052, -1009, -965, -920, -875, -829, -783, -737, -689,
- -642, -594, -546, -497, -448, -399, -350, -300, -250,
- -200, -150, -100, -50, 0
- };
-
- const int16_t filter_coefficients_3_7[] = {
- -20, 40, -55, 64, 255, 64, -55, 40
- };
-
- const int16_t filter_coefficients_4_7[] = {
- -21, -51, 123, 255, 123, -51, -21
- };
-
- const int16_t filter_coefficients_6_7[] = {
- -70, 25, 180, 255, 180, 25, -70
- };
-
- const int16_t filter_coefficients_8_7[] = {
- -35, 85, 205, 255, 205, 85, -35
- };
-
- const int16_t filter_coefficients_12_7[] = {
- 45, 149, 227, 255, 227, 149, 45
- };
-
- const int16_t filter_coefficients_16_7[] = {
- 0, 98, 180, 236, 255, 236, 180, 98
- };
-
- const int16_t* const cutoff_table[] = {
- NULL,
- NULL,
- NULL,
- filter_coefficients_3_7,
- filter_coefficients_4_7,
- NULL,
- filter_coefficients_6_7,
- NULL,
- filter_coefficients_8_7,
- NULL,
- NULL,
- NULL,
- filter_coefficients_12_7,
- NULL,
- NULL,
- NULL,
- filter_coefficients_16_7
- };
-
- void Demodulator::Init(
- uint32_t phase_increment,
- uint16_t cutoff_reciprocal,
- uint16_t symbol_duration) {
- for (uint16_t i = 0; i < kFilterSize; ++i) {
- filter_coefficients_[i] = cutoff_table[cutoff_reciprocal][i];
- }
- symbol_duration_ = symbol_duration;
- phase_increment_ = phase_increment;
- dc_offset_ = 2048 << 8;
-
- samples_.Init();
- symbols_.Init();
-
- Reset();
- }
-
- void Demodulator::Reset() {
- memset(q_taps_, 0, sizeof(q_taps_));
- memset(i_taps_, 0, sizeof(i_taps_));
- memset(q_history_, 0, sizeof(q_history_));
- memset(i_history_, 0, sizeof(i_history_));
- phase_ = 0;
- phase_error_ = 0;
- decision_counter_ = 0;
- history_ptr_ = 0;
- decision_counter_ = symbol_duration_;
- }
-
- int16_t Demodulator::DecideSymbol(bool adjust_timing) {
- int32_t q_acc = 0;
- int32_t i_acc = 0;
-
- // Summation sets for decision
- //
- // Present ---------------------------------------> Past
- //
- // ========================================= Early
- // ========================================= On time
- // ========================================= Late
- // ===== Head
- // ===== Head 2
- // ===== Tail
- // ===== Tail 2
- //
- uint16_t now = history_ptr_ + kHistorySize;
- for (uint16_t i = 1; i < symbol_duration_ - 1; ++i) {
- uint16_t ptr = (now - i) & kHistoryMask;
- q_acc += q_history_[ptr];
- i_acc += i_history_[ptr];
- }
-
- if (adjust_timing) {
- uint16_t head_ptr = history_ptr_;
- uint16_t head_2_ptr = (now - 1) & kHistoryMask;
- uint16_t tail_ptr = (now - symbol_duration_ + 2) & kHistoryMask;
- uint16_t tail_2_ptr = (now - symbol_duration_ + 1) & kHistoryMask;
-
- int32_t q_acc_late = q_history_[head_ptr] + q_acc - q_history_[tail_ptr];
- int32_t i_acc_late = i_history_[head_ptr] + i_acc - q_history_[tail_ptr];
- int32_t q_acc_early = q_acc + q_history_[tail_2_ptr] - q_history_[head_2_ptr];
- int32_t i_acc_early = i_acc + i_history_[tail_2_ptr] - i_history_[head_2_ptr];
-
- uint32_t late_strength = abs(q_acc_late) + abs(i_acc_late);
- uint32_t on_time_strength = abs(q_acc) + abs(i_acc);
- uint32_t early_strength = abs(q_acc_early) + abs(i_acc_early);
-
- if (late_strength > ((5 * on_time_strength) >> 2)) {
- q_acc = q_acc_late;
- i_acc = i_acc_late;
- ++decision_counter_;
- } else if (early_strength > ((5 * on_time_strength) >> 2)) {
- q_acc = q_acc_early;
- i_acc = i_acc_early;
- --decision_counter_;
- }
- }
-
- return (i_acc < 0 ? 0 : 1) + (q_acc < 0 ? 0 : 2);
- }
-
- void Demodulator::EstimateDcComponent() {
- while (samples_.readable()) {
- int32_t sample = samples_.ImmediateRead() - (dc_offset_ >> 8);
- dc_offset_ += sample;
- ++skipped_samples_;
- if (skipped_samples_ >= kObservationWindow) {
- skipped_samples_ = 0;
- power_ = 0;
- state_ = DEMODULATOR_STATE_ESTIMATE_POWER;
- break;
- }
- }
- }
-
- void Demodulator::EstimatePower() {
- while (samples_.readable()) {
- int32_t sample = samples_.ImmediateRead() - (dc_offset_ >> 8);
- dc_offset_ += sample;
-
- power_ += sample * sample >> 4;
-
- ++skipped_samples_;
- if (skipped_samples_ >= kObservationWindow) {
- uint32_t detected_power = power_ / kObservationWindow;
- skipped_samples_ = 0;
- power_ = 0;
- if (detected_power >= kPowerThreshold) {
- state_ = DEMODULATOR_STATE_CARRIER_SYNC;
- break;
- }
- }
- }
- }
-
- void Demodulator::Demodulate() {
- while (samples_.readable()) {
- // Remove DC offset.
- int32_t sample = samples_.ImmediateRead() - (dc_offset_ >> 8);
- dc_offset_ += sample;
- phase_ += phase_increment_;
-
- // Demodulate.
- int32_t i_osc = lut_sine[((phase_ >> 24) + 64) & 0xff];
- int32_t q_osc = lut_sine[phase_ >> 24];
- i_taps_[0] = (sample * -i_osc) >> 8;
- q_taps_[0] = (sample * q_osc) >> 8;
-
- int32_t i = 0;
- int32_t q = 0;
-
- // Carrier rejection filter.
- for (uint16_t j = 0; j < kFilterSize; ++j) {
- i += i_taps_[j] * filter_coefficients_[j];
- q += q_taps_[j] * filter_coefficients_[j];
- }
- for (uint16_t j = kFilterSize - 1; j > 0; --j) {
- i_taps_[j] = i_taps_[j - 1];
- q_taps_[j] = q_taps_[j - 1];
- }
-
- // PLL to lock onto the carrier.
- int16_t i_sign = i < 0 ? -1 : 1;
- int16_t q_sign = q < 0 ? -1 : 1;
- int32_t phase_error = i_sign * q - q_sign * i;
- if (state_ == DEMODULATOR_STATE_CARRIER_SYNC) {
- phase_error = i - q; // Lock to (-1 -1) during sync phase.
- }
- phase_error_ = (3 * phase_error_ + phase_error) >> 2;
- phase_ += phase_error_ << 3;
- phase_increment_ += (phase_error_ >> 1);
-
- // Store in history buffer (for detection).
- q_history_[history_ptr_] = q >> 8;
- i_history_[history_ptr_] = i >> 8;
- history_ptr_ = (history_ptr_ + 1) & kHistoryMask;
-
- --decision_counter_;
- if (!decision_counter_) {
- decision_counter_ = symbol_duration_;
- // In carrier sync mode, we just let the PLL stabilize until we
- // consistently decode a string of 0s.
- switch (state_) {
- case DEMODULATOR_STATE_CARRIER_SYNC:
- if (DecideSymbol(false) == 0) {
- ++skipped_symbols_;
- if (skipped_symbols_ == kNumSkippedZeroSymbols) {
- SyncDecision();
- }
- }
- break;
-
- case DEMODULATOR_STATE_DECISION_SYNC:
- symbols_.Overwrite(4);
- break;
-
- case DEMODULATOR_STATE_OK:
- symbols_.Overwrite(DecideSymbol(true));
- break;
-
- default:
- break;
- }
- }
-
- if (state_ == DEMODULATOR_STATE_DECISION_SYNC) {
- // Match the demodulated Q/I channels with the shape of the alignment
- // sequence.
- int32_t correlation = 0;
- uint16_t ptr = history_ptr_ + kHistorySize;
- for (uint16_t k = 0; k < 2 * symbol_duration_; ++k) {
- int32_t expected_q = k < symbol_duration_ ? -1 : 1;
- int32_t expected_i = -expected_q;
- correlation += q_history_[(ptr - k) & kHistoryMask] * expected_q;
- correlation += i_history_[(ptr - k) & kHistoryMask] * expected_i;
- }
- if (correlation > correlation_maximum_) {
- correlation_maximum_ = correlation;
- }
- if (correlation < 0) {
- // Reset the peak detector at each valley in the detection function,
- // so that we can detect several consecutive peaks.
- correlation_maximum_ = 0;
- }
-
- // Detect a local maximum in the output of the correlator.
- ptr = history_ptr_ + kHistorySize - (symbol_duration_ >> 1);
- if (correlation_history_[0] > correlation &&
- correlation_history_[0] > correlation_history_[1] &&
- correlation_history_[0] == correlation_maximum_ &&
- q_history_[ptr & kHistoryMask] < 0 &&
- i_history_[ptr & kHistoryMask] > 0) {
- ++skipped_symbols_;
- if (skipped_symbols_ == kNumLockedTemplateSymbols) {
- state_ = DEMODULATOR_STATE_OK;
- }
- decision_counter_ = symbol_duration_ - 1;
- }
-
- correlation_history_[1] = correlation_history_[0];
- correlation_history_[0] = correlation;
- }
- }
- }
-
- } // namespace stm_audio_bootloader
|