|
|
|||
Warning, file /include/root/Math/LFSR.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // @(#)root/mathcore:$Id$ 0002 // Author: Fernando Hueso-González 04/08/2021 0003 0004 #ifndef ROOT_Math_LFSR 0005 #define ROOT_Math_LFSR 0006 0007 #include <array> 0008 #include <bitset> 0009 #include <cassert> 0010 #include <cstdint> 0011 #include <vector> 0012 #include <set> 0013 #include <cmath> 0014 #include <cstdint> // for std::uint16_t 0015 #include "TError.h" 0016 0017 /// Pseudo Random Binary Sequence (PRBS) generator namespace with functions based 0018 /// on linear feedback shift registers (LFSR) with a periodicity of 2^n-1 0019 /// 0020 /// @note It should NOT be used for general-purpose random number generation or any 0021 /// statistical study, for those cases see e.g. std::mt19937 instead. 0022 /// 0023 /// The goal is to generate binary bit sequences with the same algorithm as the ones usually implemented 0024 /// in electronic chips, so that the theoretically expected ones can be compared with the acquired sequences. 0025 /// 0026 /// The main ingredients of a PRBS generator are a monic polynomial of maximum degree \f$n\f$, with coefficients 0027 /// either 0 or 1, and a <a href="https://www.nayuki.io/page/galois-linear-feedback-shift-register">Galois</a> 0028 /// linear-feedback shift register with a non-zero seed. When the monic polynomial exponents are chosen appropriately, 0029 /// the period of the resulting bit sequence (0s and 1s) yields \f$2^n - 1\f$. 0030 /// 0031 /// @sa https://gist.github.com/mattbierner/d6d989bf26a7e54e7135, 0032 /// https://root.cern/doc/master/civetweb_8c_source.html#l06030, 0033 /// https://cryptography.fandom.com/wiki/Linear_feedback_shift_register, 0034 /// https://www3.advantest.com/documents/11348/33b24c8a-c8cb-40b8-a2a7-37515ba4abc8, 0035 /// https://www.reddit.com/r/askscience/comments/63a10q/for_prbs3_with_clock_input_on_each_gate_how_can/, 0036 /// https://es.mathworks.com/help/serdes/ref/prbs.html, https://metacpan.org/pod/Math::PRBS, 0037 /// https://ez.analog.com/data_converters/high-speed_adcs/f/q-a/545335/ad9689-pn9-and-pn23 0038 0039 namespace ROOT::Math::LFSR { 0040 0041 /** 0042 * @brief Generate the next pseudo-random bit using the current state of a linear feedback shift register (LFSR) and 0043 * update it 0044 * @tparam k the length of the LFSR, usually also the order of the monic polynomial PRBS-k (last exponent) 0045 * @tparam nTaps the number of taps 0046 * @param lfsr the current value of the LFSR. Passed by reference, it will be updated with the next value 0047 * @param taps the taps that will be XOR-ed to calculate the new bit. They are the exponents of the monic polynomial. 0048 * Ordering is unimportant. Note that an exponent E in the polynom maps to bit index E-1 in the LFSR. 0049 * @param left if true, the direction of the register shift is to the left <<, the newBit is set on lfsr at bit position 0050 * 0 (right). If false, shift is to the right and the newBit is stored at bit position (k-1) 0051 * @return the new random bit 0052 * @throw an exception is thrown if taps are out of the range [1, k] 0053 * @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register, 0054 * https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence 0055 */ 0056 template <size_t k, size_t nTaps> 0057 bool NextLFSR(std::bitset<k> &lfsr, std::array<std::uint16_t, nTaps> taps, bool left = true) 0058 { 0059 static_assert(k <= 32, "For the moment, only supported until k == 32."); 0060 static_assert(k > 0, "Non-zero degree is needed for the LFSR."); 0061 static_assert(nTaps > 0, "At least one tap is needed for the LFSR."); 0062 static_assert(nTaps <= k, "Cannot use more taps than polynomial order"); 0063 for (std::uint16_t j = 0; j < nTaps; ++j) { 0064 assert(static_cast<size_t>(taps[j] - 1) <= k && static_cast<size_t>(taps[j] - 1) > 0 && 0065 "Tap value is out of range [1,k]"); 0066 } 0067 0068 // First, calculate the XOR (^) of all selected bits (marked by the taps) 0069 bool newBit = lfsr[taps[0] - 1]; // the exponent E of the polynomial correspond to index E - 1 in the bitset 0070 for (std::uint16_t j = 1; j < nTaps; ++j) { 0071 newBit ^= lfsr[taps[j] - 1]; 0072 } 0073 0074 // Apply the shift to the register in the right direction, and overwrite the empty one with newBit 0075 if (left) { 0076 lfsr <<= 1; 0077 lfsr[0] = newBit; 0078 } else { 0079 lfsr >>= 1; 0080 lfsr[k - 1] = newBit; 0081 } 0082 0083 return newBit; 0084 } 0085 0086 /** 0087 * @brief Generation of a sequence of pseudo-random bits using a linear feedback shift register (LFSR), until a 0088 * register value is repeated (or maxPeriod is reached) 0089 * @tparam k the length of the LFSR, usually also the order of the monic polynomial PRBS-k (last exponent) 0090 * @tparam nTaps the number of taps 0091 * @tparam Output the type of the container where the bit result (0 or 1) is stored (e.g. char, bool). It's unsigned 0092 * char by default, use bool instead if you want to save memory 0093 * @param start the start value (seed) of the LFSR 0094 * @param taps the taps that will be XOR-ed to calculate the new bit. They are the exponents of the monic polynomial. 0095 * Ordering is unimportant. Note that an exponent E in the polynom maps to bit index E-1 in the LFSR. 0096 * @param left if true, the direction of the register shift is to the left <<, the newBit is set on lfsr at bit 0097 * position 0 (right). If false, shift is to the right and the newBit is stored at bit position (k-1) 0098 * @param wrapping if true, allow repetition of values in the LFSRhistory, until maxPeriod is reached or the repeated 0099 * value == start. Enabling this option saves memory as no history is kept 0100 * @param oppositeBit if true, use the high/low bit of the LFSR to store output (for left=true/false, respectively) 0101 * instead of the newBit returned by ::NextLFSR 0102 * @return the array of pseudo random bits, or an empty array if input was incorrect 0103 * @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register, 0104 * https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence 0105 */ 0106 template <size_t k, size_t nTaps, typename Output = unsigned char> 0107 std::vector<Output> GenerateSequence(std::bitset<k> start, std::array<std::uint16_t, nTaps> taps, bool left = true, 0108 bool wrapping = false, bool oppositeBit = false) 0109 { 0110 std::vector<Output> result; // Store result here 0111 0112 // Sanity-checks 0113 static_assert(k <= 32, "For the moment, only supported until k == 32."); 0114 static_assert(k > 0, "Non-zero degree is needed for the LFSR."); 0115 static_assert(nTaps >= 2, "At least two taps are needed for a proper sequence"); 0116 static_assert(nTaps <= k, "Cannot use more taps than polynomial order"); 0117 for (auto tap : taps) { 0118 if (tap > k || tap == 0) { 0119 Error("ROOT::Math::LFSR", "Tap %u is out of range [1,%lu]", tap, k); 0120 return result; 0121 } 0122 } 0123 if (start.none()) { 0124 Error("ROOT::Math::LFSR", "A non-zero start value is needed"); 0125 return result; 0126 } 0127 0128 // Calculate maximum period and pre-allocate space in result 0129 const std::uint32_t maxPeriod = pow(2, k) - 1; 0130 result.reserve(maxPeriod); 0131 0132 std::set<uint32_t> lfsrHistory; // a placeholder to store the history of all different values of the LFSR 0133 std::bitset<k> lfsr(start); // a variable storing the current value of the LFSR 0134 std::uint32_t i = 0; // a loop counter 0135 if (oppositeBit) // if oppositeBit enabled, first value is already started with the seed 0136 result.emplace_back(left ? lfsr[k - 1] : lfsr[0]); 0137 0138 // Loop now until maxPeriod or a lfsr value is repeated. If wrapping enabled, allow repeated values if not equal 0139 // to seed 0140 do { 0141 bool newBit = NextLFSR(lfsr, taps, left); 0142 0143 if (!oppositeBit) 0144 result.emplace_back(newBit); 0145 else 0146 result.emplace_back(left ? lfsr[k - 1] : lfsr[0]); 0147 0148 ++i; 0149 0150 if (!wrapping) // If wrapping not allowed, break the loop once a repeated value is encountered 0151 { 0152 if (lfsrHistory.count(lfsr.to_ulong())) 0153 break; 0154 0155 lfsrHistory.insert(lfsr.to_ulong()); // Add to the history 0156 } 0157 } while (lfsr != start && i < maxPeriod); 0158 0159 if (oppositeBit) 0160 result.pop_back(); // remove last element, as we already pushed the one from the seed above the while loop 0161 0162 result.shrink_to_fit(); // only some special taps will lead to the maxPeriod, others will stop earlier 0163 0164 return result; 0165 } 0166 } // namespace ROOT::Math::LFSR 0167 0168 #endif
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|