+ duobinary_target now supports memoryless decoding (FFE targets DB response without MLSE) + PAMmapper.quantize now supports custom constellations for quantization + Added a new folder 'Documentations' for pdfs, slides, etc. + Added new FSO evaluation scripts in projects/FSO transmission/Evaluation Scripts + Added ffe_db (rudimentary module, not important anymore)
517 lines
18 KiB
Matlab
517 lines
18 KiB
Matlab
classdef PAMmapper
|
||
%PAMMAPPER Summary of this class goes here
|
||
% Detailed explanation goes here
|
||
|
||
properties
|
||
M
|
||
unipolar
|
||
thresholds
|
||
levels
|
||
scaling
|
||
eth_style
|
||
end
|
||
|
||
methods
|
||
function obj = PAMmapper(M, unipolar, options)
|
||
%PAMMAPPER Construct an instance of this class
|
||
% Detailed explanation goes here
|
||
arguments
|
||
M
|
||
unipolar
|
||
options.eth_style = 0;
|
||
end
|
||
|
||
obj.M = M;
|
||
|
||
obj.unipolar = unipolar;
|
||
|
||
obj.thresholds = obj.get_demodulation_thresholds();
|
||
|
||
obj.levels = obj.get_levels();
|
||
|
||
obj.scaling = obj.get_scaling;%rms(obj.get_levels());
|
||
|
||
obj.eth_style = options.eth_style;
|
||
|
||
end
|
||
|
||
function out = map(obj,signal_in)
|
||
|
||
if isa(signal_in,'Signal')
|
||
signal_in.signal = obj.map_(signal_in.signal);
|
||
|
||
% signal_in = signal_in.normalize("mode","rms");
|
||
lbdesc = ['Map bat stream to PAM ',num2str(obj.M),' symbols'];
|
||
signal_in = signal_in.logbookentry(lbdesc,obj);
|
||
out = signal_in;
|
||
else
|
||
out = obj.map_(signal_in);
|
||
end
|
||
|
||
end
|
||
|
||
function signal_out = demap(obj,signal_in)
|
||
issignalclass = 0;
|
||
|
||
if isa(signal_in,'Signal')
|
||
signalclass = signal_in;
|
||
signal_in = signal_in.signal;
|
||
issignalclass = 1;
|
||
end
|
||
|
||
signal_out = obj.demap_(signal_in);
|
||
|
||
if issignalclass
|
||
lbdesc = ['Demap PAM ',num2str(obj.M),' symbols to bit stream'];
|
||
signal_in = signalclass;
|
||
signal_in = signal_in.logbookentry(lbdesc,obj);
|
||
signal_in.signal = signal_out;
|
||
signal_out = signal_in;
|
||
end
|
||
|
||
end
|
||
|
||
function pam_sig = map_(obj,bitpattern)
|
||
|
||
switch obj.M
|
||
case 2
|
||
% 2-ASK: BPSK / OOK
|
||
if ~obj.eth_style
|
||
pam_sig = bitpattern(:,1);
|
||
if obj.unipolar==0
|
||
pam_sig=2*pam_sig-1;
|
||
end
|
||
else
|
||
pam_sig = -2*bitpattern(:,1) + 1;
|
||
end
|
||
|
||
case 4
|
||
% 4-ASK:
|
||
if ~obj.eth_style
|
||
pam_sig=2*bitpattern(:,1)+(bitpattern(:,1)==bitpattern(:,2));
|
||
|
||
if obj.unipolar==0
|
||
pam_sig=2*pam_sig-3;
|
||
end
|
||
else
|
||
pam_sig = (2*bitpattern(:,1)-1).*(-2*bitpattern(:,2)+3);
|
||
end
|
||
|
||
pam_sig = pam_sig/sqrt(5);
|
||
|
||
case 6
|
||
|
||
if ~obj.eth_style
|
||
|
||
m = 1;
|
||
if size(bitpattern,2)>size(bitpattern,1)
|
||
bitpattern = bitpattern'; %vector aufrecht stellen
|
||
end
|
||
|
||
% LUT based mapping
|
||
for k = 1:5:fix(length(bitpattern)/5)*5
|
||
pam_sig(m:m+1,1) = obj.thresholds(bin2dec(int2str(bitpattern(k:k+4)'))+1,:);
|
||
m = m+2;
|
||
end
|
||
|
||
else
|
||
|
||
bitsPerSymbol = reshape(bitpattern,5,[]).'; % reorder 5 bits per symbol
|
||
normFactor = 1;
|
||
%====================32 QAM===================%
|
||
%=============================================%
|
||
% Coding %
|
||
% 01000 01001 |11001 11000 %
|
||
% | %
|
||
% 01010 01110 01100 |11100 11110 11010 %
|
||
% | %
|
||
% 01011 01111 01101 |11101 11111 11011 %
|
||
% --------------------|------------------- %
|
||
% 00011 00111 00101 |10101 10111 10011 %
|
||
% | %
|
||
% 00010 00110 00100 |10100 10110 10010 %
|
||
% | %
|
||
% 00000 00001 |10001 10000 %
|
||
%=============================================%
|
||
% modulate three LSB first in first Quadrant
|
||
% first bit inverts real part if 0
|
||
% second bit inverts imaginary part if 0
|
||
|
||
LSB_symbols = normFactor*(...
|
||
+(1+1i) *(bitsPerSymbol(:,3)==1 & bitsPerSymbol(:,4)==0 & bitsPerSymbol(:,5)==1)...
|
||
+(3+1i) *(bitsPerSymbol(:,3)==1 & bitsPerSymbol(:,4)==1 & bitsPerSymbol(:,5)==1)...
|
||
+(5+1i) *(bitsPerSymbol(:,3)==0 & bitsPerSymbol(:,4)==1 & bitsPerSymbol(:,5)==1)...
|
||
+(1+3i) *(bitsPerSymbol(:,3)==1 & bitsPerSymbol(:,4)==0 & bitsPerSymbol(:,5)==0)...
|
||
+(3+3i) *(bitsPerSymbol(:,3)==1 & bitsPerSymbol(:,4)==1 & bitsPerSymbol(:,5)==0)...
|
||
+(5+3i) *(bitsPerSymbol(:,3)==0 & bitsPerSymbol(:,4)==1 & bitsPerSymbol(:,5)==0)...
|
||
+(1+5i) *(bitsPerSymbol(:,3)==0 & bitsPerSymbol(:,4)==0 & bitsPerSymbol(:,5)==1)...
|
||
+(3+5i) *(bitsPerSymbol(:,3)==0 & bitsPerSymbol(:,4)==0 & bitsPerSymbol(:,5)==0));
|
||
|
||
Re = real(LSB_symbols);
|
||
Im = imag(LSB_symbols);
|
||
% if first bit== 0 => invert real part
|
||
% if second bit== 0 => invert imag part
|
||
modData2 = Re.*(bitsPerSymbol(:,1)*2-1) + 1i*Im.*(bitsPerSymbol(:,2)*2-1);
|
||
Re = real(modData2(1:end/2));
|
||
Im = imag(modData2(1:end/2));
|
||
pam_sig = zeros(length(Re)*2,1);
|
||
pam_sig(1:2:length(Re)*2) = Re;
|
||
pam_sig(2:2:length(Im)*2) = Im;
|
||
|
||
|
||
end
|
||
|
||
pam_sig = pam_sig/sqrt(10);
|
||
|
||
case 8
|
||
|
||
% 8-ASK:
|
||
if ~obj.eth_style
|
||
x1 = bitpattern(:,1);
|
||
x2 = (bitpattern(:,1)==bitpattern(:,3));
|
||
x3 = x2~=bitpattern(:,2);
|
||
|
||
pam_sig = 4*x1 + 2*x2 + x3;
|
||
|
||
if obj.unipolar==0
|
||
pam_sig=2*pam_sig-7;
|
||
end
|
||
else
|
||
|
||
pam_sig = (bitpattern(:,1)*2-1).*(4+(2*bitpattern(:,2)-1).*(-2*bitpattern(:,3)+3));
|
||
|
||
end
|
||
|
||
pam_sig = pam_sig/sqrt(21);
|
||
|
||
case 16
|
||
% 16-ASK:
|
||
x1 = bitpattern(:,1);
|
||
x2 = (bitpattern(:,1)==bitpattern(:,4));
|
||
x3 = x2~=bitpattern(:,3);
|
||
x4 = x3~=bitpattern(:,2);
|
||
|
||
pam_sig = 8*x1 + 4*x2 + 2*x3 + x4;
|
||
|
||
if obj.unipolar==0
|
||
pam_sig=2*pam_sig-15;
|
||
end
|
||
|
||
end
|
||
end
|
||
|
||
function thres = get_demodulation_thresholds(obj)
|
||
%simply get the obj.thresholdseshold values for PAM
|
||
|
||
%28.03.2023 - Silas Oett. - Extracted from digi_demod.m
|
||
%
|
||
% switch obj.M
|
||
% case 2
|
||
% thres = 0;
|
||
% case 4
|
||
% thres = [-2 0 2];
|
||
% case 6
|
||
% thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1];
|
||
% case 8
|
||
% thres = [-6 -4 -2 0 2 4 6];
|
||
% case 16
|
||
% thres = [-10 -8 -6 -4 -2 0 2 4 6 8 10];
|
||
% end
|
||
|
||
|
||
switch obj.M
|
||
|
||
case 2
|
||
% 2-ASK
|
||
|
||
if obj.unipolar
|
||
thres=0.5;
|
||
else %bi polar
|
||
thres=0;
|
||
end
|
||
|
||
case 4
|
||
% 4-ASK
|
||
if obj.unipolar==0
|
||
thres=[-2,0,2];
|
||
elseif obj.unipolar==1
|
||
thres=[0.5,1.5,2.5];
|
||
end
|
||
|
||
thres = thres .* 1/sqrt(5);
|
||
|
||
case 6 %PAM 6
|
||
|
||
thres = [-3 5;-1 5;-3 -5;-1 -5;-5 3;-5 1;-5 -3;-5 -1;-1 3;-1 1;-1 -3;-1 -1;-3 3;-3 1;-3 -3;-3 -1;3 5;1 5;3 -5;1 -5;5 3;5 1;5 -3;5 -1;1 3;1 1;1 -3;1 -1;3 3;3 1;3 -3;3 -1];
|
||
% thres = [-4 -2 0 2 4];
|
||
% thres = thres ./ sqrt(10);
|
||
|
||
case 8
|
||
% 8-ASK
|
||
if obj.unipolar==0
|
||
thres=-6:2:6;
|
||
elseif obj.unipolar==1
|
||
thres=0.5:6.5;
|
||
end
|
||
|
||
thres=thres./sqrt(21);
|
||
|
||
case 16
|
||
% 16-ASK
|
||
if obj.unipolar==0 && scale_mode==1
|
||
thres=-14:2:14;
|
||
elseif obj.unipolar==1 && scale_mode==1
|
||
thres=0.5:14.5;
|
||
end
|
||
|
||
end
|
||
|
||
end
|
||
|
||
function levels = get_levels(obj)
|
||
switch obj.M
|
||
case 2
|
||
levels = [-1 1];
|
||
case 4
|
||
levels = [-3 -1 1 3];
|
||
case 6
|
||
levels = [-5 -3 -1 1 3 5];
|
||
case 8
|
||
levels = [-7 -5 -3 -1 1 3 5 7];
|
||
case 16
|
||
levels = [-11 -9 -7 -5 -3 -1 1 3 5 7 9 11];
|
||
end
|
||
end
|
||
|
||
function scaling = get_scaling(obj)
|
||
switch obj.M
|
||
case 2
|
||
scaling = 1;
|
||
case 4
|
||
scaling = sqrt(5);
|
||
case 6
|
||
scaling = sqrt(10);
|
||
case 8
|
||
scaling = sqrt(21);
|
||
case 16
|
||
scaling = 1;
|
||
end
|
||
end
|
||
|
||
function [data_out] = demap_(obj,data_in)
|
||
|
||
data_in= data_in';
|
||
|
||
if obj.M ~= 6
|
||
|
||
% create output
|
||
if ~isempty(obj.thresholds)
|
||
a = squeeze(repmat(real(data_in),[1 1 length(obj.thresholds)])); %Eingangssignal in 3 spalten
|
||
b = squeeze(repmat(reshape(obj.thresholds(:).',[1 1 length(obj.thresholds)]),[1 length(data_in) 1])); %Threshold in 3 Spalten
|
||
comp_real = a > b; %check for each symbol/ sampling if it exeeds the obj.thresholdseshold 1, 2 or 3
|
||
|
||
comp_real=repmat(real(data_in),[1 1 length(obj.thresholds)]) > repmat(reshape(obj.thresholds(:).',[1 1 length(obj.thresholds)]),[1 length(data_in) 1]);
|
||
|
||
else
|
||
comp_real=[];
|
||
end
|
||
|
||
s1=size(comp_real,1);
|
||
s2=size(comp_real,2);
|
||
|
||
end
|
||
|
||
switch obj.M
|
||
|
||
case 2
|
||
% 2-ASK
|
||
if ~obj.eth_style
|
||
data_out=comp_real(:,:,1);
|
||
else
|
||
data_out=abs(comp_real(:,:,1)-1);
|
||
end
|
||
|
||
case 4
|
||
% 4-ASK
|
||
if ~obj.eth_style
|
||
data_out=[comp_real(:,:,2); ones(s1,s2) - comp_real(:,:,1) + comp_real(:,:,3)];
|
||
else
|
||
data_out= [(data_in>=0); (abs(data_in)<=1)];
|
||
end
|
||
|
||
case 6
|
||
|
||
if ~obj.eth_style
|
||
|
||
% data_in = data_in/(sqrt(mean(abs(data_in).^2)));
|
||
data_in = data_in*sqrt(10);
|
||
%data_in = data_in*sqrt((5^2 + 3^2 + 1^2 + 5^2 + 3^2 + 1^2)/6);
|
||
|
||
if size(data_in,2) > 1
|
||
data_in = data_in.';
|
||
end
|
||
|
||
if length(data_in)/2 ~= round(length(data_in)/2)
|
||
data_in = [data_in;0];
|
||
end
|
||
|
||
m = 1;
|
||
for n = 1:2:length(data_in)
|
||
dist = sqrt((data_in(n)-obj.thresholds(:,1)).^2+(data_in(n+1)-obj.thresholds(:,2)).^2);
|
||
[~,dd_idx] = min(dist);
|
||
% dec_out(n:n+1) = LUT(dd_idx,:);
|
||
data_out(m:m+4) = bitget(dd_idx-1,5:-1:1);
|
||
m = m+5;
|
||
end
|
||
|
||
else
|
||
|
||
data_in= data_in';
|
||
rxSym = data_in(1:2:end) + 1i*data_in(2:2:end); % 16×1
|
||
|
||
% Decode the sign bits (bits 1 & 2).
|
||
rxBit1 = double(real(rxSym) > 0);
|
||
rxBit2 = double(imag(rxSym) > 0);
|
||
|
||
% Undo the quadrant inversion and normalization.
|
||
rxSym_corr = abs(real(rxSym)) + 1i*abs(imag(rxSym));
|
||
normFactor = 1/sqrt(10);
|
||
rxSym_unscaled = rxSym_corr / normFactor;
|
||
|
||
|
||
cand = [1+1i, 3+1i, 5+1i, 1+3i, 3+3i, 5+3i, 1+5i, 3+5i];
|
||
candBits = [1 0 1;
|
||
1 1 1;
|
||
0 1 1;
|
||
1 0 0;
|
||
1 1 0;
|
||
0 1 0;
|
||
0 0 1;
|
||
0 0 0];
|
||
d = abs(rxSym_unscaled - cand).^2; % 32×8 distances
|
||
[~, idx] = min(d, [], 2);
|
||
rxLSB = candBits(idx,:); % 32×3
|
||
|
||
|
||
decodedSymbols = [rxBit1, rxBit2, rxLSB];
|
||
data_out = reshape(decodedSymbols.', [], 1).';
|
||
|
||
end
|
||
|
||
|
||
|
||
case 8
|
||
% 8-ASK
|
||
if ~obj.eth_style
|
||
data_out=[comp_real(:,:,4);
|
||
comp_real(:,:,1)-comp_real(:,:,3)+comp_real(:,:,5)-comp_real(:,:,7);
|
||
1-comp_real(:,:,2)+comp_real(:,:,6)];
|
||
else
|
||
data_out = [(data_in>=0); (abs(data_in)>(4/sqrt(21))); (abs(data_in)>=(2/sqrt(21)))&(abs(data_in)<=(6/sqrt(21)))];
|
||
end
|
||
|
||
case 16
|
||
% 16-ASK
|
||
data_out=[comp_real(:,:,8);
|
||
comp_real(:,:,1)-comp_real(:,:,3)+comp_real(:,:,5)-comp_real(:,:,7)+comp_real(:,:,9)-comp_real(:,:,11)+comp_real(:,:,13)-comp_real(:,:,15);
|
||
comp_real(:,:,2)-comp_real(:,:,6)+comp_real(:,:,10)-comp_real(:,:,14);
|
||
1-comp_real(:,:,4)+comp_real(:,:,12)];
|
||
end
|
||
|
||
data_out = data_out';
|
||
|
||
|
||
end
|
||
|
||
function [data_out] = decide_pamlevel(obj,data_in,options)
|
||
arguments
|
||
obj
|
||
data_in Signal
|
||
options.symbol_levels = []
|
||
end
|
||
|
||
%A) normally return the preproduct of the decision
|
||
a = squeeze(repmat(real(data_in.signal),[1 1 length(obj.thresholds)])); %Eingangssignal in 3 spalten
|
||
b = squeeze(repmat(reshape(obj.thresholds(:).',[1 1 length(obj.thresholds)]),[1 length(data_in.signal) 1])); %Threshold in 3 Spalten
|
||
comp_real = a > b; %check for each symbol/ sampling if it exeeds the obj.thresholdseshold 1, 2 or 3
|
||
|
||
data_out = data_in;
|
||
data_out.signal = sum(comp_real,2);
|
||
|
||
%Option: return the actual level values/ just map onto given
|
||
%symbol levels
|
||
if ~isempty(options.symbol_levels)
|
||
data_out.signal = options.symbol_levels(data_out.signal+1);
|
||
end
|
||
|
||
end
|
||
|
||
function [out] = separate_pamlevels(obj,data_in)
|
||
%data_in is Signal class
|
||
%A) normally return the preproduct of the decision
|
||
a = squeeze(repmat(real(data_in.signal),[1 1 length(obj.thresholds)])); %Eingangssignal in 3 spalten
|
||
b = squeeze(repmat(reshape(obj.thresholds(:).',[1 1 length(obj.thresholds)]),[1 length(data_in.signal) 1])); %Threshold in 3 Spalten
|
||
comp_real = a > b; %check for each symbol/ sampling if it exeeds the obj.thresholdseshold 1, 2 or 3
|
||
comp_real_sum = sum(comp_real,2);
|
||
|
||
out = NaN(length(data_in),length(obj.thresholds)+1);
|
||
|
||
for idx = 1:length(data_in)
|
||
out(idx,comp_real_sum(idx)+1) = data_in.signal(idx);
|
||
end
|
||
|
||
end
|
||
|
||
function [Signal_out] = quantize(obj,Signal_in,options)
|
||
arguments
|
||
obj
|
||
Signal_in Signal
|
||
options.custom_const = [];
|
||
end
|
||
|
||
if isempty(options.custom_const)
|
||
constellation = obj.get_levels();
|
||
constellation = constellation ./ obj.scaling;
|
||
else
|
||
constellation = options.custom_const;
|
||
end
|
||
|
||
issignalclass = 0;
|
||
if isa(Signal_in,'Signal')
|
||
issignalclass = 1;
|
||
Sig_class = Signal_in;
|
||
Signal_in = Signal_in.signal;
|
||
end
|
||
|
||
[~,high_dim_sig] = max(size(Signal_in));
|
||
[~,high_dim_const] = max(size(constellation));
|
||
|
||
if high_dim_sig == high_dim_const
|
||
Signal_in = Signal_in';
|
||
end
|
||
|
||
dist = abs(Signal_in - constellation);
|
||
[~,symbol_idx] = min(dist,[],2); % decision for closest constellation point
|
||
Signal_out = constellation(symbol_idx);
|
||
Signal_out = reshape(Signal_out,size(Signal_in));
|
||
|
||
if issignalclass
|
||
Sig_class.signal = Signal_out;
|
||
Signal_out = Sig_class;
|
||
end
|
||
|
||
|
||
|
||
end
|
||
|
||
function bitmap = showBitMapping(obj)
|
||
|
||
bitmap = obj.demap((obj.levels ./ obj.scaling)');
|
||
|
||
end
|
||
|
||
end
|
||
|
||
end
|
||
|