├── .DS_Store ├── .gitattributes ├── Aux ├── decoding.png ├── encoding.png └── SC_decording.png ├── Source Files ├── .DS_Store ├── polar.h ├── example_run.cpp └── polar.cpp └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just1nGH/Polar-Code-CPP/HEAD/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Aux/decoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just1nGH/Polar-Code-CPP/HEAD/Aux/decoding.png -------------------------------------------------------------------------------- /Aux/encoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just1nGH/Polar-Code-CPP/HEAD/Aux/encoding.png -------------------------------------------------------------------------------- /Aux/SC_decording.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just1nGH/Polar-Code-CPP/HEAD/Aux/SC_decording.png -------------------------------------------------------------------------------- /Source Files/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/just1nGH/Polar-Code-CPP/HEAD/Source Files/.DS_Store -------------------------------------------------------------------------------- /Source Files/polar.h: -------------------------------------------------------------------------------- 1 | #ifndef POLAR_H 2 | #define POLAR_H 3 | 4 | //headers 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // class definition 13 | class POLAR { 14 | private: 15 | //member variables 16 | unsigned m_N; // codeword length 17 | unsigned m_M; // mother codeword length 18 | unsigned m_K; // information length 19 | std::vector m_F; // frozen bit positions (including puncture postions) 20 | std::vector m_P; // puncture pattern 21 | std::vector m_I; // information bit positions 22 | std::vector m_Q; // reliability sequence 23 | 24 | public://public member functions 25 | 26 | //constructor 27 | POLAR(uint32_t info_length, 28 | uint32_t code_length, 29 | const std::string construct_method = "Huawei Approx", 30 | const double design_para = 0); 31 | //encoder 32 | std::vector encoder(std::vector* msg); 33 | //SC decoder 34 | std::vector sc_decoder(std::vector* llr); 35 | //SCL decoder 36 | std::vector scl_decoder(std::vector* llr, std::vector crc_g, unsigned nL); 37 | //rate matching 38 | std::vector rate_matching(std::vector* in); 39 | //rate recovery 40 | std::vector rate_recovery(std::vector* in); 41 | 42 | //static member functions can be used outside without an object 43 | static std::vector channel_polarization_huawei_approx(unsigned N); 44 | template 45 | static std::vector sort_indexes(const std::vector& v); 46 | 47 | //crc related 48 | static std::vector crc_gen(std::vector* msg, std::vector crc_g); 49 | static bool crc_check_sum(std::vector* msg, std::vector crc_g); 50 | static void crc_division(std::vector* pad_msg, std::vector gen); 51 | static std::vector crc_generator(const std::string crc_type); 52 | 53 | private: //private member functions 54 | 55 | //encode 56 | std::vector polar_encode(std::vector u); 57 | 58 | //SC decode 59 | std::vector sc_node_operations(std::vector* alpha, 60 | std::vector F, 61 | std::vector* u_cap); 62 | 63 | //SCL decode 64 | void scl_node_operations(std::vector >* llr, 65 | std::vector >* hard_decision, 66 | std::vector F, 67 | std::vector* PM, 68 | std::vector >* u_cap); 69 | // core function of SCL: sort and prune sc decoders 70 | template 71 | inline void sort_and_prune(std::vector >* V, std::vector P); 72 | }; 73 | 74 | #endif // !POLAR_H 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polar-Code-CPP 2 | Polar Code C++ Implementation 3 | 4 | try out *`example_run.m` which shows how to use the implemented polar codes.* 5 | 6 | ## Features 7 | 8 | C++ 17 needed to run the examples and use the code. No additional package is used, only standard library. 9 | 10 | The functions are encapsulated in a class for easy use. 11 | 12 | ### code construction : 13 | 14 | - channel polarization using Huawei approximation [3] 15 | 16 | ### Encoding 17 | 18 | - 3GPP encoding (without bit-reversal permutation) 19 | 20 | ### Decoding 21 | 22 | - SC decoder 23 | - CRC-aided SCL decoder 24 | 25 | ### Rate matching and rate recovery 26 | 27 | - to support different codeword length and code rate 28 | 29 | ### Member Variables (Properties) 30 | 31 | ```cpp 32 | //member variables 33 | unsigned m_N; // mother codeword length 34 | unsigned m_M; // codeword length 35 | unsigned m_K; // infomation length 36 | std::vector m_F; // frozen bit positions (including puncture postions) 37 | std::vector m_P; // puncture pattern 38 | std::vector m_I; // information bit positions 39 | std::vector m_Q; // reliabilty sequence 40 | ``` 41 | 42 | ### Public member functions 43 | 44 | ```cpp 45 | //public member functions 46 | //constructor 47 | POLAR(uint32_t info_length, 48 | uint32_t code_length, 49 | const std::string constuct_method = "Huawei Approx", 50 | const double design_para = 0); 51 | //encoder 52 | std::vector encoder(std::vector* msg); 53 | //SC decoder 54 | std::vector sc_decoder(std::vector* llr); 55 | //SCL decoder 56 | std::vector scl_decoder(std::vector* llr, std::vector crc_g, unsigned nL); 57 | //rate matching 58 | std::vector rate_matching(std::vector* in); 59 | //rate recovery 60 | std::vector rate_recovery(std::vector* in); 61 | ``` 62 | 63 | ### Core functions implementation explanation 64 | 65 | ### **Encoding** 66 | 67 | ![decoing](Aux/encoding.png) 68 | 69 | With the above equation, the encoding functioncan be implemented recursively as 70 | 71 | 72 | 73 | 74 | ### SC decoding : 75 | 76 | For each non-leaf node, it receives a soft bits vector from its parents and send the corresponding hard decision vector back to its parent. It involves 6 messages and 3 operations in the following sequence ( Refer to the following diagram) 77 | 78 | ![decoing](Aux/decoding.png) 79 | 80 | 81 | ![decoing](Aux/SC_decording.png) 82 | 83 | 84 | For each leaf node: 85 | 86 | - if it corresponds to a frozen bit, the hard decision will always be 0 87 | - if it corresponds to a information bit, the hard decision is made by simple threshold detection, soft bits > =0, hard decision = 0, soft bits < 0, hard decision = 1 88 | 89 | ### SCL decoding 90 | 91 | Run L SC decoders in parallel, all operations for non-leaf node are the same. when reaching a leaf node, for information bit, instead of make threshold detection, it generates two candidates: the candidate agrees with threshold detection and the candidate opposes threshold detection. A penalty is put on the oppose candidate. so that L decoders, becomes 2L candidates. Then a sort and prune procedure is performed based on path metrics to select L candidates with least path metrics. 92 | 93 | The idea is simple, but when it comes to the implementation, it involves a big complexity in terms of the time and memory. As the information(soft bits, hard decisions, PM) of each node for each SC encoder need to be stored/ exchanged /compared when needed. It involves a 3D matrix to store these information: the first dimension is for list of SC decoders, the second dimension is for nodes in the binary tree representation, the third dimension is for soft bits/hard bits. [6] 94 | 95 | This implementation optimizes the process, in stead of store all information of each node for all SC encoders in a 3D matrix like [5], all nodes have a shared storage of soft bits which changes (push in and pop out ) accordingly to ensure only necessary soft bits staying in the storage. This reduced time/memory complexity dramatically. 96 | 97 | ### Reference 98 | 99 | [1] E. Arikan, “Channel polarization: A method for constructing capacityachieving codes for symmetric binary-input memoryless channels,” CoRR, vol. abs/0807.3917, 2008. 100 | 101 | [2]H. Ochiai, P. Mitran and H. Vincent Poor, "Capacity-Approaching Polar Codes With Long Codewords and Successive Cancellation Decoding Based on Improved Gaussian Approximation," in IEEE Transactions on Communications, vol. 69, no. 1, pp. 31-43, Jan. 2021, doi: 10.1109/TCOMM.2020.3030057. 102 | 103 | [3] 3GPP R1-167209. Polar code design and rate matching 104 | 105 | [4] Pfister, Henry D. "A brief introduction to Polar codes." Supplemental Material for Advanced Channel Coding (2014). 106 | 107 | [5] Ochiai, Hideki, Patrick Mitran, and H. Vincent Poor. "Capacity-Approaching Polar Codes with Long Codewords and Successive Cancellation Decoding Based on Improved Gaussian Approximation." IEEE Transactions on Communications 69.1 (2020): 31-43. 108 | 109 | [6] [https://nptel.ac.in/courses/108/106/108106137/](https://nptel.ac.in/courses/108/106/108106137/) 110 | -------------------------------------------------------------------------------- /Source Files/example_run.cpp: -------------------------------------------------------------------------------- 1 | // PolarCodeCPPStdClass.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | #include 4 | #include "polar.h" 5 | #include 6 | #include //for format 7 | 8 | using namespace std; 9 | 10 | // function declaration 11 | string format(const char* fmt, ...); 12 | vector linspace(double start, double ed, int num); 13 | void example_run_SCL(); 14 | void example_run_SC(); 15 | 16 | int main() 17 | { 18 | std::cout << "Hello World!\n"; 19 | example_run_SCL(); 20 | //example_run_SC(); 21 | } 22 | 23 | void example_run_SCL() 24 | { 25 | unsigned nL = 4; 26 | int M = 4000; // code word length 27 | double rate = 1.0 / 3; // code rate 28 | 29 | unsigned K = ceil(M * rate); // information length 30 | 31 | // crc generator 32 | vector crc_g = { 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1 }; 33 | unsigned crc_len = crc_g.size() - 1; 34 | 35 | // information length excluding crc 36 | unsigned A = K - crc_len; 37 | 38 | // instantiate a POLAR object 39 | POLAR polar = POLAR(K, M); 40 | 41 | // random engines 42 | default_random_engine random_engine; 43 | bernoulli_distribution bern_dist; 44 | normal_distribution norm_dist(0, 1); 45 | 46 | // Running parameters 47 | vector EsN0_dB = linspace(-5, -2, 7); 48 | vector N0; 49 | for (auto e : EsN0_dB) 50 | N0.push_back(pow(10.0, -e / 10)); 51 | 52 | vector ber(N0.size(), 0), bler(N0.size(), 0); 53 | vector n_bit_errs(N0.size(), 0), n_blk_errs(N0.size(), 0); 54 | 55 | unsigned n_max_blks = 10000; 56 | 57 | // loop each SNR 58 | for (unsigned i = 0; i < N0.size(); i++) { 59 | //print progress 60 | string str = format("\nNow running EsN0: %.2f dB [%d of %d]", EsN0_dB[i], i + 1, N0.size()); 61 | cout << str << endl; 62 | unsigned print_len = 0; 63 | 64 | unsigned n_blks_done = 0; 65 | clock_t tStart = clock(); // timing 66 | 67 | while ((n_blks_done < n_max_blks) && (n_blk_errs[i] < 100)) { 68 | // generate random bit stream 69 | vector msg; msg.reserve(A); 70 | for (unsigned j = 0; j < A; j++) 71 | msg.push_back(bern_dist(random_engine)); 72 | 73 | // generate CRC and attach it to the message 74 | vector crc = POLAR::crc_gen(&msg, crc_g); 75 | vector crc_msg = msg; crc_msg.reserve(K); 76 | crc_msg.insert(crc_msg.end(), crc.begin(), crc.end()); 77 | 78 | // polar encoding 79 | vector enc = polar.encoder(&crc_msg); 80 | 81 | //rate matching 82 | vector rm_enc = polar.rate_matching(&enc); 83 | 84 | // BPSK+ AWGN 85 | vector r; r.reserve(M); 86 | for (auto e : rm_enc) 87 | r.push_back(1 - 2.0 * e + sqrt(N0[i] / 2.0) * norm_dist(random_engine)); 88 | 89 | // compute soft bits as LLR 90 | vector llr; llr.reserve(M); 91 | for (auto e : r) 92 | llr.push_back(4.0 * e / N0[i]); 93 | 94 | // rate recovery 95 | vector rr_llr = polar.rate_recovery(&llr); 96 | 97 | // scl decoding 98 | vector msg_cap = polar.scl_decoder(&rr_llr, crc_g, nL); 99 | 100 | // count errors 101 | unsigned n_errs = 0; 102 | for (int j = 0; j < A; j++) { 103 | if (msg[j] != msg_cap[j]) 104 | n_errs++; 105 | } 106 | 107 | if (n_errs) { 108 | n_bit_errs[i] += n_errs; 109 | n_blk_errs[i]++; 110 | } 111 | 112 | n_blks_done += 1; 113 | 114 | ber[i] = n_bit_errs[i] * 1.0 / (A * n_blks_done); 115 | bler[i] = n_blk_errs[i] * 1.0 / n_blks_done; 116 | 117 | // print progress for every 10 blocks 118 | if (n_blks_done % 10 == 0) { 119 | str = format("Elapsed time: %.1f seconds, # tx blocks: %d,# error blocks:%d, ber: %.5f, bler %.5f", (clock() - tStart) / (double)CLOCKS_PER_SEC, n_blks_done, n_blk_errs[i], ber[i], bler[i]); 120 | cout << std::string(print_len, '\b'); 121 | cout << str << flush; 122 | print_len = str.length(); 123 | } 124 | } 125 | 126 | // print progress when one SNR is finished 127 | str = format("Elapsed time: %.1f seconds, # tx blocks: %d,# error blocks:%d, ber: %.5f, bler %.5f", (clock() - tStart) / (double)CLOCKS_PER_SEC, n_blks_done, n_blk_errs[i], ber[i], bler[i]); 128 | cout << std::string(print_len, '\b') << str<< flush; 129 | } 130 | 131 | // print simulation result 132 | cout << endl; 133 | cout << "Modulation:" << "BPSK" << endl; 134 | cout << "[M,R] = [ " << M << "," << rate << "]" << endl; 135 | cout << "EsN0_dB = ["; 136 | for (auto e : EsN0_dB) 137 | cout << e << " "; 138 | cout << "]" << endl; 139 | 140 | cout << "BER = ["; 141 | for (auto e : ber) 142 | cout << e << " "; 143 | cout << "]" << endl; 144 | 145 | cout << "BLER = ["; 146 | for (auto e : bler) 147 | cout << e << " "; 148 | cout << "]" << endl; 149 | } 150 | void example_run_SC() 151 | { 152 | unsigned nL = 1; // number of decoders 153 | int M = 4000; // code word length 154 | double rate = 1.0 / 3; // code rate 155 | 156 | unsigned K = ceil(M * rate); // information length 157 | 158 | // crc generator 159 | vector crc_g = { 1 }; 160 | unsigned crc_len = crc_g.size() - 1; 161 | 162 | // information length excluding crc 163 | unsigned A = K - crc_len; 164 | 165 | // instantiate a POLAR object 166 | POLAR polar = POLAR(K, M); 167 | 168 | // random engines 169 | default_random_engine random_engine; 170 | bernoulli_distribution bern_dist; 171 | normal_distribution norm_dist(0, 1); 172 | 173 | // Running parameters 174 | vector EsN0_dB = linspace(-5, -2, 7); 175 | vector N0; 176 | for (auto e : EsN0_dB) 177 | N0.push_back(pow(10.0, -e / 10)); 178 | 179 | vector ber(N0.size(), 0), bler(N0.size(), 0); 180 | vector n_bit_errs(N0.size(), 0), n_blk_errs(N0.size(), 0); 181 | 182 | unsigned n_max_blks = 10000; 183 | 184 | // loop each SNR 185 | for (unsigned i = 0; i < N0.size(); i++) { 186 | //print progress 187 | string str = format("\nNow running EsN0: %.2f dB [%d of %d]", EsN0_dB[i], i + 1, N0.size()); 188 | cout << str << endl; 189 | unsigned print_len = 0; 190 | 191 | unsigned n_blks_done = 0; 192 | clock_t tStart = clock(); // timing 193 | 194 | while ((n_blks_done < n_max_blks) && (n_blk_errs[i] < 100)) { 195 | // generate random bit stream 196 | vector msg; msg.reserve(A); 197 | for (unsigned j = 0; j < A; j++) 198 | msg.push_back(bern_dist(random_engine)); 199 | 200 | // generate CRC and attach it to the message 201 | vector crc = POLAR::crc_gen(&msg, crc_g); 202 | vector crc_msg = msg; crc_msg.reserve(K); 203 | crc_msg.insert(crc_msg.end(), crc.begin(), crc.end()); 204 | 205 | // polar encoding 206 | vector enc = polar.encoder(&crc_msg); 207 | 208 | //rate matching 209 | vector rm_enc = polar.rate_matching(&enc); 210 | 211 | // BPSK+ AWGN 212 | vector r; r.reserve(M); 213 | for (auto e : rm_enc) 214 | r.push_back(1 - 2.0 * e + sqrt(N0[i] / 2.0) * norm_dist(random_engine)); 215 | 216 | // compute soft bits as LLR 217 | vector llr; llr.reserve(M); 218 | for (auto e : r) 219 | llr.push_back(4.0 * e / N0[i]); 220 | 221 | // rate recovery 222 | vector rr_llr = polar.rate_recovery(&llr); 223 | 224 | // SC decoder 225 | vector msg_cap = polar.sc_decoder(&rr_llr); 226 | 227 | // scl decoding 228 | //vector msg_cap = polar.scl_decoder(&rr_llr, crc_g, nL); 229 | 230 | // count errors 231 | unsigned n_errs = 0; 232 | for (int j = 0; j < A; j++) { 233 | if (msg[j] != msg_cap[j]) 234 | n_errs++; 235 | } 236 | 237 | if (n_errs) { 238 | n_bit_errs[i] += n_errs; 239 | n_blk_errs[i]++; 240 | } 241 | 242 | n_blks_done += 1; 243 | 244 | ber[i] = n_bit_errs[i] * 1.0 / (A * n_blks_done); 245 | bler[i] = n_blk_errs[i] * 1.0 / n_blks_done; 246 | 247 | // print progress for every 10 blocks 248 | if (n_blks_done % 10 == 0) { 249 | str = format("Elapsed time: %.1f seconds, # tx blocks: %d,# error blocks:%d, ber: %.5f, bler %.5f", (clock() - tStart) / (double)CLOCKS_PER_SEC, n_blks_done, n_blk_errs[i], ber[i], bler[i]); 250 | cout << std::string(print_len, '\b'); 251 | cout << str << flush; 252 | print_len = str.length(); 253 | } 254 | } 255 | 256 | // print progress when one SNR is finished 257 | str = format("Elapsed time: %.1f seconds, # tx blocks: %d,# error blocks:%d, ber: %.5f, bler %.5f", (clock() - tStart) / (double)CLOCKS_PER_SEC, n_blks_done, n_blk_errs[i], ber[i], bler[i]); 258 | cout << std::string(print_len, '\b') << str<< flush; 259 | } 260 | 261 | // print simulation result 262 | cout << endl; 263 | cout << "Modulation:" << "BPSK" << endl; 264 | cout << "[M,R] = [ " << M << "," << rate << "]" << endl; 265 | cout << "EsN0_dB = ["; 266 | for (auto e : EsN0_dB) 267 | cout << e << " "; 268 | cout << "]" << endl; 269 | 270 | cout << "BER = ["; 271 | for (auto e : ber) 272 | cout << e << " "; 273 | cout << "]" << endl; 274 | 275 | cout << "BLER = ["; 276 | for (auto e : bler) 277 | cout << e << " "; 278 | cout << "]" << endl; 279 | } 280 | string format(const char* fmt, ...) { 281 | int size = 512; 282 | char* buffer = 0; 283 | buffer = new char[size]; 284 | va_list vl; 285 | va_start(vl, fmt); 286 | int nsize = vsnprintf(buffer, size, fmt, vl); 287 | if (size <= nsize) { //fail delete buffer and try again 288 | delete[] buffer; 289 | buffer = 0; 290 | buffer = new char[nsize + 1]; //+1 for /0 291 | nsize = vsnprintf(buffer, size, fmt, vl); 292 | } 293 | string ret(buffer); 294 | va_end(vl); 295 | delete[] buffer; 296 | return ret; 297 | } 298 | vector linspace(double start, double ed, int num) { 299 | // catch rarely, throw often 300 | if (num < 2) { 301 | throw new exception(); 302 | } 303 | int partitions = num - 1; 304 | vector pts; 305 | // length of each segment 306 | double length = (ed - start) / partitions; 307 | // first, not to change 308 | pts.push_back(start); 309 | for (int i = 1; i < num - 1; i++) { 310 | pts.push_back(start + i * length); 311 | } 312 | // last, not to change 313 | pts.push_back(ed); 314 | return pts; 315 | } 316 | -------------------------------------------------------------------------------- /Source Files/polar.cpp: -------------------------------------------------------------------------------- 1 | #include "polar.h" 2 | 3 | using std::string; 4 | using std::vector; 5 | 6 | // public number functions------------------------------------------------------------------------------------------------ 7 | POLAR::POLAR(uint32_t info_length, uint32_t code_length, const string construct_method, const double design_para) 8 | { 9 | //--------------------------------------------------------------------------------- 10 | // polar construction 11 | // Input: 12 | // info_length: information bits length 13 | // code_length: coded bit length 14 | // construct_method: "Huawei approx", "BP", "GA" 15 | // design_para: N/A for"Huawei approx"; erasure probability for "BP"; design SNR in dB for "GA" 16 | // Dr. J Mao 2021 Sep 17 | //--------------------------------------------------------------------------------- 18 | 19 | m_M = code_length; // codeword length 20 | m_N = static_cast(pow(2, ceil(log2(code_length)))); // mother codeword length 21 | m_K = info_length; // information length 22 | 23 | // obtain sub-channel's reliability 24 | vector W(m_N, 0); 25 | if (construct_method.compare("Huawei Approx") == 0) { 26 | W = channel_polarization_huawei_approx(m_N); 27 | } 28 | else { 29 | //other methods to be added 30 | perror("unknown construction methods!"); 31 | } 32 | 33 | // reliability sequence 34 | for (auto e : sort_indexes(W)) { 35 | m_Q.push_back(e); 36 | } 37 | 38 | // puncture pattern 39 | m_P.reserve(m_N - m_M); 40 | m_P = vector(m_Q.begin(), m_Q.begin() + (m_N - m_M)); 41 | sort(m_P.begin(), m_P.end()); 42 | 43 | //frozen bits positions 44 | m_F.reserve(m_N - m_K); 45 | m_F = vector(m_Q.begin(), m_Q.begin() + (m_N - m_K)); 46 | sort(m_F.begin(), m_F.end()); 47 | 48 | // information bit positions 49 | m_I.reserve(m_K); 50 | m_I = vector(m_Q.end() - m_K, m_Q.end()); 51 | sort(m_I.begin(), m_I.end()); 52 | } 53 | vector POLAR::encoder(vector* msg) 54 | { 55 | //--------------------------------------------------------------------------------- 56 | // Polar code encoder 57 | // Input: 58 | // msg: a bvec of size K 59 | // Return: 60 | // the polar code encoded message 61 | // Dr. J Mao 2021 Sep 62 | //--------------------------------------------------------------------------------- 63 | 64 | assert(msg->size() == m_K); 65 | 66 | // place msg into information positions 67 | vector u(m_N, 0); 68 | for (uint32_t i = 0; i < m_K; i++) { 69 | u[m_I[i]] = (*msg)[i]; 70 | } 71 | 72 | // encode 73 | return polar_encode(u); 74 | } 75 | vector POLAR::sc_decoder(vector* llr) 76 | { 77 | // make F into bitmap 78 | vector F_bit_map(m_N, 0); 79 | for (auto e : m_F) 80 | F_bit_map[e] = 1; 81 | 82 | // run the decoding algorithm to decode; 83 | std::vector u_cap; // decoded bits 84 | sc_node_operations(llr, F_bit_map, &u_cap); 85 | 86 | // extract message bits 87 | vector msg_cap; msg_cap.reserve(m_K); 88 | for (auto e : m_I) 89 | msg_cap.push_back(u_cap[e]); 90 | 91 | return msg_cap; 92 | } 93 | vector POLAR::scl_decoder(vector* llr, vector crcG, unsigned nL) 94 | { 95 | //---------------------------------------------------------------------------------------- 96 | // input: 97 | // llr: channel info as log likelihood of received bits 98 | // crcG: crc generator 99 | // nL: number of list decoders 100 | // return: 101 | // decoded message bits 102 | // Dr J Mao Sep 2021 103 | //---------------------------------------------------------------------------------------- 104 | 105 | uint32_t A = m_K - crcG.size() + 1; // information bit length excluding padded CRC 106 | 107 | // make F into bitmap 108 | vector F_bit_map(m_N, 0); 109 | for (auto e : m_F) F_bit_map[e] = 1; 110 | 111 | //path metric with first element being 0, the rest are infinity 112 | vector PM(nL, std::numeric_limits::infinity()); 113 | PM[0] = 0; 114 | 115 | // llr list initialization 116 | vector> LLR(nL, *llr); 117 | 118 | // hard decisions and coded bits for corresponding llrs 119 | vector> hard_decision(nL), u_cap_list(nL); 120 | 121 | // SCL decoding 122 | scl_node_operations(&LLR, &hard_decision, F_bit_map, &PM, &u_cap_list); 123 | 124 | // CRC checksum to find the best decoding result in the list 125 | vector msg_cap(m_K), tmp_cap(m_K); 126 | vector sorted_idx = sort_indexes(PM); 127 | 128 | uint8_t selIdx = sorted_idx[0]; 129 | for (unsigned j = 0; j < nL; j++) { 130 | for (unsigned i = 0; i < m_I.size(); i++) { 131 | tmp_cap[i] = u_cap_list[sorted_idx[j]][m_I[i]]; 132 | } 133 | if (crc_check_sum(&tmp_cap, crcG)) { 134 | selIdx = sorted_idx[j]; 135 | break; 136 | } 137 | } 138 | 139 | for (unsigned i = 0; i < A; i++) { 140 | msg_cap[i] = u_cap_list[selIdx][m_I[i]]; 141 | } 142 | 143 | return msg_cap; 144 | } 145 | vector POLAR::rate_matching(vector* in) 146 | { 147 | // note, P must be a sorted sequence 148 | unsigned iL = in->size(); 149 | unsigned pL = m_P.size(); 150 | 151 | vector out(iL - pL); 152 | 153 | unsigned i = 0, j = 0, k = 0; // index to traverse in, P and out 154 | 155 | while (k < iL - pL && j < pL) { 156 | if (i == m_P[j]) { 157 | j++; i++; 158 | } 159 | else { 160 | out[k++] = (*in)[i++]; 161 | } 162 | } 163 | 164 | while (k < iL - pL) { 165 | out[k++] = (*in)[i++]; 166 | } 167 | 168 | return out; 169 | } 170 | vector POLAR::rate_recovery(vector* in) 171 | { 172 | unsigned iL = in->size(); 173 | unsigned pL = m_P.size(); 174 | 175 | vector out(iL + pL, 0); 176 | 177 | unsigned i = 0, j = 0, k = 0; // index to traverse in, P and out 178 | 179 | while (i < iL && j < pL) { 180 | if (k == m_P[j]) { 181 | k++; 182 | j++; 183 | } 184 | else { 185 | out[k++] = (*in)[i++]; 186 | } 187 | } 188 | 189 | while (i < iL) { 190 | out[k++] = (*in)[i++]; 191 | } 192 | return out; 193 | } 194 | 195 | //Private Core functions----------------------------------------------------------------------------------------------------------------- 196 | vector POLAR::polar_encode(vector u) 197 | { 198 | //--------------------------------------------------------------------------------- 199 | // This function encodes u into x. x = u * F_N 200 | // where N = 2 ^ n bits, F_N = F kroncker N, F = [1 0; 1 1]; 201 | // Dr. J Mao 2021 Sep 202 | //--------------------------------------------------------------------------------- 203 | 204 | uint32_t N = u.size(); 205 | if (N == 1) 206 | return u; 207 | else { 208 | // butterfly operation 209 | vector u1_xor_u2; u1_xor_u2.reserve(N / 2); 210 | for (int i = 0; i < N / 2; i++) 211 | u1_xor_u2.push_back(u[i] xor u[i + N / 2]); 212 | 213 | // recursive encoding for left half 214 | vector x; x.reserve(N); 215 | x = polar_encode(u1_xor_u2); 216 | 217 | // for right half 218 | vector x2 = polar_encode(vector(u.end() - N / 2, u.end())); 219 | x.insert(x.end(), x2.begin(), x2.end()); 220 | 221 | return x; 222 | } 223 | } 224 | vector POLAR::sc_node_operations(vector* alpha, vector F, vector* u_cap) 225 | { 226 | unsigned N = alpha->size(); 227 | vector beta; beta.reserve(N); // hard decisions for corresponding soft bits 228 | 229 | //std::cout << "alpha = ["; 230 | //for (auto e : alpha) 231 | // std::cout << e << " "; 232 | //std::cout<<"]"<< std::endl; 233 | 234 | if (N == 1) { // leaf nodes 235 | if (F[0] == 1) { 236 | beta.push_back(0); // frozen bits always zero 237 | } 238 | else { 239 | beta.push_back(((*alpha)[0] < 0) ? 1 : 0); // threshold detection 240 | } 241 | 242 | u_cap->push_back(beta[0]); // save decoded bits 243 | } 244 | else { // non-leaf nodes 245 | vector a(vector(alpha->begin(), alpha->begin() + N / 2)); 246 | vector b(vector(alpha->end() - N / 2, alpha->end())); 247 | 248 | // calculate llr for its left child 249 | vector alpha_left(N / 2); 250 | for (unsigned i = 0; i < N / 2; i++) { 251 | //alpha_left[i] = 2.0 * atanhf(tanhf(a[i] / 2.0) * tanhf(b[i] / 2.0)); // slower 252 | int sign = (1 - 2 * (a[i] * b[i] < 0)); 253 | alpha_left[i] = (std::min(abs(a[i]), abs(b[i])) * sign); // much faster with ignorable loss 254 | } 255 | 256 | // recursive decoding, pass llr message to its left child and expect for corresping hard decision 257 | vector F_left(vector(F.begin(), F.begin() + N / 2)); 258 | vector beta_left = sc_node_operations(&alpha_left, F_left, u_cap); 259 | 260 | // calculate llr for its right child 261 | vector alpha_right(N / 2); 262 | for (unsigned i = 0; i < N / 2; i++) { 263 | alpha_right[i] = (beta_left[i] == 0) ? b[i] + a[i] : b[i] - a[i]; 264 | } 265 | 266 | // recursive decoding, pass llr message to its right child and expect for corresping hard decision 267 | vector F_right(vector(F.end() - N / 2, F.end())); 268 | vector beta_right = sc_node_operations(&alpha_right, F_right, u_cap); 269 | 270 | // combine hard decision from its both children and return to its parent 271 | 272 | for (int i = 0; i < N / 2; i++) { 273 | beta.push_back(beta_left[i] xor beta_right[i]); 274 | } 275 | beta.insert(beta.end(), beta_right.begin(), beta_right.end()); 276 | } 277 | //std::cout << "beta = ["; 278 | //for (auto e : beta) 279 | // std::cout << e << " "; 280 | //std::cout << "]" << std::endl; 281 | return beta; 282 | } 283 | void POLAR::scl_node_operations(vector>* llr, vector>* hard_decision, vector F, vector* PM, vector>* u_cap) 284 | { 285 | //---------------------------------------------------------------------------------------- 286 | // input: 287 | // llr: channel info as log likelihood of received bits 288 | // hard decisions: the correspondingly hard bits of the llr, will be caculted as messgaes to pass around nodes 289 | // F: frozen bits in bitmask format 290 | // PM: path metric 291 | // u_cap: decoded bits 292 | // Dr J Mao Sep 2021 293 | //---------------------------------------------------------------------------------------- 294 | 295 | // the number of bits that the current node carries 296 | unsigned nb = F.size(); 297 | unsigned nL = llr->size(); // number of decoders; 298 | 299 | // for leaf node 300 | if (nb == 1) { 301 | // retrieve the last element of llr list as the soft bit(alpha) of the node 302 | vector alpha; 303 | for (unsigned i = 0; i < nL; i++) { 304 | alpha.push_back((*llr)[i].back()); 305 | } 306 | 307 | // leaf node is frozen bit, hard decision is 0 308 | if (F[0] == 1) { 309 | for (unsigned i = 0; i < nL; i++) { 310 | (*u_cap)[i].push_back(0); 311 | (*hard_decision)[i].push_back(0); 312 | // PM update if llr < 0 , no sort and pruning in this case 313 | (*PM)[i] = (*PM)[i] + abs(alpha[i]) * double((alpha[i] < 0)); 314 | } 315 | } 316 | else { // leaf node is information bit 317 | vector candi_dec; // candidate decoded bits 318 | vector candi_PM = *PM; // candidate path metrics 319 | for (unsigned i = 0; i < nL; i++) { // hard decisions based on llr 320 | candi_dec.push_back(alpha[i] < 0); 321 | } 322 | for (unsigned i = 0; i < nL; i++) { // invert hard decisions and put penalty based on llr 323 | candi_dec.push_back(alpha[i] >= 0); 324 | candi_PM.push_back((*PM)[i] + abs(alpha[i])); 325 | } 326 | 327 | // sort candidates based on path metrics 328 | vector sorted_idx = sort_indexes(candi_PM); 329 | vector selected_idx; selected_idx.reserve(nL); 330 | 331 | // sort and prune decoders 332 | selected_idx.insert(selected_idx.end(), sorted_idx.begin(), sorted_idx.begin() + nL); 333 | for (auto& ele : selected_idx) { 334 | if (ele >= nL) ele -= nL; 335 | } 336 | sort_and_prune(llr, selected_idx); 337 | sort_and_prune(hard_decision, selected_idx); 338 | sort_and_prune(u_cap, selected_idx); 339 | 340 | // update PM and add the newly decoded bits 341 | for (unsigned i = 0; i < nL; i++) { 342 | unsigned idx = sorted_idx[i]; 343 | (*PM)[i] = candi_PM[idx]; 344 | (*u_cap)[i].push_back(candi_dec[idx]); 345 | (*hard_decision)[i].push_back(candi_dec[idx]); 346 | } 347 | } 348 | } 349 | else { 350 | // extract the last node_size bits as the node's sot bits (alpha) 351 | vector> alpha(nL); 352 | for (unsigned i = 0; i < nL; i++) { 353 | alpha[i] = vector((*llr)[i].end() - nb, (*llr)[i].end()); 354 | } 355 | 356 | //check node operation f(a,b) = sgn(a) * sgn(b) * min(|a|,|b|) (min sum approx) 357 | for (unsigned i = 0; i < nL; i++) { 358 | for (unsigned j = 0; j < nb / 2; j++) { 359 | double a = alpha[i][j]; 360 | double b = alpha[i][j + nb / 2]; 361 | int sign = 1 - 2 * (a * b < 0); 362 | (*llr)[i].push_back(std::min(abs(a), abs(b)) * sign); 363 | } 364 | } 365 | 366 | vector F_left(vector(F.begin(), F.begin() + nb / 2)); 367 | scl_node_operations(llr, hard_decision, F_left, PM, u_cap); 368 | 369 | //hard decisions returned by left child 370 | vector> beta_left(nL); 371 | for (unsigned i = 0; i < nL; i++) { 372 | beta_left[i] = vector((*hard_decision)[i].end() - nb / 2, (*hard_decision)[i].end()); 373 | } 374 | 375 | //extract alpha again as it may have been sorted by the left child 376 | for (unsigned i = 0; i < nL; i++) { 377 | alpha[i] = vector ((*llr)[i].end() - nb, (*llr)[i].end()); 378 | } 379 | 380 | //bit node handling 381 | for (unsigned i = 0; i < nL; i++) { 382 | for (unsigned j = 0; j < nb / 2; j++) { 383 | double a = alpha[i][j]; 384 | double b = alpha[i][j + nb / 2]; 385 | double c = beta_left[i][j]; 386 | 387 | (*llr)[i].push_back(b + (1 - 2 * c) * a); 388 | } 389 | } 390 | 391 | // right child decoding 392 | vector F_right(vector(F.end() - nb / 2, F.end())); 393 | scl_node_operations(llr, hard_decision, F_right, PM, u_cap); 394 | 395 | // butterfly processing does beta = [beta_left xor beta_right, beta_right] 396 | int start_pos = (*hard_decision)[0].size() - nb; 397 | for (auto& E : *hard_decision) { 398 | for (unsigned j = start_pos; j < start_pos + nb / 2; j++) { 399 | E[j] = (E[j] + E[j + nb / 2]) % 2; 400 | } 401 | } 402 | } 403 | 404 | // delete soft bits from the 2-D llr vectors (equivalent to poping out the calling stack) 405 | for (auto& ELE : *llr) { 406 | ELE.erase(ELE.end() - nb, ELE.end()); 407 | } 408 | } 409 | template 410 | inline void POLAR::sort_and_prune(vector>* V, vector P) 411 | { 412 | vector> tmp; 413 | 414 | for (auto ele : P) { 415 | tmp.push_back((*V)[ele]); 416 | } 417 | 418 | V->swap(tmp); 419 | } 420 | 421 | // static functions----------------------------------------------------------------------------------------------------------------------- 422 | vector POLAR::channel_polarization_huawei_approx(unsigned N) 423 | { 424 | //-------------------------------------------------------------------------------- 425 | // return m_N subchannel's weight as a measure of their reliability 426 | // [ref] 3GPP R1 - 167209 Polar code design and rate matching 427 | //--------------------------------------------------------------------------------- 428 | vector W(N, 0); 429 | 430 | unsigned n = static_cast(log2(N)); 431 | 432 | for (unsigned j = 0; j < N; j++) { 433 | unsigned tmp = j; 434 | 435 | for (unsigned k = 0; k < n; k++) 436 | { 437 | double bit_val = tmp % 2; 438 | W[j] += bit_val * pow(2, (k * 0.25)); 439 | tmp = tmp / 2; 440 | if (tmp == 0) 441 | break; 442 | } 443 | } 444 | 445 | return W; 446 | } 447 | template 448 | inline vector POLAR::sort_indexes(const vector& v) 449 | { 450 | // initialize original index locations 451 | vector idx(v.size()); 452 | iota(idx.begin(), idx.end(), 0); 453 | 454 | // sort indexes based on comparing values in v 455 | // using std::stable_sort instead of std::sort 456 | // to avoid unnecessary index re-orderings 457 | // when v contains elements of equal values 458 | stable_sort(idx.begin(), idx.end(), 459 | [&v](size_t i1, size_t i2) {return v[i1] < v[i2]; }); 460 | 461 | return idx; 462 | } 463 | 464 | // crc related 465 | bool POLAR::crc_check_sum(vector* msg, vector crc_g) 466 | { 467 | unsigned crc_len = crc_g.size() - 1; 468 | uint32_t n = msg->size() - crc_len; 469 | 470 | vector msg_crc(msg->begin(), msg->end()); 471 | vector gen(crc_g.begin(), crc_g.end()); 472 | 473 | crc_division(&msg_crc, gen); 474 | 475 | for (unsigned i = 0; i < crc_len; i++) { 476 | if (msg_crc[n + i]) 477 | return 0; 478 | } 479 | 480 | return 1; 481 | } 482 | void POLAR::crc_division(vector* pad_msg, vector gen) 483 | { 484 | unsigned r = gen.size() - 1; 485 | unsigned n = (*pad_msg).size() - r; 486 | 487 | for (unsigned i = 0; i < n; i++) 488 | { 489 | if (gen[0] == (*pad_msg)[i]) 490 | { 491 | for (unsigned j = 0, k = i; j < r + 1; j++, k++) { 492 | if (!((*pad_msg)[k] ^ gen[j])) { 493 | (*pad_msg)[k] = 0; 494 | } 495 | else { 496 | (*pad_msg)[k] = 1; 497 | } 498 | } 499 | } 500 | } 501 | } 502 | vector POLAR::crc_gen(vector* msg, vector crc_g) 503 | { 504 | unsigned crc_len = crc_g.size() - 1; 505 | 506 | vector pad_msg(msg->begin(), msg->end()); 507 | for (uint8_t i = 0; i < crc_len; i++) { 508 | pad_msg.push_back(0); 509 | } 510 | vector gen(crc_g.begin(), crc_g.end()); 511 | 512 | crc_division(&pad_msg, gen); 513 | 514 | vector crc(pad_msg.end() - crc_len, pad_msg.end()); 515 | return crc; 516 | } 517 | vector POLAR::crc_generator(const string crc_type) 518 | { 519 | if (!crc_type.compare("24A")) 520 | return { 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1 }; 521 | else if (!crc_type.compare("24B")) 522 | return{ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1 }; 523 | else if (!crc_type.compare("24C")) 524 | return{ 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1 }; 525 | else if (!crc_type.compare("16")) 526 | return{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; 527 | else if (!crc_type.compare("11")) 528 | return{ 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; 529 | else if (!crc_type.compare("6")) 530 | return{ 1, 1, 0, 0, 0, 0, 1 }; 531 | else if (!crc_type.compare("1")) 532 | return{ 1 }; 533 | else 534 | return {1}; 535 | } --------------------------------------------------------------------------------