freedv-gui/codec2-1.2.0/octave/ldpc_fsk_lib.m

270 lines
8.1 KiB
Matlab

% 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