mirror of https://github.com/drowe67/codec2.git
yet more Octave files rm-ed
parent
a291cfaf4c
commit
fbbd89ddf8
|
@ -1,51 +0,0 @@
|
|||
% doppler_spread_ut.m
|
||||
% David Rowe Jan 2016
|
||||
%
|
||||
% Unit test script for doppler_spread
|
||||
|
||||
f = 1;
|
||||
Fs = 8000;
|
||||
N = Fs*10;
|
||||
|
||||
[spread states] = doppler_spread(f, Fs, N);
|
||||
|
||||
% use spreading samples to modulate 1000Hz sine wave
|
||||
% You can listen to this with: sine1k_1Hz.raw
|
||||
|
||||
% $ play -t raw -r 8000 -s -2
|
||||
s = cos(2*pi*(1:N)*1000/Fs);
|
||||
s = s .* spread;
|
||||
s = real(s)*5000;
|
||||
fs = fopen("sine1k_1Hz.raw","wb"); fwrite(fs,s,"short"); fclose(fs);
|
||||
|
||||
% Some plots
|
||||
|
||||
x = states.x; y = states.y; b = states.b;
|
||||
|
||||
H = freqz(b,1,x);
|
||||
|
||||
figure(1)
|
||||
clf
|
||||
subplot(211)
|
||||
plot(x,y,';target;')
|
||||
title('Gaussian Filter Freq Resp Lin');
|
||||
legend('boxoff');
|
||||
subplot(212)
|
||||
plot(x,20*log10(y),';target;')
|
||||
hold on;
|
||||
plot(x,20*log10(y),'g+;actual;')
|
||||
hold off;
|
||||
axis([0 f*10/2 -60 0])
|
||||
title('Gaussian Filter Freq Resp dB');
|
||||
xlabel('Freq (Hz)');
|
||||
legend('boxoff');
|
||||
|
||||
figure(2);
|
||||
subplot(211)
|
||||
plot(abs(spread))
|
||||
title('Spreading Function Magnitude');
|
||||
subplot(212)
|
||||
plot(s)
|
||||
title('1000Hz Sine Wave');
|
||||
xlabel('Time (samples)')
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
% estsnr.m
|
||||
% David Rowe May 2017
|
||||
%
|
||||
% estimate SNR of a sinewave in noise
|
||||
|
||||
function snr_dB = estsnr(x, Fs=8000, Nbw = 3000)
|
||||
|
||||
[nr nc] = size(x);
|
||||
if nr == 1
|
||||
x = x';
|
||||
end
|
||||
|
||||
% find peak in +ve side of spectrum, ignoring DC
|
||||
|
||||
L = length(x);
|
||||
X = abs(fft(x));
|
||||
st = floor(0.05*L); en = floor(0.45*L);
|
||||
[A mx_ind]= max(X(st:en));
|
||||
mx_ind += st;
|
||||
|
||||
% signal energy might be spread by doppler, so sum energy
|
||||
% in frequencies +/- 1%
|
||||
|
||||
s_st = floor(mx_ind*0.99); s_en = floor(mx_ind*1.01);
|
||||
S = sum(X(s_st:s_en).^2);
|
||||
|
||||
% real signal, so -ve power is the same
|
||||
|
||||
S = 2*S;
|
||||
SdB = 10*log10(S);
|
||||
|
||||
printf("Signal Power S: %3.2f dB\n", SdB);
|
||||
|
||||
% locate a band of noise next to it and find power in band
|
||||
|
||||
st = floor(mx_ind+0.05*(L/2));
|
||||
en = st + floor(0.1*(L/2));
|
||||
|
||||
N = sum(X(st:en).^2);
|
||||
|
||||
% scale this to obtain total noise power across total bandwidth
|
||||
|
||||
N *= L/(en-st);
|
||||
NdB = 10*log10(N);
|
||||
printf("Noise Power N: %3.2f dB\n", NdB);
|
||||
|
||||
% scale noise to designed noise bandwidth /2 fudge factor as its a
|
||||
% real signal, wish I had a better way to explain that!
|
||||
|
||||
NodB = NdB - 10*log10(Fs/2);
|
||||
NscaleddB = NodB + 10*log10(Nbw);
|
||||
snr_dB = SdB - NscaleddB;
|
||||
|
||||
figure(1); clf;
|
||||
plot(20*log10(X(1:L/2)),'b');
|
||||
hold on;
|
||||
plot([s_st s_en], [NdB NdB]- 10*log10(L), 'r');
|
||||
plot([st en], [NdB NdB]- 10*log10(L), 'r');
|
||||
hold off;
|
||||
top = 10*ceil(SdB/10);
|
||||
bot = NodB - 20;
|
||||
axis([1 L/2 bot top]);
|
||||
grid
|
||||
grid("minor")
|
||||
endfunction
|
|
@ -1,272 +0,0 @@
|
|||
% hf_modem_curves
|
||||
% David Rowe Feb 2017
|
||||
%
|
||||
% Ideal implementations of a bunch of different HF modems, used to
|
||||
% generate plots for a blog post.
|
||||
|
||||
#{
|
||||
[X] ideal AWGN/HF curves
|
||||
[X] exp AWGN QPSK curves
|
||||
[X] exp AWGN DQPSK curves
|
||||
[X] exp HF channel model
|
||||
[ ] diversity
|
||||
[ ] COHPSK frames
|
||||
+ would require multiple carriers
|
||||
+ filtering or OFDM
|
||||
#}
|
||||
|
||||
1;
|
||||
|
||||
% Gray coded QPSK modulation function
|
||||
|
||||
function symbol = qpsk_mod(two_bits)
|
||||
two_bits_decimal = sum(two_bits .* [2 1]);
|
||||
switch(two_bits_decimal)
|
||||
case (0) symbol = 1;
|
||||
case (1) symbol = j;
|
||||
case (2) symbol = -j;
|
||||
case (3) symbol = -1;
|
||||
endswitch
|
||||
endfunction
|
||||
|
||||
|
||||
% Gray coded QPSK demodulation function
|
||||
|
||||
function two_bits = qpsk_demod(symbol)
|
||||
bit0 = real(symbol*exp(j*pi/4)) < 0;
|
||||
bit1 = imag(symbol*exp(j*pi/4)) < 0;
|
||||
two_bits = [bit1 bit0];
|
||||
endfunction
|
||||
|
||||
|
||||
% Rate Rs modem simulation model -------------------------------------------------------
|
||||
|
||||
function sim_out = ber_test(sim_in)
|
||||
bps = 2; % two bits/symbol for QPSK
|
||||
Rs = 50; % symbol rate (needed for HF model)
|
||||
|
||||
verbose = sim_in.verbose;
|
||||
EbNovec = sim_in.EbNovec;
|
||||
hf_en = sim_in.hf_en;
|
||||
|
||||
% user can supply number of bits per point to get good results
|
||||
% at high Eb/No
|
||||
|
||||
if length(sim_in.nbits) > 1
|
||||
nbitsvec = sim_in.nbits;
|
||||
nbitsvec += 100 - mod(nbitsvec,100); % round up to nearest 100
|
||||
else
|
||||
nbitsvec(1:length(EbNovec)) = sim_in.nbits;
|
||||
end
|
||||
|
||||
% init HF model
|
||||
|
||||
if hf_en
|
||||
|
||||
% some typical values
|
||||
|
||||
dopplerSpreadHz = 1.0; path_delay = 1E-3*Rs;
|
||||
|
||||
nsymb = max(nbitsvec)/2;
|
||||
spread1 = doppler_spread(dopplerSpreadHz, Rs, nsymb);
|
||||
spread2 = doppler_spread(dopplerSpreadHz, Rs, nsymb);
|
||||
hf_gain = 1.0/sqrt(var(spread1)+var(spread2));
|
||||
% printf("nsymb: %d lspread1: %d\n", nsymb, length(spread1));
|
||||
end
|
||||
|
||||
for ne = 1:length(EbNovec)
|
||||
|
||||
% work out noise power -------------
|
||||
|
||||
EbNodB = EbNovec(ne);
|
||||
EsNodB = EbNodB + 10*log10(bps);
|
||||
EsNo = 10^(EsNodB/10);
|
||||
variance = 1/EsNo;
|
||||
nbits = nbitsvec(ne);
|
||||
nsymb = nbits/bps;
|
||||
|
||||
% modulator ------------------------
|
||||
|
||||
tx_bits = rand(1,nbits) > 0.5;
|
||||
tx_symb = [];
|
||||
prev_tx_symb = 1;
|
||||
for s=1:nsymb
|
||||
atx_symb = qpsk_mod(tx_bits(2*s-1:2*s));
|
||||
if sim_in.dqpsk
|
||||
atx_symb *= prev_tx_symb;
|
||||
prev_tx_symb = atx_symb;
|
||||
end
|
||||
tx_symb = [tx_symb atx_symb];
|
||||
end
|
||||
|
||||
% channel ---------------------------
|
||||
|
||||
rx_symb = tx_symb;
|
||||
|
||||
if hf_en
|
||||
|
||||
% simplified rate Rs simulation model that doesn't include
|
||||
% ISI, just freq filtering. We assume perfect phase estimation
|
||||
% so it's just amplitude distortion.
|
||||
|
||||
hf_model1 = hf_model2 = zeros(1, nsymb);
|
||||
for s=1:nsymb
|
||||
hf_model1(s) = hf_gain*(spread1(s) + exp(-j*path_delay)*spread2(s));
|
||||
hf_model = abs(hf_model1(s));
|
||||
|
||||
if sim_in.diversity
|
||||
% include amplitude information from another frequency in channel model
|
||||
w1 = 7*2*pi;
|
||||
hf_model2(s) = hf_gain*(spread1(s) + exp(-j*w1*path_delay)*spread2(s));
|
||||
hf_model = 0.5*abs(hf_model1(s)) + 0.5*abs(hf_model2(s));
|
||||
end
|
||||
|
||||
rx_symb(s) = rx_symb(s).*hf_model;
|
||||
end
|
||||
end
|
||||
|
||||
% variance is noise power, which is divided equally between real and
|
||||
% imag components of noise
|
||||
|
||||
noise = sqrt(variance*0.5)*(randn(1,nsymb) + j*randn(1,nsymb));
|
||||
rx_symb += noise;
|
||||
|
||||
% demodulator ------------------------------------------
|
||||
|
||||
% demodulate rx symbols to bits
|
||||
|
||||
rx_bits = [];
|
||||
prev_rx_symb = 1;
|
||||
for s=1:nsymb
|
||||
arx_symb = rx_symb(s);
|
||||
if sim_in.dqpsk
|
||||
tmp = arx_symb;
|
||||
arx_symb *= prev_rx_symb';
|
||||
prev_rx_symb = tmp;
|
||||
end
|
||||
two_bits = qpsk_demod(arx_symb);
|
||||
rx_bits = [rx_bits two_bits];
|
||||
end
|
||||
|
||||
% count errors -----------------------------------------
|
||||
|
||||
error_pattern = xor(tx_bits, rx_bits);
|
||||
nerrors = sum(error_pattern);
|
||||
bervec(ne) = nerrors/nbits;
|
||||
if verbose
|
||||
printf("EbNodB: % 3.1f nbits: %5d nerrors: %5d ber: %4.3f\n", EbNodB, nbits, nerrors, bervec(ne));
|
||||
if verbose == 2
|
||||
figure(2); clf;
|
||||
plot(rx_symb*exp(j*pi/4),'+','markersize', 10);
|
||||
mx = max(abs(rx_symb));
|
||||
axis([-mx mx -mx mx]);
|
||||
if sim_in.diversity && sim_in.hf_en
|
||||
figure(3);
|
||||
plot(1:nsymb, abs(hf_model1), 1:nsymb, abs(hf_model2), 'linewidth', 2);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sim_out.bervec = bervec;
|
||||
endfunction
|
||||
|
||||
|
||||
% -------------------------------------------------------------
|
||||
|
||||
|
||||
function run_single
|
||||
sim_in.verbose = 2;
|
||||
sim_in.nbits = 1000;
|
||||
sim_in.EbNovec = 4;
|
||||
sim_in.dqpsk = 0;
|
||||
sim_in.hf_en = 0;
|
||||
sim_in.diversity = 0;
|
||||
|
||||
sim_qpsk = ber_test(sim_in);
|
||||
endfunction
|
||||
|
||||
|
||||
function run_curves
|
||||
max_nbits = 1E5;
|
||||
sim_in.verbose = 1;
|
||||
sim_in.EbNovec = 0:10;
|
||||
sim_in.dqpsk = 0;
|
||||
sim_in.hf_en = 0;
|
||||
sim_in.diversity = 0;
|
||||
|
||||
% AWGN -----------------------------
|
||||
|
||||
ber_awgn_theory = 0.5*erfc(sqrt(10.^(sim_in.EbNovec/10)));
|
||||
sim_in.nbits = min(max_nbits, floor(500 ./ ber_awgn_theory));
|
||||
|
||||
sim_qpsk = ber_test(sim_in);
|
||||
sim_in.dqpsk = 1;
|
||||
sim_dqpsk = ber_test(sim_in);
|
||||
|
||||
% HF -----------------------------
|
||||
|
||||
hf_sim_in = sim_in; hf_sim_in.dqpsk = 0; hf_sim_in.hf_en = 1;
|
||||
hf_sim_in.EbNovec = 0:16;
|
||||
|
||||
EbNoLin = 10.^(hf_sim_in.EbNovec/10);
|
||||
ber_hf_theory = 0.5.*(1-sqrt(EbNoLin./(EbNoLin+1)));
|
||||
|
||||
hf_sim_in.nbits = min(max_nbits, floor(500 ./ ber_hf_theory));
|
||||
sim_qpsk_hf = ber_test(hf_sim_in);
|
||||
|
||||
hf_sim_in.dqpsk = 1;
|
||||
sim_dqpsk_hf = ber_test(hf_sim_in);
|
||||
|
||||
hf_sim_in.dqpsk = 0;
|
||||
hf_sim_in.diversity = 1;
|
||||
sim_qpsk_hf_div = ber_test(hf_sim_in);
|
||||
|
||||
% Plot results --------------------
|
||||
|
||||
close all;
|
||||
figure (1, 'position', [100, 10, 600, 400]); clf;
|
||||
|
||||
semilogy(sim_in.EbNovec, ber_awgn_theory,'r+-;QPSK AWGN theory;', 'linewidth', 2)
|
||||
xlabel('Eb/No (dB)')
|
||||
ylabel('BER')
|
||||
grid("minor")
|
||||
axis([min(sim_in.EbNovec) max(sim_in.EbNovec) 1E-3 1])
|
||||
hold on;
|
||||
|
||||
semilogy([0 4 4], [ber_awgn_theory(5) ber_awgn_theory(5) 1E-3],'k--', 'linewidth', 2);
|
||||
hold off;
|
||||
|
||||
figure (2, 'position', [300, 10, 600, 400]); clf;
|
||||
semilogy(sim_in.EbNovec, ber_awgn_theory,'r+-;QPSK AWGN theory;','markersize', 10, 'linewidth', 2)
|
||||
hold on;
|
||||
semilogy(sim_in.EbNovec, sim_qpsk.bervec,'g+-;QPSK AWGN simulated;','markersize', 10, 'linewidth', 2)
|
||||
semilogy(sim_in.EbNovec, sim_dqpsk.bervec,'b+-;DQPSK AWGN simulated;','markersize', 10, 'linewidth', 2)
|
||||
xlabel('Eb/No (dB)')
|
||||
ylabel('BER')
|
||||
grid("minor")
|
||||
axis([min(sim_in.EbNovec) max(sim_in.EbNovec) 1E-3 1])
|
||||
|
||||
figure (3, 'position', [400, 10, 600, 400]); clf;
|
||||
semilogy(sim_in.EbNovec, ber_awgn_theory,'r+-;QPSK AWGN theory;','markersize', 10, 'linewidth', 2)
|
||||
hold on;
|
||||
semilogy(sim_in.EbNovec, sim_qpsk.bervec,'g+-;QPSK AWGN simulated;','markersize', 10, 'linewidth', 2)
|
||||
semilogy(sim_in.EbNovec, sim_dqpsk.bervec,'b+-;DQPSK AWGN simulated;','markersize', 10, 'linewidth', 2)
|
||||
semilogy(hf_sim_in.EbNovec, ber_hf_theory,'r+-;QPSK HF theory;','markersize', 10, 'linewidth', 2)
|
||||
semilogy(hf_sim_in.EbNovec, sim_dqpsk_hf.bervec,'b+-;DQPSK HF simulated;','markersize', 10, 'linewidth', 2)
|
||||
semilogy(hf_sim_in.EbNovec, sim_qpsk_hf.bervec,'g+-;QPSK HF simulated;','markersize', 10, 'linewidth', 2)
|
||||
semilogy(hf_sim_in.EbNovec, sim_qpsk_hf_div.bervec,'c+-;QPSK Diversity HF simulated;','markersize', 10, 'linewidth', 2)
|
||||
hold off;
|
||||
xlabel('Eb/No (dB)')
|
||||
ylabel('BER')
|
||||
grid("minor")
|
||||
axis([min(hf_sim_in.EbNovec) max(hf_sim_in.EbNovec) 1E-3 1])
|
||||
|
||||
endfunction
|
||||
|
||||
% -------------------------------------------------------------
|
||||
|
||||
more off;
|
||||
rand('seed',1); randn('seed', 1);
|
||||
run_curves
|
||||
#run_single
|
|
@ -1,142 +0,0 @@
|
|||
% ldpc_gen_c_h_file.m
|
||||
% David Rowe Sep 2015, B. Van Slyke 2019
|
||||
%
|
||||
% Create .c and h files for use in LDPC decoders
|
||||
%
|
||||
% NOTE: You'll need to install the CML library as a number of functions involved
|
||||
% in LDPC use it. See ldpc.m for instructions in installing the CML
|
||||
% library.
|
||||
%
|
||||
% usage examples:
|
||||
%
|
||||
% 1/ Using codes defined in external files:
|
||||
%
|
||||
% octave:1> ldpc_gen_c_h_file("HRA_112_112.txt")
|
||||
% octave:1> ldpc_gen_c_h_file(""H_4096_8192_3d.mat")
|
||||
%
|
||||
% 2/ Using built in CML codes:
|
||||
%
|
||||
% octave:1> ldpc_gen_c_h_file("dvbs2", 0.6, 16200)
|
||||
%
|
||||
% Output: Two files with the same filename as the LDPC input, but with .c and .h
|
||||
% extensions.
|
||||
|
||||
function ldpc_gen_c_h_file(varargin)
|
||||
|
||||
ldpc % load ldpc functions
|
||||
ldpc_fsk_lib % for ldpc_encode
|
||||
|
||||
% Assuming cml has been installed in the users' home folder, which is the
|
||||
% default install location
|
||||
init_cml();
|
||||
|
||||
if nargin == 0
|
||||
printf("Error - you must specify a file containing the LDPC codes (e.g. HRA_112_112.txt).\n");
|
||||
return;
|
||||
end
|
||||
loadStr = varargin{1};
|
||||
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
% the tests are performed using BPSK modulation, but in practice codes can be used
|
||||
% with other modulation, e.g. QPSK
|
||||
mod_order = 2; modulation = 'BPSK'; mapping = 'gray';
|
||||
|
||||
if strcmp(loadStr, "dvbs2")
|
||||
rate = varargin{2};
|
||||
framesize = varargin{3};
|
||||
code_param = ldpc_init_builtin(loadStr, rate, framesize, modulation, mod_order, mapping);
|
||||
n = code_param.ldpc_coded_bits_per_frame;
|
||||
k = code_param.ldpc_data_bits_per_frame;
|
||||
ldpcArrayName = sprintf("H_%d_%d",n,k);
|
||||
includeFileName = strcat(ldpcArrayName, '.h');
|
||||
sourceFileName = strcat(ldpcArrayName, '.c');
|
||||
else
|
||||
% The ldpc variable name may not be what we want for a file/variable names, but
|
||||
% the load filename will be, so use it.
|
||||
[~,ldpcArrayName,ext] = fileparts(loadStr);
|
||||
includeFileName = strcat(ldpcArrayName, '.h');
|
||||
sourceFileName = strcat(ldpcArrayName, '.c');
|
||||
|
||||
% Get the ext of the file first. If it's a txt, then do what we
|
||||
% are doing. If .mat, then just load, knowing the variable is HRA
|
||||
if strcmp(ext, '.mat') == 1
|
||||
load(loadStr);
|
||||
if exist("H") & !exist("HRA")
|
||||
printf("renaming H to HRA...\n");
|
||||
HRA=H;
|
||||
end
|
||||
else
|
||||
% When calling 'load' this way, it returns a struct. The code assumes the
|
||||
% struct has one element, and the one/first element is the array
|
||||
% to process
|
||||
tempStruct = load(loadStr);
|
||||
b = fieldnames(tempStruct);
|
||||
ldpcArrayName = b{1,1};
|
||||
% extract the array from the struct
|
||||
HRA = tempStruct.(ldpcArrayName);
|
||||
endif
|
||||
|
||||
code_param = ldpc_init_user(HRA, modulation, mod_order, mapping);
|
||||
end
|
||||
|
||||
code_length = code_param.coded_syms_per_frame;
|
||||
|
||||
% First, create the H file
|
||||
f = fopen(includeFileName, "wt");
|
||||
printHeader(f, includeFileName, ldpcArrayName, mfilename());
|
||||
|
||||
fprintf(f,"#define %s_NUMBERPARITYBITS %d\n", ldpcArrayName, rows(code_param.H_rows));
|
||||
fprintf(f,"#define %s_MAX_ROW_WEIGHT %d\n", ldpcArrayName, columns(code_param.H_rows));
|
||||
fprintf(f,"#define %s_CODELENGTH %d\n", ldpcArrayName, code_param.coded_syms_per_frame);
|
||||
fprintf(f,"#define %s_NUMBERROWSHCOLS %d\n", ldpcArrayName, rows(code_param.H_cols));
|
||||
fprintf(f,"#define %s_MAX_COL_WEIGHT %d\n", ldpcArrayName, columns(code_param.H_cols));
|
||||
fprintf(f,"#define %s_DEC_TYPE %d\n", ldpcArrayName, decoder_type);
|
||||
fprintf(f,"#define %s_MAX_ITER %d\n", ldpcArrayName, max_iterations);
|
||||
fprintf(f,"\n");
|
||||
fprintf(f,"extern const uint16_t %s_H_rows[];\n", ldpcArrayName);
|
||||
fprintf(f,"extern const uint16_t %s_H_cols[];\n", ldpcArrayName);
|
||||
|
||||
fclose(f);
|
||||
|
||||
|
||||
% Then, the C file
|
||||
f = fopen(sourceFileName, "wt");
|
||||
printHeader(f, sourceFileName, ldpcArrayName, mfilename());
|
||||
fprintf(f, "#include <stdint.h>\n");
|
||||
fprintf(f, "#include \"%s\"\n", includeFileName);
|
||||
|
||||
% clock out 2D array to linear C array in row order ....
|
||||
fprintf(f,"\nconst uint16_t %s_H_rows[] = {\n", ldpcArrayName);
|
||||
[r c] = size(code_param.H_rows);
|
||||
for j=1:c
|
||||
for i=1:r
|
||||
fprintf(f, "%d", code_param.H_rows(i,j));
|
||||
if (i == r) && (j ==c) % weird, this does nothing
|
||||
fprintf(f,"\n};\n");
|
||||
else
|
||||
fprintf(f,", ");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fprintf(f,"\nconst uint16_t %s_H_cols[] = {\n", ldpcArrayName);
|
||||
[r c] = size(code_param.H_cols);
|
||||
for j=1:c
|
||||
for i=1:r
|
||||
fprintf(f, "%d", code_param.H_cols(i,j));
|
||||
if (i == r) && (j == c)
|
||||
fprintf(f,"\n};\n");
|
||||
else
|
||||
fprintf(f,", ");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fclose(f);
|
||||
endfunction
|
||||
|
||||
function printHeader(f, includeFileName, ldpcArrayName, mFilename)
|
||||
fprintf(f, "/*\n FILE....: %s\n\n", includeFileName);
|
||||
fprintf(f, " Static arrays for LDPC codec %s, generated by %s.m.\n*/\n\n", ldpcArrayName, mFilename);
|
||||
endfunction
|
|
@ -1,72 +0,0 @@
|
|||
% ldpc_gen_h_file.m
|
||||
% David Rowe Sep 2015
|
||||
%
|
||||
% Create a C include file for use in mpdecode.c C cmd line LDPC decoder
|
||||
|
||||
function ldpc_gen_h_file(code_param, max_iterations, decoder_type, input_decoder_c, x_hat, detected_data)
|
||||
|
||||
f = fopen(code_param.c_include_file, "wt");
|
||||
|
||||
fprintf(f, "/*\n FILE....: %s\n\n Static arrays for LDPC codec, generated", code_param.c_include_file);
|
||||
fprintf(f, "\n ldpc_gen_h_file.m.\n\n*/\n\n");
|
||||
|
||||
fprintf(f,"#define NUMBERPARITYBITS %d\n", rows(code_param.H_rows));
|
||||
fprintf(f,"#define MAX_ROW_WEIGHT %d\n", columns(code_param.H_rows));
|
||||
fprintf(f,"#define CODELENGTH %d\n", code_param.symbols_per_frame);
|
||||
fprintf(f,"#define NUMBERROWSHCOLS %d\n", rows(code_param.H_cols));
|
||||
fprintf(f,"#define MAX_COL_WEIGHT %d\n", columns(code_param.H_cols));
|
||||
fprintf(f,"#define DEC_TYPE %d\n", decoder_type);
|
||||
fprintf(f,"#define MAX_ITER %d\n", max_iterations);
|
||||
|
||||
fprintf(f,"\ndouble H_rows[] = {\n");
|
||||
|
||||
% clock out 2D array to linear C array in row order ....
|
||||
|
||||
[r c] = size(code_param.H_rows);
|
||||
for j=1:c
|
||||
for i=1:r
|
||||
fprintf(f, "%d", code_param.H_rows(i,j));
|
||||
if (i == r) && (j ==c)
|
||||
fprintf(f,"\n};\n");
|
||||
else
|
||||
fprintf(f,", ");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fprintf(f,"\ndouble H_cols[] = {\n");
|
||||
[r c] = size(code_param.H_cols);
|
||||
for j=1:c
|
||||
for i=1:r
|
||||
fprintf(f, "%d", code_param.H_cols(i,j));
|
||||
if (i == r) && (j == c)
|
||||
fprintf(f,"\n};\n");
|
||||
else
|
||||
fprintf(f,", ");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fprintf(f,"\ndouble input[] = {\n");
|
||||
for i=1:length(input_decoder_c)
|
||||
fprintf(f, "%.17g", input_decoder_c(i));
|
||||
if i == length(input_decoder_c)
|
||||
fprintf(f,"\n};\n");
|
||||
else
|
||||
fprintf(f,", ");
|
||||
end
|
||||
end
|
||||
|
||||
fprintf(f,"\nchar detected_data[] = {\n");
|
||||
for i=1:length(detected_data)
|
||||
fprintf(f, "%d", detected_data(i));
|
||||
if i == length(detected_data)
|
||||
fprintf(f,"\n};\n");
|
||||
else
|
||||
fprintf(f,", ");
|
||||
end
|
||||
end
|
||||
|
||||
fclose(f);
|
||||
end
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
% load_f32.m
|
||||
% David Rowe Jan 2019
|
||||
%
|
||||
% load up .f32 binary files from dump_data
|
||||
|
||||
function features = load_f32(fn, ncols)
|
||||
f=fopen(fn,"rb");
|
||||
features_lin=fread(f, 'float32');
|
||||
fclose(f);
|
||||
|
||||
nrows = length(features_lin)/ncols;
|
||||
features = reshape(features_lin, ncols, nrows);
|
||||
features = features';
|
||||
endfunction
|
|
@ -1,60 +0,0 @@
|
|||
% make_hilb.m
|
||||
% David Rowe May 2015
|
||||
%
|
||||
% creates Hilber Transformer FIR coeffs
|
||||
|
||||
graphics_toolkit ("gnuplot");
|
||||
|
||||
% from https://www.dsprelated.com/freebooks/sasp/Hilbert_Transform_Design_Example.html
|
||||
|
||||
M = 257; % window length = FIR filter length (Window Method)
|
||||
fs = 8000; % sampling rate assumed (Hz)
|
||||
f1 = 100; % lower pass-band limit = transition bandwidth (Hz)
|
||||
beta = 8; % beta for Kaiser window for decent side-lobe rejection
|
||||
fn = fs/2; % Nyquist limit (Hz)
|
||||
f2 = fn - f1; % upper pass-band limit
|
||||
N = 2^(nextpow2(8*M)); % large FFT for interpolated display
|
||||
k1 = round(N*f1/fs); % lower band edge in bins
|
||||
if k1<2, k1=2; end; % cannot have dc or fn response
|
||||
kn = N/2 + 1; % bin index at Nyquist limit (1-based)
|
||||
k2 = kn-k1+1; % high-frequency band edge
|
||||
f1 = k1*fs/N % quantized band-edge frequencies
|
||||
f2 = k2*fs/N
|
||||
w = kaiser(M,beta)'; % Kaiser window in "linear phase form"
|
||||
H = [ ([0:k1-2]/(k1-1)).^8,ones(1,k2-k1+1),...
|
||||
([k1-2:-1:0]/(k1-1)).^8, zeros(1,N/2-1)];
|
||||
h = ifft(H); % desired impulse response
|
||||
hodd = imag(h(1:2:N)); % This should be zero
|
||||
|
||||
% put window in zero-phase form:
|
||||
wzp = [w((M+1)/2:M), zeros(1,N-M), w(1:(M-1)/2)];
|
||||
hw = wzp .* h; % single-sideband FIR filter, zero-centered
|
||||
Hw = fft(hw);
|
||||
hh = [hw(N-(M-1)/2+1:N),hw(1:(M+1)/2)]; % causal FIR
|
||||
hh *= 2;
|
||||
|
||||
figure(1);
|
||||
HH = fft([hh,zeros(1,N-M)]);
|
||||
plot(20*log10(abs(HH)));
|
||||
figure(2);
|
||||
subplot(211); plot(real(hh)); title('real imp resp');
|
||||
subplot(212); plot(imag(hh)); title('imag imp resp');
|
||||
|
||||
% save coeffs to a C header file
|
||||
|
||||
f=fopen("../src/ht_coeff.h","wt");
|
||||
fprintf(f,"/* Hilbert Transform FIR filter coeffs */\n");
|
||||
fprintf(f,"/* Generated by make_hilb Octave script */\n");
|
||||
|
||||
fprintf(f,"\n#define HT_N %d\n\n", M);
|
||||
|
||||
fprintf(f,"COMP ht_coeff[]={\n");
|
||||
for r=1:M
|
||||
if r < M
|
||||
fprintf(f, " {%f,%f},\n", real(hh(r)), imag(hh(r)));
|
||||
else
|
||||
fprintf(f, " {%f,%f}\n};", real(hh(r)), imag(hh(r)));
|
||||
end
|
||||
end
|
||||
|
||||
fclose(f);
|
|
@ -1,39 +0,0 @@
|
|||
% make_ssbfilt.m
|
||||
% David Rowe May 2015
|
||||
%
|
||||
% Creates low pass filter coeff used to implement a SSB filter in ch
|
||||
|
||||
graphics_toolkit ("gnuplot");
|
||||
|
||||
ssbfilt_n = 100;
|
||||
ssbfilt_bw = 2400;
|
||||
ssbfilt_centre = 1500;
|
||||
Fs = 8000;
|
||||
|
||||
ssbfilt_coeff = sbfilt_coeff = fir1(ssbfilt_n, ssbfilt_bw/Fs);
|
||||
|
||||
figure(1)
|
||||
clf;
|
||||
h = freqz(ssbfilt_coeff,1,Fs/2);
|
||||
plot(20*log10(abs(h)))
|
||||
grid minor
|
||||
|
||||
% save coeffs to a C header file
|
||||
|
||||
f=fopen("../src/ssbfilt_coeff.h","wt");
|
||||
fprintf(f,"/* %d Hz LPF FIR filter coeffs */\n", ssbfilt_bw);
|
||||
fprintf(f,"/* Generated by make_ssbfilt Octave script */\n");
|
||||
|
||||
fprintf(f,"\n#define SSBFILT_N %d\n\n", ssbfilt_n);
|
||||
fprintf(f,"\n#define SSBFILT_CENTRE %d\n\n", ssbfilt_centre);
|
||||
|
||||
fprintf(f,"float ssbfilt_coeff[]={\n");
|
||||
for r=1:ssbfilt_n
|
||||
if r < ssbfilt_n
|
||||
fprintf(f, " %f,\n", ssbfilt_coeff(r));
|
||||
else
|
||||
fprintf(f, " %f\n};", ssbfilt_coeff(r));
|
||||
end
|
||||
end
|
||||
|
||||
fclose(f);
|
|
@ -1,13 +0,0 @@
|
|||
% pl_scatter.m
|
||||
% Render scatter plot from freedv_data_raw_rx --scatter
|
||||
|
||||
function pl_scatter(filename)
|
||||
s=load(filename);
|
||||
figure(1); clf;
|
||||
for b=1:length(fieldnames(s))
|
||||
field_name = fieldnames(s){b};
|
||||
x = s.(field_name);
|
||||
plot(x,'+');
|
||||
end
|
||||
print("scatter.png", "-dpng");
|
||||
endfunction
|
|
@ -1,141 +0,0 @@
|
|||
% qam16_test.m
|
||||
% David Rowe May 2020
|
||||
%
|
||||
% Octave symbol rate QAM16/LDPC experiments
|
||||
|
||||
% Libraries we need
|
||||
|
||||
1;
|
||||
qam16;
|
||||
ldpc;
|
||||
|
||||
function test_qam16(fg=2)
|
||||
printf("QAM16 ----------------------------------------\n");
|
||||
|
||||
mod_order = 16; bps = log2(mod_order);
|
||||
modulation = 'QAM'; mapping = ""; demod_type = 0; decoder_type = 0;
|
||||
max_iterations = 100; EsNo_dec = 10;
|
||||
qam16_const = [
|
||||
1 + j, 1 + j*3, 3 + j, 3 + j*3;
|
||||
1 - j, 1 - j*3, 3 - j, 3 - j*3;
|
||||
-1 + j, -1 + j*3, -3 + j, -3 + j*3;
|
||||
-1 - j, -1 - j*3, -3 - j, -3 - j*3];
|
||||
rms = sqrt(qam16_const(:)'*qam16_const(:)/16);
|
||||
qam16_const = qam16_const/rms;
|
||||
constellation_source = 'custom';
|
||||
test_qam16_mod_demod(qam16_const);
|
||||
|
||||
load HRA_504_396.txt
|
||||
if strcmp(constellation_source,'cml')
|
||||
code_param = ldpc_init_user(HRA_504_396, modulation, mod_order, mapping);
|
||||
else
|
||||
code_param = ldpc_init_user(HRA_504_396, modulation, mod_order, mapping, reshape(qam16_const,1,16));
|
||||
end
|
||||
rate = code_param.ldpc_data_bits_per_frame/code_param.ldpc_coded_bits_per_frame;
|
||||
|
||||
printf("EbNodB Tbits Terrs BER Tcbits Tcerrs Perrs CBER CPER\n");
|
||||
EbNodBvec = 3:10; Ntrials = 1000;
|
||||
for i=1:length(EbNodBvec)
|
||||
EbNodB = EbNodBvec(i);
|
||||
EsNodB = EbNodB + 10*log10(rate) + 10*log10(bps); EsNodBvec(i) = EsNodB;
|
||||
EsNo = 10^(EsNodB/10);
|
||||
variance = 1/EsNo;
|
||||
Terrs = Tbits = 0; Tcerrs = 0; Tcbits = 0; Perrs = 0; rx_symbols_log = [];
|
||||
for nn = 1:Ntrials
|
||||
tx_bits = round(rand(1, code_param.ldpc_data_bits_per_frame));
|
||||
[tx_codeword, tx_symbols] = ldpc_enc(tx_bits, code_param);
|
||||
noise = sqrt(variance*0.5)*(randn(1,length(tx_symbols)) + j*randn(1,length(tx_symbols)));
|
||||
rx_symbols = tx_symbols + noise;
|
||||
rx_symbols_log = [rx_symbols_log rx_symbols];
|
||||
|
||||
% uncoded decode/demod and count errors
|
||||
rx_codeword = zeros(1,code_param.ldpc_coded_bits_per_frame);
|
||||
for s=1:length(rx_symbols)
|
||||
rx_codeword((s-1)*bps+1:s*bps) = qam16_demod(qam16_const,rx_symbols(s));
|
||||
end
|
||||
Nerr = sum(xor(tx_codeword,rx_codeword));
|
||||
Terrs += Nerr;
|
||||
Tbits += code_param.ldpc_coded_bits_per_frame;
|
||||
|
||||
% LDPC demod/decode and count errors
|
||||
dec_rx_codeword = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, rx_symbols, EsNo_dec, ones(1,length(rx_symbols)));
|
||||
errors_positions = xor(tx_bits, dec_rx_codeword(1:code_param.ldpc_data_bits_per_frame));
|
||||
Ncerr = sum(errors_positions);
|
||||
Tcbits += code_param.ldpc_data_bits_per_frame; Tcerrs += Ncerr;
|
||||
if Ncerr Perrs++; end
|
||||
end
|
||||
figure(fg); clf; plot(rx_symbols_log,"."); axis([-1.5 1.5 -1.5 1.5]); drawnow;
|
||||
printf("%5.1f %6d %6d %5.2f %6d %6d %6d %5.2f %5.2f\n",
|
||||
EbNodB, Tbits, Terrs, Terrs/Tbits, Tcbits, Tcerrs, Perrs, Tcerrs/Tcbits, Perrs/Ntrials);
|
||||
ber(i) = Terrs/Tbits; cber(i) = Tcerrs/Tcbits; cper(i) = Perrs/Ntrials;
|
||||
end
|
||||
print("qam64_scatter.png","-dpng");
|
||||
|
||||
figure(fg+1); clf; title('QAM16 Uncoded');
|
||||
uncoded_EbNodBvec = EbNodBvec + 10*log10(rate);
|
||||
ber_theory = ber_qam(uncoded_EbNodBvec);
|
||||
semilogy(uncoded_EbNodBvec,ber_theory,'b+-;uncoded QAM16 BER theory;','markersize', 10, 'linewidth', 2); hold on;
|
||||
semilogy(uncoded_EbNodBvec,ber+1E-10,'g+-;uncoded QAM16 BER;','markersize', 10, 'linewidth', 2); hold on;
|
||||
grid; axis([min(uncoded_EbNodBvec) max(uncoded_EbNodBvec) 1E-5 1]); xlabel('Uncoded Eb/No (dB)');
|
||||
print("qam16_uncoded_ber.png","-dpng");
|
||||
|
||||
figure(fg+2); clf; title('QAM16 with LDPC (504,396)');
|
||||
semilogy(EbNodBvec,cber+1E-10,'b+-;QAM16 coded BER;','markersize', 10, 'linewidth', 2); hold on;
|
||||
semilogy(EbNodBvec,cper+1E-10,'g+-;QAM16 coded PER;','markersize', 10, 'linewidth', 2); hold off;
|
||||
grid; axis([min(EbNodBvec) max(EbNodBvec) 1E-5 1]); xlabel('Eb/No (dB)');
|
||||
|
||||
figure(fg+3); clf; title('QAM16 with LDPC (504,396)');
|
||||
semilogy(EsNodBvec,cber+1E-10,'b+-;QAM16 coded BER;','markersize', 10, 'linewidth', 2); hold on;
|
||||
semilogy(EsNodBvec,cper+1E-10,'g+-;QAM16 coded PER;','markersize', 10, 'linewidth', 2); hold off;
|
||||
grid; axis([min(EsNodBvec) max(EsNodBvec) 1E-5 1]); xlabel('Es/No (dB)');
|
||||
print("qam16_504_396.png","-dpng");
|
||||
endfunction
|
||||
|
||||
|
||||
% Thanks Bill VK5DSP, for the QAM BER functions
|
||||
|
||||
function p = ber_qam(ebn0)
|
||||
% Calculate the bit error rate (BER) for square 16QAM in AWGN
|
||||
% given the Eb/N0 in dB, ebn0 can be a scalar or vector
|
||||
% (assuming coherent detection, uncoded)
|
||||
% [section 5.3 Webb and Hanzo text]
|
||||
|
||||
e = 4*10.^(ebn0/10); % Es/N0 vector in linear
|
||||
b2 = qfn(sqrt(e/5));
|
||||
b1 = (qfn(sqrt(e/5)) + qfn(3*sqrt(e/5)))/2;
|
||||
p = (b1+b2)/2;
|
||||
endfunction
|
||||
|
||||
function tail=qfn(a)
|
||||
% Usage: tail=qfn(a)
|
||||
% where: tail=area under the tail of the normal dist. from a to inf.
|
||||
% for zero mean, unit variance distribution
|
||||
%
|
||||
% If no argument is given, plot Q(x) for x = 0 to 5
|
||||
|
||||
% use erfc instead of 1-erf to avoid truncation errors! April 2010
|
||||
|
||||
fact = 1 / sqrt(2);
|
||||
if exist('a')
|
||||
|
||||
% tail = 0.5 * ( 1 - erf(a * fact));
|
||||
tail = 0.5 * erfc(a * fact);
|
||||
else
|
||||
x=(0: 0.1: 6); semilogy(x, 0.5*( erfc(x * fact)));
|
||||
title('Q function plot');
|
||||
xlabel('x'); ylabel('Q(x)');
|
||||
end
|
||||
endfunction
|
||||
|
||||
|
||||
% --------------------------------------------------------------------------------
|
||||
% START SIMULATIONS
|
||||
% --------------------------------------------------------------------------------
|
||||
|
||||
more off;
|
||||
format;
|
||||
|
||||
% Start CML library (see CML set up instructions in ldpc.m)
|
||||
init_cml();
|
||||
|
||||
test_qam16(1)
|
|
@ -1,851 +0,0 @@
|
|||
% test_ldpc_fsk_lib
|
||||
% David Rowe 16 April 2016
|
||||
%
|
||||
% A series of tests for ldpc_fsk_lib, and C versions ldpc_enc and ldpc_dec.
|
||||
% Gradually builds up complete C command line for SSTV balloon system,
|
||||
% using Octave versions of LDPC and FSK modem as reference points.
|
||||
|
||||
1;
|
||||
|
||||
% encodes and decodes one frame, also writes codeword.bin for testing
|
||||
% decode_from_file() below, and can optionally generate include file for
|
||||
% C version of decoder.
|
||||
|
||||
function [data code_param] = simple_ut(c_include_file)
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
EsNodB = 3;
|
||||
mod_order = 2;
|
||||
|
||||
code_param = ldpc_init(HRA, mod_order);
|
||||
data = round( rand( 1, code_param.data_bits_per_frame ) );
|
||||
codeword = ldpc_encode(code_param, data);
|
||||
f = fopen("codeword.bin","wt"); fwrite(f, codeword, "uint8"); fclose(f);
|
||||
s = 1 - 2 * codeword;
|
||||
code_param.symbols_per_frame = length( s );
|
||||
|
||||
EsNo = 10^(EsNodB/10);
|
||||
variance = 1/(2*EsNo);
|
||||
noise = sqrt(variance)* randn(1,code_param.symbols_per_frame);
|
||||
rx = s + noise;
|
||||
|
||||
if nargin == 1
|
||||
code_param.c_include_file = c_include_file;
|
||||
end
|
||||
[detected_data Niters] = ldpc_decode(rx, code_param, max_iterations, decoder_type);
|
||||
|
||||
error_positions = xor(detected_data(1:code_param.data_bits_per_frame), data);
|
||||
Nerrs = sum(error_positions);
|
||||
|
||||
printf("Nerrs = %d\n", Nerrs);
|
||||
end
|
||||
|
||||
|
||||
% This version decodes from a file of bits
|
||||
|
||||
function detected_data = decode_from_file(filename)
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
mod_order = 2;
|
||||
|
||||
f = fopen(filename,"rb"); codeword = fread(f, "uint8")'; fclose(f);
|
||||
r = 1 - 2 * codeword;
|
||||
code_param = ldpc_init(HRA, mod_order);
|
||||
[detected_data Niters] = ldpc_decode(r, code_param, max_iterations, decoder_type);
|
||||
end
|
||||
|
||||
|
||||
% plots a BER curve for the LDPC decoder. Takes a while to run, uses parallel cores
|
||||
|
||||
function plot_curve
|
||||
num_cores = 4; % set this to the number of cores you have
|
||||
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
[Nr Nc] = size(HRA);
|
||||
sim_in.rate = (Nc-Nr)/Nc;
|
||||
|
||||
sim_in.HRA = HRA;
|
||||
sim_in.mod_order = 2;
|
||||
sim_in.framesize = Nc;
|
||||
sim_in.mod_order = 2;
|
||||
sim_in.Lim_Ferrs = 100;
|
||||
|
||||
% note we increase number of trials as BER goes down
|
||||
|
||||
Esvec = [ 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 ];
|
||||
Ntrials = [ 1E4 1E4 1E4 1E4 1E5 1E5 1E5 1E5 1E5 ];
|
||||
num_runs = length(Esvec)
|
||||
|
||||
sim_in_vec(1:num_runs) = sim_in;
|
||||
for i = 1:num_runs
|
||||
sim_in_vec(i).Esvec = Esvec(i);
|
||||
sim_in_vec(i).Ntrials = Ntrials(i);
|
||||
end
|
||||
|
||||
%sim_out = ldpc5(sim_in_vec(1));
|
||||
tstart = time();
|
||||
sim_out = pararrayfun(num_cores, @ldpc5, sim_in_vec);
|
||||
tend = time();
|
||||
|
||||
total_bits = sum(Ntrials)*sim_in.framesize;
|
||||
total_secs = tend - tstart;
|
||||
printf("%d bits in %4.1f secs, or %5f bits/s\n", total_bits, total_secs, total_bits/total_secs);
|
||||
|
||||
for i=1:num_runs
|
||||
Ebvec(i) = sim_out(i).Ebvec;
|
||||
BERvec(i) = sim_out(i).BERvec;
|
||||
end
|
||||
semilogy(Ebvec, BERvec, '+-')
|
||||
xlabel('Eb/N0')
|
||||
ylabel('BER')
|
||||
title(['H2064 516 sparse.mat' ' ' date])
|
||||
|
||||
end
|
||||
|
||||
|
||||
% Test C encoder
|
||||
|
||||
function test_c_encoder
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
EsNodB = 3;
|
||||
mod_order = 2;
|
||||
frames = 100;
|
||||
|
||||
EsNo = 10^(EsNodB/10);
|
||||
variance = 1/(2*EsNo);
|
||||
|
||||
code_param = ldpc_init(HRA, mod_order);
|
||||
|
||||
data = round(rand(1,frames*code_param.data_bits_per_frame));
|
||||
f = fopen("data.bin","wt"); fwrite(f, data, "uint8"); fclose(f);
|
||||
|
||||
% Outboard C encoder
|
||||
|
||||
system("../src/ldpc_enc data.bin codewords.bin");
|
||||
|
||||
% Test with Octave decoder
|
||||
|
||||
f = fopen("codewords.bin","rb"); codewords = fread(f, "uint8")'; fclose(f);
|
||||
|
||||
Nerrs = 0;
|
||||
for i=1:frames
|
||||
st = (i-1)*code_param.symbols_per_frame+1; en = st+code_param.symbols_per_frame-1;
|
||||
tx = 1 - 2 * codewords(st:en);
|
||||
|
||||
noise = sqrt(variance)*randn(1,code_param.symbols_per_frame);
|
||||
rx = tx + noise;
|
||||
|
||||
[detected_data Niters] = ldpc_decode(rx, code_param, max_iterations, decoder_type);
|
||||
|
||||
st = (i-1)*code_param.data_bits_per_frame+1; en = st+code_param.data_bits_per_frame-1;
|
||||
error_positions = xor(detected_data(1:code_param.data_bits_per_frame), data(st:en));
|
||||
Nerrs += sum(error_positions);
|
||||
end
|
||||
|
||||
printf("Nerrs = %d\n", Nerrs);
|
||||
end
|
||||
|
||||
|
||||
function test_c_decoder
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
mod_order = 2;
|
||||
frames = 10;
|
||||
EsNodB = 2;
|
||||
sdinput = 1;
|
||||
|
||||
EsNo = 10^(EsNodB/10);
|
||||
variance = 1/(2*EsNo);
|
||||
|
||||
code_param = ldpc_init(HRA, mod_order);
|
||||
data = round(rand(1,code_param.data_bits_per_frame*frames));
|
||||
|
||||
f = fopen("data.bin","wt"); fwrite(f, data, "uint8"); fclose(f);
|
||||
system("../src/ldpc_enc data.bin codewords.bin");
|
||||
f = fopen("codewords.bin","rb"); codewords = fread(f, "uint8")'; fclose(f);
|
||||
|
||||
s = 1 - 2 * codewords;
|
||||
noise = sqrt(variance)*randn(1,code_param.symbols_per_frame*frames);
|
||||
r = s + noise;
|
||||
|
||||
% calc LLRs frame by frame
|
||||
|
||||
for i=1:frames
|
||||
st = (i-1)*code_param.symbols_per_frame+1;
|
||||
en = st + code_param.symbols_per_frame-1;
|
||||
llr(st:en) = sd_to_llr(r(st:en));
|
||||
end
|
||||
|
||||
% Outboard C decoder
|
||||
|
||||
if sdinput
|
||||
f = fopen("sd.bin","wb"); fwrite(f, r, "double"); fclose(f);
|
||||
system("../src/ldpc_dec sd.bin data_out.bin --sd");
|
||||
else
|
||||
f = fopen("llr.bin","wb"); fwrite(f, llr, "double"); fclose(f);
|
||||
system("../src/ldpc_dec llr.bin data_out.bin");
|
||||
end
|
||||
|
||||
f = fopen("data_out.bin","rb"); data_out = fread(f, "uint8")'; fclose(f);
|
||||
|
||||
Nerrs = Nerrs2 = zeros(1,frames);
|
||||
for i=1:frames
|
||||
|
||||
% Check C decoder
|
||||
|
||||
data_st = (i-1)*code_param.data_bits_per_frame+1;
|
||||
data_en = data_st+code_param.data_bits_per_frame-1;
|
||||
st = (i-1)*code_param.symbols_per_frame+1;
|
||||
en = st+code_param.data_bits_per_frame-1;
|
||||
data_out_c = data_out(st:en);
|
||||
error_positions = xor(data_out_c, data(data_st:data_en));
|
||||
Nerrs(i) = sum(error_positions);
|
||||
|
||||
% Octave decoder
|
||||
|
||||
st = (i-1)*code_param.symbols_per_frame+1; en = st+code_param.symbols_per_frame-1;
|
||||
[detected_data Niters] = ldpc_decode(r(st:en), code_param, max_iterations, decoder_type);
|
||||
st = (i-1)*code_param.data_bits_per_frame+1; en = st+code_param.data_bits_per_frame-1;
|
||||
data_out_octave = detected_data(1:code_param.data_bits_per_frame);
|
||||
error_positions = xor(data_out_octave, data(st:en));
|
||||
Nerrs2(i) = sum(error_positions);
|
||||
%printf("%4d ", Niters);
|
||||
end
|
||||
printf("Errors per frame:\nC.....:");
|
||||
for i=1:frames
|
||||
printf("%4d ", Nerrs(i));
|
||||
end
|
||||
printf("\nOctave:");
|
||||
for i=1:frames
|
||||
printf("%4d ", Nerrs2(i));
|
||||
end
|
||||
printf("\n");
|
||||
|
||||
end
|
||||
|
||||
% Saves a complex vector s to a file "filename" of IQ unsigned 8 bit
|
||||
% chars, same as RTLSDR format.
|
||||
|
||||
function save_rtlsdr(filename, s)
|
||||
mx = max(abs(s));
|
||||
re = real(s); im = imag(s);
|
||||
l = length(s);
|
||||
iq = zeros(1,2*l);
|
||||
%iq(1:2:2*l) = 127 + re*(127/mx);
|
||||
%iq(2:2:2*l) = 127 + im*(127/mx);
|
||||
iq(1:2:2*l) = 127 + 32*re;
|
||||
iq(2:2:2*l) = 127 + 32*im;
|
||||
figure(3); clf; plot(iq); title('simulated IQ signal from RTL SDR');
|
||||
fs = fopen(filename,"wb");
|
||||
fwrite(fs,iq,"uchar");
|
||||
fclose(fs);
|
||||
endfunction
|
||||
|
||||
|
||||
% Oversamples by a factor of 2 using Octaves resample() function then
|
||||
% uses linear interpolation to achieve fractional sample rate
|
||||
|
||||
function rx_resample_fract = fractional_resample(rx, resample_rate);
|
||||
assert(resample_rate < 2, "keep resample_rate between 0 and 2");
|
||||
rx_resample2 = resample(rx, 2, 1);
|
||||
l = length(rx_resample2);
|
||||
rx_resample_fract = zeros(1,l);
|
||||
k = 1;
|
||||
step = 2/resample_rate;
|
||||
for i=1:step:l-1
|
||||
i_low = floor(i);
|
||||
i_high = ceil(i);
|
||||
f = i - i_low;
|
||||
rx_resample_fract(k) = (1-f)*rx_resample2(i_low) + f*rx_resample2(i_high);
|
||||
%printf("i: %f i_low: %d i_high: %d f: %f\n", i, i_low, i_high, f);
|
||||
k++;
|
||||
end
|
||||
rx_resample_fract = rx_resample_fract(1:k-1);
|
||||
endfunction
|
||||
|
||||
|
||||
% Using simulated SSTV packet, generate complex fsk mod signals, 8-bit
|
||||
% unsigned IQ for feeding into C demod chain. Can also be used to
|
||||
% generate BER curves. Found bugs in UW size and our use of csdr
|
||||
% re-sampler using this function, and by gradually and carefully
|
||||
% building up the C command line.
|
||||
|
||||
#{
|
||||
todo: [X] uncoded BER
|
||||
[X] octave fsk demod
|
||||
[X] use C demod
|
||||
[X] compare uncoded BER to unsigned 8 bit IQ to regular 16-bit
|
||||
[X] generate complex rx signal with noise
|
||||
[X] used cmd line utils to drive demod
|
||||
[X] test with resampler
|
||||
[X] measure effect on PER with coding
|
||||
#}
|
||||
|
||||
function [n_uncoded_errs n_uncoded_bits] = run_sstv_sim(sim_in, EbNodB)
|
||||
|
||||
frames = sim_in.frames;
|
||||
demod_type = sim_in.demod_type;
|
||||
|
||||
% init LDPC code
|
||||
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
mod_order = 2;
|
||||
|
||||
code_param = ldpc_init(HRA, mod_order);
|
||||
|
||||
% note fixed frame of bits used for BER testing
|
||||
|
||||
tx_codeword = gen_sstv_frame;
|
||||
|
||||
% init FSK modem
|
||||
|
||||
fsk_horus_as_a_lib = 1;
|
||||
fsk_horus;
|
||||
states = fsk_horus_init_hbr(9600, 8, 1200, 2, length(tx_codeword));
|
||||
states.df(1:states.M) = 0;
|
||||
states.dA(1:states.M) = 1;
|
||||
states.tx_real = 0; % Octave fsk_mod generates complex valued output
|
||||
% so we can simulate rtl_sdr complex output
|
||||
|
||||
% Set up simulated tx tones to sit in the middle of cdsr passband
|
||||
|
||||
filt_low_norm = 0.1; filt_high_norm = 0.4;
|
||||
fc = states.Fs*(filt_low_norm + filt_high_norm)/2;
|
||||
%fc = 1800;
|
||||
f1 = fc - states.Rs/2;
|
||||
f2 = fc + states.Rs/2;
|
||||
states.ftx = [f1 f2];
|
||||
|
||||
% set up AWGN channel
|
||||
|
||||
EbNo = 10^(EbNodB/10);
|
||||
variance = states.Fs/(states.Rs*EbNo*states.bitspersymbol);
|
||||
|
||||
% start simulation ----------------------------------------
|
||||
|
||||
tx_bit_stream = [];
|
||||
for i=1:frames
|
||||
% uncomment for different data on each frame
|
||||
%tx_codeword = gen_sstv_frame;
|
||||
tx_bit_stream = [tx_bit_stream tx_codeword];
|
||||
end
|
||||
|
||||
printf("%d bits at %d bit/s is a %3.1f second run\n", length(tx_bit_stream), 115200,length(tx_bit_stream)/115200);
|
||||
|
||||
% modulate and channel model
|
||||
|
||||
tx = fsk_horus_mod(states, tx_bit_stream);
|
||||
noise_real = sqrt(variance)*randn(length(tx),1);
|
||||
noise_complex = sqrt(variance/2)*(randn(length(tx),1) + j*randn(length(tx),1));
|
||||
|
||||
% demodulate -----------------------------------------------------
|
||||
|
||||
if demod_type == 1
|
||||
|
||||
% Octave demod
|
||||
|
||||
if states.tx_real
|
||||
rx = tx + noise_real;
|
||||
else
|
||||
rx = tx + noise_complex;
|
||||
end
|
||||
SNRdB = 10*log10(var(tx)/var(noise_complex));
|
||||
|
||||
% demodulate frame by frame using Octave demod
|
||||
|
||||
st = 1;
|
||||
run_frames = floor(length(rx)/states.N);
|
||||
rx_bit_stream = [];
|
||||
rx_sd_stream = [];
|
||||
for f=1:run_frames
|
||||
|
||||
% extract nin samples from rx sample stream
|
||||
|
||||
nin = states.nin;
|
||||
en = st + states.nin - 1;
|
||||
|
||||
if en <= length(rx) % due to nin variations its possible to overrun buffer
|
||||
sf = rx(st:en);
|
||||
st += nin;
|
||||
|
||||
% demodulate to stream of bits
|
||||
|
||||
states.f = [f1 f2]; % note that for Octave demod we cheat and use known tone frequencies
|
||||
% allows us to determine if freq offset estimation in C demod is a problem
|
||||
|
||||
[rx_bits states] = fsk_horus_demod(states, sf);
|
||||
rx_bit_stream = [rx_bit_stream rx_bits];
|
||||
rx_sd_stream = [rx_sd_stream states.rx_bits_sd];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if demod_type == 2
|
||||
% baseline C demod
|
||||
|
||||
if states.tx_real
|
||||
rx = tx + noise_real;
|
||||
else
|
||||
rx = 2*real(tx) + noise_real;
|
||||
end
|
||||
SNRdB = 10*log10(var(tx)/var(noise_real));
|
||||
rx_scaled = 1000*real(rx);
|
||||
f = fopen("fsk_demod.raw","wb"); fwrite(f, rx_scaled, "short"); fclose(f);
|
||||
system("../build_linux/src/fsk_demod 2X 8 9600 1200 fsk_demod.raw fsk_demod.bin");
|
||||
f = fopen("fsk_demod.bin","rb"); rx_bit_stream = fread(f, "uint8")'; fclose(f);
|
||||
end
|
||||
|
||||
if demod_type == 3
|
||||
% C demod driven by csdr command line kung fu
|
||||
|
||||
assert(states.tx_real == 0, "need complex signal for this test");
|
||||
rx = tx + noise_complex;
|
||||
SNRdB = 10*log10(var(tx)/var(noise_real));
|
||||
save_rtlsdr("fsk_demod.iq", rx);
|
||||
system("cat fsk_demod.iq | csdr convert_u8_f | csdr bandpass_fir_fft_cc 0.1 0.4 0.05 | csdr realpart_cf | csdr convert_f_s16 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - fsk_demod.bin");
|
||||
f = fopen("fsk_demod.bin","rb"); rx_bit_stream = fread(f, "uint8")'; fclose(f);
|
||||
end
|
||||
|
||||
if demod_type == 4
|
||||
% C demod with resampler ....... getting closer to Mark's real time cmd line
|
||||
|
||||
assert(states.tx_real == 0, "need complex signal for this test");
|
||||
rx = tx + noise_complex;
|
||||
SNRdB = 10*log10(var(tx)/var(noise_real));
|
||||
|
||||
printf("resampling ...\n");
|
||||
rx_resample_fract = fractional_resample(rx, 1.08331);
|
||||
%rx_resample_fract = fractional_resample(rx_resample_fract, 1/1.08331);
|
||||
save_rtlsdr("fsk_demod_resample.iq", rx_resample_fract);
|
||||
|
||||
printf("run C cmd line chain ...\n");
|
||||
% system("cat fsk_demod_resample.iq | csdr convert_u8_f | csdr bandpass_fir_fft_cc 0.1 0.4 0.05 | csdr realpart_cf | csdr convert_f_s16 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - fsk_demod.bin");
|
||||
system("cat fsk_demod_resample.iq | csdr convert_u8_f | csdr bandpass_fir_fft_cc 0.1 0.4 0.05 | csdr realpart_cf | csdr convert_f_s16 | ../unittest/tsrc - - 0.9230968 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - fsk_demod.bin");
|
||||
% system("cat fsk_demod_resample.iq | csdr convert_u8_f | csdr bandpass_fir_fft_cc 0.1 0.4 0.05 | csdr realpart_cf | csdr fractional_decimator_ff 1.08331 | csdr convert_f_s16 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - fsk_demod.bin");
|
||||
f = fopen("fsk_demod.bin","rb"); rx_bit_stream = fread(f, "uint8")'; fclose(f);
|
||||
end
|
||||
|
||||
|
||||
if demod_type == 5
|
||||
|
||||
% C demod with resampler and use C code to measure PER, in this
|
||||
% test we don't need to run state machine below as C code gives us
|
||||
% the outputs we need
|
||||
|
||||
assert(states.tx_real == 0, "need complex signal for this test");
|
||||
rx = tx + noise_complex;
|
||||
SNRdB = 10*log10(var(tx)/var(noise_real));
|
||||
|
||||
printf("fract resampling ...\n");
|
||||
rx_resample_fract = fractional_resample(rx, 1.08331);
|
||||
save_rtlsdr("fsk_demod_resample.iq", rx_resample_fract);
|
||||
|
||||
% useful for HackRF
|
||||
%printf("10X resampling ...\n");
|
||||
%rx_resample_10M = resample(rx_resample_fract, 10, 1);
|
||||
%save_rtlsdr("fsk_demod_10M.iq", rx_resample_10M);
|
||||
|
||||
printf("run C cmd line chain - uncoded PER\n");
|
||||
system("cat fsk_demod_resample.iq | csdr convert_u8_f | csdr bandpass_fir_fft_cc 0.1 0.4 0.05 | csdr realpart_cf | csdr convert_f_s16 | ../unittest/tsrc - - 0.9230968 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - - | ../src/drs232 - /dev/null -v");
|
||||
|
||||
printf("run C cmd line chain - LDPC coded PER\n");
|
||||
system("cat fsk_demod_resample.iq | csdr convert_u8_f | csdr bandpass_fir_fft_cc 0.1 0.4 0.05 | csdr realpart_cf | csdr convert_f_s16 | ../unittest/tsrc - - 0.9230968 | ../build_linux/src/fsk_demod 2XS 8 9600 1200 - - | ../src/drs232_ldpc - /dev/null -v");
|
||||
end
|
||||
|
||||
if demod_type == 6
|
||||
% C demod with complex input driven simplfied csdr command line, just measure BER of demod
|
||||
|
||||
assert(states.tx_real == 0, "need complex signal for this test");
|
||||
rx = tx + noise_complex;
|
||||
SNRdB = 10*log10(var(tx)/var(noise_real));
|
||||
save_rtlsdr("fsk_demod.iq", rx);
|
||||
system("cat fsk_demod.iq | csdr convert_u8_f | csdr convert_f_s16 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - fsk_demod.bin C");
|
||||
|
||||
f = fopen("fsk_demod.bin","rb"); rx_bit_stream = fread(f, "uint8")'; fclose(f);
|
||||
end
|
||||
|
||||
if demod_type == 7
|
||||
% C demod with complex input, measure uncoded and uncoded PER
|
||||
|
||||
assert(states.tx_real == 0, "need complex signal for this test");
|
||||
rx = tx + noise_complex;
|
||||
SNRdB = 10*log10(var(tx)/var(noise_real));
|
||||
save_rtlsdr("fsk_demod.iq", rx);
|
||||
|
||||
printf("run C cmd line chain - uncoded PER\n");
|
||||
system("cat fsk_demod.iq | csdr convert_u8_f | csdr convert_f_s16 | ../build_linux/src/fsk_demod 2X 8 9600 1200 - - C | ../src/drs232 - /dev/null -v");
|
||||
|
||||
printf("run C cmd line chain - LDPC coded PER\n");
|
||||
%system("cat fsk_demod.iq | csdr convert_u8_f | csdr convert_f_s16 | ../build_linux/src/fsk_demod 2XS 8 9600 1200 - - C | ../src/drs232_ldpc - /dev/null -v");
|
||||
system("cat fsk_demod.iq | ../build_linux/src/fsk_demod 2XS 8 9600 1200 - - CU8 | ../src/drs232_ldpc - /dev/null -v");
|
||||
end
|
||||
|
||||
if (demod_type != 5) && (demod_type != 7)
|
||||
% state machine. Look for SSTV UW. When found count bit errors over one frame of bits
|
||||
|
||||
state = "wait for uw";
|
||||
start_uw_ind = 16*10+1; end_uw_ind = start_uw_ind + 5*10 - 1;
|
||||
uw_rs232 = tx_codeword(start_uw_ind:end_uw_ind); luw = length(uw_rs232);
|
||||
start_frame_ind = end_uw_ind + 1;
|
||||
nbits = length(rx_bit_stream);
|
||||
uw_thresh = 5;
|
||||
n_uncoded_errs = 0;
|
||||
n_uncoded_bits = 0;
|
||||
n_packets_rx = 0;
|
||||
last_i = 0;
|
||||
|
||||
% might as well include RS232 framing bits in uncoded error count
|
||||
|
||||
nbits_frame = code_param.data_bits_per_frame*10/8;
|
||||
|
||||
uw_errs = zeros(1, nbits);
|
||||
for i=luw:nbits
|
||||
uw_errs(i) = sum(xor(rx_bit_stream(i-luw+1:i), uw_rs232));
|
||||
end
|
||||
|
||||
for i=luw:nbits
|
||||
next_state = state;
|
||||
if strcmp(state, 'wait for uw')
|
||||
if uw_errs(i) <= uw_thresh
|
||||
next_state = 'count errors';
|
||||
tx_frame_ind = start_frame_ind;
|
||||
rx_frame_ind = i + 1;
|
||||
n_uncoded_errs_this_frame = 0;
|
||||
%printf("%d %s %s\n", i, state, next_state);
|
||||
if last_i
|
||||
printf("i: %d i-last_i: %d ", i, i-last_i);
|
||||
end
|
||||
end
|
||||
end
|
||||
if strcmp(state, 'count errors')
|
||||
n_uncoded_errs_this_frame += xor(rx_bit_stream(i), tx_codeword(tx_frame_ind));
|
||||
n_uncoded_bits++;
|
||||
tx_frame_ind++;
|
||||
if tx_frame_ind == (start_frame_ind+nbits_frame)
|
||||
n_uncoded_errs += n_uncoded_errs_this_frame;
|
||||
printf("n_uncoded_errs_this_frame: %d\n", n_uncoded_errs_this_frame);
|
||||
frame_rx232_rx = rx_bit_stream(rx_frame_ind:rx_frame_ind+nbits_frame-1);
|
||||
%tx_codeword(start_frame_ind+1:start_frame_ind+10)
|
||||
%frame_rx232_rx(1:10)
|
||||
sstv_checksum(frame_rx232_rx);
|
||||
last_i = i;
|
||||
n_packets_rx++;
|
||||
next_state = 'wait for uw';
|
||||
end
|
||||
end
|
||||
state = next_state;
|
||||
end
|
||||
|
||||
uncoded_ber = n_uncoded_errs/n_uncoded_bits;
|
||||
printf("EbNodB: %4.1f SNRdB: %4.1f pkts: %d bits: %d errs: %d BER: %4.3f\n",
|
||||
EbNodB, SNRdB, n_packets_rx, n_uncoded_bits, n_uncoded_errs, uncoded_ber);
|
||||
|
||||
figure(2);
|
||||
plot(uw_errs);
|
||||
title('Unique Word Hamming Distance')
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
% Function to test flight mode software. Takes a rx stream of
|
||||
% demodulated bits, and locates frames using UW detection. Extracts
|
||||
% data and parity bits. Uses data bits to generate parity bits here
|
||||
% and compare.
|
||||
|
||||
function compare_parity_bits(rx_bit_stream)
|
||||
nframes = 500;
|
||||
|
||||
% init LDPC code
|
||||
|
||||
load('H2064_516_sparse.mat');
|
||||
HRA = full(HRA);
|
||||
max_iterations = 100;
|
||||
decoder_type = 0;
|
||||
mod_order = 2;
|
||||
|
||||
code_param = ldpc_init(HRA, mod_order);
|
||||
|
||||
% generate frame, this will have random bits not related to
|
||||
% rx_stream, however we just use it for the UW
|
||||
|
||||
tx_codeword = gen_sstv_frame;
|
||||
l = length(tx_codeword);
|
||||
printf("expected rs232 frames codeword length: %d\n", l);
|
||||
|
||||
% state machine. Look for SSTV UW. When found count bit errors over one frame of bits
|
||||
|
||||
state = "wait for uw";
|
||||
start_uw_ind = 16*10+1; end_uw_ind = start_uw_ind + 4*10 - 1;
|
||||
uw_rs232 = tx_codeword(start_uw_ind:end_uw_ind); luw = length(uw_rs232);
|
||||
start_frame_ind = end_uw_ind + 1;
|
||||
nbits = nframes*l;
|
||||
uw_thresh = 5;
|
||||
n_uncoded_errs = 0;
|
||||
n_uncoded_bits = 0;
|
||||
n_packets_rx = 0;
|
||||
last_i = 0;
|
||||
|
||||
% might as well include RS232 framing bits in uncoded error count
|
||||
|
||||
uw_errs = luw*ones(1, nbits);
|
||||
for i=luw:nbits
|
||||
uw_errs(i) = sum(xor(rx_bit_stream(i-luw+1:i), uw_rs232));
|
||||
end
|
||||
|
||||
frame_start = find(uw_errs < 2)+1;
|
||||
nframes = length(frame_start)
|
||||
for i=1:nframes
|
||||
|
||||
% double check UW OK
|
||||
|
||||
st_uw = frame_start(i) - luw; en_uw = frame_start(i) - 1;
|
||||
uw_err_check = sum(xor(rx_bit_stream(st_uw:en_uw), uw_rs232));
|
||||
%printf("uw_err_check: %d\n", uw_err_check);
|
||||
|
||||
% strip off rs232 start/stop bits
|
||||
|
||||
nbits_rs232 = (256+2+65)*10;
|
||||
nbits = (256+2+65)*8;
|
||||
nbits_byte = 10;
|
||||
rx_codeword = zeros(1,nbits);
|
||||
pdb = 1;
|
||||
|
||||
for k=1:nbits_byte:nbits_rs232
|
||||
for l=1:8
|
||||
rx_codeword(pdb) = rx_bit_stream(frame_start(i)-1+k+l);
|
||||
pdb++;
|
||||
end
|
||||
end
|
||||
assert(pdb == (nbits+1));
|
||||
|
||||
data_bits = rx_codeword(1:256*8);
|
||||
checksum_bits = rx_codeword(256*8+1:258*8);
|
||||
parity_bits = rx_codeword(258*8+1:258*8+516);
|
||||
padding_bits = rx_codeword(258*8+516+1:258*8+516+1);
|
||||
|
||||
% stopped here as we found bug lol!
|
||||
end
|
||||
|
||||
figure(1); clf;
|
||||
plot(uw_errs);
|
||||
title('Unique Word Hamming Distance')
|
||||
figure(2); clf;
|
||||
lframe_start = length(frame_start);
|
||||
plot(frame_start(2:lframe_start)-frame_start(1:lframe_start-1));
|
||||
%title('Unique Word Hamming Distance')
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
% Start simulation --------------------------------------------------------
|
||||
|
||||
more off;
|
||||
currentdir = pwd;
|
||||
thiscomp = computer;
|
||||
|
||||
if strcmpi(thiscomp, 'MACI64')==1
|
||||
if exist('CMLSimulate')==0
|
||||
cd '/Users/bill/Current/Projects/DLR_FSO/Visit2013_FSO_GEO/cml'
|
||||
addpath '../' % assume the source files stored here
|
||||
CmlStartup % note that this is not in the cml path!
|
||||
disp('added MACI64 path and run CmlStartup')
|
||||
end
|
||||
end
|
||||
|
||||
if strfind(thiscomp, 'pc-linux-gnu')==8
|
||||
if exist('LdpcEncode')==0,
|
||||
cd '~/tmp/cml'
|
||||
CmlStartup
|
||||
disp('CmlStartup has been run')
|
||||
% rmpath '/home/bill/cml/mexhelp' % why is this needed?
|
||||
% maybe different path order in octave cf matlab ?
|
||||
end
|
||||
end
|
||||
|
||||
cd(currentdir)
|
||||
|
||||
ldpc_fsk_lib;
|
||||
randn('state',1);
|
||||
rand('state',1);
|
||||
|
||||
% ------------------ select which demo/test to run here ---------------
|
||||
|
||||
demo = 12;
|
||||
|
||||
if demo == 1
|
||||
printf("simple_ut....\n");
|
||||
data = simple_ut;
|
||||
end
|
||||
|
||||
if demo == 2
|
||||
printf("generate C header file....\n");
|
||||
data = simple_ut("../src/H2064_516_sparse.h");
|
||||
end
|
||||
|
||||
if demo == 3
|
||||
printf("decode_from_file ......\n");
|
||||
data = simple_ut;
|
||||
detected_data = decode_from_file("codeword.bin");
|
||||
error_positions = xor( detected_data(1:length(data)), data );
|
||||
Nerrs = sum(error_positions);
|
||||
printf(" Nerrs = %d\n", Nerrs);
|
||||
end
|
||||
|
||||
if demo == 4
|
||||
printf("plot a curve....\n");
|
||||
plot_curve;
|
||||
end
|
||||
|
||||
if demo == 5
|
||||
|
||||
% generate test data and save to disk
|
||||
|
||||
[data code_param] = simple_ut;
|
||||
f = fopen("dat_in2064.bin","wb"); fwrite(f, data, "uint8"); fclose(f);
|
||||
|
||||
% Outboard C encoder
|
||||
|
||||
system("../src/ldpc_enc dat_in2064.bin dat_op2064.bin");
|
||||
|
||||
% Test with Octave decoder
|
||||
|
||||
detected_data = decode_from_file("dat_op2064.bin");
|
||||
error_positions = xor(detected_data(1:length(data)), data);
|
||||
Nerrs = sum(error_positions);
|
||||
printf("Nerrs = %d\n", Nerrs);
|
||||
end
|
||||
|
||||
if demo == 6
|
||||
test_c_encoder;
|
||||
end
|
||||
|
||||
if demo == 7
|
||||
test_c_decoder;
|
||||
end
|
||||
|
||||
% generates simulated demod soft decision symbols to drive C ldpc decoder with
|
||||
|
||||
if demo == 8
|
||||
frames = 100;
|
||||
EsNodB = 3;
|
||||
EsNo = 10^(EsNodB/10);
|
||||
variance = 1/(2*EsNo);
|
||||
|
||||
frame_rs232 = [];
|
||||
for i=1:frames
|
||||
frame_rs232 = [frame_rs232 gen_sstv_frame];
|
||||
end
|
||||
|
||||
% write hard decn version to disk file, useful for fsk_mod input
|
||||
|
||||
f = fopen("sstv.bin","wb"); fwrite(f, frame_rs232, "char"); fclose(f);
|
||||
|
||||
% soft decision version (with noise)
|
||||
|
||||
s = 1 - 2*frame_rs232;
|
||||
noise = sqrt(variance)*randn(1,length(frame_rs232));
|
||||
r = s + noise;
|
||||
f = fopen("sstv_sd.bin","wb"); fwrite(f, r, "float32"); fclose(f);
|
||||
end
|
||||
|
||||
|
||||
if demo == 9
|
||||
frames = 100;
|
||||
EbNodB = 11;
|
||||
|
||||
frame_rs232 = [];
|
||||
for i=1:frames
|
||||
frame_rs232 = [frame_rs232 gen_sstv_frame];
|
||||
end
|
||||
|
||||
% Use C FSK modulator to generate modulated signal
|
||||
|
||||
f = fopen("sstv.bin","wb"); fwrite(f, frame_rs232, "char"); fclose(f);
|
||||
system("../build_linux/src/fsk_mod 2 9600 1200 1200 2400 sstv.bin fsk_mod.raw");
|
||||
|
||||
% Add some channel noise here in Octave
|
||||
|
||||
f = fopen("fsk_mod.raw","rb"); tx = fread(f, "short")'; fclose(f); tx_pwr = var(tx);
|
||||
Fs = 9600; Rs=1200; EbNolin = 10 ^ (EbNodB/10);
|
||||
variance = (tx_pwr/2)*states.Fs/(states.Rs*EbNolin*states.bitspersymbol);
|
||||
noise = sqrt(variance)*randn(1,length(tx));
|
||||
SNRdB = 10*log10(var(tx)/var(noise));
|
||||
rx = tx + noise;
|
||||
f = fopen("fsk_demod.raw","wb"); tx = fwrite(f, rx, "short"); fclose(f);
|
||||
|
||||
% Demodulate using C modem and C de-framer/LDPC decoder
|
||||
|
||||
system("../build_linux/src/fsk_demod 2XS 8 9600 1200 fsk_demod.raw - | ../src/drs232_ldpc - dummy_out.bin");
|
||||
end
|
||||
|
||||
|
||||
% Plots uncoded BER curves for two different SSTV simulations. Used
|
||||
% to compare results with different processing steps as we build up C
|
||||
% command line. BER curves are powerful ways to confirm system is
|
||||
% operating as expected, several bugs were found using this system.
|
||||
|
||||
if demo == 10
|
||||
sim_in.frames = 10;
|
||||
EbNodBvec = 7:10;
|
||||
|
||||
sim_in.demod_type = 3;
|
||||
ber_test1 = [];
|
||||
for i = 1:length(EbNodBvec)
|
||||
[n_uncoded_errs n_uncoded_bits] = run_sstv_sim(sim_in, EbNodBvec(i));
|
||||
ber_test1(i) = n_uncoded_errs/n_uncoded_bits;
|
||||
end
|
||||
|
||||
sim_in.demod_type = 4;
|
||||
ber_c = [];
|
||||
for i = 1:length(EbNodBvec)
|
||||
[n_uncoded_errs n_uncoded_bits] = run_sstv_sim(sim_in, EbNodBvec(i));
|
||||
ber_test2(i) = n_uncoded_errs/n_uncoded_bits;
|
||||
end
|
||||
|
||||
figure(1);
|
||||
clf;
|
||||
semilogy(EbNodBvec, ber_test1, '+-;first test;')
|
||||
grid;
|
||||
xlabel('Eb/No (dB)')
|
||||
ylabel('BER')
|
||||
|
||||
hold on;
|
||||
semilogy(EbNodBvec, ber_test2, 'g+-;second test;')
|
||||
legend("boxoff");
|
||||
hold off;
|
||||
|
||||
end
|
||||
|
||||
% Measure PER of complete coded and uncoded system
|
||||
|
||||
if demo == 11
|
||||
sim_in.frames = 10;
|
||||
EbNodB = 9;
|
||||
sim_in.demod_type = 7;
|
||||
run_sstv_sim(sim_in, EbNodB);
|
||||
end
|
||||
|
||||
|
||||
% Compare parity bits from an off-air stream of demodulated bits
|
||||
% Use something like:
|
||||
% cat ~/Desktop/923096fs_wenet.iq | ../build_linux/src/fsk_demod 2X 8 9600 1200 - fsk_demod.bin CU8
|
||||
% (note not soft dec mode)
|
||||
if demo == 12
|
||||
f = fopen("fsk_demod.bin","rb"); rx_bit_stream = fread(f, "uint8")'; fclose(f);
|
||||
|
||||
compare_parity_bits(rx_bit_stream);
|
||||
end
|
|
@ -1,648 +0,0 @@
|
|||
% tfsk.m
|
||||
% Author: Brady O'Brien 8 January 2016
|
||||
|
||||
|
||||
|
||||
% Copyright 2016 David Rowe
|
||||
%
|
||||
% All rights reserved.
|
||||
%
|
||||
% This program is free software; you can redistribute it and/or modify
|
||||
% it under the terms of the GNU Lesser General Public License version 2.1, as
|
||||
% published by the Free Software Foundation. This program is
|
||||
% distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
% WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
% FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
% License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU Lesser General Public License
|
||||
% along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
% Octave script to check c port of fsk_horus against the fsk_horus.m
|
||||
%
|
||||
% [X] - Functions to wrap around fsk_mod and fsk_demod executables
|
||||
% [X] - fsk_mod
|
||||
% [X] - fsk_demod
|
||||
% [X] - Functions to wrap around octave and c implementations, pass
|
||||
% same dataset, compare outputs, and give clear go/no-go
|
||||
% [X] - fsk_mod_test
|
||||
% [X] - fsk_demod_test
|
||||
% [X] - Port of run_sim and EbNodB curve test battery
|
||||
% [X] - Extract and compare more parameters from demod
|
||||
% [X] - Run some tests in parallel
|
||||
|
||||
#{
|
||||
|
||||
FSK Modem automated test instructions:
|
||||
|
||||
1. Use cmake to build in debug mode to ensure unittest/tfsk is built:
|
||||
|
||||
$ cd ~/codec2
|
||||
$ rm -Rf build_linux && mkdir build_linux
|
||||
$ cd build_linux
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
$ make
|
||||
|
||||
2 - Change tfsk_location below if required
|
||||
3 - Ensure Octave packages signal and parallel are installed
|
||||
4 - Start Octave and run tfsk_2400a.m. It will perform all tests automatically
|
||||
|
||||
#}
|
||||
|
||||
|
||||
|
||||
%tfsk executable path/file
|
||||
global tfsk_location = '../build_linux/unittest/tfsk';
|
||||
|
||||
%Set to 1 for verbose printouts
|
||||
global print_verbose = 0;
|
||||
|
||||
|
||||
fsk_horus_as_a_lib = 1; % make sure calls to test functions at bottom are disabled
|
||||
%fsk_horus_2fsk;
|
||||
fsk_horus
|
||||
pkg load signal;
|
||||
pkg load parallel;
|
||||
graphics_toolkit('gnuplot');
|
||||
|
||||
|
||||
global mod_pass_fail_maxdiff = 1e-3/5000;
|
||||
|
||||
function mod = fsk_mod_c(Fs,Rs,f1,fsp,bits,M)
|
||||
global tfsk_location;
|
||||
%command to be run by system to launch the modulator
|
||||
command = sprintf('%s MX %d %d %d %d %d fsk_mod_ut_bitvec fsk_mod_ut_modvec fsk_mod_ut_log.txt',tfsk_location,M,f1,fsp,Fs,Rs);
|
||||
%save input bits into a file
|
||||
bitvecfile = fopen('fsk_mod_ut_bitvec','wb+');
|
||||
fwrite(bitvecfile,bits,'uint8');
|
||||
fclose(bitvecfile);
|
||||
|
||||
%run the modulator
|
||||
system(command);
|
||||
|
||||
modvecfile = fopen('fsk_mod_ut_modvec','rb');
|
||||
mod = fread(modvecfile,'single');
|
||||
fclose(modvecfile);
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
%Compare 2 vectors, fail if they are not close enough
|
||||
function pass = vcompare(vc,voct,vname,tname,tol,pnum)
|
||||
global print_verbose;
|
||||
%Get delta of vectors
|
||||
dvec = abs(abs(vc)-abs(voct));
|
||||
|
||||
%Normalize difference
|
||||
dvec = dvec ./ abs(max(abs(voct))+1e-8);
|
||||
|
||||
maxdvec = abs(max(dvec));
|
||||
pass = maxdvec<tol;
|
||||
|
||||
if print_verbose == 1
|
||||
printf(' Comparing vectors %s in test %s. Diff is %f\n',vname,tname,maxdvec);
|
||||
end
|
||||
|
||||
if pass == 0
|
||||
printf('\n*** vcompare failed %s in test %s. Diff: %f Tol: %f\n\n',vname,tname,maxdvec,tol);
|
||||
|
||||
titlestr = sprintf('Diff between C and Octave of %s for %s',vname,tname)
|
||||
figure(10+pnum*2)
|
||||
plot(abs(dvec))
|
||||
title(titlestr)
|
||||
|
||||
figure(11+pnum*2)
|
||||
plot((1:length(vc)),abs(vc),(1:length(voct)),abs(voct))
|
||||
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
function test_stats = fsk_demod_xt(Fs,Rs,f1,fsp,mod,tname,M=2)
|
||||
global tfsk_location;
|
||||
global print_verbose;
|
||||
%Name of executable containing the modulator
|
||||
fsk_demod_ex_file = '../build/unittest/tfsk';
|
||||
modvecfilename = sprintf('fsk_demod_ut_modvec_%d',getpid());
|
||||
bitvecfilename = sprintf('fsk_demod_ut_bitvec_%d',getpid());
|
||||
tvecfilename = sprintf('fsk_demod_ut_tracevec_%d.txt',getpid());
|
||||
|
||||
%command to be run by system to launch the demod
|
||||
command = sprintf('%s DX %d %d %d %d %d %s %s %s',tfsk_location,M,f1,fsp,Fs,Rs,modvecfilename,bitvecfilename,tvecfilename);
|
||||
|
||||
%save modulated input into a file
|
||||
modvecfile = fopen(modvecfilename,'wb+');
|
||||
fwrite(modvecfile,mod,'single');
|
||||
fclose(modvecfile);
|
||||
|
||||
%run the modulator
|
||||
system(command);
|
||||
|
||||
bitvecfile = fopen(bitvecfilename,'rb');
|
||||
bits = fread(bitvecfile,'uint8');
|
||||
fclose(bitvecfile);
|
||||
bits = bits!=0;
|
||||
|
||||
%Load test vec dump
|
||||
load(tvecfilename);
|
||||
|
||||
%Clean up files
|
||||
delete(bitvecfilename);
|
||||
delete(modvecfilename);
|
||||
delete(tvecfilename);
|
||||
|
||||
o_f1_dc = [];
|
||||
o_f2_dc = [];
|
||||
o_f3_dc = [];
|
||||
o_f4_dc = [];
|
||||
o_f1_int = [];
|
||||
o_f2_int = [];
|
||||
o_f3_int = [];
|
||||
o_f4_int = [];
|
||||
o_f1 = [];
|
||||
o_f2 = [];
|
||||
o_f3 = [];
|
||||
o_f4 = [];
|
||||
o_EbNodB = [];
|
||||
o_ppm = [];
|
||||
o_Sf = [];
|
||||
o_fest = [];
|
||||
o_rx_timing = [];
|
||||
o_norm_rx_timing = [];
|
||||
o_nin = [];
|
||||
%Run octave demod, dump some test vectors
|
||||
states = fsk_horus_init_hbr(Fs,10,Rs,M);
|
||||
Ts = states.Ts;
|
||||
P = states.P;
|
||||
states.ftx(1) = f1;
|
||||
states.ftx(2) = f1+fsp;
|
||||
states.ftx(3) = f1+fsp*2;
|
||||
states.ftx(4) = f1+fsp*3;
|
||||
states.dA = 1;
|
||||
states.dF = 0;
|
||||
modin = mod;
|
||||
obits = [];
|
||||
while length(modin)>=states.nin
|
||||
ninold = states.nin;
|
||||
states = est_freq(states, modin(1:states.nin), states.M);
|
||||
[bitbuf,states] = fsk_horus_demod(states, modin(1:states.nin));
|
||||
modin=modin(ninold+1:length(modin));
|
||||
obits = [obits bitbuf];
|
||||
|
||||
%Save other parameters
|
||||
o_f1_dc = [o_f1_dc states.f_dc(1,1:states.Nmem-Ts/P)];
|
||||
o_f2_dc = [o_f2_dc states.f_dc(2,1:states.Nmem-Ts/P)];
|
||||
o_f1_int = [o_f1_int states.f_int(1,:)];
|
||||
o_f2_int = [o_f2_int states.f_int(2,:)];
|
||||
o_EbNodB = [o_EbNodB states.EbNodB];
|
||||
o_ppm = [o_ppm states.ppm];
|
||||
o_rx_timing = [o_rx_timing states.rx_timing];
|
||||
o_norm_rx_timing = [o_norm_rx_timing states.norm_rx_timing];
|
||||
o_Sf = [o_Sf states.Sf'];
|
||||
o_f1 = [o_f1 states.f(1)];
|
||||
o_f2 = [o_f1 states.f(2)];
|
||||
o_fest = [o_fest states.f];
|
||||
o_nin = [o_nin states.nin];
|
||||
if M==4
|
||||
o_f3_dc = [o_f3_dc states.f_dc(3,1:states.Nmem-Ts/P)];
|
||||
o_f4_dc = [o_f4_dc states.f_dc(4,1:states.Nmem-Ts/P)];
|
||||
o_f3_int = [o_f3_int states.f_int(3,:)];
|
||||
o_f4_int = [o_f4_int states.f_int(4,:)];
|
||||
o_f3 = [o_f1 states.f(3)];
|
||||
o_f4 = [o_f1 states.f(4)];
|
||||
end
|
||||
end
|
||||
|
||||
%close all
|
||||
|
||||
pass = 1;
|
||||
|
||||
pass = vcompare(o_Sf, t_fft_est(1:length(o_Sf)),'fft est',tname,1,1) && pass;
|
||||
pass = vcompare(o_fest, t_f_est,'f est',tname,1,2) && pass;
|
||||
pass = vcompare(o_rx_timing, t_rx_timing,'rx timing',tname,1,3) && pass;
|
||||
|
||||
if M==4
|
||||
pass = vcompare(o_f3_dc, t_f3_dc, 'f3 dc', tname,1,4) && pass;
|
||||
pass = vcompare(o_f4_dc, t_f4_dc, 'f4 dc', tname,1,5) && pass;
|
||||
pass = vcompare(o_f3_int, t_f3_int, 'f3 int', tname,1,6) && pass;
|
||||
pass = vcompare(o_f4_int, t_f4_int, 'f4 int', tname,1,7) && pass;
|
||||
end
|
||||
|
||||
pass = vcompare(o_f1_dc, t_f1_dc, 'f1 dc', tname,1,8) && pass;
|
||||
pass = vcompare(o_f2_dc, t_f2_dc, 'f2 dc', tname,1,9) && pass;
|
||||
pass = vcompare(o_f2_int, t_f2_int, 'f2 int', tname,1,10) && pass;
|
||||
pass = vcompare(o_f1_int, t_f1_int, 'f1 int', tname,1,11) && pass;
|
||||
|
||||
pass = vcompare(o_ppm , t_ppm, 'ppm', tname,1,12) && pass;
|
||||
pass = vcompare(o_EbNodB, t_EbNodB,'EbNodB', tname,1,13) && pass;
|
||||
pass = vcompare(o_nin, t_nin, 'nin', tname,1,14) && pass;
|
||||
pass = vcompare(o_norm_rx_timing, t_norm_rx_timing,'norm rx timing',tname,1,15) && pass;
|
||||
|
||||
|
||||
diffpass = sum(xor(obits,bits'))<5;
|
||||
diffbits = sum(xor(obits,bits'));
|
||||
|
||||
if print_verbose == 1
|
||||
printf('%d bit diff in test %s\n',diffbits,tname);
|
||||
end
|
||||
if diffpass==0
|
||||
printf('\n***bitcompare test failed test %s diff %d\n\n',tname,sum(xor(obits,bits')))
|
||||
figure(15)
|
||||
plot(xor(obits,bits'))
|
||||
title(sprintf('Bitcompare failure test %s',tname))
|
||||
end
|
||||
|
||||
pass = pass && diffpass;
|
||||
|
||||
assert(pass);
|
||||
|
||||
test_stats.pass = pass;
|
||||
test_stats.diff = sum(xor(obits,bits'));
|
||||
test_stats.cbits = bits';
|
||||
test_stats.obits = obits;
|
||||
|
||||
endfunction
|
||||
|
||||
function [dmod,cmod,omod,pass] = fsk_mod_test(Fs,Rs,f1,fsp,bits,tname,M=2)
|
||||
global mod_pass_fail_maxdiff;
|
||||
%Run the C modulator
|
||||
cmod = fsk_mod_c(Fs,Rs,f1,fsp,bits,M);
|
||||
%Set up and run the octave modulator
|
||||
states.M = M;
|
||||
states = fsk_horus_init_hbr(Fs,10,Rs,M);
|
||||
|
||||
states.ftx(1) = f1;
|
||||
states.ftx(2) = f1+fsp;
|
||||
|
||||
if states.M == 4
|
||||
states.ftx(3) = f1+fsp*2;
|
||||
states.ftx(4) = f1+fsp*3;
|
||||
end
|
||||
|
||||
states.dA = [1 1 1 1];
|
||||
states.dF = 0;
|
||||
omod = fsk_horus_mod(states,bits);
|
||||
|
||||
dmod = cmod-omod;
|
||||
pass = max(dmod)<(mod_pass_fail_maxdiff*length(dmod));
|
||||
if !pass
|
||||
printf('Mod failed test %s!\n',tname);
|
||||
end
|
||||
endfunction
|
||||
|
||||
% Random bit modulator test
|
||||
% Pass random bits through the modulators and compare
|
||||
function pass = test_mod_2400a_randbits
|
||||
rand('state',1);
|
||||
randn('state',1);
|
||||
bits = rand(1,96000)>.5;
|
||||
[dmod,cmod,omod,pass] = fsk_mod_test(48000,1200,1200,1200,bits,"mod 2400a randbits",4);
|
||||
|
||||
if(!pass)
|
||||
figure(1)
|
||||
plot(dmod)
|
||||
title("Difference between octave and C mod impl");
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
% A big ol' channel impairment tester
|
||||
% Shamlessly taken from fsk_horus
|
||||
% This throws some channel imparment or another at the C and octave modem so they
|
||||
% may be compared.
|
||||
function stats = tfsk_run_sim(test_frame_mode,EbNodB,timing_offset,fading,df,dA,M=2)
|
||||
global print_verbose;
|
||||
frames = 190;
|
||||
%EbNodB = 10;
|
||||
%timing_offset = 2.0; % see resample() for clock offset below
|
||||
%fading = 0; % modulates tx power at 2Hz with 20dB fade depth,
|
||||
% to simulate balloon rotating at end of mission
|
||||
%df = 0; % tx tone freq drift in Hz/s
|
||||
%dA = 1; % amplitude imbalance of tones (note this affects Eb so not a gd idea)
|
||||
|
||||
more off
|
||||
rand('state',10);
|
||||
randn('state',10);
|
||||
|
||||
% ----------------------------------------------------------------------
|
||||
|
||||
% sm2000 config ------------------------
|
||||
%states = fsk_horus_init(96000, 1200);
|
||||
%states.f1_tx = 4000;
|
||||
%states.f2_tx = 5200;
|
||||
|
||||
if test_frame_mode == 2
|
||||
% 2400A config
|
||||
states = fsk_horus_init_hbr(48000,10, 1200, M);
|
||||
states.f1_tx = 1200;
|
||||
states.f2_tx = 2400;
|
||||
states.f3_tx = 3600;
|
||||
states.f4_tx = 4800;
|
||||
states.ftx(1) = 1200;
|
||||
states.ftx(2) = 2400;
|
||||
states.ftx(3) = 3600;
|
||||
states.ftx(4) = 4800;
|
||||
|
||||
end
|
||||
|
||||
if test_frame_mode == 4
|
||||
% horus rtty config ---------------------
|
||||
states = fsk_horus_init_hbr(48000,10, 1200, M);
|
||||
states.f1_tx = 1200;
|
||||
states.f2_tx = 2400;
|
||||
states.f3_tx = 3600;
|
||||
states.f4_tx = 4800;
|
||||
states.ftx(1) = 1200;
|
||||
states.ftx(2) = 2400;
|
||||
states.ftx(3) = 3600;
|
||||
states.ftx(4) = 4800;
|
||||
|
||||
states.tx_bits_file = "horus_tx_bits_rtty.txt"; % Octave file of bits we FSK modulate
|
||||
|
||||
end
|
||||
|
||||
if test_frame_mode == 5
|
||||
% horus binary config ---------------------
|
||||
states = fsk_horus_init_hbr(48000,10, 1200, M);
|
||||
states.f1_tx = 1200;
|
||||
states.f2_tx = 2400;
|
||||
states.f3_tx = 3600;
|
||||
states.f4_tx = 4800;
|
||||
states.ftx(1) = 1200;
|
||||
states.ftx(2) = 2400;
|
||||
states.ftx(3) = 3600;
|
||||
states.ftx(4) = 4800;
|
||||
%%%states.tx_bits_file = "horus_tx_bits_binary.txt"; % Octave file of bits we FSK modulate
|
||||
states.tx_bits_file = "horus_payload_rtty.txt";
|
||||
end
|
||||
|
||||
% ----------------------------------------------------------------------
|
||||
|
||||
states.verbose = 0;%x1;
|
||||
N = states.N;
|
||||
P = states.P;
|
||||
Rs = states.Rs;
|
||||
nsym = states.nsym;
|
||||
Fs = states.Fs;
|
||||
states.df = df;
|
||||
states.dA = [dA dA dA dA];
|
||||
states.M = M;
|
||||
|
||||
EbNo = 10^(EbNodB/10);
|
||||
variance = states.Fs/(states.Rs*EbNo*states.bitspersymbol);
|
||||
|
||||
% set up tx signal with payload bits based on test mode
|
||||
|
||||
if test_frame_mode == 1
|
||||
% test frame of bits, which we repeat for convenience when BER testing
|
||||
test_frame = round(rand(1, states.nsym));
|
||||
tx_bits = [];
|
||||
for i=1:frames+1
|
||||
tx_bits = [tx_bits test_frame];
|
||||
end
|
||||
end
|
||||
if test_frame_mode == 2
|
||||
% random bits, just to make sure sync algs work on random data
|
||||
tx_bits = round(rand(1, states.nbit*(frames+1)));
|
||||
end
|
||||
if test_frame_mode == 3
|
||||
% ...10101... sequence
|
||||
tx_bits = zeros(1, states.nsym*(frames+1));
|
||||
tx_bits(1:2:length(tx_bits)) = 1;
|
||||
end
|
||||
|
||||
if (test_frame_mode == 4) || (test_frame_mode == 5)
|
||||
|
||||
% load up a horus msg from disk and modulate that
|
||||
|
||||
test_frame = load(states.tx_bits_file);
|
||||
ltf = length(test_frame);
|
||||
ntest_frames = ceil((frames+1)*nsym/ltf);
|
||||
tx_bits = [];
|
||||
for i=1:ntest_frames
|
||||
tx_bits = [tx_bits test_frame];
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
f1 = states.f1_tx;
|
||||
fsp = states.f2_tx-f1;
|
||||
states.dA = [dA dA dA dA];
|
||||
states.ftx(1) = f1;
|
||||
states.ftx(2) = f1+fsp;
|
||||
|
||||
if states.M == 4
|
||||
states.ftx(3) = f1+fsp*2;
|
||||
states.ftx(4) = f1+fsp*3;
|
||||
end
|
||||
|
||||
tx = fsk_horus_mod(states, tx_bits);
|
||||
|
||||
if timing_offset
|
||||
tx = resample(tx, 1000, 1001); % simulated 1000ppm sample clock offset
|
||||
end
|
||||
|
||||
if fading
|
||||
ltx = length(tx);
|
||||
tx = tx .* (1.1 + cos(2*pi*2*(0:ltx-1)/Fs))'; % min amplitude 0.1, -20dB fade, max 3dB
|
||||
end
|
||||
|
||||
noise = sqrt(variance)*randn(length(tx),1);
|
||||
rx = tx + noise;
|
||||
|
||||
test_name = sprintf("tfsk EbNodB:%d frames:%d timing_offset:%d fading:%d df:%d",EbNodB,frames,timing_offset,fading,df);
|
||||
tstats = fsk_demod_xt(Fs,Rs,states.f1_tx,fsp,rx,test_name,M);
|
||||
|
||||
pass = tstats.pass;
|
||||
obits = tstats.obits;
|
||||
cbits = tstats.cbits;
|
||||
stats.name = test_name;
|
||||
|
||||
if tstats.pass
|
||||
printf("Test %s passed\n",test_name);
|
||||
else
|
||||
printf("Test %s failed\n",test_name);
|
||||
end
|
||||
|
||||
% Figure out BER of octave and C modems
|
||||
bitcnt = length(tx_bits);
|
||||
rx_bits = obits;
|
||||
ber = 1;
|
||||
ox = 1;
|
||||
for offset = (1:100)
|
||||
nerr = sum(xor(rx_bits(offset:length(rx_bits)),tx_bits(1:length(rx_bits)+1-offset)));
|
||||
bern = nerr/(bitcnt-offset);
|
||||
if(bern < ber)
|
||||
ox = offset;
|
||||
best_nerr = nerr;
|
||||
end
|
||||
ber = min([ber bern]);
|
||||
end
|
||||
offset = ox;
|
||||
bero = ber;
|
||||
ber = 1;
|
||||
rx_bits = cbits;
|
||||
ox = 1;
|
||||
for offset = (1:100)
|
||||
nerr = sum(xor(rx_bits(offset:length(rx_bits)),tx_bits(1:length(rx_bits)+1-offset)));
|
||||
bern = nerr/(bitcnt-offset);
|
||||
if(bern < ber)
|
||||
ox = offset;
|
||||
best_nerr = nerr;
|
||||
end
|
||||
ber = min([ber bern]);
|
||||
end
|
||||
offset = ox;
|
||||
berc = ber;
|
||||
stats.berc = berc;
|
||||
stats.bero = bero;
|
||||
% coherent BER theory calculation
|
||||
|
||||
if print_verbose == 1
|
||||
printf("C BER: %f Oct BER: %f Test %s\n",berc,bero,test_name);
|
||||
end
|
||||
|
||||
stats.thrcoh = .5*(M-1)*erfc(sqrt( (log2(M)/2) * EbNo ));
|
||||
|
||||
% non-coherent BER theory calculation
|
||||
% It was complicated, so I broke it up
|
||||
|
||||
ms = M;
|
||||
ns = (1:ms-1);
|
||||
as = (-1).^(ns+1);
|
||||
bs = (as./(ns+1));
|
||||
|
||||
cs = ((ms-1)./ns);
|
||||
|
||||
ds = ns.*log2(ms);
|
||||
es = ns+1;
|
||||
fs = exp( -(ds./es)*EbNo );
|
||||
|
||||
thrncoh = ((ms/2)/(ms-1)) * sum(bs.*((ms-1)./ns).*exp( -(ds./es)*EbNo ));
|
||||
|
||||
stats.thrncoh = thrncoh;
|
||||
stats.pass = pass;
|
||||
endfunction
|
||||
|
||||
|
||||
function pass = ebno_battery_test(timing_offset,fading,df,dA,M)
|
||||
%Range of EbNodB over which to test
|
||||
ebnodbrange = fliplr(5:2:13);
|
||||
ebnodbs = length(ebnodbrange);
|
||||
|
||||
mode = 2;
|
||||
%Replication of other parameters for parcellfun
|
||||
modev = repmat(mode,1,ebnodbs);
|
||||
timingv = repmat(timing_offset,1,ebnodbs);
|
||||
fadingv = repmat(fading,1,ebnodbs);
|
||||
dfv = repmat(df,1,ebnodbs);
|
||||
dav = repmat(dA,1,ebnodbs);
|
||||
mv = repmat(M,1,ebnodbs);
|
||||
statv = pararrayfun(floor(1.25*nproc()),@tfsk_run_sim,modev,ebnodbrange,timingv,fadingv,dfv,dav,mv);
|
||||
%statv = arrayfun(@tfsk_run_sim,modev,ebnodbrange,timingv,fadingv,dfv,dav,mv);
|
||||
|
||||
passv = zeros(1,length(statv));
|
||||
for ii=(1:length(statv))
|
||||
passv(ii)=statv(ii).pass;
|
||||
if statv(ii).pass
|
||||
printf("Test %s passed\n",statv(ii).name);
|
||||
else
|
||||
printf("Test %s failed\n",statv(ii).name);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
%All pass flags are '1'
|
||||
pass = sum(passv)>=length(passv);
|
||||
%and no tests died
|
||||
pass = pass && length(passv)==ebnodbs;
|
||||
passv;
|
||||
assert(pass)
|
||||
endfunction
|
||||
|
||||
%Test with and without sample clock offset
|
||||
function pass = test_timing_var(df,dA,M)
|
||||
pass = ebno_battery_test(1,0,df,dA,M)
|
||||
assert(pass)
|
||||
pass = pass && ebno_battery_test(0,0,df,dA,M)
|
||||
assert(pass)
|
||||
endfunction
|
||||
|
||||
%Test with and without 1 Hz/S freq drift
|
||||
function pass = test_drift_var(M)
|
||||
pass = test_timing_var(1,1,M)
|
||||
assert(pass)
|
||||
pass = pass && test_timing_var(0,1,M)
|
||||
assert(pass)
|
||||
endfunction
|
||||
|
||||
function pass = test_fsk_battery()
|
||||
pass = 1;
|
||||
pass = pass && test_mod_2400a_randbits;
|
||||
assert(pass)
|
||||
pass = pass && test_drift_var(4);
|
||||
assert(pass)
|
||||
if pass
|
||||
printf("***** All tests passed! *****\n");
|
||||
end
|
||||
endfunction
|
||||
|
||||
function plot_fsk_bers(M=2)
|
||||
%Range of EbNodB over which to plot
|
||||
ebnodbrange = (4:13);
|
||||
|
||||
berc = ones(1,length(ebnodbrange));
|
||||
bero = ones(1,length(ebnodbrange));
|
||||
berinc = ones(1,length(ebnodbrange));
|
||||
beric = ones(1,length(ebnodbrange));
|
||||
ebnodbs = length(ebnodbrange)
|
||||
mode = 2;
|
||||
%Replication of other parameters for parcellfun
|
||||
modev = repmat(mode,1,ebnodbs);
|
||||
timingv = repmat(1,1,ebnodbs);
|
||||
fadingv = repmat(0,1,ebnodbs);
|
||||
dfv = repmat(1,1,ebnodbs);
|
||||
dav = repmat(1,1,ebnodbs);
|
||||
Mv = repmat(M,1,ebnodbs);
|
||||
|
||||
|
||||
statv = pararrayfun(floor(nproc()),@tfsk_run_sim,modev,ebnodbrange,timingv,fadingv,dfv,dav,Mv);
|
||||
%statv = arrayfun(@tfsk_run_sim,modev,ebnodbrange,timingv,fadingv,dfv,dav,Mv);
|
||||
|
||||
for ii = (1:length(statv))
|
||||
stat = statv(ii);
|
||||
berc(ii)=stat.berc;
|
||||
bero(ii)=stat.bero;
|
||||
berinc(ii)=stat.thrncoh;
|
||||
beric(ii) = stat.thrcoh;
|
||||
end
|
||||
clf;
|
||||
figure(M)
|
||||
|
||||
semilogy(ebnodbrange, berinc,sprintf('r;%dFSK non-coherent theory;',M))
|
||||
hold on;
|
||||
semilogy(ebnodbrange, beric ,sprintf('g;%dFSK coherent theory;',M))
|
||||
semilogy(ebnodbrange, bero ,sprintf('b;Octave fsk horus %dFSK Demod;',M))
|
||||
semilogy(ebnodbrange, berc,sprintf('+;C fsk horus %dFSK Demod;',M))
|
||||
hold off;
|
||||
grid("minor");
|
||||
axis([min(ebnodbrange) max(ebnodbrange) 1E-5 1])
|
||||
legend("boxoff");
|
||||
xlabel("Eb/No (dB)");
|
||||
ylabel("Bit Error Rate (BER)")
|
||||
|
||||
endfunction
|
||||
|
||||
|
||||
xpass = test_fsk_battery
|
||||
%plot_fsk_bers(2)
|
||||
plot_fsk_bers(4)
|
||||
|
||||
if xpass
|
||||
printf("***** All tests passed! *****\n");
|
||||
else
|
||||
printf("***** Some test failed! Look back through output to find failed test *****\n");
|
||||
end
|
|
@ -1,210 +0,0 @@
|
|||
% vq_binary_switch.m
|
||||
% David Rowe Sep 2021
|
||||
%
|
||||
% Experiments in making VQs robust to bit errors, this is an Octave
|
||||
% implementation of [1].
|
||||
%
|
||||
% [1] Pseudo Gray Coding, Zeger & Gersho 1990
|
||||
|
||||
1;
|
||||
|
||||
% returns indexes of hamming distance 1 neighbours
|
||||
function index_neighbours = distance_one_neighbours(N,k)
|
||||
log2N = log2(N);
|
||||
index_neighbours = [];
|
||||
for b=0:log2N-1
|
||||
index_neighbour = bitxor(k-1,2.^b) + 1;
|
||||
index_neighbours = [index_neighbours index_neighbour];
|
||||
end
|
||||
end
|
||||
|
||||
% equation (33) of [1], for hamming distance 1
|
||||
function c = cost_of_distance_one(vq, prob, k, verbose=0)
|
||||
[N K] = size(vq);
|
||||
log2N = log2(N);
|
||||
c = 0;
|
||||
for b=0:log2N-1
|
||||
index_neighbour = bitxor(k-1,2.^b) + 1;
|
||||
diff = vq(k,:) - vq(index_neighbour, :);
|
||||
dist = sum(diff*diff');
|
||||
c += prob(k)*dist;
|
||||
if verbose
|
||||
printf("k: %d b: %d index_neighbour: %d dist: %f prob: %f c: %f \n", k, b, index_neighbour, dist, prob(k), c);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
% equation (39) of [1]
|
||||
function d = distortion_of_current_mapping(vq, prob, verbose=0)
|
||||
[N K] = size(vq);
|
||||
|
||||
d = 0;
|
||||
for k=1:N
|
||||
c = cost_of_distance_one(vq, prob, k);
|
||||
d += c;
|
||||
if verbose
|
||||
printf("k: %2d c: %f d: %f\n", k, c, d);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
function [vq distortion] = binary_switching(vq, prob, max_iteration, fast_en=1)
|
||||
[N K] = size(vq);
|
||||
iteration = 0;
|
||||
i = 1;
|
||||
finished = 0;
|
||||
switches = 0;
|
||||
distortion0 = distortion_of_current_mapping(vq, prob)
|
||||
|
||||
while !finished
|
||||
|
||||
% generate a list A(i) of which vectors have the largest cost of bit errors
|
||||
c = zeros(1,N);
|
||||
for k=1:N
|
||||
c(k) = cost_of_distance_one(vq, prob, k);
|
||||
end
|
||||
[tmp A] = sort(c,"descend");
|
||||
|
||||
% Try switching each vector with A(i)
|
||||
best_delta = 0;
|
||||
for j=2:N
|
||||
% we can't switch with ourself
|
||||
if j != A(i)
|
||||
if fast_en
|
||||
delta = -cost_of_distance_one(vq, prob, A(i)) - cost_of_distance_one(vq, prob, j);
|
||||
n1 = [distance_one_neighbours(N,A(i)) distance_one_neighbours(N,j)];
|
||||
n1(n1 == A(i)) = [];
|
||||
n1(n1 == j) = [];
|
||||
for l=1:length(n1)
|
||||
delta -= cost_of_distance_one(vq, prob, n1(l));
|
||||
end
|
||||
else
|
||||
distortion1 = distortion_of_current_mapping(vq, prob);
|
||||
end
|
||||
|
||||
% switch vq entries A(i) and j
|
||||
tmp = vq(A(i),:);
|
||||
vq(A(i),:) = vq(j,:);
|
||||
vq(j,:) = tmp;
|
||||
|
||||
if fast_en
|
||||
delta += cost_of_distance_one(vq, prob, A(i)) + cost_of_distance_one(vq, prob, j);
|
||||
for l=1:length(n1)
|
||||
delta += cost_of_distance_one(vq, prob, n1(l));
|
||||
end
|
||||
else
|
||||
distortion2 = distortion_of_current_mapping(vq, prob);
|
||||
delta = distortion2 - distortion1;
|
||||
end
|
||||
|
||||
if delta < 0
|
||||
if abs(delta) > best_delta
|
||||
best_delta = abs(delta);
|
||||
best_j = j;
|
||||
end
|
||||
end
|
||||
|
||||
% unswitch
|
||||
tmp = vq(A(i),:);
|
||||
vq(A(i),:) = vq(j,:);
|
||||
vq(j,:) = tmp;
|
||||
end
|
||||
end % next j
|
||||
|
||||
% printf("best_delta: %f best_j: %d\n", best_delta, best_j);
|
||||
if best_delta == 0
|
||||
% Hmm, no improvement, lets try the next vector in the sorted cost list
|
||||
if i == N
|
||||
finished = 1;
|
||||
else
|
||||
i++;
|
||||
end
|
||||
else
|
||||
% OK keep the switch that minimised the distortion
|
||||
|
||||
tmp = vq(A(i),:);
|
||||
vq(A(i),:) = vq(best_j,:);
|
||||
vq(best_j,:) = tmp;
|
||||
switches++;
|
||||
|
||||
% set up for next iteration
|
||||
iteration++;
|
||||
distortion = distortion_of_current_mapping(vq, prob);
|
||||
printf("it: %3d dist: %f %3.2f i: %3d sw: %3d\n", iteration, distortion,
|
||||
distortion/distortion0, i, switches);
|
||||
if iteration >= max_iteration, finished = 1, end
|
||||
i = 1;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
% return indexes of hamming distance one vectors
|
||||
function ind = neighbour_indexes(vq, k)
|
||||
[N K] = size(vq);
|
||||
log2N = log2(N);
|
||||
ind = [];
|
||||
for b=0:log2N-1
|
||||
index_neighbour = bitxor(k-1,2.^b) + 1;
|
||||
ind = [ind index_neighbour];
|
||||
end
|
||||
endfunction
|
||||
|
||||
function test_binary_switch
|
||||
vq1 = [1 1; -1 1; -1 -1; 1 -1];
|
||||
%f=fopen("vq1.f32","wb"); fwrite(f, vq1, 'float32'); fclose(f);
|
||||
[vq2 distortion] = binary_switching(vq1, ones(1,4), 10);
|
||||
% algorithm should put hamming distance 1 neighbours in adjacent quadrants
|
||||
distance_to_closest_neighbours = 2;
|
||||
% there are two hamming distance 1 neighbours
|
||||
target_distortion = 2^2*distance_to_closest_neighbours*length(vq1);
|
||||
assert(target_distortion == distortion);
|
||||
printf("test_binary_switch OK!\n");
|
||||
endfunction
|
||||
|
||||
function test_fast
|
||||
N=16; % Number of VQ codebook vectors
|
||||
K=2; % Vector length
|
||||
Ntrain=10000;
|
||||
|
||||
training_data = randn(Ntrain,K);
|
||||
[idx vq1] = kmeans(training_data, N);
|
||||
f=fopen("vq1.f32","wb");
|
||||
for r=1:rows(vq1)
|
||||
fwrite(f,vq1(r,:),"float32");
|
||||
end
|
||||
fclose(f);
|
||||
[vq2 distortion] = binary_switching(vq1, [1 ones(1,N-1)], 1000, fast_en = 0);
|
||||
[vq3 distortion] = binary_switching(vq1, [1 ones(1,N-1)], 1000, fast_en = 1);
|
||||
assert(vq2 == vq3);
|
||||
printf("test_fast OK!\n");
|
||||
endfunction
|
||||
|
||||
function demo
|
||||
N=16; % Number of VQ codebook vectors
|
||||
K=2; % Vector length
|
||||
Ntrain=10000;
|
||||
training_data = randn(Ntrain,K);
|
||||
[idx vq1] = kmeans(training_data, N);
|
||||
[vq2 distortion] = binary_switching(vq1, [1 ones(1,N-1)], 1000, 1);
|
||||
|
||||
figure(1); clf; plot(training_data(:,1), training_data(:,2),'+');
|
||||
hold on;
|
||||
plot(vq1(:,1), vq1(:,2),'og','linewidth', 2);
|
||||
plot(vq2(:,1), vq2(:,2),'or','linewidth', 2);
|
||||
|
||||
% plot hamming distance 1 neighbours
|
||||
k = 1;
|
||||
ind = neighbour_indexes(vq2, k);
|
||||
for i=1:length(ind)
|
||||
plot([vq2(k,1) vq2(ind(i),1)],[vq2(k,2) vq2(ind(i),2)],'r-','linewidth', 2);
|
||||
end
|
||||
hold off;
|
||||
endfunction
|
||||
|
||||
pkg load statistics
|
||||
%test_binary_switch;
|
||||
test_fast;
|
||||
%demo
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue