// Copyright 2020 Mobilinkd LLC. #pragma once #include #include #include #include #include #include #include #include namespace modemm17 { // The make_bitset stuff only works as expected in GCC10 and later. namespace detail { /** * This is the max value for the LLR based on size N. */ template constexpr size_t llr_limit() { return (1 << (N - 1)) - 1; } /** * There are (2^(N-1)-1) elements (E) per segment (e.g. N=4, E=7; N=3, E=3). * These contain the LLR values 1..E. There are 6 segments in the LLR map: * 1. (-Inf,-2] * 2. (-2, -1] * 3. (-1, 0] * 4. (0, 1] * 5. (1, 2] * 6. (2, Inf) * * Note the slight asymmetry. This is OK as we are dealing with floats and * it only matters to an epsilon of the float type. */ template constexpr size_t llr_size() { return llr_limit() * 6 + 1; } template constexpr std::array>, llr_size()> make_llr_map() { constexpr size_t size = llr_size(); std::array>, size> result; constexpr int8_t limit = llr_limit(); constexpr float inc = 1.0 / float(limit); int8_t i = limit; int8_t j = limit; // Output must be ordered by k, ascending. float k = -3.0 + inc; for (size_t index = 0; index != size; ++index) { auto& a = result[index]; std::get<0>(a) = k; std::get<0>(std::get<1>(a)) = i; std::get<1>(std::get<1>(a)) = j; if (k + 1.0 < 0) { j--; if (j == 0) j = -1; if (j < -limit) j = -limit; } else if (k - 1.0 < 0) { i--; if (i == 0) i = -1; if (i < -limit) i = -limit; } else { j++; if (j == 0) j = 1; if (j > limit) j = limit; } k += inc; } return result; } } // template // constexpr auto make_bitset(Bools&&...bools) // { // return detail::make_bitset(std::make_index_sequence(), // std::make_tuple(bool(bools)...)); // } inline int from_4fsk(int symbol) { // Convert a 4-FSK symbol to a pair of bits. switch (symbol) { case 1: return 0; case 3: return 1; case -1: return 2; case -3: return 3; default: abort(); } } template auto llr(float sample) { static auto symbol_map = detail::make_llr_map(); static constexpr float MAX_VALUE = 3.0; static constexpr float MIN_VALUE = -3.0; float s = std::min(MAX_VALUE, std::max(MIN_VALUE, sample)); auto it = std::lower_bound(symbol_map.begin(), symbol_map.end(), s, [](std::tuple> const& e, float s){ return std::get<0>(e) < s; }); if (it == symbol_map.end()) return std::get<1>(*symbol_map.rbegin()); return std::get<1>(*it); } template size_t depuncture( // FIXED: MSVC (MULTIPLE DEFINITIONS SAME TEMPLATE) const std::array& in, std::array& out, const std::array& p ) { size_t index = 0; size_t pindex = 0; size_t bit_count = 0; for (size_t i = 0; i != OUT1 && index < IN1; ++i) { if (!p[pindex++]) { out[i] = 0; bit_count++; } else { out[i] = in[index++]; } if (pindex == P) { pindex = 0; } } return bit_count; } template size_t puncture( // FIXED::MSVC (MULTIPLE DEFINITIONS OF THE SAME TEMPLATE) const std::array& in, std::array& out, const std::array& p ) { size_t index = 0; size_t pindex = 0; size_t bit_count = 0; for (size_t i = 0; i != IN0 && index != OUT0; ++i) { if (p[pindex++]) { out[index++] = in[i]; bit_count++; } if (pindex == P) pindex = 0; } return bit_count; } template constexpr bool get_bit_index( const std::array& input, size_t index ) { auto byte_index = index >> 3; assert(byte_index < N); auto bit_index = 7 - (index & 7); return (input[byte_index] & (1 << bit_index)) >> bit_index; } template void set_bit_index( std::array& input, size_t index ) { auto byte_index = index >> 3; assert(byte_index < N); auto bit_index = 7 - (index & 7); input[byte_index] |= (1 << bit_index); } template void reset_bit_index( std::array& input, size_t index ) { auto byte_index = index >> 3; assert(byte_index < N); auto bit_index = 7 - (index & 7); input[byte_index] &= ~(1 << bit_index); } template void assign_bit_index( std::array& input, size_t index, bool value ) { if (value) set_bit_index(input, index); else reset_bit_index(input, index); } /** * Sign-extend an n-bit value to a specific signed integer type. */ template constexpr T to_int(uint8_t v) { constexpr auto MAX_LOCAL_INPUT = (1 << (n - 1)); constexpr auto NEGATIVE_OFFSET = std::numeric_limits::type>::max() - (MAX_LOCAL_INPUT - 1); T r = v & (1 << (n - 1)) ? NEGATIVE_OFFSET : 0; return r + (v & (MAX_LOCAL_INPUT - 1)); } template constexpr auto to_byte_array(std::array in) { std::array out{}; out.fill(0); size_t i = 0; size_t b = 0; for (auto c : in) { out[i] |= (c << (7 - b)); if (++b == 8) { ++i; b = 0; } } return out; } template constexpr void to_byte_array( std::array in, std::array& out ) { size_t i = 0; size_t b = 0; uint8_t tmp = 0; for (auto c : in) { tmp |= (c << (7 - b)); if (++b == 8) { out[i] = tmp; tmp = 0; ++i; b = 0; } } if (i < out.size()) out[i] = tmp; } struct PRBS9 { static constexpr uint16_t MASK = 0x1FF; static constexpr uint8_t TAP_1 = 8; // Bit 9 static constexpr uint8_t TAP_2 = 4; // Bit 5 static constexpr uint8_t LOCK_COUNT = 18; // 18 consecutive good bits. static constexpr uint8_t UNLOCK_COUNT = 25; // bad bits in history required to unlock. uint16_t state = 1; bool synced = false; uint8_t sync_count = 0; uint32_t bit_count = 0; uint32_t err_count = 0; std::array history; size_t hist_count = 0; size_t hist_pos = 0; void count_errors(bool error) { bit_count += 1; hist_count -= (history[hist_pos >> 3] & (1 << (hist_pos & 7))) != 0; if (error) { err_count += 1; hist_count += 1; history[hist_pos >> 3] |= (1 << (hist_pos & 7)); if (hist_count >= UNLOCK_COUNT) synced = false; } else { history[hist_pos >> 3] &= ~(1 << (hist_pos & 7)); } if (++hist_pos == 128) hist_pos = 0; } // PRBS generator. bool generate() { bool result = ((state >> TAP_1) ^ (state >> TAP_2)) & 1; state = ((state << 1) | result) & MASK; return result; } // PRBS Syncronizer. Returns 0 if the bit matches the PRBS, otherwise 1. // When synchronizing the LFSR used in the PRBS, a single bad input bit will // result in 3 error bits being emitted. bool synchronize(bool bit) { bool result = (bit ^ (state >> TAP_1) ^ (state >> TAP_2)) & 1; state = ((state << 1) | bit) & MASK; if (result) { sync_count = 0; // error } else { if (++sync_count == LOCK_COUNT) { synced = true; bit_count += LOCK_COUNT; history.fill(0); hist_count = 0; hist_pos = 0; sync_count = 0; } } return result; } // PRBS validator. Returns 0 if the bit matches the PRBS, otherwise 1. // The results are only valid when sync() returns true; bool validate(bool bit) { bool result; if (!synced) { result = synchronize(bit); } else { // PRBS is now free-running. result = bit ^ generate(); count_errors(result); } return result; } bool sync() const { return synced; } uint32_t errors() const { assert(synced); return err_count; } uint32_t bits() const { assert(synced); return bit_count; } // Reset the state. void reset() { state = 1; synced = false; sync_count = 0; bit_count = 0; err_count = 0; history.fill(0); hist_count = 0; hist_pos = 0; } }; template< class T > constexpr int popcount( T x ) noexcept { int count = 0; while (x) { count += x & 1; x >>= 1; } return count; } } // modemm17