Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
LFSR.h
Go to the documentation of this file.
1// @(#)root/mathcore:$Id$
2// Author: Fernando Hueso-González 04/08/2021
3
4#ifndef ROOT_Math_LFSR
5#define ROOT_Math_LFSR
6
7#include <array>
8#include <bitset>
9#include <cassert>
10#include <cstdint>
11#include <vector>
12#include <set>
13#include <cmath>
14#include <cstdint> // for std::uint16_t
15#include "TError.h"
16
17/// Pseudo Random Binary Sequence (PRBS) generator namespace with functions based
18/// on linear feedback shift registers (LFSR) with a periodicity of 2^n-1
19///
20/// @note It should NOT be used for general-purpose random number generation or any
21/// statistical study, for those cases see e.g. std::mt19937 instead.
22///
23/// The goal is to generate binary bit sequences with the same algorithm as the ones usually implemented
24/// in electronic chips, so that the theoretically expected ones can be compared with the acquired sequences.
25///
26/// The main ingredients of a PRBS generator are a monic polynomial of maximum degree \f$n\f$, with coefficients
27/// either 0 or 1, and a <a href="https://www.nayuki.io/page/galois-linear-feedback-shift-register">Galois</a>
28/// linear-feedback shift register with a non-zero seed. When the monic polynomial exponents are chosen appropriately,
29/// the period of the resulting bit sequence (0s and 1s) yields \f$2^n - 1\f$.
30///
31/// @sa https://gist.github.com/mattbierner/d6d989bf26a7e54e7135,
32/// https://root.cern/doc/master/civetweb_8c_source.html#l06030,
33/// https://cryptography.fandom.com/wiki/Linear_feedback_shift_register,
34/// https://www3.advantest.com/documents/11348/33b24c8a-c8cb-40b8-a2a7-37515ba4abc8,
35/// https://www.reddit.com/r/askscience/comments/63a10q/for_prbs3_with_clock_input_on_each_gate_how_can/,
36/// https://es.mathworks.com/help/serdes/ref/prbs.html, https://metacpan.org/pod/Math::PRBS,
37/// https://ez.analog.com/data_converters/high-speed_adcs/f/q-a/545335/ad9689-pn9-and-pn23
38
40
41/**
42 * @brief Generate the next pseudo-random bit using the current state of a linear feedback shift register (LFSR) and
43 * update it
44 * @tparam k the length of the LFSR, usually also the order of the monic polynomial PRBS-k (last exponent)
45 * @tparam nTaps the number of taps
46 * @param lfsr the current value of the LFSR. Passed by reference, it will be updated with the next value
47 * @param taps the taps that will be XOR-ed to calculate the new bit. They are the exponents of the monic polynomial.
48 * Ordering is unimportant. Note that an exponent E in the polynom maps to bit index E-1 in the LFSR.
49 * @param left if true, the direction of the register shift is to the left <<, the newBit is set on lfsr at bit position
50 * 0 (right). If false, shift is to the right and the newBit is stored at bit position (k-1)
51 * @return the new random bit
52 * @throw an exception is thrown if taps are out of the range [1, k]
53 * @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register,
54 * https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
55 */
56template <size_t k, size_t nTaps>
57bool NextLFSR(std::bitset<k> &lfsr, std::array<std::uint16_t, nTaps> taps, bool left = true)
58{
59 static_assert(k <= 32, "For the moment, only supported until k == 32.");
60 static_assert(k > 0, "Non-zero degree is needed for the LFSR.");
61 static_assert(nTaps > 0, "At least one tap is needed for the LFSR.");
62 static_assert(nTaps <= k, "Cannot use more taps than polynomial order");
63 for (std::uint16_t j = 0; j < nTaps; ++j) {
64 assert(static_cast<size_t>(taps[j] - 1) <= k && static_cast<size_t>(taps[j] - 1) > 0 &&
65 "Tap value is out of range [1,k]");
66 }
67
68 // First, calculate the XOR (^) of all selected bits (marked by the taps)
69 bool newBit = lfsr[taps[0] - 1]; // the exponent E of the polynomial correspond to index E - 1 in the bitset
70 for (std::uint16_t j = 1; j < nTaps; ++j) {
71 newBit ^= lfsr[taps[j] - 1];
72 }
73
74 // Apply the shift to the register in the right direction, and overwrite the empty one with newBit
75 if (left) {
76 lfsr <<= 1;
77 lfsr[0] = newBit;
78 } else {
79 lfsr >>= 1;
80 lfsr[k - 1] = newBit;
81 }
82
83 return newBit;
84}
85
86/**
87 * @brief Generation of a sequence of pseudo-random bits using a linear feedback shift register (LFSR), until a
88 * register value is repeated (or maxPeriod is reached)
89 * @tparam k the length of the LFSR, usually also the order of the monic polynomial PRBS-k (last exponent)
90 * @tparam nTaps the number of taps
91 * @tparam Output the type of the container where the bit result (0 or 1) is stored (e.g. char, bool). It's unsigned
92 * char by default, use bool instead if you want to save memory
93 * @param start the start value (seed) of the LFSR
94 * @param taps the taps that will be XOR-ed to calculate the new bit. They are the exponents of the monic polynomial.
95 * Ordering is unimportant. Note that an exponent E in the polynom maps to bit index E-1 in the LFSR.
96 * @param left if true, the direction of the register shift is to the left <<, the newBit is set on lfsr at bit
97 * position 0 (right). If false, shift is to the right and the newBit is stored at bit position (k-1)
98 * @param wrapping if true, allow repetition of values in the LFSRhistory, until maxPeriod is reached or the repeated
99 * value == start. Enabling this option saves memory as no history is kept
100 * @param oppositeBit if true, use the high/low bit of the LFSR to store output (for left=true/false, respectively)
101 * instead of the newBit returned by ::NextLFSR
102 * @return the array of pseudo random bits, or an empty array if input was incorrect
103 * @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register,
104 * https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
105 */
106template <size_t k, size_t nTaps, typename Output = unsigned char>
107std::vector<Output> GenerateSequence(std::bitset<k> start, std::array<std::uint16_t, nTaps> taps, bool left = true,
108 bool wrapping = false, bool oppositeBit = false)
109{
110 std::vector<Output> result; // Store result here
111
112 // Sanity-checks
113 static_assert(k <= 32, "For the moment, only supported until k == 32.");
114 static_assert(k > 0, "Non-zero degree is needed for the LFSR.");
115 static_assert(nTaps >= 2, "At least two taps are needed for a proper sequence");
116 static_assert(nTaps <= k, "Cannot use more taps than polynomial order");
117 for (auto tap : taps) {
118 if (tap > k || tap == 0) {
119 Error("ROOT::Math::LFSR", "Tap %u is out of range [1,%lu]", tap, k);
120 return result;
121 }
122 }
123 if (start.none()) {
124 Error("ROOT::Math::LFSR", "A non-zero start value is needed");
125 return result;
126 }
127
128 // Calculate maximum period and pre-allocate space in result
129 const std::uint32_t maxPeriod = pow(2, k) - 1;
130 result.reserve(maxPeriod);
131
132 std::set<uint32_t> lfsrHistory; // a placeholder to store the history of all different values of the LFSR
133 std::bitset<k> lfsr(start); // a variable storing the current value of the LFSR
134 std::uint32_t i = 0; // a loop counter
135 if (oppositeBit) // if oppositeBit enabled, first value is already started with the seed
136 result.emplace_back(left ? lfsr[k - 1] : lfsr[0]);
137
138 // Loop now until maxPeriod or a lfsr value is repeated. If wrapping enabled, allow repeated values if not equal
139 // to seed
140 do {
141 bool newBit = NextLFSR(lfsr, taps, left);
142
143 if (!oppositeBit)
144 result.emplace_back(newBit);
145 else
146 result.emplace_back(left ? lfsr[k - 1] : lfsr[0]);
147
148 ++i;
149
150 if (!wrapping) // If wrapping not allowed, break the loop once a repeated value is encountered
151 {
152 if (lfsrHistory.count(lfsr.to_ulong()))
153 break;
154
155 lfsrHistory.insert(lfsr.to_ulong()); // Add to the history
156 }
157 } while (lfsr != start && i < maxPeriod);
158
159 if (oppositeBit)
160 result.pop_back(); // remove last element, as we already pushed the one from the seed above the while loop
161
162 result.shrink_to_fit(); // only some special taps will lead to the maxPeriod, others will stop earlier
163
164 return result;
165}
166} // namespace ROOT::Math::LFSR
167
168#endif
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Pseudo Random Binary Sequence (PRBS) generator namespace with functions based on linear feedback shif...
Definition LFSR.h:39
bool NextLFSR(std::bitset< k > &lfsr, std::array< std::uint16_t, nTaps > taps, bool left=true)
Generate the next pseudo-random bit using the current state of a linear feedback shift register (LFSR...
Definition LFSR.h:57
std::vector< Output > GenerateSequence(std::bitset< k > start, std::array< std::uint16_t, nTaps > taps, bool left=true, bool wrapping=false, bool oppositeBit=false)
Generation of a sequence of pseudo-random bits using a linear feedback shift register (LFSR),...
Definition LFSR.h:107