Hoffice push
This commit is contained in:
@@ -47,9 +47,9 @@ classdef ChannelFreqResp < handle
|
||||
% Detailed explanation goes here
|
||||
|
||||
arguments
|
||||
options.Nacq = 4096;
|
||||
options.Navg = 30;
|
||||
options.Ncp = 100;
|
||||
options.Nacq = 1024;
|
||||
options.Navg = 64;
|
||||
options.Ncp = 63;
|
||||
options.f_ref;
|
||||
options.f_observe;
|
||||
end
|
||||
@@ -188,8 +188,12 @@ classdef ChannelFreqResp < handle
|
||||
if 0
|
||||
figure(7);hold on;plot(fnew,20*log10(abs(iH)))
|
||||
end
|
||||
|
||||
% iH(1) is DC ---> iH(end) is High Freq.
|
||||
H_inv = [iH(1) iH fliplr(conj(iH)) conj(iH(1))];
|
||||
|
||||
H_inv = [iH(1) iH iH(end) fliplr(conj(iH)) conj(iH(1))];
|
||||
|
||||
obj.H_apply = H_inv;
|
||||
|
||||
Target.signal = real((ifft( ( fft(real( Target.signal )) .* H_inv' ) )));
|
||||
|
||||
@@ -6,6 +6,8 @@ classdef PAMmapper
|
||||
M
|
||||
unipolar
|
||||
thresholds
|
||||
levels
|
||||
scaling
|
||||
end
|
||||
|
||||
methods
|
||||
@@ -17,6 +19,10 @@ classdef PAMmapper
|
||||
|
||||
obj.thresholds = obj.get_demodulation_thresholds();
|
||||
|
||||
obj.levels = obj.get_levels();
|
||||
|
||||
obj.scaling = rms(obj.get_levels());
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -111,6 +117,19 @@ classdef PAMmapper
|
||||
|
||||
%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
|
||||
|
||||
@@ -159,6 +178,22 @@ classdef PAMmapper
|
||||
|
||||
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 [data_out] = demap_(obj,data_in)
|
||||
data_in= data_in';
|
||||
if obj.M ~= 6
|
||||
|
||||
@@ -11,27 +11,17 @@ classdef Duobinary
|
||||
|
||||
function obj = Duobinary()
|
||||
%NAME Construct an instance of this class
|
||||
% Detailed explanation goes here
|
||||
|
||||
arguments
|
||||
|
||||
end
|
||||
|
||||
% %
|
||||
% fn = fieldnames(options);
|
||||
% for n = 1:numel(fn)
|
||||
% try
|
||||
% obj.(fn{n}) = options.(fn{n});
|
||||
% end
|
||||
% end
|
||||
|
||||
% do more stuff
|
||||
|
||||
end
|
||||
|
||||
function signalclass = precode(~,signalclass)
|
||||
function signal = precode(~,signal)
|
||||
|
||||
if isa(signal,'Signal')
|
||||
data = signal.signal;
|
||||
else
|
||||
data = signal;
|
||||
end
|
||||
|
||||
data = signalclass.signal;
|
||||
u = unique(data);
|
||||
M = numel(u);
|
||||
|
||||
@@ -66,21 +56,34 @@ classdef Duobinary
|
||||
bk = bk + b;
|
||||
|
||||
if M == 4
|
||||
bk = bk ./ sqrt(5);
|
||||
bk = bk ./ sqrt(5);
|
||||
elseif M == 6
|
||||
bk = bk ./ sqrt(10);
|
||||
elseif M == 8
|
||||
bk = bk ./ sqrt(21);
|
||||
bk = bk ./ sqrt(21);
|
||||
end
|
||||
|
||||
assert(isequal(unique(bk),u),'Check Duobinary Precoding'); %seems the signal is not the same as before
|
||||
|
||||
|
||||
if isa(signal,'Signal')
|
||||
signal.signal = bk;
|
||||
else
|
||||
signal = bk;
|
||||
end
|
||||
signalclass.signal = bk;
|
||||
|
||||
assert(isequal(unique(signalclass.signal),u),'Check Duobinary Precoding'); %seems the signal is not the same as before
|
||||
|
||||
end
|
||||
|
||||
function signalclass = encode(~,signalclass)
|
||||
|
||||
data = signalclass.signal;
|
||||
function signal = encode(~,signal)
|
||||
|
||||
if isa(signal,'Signal')
|
||||
data = signal.signal;
|
||||
else
|
||||
data = signal;
|
||||
end
|
||||
|
||||
data = data./rms(data);
|
||||
|
||||
u = unique(data);
|
||||
M = numel(u);
|
||||
|
||||
@@ -102,7 +105,7 @@ classdef Duobinary
|
||||
data = data ./ 2;
|
||||
|
||||
assert(isequal((0:M-1)',unique(data)),'Check Duobinary Precoding'); %seems the signal is not unipolar
|
||||
|
||||
|
||||
% duobinary coding (1+D)
|
||||
coeff = [1,1];
|
||||
|
||||
@@ -119,7 +122,11 @@ classdef Duobinary
|
||||
data = data ./ sqrt(10.5); % 15-level constellation weighted with probability after DB code i.e.
|
||||
end
|
||||
|
||||
signalclass.signal = data;
|
||||
if isa(signal,'Signal')
|
||||
signal.signal = data;
|
||||
else
|
||||
signal = data;
|
||||
end
|
||||
|
||||
% Code to evaluate the s in sqrt(s):
|
||||
% M=6;
|
||||
@@ -138,7 +145,7 @@ classdef Duobinary
|
||||
data = signalclass.signal;
|
||||
u = unique(data);
|
||||
I = numel(u); %number of duobinary coded const. points
|
||||
M = (I+1)/2; %PAM-M order
|
||||
M = (I+1)/2; %PAM-M order
|
||||
|
||||
%make unipolar
|
||||
if I == 7
|
||||
@@ -166,11 +173,11 @@ classdef Duobinary
|
||||
data = data - round(mean(data));
|
||||
|
||||
if M == 4
|
||||
data = data ./ sqrt(5);
|
||||
data = data ./ sqrt(5);
|
||||
elseif M == 6
|
||||
data = data ./ sqrt(10);
|
||||
elseif M == 8
|
||||
data = data ./ sqrt(21);
|
||||
data = data ./ sqrt(21);
|
||||
end
|
||||
signalclass.signal = data;
|
||||
|
||||
@@ -180,7 +187,7 @@ classdef Duobinary
|
||||
data = signalclass.signal;
|
||||
u = unique(data);
|
||||
I = numel(u); %number of duobinary coded const. points
|
||||
M = (I+1)/2; %PAM-M order
|
||||
M = (I+1)/2; %PAM-M order
|
||||
|
||||
%make unipolar
|
||||
if I == 7
|
||||
@@ -206,11 +213,11 @@ classdef Duobinary
|
||||
%%FALSCH!
|
||||
for k = 1:length(data)-1
|
||||
|
||||
d_ = data(k+1) - data_out(k);
|
||||
|
||||
[~,b] = min(abs(d_-[0:M-1]));
|
||||
d_ = data(k+1) - data_out(k);
|
||||
|
||||
data_out(k+1) = const(b);
|
||||
[~,b] = min(abs(d_-[0:M-1]));
|
||||
|
||||
data_out(k+1) = const(b);
|
||||
|
||||
end
|
||||
|
||||
@@ -219,13 +226,13 @@ classdef Duobinary
|
||||
data_out = data_out - round(mean(data_out));
|
||||
|
||||
if M == 4
|
||||
data_out = data_out ./ sqrt(5);
|
||||
data_out = data_out ./ sqrt(5);
|
||||
elseif M == 6
|
||||
data_out = data_out ./ sqrt(10);
|
||||
elseif M == 8
|
||||
data_out = data_out ./ sqrt(21);
|
||||
data_out = data_out ./ sqrt(21);
|
||||
end
|
||||
|
||||
|
||||
signalclass.signal = data_out;
|
||||
|
||||
|
||||
|
||||
165
Classes/04_DSP/Sequence Detection/MLSE.m
Normal file
165
Classes/04_DSP/Sequence Detection/MLSE.m
Normal file
@@ -0,0 +1,165 @@
|
||||
classdef MLSE
|
||||
%MLSE calculates the most probable sequence for an input signal with given/ known channel impulse response of any length
|
||||
|
||||
properties(Access=public)
|
||||
M %PAM-M
|
||||
DIR
|
||||
trellis_states
|
||||
duobinary_output
|
||||
end
|
||||
|
||||
methods (Access=public)
|
||||
|
||||
function obj = MLSE(options)
|
||||
%NAME Construct an instance of this class
|
||||
% Detailed explanation goes here
|
||||
|
||||
arguments
|
||||
options.M double = 4;
|
||||
options.DIR double = [1];
|
||||
options.trellis_states double = [-3 -1 1 3];
|
||||
options.duobinary_output logical = false;
|
||||
|
||||
end
|
||||
|
||||
%
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
try
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
end
|
||||
|
||||
% do more stuff
|
||||
|
||||
end
|
||||
|
||||
function signalclass = process(obj,signalclass)
|
||||
|
||||
data_in = signalclass.signal;
|
||||
|
||||
data_out = obj.process_(data_in);
|
||||
|
||||
signalclass.signal = data_out;
|
||||
|
||||
end
|
||||
|
||||
function data_out = process_(obj,data_in)
|
||||
|
||||
|
||||
% remove unnecessary zeros at start of impulse response to keep
|
||||
% number of trellis states minimal
|
||||
DIR_nonzero = find(obj.DIR ~= 0);
|
||||
if DIR_nonzero(1) > 1
|
||||
obj.DIR(1:DIR_nonzero(1)-1) = [];
|
||||
end
|
||||
|
||||
if length(obj.DIR) == 1
|
||||
obj.DIR = [0 obj.DIR];
|
||||
end
|
||||
|
||||
% RMS normalization of input data
|
||||
data_in = data_in ./ rms(data_in);
|
||||
|
||||
% seems to be the only way to use combvec for a flexible amount
|
||||
% of vectors. 'combs' contains all trellis states
|
||||
pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1);
|
||||
pre_comb_cell = mat2cell(pre_comb_mat,ones(1,size(pre_comb_mat,1)),size(pre_comb_mat,2));
|
||||
combs = fliplr(combvec(pre_comb_cell{:}).');
|
||||
|
||||
% Save first and last symbol of each state
|
||||
first_sym = combs(:,1);
|
||||
last_sym = combs(:,end);
|
||||
states = sum(combs,2);
|
||||
|
||||
% Calculate all possible input symbols for the desired impulse
|
||||
% response. Row number is the index of the previous state,
|
||||
% column number is the index of the next state
|
||||
noise_free_received = zeros(length(states),length(states));
|
||||
count_row = 1;
|
||||
count_col = 1;
|
||||
for l1 = 1:length(states)
|
||||
for l2 = 1:length(states)
|
||||
if sum(combs(l2,2:end) == combs(l1,1:end-1)) == size(combs,2)-1
|
||||
noise_free_received(count_row,count_col) = sum(combs(l2,:).*obj.DIR(end:-1:2)) + last_sym(l1)*obj.DIR(1);
|
||||
else
|
||||
noise_free_received(count_row,count_col) = inf;
|
||||
end
|
||||
count_row = count_row + 1;
|
||||
end
|
||||
count_col = count_col + 1;
|
||||
count_row = 1;
|
||||
end
|
||||
|
||||
% match amplitude levels of input signal to those of the calculated ideal symbols
|
||||
% i.e. match the rms values of data_in to noise_free_received
|
||||
if isreal(data_in)
|
||||
if obj.M == round(obj.M)
|
||||
data_in = data_in * rms(noise_free_received,'all','omitnan');
|
||||
end
|
||||
end
|
||||
|
||||
% initilaize the output vector
|
||||
data_out = NaN(size(data_in));
|
||||
|
||||
sum_path_metrics = zeros(length(states),length(states));
|
||||
|
||||
% first trellis path
|
||||
% euclidian distance as path metric
|
||||
path_metrics = (abs(repmat(data_in(1),size(noise_free_received)) - noise_free_received)).^2;
|
||||
sum_path_metrics = sum_path_metrics + path_metrics; % calculation of all possible sum path metrics
|
||||
[sum_path_metrics_res(:,1),path_idx(:,1)] = min(sum_path_metrics,[],2); % find the best path to each state, store sum path metric and predecessor for each state
|
||||
|
||||
% remaining trellis paths
|
||||
for n = 2:length(data_in)
|
||||
sum_path_metrics = repmat(sum_path_metrics_res(:,n-1).',length(states),1);
|
||||
% path_metrics = (repmat(data_in(n),size(noise_free_received)) - noise_free_received).^2;%.*prob_mat;
|
||||
path_metrics = (abs(repmat(data_in(n),size(noise_free_received)) - noise_free_received)).^2;
|
||||
sum_path_metrics = sum_path_metrics + path_metrics;
|
||||
[sum_path_metrics_res(:,n),path_idx(:,n)] = min(sum_path_metrics,[],2);
|
||||
end
|
||||
|
||||
%% trace back
|
||||
ideal_path = NaN(1,length(data_in)+1);
|
||||
% find ideal trellis path by going through the trellis
|
||||
% backwards
|
||||
[~,ideal_path(length(data_in)+1)] = min(sum_path_metrics_res(:,length(data_in)));
|
||||
|
||||
% starting with the state that has the lowest sum path
|
||||
% metric, follow the stored information about the
|
||||
% predecessor
|
||||
for h = length(data_in):-1:1
|
||||
ideal_path(h) = path_idx(ideal_path(h+1),h);
|
||||
end
|
||||
|
||||
idx_out = ideal_path(1:length(data_in));
|
||||
|
||||
if obj.duobinary_output
|
||||
%use duobinary encoder, output is already scaled inside this
|
||||
%one
|
||||
data_out = Duobinary().encode(first_sym(idx_out));
|
||||
else
|
||||
%
|
||||
data_out(1:length(data_in)) = first_sym(idx_out);
|
||||
% scale to rms = 1 using the standard sqrt() expressions
|
||||
if obj.M == 4
|
||||
data_out = data_out./sqrt(5);
|
||||
elseif obj.M == 6
|
||||
data_out = data_out./sqrt(10);
|
||||
elseif obj.M == 8
|
||||
data_out = data_out./sqrt(21);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
methods (Access=private)
|
||||
% Cant be seen from outside! So put all your functions here that can/
|
||||
% shall not be called from outside
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user