Files
imdd_silas/Classes/01_transmit/PAMmapper.m
2026-03-26 16:04:30 +01:00

626 lines
23 KiB
Matlab
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 [ax, mapping_table] = plotBitMapping(obj)
[constellation, bitmap] = obj.getConstellationBitMapping();
constellation = constellation .* obj.get_scaling;
bit_labels = string(cellstr(char(bitmap + '0')));
if obj.M == 6
[~, order] = sortrows([-constellation(:,2), constellation(:,1)]);
constellation = constellation(order, :);
bit_labels = bit_labels(order);
mapping_table = table(constellation(:,1), constellation(:,2), bit_labels, ...
'VariableNames', {'symbol_1', 'symbol_2', 'bits'});
fig = figure("Name", sprintf("PAM-%d Bit Mapping", obj.M));
ax = axes(fig);
scatter(ax, constellation(:,1), constellation(:,2), 60, "filled");
hold(ax, "on");
grid(ax, "on");
box(ax, "on");
x_step = min(diff(unique(constellation(:,1))));
y_step = min(diff(unique(constellation(:,2))));
text(ax, constellation(:,1), constellation(:,2) + 0.18*y_step, bit_labels, ...
"HorizontalAlignment", "center", "FontName", "Consolas");
xlabel(ax, "Symbol 1");
ylabel(ax, "Symbol 2");
title(ax, sprintf("PAM-%d Mapping", obj.M));
axis(ax, "equal");
xticks(ax, unique(constellation(:,1)));
yticks(ax, unique(constellation(:,2)));
xlim(ax, [min(constellation(:,1)) - 0.8*x_step, max(constellation(:,1)) + 1.4*x_step]);
ylim(ax, [min(constellation(:,2)) - 0.6*y_step, max(constellation(:,2)) + 0.9*y_step]);
else
constellation = constellation(:);
mapping_table = table(constellation, bit_labels, ...
'VariableNames', {'symbol', 'bits'});
fig = figure("Name", sprintf("PAM-%d Bit Mapping", obj.M));
ax = axes(fig);
scatter(ax, constellation, zeros(size(constellation)), 60, "filled");
hold(ax, "on");
grid(ax, "on");
box(ax, "on");
text(ax, constellation, 0.08*ones(size(constellation)), bit_labels, ...
"HorizontalAlignment", "center", "FontName", "Consolas");
xlabel(ax, "Symbol");
title(ax, sprintf("PAM-%d Mapping", obj.M));
yticks(ax, []);
ylim(ax, [-0.2 0.2]);
xticks(ax, constellation);
if numel(constellation) > 1
x_step = min(diff(unique(constellation)));
else
x_step = 1;
end
xlim(ax, [min(constellation) - 0.8*x_step, max(constellation) + 0.8*x_step]);
end
if nargout == 0
disp(mapping_table);
end
end
function bitmap = showBitMapping(obj)
[~, bitmap] = obj.getConstellationBitMapping();
end
function [Gp, details] = graypenalty(obj)
[constellation, bit_labels] = obj.getConstellationBitMapping();
num_points = size(constellation, 1);
dist2 = inf(num_points);
for idx = 1:num_points
delta = constellation - constellation(idx, :);
dist2(idx, :) = sum(delta.^2, 2).';
end
dist2(1:num_points+1:end) = inf;
min_dist2 = min(dist2(:));
is_neighbor = abs(dist2 - min_dist2) < 1e-12;
hamming_dist = zeros(num_points);
for idx = 1:num_points
hamming_dist(idx, :) = sum(bit_labels ~= bit_labels(idx, :), 2).';
end
Gp = sum(hamming_dist(is_neighbor)) / nnz(is_neighbor);
if nargout > 1
details = struct( ...
"constellation", constellation, ...
"bit_labels", bit_labels, ...
"neighbor_mask", is_neighbor, ...
"nearest_distance", sqrt(min_dist2));
end
end
function [constellation, bitmap] = getConstellationBitMapping(obj)
if obj.M == 6
constellation = obj.thresholds ./ obj.scaling;
bitmap = obj.demap(reshape(constellation.', [], 1));
bitmap = reshape(bitmap, 5, []).';
else
constellation = (obj.levels ./ obj.scaling)';
bitmap = obj.demap(constellation);
end
end
end
end