Add partial response symbol mapping and IM/DD system example
- Implemented a new MATLAB script for partial response symbol mapping using Duobinary precoding. - Added functions for printing constellation bit mapping and partial response state tables. - Created a minimal example for an IM/DD system, including signal generation, modulation, and equalization processes. - Integrated various components such as pulse shaping, optical modulation, and receiver processing. - Included detailed configurations for parameters like bias, link length, and filter settings.
This commit is contained in:
@@ -1,23 +1,21 @@
|
||||
for M = [2 4 8]
|
||||
bits = Signalgenerator("form", signalform.prms,"M", M,"order", 15).process();
|
||||
|
||||
bits = Signalgenerator("form", signalform.prms,"M", M,"order", 16).process();
|
||||
mapper = PAMmapper(M,0);
|
||||
symbols = mapper.map(bits);
|
||||
|
||||
symbols_pre = Duobinary().precode(symbols);
|
||||
|
||||
symbols_db = Duobinary().encode(symbols_pre);
|
||||
|
||||
symbols_rx = Duobinary().decode(symbols_db);
|
||||
db = Duobinary();
|
||||
pr1 = Partialresponse();
|
||||
|
||||
symbols_pre = db.precode(symbols);
|
||||
symbols_db = db.encode(symbols_pre);
|
||||
symbols_rx = db.decode(symbols_db);
|
||||
bits_rx = PAMmapper(M,0).demap(symbols_rx);
|
||||
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
||||
disp(['Class BER: ',sprintf('%.1E',ber),' - PAM-',num2str(M)]);
|
||||
|
||||
symbols_pre_pr = Partialresponse("order",1).precode(symbols,"M",M);
|
||||
symbols_db_pr = Partialresponse("order",1).encode(symbols_pre_pr,"M",M);
|
||||
symbols_rx_pr = Partialresponse("order",1).decode(symbols_db_pr,"M",M);
|
||||
|
||||
symbols_pre_pr = pr1.precode(symbols,"M",M);
|
||||
symbols_db_pr = pr1.encode(symbols_pre_pr,"M",M);
|
||||
symbols_rx_pr = pr1.decode(symbols_db_pr,"M",M);
|
||||
bits_rx_pr = mapper.demap(symbols_rx_pr);
|
||||
[~,~,ber_pr,~] = calc_ber(bits.signal,bits_rx_pr.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
||||
disp(['Partialresponse BER: ',sprintf('%.1E',ber_pr),' - PAM-',num2str(M)]);
|
||||
@@ -37,4 +35,43 @@ for M = [2 4 8]
|
||||
disp(['Decode match: ',num2str(dec_match), ...
|
||||
' | max abs diff: ',sprintf('%.3g',dec_maxdiff)]);
|
||||
disp('---');
|
||||
|
||||
f = figure("Name",sprintf("PAM-%d Partial-Response Histograms",M));
|
||||
t = tiledlayout(f,1,4,"TileSpacing","compact","Padding","compact");
|
||||
title(t,sprintf("PAM-%d Symbol Histograms",M));
|
||||
|
||||
nexttile;
|
||||
plotDiscreteHistogram(symbols.signal,sprintf("PAM-%d",M));
|
||||
|
||||
nexttile;
|
||||
plotDiscreteHistogram(symbols_db_pr.signal,"Duobinary 1st Order");
|
||||
|
||||
nexttile;
|
||||
plotDiscreteHistogram(getEncodedSymbols(symbols,M,2),"Duobinary 2nd Order");
|
||||
|
||||
nexttile;
|
||||
plotDiscreteHistogram(getEncodedSymbols(symbols,M,3),"Duobinary 3rd Order");
|
||||
|
||||
end
|
||||
|
||||
function y = getEncodedSymbols(symbols, M, order)
|
||||
pr = Partialresponse("order",order);
|
||||
y = pr.encode(pr.precode(symbols,"M",M),"M",M).signal;
|
||||
end
|
||||
|
||||
function plotDiscreteHistogram(x, plot_title)
|
||||
levels = unique(x(:)).';
|
||||
counts = zeros(size(levels));
|
||||
|
||||
for idx = 1:numel(levels)
|
||||
counts(idx) = sum(abs(x - levels(idx)) < 1e-12);
|
||||
end
|
||||
|
||||
bar(levels, counts, 0.9, "FaceColor", [0.2 0.45 0.75], "EdgeColor", "none");
|
||||
grid on;
|
||||
box on;
|
||||
xlabel("Level");
|
||||
ylabel("Count");
|
||||
title(plot_title);
|
||||
xticks(levels);
|
||||
end
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
useprbs = 1;
|
||||
M = 2;
|
||||
randkey = 1;
|
||||
datarate = 448e9;
|
||||
fsym = round(datarate / log2(M)) ;
|
||||
|
||||
Tx_bits = Signalgenerator( ...
|
||||
"form", signalform.prms, ...
|
||||
"M", M, ...
|
||||
"order", 15).process();
|
||||
|
||||
|
||||
Symbols_tx = PAMmapper(M,0,"eth_style",0).map(Tx_bits);
|
||||
Symbols_tx.fs = fsym;
|
||||
|
||||
precode = db_mode.db_precoded;
|
||||
|
||||
%%% precode
|
||||
switch precode
|
||||
case db_mode.db_precoded
|
||||
Symbols_tx = Duobinary().precode(Symbols_tx);
|
||||
case db_mode.db_encoded
|
||||
Symbols_tx = Duobinary().precode(Symbols_tx);
|
||||
Symbols_tx = Duobinary().encode(Symbols_tx);
|
||||
case db_mode.no_db
|
||||
|
||||
end
|
||||
|
||||
for n = 10
|
||||
|
||||
% Start from the transmitted symbol sequence for this impairment case.
|
||||
Symbols_rx = Symbols_tx;
|
||||
|
||||
% Insert n deterministic symbol errors near the beginning of the sequence.
|
||||
% Each replacement is chosen from another symbol position with a different
|
||||
% amplitude, so this produces actual symbol decisions errors.
|
||||
pos = 1;
|
||||
if n~=0
|
||||
for pos = 1:n
|
||||
po = randi(100);
|
||||
a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
|
||||
while a == 1
|
||||
po = po+1;
|
||||
po = randi(100);
|
||||
a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
|
||||
end
|
||||
Symbols_rx.signal(100+pos) = Symbols_tx.signal(100+po);
|
||||
end
|
||||
end
|
||||
|
||||
% Keep track of the positions where the artificial symbol errors were
|
||||
% inserted before duobinary encoding/decoding is applied.
|
||||
error_positions = ~(Symbols_rx.signal == Symbols_tx.signal);
|
||||
error_positions = find(error_positions==1);
|
||||
|
||||
% Receiver-side duobinary processing depends on what was already done at
|
||||
% the transmitter side:
|
||||
% - db_precoded: the sequence is precoded only, so emulate the channel PR
|
||||
% encoder first and then apply memoryless duobinary decoding.
|
||||
% - db_encoded: the sequence already contains duobinary levels, so only
|
||||
% the memoryless duobinary decoder is applied.
|
||||
switch precode
|
||||
|
||||
case db_mode.db_precoded
|
||||
Symbols_rx = Duobinary().encode(Symbols_rx);
|
||||
Symbols_rx = Duobinary().decode(Symbols_rx);
|
||||
case db_mode.db_encoded
|
||||
Symbols_rx = Duobinary().decode(Symbols_rx);
|
||||
|
||||
end
|
||||
|
||||
% Convert the recovered PAM symbols back to bits with the framework mapper.
|
||||
Rx_bits = PAMmapper(M,0).demap(Symbols_rx);
|
||||
|
||||
%%%%% Check BER of Bit Sequence %%%%%
|
||||
% Skip a few edge samples because the first/last symbols can include
|
||||
% state/transient effects from the duobinary recursion.
|
||||
[~,error_num(n+1),ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
|
||||
|
||||
% disp(['BER: ',sprintf('%.1E',ber),sprintf(' - Num. Err: %.1d',error_num(n+1)-2),' - - PAM-',num2str(M)]);
|
||||
fprintf('n: %d - Num. Err: %.1d \n',n,error_num(n+1));
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
% Plot bit-level and symbol-level comparisons for a quick visual sanity check.
|
||||
figure(3);
|
||||
clf
|
||||
%sgtitle(['BER: ',num2str(ber),' // Error is at position: ',num2str(error_pos),''])
|
||||
|
||||
% Compare the first few transmitted and received bits.
|
||||
subplot(2,2,1)
|
||||
hold on
|
||||
title('First Bits')
|
||||
stairs(Tx_bits.signal(100:150,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
|
||||
stairs(Rx_bits.signal(100:150,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
|
||||
legend
|
||||
|
||||
% Compare the last few transmitted and received bits.
|
||||
subplot(2,2,2)
|
||||
title('Last Bits')
|
||||
hold on
|
||||
stairs(Tx_bits.signal(end-50:end,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
|
||||
stairs(Rx_bits.signal(end-50:end,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
|
||||
legend
|
||||
|
||||
% Compare the first few symbol decisions after the duobinary processing chain.
|
||||
subplot(2,2,3)
|
||||
hold on
|
||||
title('First Symbols Compare')
|
||||
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-')
|
||||
stairs(Symbols_rx.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
|
||||
legend
|
||||
|
||||
% Compare the last few symbol decisions after the duobinary processing chain.
|
||||
subplot(2,2,4)
|
||||
hold on
|
||||
title('Last Symbols Compare')
|
||||
stairs(Symbols_tx.signal(end-50:end,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-')
|
||||
stairs(Symbols_rx.signal(end-50:end,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
|
||||
legend
|
||||
@@ -0,0 +1,147 @@
|
||||
|
||||
M = 4;
|
||||
useUnitRmsScaling = false;
|
||||
bits = Signalgenerator("form", signalform.prms,"M", M,"order", 16).process();
|
||||
mapper = PAMmapper(M,0);
|
||||
symbols = mapper.map(bits);
|
||||
|
||||
|
||||
[constellation, bitmap] = mapper.getConstellationBitMapping();
|
||||
printConstellationBitMapping(M, constellation, bitmap);
|
||||
printPartialResponseStateTable(M, bitmap, "Duobinary", 1, useUnitRmsScaling);
|
||||
|
||||
db = Duobinary();
|
||||
pr1 = Partialresponse();
|
||||
|
||||
symbols_pre = db.precode(symbols);
|
||||
symbols_db = db.encode(symbols_pre);
|
||||
symbols_rx = db.decode(symbols_db);
|
||||
bits_rx = PAMmapper(M,0).demap(symbols_rx);
|
||||
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
|
||||
disp(['Class BER: ',sprintf('%.1E',ber),' - PAM-',num2str(M)]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function printConstellationBitMapping(M, constellation, bitmap)
|
||||
fprintf("\n=== PAM-%d Bit Mapping ===\n", M);
|
||||
|
||||
bitLabels = string(cellstr(char(bitmap + '0')));
|
||||
|
||||
if size(constellation, 2) == 1
|
||||
mappingTable = table(constellation(:), bitLabels, ...
|
||||
'VariableNames', {'Symbol', 'Bits'});
|
||||
else
|
||||
mappingTable = table(constellation(:,1), constellation(:,2), bitLabels, ...
|
||||
'VariableNames', {'Symbol_1', 'Symbol_2', 'Bits'});
|
||||
end
|
||||
|
||||
disp(mappingTable);
|
||||
fprintf("Number of constellation points: %d\n\n", size(constellation, 1));
|
||||
end
|
||||
|
||||
function printPartialResponseStateTable(M, bitmap, label, order, useUnitRmsScaling)
|
||||
if useUnitRmsScaling
|
||||
scalingLabel = "unit-RMS scaling";
|
||||
encodedScaling = getEncodedScaling(M, order);
|
||||
else
|
||||
scalingLabel = "raw encoder levels";
|
||||
encodedScaling = 1;
|
||||
end
|
||||
|
||||
fprintf("=== %s Effective State Table (order = %d, %s) ===\n", label, order, scalingLabel);
|
||||
|
||||
mapper = PAMmapper(M, 0);
|
||||
h = arrayfun(@(k) nchoosek(order, k), 0:order);
|
||||
center = ((M - 1) * sum(h)) / 2;
|
||||
|
||||
if useUnitRmsScaling
|
||||
pamScaling = mapper.scaling;
|
||||
else
|
||||
pamScaling = 1;
|
||||
end
|
||||
|
||||
numStates = M^order;
|
||||
numInputs = M;
|
||||
|
||||
prevStateLabels = strings(numStates * numInputs, 1);
|
||||
bitLabels = strings(numStates * numInputs, 1);
|
||||
aAmp = zeros(numStates * numInputs, 1);
|
||||
uIdx = zeros(numStates * numInputs, 1);
|
||||
uAmp = zeros(numStates * numInputs, 1);
|
||||
yAmp = zeros(numStates * numInputs, 1);
|
||||
nextStateLabels = strings(numStates * numInputs, 1);
|
||||
|
||||
row = 1;
|
||||
for stateIdx = 0:(numStates - 1)
|
||||
state = indexToState(stateIdx, M, order);
|
||||
|
||||
for a = 0:(numInputs - 1)
|
||||
u = mod(a - sum(h(2:end) .* state), M);
|
||||
y = (sum(h .* [u, state]) - center) / encodedScaling;
|
||||
|
||||
prevStateLabels(row) = formatState(state);
|
||||
bitLabels(row) = bitsFromIndex(bitmap, a + 1);
|
||||
aAmp(row) = indexToPamAmplitude(a, pamScaling, M);
|
||||
uIdx(row) = u;
|
||||
uAmp(row) = indexToPamAmplitude(u, pamScaling, M);
|
||||
yAmp(row) = y;
|
||||
nextStateLabels(row) = formatState([u, state(1:end-1)]);
|
||||
row = row + 1;
|
||||
end
|
||||
end
|
||||
|
||||
stateTable = table(prevStateLabels, bitLabels, aAmp, uIdx, uAmp, yAmp, nextStateLabels, ...
|
||||
'VariableNames', {'PrevState', 'Bits', 'InputSymbol', 'PrecodedIndex', 'PrecodedSymbol', 'EncodedLevel', 'NextState'});
|
||||
|
||||
disp(stateTable);
|
||||
fprintf("States: %d, transitions: %d\n\n", numStates, height(stateTable));
|
||||
end
|
||||
|
||||
function state = indexToState(stateIdx, M, order)
|
||||
state = zeros(1, order);
|
||||
tmp = stateIdx;
|
||||
|
||||
for k = 1:order
|
||||
state(k) = mod(tmp, M);
|
||||
tmp = floor(tmp / M);
|
||||
end
|
||||
end
|
||||
|
||||
function label = formatState(state)
|
||||
if isempty(state)
|
||||
label = "[]";
|
||||
else
|
||||
label = "[" + join(string(state), " ") + "]";
|
||||
end
|
||||
end
|
||||
|
||||
function bitLabel = bitsFromIndex(bitmap, idx)
|
||||
bitLabel = string(char(bitmap(idx, :) + '0'));
|
||||
end
|
||||
|
||||
function amplitude = indexToPamAmplitude(a, scaling, M)
|
||||
amplitude = (2 * a - (M - 1)) / scaling;
|
||||
end
|
||||
|
||||
function scaling = getEncodedScaling(M, order)
|
||||
h = arrayfun(@(k) nchoosek(order, k), 0:order);
|
||||
numCombinations = M^(order + 1);
|
||||
y = zeros(numCombinations, 1);
|
||||
|
||||
for n = 0:(numCombinations - 1)
|
||||
state = zeros(1, order + 1);
|
||||
tmp = n;
|
||||
|
||||
for k = 1:numel(state)
|
||||
state(k) = mod(tmp, M);
|
||||
tmp = floor(tmp / M);
|
||||
end
|
||||
|
||||
y(n + 1) = sum(h .* state);
|
||||
end
|
||||
|
||||
center = ((M - 1) * sum(h)) / 2;
|
||||
scaling = sqrt(mean((y - center).^2));
|
||||
end
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
M = 8;
|
||||
apply_precode_at_tx = 1;
|
||||
|
||||
bits = Signalgenerator( ...
|
||||
"form", signalform.prms, ...
|
||||
"M", M, ...
|
||||
"order", 15).process();
|
||||
|
||||
symbols = PAMmapper(M,0).map(bits);
|
||||
|
||||
if apply_precode_at_tx
|
||||
symbols_tx = Duobinary().precode(symbols);
|
||||
else
|
||||
symbols_tx = symbols;
|
||||
end
|
||||
|
||||
disp(['Tx Sequenz: -- RMS:',sprintf('%.1f',rms(symbols_tx.signal)),' - - Levels -',num2str(numel(unique(symbols_tx.signal)))]);
|
||||
unique(symbols_tx.signal)
|
||||
disp('- - - - - - - - - -');
|
||||
|
||||
show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241);
|
||||
|
||||
if apply_precode_at_tx
|
||||
% Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6])
|
||||
symbols_db = Duobinary().encode(symbols_tx);
|
||||
|
||||
disp(['DB encoded -- RMS:',sprintf('%.1f',rms(symbols_db.signal)),' - - Levels -',num2str(numel(unique(symbols_db.signal)))]);
|
||||
unique(symbols_db.signal)
|
||||
disp('- - - - - - - - - -');
|
||||
|
||||
% Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4
|
||||
symbols_rx = Duobinary().decode(symbols_db);
|
||||
else
|
||||
symbols_db = Duobinary().encode(symbols_tx);
|
||||
symbols_rx = Duobinary().decode(symbols_db);
|
||||
end
|
||||
|
||||
% Vergleichen von b(n) und d_dec(n)
|
||||
bits_rx = PAMmapper(M,0).demap(symbols_rx);
|
||||
disp(['Wieder normal -- RMS:',sprintf('%.1f',rms(symbols_rx.signal)),' - - Levels -',num2str(numel(unique(symbols_rx.signal)))]);
|
||||
unique(symbols_rx.signal)
|
||||
disp('- - - - - - - - - -');
|
||||
|
||||
|
||||
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
|
||||
|
||||
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
|
||||
|
||||
figure()
|
||||
subplot(1,2,1)
|
||||
histogram(symbols_tx.signal,100,'Normalization','count')
|
||||
|
||||
subplot(1,2,2)
|
||||
histogram(symbols_db.signal,100,'Normalization','count')
|
||||
Reference in New Issue
Block a user