aboutsummaryrefslogtreecommitdiff
path: root/octave/ldpc_fsk_lib.m
blob: fea8a978418f9d6406836c9521e66c8d8d6120d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
% lpdc_fsk_lib.m
% April 2015
%
% Library version of ldpc4.m written by vk5dsp.  Application is high bit rate
% balloon telemtry
%
% LDPC demo
% Call the CML routines and simulate one set of SNRs.  
% This function is an updated version of ldpc3() which uses less 
% of the CML functions 
%
% sim_in the input parameter structure
% sim_out contains BERs and other stats for each value of SNR
% resfile is the result file
%

1;

function sim_out = ldpc5(sim_in, resfile, testmode, genie_Es, logging=0);

    if nargin<4, testmode = 0; end
    estEsN0 = 0; 

    HRA       = sim_in.HRA;
    framesize = sim_in.framesize;
    rate      = sim_in.rate;
    mod_order = sim_in.mod_order;

    Lim_Ferrs = sim_in.Lim_Ferrs;
    Ntrials   = sim_in.Ntrials;
    Esvec     = sim_in.Esvec;

    demod_type = 0;
    decoder_type = 0;
    max_iterations = 100;
    code_param = ldpc_init(HRA, mod_order);
    bps = code_param.bits_per_symbol;


    if (logging) 
       fod = fopen('decode.log', 'w'); 
       fwrite(fod, 'Es estEs Its  secs \n'); 
    end 


    for ne = 1:length(Esvec)
        Es = Esvec(ne);
        EsNo = 10^(Es/10);


        Terrs = 0;  Tbits =0;  Ferrs =0;
        for nn = 1: Ntrials

          data = round( rand( 1, code_param.data_bits_per_frame ) );
          codeword = ldpc_encode(code_param, data);

          code_param.code_bits_per_frame = length( codeword );
          Nsymb = code_param.code_bits_per_frame/bps;

            if testmode==1
               f1 = fopen("dat_in2064.txt", "w");
               for k=1:length(data);  fprintf(f1, "%u\n", data(k)); end
               fclose(f1); 
               system("./ra_enc"); 

               load("dat_op2064.txt"); 
               pbits = codeword(length(data)+1:end);   %   print these to compare with C code 
               dat_op2064(1:16)', pbits(1:16)  
               differences_in_parity =  sum(abs(pbits - dat_op2064'))
               pause;  
            end


            % modulate
            % s = Modulate( codeword, code_param.S_matrix );
            s= 1 - 2 * codeword;   
            code_param.symbols_per_frame = length( s );

            variance = 1/(2*EsNo);
            noise = sqrt(variance)* randn(1,code_param.symbols_per_frame); 
            %  +  j*randn(1,code_param.symbols_per_frame) );
            r = s + noise;
            Nr = length(r);  

            [detected_data Niters] = ldpc_decode(r, code_param, max_iterations, decoder_type);

            error_positions = xor( detected_data(1:code_param.data_bits_per_frame), data );
            Nerrs = sum( error_positions);

            t = clock;   t =  fix(t(5)*60+t(6)); 
            if (logging)  
              fprintf(fod, ' %3d  %4d\n', Niters, t); 
              end 

            if Nerrs>0, fprintf(1,'x'),  else fprintf(1,'.'),  end
            if (rem(nn, 50)==0),  fprintf(1,'\n'),  end

            if Nerrs>0,  Ferrs = Ferrs +1;  end
            Terrs = Terrs + Nerrs;
            Tbits = Tbits + code_param.data_bits_per_frame;

            if Ferrs > Lim_Ferrs, disp(['exit loop with #cw errors = ' ...
                    num2str(Ferrs)]);  break,  end
        end

        TERvec(ne) = Terrs;
        FERvec(ne) = Ferrs;
        BERvec(ne) = Terrs/ Tbits;
        Ebvec = Esvec - 10*log10(code_param.bits_per_symbol * rate);

        cparams= [code_param.data_bits_per_frame  code_param.symbols_per_frame ...
            code_param.code_bits_per_frame];

        sim_out.BERvec = BERvec;
        sim_out.Ebvec = Ebvec;
        sim_out.FERvec = FERvec;
        sim_out.TERvec  = TERvec;
        sim_out.cpumins = cputime/60;

        if nargin > 2
            save(resfile,  'sim_in',  'sim_out',  'cparams');
            disp(['Saved results to ' resfile '  at Es =' num2str(Es) 'dB']);
        end
      end
end


function code_param = ldpc_init(HRA, mod_order)
  code_param.bits_per_symbol = log2(mod_order);
  [H_rows, H_cols] = Mat2Hrows(HRA); 
  code_param.H_rows = H_rows; 
  code_param.H_cols = H_cols;
  code_param.P_matrix = [];
  code_param.data_bits_per_frame = length(code_param.H_cols) - length( code_param.P_matrix ); 
  code_param.symbols_per_frame = length(HRA);
end


function codeword = ldpc_encode(code_param, data)
      codeword = LdpcEncode( data, code_param.H_rows, code_param.P_matrix );
endfunction


% Takes soft decision symbols (e.g. output of 2fsk demod) and converts
% them to LLRs.  Note we calculate mean and var manually instead of
% using internal functions.  This was required to get a bit exact
% results against the C code.

function llr = sd_to_llr(sd)
    sd = sd / mean(abs(sd));
    x = sd - sign(sd);
    sumsq = sum(x.^2);
    summ = sum(x);
    mn = summ/length(sd);
    estvar = sumsq/length(sd) - mn*mn; 
    estEsN0 = 1/(2* estvar + 1E-3); 
    llr = 4 * estEsN0 * sd;
endfunction


% LDPC decoder - note it estimates EsNo from received symbols

function [detected_data Niters] = ldpc_decode(r, code_param, max_iterations, decoder_type)
  % in the binary case the LLRs are just a scaled version of the rx samples ..

 #{
  r = r / mean(abs(r));       % scale for signal unity signal  
  estvar = var(r-sign(r)); 
  estEsN0 = 1/(2* estvar + 1E-3); 
  input_decoder_c = 4 * estEsN0 * r;
 #}
  llr = sd_to_llr(r);

  [x_hat, PCcnt] = MpDecode(llr, code_param.H_rows, code_param.H_cols, ...
                            max_iterations, decoder_type, 1, 1);         
  Niters = sum(PCcnt!=0);
  detected_data = x_hat(Niters,:);
  
  if isfield(code_param, "c_include_file")
    ldpc_gen_h_file(code_param, max_iterations, decoder_type, llr, x_hat, detected_data);
  end
end


% One application of FSK LDPC work is SSTV.  This function generates a
% simulated frame for testing

function frame_rs232 = gen_sstv_frame
  load('H2064_516_sparse.mat');
  HRA = full(HRA);  
  mod_order = 2;
  code_param = ldpc_init(HRA, mod_order);

  % generate payload data bytes and checksum

  data = floor(rand(1,256)*256);
  %data = zeros(1,256);
  checksum = crc16(data);
  data = [data hex2dec(checksum(3:4)) hex2dec(checksum(1:2))];

  % unpack bytes to bits and LPDC encode

  mask = 2.^(7:-1:0);       % MSB to LSB unpacking to match python tx code.
  unpacked_data = [];
  for b=1:length(data)
    unpacked_data = [unpacked_data bitand(data(b), mask) > 0];
  end
  codeword = [ldpc_encode(code_param, unpacked_data) 0 0 0 0];  % pad with 0s to get integer number of bytes

  % pack back into bytes to match python code 

  lpacked_codeword = length(codeword)/8;
  packed_codeword = zeros(1,lpacked_codeword);
  for b=1:lpacked_codeword
    st = (b-1)*8 + 1;
    packed_codeword(b) = sum(codeword(st:st+7) .* mask);
  end

  % generate header bits

  header = [hex2dec('55')*ones(1,16) hex2dec('ab') hex2dec('cd') hex2dec('ef') hex2dec('01')];

  % now construct entire unpacked frame

  packed_frame = [header packed_codeword];
  mask = 2.^(0:7);        % LSB to MSB packing for header
  lpacked_frame = length(packed_frame);
  frame = [];
  for b=1:lpacked_frame
    frame = [frame bitand(packed_frame(b), mask) > 0];
  end

  % insert rs232 framing bits

  frame_rs232 = [];
  for b=1:8:length(frame)
    frame_rs232 = [frame_rs232 0 frame(b:b+7) 1];
  end

  %printf("codeword: %d unpacked_header: %d frame: %d frame_rs232: %d \n", length(codeword), length(unpacked_header), length(frame), length(frame_rs232));
endfunction


% calculates and compares the checksum of a SSTV frame, that has RS232
% start and stop bits

function checksum_ok = sstv_checksum(frame_rs232)
  l = length(frame_rs232);
  expected_l = (256+2)*10;
  assert(l == expected_l);

  % extract rx bytes

  rx_data = zeros(1,256);
  mask = 2.^(0:7);          % LSB to MSB
  k = 1;
  for i=1:10:expected_l
    rx_bits = frame_rs232(i+1:i+8);
    rx_data(k) = sum(rx_bits .* mask); 
    k++;
  end

  % calc rx checksum and extract tx checksum

  rx_checksum = crc16(rx_data(1:256));
  tx_checksum = sprintf("%02X%02X", rx_data(258), rx_data(257));
  %printf("tx_checksum: %s rx_checksum: %s\n", tx_checksum, rx_checksum);
  checksum_ok = strcmp(tx_checksum, rx_checksum);
endfunction