aboutsummaryrefslogtreecommitdiff
path: root/octave/trellis.m
diff options
context:
space:
mode:
Diffstat (limited to 'octave/trellis.m')
-rw-r--r--octave/trellis.m594
1 files changed, 0 insertions, 594 deletions
diff --git a/octave/trellis.m b/octave/trellis.m
deleted file mode 100644
index 01649ec..0000000
--- a/octave/trellis.m
+++ /dev/null
@@ -1,594 +0,0 @@
-% trellis.m
-% David Rowe July 2021
-%
-% Testing trellis decoding of Codec 2 Vector Quantiser (VQ)
-% information. Uses soft decision information, probablility of state
-% transitions, and left over redundancy to correct errors on VQ
-% reception.
-%
-% VQ indexes are transmitted as codewords mapped to +-1
-%
-% y = c + n
-%
-% where c is the transmitted codeword, y is the received codeword,
-% and n is Gaussian noise.
-%
-% This script generates the test data files:
-%
-% cd codec2/build_linux
-% ../script/train_trellis.sh
-%
-% Results so far (August 2021):
-%
-% 1/ 2dB improvement with nstages=3, dec=1
-% 2/ No useful improvement with nstages=3, dec=4. This is required for a practical codec to
-% get a useful bit rate.
-
-1;
-
-% converts a decimal value to a soft dec binary value
-function c = dec2sd(dec, nbits)
-
- % convert to binary
-
- c = zeros(1,nbits);
- for j=0:nbits-1
- mask = 2.^j;
- if bitand(dec,mask)
- c(nbits-j) = 1;
- end
- end
-
- % map to +/- 1
-
- c = -1 + 2*c;
-endfunction
-
-
-% y is vector of received soft decision values (e.g +/-1 + noise)
-function [txp indexes] = ln_tx_codeword_prob_given_rx_codeword_y(y, nstates, C)
- nbits = length(y);
- np = 2.^nbits;
-
- % Find log probability of all possible transmitted codewords
- txp = C * y';
-
- % return most probable codewords (number of states to search)
- [txp indexes] = sort(txp,"descend");
- txp = txp(1:nstates);
- indexes = indexes(1:nstates) - 1;
-endfunction
-
-% A matrix of all possible tx codewords C, one per row
-function C = precompute_C(nbits)
- np = 2.^nbits;
-
- C = zeros(np, nbits);
- for r=0:np-1
- C(r+1,:) = dec2sd(r,nbits);
- end
-
-endfunction
-
-
-% work out transition probability matrix, given lists of current and next
-% candidate codewords
-
-function tp = calculate_tp(vq, sd_table, h_table, indexes_current, indexes_next, verbose)
- ntxcw = length(indexes_current);
- tp = zeros(ntxcw, ntxcw);
- for txcw_current=1:ntxcw
- index_current = indexes_current(txcw_current);
- for txcw_next=1:ntxcw
- index_next = indexes_next(txcw_next);
- dist = vq(index_current+1,:) - vq(index_next+1,:);
- sd = mean(dist.^2);
- p = prob_from_hist(sd_table, h_table, sd);
- if bitand(verbose, 0x2)
- printf("index_current: %d index_next: %d sd: %f p: %f\n", index_current, index_next, sd, p);
- end
- tp(txcw_current, txcw_next) = log(p);
- end
- end
-endfunction
-
-
-% y is the sequence received soft decision codewords, each row is one
-% codeword in time. sd_table and h_table map SD to
-% probability. Returns the most likely transmitted VQ index ind in the
-% middle of the codeword sequence y. We search the most likely ntxcw
-% tx codewords out of 2^nbits possibilities.
-
-function ind = find_most_likely_index(y, vq, C, sd_table, h_table, nstages, ntxcw, verbose)
- [ncodewords nbits] = size(y);
-
- % populate the nodes of the trellis with the most likely transmitted codewords
- txp = zeros(nstages, ntxcw); indexes = zeros(nstages, ntxcw);
- for s=1:nstages
- [atxp aindexes] = ln_tx_codeword_prob_given_rx_codeword_y(y(s,:), ntxcw, C);
- txp(s,:) = atxp;
- indexes(s,:) = aindexes;
- end
-
- if verbose
- printf("rx_codewords:\n");
- for r=1:ncodewords
- for c=1:nbits
- printf("%7.2f", y(r,c));
- end
- printf("\n");
- end
-
- printf("\nProbability of each tx codeword index/binary/ln(prob):\n");
- printf(" ");
- for s=1:nstages
- printf("Time n%+d ", s - (floor(nstages/2)+1));
- end
- printf("\n");
-
- for i=1:ntxcw
- printf("%d ", i);
- for s=1:nstages
- ind = indexes(s,i);
- printf("%4d %12s %5.2f ", ind, dec2bin(ind,nbits), txp(s, i));
- end
- printf("\n");
- end
- printf("\n");
- end
-
- % Determine transition probability matrix for each stage, this
- % changes between stages as lists of candidate tx codewords
- % changes
-
- tp = zeros(nstages, ntxcw, ntxcw);
- for s=1:nstages-1
- if verbose printf("Calc tp(%d,:,:)\n", s), end
- tp(s,:,:) = calculate_tp(vq, sd_table, h_table, indexes(s,:), indexes(s+1,:), verbose);
- end
-
- if verbose
- printf("Evaluation of all possible paths:\n");
- printf(" ");
- for s=1:nstages
- printf(" n%+d", s - (floor(nstages/2)+1));
- end
- printf(" indexes");
- printf(" ");
-
- for s=1:nstages
- printf(" txp(%d)", s-1);
- if s < nstages
- printf(" tp(%d,%d) ", s-1,s);
- end
- end
- printf(" prob max_prob\n");
- end
-
- % OK lets search all possible paths and find most probable
-
- n = ones(1,nstages); % current node at each stage through trellis, describes current path
- max_prob = -100;
- do
-
- if bitand(verbose, 0x4)
- printf(" ");
- for s=1:nstages
- printf("%4d", n(s)-1);
- end
- printf(" ");
- for s=1:nstages
- printf("%4d ", indexes(s,n(s)));
- end
- end
-
- % find the probability of current path
- prob = 0;
- for s=1:nstages
- prob += txp(s, n(s));
- if bitand(verbose,0x4)
- printf("%8.2f ", txp(s, n(s)));
- end
- if s < nstages
- prob += tp(s, n(s), n(s+1));
- if bitand(verbose,0x4)
- printf("%8.2f ", tp(s, n(s), n(s+1)));
- end
- end
- end
-
- if (prob > max_prob)
- max_prob = prob;
- max_n = n;
- end
-
- if bitand(verbose,0x4)
- printf("%9.2f %9.2f\n", prob, max_prob);
- end
-
- % next path
-
- s = nstages;
- n(s)++;
- while (s && (n(s) == (ntxcw+1)))
- n(s) = 1;
- s--;
- if s > 0
- n(s)++;
- end
- end
- until (sum(n) == nstages)
-
- middle = floor(nstages/2)+1;
- ind = indexes(middle, max_n(middle));
- if verbose
- printf("\nMost likely path through nodes... ");
- for s=1:nstages
- printf("%4d ", max_n(s)-1);
- end
- printf("\nMost likely path through indexes: ");
- for s=1:nstages
- printf("%4d ", indexes(s,max_n(s)));
- end
- printf("\nMost likely VQ index at time n..: %4d\n", ind);
- end
-endfunction
-
-
-% Given a normalised histogram, estimate probability from SD
-function p = prob_from_hist(sd_table, h_table, sd)
- p = interp1 (sd_table, h_table, sd, "extrap", "nearest");
-endfunction
-
-
-% Calculate a normalised histogram of the SD of adjacent frames from
-% a file of output vectors from the VQ.
-function [sd_table h_table] = vq_hist(vq_output_fn, dec=1)
- K=20; K_st=2+1; K_en=16+1;
- vq_out = load_f32(vq_output_fn, K);
- [r c]= size(vq_out);
- diff = vq_out(dec+1:end,K_st:K_en) - vq_out(1:end-dec,K_st:K_en);
- % Octave efficient way to determine MSE or each row of matrix
- sd_adj = meansq(diff');
- [h_table sd_table] = hist(sd_adj,100,1);
- h_table = max(h_table, 1E-5);
-endfunction
-
-
-% vector quantise a sequence of target input vectors, returning the VQ indexes and
-% quantised vectors target_
-function [indexes target_] = vector_quantiser(vq, target, verbose=1)
- [vq_size K] = size(vq);
- [ntarget tmp] = size(target);
- target_ = zeros(ntarget,K);
- indexes = zeros(1,ntarget);
- for i=1:ntarget
- best_e = 1E32;
- for ind=1:vq_size
- e = sum((vq(ind,:)-target(i,:)).^2);
- if verbose printf("i: %d ind: %d e: %f\n", i, ind, e), end;
- if e < best_e
- best_e = e;
- best_ind = ind;
- end
- end
- if verbose printf("best_e: %f best_ind: %d\n", best_e, best_ind), end;
- target_(i,:) = vq(best_ind,:); indexes(i) = best_ind;
- end
-endfunction
-
-
-% faster version of vector quantiser
-function [indexes target_] = vector_quantiser_fast(vq, target, verbose=1)
- [vq_size K] = size(vq);
- [ntarget tmp] = size(target);
- target_ = zeros(ntarget,K);
- indexes = zeros(1,ntarget);
-
- % pre-compute energy of each VQ vector
- vqsq = zeros(vq_size,1);
- for i=1:vq_size
- vqsq(i) = vq(i,:)*vq(i,:)';
- end
-
- % use efficient matrix multiplies to search for best match to target
- for i=1:ntarget
- best_e = 1E32;
- e = vqsq - 2*(vq * target(i,:)');
- [best_e best_ind] = min(e);
- if verbose printf("best_e: %f best_ind: %d\n", best_e, best_ind), end;
- target_(i,:) = vq(best_ind,:); indexes(i) = best_ind;
- end
-endfunction
-
-
-% VQ a target sequence of frames then run a test using vanilla uncoded/trellis decoder
-function results = run_test(target, vq, sd_table, h_table, ntxcw, nstages, EbNo, verbose)
- [frames tmp] = size(target);
- [vq_length tmp] = size(vq);
- nbits = log2(vq_length);
- nerrors = 0;
- nerrors_vanilla = 0;
- tbits = 0;
- nframes = 0;
- nper = 0;
- nper_vanilla = 0;
-
- C = precompute_C(nbits);
-
- % Vector Quantise target vectors sequence
- [tx_indexes target_ ] = vector_quantiser_fast(vq, target, verbose);
- % use convention of indexes starting from 0
- tx_indexes -= 1;
- % mean SD of VQ with no errors
- diff = target - target_;
- mse_noerrors = mean(diff(:).^2);
-
- % construct tx symbol codewords from VQ indexes
- tx_codewords = zeros(frames, nbits);
- for f=1:frames
- tx_codewords(f,:) = dec2sd(tx_indexes(f), nbits);
- end
-
- rx_codewords = tx_codewords + randn(frames, nbits)*sqrt(1/(2*EbNo));
- rx_indexes = zeros(1,frames);
- rx_indexes_vanilla = ones(1,frames);
-
- ns2 = floor(nstages/2);
- for f=ns2+1:frames-ns2
- %if f==10 verbose = 1+0x2, else verbose = 0;, end
- if verbose
- printf("f: %d tx_indexes: ", f);
- for i=f-ns2:f+ns2
- printf("%d ", tx_indexes(i));
- end
- printf("\n");
- end
- tx_bits = tx_codewords(f,:) > 0;
- if verbose
- printf("tx_bits: ");
- for i=1:nbits
- printf("%d",tx_bits(i));
- end
- printf("\n");
- end
- rx_bits_vanilla = rx_codewords(f,:) > 0;
- rx_indexes(f) = find_most_likely_index(rx_codewords(f-ns2:f+ns2,:)*EbNo,
- vq, C, sd_table, h_table, nstages, ntxcw, verbose);
- rx_bits = dec2sd(rx_indexes(f), nbits) > 0;
- rx_indexes_vanilla(f) = sum(rx_bits_vanilla .* 2.^(nbits-1:-1:0));
- errors = sum(xor(tx_bits, rx_bits));
- nerrors += errors;
- if errors nper++;, end
- errors = sum(xor(tx_bits, rx_bits_vanilla));
- nerrors_vanilla += errors;
- if errors nper_vanilla++;, end
- if verbose
- printf("[%d] %d %d\n", f, nerrors, nerrors_vanilla);
- end
- tbits += nbits;
- nframes++;
- end
-
- EbNodB = 10*log10(EbNo);
- target = target(ns2+1:frames-ns2,:);
- target_vanilla_ = vq(rx_indexes_vanilla(ns2+1:frames-ns2)+1,:);
- target_ = vq(rx_indexes(ns2+1:frames-ns2)+1,:);
- diff_vanilla = target - target_vanilla_;
- mse_vanilla = mean(diff_vanilla(:).^2);
- diff = target - target_;
- mse = mean(diff(:).^2);
- printf("Eb/No: %3.2f dB nframes: %2d nerrors %3d %3d BER: %4.3f %4.3f PER: %3.2f %3.2f mse: %3.2f %3.2f %3.2f\n",
- EbNodB, nframes, nerrors, nerrors_vanilla, nerrors/tbits, nerrors_vanilla/tbits,
- nper/nframes, nper_vanilla/nframes,
- mse_noerrors, mse, mse_vanilla);
- results.ber = nerrors/tbits;
- results.ber_vanilla = nerrors_vanilla/tbits;
- results.per = nper/nframes;
- results.per_vanilla = nper_vanilla/nframes;
- results.mse_noerrors = mse_noerrors;
- results.mse = mse;
- results.mse_vanilla = mse_vanilla;
- results.tx_indexes = tx_indexes;
- results.rx_indexes = rx_indexes;
- results.rx_indexes_vanilla = rx_indexes_vanilla;
-endfunction
-
-% Simulations ---------------------------------------------------------------------
-
-% top level function to set up and run a test
-function [results target_] = test_trellis(target_fn, nframes=100, dec=1, ntxcw=8, nstages=3, EbNodB=3, verbose=0)
- K = 20; K_st=2+1; K_en=16+1;
- vq_fn = "../build_linux/vq_stage1_bs004.f32";
- vq_output_fn = "../build_linux/all_speech_8k_test.f32";
-
- % load VQ
- vq = load_f32(vq_fn, K);
- [vq_size tmp] = size(vq);
- vqsub = vq(:,K_st:K_en);
-
- % load file of VQ-ed vectors to train up SD PDF estimator
- [sd_table h_table] = vq_hist(vq_output_fn, dec);
-
- % load sequence of target vectors we wish to VQ
- target = load_f32(target_fn, K);
-
- % limit test to the first nframes vectors
- if nframes != -1
- last = nframes;
- else
- last = length(target);
- end
- target = target(1:dec:last,K_st:K_en);
-
- % run a test
- EbNo=10^(EbNodB/10);
- results = run_test(target, vqsub, sd_table, h_table, ntxcw, nstages, EbNo, verbose);
- if verbose
- for f=2:nframes-1
- printf("f: %03d tx_index: %04d rx_index: %04d\n", f, results.tx_indexes(f), results.rx_indexes(f));
- end
- end
-
- % return full band vq-ed vectors
- target_ = zeros(last,K);
- target_(1:dec:last,:) = vq(results.rx_indexes+1,:);
-
- % use linear interpolation to restore original frame rate
- for f=1:dec:last-dec
- prev = f; next = f + dec;
- for g=prev+1:next-1
- cnext = (g-prev)/dec; cprev = 1 - cnext;
- target_(g,:) = cprev*target_(prev,:) + cnext*target_(next,:);
- %printf("f: %d g: %d cprev: %f cnext: %f\n", f, g, cprev, cnext);
- end
- end
-endfunction
-
-% Plot histograms of SD at different decimations in time
-function vq_hist_dec(vq_output_fn)
- figure(1); clf;
- [sd_table h_table] = vq_hist(vq_output_fn, dec=1);
- plot(sd_table, h_table, "b;dec=1;");
- hold on;
- [sd_table h_table] = vq_hist(vq_output_fn, dec=2);
- plot(sd_table, h_table, "r;dec=2;");
- [sd_table h_table] = vq_hist(vq_output_fn, dec=3);
- plot(sd_table, h_table, "g;dec=3;");
- [sd_table h_table] = vq_hist(vq_output_fn, dec=4);
- plot(sd_table, h_table, "c;dec=4;");
- hold off;
- axis([0 300 0 0.5])
- xlabel("SD dB*dB"); title('Histogram of SD(n,n+1)');
-endfunction
-
-% Automated tests for vanilla and fast VQ search functions
-function test_vq(vq_fn)
- K=20;
- vq = load_f32(vq_fn, K);
- vq_size = 100;
- target = vq(1:vq_size,:);
- indexes = vector_quantiser(target,target, verbose=0);
- assert(indexes == 1:vq_size);
- printf("Vanilla OK!\n");
- indexes = vector_quantiser_fast(target,target, verbose=0);
- assert(indexes == 1:vq_size);
- printf("Fast OK!\n");
-endfunction
-
-% Test trellis decoding a single vector in a sequence of 3
-function ind = run_test_single(tx_codewords, ntxcw, var, verbose)
- nstages = 3;
- nbits = 2;
-
- rx_codewords = tx_codewords + randn(nstages, nbits)*var;
- vq = [0 0 0 1;
- 0 0 1 0;
- 0 1 0 0;
- 1 0 0 0];
- sd_table = [0 1 2 4];
- h_table = [0.5 0.25 0.15 0.1];
- C = precompute_C(nbits);
- ind = find_most_likely_index(rx_codewords, vq, C, sd_table, h_table, nstages, ntxcw, verbose);
-endfunction
-
-% Series of single point sanity checks
-function test_single
- printf("Single vector decode tests....\n");
- ind = run_test_single([-1 -1; -1 -1; -1 -1], ntxcw=1, var=0, verbose=0);
- assert(ind == 0);
- printf("00 with no noise OK!\n");
-
- ind = run_test_single([-1 1; 1 1; -1 1], ntxcw=1, var=0, verbose=0);
- assert(ind == 3);
- printf("11 with no noise OK!\n");
-
- ind = run_test_single([-1 -1; -1 1; -1 -1], ntxcw=4, var=1, verbose=0);
- assert(ind == 1);
- printf("01 with noise OK!\n");
-endfunction
-
-% BPSK simulation to check noise injection
-function test_bpsk_ber
- nbits = 12;
- frames = 10000;
- tx_codewords = zeros(frames,nbits);
- tx_bits = zeros(frames,nbits);
- for f=1:frames
- tx_codewords(f,:) = dec2sd(f, nbits);
- tx_bits(f,:) = tx_codewords(f,:) > 0;
- end
-
- EbNodB = 5;
- EbNo = 10^(EbNodB/10);
- rx_codewords = tx_codewords + randn(frames, nbits)*sqrt(1/(2*EbNo));
- rx_bits = rx_codewords > 0;
- nerrors = sum(xor(tx_bits, rx_bits)(:));
- tbits = frames*nbits;
- printf("EbNo: %4.2f dB tbits: %d errs: %d BER: %4.3f %4.3f\n", EbNodB, tbits, nerrors, nerrors/tbits, 0.5*erfc(sqrt(EbNo)));
-endfunction
-
-% generate sets of curves
-function [EbNodB rms_sd] = run_curves(frames=100, dec=1, nstages=5)
- results_log = [];
- EbNodB = [0 1 2 3 4 5];
- target_fn = "../build_linux/all_speech_8k_lim.f32";
-
- for i=1:length(EbNodB)
- results = test_trellis(target_fn, frames, dec, ntxcw=8, nstages, EbNodB(i), verbose=0);
- results_log = [results_log results];
- end
- for i=1:length(results_log)
- ber(i) = results_log(i).ber;
- ber_vanilla(i) = results_log(i).ber_vanilla;
- per(i) = results_log(i).per;
- per_vanilla(i) = results_log(i).per_vanilla;
- rms_sd_noerrors(i) = sqrt(results_log(i).mse_noerrors);
- rms_sd(i) = sqrt(results_log(i).mse);
- rms_sd_vanilla(i) = sqrt(results_log(i).mse_vanilla);
- end
-
- figure(1); clf; semilogy(EbNodB, ber_vanilla, "r+-;uncoded;"); hold on;
- semilogy(EbNodB, ber, "g+-;trellis;"); hold off;
- grid('minor'); title(sprintf("BER dec=%d nstages=%d",dec,nstages));
- print("-dpng", sprintf("trellis_dec_%d_ber.png",dec));
-
- figure(2); clf; semilogy(EbNodB, per_vanilla, "r+-;uncoded;"); hold on;
- semilogy(EbNodB, per, "g+-;trellis;");
- grid('minor'); title(sprintf("PER dec=%d nstages=%d",dec,nstages));
- print("-dpng", sprintf("trellis_dec_%d_per.png",dec));
-
- figure(3); clf; plot(EbNodB, rms_sd_noerrors, "b+-;no errors;"); hold on;
- plot(EbNodB, rms_sd_vanilla, "r+-;uncoded;");
- plot(EbNodB, rms_sd, "g+-;trellis;"); hold off;
- grid('minor'); title(sprintf("RMS SD dec=%d nstages=%d",dec,nstages));
- print("-dpng", sprintf("trellis_dec_%d_rms_sd.png",dec));
-endfunction
-
-function vq_file(vq_fn, dec, EbNodB, in_fn, out_fn)
- [results target_] = test_trellis(in_fn, nframes=-1, dec, ntxcw=8, nstages=3, EbNodB, verbose=0);
- save_f32(out_fn, target_);
-endfunction
-
-% -------------------------------------------------------------------
-
-more off;
-randn('state',1);
-
-% uncomment one of the below to run a test or simulation
-
-% These two tests show where we are at:
-%test_trellis(target_fn, nframes=600, dec=1, ntxcw=8, nstages=3, EbNodB=3, verbose=0);
-%test_trellis(target_fn, nframes=600, dec=4, ntxcw=8, nstages=3, EbNodB=3, verbose=0);
-
-%run_curves(600,1)
-%run_curves(600,2)
-%run_curves(600,4)
-%[EbNodB rms_sd] = run_curves(30*100,3,3)
-
-%test_trellis(target_fn, nframes=200, dec=1, ntxcw=1, nstages=3, EbNodB=3, verbose=0);
-%test_trellis(target_fn, nframes=100, dec=2, ntxcw=8, nstages=3, EbNodB=3, verbose=0);
-%test_vq("../build_linux/vq_stage1.f32");
-%vq_hist_dec("../build_linux/all_speech_8k_test.f32");
-%test_single
-%test_bpsk_ber