Merge pull request #44 from drowe67/dr-datac14

datac14 - FreeDATA Feature Request 002 for a < 1s, 5 byte signalling mode
dr-freedata-001^2
drowe67 2024-05-06 06:11:39 +09:30 committed by GitHub
commit d21ff74c5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 721 additions and 16 deletions

View File

@ -662,6 +662,13 @@ endif()
cd ${CMAKE_CURRENT_BINARY_DIR}/src;
cat test.raw | ./ofdm_demod --mode datac13 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1")
# DATAC14 Octave Tx, C Rx, burst mode
add_test(NAME test_OFDM_modem_datac14_octave
COMMAND sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/octave;
DISPLAY=\"\" octave-cli -qf --eval 'ofdm_ldpc_tx(\"${CMAKE_CURRENT_BINARY_DIR}/src/test.raw\",\"datac14\",1,3,\"awgn\",\"bursts\",5)';
cd ${CMAKE_CURRENT_BINARY_DIR}/src;
cat test.raw | ./ofdm_demod --mode datac14 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1")
# DATAC4 C Tx, C Rx, burst mode
add_test(NAME test_OFDM_modem_datac4_ldpc_burst
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
@ -676,7 +683,14 @@ endif()
./ch - - --No -17 |
./ofdm_demod --mode datac13 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1")
# -------------------------------------------------------------------------
# DATAC14 C Tx, C Rx, burst mode
add_test(NAME test_OFDM_modem_datac14_ldpc_burst
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
./ofdm_mod --mode datac14 --in /dev/zero --testframes 1 --verbose 1 --ldpc --bursts 10 |
./ch - - --No -17 |
./ofdm_demod --mode datac14 --out /dev/null --testframes --ldpc --verbose 2 --packetsperburst 1")
# -------------------------------------------------------------------------
# LDPC
# -------------------------------------------------------------------------
@ -1064,6 +1078,12 @@ if (NOT APPLE)
./freedv_data_raw_tx --testframes 10 DATAC13 /dev/zero /dev/null")
set_tests_properties(test_memory_leak_FreeDV_DATAC13_tx PROPERTIES PASS_REGULAR_EXPRESSION "ERROR SUMMARY: 0 errors")
add_test(NAME test_memory_leak_FreeDV_DATAC14_tx
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes \
./freedv_data_raw_tx --testframes 10 DATAC14 /dev/zero /dev/null")
set_tests_properties(test_memory_leak_FreeDV_DATAC14_tx PROPERTIES PASS_REGULAR_EXPRESSION "ERROR SUMMARY: 0 errors")
add_test(NAME test_memory_leak_FreeDV_700E_tx
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes \
@ -1307,6 +1327,13 @@ endif(NOT APPLE)
./freedv_data_raw_rx DATAC13 - binaryOut.bin -v;
diff binaryIn.bin binaryOut.bin")
add_test(NAME test_freedv_data_raw_ofdm_datac14_burst_file
COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src;
head -c $((3*10)) </dev/urandom > binaryIn.bin;
./freedv_data_raw_tx DATAC14 binaryIn.bin - --bursts 10 |
./freedv_data_raw_rx DATAC14 - binaryOut.bin -v;
diff binaryIn.bin binaryOut.bin")
# FSK LDPC default 100 bit/s 2FSK, enough noise for several % raw BER to give
# FEC/acquisition a work out, bursts of 1 frame as that stresses acquisition
add_test(NAME test_freedv_data_raw_fsk_ldpc_100
@ -1392,6 +1419,7 @@ endif(NOT APPLE)
test_OFDM_modem_datac3_octave
test_OFDM_modem_datac4_octave
test_OFDM_modem_datac13_octave
test_OFDM_modem_datac14_octave
test_fsk_lib_4fsk_ldpc
test_OFDM_modem_datac0_compression
PROPERTIES

View File

@ -30,7 +30,7 @@ If you have a Feature Request, please answer the questions in the [Feature Reque
Before writing any code or submitting a PR - **please discuss** the PR with developers by raising a GitHub Issue. We have many years of experience and a carefully considered plan for Codec 2 development, and can guide you on work that will most benefit this project.
Some key guidelines about the code in the `codec2` repo:
1. Code that is required to build libcodec2, or to test libcodec2 goes in codec2.
1. Only code that is required to build, test, or document libcodec2 goes in codec2.
2. Experimental work, code used for algorithm development, should probably go into some other repo.
3. Only widely used “production” code goes in codec2. If it has an user base of < 2 (e.g. personal projects, early R&D) - it should probably be application code or a fork.

View File

@ -146,6 +146,7 @@ These modes use an OFDM modem with powerful LDPC codes and are designed for send
| DATAC3 | 500 | 321 | 126 | (2048,1024) | 3.19 | 74/100 at 0dB | Forward link data (low SNR) |
| DATAC4 | 250 | 87 | 56 | (1472,448) | 5.17 | 90/100 at -4dB | Forward link data (low SNR) |
| DATAC13 | 200 | 64 | 14 | (384,128) | 2.0 | 90/100 at -4dB | Reverse link ACK packets (low SNR) |
| DATAC14 | 250 | 58 | 3 | (112,56) | 0.69 | 90/100 at -2dB | Reverse link ACK packets (low SNR) |
Notes:
1. 16 bits (2 bytes) per frame are reserved for a 16 bit CRC, e.g. for `datac3` we have 128 byte frames, and 128-2=126 bytes/frame of payload data.
@ -245,7 +246,7 @@ This command line demonstrates the effect:
```
Try adjusting `--clip` and `No` argument of `ch` (noise level) for different modes. Note the SNR estimates returned from `freedv_data_raw_rx` compared to the SNR from the channel simulator `ch`. You will notice clipping also increases the RMS power and reduces the PER for a given channel noise power. CPAPR will also reduce with clipping enabled.
The following plots illustrate the SNR estimates versus actual channel SNR with and without compression (clipping). Not that even with the uncompressed waveform there is a small offset of around 1dB, possibly due to modem implementation loss or noise in the frequency, phase, or timing estimators.
The following plots illustrate the SNR estimates versus actual channel SNR with and without compression (clipping). Note that even with the uncompressed waveform there is a small offset of around 1dB, possibly due to modem implementation loss or noise in the frequency, phase, or timing estimators.
![](doc/snrest_snr_ctx.png)
![](doc/snrest_snr_ctxc.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

View File

@ -44,7 +44,8 @@ function print_config(states)
printf("Nc=%d Ts=%4.3f Tcp=%4.3f Ns: %d Np: %d\n", Nc, 1/Rs, Tcp, Ns, Np);
printf("Nsymperframe: %d Nbitsperpacket: %d Nsamperframe: %d Ntxtbits: %d Nuwbits: %d Nuwframes: %d\n",
Ns*Nc, Nbitsperpacket, Nsamperframe, Ntxtbits, Nuwbits, Nuwframes);
printf("uncoded bits/s: %4.1f\n", Nbitsperpacket*Fs/(Np*Nsamperframe));
printf("uncoded bits/s: %4.1f Duration (incl post/preamble): %4.2f s\n",
Nbitsperpacket*Fs/(Np*Nsamperframe), (Np+2)*Ns*(Tcp+1/Rs));
end
%-----------------------------------------------------------------------

View File

@ -1254,7 +1254,7 @@ endfunction
% with acquisition
function [rx delay_samples] = ofdm_rx_filter(states, mode, rx)
delay_samples = 0;
if strcmp(mode,"datac4") || strcmp(mode,"datac13")
if strcmp(mode,"datac4") || strcmp(mode,"datac13") || strcmp(mode,"datac14")
w_centre = mean(states.w); centre_norm = w_centre/(2*pi);
n_coeffs = 100;
cutoff_Hz = 400; cutoff_norm = cutoff_Hz/states.Fs;

View File

@ -134,6 +134,18 @@ function config = ofdm_init_mode(mode="700D")
config.state_machine = "data";
config.amp_scale = 2.5*300E3; config.clip_gain1 = 1.2; config.clip_gain2 = 1.0;
config.txbpf_width_Hz = 400;
elseif strcmp(mode,"datac14")
Ns=5; config.Np=4; Tcp = 0.005; Ts = 0.018; Nc = 4; config.data_mode = "streaming";
config.edge_pilots = 0;
config.Ntxtbits = 0; config.Nuwbits = 32; config.bad_uw_errors = 12;
config.ftwindow_width = 80; config.timing_mx_thresh = 0.45;
config.tx_uw = zeros(1,config.Nuwbits);
config.tx_uw(1:24) = [1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0];
config.tx_uw(end-24+1:end) = [1 1 0 0 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0];
config.amp_est_mode = 1; config.EsNodB = 3;
config.state_machine = "data";
config.amp_scale = 2*300E3; config.clip_gain1 = 2; config.clip_gain2 = 1;
config.txbpf_width_Hz = 400;
elseif strcmp(mode,"1")
Ns=5; config.Np=10; Tcp=0; Tframe = 0.1; Ts = Tframe/Ns; Nc = 1;
else
@ -231,9 +243,17 @@ function [code_param Nbitspercodecframe Ncodecframespermodemframe] = codec_to_fr
code_param.coded_bits_per_frame = code_param.data_bits_per_frame + code_param.ldpc_parity_bits_per_frame;
code_param.coded_syms_per_frame = code_param.coded_bits_per_frame/code_param.bits_per_symbol;
end
if strcmp(mode, "datac14")
load HRA_56_56.txt
code_param = ldpc_init_user(HRA_56_56, modulation, mod_order, mapping);
code_param.data_bits_per_frame = 40;
code_param.coded_bits_per_frame = code_param.data_bits_per_frame + code_param.ldpc_parity_bits_per_frame;
code_param.coded_syms_per_frame = code_param.coded_bits_per_frame/code_param.bits_per_symbol;
end
if strcmp(mode, "datac0") || strcmp(mode, "datac1") || strcmp(mode, "datac3") ...
|| strcmp(mode, "datac4") || strcmp(mode, "qam16c1") ...
|| strcmp(mode, "qam16c2") || strcmp(mode, "datac5") || strcmp(mode, "datac13")
|| strcmp(mode, "qam16c2") || strcmp(mode, "datac5") || strcmp(mode, "datac13") ...
|| strcmp(mode, "datac14")
printf("ldpc_data_bits_per_frame = %d\n", code_param.ldpc_data_bits_per_frame);
printf("ldpc_coded_bits_per_frame = %d\n", code_param.ldpc_coded_bits_per_frame);
printf("ldpc_parity_bits_per_frame = %d\n", code_param.ldpc_parity_bits_per_frame);

View File

@ -83,7 +83,9 @@ function ofdm_rx(filename, mode="700D", varargin)
Nerrs = 0; rx_uw = zeros(1,states.Nuwbits);
% main loop ----------------------------------------------------------------
rx = ofdm_rx_filter(states, mode, rx);
f = 1;
while(prx < Nsam)

View File

@ -0,0 +1,267 @@
% snr_curves_plot.m
%
% Companion script for unittest/raw_data_curves
1;
function state_vec = set_graphics_state_print()
textfontsize = get(0,"defaulttextfontsize");
linewidth = get(0,"defaultlinelinewidth");
markersize = get(0, "defaultlinemarkersize");
set(0, "defaulttextfontsize", 16);
set(0, "defaultaxesfontsize", 16);
set(0, "defaultlinelinewidth", 1);
state_vec = [textfontsize linewidth markersize];
endfunction
function set_graphics_state_screen(state_vec)
textfontsize = state_vec(1);
linewidth = state_vec(2);
markersize = state_vec(3);
set(0, "defaulttextfontsize", textfontsize);
set(0, "defaultaxesfontsize", textfontsize);
set(0, "defaultlinelinewidth", linewidth);
set(0, "defaultlinemarkersize", markersize);
endfunction
function [snr_ch per] = snr_scatter(source, mode, channel, colour)
suffix = sprintf("_%s_%s_%s",source, mode, channel);
snr = load(sprintf("snr%s.txt",suffix));
offset = load(sprintf("offset%s.txt",suffix));
snr -= offset;
snr_x = []; snrest_y = [];
for i=1:length(snr)
fn = sprintf('snrest%s_%d.txt',suffix,i);
if exist(fn,'file') == 2
snrest=load(fn);
if i == length(snr)
plot(snr(i)*ones(1,length(snrest)), snrest, sprintf('%s;%s %s;',colour,source,mode));
else
plot(snr(i)*ones(1,length(snrest)), snrest, sprintf('%s',colour));
end
snr_x = [snr_x snr(i)]; snrest_y = [snrest_y mean(snrest)];
end
end
plot(snr_x, snrest_y, sprintf('%s', colour));
endfunction
function [snr_ch per] = per_snr(mode, colour)
snrch = load(sprintf("snrch_%s.txt",mode));
snroffset = load(sprintf("snroffset_%s.txt",mode));
snrch -= snroffset;
per = load(sprintf("per_%s.txt",mode));
plot(snrch, per, sprintf('%so-;%s;', colour, mode));
endfunction
function snrest_snr_screen(source, channel)
clf; hold on;
snr_scatter(source, 'datac0', channel,'b+-')
snr_scatter(source, 'datac1', channel,'g+-')
snr_scatter(source, 'datac3', channel,'r+-')
snr_scatter(source, 'datac4', channel,'c+-')
snr_scatter(source, 'datac13', channel,'m+-')
xlabel('SNR (dB)'); ylabel('SNRest (dB)'); grid('minor');
axis([-12 12 -12 12]);
a = axis;
plot([a(1) a(2)],[a(1) a(2)],'bk-');
hold off; grid;
if strcmp(source,'ctx')
title(sprintf('SNR estimate versus SNR (%s) (no compression)', channel));
else
title(sprintf('SNR estimate versus SNR (%s) (with compression)', channel));
end
legend('location','northwest');
endfunction
function snrest_snr_print(source, channel)
state_vec = set_graphics_state_print();
snrest_snr_screen(source, channel);
print(sprintf("snrest_snr_%s.png", source), "-dpng", "-S1000,800");
set_graphics_state_screen(state_vec);
endfunction
function ber_per_v_snr(source, mode, channel, colour)
suffix = sprintf("_%s_%s_%s.txt",source, mode, channel);
snr = load(sprintf("snr%s",suffix));
offset = load(sprintf("offset%s",suffix));
snr -= offset;
ber = load(sprintf("ber%s",suffix)) + 1E-6;
per = load(sprintf("per%s",suffix)) + 1E-6;
semilogy(snr, ber, sprintf('%s;%s %s ber;', colour, source, mode));
semilogy(snr, per, sprintf('%s;%s %s per;', colour, source, mode),'linewidth',3,'markersize',10);
endfunction
function per_v_snr(source, mode, channel, colour)
suffix = sprintf("_%s_%s_%s.txt",source, mode, channel);
snr = load(sprintf("snr%s",suffix));
offset = load(sprintf("offset%s",suffix));
snr -= offset;
per = load(sprintf("per%s",suffix)) + 1E-6;
if strcmp(channel,"awgn")
semilogy(snr, per, sprintf('%s;%s %s;', colour, mode, channel));
else
semilogy(snr, per, sprintf('%s;%s %s;', colour, mode, channel),'linewidth',3,'markersize',10);
end
endfunction
function thruput_v_snr(source, mode, channel, colour)
suffix = sprintf("_%s_%s_%s.txt",source, mode, channel);
snr = load(sprintf("snr%s",suffix));
offset = load(sprintf("offset%s",suffix));
snr -= offset;
per = load(sprintf("per%s",suffix)) + 1E-6;
if strcmp(mode,"datac0") Rb=291; end;
if strcmp(mode,"datac1") Rb=980; end;
if strcmp(mode,"datac3") Rb=321; end;
if strcmp(mode,"datac4") Rb=87; end;
if strcmp(mode,"datac13") Rb=65; end;
if strcmp(mode,"datac14") Rb=58; end;
if strcmp(channel,"awgn")
plot(snr, Rb*(1-per), sprintf('%s;%s %s;', colour, mode, channel));
else
plot(snr, Rb*(1-per), sprintf('%s;%s %s;', colour, mode, channel),'linewidth',3,'markersize',10);
end
endfunction
function octave_ch_noise_screen(channel)
clf; hold on;
ber_per_v_snr('oct','datac0',channel,'bo-')
ber_per_v_snr('ch' ,'datac0',channel,'bx-')
ber_per_v_snr('oct','datac1',channel,'go-')
ber_per_v_snr('ch' ,'datac1',channel,'gx-')
ber_per_v_snr('oct','datac3',channel,'ro-')
ber_per_v_snr('ch' ,'datac3',channel,'rx-')
xlabel('SNR (dB)'); grid;
hold off;
if strcmp(channel,"awgn")
axis([-6 8 1E-3 1]);
else
axis([-2 12 1E-3 1]);
end
title(sprintf('Comparsion of Measuring SNR from Octave and ch tool (%s)', channel));
endfunction
function octave_ch_noise_print(channel)
state_vec = set_graphics_state_print();
octave_ch_noise_screen(channel);
print(sprintf("octave_ch_noise_%s.png", channel), "-dpng","-S1000,800");
set_graphics_state_screen(state_vec);
endfunction
function octave_c_tx_screen(channel)
clf; hold on;
ber_per_v_snr('oct','datac0',channel,'bo-')
ber_per_v_snr('ctx','datac0',channel,'bx-')
ber_per_v_snr('oct','datac1',channel,'go-')
ber_per_v_snr('ctx','datac1',channel,'gx-')
ber_per_v_snr('oct','datac3',channel,'ro-')
ber_per_v_snr('ctx','datac3',channel,'rx-')
xlabel('SNR (dB)'); grid;
hold off;
if strcmp(channel,"awgn")
axis([-6 8 1E-3 1]);
else
axis([-2 12 1E-3 1]);
end
title(sprintf('Comparsion of Octave Tx and C Tx (no compression) (%s)', channel));
endfunction
function octave_c_tx_print(channel)
state_vec = set_graphics_state_print();
octave_c_tx_screen(channel);
print(sprintf("octave_c_tx_%s.png", channel), "-dpng","-S1000,800");
set_graphics_state_screen(state_vec);
endfunction
function octave_c_tx_comp_screen(channel)
clf; hold on;
ber_per_v_snr('oct','datac0',channel,'bo-')
ber_per_v_snr('ctxc','datac0',channel,'bx-')
ber_per_v_snr('oct','datac1',channel,'go-')
ber_per_v_snr('ctxc','datac1',channel,'gx-')
ber_per_v_snr('oct','datac3',channel,'ro-')
ber_per_v_snr('ctxc','datac3',channel,'rx-')
xlabel('SNR (dB)'); grid;
hold off;
if strcmp(channel,"awgn")
axis([-6 8 1E-3 1]);
else
axis([-2 12 1E-3 1]);
end
title(sprintf('Comparsion of Octave Tx and C Tx (with compression) (%s)', channel));
endfunction
function octave_c_tx_comp_print(channel)
state_vec = set_graphics_state_print();
octave_c_tx_comp_screen(channel);
print(sprintf("octave_c_tx_comp_%s.png", channel), "-dpng","-S1000,800");
set_graphics_state_screen(state_vec);
endfunction
% composite AWGN and MPP for compressed
function c_tx_comp_screen
clf; hold on;
per_v_snr('ctxc','datac0','awgn','bo-')
per_v_snr('ctxc','datac1','awgn','go-')
per_v_snr('ctxc','datac3','awgn','ro-')
per_v_snr('ctxc','datac4','awgn','co-')
per_v_snr('ctxc','datac13','awgn','mo-')
per_v_snr('ctxc','datac14','awgn','ko-')
per_v_snr('ctxc','datac0','mpp','bx-')
per_v_snr('ctxc','datac1','mpp','gx-')
per_v_snr('ctxc','datac3','mpp','rx-')
per_v_snr('ctxc','datac4','mpp','cx-')
per_v_snr('ctxc','datac13','mpp','mx-')
per_v_snr('ctxc','datac14','mpp','kx-')
xlabel('SNR (dB)'); ylabel('PER'); grid;
hold off;
axis([-10 14 1E-3 1]);
title('PER of C Raw Data Modes (with compression)');
endfunction
function c_tx_comp_print;
state_vec = set_graphics_state_print();
c_tx_comp_screen;
print("c_tx_comp.png", "-dpng","-S1000,800");
set_graphics_state_screen(state_vec);
endfunction
function c_tx_comp_thruput_screen
clf; hold on;
thruput_v_snr('ctxc','datac0','awgn','bo-')
thruput_v_snr('ctxc','datac1','awgn','go-')
thruput_v_snr('ctxc','datac3','awgn','ro-')
thruput_v_snr('ctxc','datac4','awgn','co-')
thruput_v_snr('ctxc','datac13','awgn','mo-')
thruput_v_snr('ctxc','datac14','awgn','ko-')
thruput_v_snr('ctxc','datac0','mpp','bx-')
thruput_v_snr('ctxc','datac1','mpp','gx-')
thruput_v_snr('ctxc','datac3','mpp','rx-')
thruput_v_snr('ctxc','datac4','mpp','cx-')
thruput_v_snr('ctxc','datac13','mpp','mx-')
thruput_v_snr('ctxc','datac14','mpp','kx-')
xlabel('SNR (dB)'); ylabel('bits/s'); grid;
hold off;
axis([-10 10 0 1000]);
title(' Throughput for C Tx (with compression)');
legend('location','west');
endfunction
function c_tx_comp_thruput_print;
state_vec = set_graphics_state_print;
c_tx_comp_thruput_screen;
print("c_tx_comp_thruput.png", "-dpng","-S1000,800");
set_graphics_state_screen(state_vec);
endfunction
#{
figure(1); octave_ch_noise_screen;
figure(2); octave_c_tx_screen;
figure(3); octave_c_tx_comp_screen
figure(4); snrest_snr_screen;
figure(5); octave_ch_noise_print;
figure(6); octave_c_tx_print;
figure(7); octave_c_tx_comp_print;
figure(8); snrest_snr_print;
#}

View File

@ -256,7 +256,7 @@ int main(int argc, char *argv[]) {
stderr,
"\nAdjust path --fading_dir or use GNU Octave to generate:\n\n");
gen_fading_file:
fprintf(stderr, "$ octave --no-gui\n");
fprintf(stderr, "$ octave-cli\n");
fprintf(stderr, "octave:24> pkg load signal\n");
fprintf(stderr, "octave:24> time_secs=60\n");
fprintf(stderr,

View File

@ -197,6 +197,7 @@ void freedv_ofdm_data_open(struct freedv *f) {
if (f->mode == FREEDV_MODE_DATAC3) strcpy(mode, "datac3");
if (f->mode == FREEDV_MODE_DATAC4) strcpy(mode, "datac4");
if (f->mode == FREEDV_MODE_DATAC13) strcpy(mode, "datac13");
if (f->mode == FREEDV_MODE_DATAC14) strcpy(mode, "datac14");
ofdm_init_mode(mode, &ofdm_config);
f->ofdm = ofdm_create(&ofdm_config);

View File

@ -125,7 +125,8 @@ struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) {
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode)) == false)
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) == false)
return NULL;
/* set everything to zero just in case */
@ -154,6 +155,7 @@ struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) {
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode)) freedv_ofdm_data_open(f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) freedv_ofdm_data_open(f);
varicode_decode_init(&f->varicode_dec_states, 1);
@ -235,7 +237,8 @@ void freedv_close(struct freedv *freedv) {
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, freedv->mode)) {
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, freedv->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, freedv->mode)) {
FREE(freedv->rx_syms);
FREE(freedv->rx_amps);
FREE(freedv->ldpc);
@ -266,7 +269,8 @@ static int is_ofdm_mode(struct freedv *f) {
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode);
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode);
}
static int is_ofdm_data_mode(struct freedv *f) {
@ -274,7 +278,8 @@ static int is_ofdm_data_mode(struct freedv *f) {
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode);
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode);
}
/*---------------------------------------------------------------------------*\
@ -464,7 +469,8 @@ void freedv_rawdatacomptx(struct freedv *f, COMP mod_out[],
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode))
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode))
freedv_comptx_ofdm(f, mod_out);
if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) {
@ -1063,7 +1069,8 @@ int freedv_rawdatacomprx(struct freedv *f, unsigned char *packed_payload_bits,
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode))
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) ||
FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode))
rx_status = freedv_comp_short_rx_ofdm(f, (void *)demod_in, 0, 1.0f);
if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) {
rx_status = freedv_rx_fsk_ldpc_data(f, demod_in);

View File

@ -61,6 +61,7 @@ extern "C" {
#define FREEDV_MODE_DATAC0 14
#define FREEDV_MODE_DATAC4 18
#define FREEDV_MODE_DATAC13 19
#define FREEDV_MODE_DATAC14 20
// Sample rates used
#define FREEDV_FS_8000 8000
@ -140,6 +141,9 @@ extern "C" {
#if !defined(FREEDV_MODE_DATAC13_EN)
#define FREEDV_MODE_DATAC13_EN FREEDV_MODE_EN_DEFAULT
#endif
#if !defined(FREEDV_MODE_DATAC14_EN)
#define FREEDV_MODE_DATAC14_EN FREEDV_MODE_EN_DEFAULT
#endif
#define FDV_MODE_ACTIVE(mode_name, var) \
((mode_name##_EN) == 0 ? 0 : (var) == mode_name)

View File

@ -214,6 +214,8 @@ int main(int argc, char *argv[]) {
mode = FREEDV_MODE_DATAC4;
if (!strcmp(argv[dx], "DATAC13") || !strcmp(argv[dx], "datac13"))
mode = FREEDV_MODE_DATAC13;
if (!strcmp(argv[dx], "DATAC14") || !strcmp(argv[dx], "datac14"))
mode = FREEDV_MODE_DATAC14;
if (mode == -1) {
fprintf(stderr, "Error in mode: %s\n", argv[dx]);
exit(1);

View File

@ -236,6 +236,8 @@ int main(int argc, char *argv[]) {
mode = FREEDV_MODE_DATAC4;
if (!strcmp(argv[dx], "DATAC13") || !strcmp(argv[dx], "datac13"))
mode = FREEDV_MODE_DATAC13;
if (!strcmp(argv[dx], "DATAC14") || !strcmp(argv[dx], "datac14"))
mode = FREEDV_MODE_DATAC14;
if (mode == -1) {
fprintf(stderr, "Error: in mode: %s", argv[dx]);
exit(1);

View File

@ -44,6 +44,7 @@
*/
static const int b_table[] = {
48, 31, /* datac14: HRA_56_56, 40 data bits used */
56, 37, /* 700E: HRA_56_56 */
106, 67, /* 2020B: (112,56) partial protection */
112, 71, /* 700D: HRA_112_112 */

View File

@ -78,6 +78,7 @@ void ldpc_mode_specific_setup(struct OFDM *ofdm, struct LDPC *ldpc) {
}
if (!strcmp(ofdm->mode, "datac4")) set_data_bits_per_frame(ldpc, 448);
if (!strcmp(ofdm->mode, "datac13")) set_data_bits_per_frame(ldpc, 128);
if (!strcmp(ofdm->mode, "datac14")) set_data_bits_per_frame(ldpc, 40);
}
/* LDPC encode frame - generate parity bits and a codeword, applying the

View File

@ -559,7 +559,8 @@ static void allocate_tx_bpf(struct OFDM *ofdm) {
quisk_filt_cfInit(ofdm->tx_bpf, filtP400S600,
sizeof(filtP400S600) / sizeof(float));
quisk_cfTune(ofdm->tx_bpf, ofdm->tx_centre / ofdm->fs);
} else if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13")) {
} else if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13") ||
!strcmp(ofdm->mode, "datac14")) {
quisk_filt_cfInit(ofdm->tx_bpf, filtP200S400,
sizeof(filtP200S400) / sizeof(float));
// centre the filter on the mean carrier freq, allows a narrower filter to
@ -590,7 +591,8 @@ static void allocate_rx_bpf(struct OFDM *ofdm) {
/* Receive bandpass filter; complex coefficients, center frequency */
if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13")) {
if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13") ||
!strcmp(ofdm->mode, "datac14")) {
quisk_filt_cfInit(ofdm->rx_bpf, filtP200S400,
sizeof(filtP200S400) / sizeof(float));
// centre the filter on the mean carrier freq, allows a narrower filter to

View File

@ -226,6 +226,31 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) {
config->clip_gain1 = 1.2;
config->clip_gain2 = 1.0;
config->rx_bpf_en = true;
} else if (strcmp(mode, "datac14") == 0) {
config->ns = 5;
config->np = 4;
config->tcp = 0.005;
config->ts = 0.018;
config->nc = 4;
config->edge_pilots = 0;
config->txtbits = 0;
config->state_machine = "data";
config->ftwindowwidth = 80;
config->timing_mx_thresh = 0.45;
config->codename = "HRA_56_56";
config->amp_est_mode = 1;
config->nuwbits = 32;
config->bad_uw_errors = 12;
uint8_t uw[] = {1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0};
assert(sizeof(uw) <= MAX_UW_BITS);
memcpy(config->tx_uw, uw, sizeof(uw));
memcpy(&config->tx_uw[config->nuwbits - sizeof(uw)], uw, sizeof(uw));
config->data_mode = "streaming";
config->amp_scale = 2.0 * 300E3;
config->clip_gain1 = 2.0;
config->clip_gain2 = 1.0;
config->rx_bpf_en = true;
} else {
assert(0);
}

View File

@ -51,6 +51,7 @@ if [ "$1" == "LPCNet" ]; then
data_test "datac3"
data_test "datac4"
data_test "datac13"
data_test "datac14"
fi
exit 0

View File

@ -0,0 +1,149 @@
# Makefile
# Dec 2022
#
# Automates PER/BER curve generation for raw data mode:
#
# 1. Compare "ch" noise injection/SNR measurement against reference Octave Tx
# 2. Compare C Tx against Octave Tx (with and without compression)
# 3. Plot curves for SNR estimated from C Rx against actual SNR
# 4. Plot AWGN/PER C Tx curves for end user documentation
#
# usage:
#
# 1. Create 20 minutes of MPP fading samples:
# $ cd codec2/octave/
# $ octave-cli
# octave:24> pkg load signal
# octave:24> time_secs=60*20
# octave:26> ch_fading("~/codec2/build_linux/unittest/fast_fading_samples.float", 8000, 1.0, 8000*time_secs)
#
# 2. Run scripts:
#
# $ make
SHELL := /bin/bash
CODEC2 := $(HOME)/codec2
all: test \
octave_ch_noise_awgn.png octave_c_tx_awgn.png octave_c_tx_comp_awgn.png \
octave_ch_noise_mpp.png octave_c_tx_mpp.png octave_c_tx_comp_mpp.png \
snrest_snr_ctx.png snrest_snr_ctxc.png \
c_tx_comp.png c_tx_comp_thruput.png
clean:
rm -f *.txt *.png *.raw
# run this first, traps common CML setup error
test:
source snr_curves.sh; test_ldpc
# subset of files generated, but enough to set up Makefile dependencies
snr_oct = snr_oct_datac0_awgn.txt snr_oct_datac1_awgn.txt snr_oct_datac3_awgn.txt
snr_ch = snr_ch_datac0_awgn.txt snr_ch_datac1_awgn.txt snr_ch_datac3_awgn.txt
snr_ctx = snr_ctx_datac0_awgn.txt snr_ctx_datac1_awgn.txt snr_ctx_datac3_awgn.txt
snr_ctxc = snr_ctxc_datac0_awgn.txt snr_ctxc_datac3_awgn.txt
snr_oct_mpp = snr_oct_datac0_mpp.txt snr_oct_datac1_mpp.txt snr_oct_datac3_mpp.txt
snr_ch_mpp = snr_ch_datac0_mpp.txt snr_ch_datac1_mpp.txt snr_ch_datac3_mpp.txt
snr_ctx_mpp = snr_ctx_datac0_mpp.txt snr_ctx_datac1_mpp.txt snr_ctx_datac3_mpp.txt
snr_ctxc_mpp = snr_ctxc_datac0_mpp.txt snr_ctxc_datac3_mpp.txt
$(snr_oct):
source snr_curves.sh; generate_octave_tx_data datac0 awgn
source snr_curves.sh; generate_octave_tx_data datac1 awgn
source snr_curves.sh; generate_octave_tx_data datac3 awgn
$(snr_oct_mpp):
source snr_curves.sh; generate_octave_tx_data datac0 mpp
source snr_curves.sh; generate_octave_tx_data datac1 mpp
source snr_curves.sh; generate_octave_tx_data datac3 mpp
$(snr_ch):
source snr_curves.sh; generate_ch_data datac0 awgn
source snr_curves.sh; generate_ch_data datac1 awgn
source snr_curves.sh; generate_ch_data datac3 awgn
$(snr_ch_mpp):
source snr_curves.sh; generate_ch_data datac0 mpp
source snr_curves.sh; generate_ch_data datac1 mpp
source snr_curves.sh; generate_ch_data datac3 mpp
# C without compression
$(snr_ctx):
source snr_curves.sh; generate_snrest_v_snr_data datac0 awgn
source snr_curves.sh; generate_snrest_v_snr_data datac1 awgn
source snr_curves.sh; generate_snrest_v_snr_data datac3 awgn
source snr_curves.sh; generate_snrest_v_snr_data datac4 awgn
source snr_curves.sh; generate_snrest_v_snr_data datac13 awgn
source snr_curves.sh; generate_snrest_v_snr_data datac14 awgn
$(snr_ctx_mpp):
source snr_curves.sh; generate_snrest_v_snr_data datac0 mpp
source snr_curves.sh; generate_snrest_v_snr_data datac1 mpp
source snr_curves.sh; generate_snrest_v_snr_data datac3 mpp
source snr_curves.sh; generate_snrest_v_snr_data datac4 mpp
source snr_curves.sh; generate_snrest_v_snr_data datac13 mpp
source snr_curves.sh; generate_snrest_v_snr_data datac14 mpp
# C with compression
$(snr_ctxc):
source snr_curves.sh; generate_snrest_v_snr_data datac0 awgn 1
source snr_curves.sh; generate_snrest_v_snr_data datac1 awgn 1
source snr_curves.sh; generate_snrest_v_snr_data datac3 awgn 1
source snr_curves.sh; generate_snrest_v_snr_data datac4 awgn 1
source snr_curves.sh; generate_snrest_v_snr_data datac13 awgn 1
source snr_curves.sh; generate_snrest_v_snr_data datac14 awgn 1
$(snr_ctxc_mpp):
source snr_curves.sh; generate_snrest_v_snr_data datac0 mpp 1
source snr_curves.sh; generate_snrest_v_snr_data datac1 mpp 1
source snr_curves.sh; generate_snrest_v_snr_data datac3 mpp 1
source snr_curves.sh; generate_snrest_v_snr_data datac4 mpp 1
source snr_curves.sh; generate_snrest_v_snr_data datac13 mpp 1
source snr_curves.sh; generate_snrest_v_snr_data datac14 mpp 1
# Octave and C curves should be on top of each other, indicating Octave
# and ch noise injection/SNR measurement are equivalent
octave_ch_noise_awgn.png: $(snr_oct) $(snr_ch)
echo "snr_curves_plot; octave_ch_noise_print('awgn'); quit" | \
octave-cli -p $(CODEC2)/octave
octave_ch_noise_mpp.png: $(snr_oct_mpp) $(snr_ch_mpp)
echo "snr_curves_plot; octave_ch_noise_print('mpp'); quit" | \
octave-cli -p $(CODEC2)/octave
# Octave Tx and C Tx curves should be on top of each other
octave_c_tx_awgn.png: $(snr_oct) $(snr_ctx)
echo "snr_curves_plot; octave_c_tx_print('awgn'); quit" | \
octave-cli -p $(CODEC2)/octave
octave_c_tx_mpp.png: $(snr_oct_mpp) $(snr_ctx_mpp)
echo "snr_curves_plot; octave_c_tx_print('mpp'); quit" | \
octave-cli -p $(CODEC2)/octave
# Octave Tx and C Tx (compressed) curves should be close, but C may be 1dB
# poorer
octave_c_tx_comp_awgn.png: $(snr_oc) $(snr_ctxc)
echo "snr_curves_plot; octave_c_tx_comp_print('awgn'); quit" | \
octave-cli -p $(CODEC2)/octave
octave_c_tx_comp_mpp.png: $(snr_oct_mpp) $(snr_ctxc_mpp)
echo "snr_curves_plot; octave_c_tx_comp_print('mpp'); quit" | \
octave-cli -p $(CODEC2)/octave
# combined AWGN and MPP from C Tx (compressed) - what end users would run
c_tx_comp.png: $(snr_ctxc) $(snr_ctxc_mpp)
echo "snr_curves_plot; c_tx_comp_print; quit" | \
octave-cli -p $(CODEC2)/octave
# Curves of SNR estimates from C Rx compared to actual SNR, useful for "gear shifting"
snrest_snr_ctx.png: $(snr_ctx)
echo "snr_curves_plot; snrest_snr_print('ctx', 'awgn'); quit" | \
octave-cli -p $(CODEC2)/octave
snrest_snr_ctxc.png: $(snr_ctxc)
echo "snr_curves_plot; snrest_snr_print('ctxc', 'awgn'); quit" | \
octave-cli -p $(CODEC2)/octave
# Throughput of payload data in bits/s of modes against SNR
c_tx_comp_thruput.png: $(snr_ctxc) $(snr_ctxc_mpp)
echo "snr_curves_plot; c_tx_comp_thruput_print; quit" | \
octave-cli -p $(CODEC2)/octave

View File

@ -0,0 +1,191 @@
# snr_curves.sh
#
# Library of bash functions to generate data for SNR curves.
#
# testing a function example:
# $ bash -c "source ./snr_curves.sh; generate_octave_tx_data datac0 awgn"
set -x
PATH=${PATH}:${HOME}/codec2/build_linux/src
CODEC2=${HOME}/codec2
FADING_DIR=${CODEC2}/build_linux/unittest
snr_list='-5 -4 -3 -2 0 1 2 4'
No_list='-13 -14 -15 -16 -18 -20 -22 -24 -26'
Nbursts_awgn=20
Nbursts_mpp=100
# Octave Tx injects noise and is source of truth for SNR, measure BER/PER v SNR
function generate_octave_tx_data {
mode=$1
channel=$2
Nbursts=$Nbursts_awgn
snr_nudge=0
if [ "$channel" == "mpp" ]; then
Nbursts=$Nbursts_mpp
snr_nudge=4
fi
rx_log=$(mktemp)
i=1
rm -f snr_oct_${mode}_${channel}*.txt
rm -f ber_oct_${mode}_${channel}*.txt
rm -f per_oct_${mode}_${channel}*.txt
for snr in $snr_list
do
snr_adj=$((${snr}+${snr_nudge}))
echo "warning ('off', 'Octave:data-file-in-path');
ofdm_ldpc_tx('test_${mode}.raw','${mode}',1,${snr_adj},'${channel}','bursts',${Nbursts},'crc');
quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave
freedv_data_raw_rx --testframes $mode test_${mode}.raw /dev/null 2>${rx_log} -v
BERmeas=$(cat ${rx_log} | grep 'BER......:' | cut -d' ' -f2)
PERmeas=$(cat ${rx_log} | grep 'Coded FER' | cut -d' ' -f3)
echo ${snr_adj} >> snr_oct_${mode}_${channel}.txt
echo ${BERmeas} >> ber_oct_${mode}_${channel}.txt
echo ${PERmeas} >> per_oct_${mode}_${channel}.txt
i=$((i+1))
done
echo 0 > offset_oct_${mode}_${channel}.txt
}
# ch injects noise and is source of truth for SNR, measure BER/PER v SNR
# Octave Tx
function generate_ch_data {
mode=$1
channel=$2
ch_multipath=''
Nbursts=$Nbursts_awgn
snr_nudge=0
if [ "$channel" == "mpp" ]; then
ch_multipath='--mpp'
Nbursts=$Nbursts_mpp
snr_nudge=4
fi
octave_log=$(mktemp)
ch_log=$(mktemp)
rx_log=$(mktemp)
i=1
rm -f snr_ch_${mode}_${channel}*.txt
rm -f ber_ch_${mode}_${channel}*.txt
rm -f per_ch_${mode}_${channel}*.txt
for No in $No_list
do
No_adj=$((${No}-${snr_nudge}))
echo "warning ('off', 'Octave:data-file-in-path');
ofdm_ldpc_tx('test_${mode}.raw','${mode}',1,100,'awgn','bursts',${Nbursts},'crc');
quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave 1>${octave_log}
SNRoffset=$(cat ${octave_log} | grep 'Burst offset:' | cut -d' ' -f5)
ch test_${mode}.raw - --No $No_adj ${ch_multipath} --fading_dir ${FADING_DIR} 2>>${ch_log} | \
freedv_data_raw_rx --testframes $mode - /dev/null -v 2>${rx_log}
BERmeas=$(cat ${rx_log} | grep 'BER......:' | cut -d' ' -f2)
PERmeas=$(cat ${rx_log} | grep 'Coded FER' | cut -d' ' -f3)
echo ${BERmeas} >> ber_ch_${mode}_${channel}.txt
echo ${PERmeas} >> per_ch_${mode}_${channel}.txt
i=$((i+1))
# trap not enough fading file samples (with mpp)
grep "Fading file finished" ${ch_log}
if [ $? -eq 0 ]; then
cat ${ch_log}
exit 1
fi
done
echo ${SNRoffset} > offset_ch_${mode}_${channel}.txt
SNRch=$(cat ${ch_log} | grep SNR3k | tr -s ' ' | cut -d' ' -f3)
echo ${SNRch} > snr_ch_${mode}_${channel}.txt
}
# ch injects noise and is source of truth for SNR, measure BER/PER v SNR and
# SNR estimates v SNR from rx, C Tx
function generate_snrest_v_snr_data {
mode=$1
channel=$2
snr_nudge=0
aNo_list=$No_list
# nudge SNR test range to get meaningful results for these tests
if [ "$mode" == "datac1" ]; then
snr_nudge=4
fi
if [[ "$mode" == "datac4" || "$mode" == "datac13" || "$mode" == "datac14" ]]; then
snr_nudge=-6
fi
ch_multipath=''
Nbursts=$Nbursts_awgn
if [ "$channel" == "mpp" ]; then
ch_multipath='--mpp'
Nbursts=$Nbursts_mpp
snr_nudge=$((${snr_nudge}+4))
fi
clip=0
id='ctx'
if [ "$#" -eq 3 ]; then
clip=$3
id='ctxc'
snr_nudge=$((${snr_nudge}-4))
fi
tx_log=$(mktemp)
ch_log=$(mktemp)
rx_log=$(mktemp)
i=1
rm -f snrest_${id}_${mode}_${channel}*.txt
rm -f ber_${id}_${mode}_${channel}*.txt
rm -f per_${id}_${mode}_${channel}*.txt
for No in $aNo_list
do
No_adj=$((${No}-${snr_nudge}))
freedv_data_raw_tx --clip ${clip} --delay 1000 --txbpf ${clip} --bursts $Nbursts --testframes $Nbursts $mode /dev/zero - 2>${tx_log} | \
ch - - --No $No_adj ${ch_multipath} --fading_dir ${FADING_DIR} 2>>${ch_log} | \
freedv_data_raw_rx --testframes $mode - /dev/null 2>${rx_log} -v
SNRoffset=$(cat ${tx_log} | grep "mark:space" | tr -s ' ' | cut -d' ' -f 5)
SNRest=$(cat ${rx_log} | grep '\-BS\-' | tr -s ' ' | cut -d' ' -f17)
if [ ! -z "$SNRest" ]; then
echo ${SNRest} > snrest_${id}_${mode}_${channel}_${i}.txt
fi
BERmeas=$(cat ${rx_log} | grep 'BER......:' | cut -d' ' -f2)
PERmeas=$(cat ${rx_log} | grep 'Coded FER' | cut -d' ' -f3)
echo ${BERmeas} >> ber_${id}_${mode}_${channel}.txt
echo ${PERmeas} >> per_${id}_${mode}_${channel}.txt
i=$((i+1))
done
echo ${SNRoffset} > offset_${id}_${mode}_${channel}.txt
# trap not enough fading file samples (with mpp)
grep "Fading file finished" ${ch_log}
if [ $? -eq 0 ]; then
cat ${ch_log}
exit 1
fi
SNRch=$(cat ${ch_log} | grep SNR3k | tr -s ' ' | cut -d' ' -f3)
echo ${SNRch} > snr_${id}_${mode}_${channel}.txt
}
# Sanity check to make sure Octave/CML is set up OK
function test_ldpc {
echo "ldpcut; quit" | DISPLAY="" octave-cli -p ${CODEC2}/octave
if [ "$?" -ne 0 ]; then
echo "basic octave test failed, you may need to"
echo "(a) run ctests to create build_xxx/cml"
echo "(b) set up ~/.octaverc as per octave/ldpc.m"
exit 1
else
echo "OK"
fi
}