213 lines
7.2 KiB
Matlab
213 lines
7.2 KiB
Matlab
|
|
|
|
M = 6;
|
|
|
|
bitpattern = [];
|
|
s = RandStream('twister','Seed',1);
|
|
for i = 1:log2(M)
|
|
N = 2^(17-1); %length of prbs
|
|
bitpattern(:,i) = randi(s,[0 1], N, 1);
|
|
end
|
|
|
|
if M == 6
|
|
bitpattern = reshape(bitpattern',[],1);
|
|
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
|
|
end
|
|
|
|
bits = Informationsignal(bitpattern);
|
|
|
|
symbols = PAMmapper(M,0).map(bits);
|
|
|
|
bits_rx = PAMmapper(M,0).demap(symbols);
|
|
[~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
|
assert(ber_direct==0,'Mapping is wrong');
|
|
|
|
nBursts = 0;
|
|
% No Precoding %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
%SEND DIRECTLY
|
|
symbols_tx = symbols;
|
|
|
|
symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, nBursts, 42);
|
|
|
|
%RECEIVE BRANCH (do nothing special)
|
|
bits_rx = PAMmapper(M,0).demap(symbols_rx);
|
|
|
|
[~,~,ber,errpos] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
|
disp(['BER normal: - ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
|
|
bursts_normal = count_error_bursts(errpos, 20);
|
|
|
|
|
|
% Precode Emulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
%SEND DIRECTLY
|
|
symbols_tx = symbols;
|
|
|
|
symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, nBursts, 42);
|
|
|
|
%REFERENCE BRACH
|
|
symbols_db = Duobinary().encode(symbols_tx);
|
|
symbols_tx_emu = Duobinary().decode(symbols_db);
|
|
bits_tx_emu = PAMmapper(M,0).demap(symbols_tx_emu);
|
|
|
|
% symbols_rx = introduce_symbol_errors(symbols_tx, 1, 10, 200, 42);
|
|
|
|
%RECEIVE BRANCH
|
|
symbols_db = Duobinary().encode(symbols_rx);
|
|
symbols_rx_emu = Duobinary().decode(symbols_db);
|
|
bits_rx = PAMmapper(M,0).demap(symbols_rx_emu);
|
|
|
|
[~,~,ber_precode_emulation,errpos_precode_emulation] = calc_ber(bits_tx_emu.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
|
disp(['BER precode emulation: ',sprintf('%.1E',ber_precode_emulation),' - - PAM-',num2str(M)]);
|
|
bursts_precode_emulation = count_error_bursts(errpos_precode_emulation, 20);
|
|
|
|
|
|
|
|
|
|
% Precode at Tx %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
%SEND PRECODED DATA
|
|
symbols_tx_prec = Duobinary().precode(symbols);
|
|
|
|
symbols_rx_prec = introduce_symbol_errors(symbols_tx_prec, 1, 10, nBursts, 42);
|
|
|
|
%RECEIVE BRANCH
|
|
symbols_db = Duobinary().encode(symbols_rx_prec);
|
|
symbols_rx_prec = Duobinary().decode(symbols_db);
|
|
bits_rx = PAMmapper(M,0).demap(symbols_rx_prec);
|
|
|
|
[~,~,ber_precoded,errpos_precoded] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
|
disp(['BER precoded: ',sprintf('%.1E',ber_precoded),' - - PAM-',num2str(M)]);
|
|
burst_precoded = count_error_bursts(errpos_precoded, 20);
|
|
|
|
|
|
% Precode at Tx but omit at Rx %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
%SEND PRECODED DATA
|
|
symbols_tx_prec = Duobinary().precode(symbols);
|
|
bits_tx_prec = PAMmapper(M,0).demap(symbols_tx_prec);
|
|
|
|
symbols_rx_omit = introduce_symbol_errors(symbols_tx_prec, 1, 10, nBursts, 42);
|
|
|
|
%RECEIVE BRANCH
|
|
bits_rx = PAMmapper(M,0).demap(symbols_rx_omit);
|
|
|
|
[~,~,ber_omit,errpos_omit] = calc_ber(bits_tx_prec.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
|
disp(['BER (omit precode): ',sprintf('%.1E',ber_omit),' - - PAM-',num2str(M)]);
|
|
burst_omit = count_error_bursts(errpos_omit, 20);
|
|
|
|
if 0
|
|
cols = linspecer(8);
|
|
figure();hold on;
|
|
stem(1:20,bursts_normal,'LineWidth',2,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder');
|
|
stem(1:20,bursts_precode_emulation,'LineWidth',2,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','emulated precoder');
|
|
stem(1:20,burst_precoded,'LineWidth',1,'Color',cols(6,:),'Marker','_','DisplayName','w/ diff. precoder');
|
|
stem(1:20,burst_omit,'LineWidth',1,'Color',cols(5,:),'Marker','.','LineStyle',':','DisplayName','omit precoder');
|
|
xlabel('Bit Error Burst Length')
|
|
ylabel('Occurence')
|
|
set(gca, 'yscale', 'log');
|
|
end
|
|
|
|
|
|
%% State Analysis
|
|
signal_to_analyze = symbols_tx_emu;
|
|
x = signal_to_analyze.signal(:);
|
|
levels = sort(unique(x)).'; % or provide known 1x6 level values
|
|
|
|
[~,ix] = min(abs(x - levels),[],2);
|
|
x = levels(ix); % snapped/quantized
|
|
|
|
%% TRANSITION COUNTS & PROBABILITIES
|
|
K = numel(levels);
|
|
% map to state indices 1..K
|
|
[tf, idx] = ismember(x, levels);
|
|
idx = idx(:);
|
|
from = idx(1:end-1);
|
|
to = idx(2:end);
|
|
from = idx(1:2:end);
|
|
to = idx(2:2:end);
|
|
|
|
% counts C(from,to)
|
|
C = accumarray([from,to], 1, [K K], @sum, 0);
|
|
% row-stochastic transition matrix P(to|from)
|
|
rowSums = sum(C,2);
|
|
P = C ./ max(rowSums,1);
|
|
|
|
%% 1) HEATMAP (which transitions are more probable?)
|
|
figure('Name','Transition Probabilities (to | from)');
|
|
h = heatmap(levels, levels, P, 'Colormap', parula, 'ColorbarVisible','on');
|
|
colormap(gca,[[1,1,1];flip(cbrewer2('Spectral',100))]);clim([0,ceil(max(P(:))*10)/10]);
|
|
h.XLabel = 'From state (level)';
|
|
h.YLabel = 'To state (level)';
|
|
h.Title = 'P(to | from)';
|
|
|
|
%% 2) WEIGHTED TRANSITION GRAPH
|
|
% Use dtmc if you have Econometrics Toolbox:
|
|
mc = dtmc(P, 'StateNames', string(levels.*PAMmapper(M,0).get_scaling));
|
|
figure('Name','Markov Graph (dtmc)');
|
|
gp = graphplot(mc, 'ColorEdges',true, 'LabelEdges',true);
|
|
|
|
|
|
function symbols = introduce_symbol_errors(symbols, j, maxBurstLen, nBursts, seed)
|
|
%INTRODUCE_SYMBOL_ERRORS injects bursty level errors into symbols.signal.
|
|
% symbols.signal : column/row vector of quantized levels (exactly one of 6 values)
|
|
% j : max level step per sample (default 1)
|
|
% maxBurstLen : maximum burst length (default 8)
|
|
% nBursts : number of bursts to insert (default ~1% of length)
|
|
% seed : RNG seed (optional)
|
|
|
|
if nargin < 2 || isempty(j), j = 1; end
|
|
if nargin < 3 || isempty(maxBurstLen), maxBurstLen = 8; end
|
|
x = symbols.signal(:);
|
|
N = numel(x);
|
|
if nargin < 4 || isempty(nBursts), nBursts = max(1, round(0.01*N)); end
|
|
if nargin >= 5 && ~isempty(seed), rng(seed); end
|
|
|
|
% known levels and index mapping
|
|
lvls = sort(unique(x)).';
|
|
K = numel(lvls);
|
|
|
|
[~, idx] = ismember(x, lvls); % idx in 1..6
|
|
|
|
used = false(N,1); % avoid overlapping bursts
|
|
burst_ranges = zeros(nBursts,2);
|
|
|
|
for b = 1:nBursts
|
|
% pick start not inside an existing burst
|
|
s = randi(N);
|
|
while used(s), s = randi(N); end
|
|
L = randi(maxBurstLen);
|
|
e = min(N, s+L-1);
|
|
|
|
% mark used range
|
|
used(s:e) = true;
|
|
burst_ranges(b,:) = [s e];
|
|
|
|
% choose one direction for the whole burst: -1 (down) or +1 (up)
|
|
dir = randi([0 1])*2 - 1;
|
|
|
|
% apply level errors within the burst
|
|
for t = s:e
|
|
k = idx(t); % current level index (1..6)
|
|
|
|
% force inward movement at edges; prevents "flipping" to opposite edge
|
|
if k == 1 && dir == -1, dir = +1; end
|
|
if k == K && dir == +1, dir = -1; end
|
|
|
|
step = randi([1 j]); % 1..j steps
|
|
kNew = k + dir*step;
|
|
|
|
% clamp to [1,K], no wrap-around
|
|
if kNew < 1, kNew = 1; elseif kNew > K, kNew = K; end
|
|
|
|
% if clamped to the same edge repeatedly, flip direction to keep changing
|
|
if kNew == k
|
|
dir = -dir;
|
|
kNew = max(1, min(K, k + dir*step));
|
|
end
|
|
|
|
idx(t) = kNew;
|
|
end
|
|
end
|
|
|
|
x_err = lvls(idx);
|
|
symbols.signal = reshape(x_err, size(symbols.signal)); % preserve original shape
|
|
|
|
end
|