├── .gitignore ├── BCJR ├── BCJR.c ├── BCJR.m ├── BCJR.mexa64 ├── BCJR.mexmaci ├── BCJR.mexmaci64 ├── BCJR.mexsol ├── BCJR.mexw64 ├── matrixarith.h ├── mexheader.h └── mexutils.h ├── README.md └── precoder_linear_ofdm_sim.m /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx 3 | 4 | ### OSX ### 5 | *.DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | # End of https://www.gitignore.io/api/osx 32 | -------------------------------------------------------------------------------- /BCJR/BCJR.c: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | ** Title : Max-log BCJR Implementation 3 | ** File : BCJR.c 4 | ** ------------------------------------------------------------------------ 5 | ** Description : 6 | ** Fast implementation of a max-log soft-in soft-out MAP decoder 7 | ** [Bahl et.al.] aka. forward-backward algorithm. Decoder has two 8 | ** outputs: a posteriori LLRs for coded bits and sliced output estimates 9 | ** (bits). 10 | ** ------------------------------------------------------------------------ 11 | ** Revisions : 12 | ** Date Version Author Description 13 | ** 17-aug-06 1.3 studer some bugfixes/cleanup 14 | ** 12-jul-06 1.2 studer minor speed-ups 15 | ** 01-jun-06 1.1 studer a posteriori output and minor speedups 16 | ** 01-may-06 1.0 studer initial version adapted from softviterbi 17 | ** ------------------------------------------------------------------------- 18 | ** (C) 2006-2018 VIP Information Processing Group 19 | ** Author: Christoph Studer (e-mail: studer@cornell.edu) 20 | * =======================================================================*/ 21 | 22 | #include "mexheader.h" 23 | #include "mexutils.h" 24 | #include "matrixarith.h" 25 | #include "math.h" 26 | 27 | unsigned int log2floor(unsigned int x) 28 | { 29 | unsigned int result; 30 | result=0; 31 | while((1 << result) <= x) result++; 32 | result--; 33 | return result; 34 | } 35 | 36 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 37 | { 38 | int k, K, i, j, q; 39 | unsigned int p; 40 | 41 | mxArrayConstPtr mtrellisArr, tempArr; 42 | mxArrayPtr outSymbolsArr, nextStatesArr; 43 | mxArrayConstPtr LLRVector; 44 | mxArrayPtr BMVector; 45 | 46 | unsigned int numOutputSymbols, numInputSymbols; 47 | unsigned int numOutputBits, numInputBits; 48 | unsigned int numStates; 49 | unsigned int numBits, numSymbols; 50 | 51 | mxArrayPtr AlphaMetric, BetaMetric, oldBetaMetric; 52 | mxArrayPtr xhatk, xhatk0, xhatk1; 53 | mxArrayPtr llr_0, llr_1, llr_d; 54 | 55 | /* Examine input and output arguments. */ 56 | userAssert(nrhs == 2, "BCJR.c requires 2 input arguments!"); 57 | userAssert(nlhs == 2, "BCJR.c requires 2 output arguments!"); 58 | 59 | /* Read out mtrellis structure into variables */ 60 | mtrellisArr = prhs[0]; 61 | 62 | tempArr = mxGetField(mtrellisArr, 0, "numInputSymbols"); 63 | mxAssert(tempArr != NIL, "Missing field 'numInputSymbols' in mtrellis parameter"); 64 | numInputSymbols = (unsigned int)(*mxGetPr(tempArr)); 65 | numInputBits = (int)log2floor(numInputSymbols); 66 | 67 | tempArr = mxGetField(mtrellisArr, 0, "numOutputSymbols"); 68 | mxAssert(tempArr != NIL, "Missing field 'numOutputSymbols' in mtrellis parameter"); 69 | numOutputSymbols = (unsigned int)*mxGetPr(tempArr); 70 | numOutputBits = (int)log2floor(numOutputSymbols); 71 | 72 | tempArr = mxGetField(mtrellisArr, 0, "numStates"); 73 | mxAssert(tempArr != NIL, "Missing field 'numStates' in mtrellis parameter"); 74 | numStates = (unsigned int)*mxGetPr(tempArr); 75 | 76 | nextStatesArr = mxGetField(mtrellisArr, 0, "nextStates"); 77 | userAssert(nextStatesArr != NIL, "Missing field 'nextStates' in mtrellis parameter"); 78 | 79 | outSymbolsArr = mxGetField(mtrellisArr, 0, "outputs"); 80 | userAssert(outSymbolsArr != NIL, "Missing field 'outputs' in mtrellis parameter"); 81 | 82 | /* Get received bits */ 83 | LLRVector = prhs[1]; 84 | if(mxGetM(LLRVector) > mxGetN(LLRVector)) numBits = mxGetM(LLRVector); 85 | else numBits = mxGetN(LLRVector); 86 | numSymbols = numBits/numOutputBits; 87 | 88 | /* -------------------------------- */ 89 | 90 | /* detected systematic message */ 91 | xhatk0 = mxCreateDoubleMatrix(1, numInputBits*numSymbols, mxREAL); 92 | xhatk1 = mxCreateDoubleMatrix(1, numInputBits*numSymbols, mxREAL); 93 | xhatk = mxCreateDoubleMatrix(1, numInputBits*numSymbols, mxREAL); 94 | 95 | /* a posteriori llr output */ 96 | llr_0 = mxCreateDoubleMatrix(1,numBits,mxREAL); 97 | llr_1 = mxCreateDoubleMatrix(1,numBits,mxREAL); 98 | llr_d = mxCreateDoubleMatrix(1,numBits,mxREAL); 99 | 100 | /* Branch Metric Vector (precomputed) */ 101 | BMVector = mxCreateDoubleMatrix(numOutputSymbols, 1, mxREAL); 102 | 103 | /* ------------------------------------------------------------------- */ 104 | /* Alpha-Metric (Forward Iteration) */ 105 | /* ------------------------------------------------------------------- */ 106 | AlphaMetric = mxCreateDoubleMatrix(numStates, (int)(numSymbols+1), mxREAL); /* initializes with zero */ 107 | /* By definition, we start in state 0. In order to make this clear 108 | ** to the BCJR, we severely discriminate against all other states... */ 109 | for(i = 0; i=0;b--) { 122 | if(tmp>=(1 << b)) { 123 | tmp = tmp - (1 << b); 124 | doubleElementR(BMVector,q,0) += doubleElementR(LLRVector,(i*numOutputBits+numOutputBits-1-b),0); /* first bit is MSB */ 125 | } else { 126 | doubleElementR(BMVector,q,0) -= doubleElementR(LLRVector,(i*numOutputBits+numOutputBits-1-b),0); /* first bit is MSB */ 127 | } 128 | } 129 | /* adjust to max-log metric */ 130 | doubleElementR(BMVector,q,0) = 0.5*doubleElementR(BMVector,q,0); 131 | } 132 | 133 | /* ------------------ carry out one trellis step ------------------------- */ 134 | for (j = 0;j doubleElementR(AlphaMetric,nextState,i+1)) doubleElementR(AlphaMetric, nextState, i+1) = curMetric; 143 | } /* for p */ 144 | } /* for j */ 145 | } /* for i */ 146 | 147 | /* ------------------------------------------------------------------- */ 148 | /* Beta-Metric (Backward Iteration) */ 149 | /* ------------------------------------------------------------------- */ 150 | BetaMetric = mxCreateDoubleMatrix(numStates, 1, mxREAL); 151 | oldBetaMetric = mxCreateDoubleMatrix(numStates, 1, mxREAL); 152 | for(j = 0;j0; i-=1) { 155 | int tmp,q,b; 156 | /* precompute the Branch Metric for each output Symbol, based on the LLRs */ 157 | for(q=0;q=0;b--) { 161 | if(tmp>=(1 << b)) { 162 | tmp = tmp - (1 << b); 163 | doubleElementR(BMVector,q,0) += doubleElementR(LLRVector,(i*numOutputBits-1-b),0); /* first bit is MSB */ 164 | } else { 165 | doubleElementR(BMVector,q,0) -= doubleElementR(LLRVector,(i*numOutputBits-1-b),0); /* first bit is MSB */ 166 | } 167 | } 168 | /* adjust to max-log metric */ 169 | doubleElementR(BMVector,q,0) = 0.5*doubleElementR(BMVector,q,0); 170 | } 171 | 172 | /* ------------------ carry out one trellis step ------------------------- */ 173 | for (j = 0;j doubleElementR(BetaMetric,j,0)) || (p==0)) 185 | doubleElementR(BetaMetric, j, 0) = curMetric; 186 | 187 | /* ---- output metric of current state ---- */ 188 | outputMetric = curMetric + doubleElementR(AlphaMetric, j, i-1); 189 | 190 | /* ---- LLR output of input bits ---- */ 191 | for (b=numInputBits-1;b>=0;b--) 192 | { 193 | int BitIdx; 194 | BitIdx = numInputBits*i-1-b; 195 | if (p & (1<doubleElementR(xhatk1,BitIdx,0)) doubleElementR(xhatk1,BitIdx,0) = outputMetric; 198 | } else 199 | { /* bit is -1 */ 200 | if (outputMetric>doubleElementR(xhatk0,BitIdx,0)) doubleElementR(xhatk0,BitIdx,0) = outputMetric; 201 | } 202 | } 203 | 204 | /* ---- a posteriori LLR output calculation ---- */ 205 | bit = (int) doubleElementR(outSymbolsArr,j,p); 206 | for (b=numOutputBits-1;b>=0;b--) 207 | { 208 | int BitIdx; 209 | BitIdx = i*numOutputBits-1-b; 210 | if (bit>=(1<doubleElementR(llr_1,BitIdx,0)) doubleElementR(llr_1,BitIdx,0) = outputMetric; 214 | } else 215 | { /* bit is -1 */ 216 | if (outputMetric>doubleElementR(llr_0,BitIdx,0)) doubleElementR(llr_0,BitIdx,0) = outputMetric; 217 | } 218 | } 219 | 220 | } /* for p input symbols */ 221 | } /* for j states */ 222 | 223 | /* copy the beta state metric */ 224 | for(q=0;qdoubleElementR(xhatk0,i,0)) 232 | doubleElementR(xhatk,i,0) = 1.0; 233 | else 234 | doubleElementR(xhatk,i,0) = 0.0; 235 | 236 | /* Compute a posteriori information [LLRs] */ 237 | for (i=0;i 10 | #include 11 | #include 12 | 13 | #define MAX(a,b) ((a)>(b) ? (a) : (b)) 14 | #define NIL 0L 15 | #define nil 0L 16 | 17 | typedef mxArray * mxArrayPtr; 18 | typedef const mxArray * mxArrayConstPtr; 19 | typedef double * doublePtr; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /BCJR/mexutils.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | ** Title : Interface for mexutils.c 3 | ** File : mexutils.c 4 | ** =======================================================================*/ 5 | 6 | #ifndef __MEXUTILS__ 7 | #define __MEXUTILS__ 8 | 9 | #include "mexheader.h" 10 | 11 | typedef struct { 12 | long buflen; 13 | doublePtr r, i; 14 | } arrBase; 15 | 16 | 17 | void mxCopyArray(const mxArray* arr1, const mxArray* arr2); 18 | void* getColPr(const mxArray* arr, unsigned int col); 19 | double *getDoubleColPr(const mxArray* arr, unsigned int col); 20 | UINT16_T *getUint16ColPr(const mxArray* arr, unsigned int col); 21 | 22 | 23 | void getBase(mxArrayConstPtr arr, arrBase *base); 24 | void setBase(mxArrayPtr arr, arrBase const *base); 25 | void setBaseOffset(mxArrayPtr arr, arrBase const *base, long offset); 26 | 27 | 28 | /* Useful accessors for array elements */ 29 | doublePtr arrGetElementPtrR(mxArrayConstPtr arr, long r, long c); 30 | doublePtr arrGetElementPtrI(mxArrayConstPtr arr, long r, long c); 31 | 32 | #ifdef NDEBUG 33 | #define doubleElementR(arr, m, n) (*(mxGetPr(arr) + ((unsigned long)n)*mxGetM(arr) + ((unsigned long)m))) 34 | #define doubleElementI(arr, m, n) (*(mxGetPi(arr) + ((unsigned long)n)*mxGetM(arr) + ((unsigned long)m))) 35 | #else 36 | #define doubleElementR(arr, m, n) (*arrGetElementPtrR(arr, m, n)) 37 | #define doubleElementI(arr, m, n) (*arrGetElementPtrI(arr, m, n)) 38 | #endif 39 | 40 | #define doubleElement(arr, m, n) doubleElementR(arr, m, n) 41 | 42 | 43 | #define uint16Element(arr, m, n) (*(((UINT16_T*)mxGetData(arr)) + ((unsigned long)n)*mxGetM(arr) + ((unsigned long)m))) 44 | 45 | #define dumpMatrix(a) dumpMatrix_(a, #a) 46 | void dumpMatrix_(const mxArray* arr, const char *s); 47 | 48 | /* Debugging functions */ 49 | #define debugInt(a) mexPrintf(#a " = %d\n", a) 50 | 51 | #define userAssert(test, msg) if(!(test)) mexErrMsgTxt(msg) 52 | void userAssertValidArgument(const mxArray *prhs[], unsigned int ind, unsigned int m, unsigned int n, mxClassID class); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simulator for "Linear precoding with low-resolution DACs for massive MU-MIMO-OFDM downlink" 2 | (c) 2019 Sven Jacobsson and Christoph Studer; 3 | e-mail: sven.jacobsson@ericsson.com and studer@cornell.edu 4 | 5 | ### Important information 6 | 7 | If you are thinking of contacting us, please do not e-mail the author to ask for download instructions, installation guidelines, or the simulator itself. The simulator itself is well-documented and provides the essential information about the code. Note that we will NOT help to debug user-generated code that was not included in the provided software package. If, however, you notice a bug in our code, please be so kind to contact the Author. 8 | 9 | The software package is supplied "as is," without any accompanying support services, maintenance, or future updates. We make no warranties, explicit or implicit, that the software contained in this package is free of error or that it will meet your requirements for any particular application. It should not be relied on for any purpose where incorrect results could result in loss of property, personal injury, liability or whatsoever. If you do use our software for any such purpose, it is at your own risk. The authors disclaim all liability of any kind, either direct or consequential, resulting from your use of these programs. 10 | 11 | If you are using the simulator (or parts of it) for a publication, then you *must* cite our paper: 12 | 13 | [1] S. Jacobsson, G. Durisi, M. Coldrey, and C. Studer, “Linear precoding with low-resolution DACs for massive MU-MIMO-OFDM downlink,” IEEE Trans. Wireless Commun., to appear. 14 | 15 | and clearly mention this in your paper. 16 | 17 | ### How to start a simulation 18 | 19 | Simply run 20 | 21 | ```sh 22 | precoder_linear_ofdm_sim 23 | ``` 24 | 25 | which starts a simulation for a massive MU-MIMO-OFDM system (with 32 BS antennas, 8 users, 300 occupied subcarriers, and OSR = 1.7) with QPSK modulation using ZF precoding (for the case of 1-bit DACs and infinite resolution). 26 | 27 | The simulator runs with predefined parameters. You can specify your own system and simulation parameters by passing your own "par"-structure (see the simulator for an example). Note that we use default parameters for the considered system configuration; if you want to run the simulation with different parameters, then please refer to the MATLAB code for other parameter settings. 28 | 29 | We highly recommend you to execute the code step-by-step (using MATLAB's debug mode) in order to get a detailed understanding of the simulator. 30 | 31 | ### Version history 32 | - Version 0.1: sven.jacobsson@ericsson.com - simplified/commented code for GitHub 33 | -------------------------------------------------------------------------------- /precoder_linear_ofdm_sim.m: -------------------------------------------------------------------------------- 1 | function res = precoder_linear_ofdm_sim(varargin) 2 | % ========================================================================= 3 | % Simulator for "Linear precoding with low-resolution DACs for massive 4 | % MU-MIMO-OFDM downlink" 5 | % ------------------------------------------------------------------------- 6 | % Revision history: 7 | % - jan-17-2019 v0.1 sj: simplified/commented code for GitHub 8 | % ------------------------------------------------------------------------- 9 | % (c) 2019 Sven Jacobsson and Christoph Studer 10 | % e-mail: sven.jacobsson@ericsson.com and studer@cornell.edu 11 | % ------------------------------------------------------------------------- 12 | % If you this simulator or parts of it, then you must cite our paper: 13 | % -- S. Jacobsson, G. Durisi, M. Coldrey, and C. Studer, “Linear precoding 14 | % with low-resolution DACs for massive MU-MIMO-OFDM downlink,” IEEE Trans. 15 | % Wireless Commun., Jun., to appear. 16 | %========================================================================= 17 | 18 | % -- set up default/custom parameters 19 | if isempty(varargin) 20 | 21 | % set default simulation parameters 22 | disp('using default simulation settings and parameters...'); 23 | par.runId = 1; % simulation ID (used to reproduce results) 24 | par.save = false; % save results (true,false) 25 | par.plot = true; % plot results (true,false) 26 | par.B = 32; % number of BS antenns 27 | par.U = 8; % number of single-antenna UEs 28 | par.S = 300; % number of occupied subcarriers: '72', '144', '300', '600', '1200' 29 | par.T = 10; % number of channels taps 30 | par.L = 2; % number of DAC levels per I and Q dimension 31 | par.approximation = 'rounding'; % distortion model: 'none', 'diagonal', 'rounding' 32 | par.sampling_rate_multiplier = 1; % sampling rate multiplier 33 | par.nrof_channels = 1e1; % number of channel realizations 34 | par.nrof_ofdm_symbols = 1e1; % number of OFDM symbols per channel realization 35 | par.precoder = {'ZFQ', 'ZFI'}; % select precoding scheme(s) to be evaluated 36 | par.SNRdB_list = -10:3:20; % list of SNR [dB] values to be simulated 37 | par.relerr = 0; % relative channel estimate error 38 | par.mod = 'QPSK'; % modulation type: 'QPSK', '8PSK', '16QAM','64QAM' 39 | par.code.rate = 1; % code rate: '1/2','3/4','2/3','5/6', '1' 40 | 41 | else 42 | 43 | % use custom simulation parameters 44 | disp('use custom simulation settings and parameters...') 45 | par = varargin{1}; % load custom simulation parameters 46 | 47 | end 48 | 49 | % -- initialization 50 | 51 | % use runId random seed (enables reproducibility) 52 | rng(par.runId); 53 | 54 | % set unique filename 55 | par.simName = ['PRE_',num2str(par.U),'x',num2str(par.B),'_',num2str(par.mod),'_',num2str(par.runId),' - ',datestr(clock,0)]; % simulation name (used for saving results) 56 | 57 | % -- plotting properties 58 | if par.plot == 1 59 | 60 | % number of rows/columns in constellation/spectrum plots 61 | if length(par.precoder)==1 62 | nd = 1; 63 | elseif length(par.precoder)<=6 64 | nd = 2; 65 | elseif length(par.precoder)<=9 66 | nd = 3; 67 | elseif length(par.precoder)<=12 68 | nd = 4; 69 | end 70 | md = ceil(length(par.precoder)/nd); 71 | 72 | % marker properties 73 | marker_style = {'--o','--s','--v','--+','--<','-->','--x','--^','--*','--d','--h','--p'}; 74 | marker_color = [0.000, 0.447, 0.741;0.850, 0.325, 0.098; 0.929, 0.694, 0.125; 0.494, 0.184, 0.556; 75 | 0.466, 0.674, 0.188; 0.301, 0.745, 0.933; 0.635, 0.078, 0.184]; 76 | 77 | end 78 | 79 | % set up constellation alphabets 80 | switch (par.mod) 81 | case 'QPSK' 82 | par.symbols = [ -1-1i,-1+1i,+1-1i,+1+1i ]; 83 | case '8PSK' 84 | par.symbols = [... 85 | exp(1i*2*pi/8*0), exp(1i*2*pi/8*1), ... 86 | exp(1i*2*pi/8*7), exp(1i*2*pi/8*6), ... 87 | exp(1i*2*pi/8*3), exp(1i*2*pi/8*2), ... 88 | exp(1i*2*pi/8*4), exp(1i*2*pi/8*5)]; 89 | case '16QAM' 90 | par.symbols = [... 91 | -3-3i,-3-1i,-3+3i,-3+1i, ... 92 | -1-3i,-1-1i,-1+3i,-1+1i, ... 93 | +3-3i,+3-1i,+3+3i,+3+1i, ... 94 | +1-3i,+1-1i,+1+3i,+1+1i ]; 95 | case '64QAM' 96 | par.symbols = [... 97 | -7-7i,-7-5i,-7-1i,-7-3i,-7+7i,-7+5i,-7+1i,-7+3i, ... 98 | -5-7i,-5-5i,-5-1i,-5-3i,-5+7i,-5+5i,-5+1i,-5+3i, ... 99 | -1-7i,-1-5i,-1-1i,-1-3i,-1+7i,-1+5i,-1+1i,-1+3i, ... 100 | -3-7i,-3-5i,-3-1i,-3-3i,-3+7i,-3+5i,-3+1i,-3+3i, ... 101 | +7-7i,+7-5i,+7-1i,+7-3i,+7+7i,+7+5i,+7+1i,+7+3i, ... 102 | +5-7i,+5-5i,+5-1i,+5-3i,+5+7i,+5+5i,+5+1i,+5+3i, ... 103 | +1-7i,+1-5i,+1-1i,+1-3i,+1+7i,+1+5i,+1+1i,+1+3i, ... 104 | +3-7i,+3-5i,+3-1i,+3-3i,+3+7i,+3+5i,+3+1i,+3+3i ]; 105 | end 106 | 107 | % normalize symbol energy 108 | par.symbols = par.symbols/sqrt(mean(abs(par.symbols).^2)); 109 | 110 | % subcarrier allocation and size of discrete Fourier transform 111 | switch par.S 112 | case 72 113 | par.N = 128*par.sampling_rate_multiplier; 114 | par.submap = [par.N-par.S/2+1:par.N, 2:ceil(par.S/2)+1]; 115 | case 144 116 | par.N = 256*par.sampling_rate_multiplier; 117 | par.submap = [par.N-par.S/2+1:par.N, 2:ceil(par.S/2)+1]; 118 | case 300 119 | par.N = 512*par.sampling_rate_multiplier; 120 | par.submap = [par.N-par.S/2+1:par.N, 2:ceil(par.S/2)+1]; 121 | case 600 122 | par.N = 1024*par.sampling_rate_multiplier; 123 | par.submap = [par.N-par.S/2+1:par.N, 2:ceil(par.S/2)+1]; 124 | case 900 125 | par.N = 1536*par.sampling_rate_multiplier; 126 | par.submap = [par.N-par.S/2+1:par.N, 2:ceil(par.S/2)+1]; 127 | case 1200 128 | par.N = 2048*par.sampling_rate_multiplier; 129 | par.submap = [par.N-par.S/2+1:par.N, 2:ceil(par.S/2)+1]; 130 | end 131 | 132 | % sampling rate 133 | par.samplingrate = 15e3*par.N; 134 | 135 | % precompute bit labels 136 | par.card = length(par.symbols); % cardinality 137 | par.bps = log2(par.card); % number of bits per symbol 138 | par.bits = de2bi(0:par.card-1,par.bps,'left-msb'); % symbols-to-bits 139 | 140 | % -- code parameters 141 | if par.code.rate < 1 142 | 143 | addpath('BCJR'); % add path to decoder 144 | 145 | par.code.constraintlength = 7; % constraint Length 146 | par.code.generator = [133 171]; % generator polynomial 147 | par.code.trellis = poly2trellis(par.code.constraintlength, par.code.generator); % trellis 148 | par.code.n = par.nrof_ofdm_symbols*par.bps*par.S; % length of code word 149 | par.code.m = floor(par.code.n*par.code.rate); % length of message 150 | for u=1:par.U 151 | par.code.interleaverperm(u,:) = randperm(par.code.n); % random interleaver 152 | end 153 | 154 | % puncturing pattern 155 | switch (par.code.rate) 156 | case 1/2 157 | par.code.puncturing.period = 1; 158 | par.code.puncturing.pattern = 1; 159 | case 3/4 160 | par.code.puncturing.period = 18; 161 | par.code.puncturing.pattern = [1 2 3 6 7 8 9 12 13 14 15 18]; 162 | case 5/6 163 | par.code.puncturing.period = 10; 164 | par.code.puncturing.pattern = [1 2 3 6 7 10]; 165 | otherwise 166 | error('coding rate not supported') 167 | end 168 | 169 | % extend puncturing pattern 170 | nrofcodedbits = par.code.n; % bits per terminal 171 | patlen = length(par.code.puncturing.pattern); 172 | for i=1:ceil(nrofcodedbits/patlen) 173 | par.code.puncturing.index(patlen*(i-1)+1:patlen*i) = ... 174 | (i-1)*par.code.puncturing.period+par.code.puncturing.pattern; 175 | end 176 | par.code.puncturing.index(:,nrofcodedbits+1:end) = []; 177 | 178 | end 179 | 180 | % quantizer parameters 181 | par.clip_prb = 1e-3; % clipping probability (needs to be adjusted with the number of levels) 182 | par.clip_lvl = sqrt(par.S/par.N)/sqrt(2*par.B) * qfuncinv(par.clip_prb/2); % clipping level 183 | par.lsb = 2*par.clip_lvl/par.L; % least significant bit 184 | par.labels = par.lsb *((0:par.L-1) - (par.L-1)/2); % uniform quantization labels 185 | par.thresholds = [-10^100, bsxfun(@minus, par.labels(:,2:end), par.lsb/2), 10^100]; % uniform quantization thresholds 186 | par.alpha = sqrt(2*par.B/(par.S/par.N)*sum(par.labels.^2.* ... 187 | (normcdf(par.thresholds(2:end)*sqrt(2*par.B)/sqrt(par.S/par.N)) ... 188 | -normcdf(par.thresholds(1:end-1)*sqrt(2*par.B)/sqrt(par.S/par.N)))))^-1; % normalization constant (approximate, but accurate) 189 | par.labels = par.alpha*par.labels; % normalize quantization labels 190 | 191 | % clipping and quantization 192 | par.clipper = @(x) max(min(x,par.clip_lvl-par.lsb/1e5),-(par.clip_lvl-par.lsb/1e5)); % clipper 193 | if mod(par.L,2) == 0 194 | par.quantizer = @(x) par.alpha * (par.lsb*floor(par.clipper(x)/par.lsb) + par.lsb/2); % midrise quantizer (without clipping) 195 | else 196 | par.quantizer = @(x) par.alpha * par.lsb*floor(par.clipper(x)/par.lsb + 1/2); % midtread quantizer (without clipping) 197 | end 198 | par.quantizer = @(x) par.quantizer(par.clipper(real(x))) + 1i*par.quantizer(par.clipper(imag(x))); % quantizer 199 | 200 | % initialize result arrays 201 | [res.BER_uncoded, res.BER_coded] = deal(zeros(length(par.precoder),length(par.SNRdB_list))); 202 | [res.BER_uncoded_approx, res.sumrate_approx] = deal(zeros(length(par.precoder),length(par.SNRdB_list))); 203 | [res.TxAvgPower, res.RxAvgPower, res.TxMaxPower, res.RxMaxPower] = deal(zeros(length(par.precoder),1)); 204 | pow_xf = zeros(length(par.precoder), par.N); 205 | pow_xf_emp = zeros(length(par.precoder), par.N); 206 | pow_yf = zeros(length(par.precoder), par.N); 207 | pow_yf_emp = zeros(length(par.precoder), par.N); 208 | 209 | % save detected symbols for later viewing (if not too many of them) 210 | if par.nrof_ofdm_symbols * par.nrof_channels * par.S <= 1e5 211 | shat_list = nan(par.U, par.S*par.nrof_ofdm_symbols,par.nrof_channels,length(par.precoder)); 212 | end 213 | 214 | % track simulation time 215 | time_elapsed = 0; tic; 216 | 217 | % -- start of simulation 218 | 219 | fprintf(' running numerical simulation. \n'); 220 | 221 | for cc = 1:par.nrof_channels 222 | 223 | % time-domain channel matrix 224 | Ht = sqrt(0.5/par.T)*(randn(par.U,par.B,par.T) + 1i*randn(par.U,par.B,par.T)); 225 | 226 | % channel estimation error 227 | if par.relerr > 0 228 | Ht_est = sqrt(1-par.relerr)*Ht + sqrt(par.relerr/2)*(randn(par.U,par.B,par.T) + 1i*randn(par.U,par.B,par.T)); 229 | else 230 | Ht_est = Ht; 231 | end 232 | 233 | % account for sampling rate multiplier 234 | if par.T > 1 235 | Ht = permute(upsample(permute(Ht,[3,2,1]),round(par.sampling_rate_multiplier)),[3,2,1]); 236 | if par.relerr > 0 237 | Ht_est = permute(upsample(permute(Ht_est,[3,2,1]),round(par.sampling_rate_multiplier)),[3,2,1]); 238 | else 239 | Ht_est = Ht; 240 | end 241 | end 242 | 243 | % frequency-domain channel matrix 244 | if par.T > 1 245 | Hf = fft(Ht,par.N,3); 246 | Hf_est = fft(Ht_est,par.N,3); 247 | else 248 | Hf = nan(par.U,par.B,par.N); 249 | Hf_est = nan(par.U,par.B,par.N); 250 | for n = 1:par.N 251 | Hf(:,:,n) = Ht; 252 | Hf_est(:,:,n) = Ht_est; 253 | end 254 | end 255 | 256 | % generate noise vector 257 | nt = sqrt(0.5)*(randn(par.U,par.N,par.nrof_ofdm_symbols)+1i*randn(par.U,par.N,par.nrof_ofdm_symbols)); % time domain 258 | nf = sqrt(1/par.N)*fft(nt,par.N,2); % frequency domain 259 | 260 | % information bits and coded bits 261 | if par.code.rate < 1 262 | par.code.padbits = ceil(par.U*par.code.n*par.code.rate)-floor(par.U*par.code.n*par.code.rate); 263 | b_data = round(rand(par.U,ceil(par.code.n*par.code.rate))); 264 | b_data(1:par.U,size(b_data,2)+1-(par.code.constraintlength-1)-par.code.padbits:size(b_data,2)) = 0; 265 | b_encoded = nan(par.U, par.code.n); % encoded bits 266 | for u = 1:par.U 267 | b_trellis = convenc(b_data(u,:),par.code.trellis); % encoding 268 | b_punctured = b_trellis(par.code.puncturing.index); % puncturing 269 | b_encoded(u,:) = b_punctured(par.code.interleaverperm(u,:)); % interleaving 270 | end 271 | else 272 | b_encoded = randi([0 1], par.U, par.bps*par.S*par.nrof_ofdm_symbols); % data bits 273 | end 274 | 275 | % map bits to subcarriers 276 | s = zeros(par.U,par.N,par.nrof_ofdm_symbols); 277 | for u = 1:par.U 278 | idx = reshape(bi2de(reshape(b_encoded(u,:),par.bps,[])','left-msb')+1,par.S,[]); % index 279 | s(u,par.submap,:) = par.symbols(idx); 280 | end 281 | 282 | % algorithm loop 283 | for pp = 1:length(par.precoder) 284 | 285 | % ZF precoding 286 | zf = zeros(par.B,par.N,par.nrof_ofdm_symbols); % precoded frequency-domain vector 287 | Pf = zeros(par.B,par.U,par.N); % precoding matrix 288 | [zf(:,par.submap,:), beta, Pf(:,:,par.submap)] = ZF(par, s(:,par.submap,:), Hf_est(:,:,par.submap)); 289 | zt = sqrt(par.N)*ifft(zf, par.N, 2); % transform to time domain 290 | 291 | % quantization 292 | if ismember(par.precoder{pp}, 'ZFQ') 293 | xt = par.quantizer(zt); % quantize time-domain signal 294 | bussgang_gain = par.alpha*par.lsb*sqrt(par.B/(par.S/par.N)/pi)* ... 295 | sum(exp(-par.B/(par.S/par.N)*par.lsb^2*((1:par.L-1)-par.L/2).^2)); % Bussgang gain 296 | else 297 | xt = zt; % do nothing 298 | bussgang_gain = 1; % Bussgang gain 299 | end 300 | 301 | if ~strcmpi(par.approximation, 'none') 302 | 303 | % input covariance matrix 304 | Czf = zeros(par.B,par.B,par.N); 305 | for k = par.submap 306 | Czf(:,:,k) = Pf(:,:,k)*Pf(:,:,k)'; 307 | end 308 | Czt = fft(Czf, par.N, 3)/par.N; 309 | 310 | % linear gain matrix and output covariance matrix 311 | if ismember(par.precoder{pp}, 'ZFQ') 312 | [G, Cxt, Cdt] = bussgang_decomposition(par, Czt); 313 | else 314 | G = eye(par.B); 315 | Cxt = Czt; 316 | Cdt = zeros(size(Czt)); 317 | end 318 | Cxf = par.N*ifft(Cxt, par.N, 3); 319 | Cdf = par.N*ifft(Cdt, par.N, 3); 320 | 321 | % signal-to-interference-noise-and-distortion ratio (SINDR) 322 | sindr = nan(par.U,par.S,length(par.SNRdB_list)); 323 | for k = 1:par.S 324 | S = repmat(diag(abs(Hf(:,:,par.submap(k))*G*Pf(:,:,par.submap(k))).^2),1,length(par.SNRdB_list)); % signal power 325 | I = repmat(sum(abs(Hf(:,:,par.submap(k))*G*Pf(:,:,par.submap(k))).^2,2),1,length(par.SNRdB_list)) - S; % interference power 326 | D = repmat(real(diag(Hf(:,:,par.submap(k))*Cdf(:,:,par.submap(k))*Hf(:,:,par.submap(k))')),1,length(par.SNRdB_list)); % distortion power 327 | N = repmat(10.^(-par.SNRdB_list/10),par.U,1); % noise power 328 | sindr(:,k,:) = S ./ (I + D + N); 329 | end 330 | 331 | % compute uncoded BER and achievable rate (analytical) 332 | res.BER_uncoded_approx(pp,:) = res.BER_uncoded_approx(pp,:) + shiftdim(mean(mean(qfunc(sqrt(sindr)),1),2),1)/par.nrof_channels; 333 | res.sumrate_approx(pp,:) = res.sumrate_approx(pp,:) + shiftdim(sum(sum(log2(1 + sindr),1),2),1)/par.nrof_channels/par.S; 334 | 335 | % power spectral density (analytical) 336 | for n = 1:par.N 337 | pow_xf(pp,n) = pow_xf(pp,n) + mean(diag(Cxf(:,:,n)))/par.nrof_channels; 338 | pow_yf(pp,n) = pow_yf(pp,n) + mean(diag(Hf(:,:,n)*Cxf(:,:,n)*Hf(:,:,n)'))/par.nrof_channels; 339 | end 340 | 341 | end 342 | 343 | for ss = 1:length(par.SNRdB_list) % SNR loop 344 | 345 | % noise variance 346 | N0 = 10.^(-par.SNRdB_list(ss)/10); 347 | 348 | % convert transmit signal to frequency domain 349 | xf = 1/sqrt(par.N)*fft(xt, par.N, 2); 350 | 351 | % transmit signal over wireless channel 352 | Hxf = nan(par.U,par.N,par.nrof_ofdm_symbols); 353 | for n = 1:par.N 354 | Hxf(:,n,:) = Hf(:,:,n)*squeeze(xf(:,n,:)); 355 | end 356 | 357 | % add noise 358 | yf = Hxf + sqrt(N0)*nf; 359 | 360 | % extract transmitted/received power 361 | res.TxMaxPower(pp) = max(res.TxMaxPower(pp), max(squeeze(sum(sum(abs(xf).^2,1),2)))/par.S); 362 | res.TxAvgPower(pp) = res.TxAvgPower(pp) + sum(sum(squeeze(sum(abs(xf).^2))))/par.S/par.nrof_ofdm_symbols/par.nrof_channels/length(par.SNRdB_list); 363 | 364 | % estimated symbols (remove guard carriers and compensate for gain loss) 365 | shat = beta * yf(:,par.submap,:) / bussgang_gain; % = yf(:,par.submap,:) ./ sqrt(mean(abs(yf(:,par.submap,:)).^2,2) - N0); 366 | 367 | % symbol detection and decoding 368 | if par.code.rate == 1 % nearest-neighbor 369 | b_detected = nan(size(b_encoded)); 370 | for u = 1:par.U 371 | [~,idxhat] = min(abs(reshape(shat(u,:,:),[],1)*ones(1,length(par.symbols))-ones(par.S*par.nrof_ofdm_symbols,1)*par.symbols).^2,[],2); 372 | b_detected(u,:) = reshape(par.bits(idxhat,:)',1,[]); 373 | end 374 | else % log-max BCJR 375 | llr = detector(par,reshape(shat,par.U,par.S*par.nrof_ofdm_symbols),N0); 376 | b_detected = (llr>0); 377 | [~,b_decoded] = decoder(par,llr); 378 | end 379 | 380 | % compute error metrics 381 | res.BER_uncoded(pp,ss) = res.BER_uncoded(pp,ss) + sum(sum(b_encoded~=b_detected))/par.U/par.bps/par.S/par.nrof_ofdm_symbols/par.nrof_channels; 382 | if par.code.rate < 1 383 | res.BER_coded(pp,ss) = res.BER_coded(pp,ss) + sum(sum(b_data~=b_decoded))/par.U/par.code.m/par.nrof_channels; 384 | end 385 | 386 | end % end of SNR loop 387 | 388 | % save data for later viewing 389 | if par.nrof_ofdm_symbols * par.nrof_channels * par.S <= 1e5 390 | shat_list(:,:,cc,pp) = reshape(shat,par.U,[]); 391 | end 392 | 393 | % power spectral density (simulated) 394 | pow_xf_emp(pp,:) = pow_xf_emp(pp,:) + mean(mean(abs(xf).^2,3),1)/par.nrof_channels; 395 | pow_yf_emp(pp,:) = pow_yf_emp(pp,:) + mean(mean(abs(Hxf).^2,3),1)/par.nrof_channels; 396 | 397 | end % end of algorithm loop 398 | 399 | % keep track of simulation time 400 | if toc>10 401 | time=toc; 402 | time_elapsed = time_elapsed + time; 403 | fprintf('\t estimated remaining simulation time: %3.0f min. \n',time_elapsed*(par.nrof_channels/cc-1)/60); 404 | tic 405 | end 406 | 407 | end % end of channels loop 408 | 409 | fprintf('\t numerical simulation finished after %.2f seconds. \n', time_elapsed); 410 | 411 | % -- end of simulation 412 | 413 | if par.plot 414 | 415 | close all; 416 | 417 | % plot tranmitted and received power spectrum 418 | ylim_min = -50; ylim_max = ceil(10*log10(max([max(abs(pow_xf_emp(pp,:))), max(abs(pow_yf_emp(pp,:)))])))+10; % limits 419 | fig_txspec = figure; set(fig_txspec,'name','Tx Spectrum','numbertitle','off'); % transmitted spectrum 420 | for pp = 1:length(par.precoder) 421 | subplot(md,nd,pp); hold all; 422 | plot(15e-3*(-par.N/2:par.N/2-1),10*log10(eps+fftshift(pow_xf_emp(pp,:)+eps)),'-x','color',marker_color(pp,:)); 423 | if ~strcmpi(par.approximation, 'none') 424 | plot(15e-3*(-par.N/2:par.N/2-1),10*log10(fftshift(abs(pow_xf(pp,:))+eps)),'k-'); 425 | set(legend('Simulated', 'Analytical'), 'fontsize',14, 'location', 'southeast'); 426 | else 427 | set(legend('Simulated'), 'fontsize',14, 'location', 'southeast'); 428 | end 429 | xlim(15e-3*[-par.N/2, par.N/2-1]); ylim([ylim_min, ylim_max]); 430 | xlabel('Frequency [MHz]','fontsize',14); ylabel('PSD [dB]','fontsize',14); box on; grid on; 431 | title(par.precoder{pp},'fontsize',12); 432 | end 433 | fig_rxspec = figure; set(fig_rxspec,'name','Rx Spectrum','numbertitle','off'); % received spectrum 434 | for pp = 1:length(par.precoder) 435 | subplot(md,nd,pp); hold all; 436 | plot(15e-3*(-par.N/2:par.N/2-1),10*log10(eps+fftshift(pow_yf_emp(pp,:)+eps)),'-x','color',marker_color(pp,:)); 437 | if ~strcmpi(par.approximation, 'none') 438 | plot(15e-3*(-par.N/2:par.N/2-1),10*log10(fftshift(abs(pow_yf(pp,:))+eps)),'k-'); 439 | set(legend('Simulated', 'Analytical'), 'fontsize',14, 'location', 'southeast'); 440 | else 441 | set(legend('Simulated'), 'fontsize',14, 'location', 'southeast'); 442 | end 443 | xlim(15e-3*[-par.N/2, par.N/2-1]); ylim([ylim_min, ylim_max]); 444 | xlabel('Frequency [MHz]','fontsize',14); ylabel('PSD [dB]','fontsize',14); box on; grid on; 445 | title(par.precoder{pp},'fontsize',12); 446 | end 447 | 448 | % plot constellation 449 | if par.nrof_ofdm_symbols * par.nrof_channels * par.S <= 1e5 450 | fig_const = figure; set(fig_const,'name','Const.','numbertitle','off'); 451 | for pp = 1:length(par.precoder) 452 | subplot(md,nd,pp); hold all; 453 | plot(reshape(shat_list(:,:,:,pp),1,[]),'*', 'color', marker_color(pp,:),'markersize',7); 454 | plot(par.symbols, 'ko','MarkerSize',7); 455 | axis(max(reshape(abs(shat_list(:,:,:,pp)),1,[]))*[-1 1 -1 1]); 456 | axis square; box on; 457 | title(par.precoder{pp},'fontsize',12); 458 | xlabel(['P_{avg}= ',num2str(pow2db(res.TxAvgPower(pp)),'%0.2f'),' dB and P_{max}= ',num2str(pow2db(res.TxMaxPower(pp)),'%0.2f'),' dB'],'fontsize',12); 459 | end 460 | 461 | end 462 | 463 | % plot uncoded BER 464 | fig_uncodedber = figure; set(fig_uncodedber,'name','Uncoded BER','numbertitle','off'); 465 | for pp=1:length(par.precoder) % simulated BER 466 | semilogy(par.SNRdB_list,res.BER_uncoded(pp,:),marker_style{pp},'color',marker_color(pp,:),'LineWidth',2); hold on; 467 | end 468 | if strcmpi(par.mod, 'QPSK') && ~strcmpi(par.approximation, 'none') 469 | for pp=1:length(par.precoder) 470 | semilogy(par.SNRdB_list,res.BER_uncoded_approx(pp,:),'-','color',marker_color(pp,:),'LineWidth',2); hold on; 471 | end 472 | end 473 | grid on; box on; 474 | xlabel('SNR [dB]','FontSize',12) 475 | ylabel('uncoded BER','FontSize',12); 476 | if length(par.SNRdB_list) > 1 477 | axis([min(par.SNRdB_list) max(par.SNRdB_list) 1e-4 1]); 478 | end 479 | if strcmpi(par.mod, 'QPSK') && ~strcmpi(par.approximation, 'none') 480 | leg = [par.precoder, par.precoder]; 481 | for i = 1:length(leg)/2 482 | leg{i} = [par.precoder{i}, ' (simulated)']; 483 | leg{i + length(leg)/2} = [par.precoder{i}, ' (analytical)']; 484 | end 485 | legend(leg,'FontSize',12,'location','southwest'); 486 | else 487 | leg = [par.precoder, par.precoder]; 488 | for i = 1:length(leg) 489 | leg{i} = [par.precoder{i}, ' (simulated)']; 490 | end 491 | legend(leg,'FontSize',12,'location','southwest'); 492 | end 493 | 494 | set(gca,'FontSize',12); 495 | 496 | % plot coded BER 497 | if par.code.rate < 1 498 | fig_codedber = figure; set(fig_codedber,'name','Coded BER','numbertitle','off'); 499 | for pp=1:length(par.precoder) % simulated BER 500 | semilogy(par.SNRdB_list,res.BER_coded(pp,:),marker_style{pp},'color',marker_color(pp,:),'LineWidth',2); hold on; 501 | end 502 | grid on; box on; 503 | xlabel('SNR [dB]','FontSize',12) 504 | ylabel('coded BER','FontSize',12); 505 | if length(par.SNRdB_list) > 1 506 | axis([min(par.SNRdB_list) max(par.SNRdB_list) 1e-6 1]); 507 | end 508 | legend(par.precoder,'FontSize',12,'location','southwest') 509 | set(gca,'FontSize',12); 510 | end 511 | 512 | end 513 | 514 | % save final results 515 | if par.save 516 | save(par.simName,'par','res'); 517 | end 518 | 519 | end 520 | 521 | function [xf, beta, Pf] = ZF(par, s, Hf) 522 | 523 | % initialize vectors 524 | xf = zeros(par.B,par.S,par.nrof_ofdm_symbols); 525 | Pf = nan(par.B,par.U,par.S); 526 | beta = 0; 527 | 528 | % precoding 529 | for k = 1:par.S 530 | Pf(:,:,k) = Hf(:,:,k)'/(Hf(:,:,k)*Hf(:,:,k)'); % precoding matrix 531 | xf(:,k,:) = Pf(:,:,k)*reshape(s(:,k,:),par.U,[]); % precoded vector 532 | beta = beta + trace(Pf(:,:,k)*Pf(:,:,k)')/par.S; % precoding factor (squared) 533 | end 534 | beta = sqrt(beta); 535 | 536 | % scale output 537 | xf = xf/beta; 538 | Pf = Pf/beta; 539 | 540 | end 541 | 542 | function [G, Cxt, Cdt] = bussgang_decomposition(par, Czt) 543 | 544 | % diagonal of the zero-lag covariance matrix 545 | Dzt = real(diag(diag(Czt(:,:,1)))); 546 | 547 | % Bussgang matrix 548 | if strcmpi(par.approximation, 'rounding') || strcmpi(par.approximation, 'diagonal') 549 | G = zeros(par.B,par.B); 550 | for i = 1:par.L-1 551 | G = G + par.alpha*par.lsb/sqrt(pi)*Dzt^-.5 * diag(exp(-par.lsb^2*(i-par.L/2)^2*diag(Dzt^-1))); 552 | end 553 | end 554 | 555 | switch par.approximation 556 | 557 | case 'diagonal' 558 | 559 | % output covariance matrix 560 | Cxt = zeros(par.B,par.B,par.N); 561 | for i = 1:par.L 562 | Cxt(:,:,1) = Cxt(:,:,1) + diag(2 * par.labels(i)^2 * (qfunc(sqrt(2)*par.thresholds(i)./sqrt(diag(Dzt))) - qfunc(sqrt(2)*par.thresholds(i+1)./sqrt(diag(Dzt))))); 563 | end 564 | Cxt(:,:,1) = Cxt(:,:,1) + G*(Czt(:,:,1) - Dzt)*G'; 565 | for n = 2:par.N 566 | Cxt(:,:,n) = G*Czt(:,:,n)*G'; 567 | end 568 | 569 | case 'rounding' 570 | 571 | % output covariance matrix 572 | Cxt = zeros(size(Czt)); 573 | if par.L == 2 574 | 575 | % arcsine law 576 | for n = 1:par.N 577 | Cxt(:,:,n) = 2/pi * par.S/(par.N*par.B) * (asin(Dzt^-.5*real(Czt(:,:,n))*Dzt^-.5) ... 578 | + 1i*asin(Dzt^-.5*imag(Czt(:,:,n))*Dzt^-.5)); 579 | end 580 | 581 | elseif par.L > 2 582 | 583 | Cet = zeros(size(Czt)); 584 | 585 | for n = 1:par.N 586 | 587 | % limit the infinite sum 588 | lmax = 30; lvec = combvec(1:lmax,1:lmax); 589 | 590 | % quantization error covariance matrix 591 | for l = 1:lmax^2 592 | cv = 2*pi^2/par.lsb^2*(lvec(1,l).^2*real(Dzt*ones(par.B,par.B))/2 + lvec(2,l).^2*real(ones(par.B,par.B)*Dzt)/2); 593 | cr = 4*pi^2/par.lsb^2*lvec(1,l).*lvec(2,l)*real(Czt(:,:,n))/2; 594 | ci = 4*pi^2/par.lsb^2*lvec(1,l).*lvec(2,l)*imag(Czt(:,:,n))/2; 595 | Cet(:,:,n) = Cet(:,:,n) + 2* par.lsb^2/(2*pi^2) .* ... 596 | cos(pi*par.L).^(lvec(1,l)+lvec(2,l)) ./ (lvec(1,l).*lvec(2,l)) .* ... 597 | (exp(-cv+cr) - exp(-cv-cr) + 1i*exp(-cv+ci) - 1i*exp(-cv-ci)); 598 | end 599 | 600 | end 601 | 602 | % output covariance matrix 603 | for n = 1:par.N 604 | Cxt(:,:,n) = par.alpha^2*(Cet(:,:,n) + Czt(:,:,n)*(G/par.alpha-eye(par.B)) + (G/par.alpha-eye(par.B))*Czt(:,:,n) + Czt(:,:,n)); 605 | end 606 | 607 | end 608 | 609 | end 610 | 611 | % distortion covariance matrix 612 | Cdt = zeros(size(Czt)); 613 | for n = 1:par.N 614 | Cdt(:,:,n) = Cxt(:,:,n) - G*Czt(:,:,n)*G'; 615 | end 616 | 617 | end 618 | 619 | % -- auxilliary functions 620 | 621 | % soft-output data detection using the max-log approximation. 622 | function llr = detector(par,shat,N0) 623 | 624 | % initialization 625 | llr = nan(par.U, par.code.n); 626 | bin_array = sign(par.bits-.5); 627 | 628 | % perform detection for each terminal 629 | for u = 1:par.U 630 | for k = 1:par.S*par.nrof_ofdm_symbols 631 | 632 | % compute distance metric 633 | metrics = abs(shat(u,k)-par.symbols).^2/N0; 634 | 635 | % compute max-log LLRs 636 | for b = 1:par.bps 637 | 638 | pmin = inf; 639 | nmin = inf; 640 | 641 | for z = 1:2^par.bps 642 | if bin_array(z,b)==1 643 | pmin = min(pmin,metrics(z)); 644 | else 645 | nmin = min(nmin,metrics(z)); 646 | end 647 | end 648 | 649 | % log-likelihood ratio 650 | llr(u,(k-1)*par.bps+b) = nmin-pmin; 651 | 652 | end 653 | 654 | end 655 | 656 | end 657 | 658 | end 659 | 660 | % deinterleaving, depuncturing, and decoding using the BCJR algorithm 661 | function [llr_out,bhat] = decoder(par,llr_in) 662 | 663 | % initialization 664 | llr_temp = nan(1,size(llr_in,2)); 665 | llr_out = nan(par.U, 2*par.code.m); 666 | bhat = nan(par.U, par.code.m); 667 | 668 | for u = 1:par.U 669 | 670 | % deinterleaving 671 | llr_temp(1,par.code.interleaverperm(u,:)) = llr_in(u,:); 672 | 673 | % depuncturing 674 | llr_punctured = zeros(1,2*length(llr_temp(1,:))*par.code.rate); 675 | llr_punctured(par.code.puncturing.index) = llr_temp; 676 | 677 | % decoding 678 | [llr_out(u,:),bhat(u,:)] = BCJR(par.code.trellis,llr_punctured); 679 | 680 | end 681 | 682 | end 683 | 684 | 685 | 686 | 687 | 688 | 689 | --------------------------------------------------------------------------------