Merge branch 'main' of https://cau-git.rz.uni-kiel.de/nt/mitarbeiter/silas/imdd_simulation
This commit is contained in:
@@ -359,10 +359,10 @@ classdef Signal
|
||||
end
|
||||
|
||||
if options.normalizeToNyquist == 0
|
||||
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,obj.fs,"centered","psd","mean");
|
||||
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,obj.fs,"centered","power","mean");
|
||||
w = w.*1e-9;
|
||||
else
|
||||
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,"centered","psd","mean");
|
||||
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,"centered","power","mean");
|
||||
end
|
||||
|
||||
if options.normalizeTo0dB
|
||||
@@ -411,8 +411,9 @@ classdef Signal
|
||||
try
|
||||
ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]);
|
||||
catch
|
||||
ylim([min(floor(min(p_dbm))-3, ax.YLim(1)), max(ceil(max(p_dbm))+3, ax.YLim(2))]);
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
end
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
yticks(-200:10:10);
|
||||
grid on; grid minor;
|
||||
legend
|
||||
@@ -655,13 +656,22 @@ classdef Signal
|
||||
end
|
||||
|
||||
%%
|
||||
function [obj,S,isFlipped] = tsynch(obj,options)
|
||||
function [obj,S,isFlipped,sequenceFound] = tsynch(obj,options)
|
||||
% time sync and cut
|
||||
arguments
|
||||
obj Signal
|
||||
options.reference Signal
|
||||
options.fs_ref = 0;
|
||||
options.debug_plots = 0;
|
||||
end
|
||||
|
||||
|
||||
|
||||
S = {};
|
||||
isFlipped=0;
|
||||
sequenceFound = 0;
|
||||
|
||||
|
||||
|
||||
%normalize the signal
|
||||
a = obj.normalize("mode","oneone").signal;
|
||||
@@ -679,29 +689,46 @@ classdef Signal
|
||||
|
||||
%estimate start pos of signal
|
||||
maxpeaknum = floor(length(a)/length(b));
|
||||
[pks,pkpos] = findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend');
|
||||
try
|
||||
[pks,pkpos,w,p] = findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend');
|
||||
catch
|
||||
warning(['Error in findpeaks, ususally the seuqnece is too short. Max peak num: ', num2str(maxpeaknum)]);
|
||||
return
|
||||
end
|
||||
|
||||
if mean(w) > 10 || mean(p) > 10
|
||||
return
|
||||
else
|
||||
sequenceFound = 1;
|
||||
end
|
||||
|
||||
if options.debug_plots
|
||||
figure()
|
||||
findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend')
|
||||
end
|
||||
|
||||
|
||||
|
||||
shifts = lags(pkpos);
|
||||
|
||||
shifts = shifts(shifts>=0);
|
||||
|
||||
S = {};
|
||||
isFlipped=0;
|
||||
|
||||
if numel(shifts) > 0
|
||||
|
||||
%Cut occurences of ref signal from signal (only positive shifts)
|
||||
if all(sign(co(pkpos)))
|
||||
isFlipped = 1;
|
||||
end
|
||||
|
||||
for c = shifts(shifts>=0)
|
||||
sig = obj.delay(-c,'mode','samples');
|
||||
sig.signal = sig.signal(1:length(b));
|
||||
sig.signal = sig.signal(1:length(b)).*-isFlipped;
|
||||
S{end+1,1} = sig;
|
||||
end
|
||||
|
||||
%
|
||||
|
||||
if all(sign(co(pkpos)))
|
||||
isFlipped = 1;
|
||||
end
|
||||
|
||||
|
||||
%return/keep the sinal with the highest correlation (only within positive shifts)
|
||||
[~,idx]=max(pks(shifts>=0));
|
||||
@@ -722,8 +749,7 @@ classdef Signal
|
||||
end
|
||||
|
||||
%plot all synced signals and the ref signal
|
||||
debug = 0;
|
||||
if debug
|
||||
if options.debug_plots
|
||||
figure;hold on;
|
||||
for i = 1:size(S,1)
|
||||
plot(S{i}.normalize('mode','oneone').signal(1000:1100),'LineWidth',0.1,'Color',[0.2157 0.4941 0.7216]);
|
||||
@@ -735,6 +761,12 @@ classdef Signal
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
%%
|
||||
function obj = filter(obj,a,b)
|
||||
|
||||
@@ -850,7 +882,10 @@ classdef Signal
|
||||
sig = obj.signal;
|
||||
end
|
||||
|
||||
x = (sig); %% make input signal rea)l
|
||||
startpos = floor(0.1*length(sig));
|
||||
endpos = floor(0.9*length(sig));
|
||||
endpos = min(endpos,startpos+200000);
|
||||
x = sig(startpos:endpos); %% make input signal rea)l
|
||||
|
||||
x = resample(x,fsym*histpoints_horizontal/2,obj.fs); %% up sample to original fsym rate
|
||||
|
||||
|
||||
@@ -241,10 +241,13 @@ classdef ChannelFreqResp < handle
|
||||
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
|
||||
|
||||
%%% plot for publication
|
||||
figure(1234);hold all;box on;title('Magnitude Freq. Response');
|
||||
%xlim([0.2 .5*max(obj.faxis)*1e-9]);
|
||||
%ylim([-40, 2]);
|
||||
Havg_smooth = smooth(Havg,50);
|
||||
figure(1234);
|
||||
hold all;
|
||||
box on;
|
||||
title('Magnitude Freq. Response');
|
||||
xlim([0.2 .5*max(obj.faxis)*1e-9]);
|
||||
ylim([-40, 2]);
|
||||
% Havg_smooth = smooth(Havg,10);
|
||||
symaxis = (obj.faxis-(obj.f_ref/2))/1e9;
|
||||
Havg = fftshift(Havg);
|
||||
%Havg = smooth(Havg);
|
||||
|
||||
@@ -8,28 +8,38 @@ classdef PAMmapper
|
||||
thresholds
|
||||
levels
|
||||
scaling
|
||||
eth_style
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = PAMmapper(M, unipolar)
|
||||
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 = 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 = 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);
|
||||
@@ -37,19 +47,19 @@ classdef PAMmapper
|
||||
else
|
||||
out = 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;
|
||||
signalclass = signal_in;
|
||||
signal_in = signal_in.signal;
|
||||
issignalclass = 1;
|
||||
end
|
||||
|
||||
signal_out = obj.demap_(signal_in);
|
||||
signal_out = obj.demap_(signal_in);
|
||||
|
||||
if issignalclass
|
||||
lbdesc = ['Demap PAM ',num2str(obj.M),' symbols to bit stream'];
|
||||
@@ -58,7 +68,7 @@ classdef PAMmapper
|
||||
signal_in.signal = signal_out;
|
||||
signal_out = signal_in;
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
function pam_sig = map_(obj,bitpattern)
|
||||
@@ -66,50 +76,114 @@ classdef PAMmapper
|
||||
switch obj.M
|
||||
case 2
|
||||
% 2-ASK: BPSK / OOK
|
||||
pam_sig=bitpattern(:,1);
|
||||
|
||||
if obj.unipolar==0
|
||||
pam_sig=2*pam_sig-1;
|
||||
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:
|
||||
pam_sig=2*bitpattern(:,1)+(bitpattern(:,1)==bitpattern(:,2));
|
||||
if ~obj.eth_style
|
||||
pam_sig=2*bitpattern(:,1)+(bitpattern(:,1)==bitpattern(:,2));
|
||||
|
||||
if obj.unipolar==0
|
||||
pam_sig=2*pam_sig-3;
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
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:
|
||||
x1 = bitpattern(:,1);
|
||||
x2 = (bitpattern(:,1)==bitpattern(:,3));
|
||||
x3 = x2~=bitpattern(:,2);
|
||||
if ~obj.eth_style
|
||||
x1 = bitpattern(:,1);
|
||||
x2 = (bitpattern(:,1)==bitpattern(:,3));
|
||||
x3 = x2~=bitpattern(:,2);
|
||||
|
||||
pam_sig = 4*x1 + 2*x2 + x3;
|
||||
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));
|
||||
|
||||
if obj.unipolar==0
|
||||
pam_sig=2*pam_sig-7;
|
||||
end
|
||||
|
||||
pam_sig = pam_sig/sqrt(21);
|
||||
|
||||
|
||||
case 16
|
||||
% 16-ASK:
|
||||
x1 = bitpattern(:,1);
|
||||
@@ -143,7 +217,7 @@ classdef PAMmapper
|
||||
% case 16
|
||||
% thres = [-10 -8 -6 -4 -2 0 2 4 6 8 10];
|
||||
% end
|
||||
|
||||
|
||||
|
||||
switch obj.M
|
||||
|
||||
@@ -166,12 +240,12 @@ classdef PAMmapper
|
||||
|
||||
thres = thres .* 1/sqrt(5);
|
||||
|
||||
case 6 %PAM 6
|
||||
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
|
||||
@@ -179,7 +253,7 @@ classdef PAMmapper
|
||||
elseif obj.unipolar==1
|
||||
thres=0.5:6.5;
|
||||
end
|
||||
|
||||
|
||||
thres=thres./sqrt(21);
|
||||
|
||||
case 16
|
||||
@@ -210,65 +284,115 @@ classdef PAMmapper
|
||||
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
|
||||
|
||||
data_out=comp_real(:,:,1);
|
||||
if ~obj.eth_style
|
||||
data_out=comp_real(:,:,1);
|
||||
else
|
||||
data_out=abs(comp_real(:,:,1)-1);
|
||||
end
|
||||
|
||||
case 4
|
||||
% 4-ASK
|
||||
|
||||
data_out=[comp_real(:,:,2); ones(s1,s2) - comp_real(:,:,1) + comp_real(:,:,3)];
|
||||
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
|
||||
|
||||
data_in = data_in/(sqrt(mean(abs(data_in).^2)));
|
||||
data_in = data_in*sqrt(10);
|
||||
if ~obj.eth_style
|
||||
|
||||
data_in = data_in/(sqrt(mean(abs(data_in).^2)));
|
||||
data_in = data_in*sqrt(10);
|
||||
|
||||
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).';
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
case 8
|
||||
% 8-ASK
|
||||
data_out=[comp_real(:,:,4);
|
||||
comp_real(:,:,1)-comp_real(:,:,3)+comp_real(:,:,5)-comp_real(:,:,7);
|
||||
1-comp_real(:,:,2)+comp_real(:,:,6)];
|
||||
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
|
||||
@@ -289,21 +413,21 @@ classdef PAMmapper
|
||||
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)
|
||||
@@ -312,7 +436,7 @@ classdef PAMmapper
|
||||
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);
|
||||
comp_real_sum = sum(comp_real,2);
|
||||
|
||||
out = NaN(length(data_in),length(obj.thresholds)+1);
|
||||
|
||||
@@ -323,7 +447,7 @@ classdef PAMmapper
|
||||
end
|
||||
|
||||
function [Signal_out] = quantize(obj,Signal_in)
|
||||
|
||||
|
||||
constellation = obj.get_levels();
|
||||
constellation = constellation ./ rms(constellation);
|
||||
|
||||
@@ -331,9 +455,9 @@ classdef PAMmapper
|
||||
if isa(Signal_in,'Signal')
|
||||
issignalclass = 1;
|
||||
Sig_class = Signal_in;
|
||||
Signal_in = Signal_in.signal;
|
||||
Signal_in = Signal_in.signal;
|
||||
end
|
||||
|
||||
|
||||
[~,high_dim_sig] = max(size(Signal_in));
|
||||
[~,high_dim_const] = max(size(constellation));
|
||||
|
||||
@@ -346,7 +470,7 @@ classdef PAMmapper
|
||||
Signal_out = constellation(symbol_idx);
|
||||
Signal_out = reshape(Signal_out,size(Signal_in));
|
||||
|
||||
if issignalclass
|
||||
if issignalclass
|
||||
Sig_class.signal = Signal_out;
|
||||
Signal_out = Sig_class;
|
||||
end
|
||||
@@ -355,6 +479,10 @@ classdef PAMmapper
|
||||
|
||||
end
|
||||
|
||||
function bitmap = showBitMapping(obj)
|
||||
bitmap = obj.demap([obj.levels ./ obj.scaling]');
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -71,18 +71,17 @@ classdef PAMsource
|
||||
function [digi_sig,symbols,bits] = process(obj)
|
||||
|
||||
%%%%% PRBS Generation in correct shape for Modulation Format %%%%%%
|
||||
O = obj.order; %order of prbs
|
||||
N = 2^(O-1); %length of prbs
|
||||
[~,seed] = prbs(O,1); %initialize first seed of prbs
|
||||
bitpattern=[];
|
||||
|
||||
if obj.useprbs
|
||||
% for i = 1:log2(obj.M)
|
||||
% [bitpattern(:,i),seed] = prbs(O,N,seed);
|
||||
% end
|
||||
% O = obj.order; %order of prbs
|
||||
% N = 2^(O-1); %length of prbs
|
||||
% [~,seed] = prbs(O,1); %initialize first seed of prbs
|
||||
% % for i = 1:log2(obj.M)
|
||||
% % [bitpattern(:,i),seed] = prbs(O,N,seed);
|
||||
% % end
|
||||
|
||||
%%%%% MOVE-IT PRMS %%%%
|
||||
|
||||
state = struct();
|
||||
|
||||
para = struct();
|
||||
@@ -103,6 +102,8 @@ classdef PAMsource
|
||||
para.reset_prms = 0;
|
||||
para.method = 1;
|
||||
|
||||
|
||||
|
||||
data_in = [];
|
||||
global loop;
|
||||
loop = 0;
|
||||
@@ -116,6 +117,7 @@ classdef PAMsource
|
||||
else
|
||||
s = RandStream('twister','Seed',obj.randkey);
|
||||
for i = 1:log2(obj.M)
|
||||
N = 2^(obj.order-1); %length of prbs
|
||||
bitpattern(:,i) = randi(s,[0 1], N, 1);
|
||||
end
|
||||
end
|
||||
@@ -151,23 +153,52 @@ classdef PAMsource
|
||||
sym_max = max(symbols.signal);
|
||||
end
|
||||
|
||||
% symbols.move_it_spectrum("fignum",222,"displayname","Symbols only");
|
||||
|
||||
|
||||
% symbols.spectrum("fignum",222,"displayname","Symbols only","normalizeTo0dB",1);
|
||||
|
||||
|
||||
%%%%% Pulse-forming %%%%%%
|
||||
if obj.applypulseform
|
||||
%%% MY CODE
|
||||
digi_sig = obj.pulseformer.process(symbols);
|
||||
|
||||
%%% MOVEIT WRAPPER BETA
|
||||
% symbols.spectrum("fignum",111,"displayname","1) RAW SIGNAL");
|
||||
%
|
||||
% pulsf = Moveit_wrapper('pulsef');
|
||||
% pulsf.para.alpharacos = obj.pulseformer.alpha;
|
||||
% pulsf.para.f_sym = obj.fsym;
|
||||
% pulsf.para.fs = obj.fsym;
|
||||
% pulsf.para.pulse = 2;
|
||||
%
|
||||
% digi_sig = pulsf.process(symbols);
|
||||
|
||||
% Design raised cosine filter with given order in symbols
|
||||
% digi_sig = symbols;
|
||||
% sps = 4;
|
||||
% nsym = 128;
|
||||
% rctFilt3 = comm.RaisedCosineTransmitFilter(...
|
||||
% Shape='Square root', ...
|
||||
% RolloffFactor=1, ...
|
||||
% FilterSpanInSymbols=nsym, ...
|
||||
% OutputSamplesPerSymbol=sps);
|
||||
%
|
||||
% digi_sig.signal = rctFilt3([symbols.signal; zeros(nsym/2,1)]);
|
||||
% % Correct for propagation delay by removing filter transients
|
||||
% fltDelay = nsym / (2*obj.fsym);
|
||||
% digi_sig.signal = digi_sig.signal(fltDelay*sps*obj.fsym+1:end);
|
||||
%
|
||||
% digi_sig.fs = sps .* symbols.fs;
|
||||
|
||||
|
||||
else
|
||||
digi_sig = symbols;
|
||||
end
|
||||
|
||||
%%%%% Re-sample to f DAC %%%%%%
|
||||
digi_sig = digi_sig.resample("fs_in",digi_sig.fs,"fs_out",obj.fs_out,"n",10,"beta",5);
|
||||
|
||||
% digi_sig.spectrum("fignum",111,"displayname","after pulseforming");
|
||||
%%%%% Re-sample to f DAC %%%%%%
|
||||
n = 10;
|
||||
digi_sig = digi_sig.resample("fs_in",digi_sig.fs,"fs_out",obj.fs_out,"n",n,"beta",5);
|
||||
% digi_sig.spectrum("fignum",111,"displayname",['3) Shaped + Resampled; n: ',num2str(n)]);
|
||||
|
||||
|
||||
%%%%% Hard clip digital signal to PAM range before DAC %%%%%%
|
||||
if obj.applyclipping
|
||||
|
||||
@@ -3,13 +3,17 @@ classdef Pulseformer
|
||||
% Detailed explanation goes here
|
||||
|
||||
properties(Access=public)
|
||||
fdac
|
||||
|
||||
end
|
||||
properties(Access=private)
|
||||
properties(Access=public)
|
||||
fs
|
||||
fsym
|
||||
matched_sps
|
||||
output_sps
|
||||
pulse
|
||||
pulselength
|
||||
rrcalpha
|
||||
alpha
|
||||
matched
|
||||
end
|
||||
|
||||
methods (Access=public)
|
||||
@@ -18,11 +22,14 @@ classdef Pulseformer
|
||||
% Detailed explanation goes here
|
||||
|
||||
arguments
|
||||
options.fdac double
|
||||
options.fs double
|
||||
options.fsym double
|
||||
options.pulse pulseform = pulseform.rrc
|
||||
options.matched_sps double
|
||||
options.output_sps double
|
||||
options.pulse pulseform = pulseform.rc
|
||||
options.pulselength double {mustBeInteger} = 32
|
||||
options.rrcalpha double = 0.05
|
||||
options.alpha double = 0.05
|
||||
options.matched = 0;
|
||||
end
|
||||
|
||||
%
|
||||
@@ -47,7 +54,11 @@ classdef Pulseformer
|
||||
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
||||
|
||||
% write fs to signal
|
||||
signalclass_in.fs = obj.fdac;
|
||||
if obj.matched
|
||||
signalclass_in.fs = obj.fsym .* obj.output_sps;%.* (obj.fdac./obj.fsym);
|
||||
else
|
||||
signalclass_in.fs = obj.fs;%.* (obj.fdac./obj.fsym);
|
||||
end
|
||||
|
||||
% write to output
|
||||
signalclass_out = signalclass_in;
|
||||
@@ -74,60 +85,99 @@ classdef Pulseformer
|
||||
data_out
|
||||
end
|
||||
|
||||
if ~rem(obj.fdac,obj.fsym)
|
||||
if ~isempty(obj.output_sps)
|
||||
obj.fsym = obj.fsym.*obj.output_sps;
|
||||
end
|
||||
|
||||
if ~rem(obj.fs,obj.fsym)
|
||||
%ist ein Vielfaches
|
||||
sps = obj.fdac / obj.fsym;
|
||||
up = sps;
|
||||
dn = 1;
|
||||
sps = obj.fs / obj.fsym;
|
||||
p = sps;
|
||||
q = 1;
|
||||
else
|
||||
%ist kein Vielfaches
|
||||
up = obj.fdac / gcd(obj.fdac, obj.fsym);
|
||||
dn = obj.fsym / gcd(obj.fdac, obj.fsym);
|
||||
sps= up;
|
||||
p = obj.fsym / gcd(obj.fs, obj.fsym); %upsampling p->->->
|
||||
q = obj.fs/ gcd(obj.fs, obj.fsym); %downsampling <-q
|
||||
sps= q; %sps während dem pulse shaping
|
||||
end
|
||||
|
||||
if obj.pulse == pulseform.rrc
|
||||
%Bau das Filter (hier rrc)
|
||||
racos_len = obj.pulselength*2;
|
||||
alpha = obj.rrcalpha;
|
||||
h = rcosdesign(alpha,racos_len,sps,"normal");
|
||||
h = h./ max(h);
|
||||
if obj.pulse == pulseform.rc
|
||||
filtertype = 'normal';
|
||||
elseif pulseform.rrc
|
||||
filtertype = 'sqrt';
|
||||
end
|
||||
|
||||
% Apply filter the long way (from move_it)
|
||||
% block length in samples
|
||||
|
||||
data_in = data_in';
|
||||
blen = length(data_in)*sps;
|
||||
|
||||
% oversample symbol sequence
|
||||
|
||||
symbolov=zeros(size(data_in,1),blen);
|
||||
symbolov(:,1:sps:blen-sps+1)=data_in;
|
||||
%Bau das Filter (hier rc)
|
||||
racos_len = obj.pulselength*2;
|
||||
h = rcosdesign(obj.alpha,racos_len,sps,filtertype);
|
||||
% h = h./ max(h);
|
||||
|
||||
H=fft(h,blen);
|
||||
|
||||
% Convolution of Bit sequence with impulse response
|
||||
if obj.matched
|
||||
h = conj(fliplr(h));
|
||||
end
|
||||
|
||||
manual_cyclic_convolution = 0;
|
||||
upsample_filter = 0;
|
||||
upfirdn_convolution = 1;
|
||||
|
||||
% cyclic convolution
|
||||
data_out=ifft( fft(symbolov.') .* repmat( H,size(data_in,1),1 ).' ).';
|
||||
if manual_cyclic_convolution
|
||||
|
||||
data_out = circshift(data_out,[0 -(obj.pulselength*sps)]);
|
||||
% Apply filter the long way (from move_it)
|
||||
data_in = data_in';
|
||||
blen = length(data_in)*sps;
|
||||
|
||||
% oversample symbol sequence
|
||||
symbolov=zeros(size(data_in,1),blen);
|
||||
symbolov(:,1:sps:blen-sps+1)=data_in;
|
||||
H=fft(h,blen);
|
||||
|
||||
% Convolution of Bit sequence with impulse response
|
||||
data_out=ifft( fft(symbolov.') .* repmat( H,size(data_in,1),1 ).' ).';
|
||||
data_out = circshift(data_out,[0 -(obj.pulselength*sps)]);
|
||||
|
||||
if rem(obj.fs,obj.fsym)
|
||||
data_out = data_out(1:q:end);
|
||||
end
|
||||
|
||||
if rem(obj.fdac,obj.fsym)
|
||||
data_out = data_out(1:dn:end); %!
|
||||
end
|
||||
|
||||
if upsample_filter
|
||||
data_out_ = upsample(data_in,p);
|
||||
|
||||
% %Apply Filter using Matlab build in fctn.
|
||||
% h = rcosdesign(alpha,racos_len,sps);
|
||||
% h = h./ max(h);
|
||||
%data_out_ = upfirdn(data_in,h,up,dn);
|
||||
%
|
||||
% %cut signal, which is longer due to fir filter
|
||||
% st = round(up/dn*racos_len/2); %we need to cut y_out
|
||||
% en = round(st + (length(data_in)*up/dn) -1);
|
||||
% data_out = data_out(st:en);
|
||||
mfOutput = filter(h, 1, data_out_); % Matched filter output
|
||||
|
||||
figure()
|
||||
hold on
|
||||
stem(mfOutput(1:1000),'Marker','o','MarkerSize',1,'LineStyle','-','LineWidth',1);
|
||||
stem(data_out_(1:1000),'Marker','o','MarkerSize',1,'LineStyle','-','LineWidth',1);
|
||||
|
||||
|
||||
end
|
||||
|
||||
if upfirdn_convolution
|
||||
|
||||
%Apply Filter using Matlab build in fctn.
|
||||
data_out_ = upfirdn(data_in,h,p,q);
|
||||
|
||||
%cut signal, which is longer due to fir filter
|
||||
st = round(p/q*racos_len/2); %we need to cut y_out
|
||||
en = round(st + (length(data_in)*p/q));
|
||||
data_out = data_out_(st:en);
|
||||
|
||||
end
|
||||
|
||||
if upfirdn_convolution && manual_cyclic_convolution
|
||||
figure()
|
||||
subplot(2,1,1)
|
||||
title("Convolution vs. Upfirdn and Cut")
|
||||
hold on
|
||||
% plot(data_out_(1:200),'DisplayName','Matlab upfirdn');
|
||||
plot(data_out(1:200),'DisplayName','By Hand cyclic convolution')
|
||||
subplot(2,1,2)
|
||||
hold on
|
||||
plot(data_out(1:2000),'DisplayName','OUT');
|
||||
plot(data_in(1:2000),'DisplayName','IN');
|
||||
end
|
||||
|
||||
%scaling?! see pulsef module line 696
|
||||
% scale = max(max([abs(real(data_out)) abs(imag(data_out))])); %find max value from real and imag part
|
||||
@@ -136,7 +186,7 @@ classdef Pulseformer
|
||||
data_out = data_out';
|
||||
|
||||
%Check output integrity
|
||||
if abs(round(up/dn * length(data_in)) - length(data_out)) > 4
|
||||
if abs(round(p/q * length(data_in)) - length(data_out)) > 4
|
||||
warning('Check signal length after pulse shaping');
|
||||
%disp('Check signal length after pulse shaping');
|
||||
end
|
||||
|
||||
@@ -26,7 +26,9 @@ classdef Duobinary
|
||||
M = numel(u);
|
||||
|
||||
%make unipolar
|
||||
if M == 4
|
||||
if M == 2
|
||||
data = data;
|
||||
elseif M == 4
|
||||
data = data .* sqrt(5);
|
||||
elseif M == 6
|
||||
data = data .* sqrt(10);
|
||||
@@ -59,7 +61,9 @@ classdef Duobinary
|
||||
bk = bk .* 2;
|
||||
bk = bk + b;
|
||||
|
||||
if M == 4
|
||||
if M == 2
|
||||
bk = bk;
|
||||
elseif M == 4
|
||||
bk = bk ./ sqrt(5);
|
||||
elseif M == 6
|
||||
bk = bk ./ sqrt(10);
|
||||
@@ -100,7 +104,9 @@ classdef Duobinary
|
||||
end
|
||||
|
||||
%make unipolar
|
||||
if options.M == 4
|
||||
if options.M == 2
|
||||
data = data;
|
||||
elseif options.M == 4
|
||||
data = data .* sqrt(5);
|
||||
elseif options.M == 6
|
||||
data = data .* sqrt(10);
|
||||
@@ -140,7 +146,9 @@ classdef Duobinary
|
||||
mean_power = sum((unique_points .^ 2) .* probabilities);
|
||||
scaling_factor = sqrt(mean_power);
|
||||
|
||||
if options.M == 4
|
||||
if options.M == 2
|
||||
data = data ./ sqrt(0.5);
|
||||
elseif options.M == 4
|
||||
data = data ./ sqrt(2.5); % 7-level constellation weighted with probability after DB code i.e. mean([-3 3 -2 -2 2 2 -1 -1 -1 1 1 1 0 0 0 0].^2) = 2.5 --> sqrt(2.5) == rms(constellation)
|
||||
elseif options.M == 6
|
||||
data = data ./ sqrt(5.8);
|
||||
@@ -186,7 +194,9 @@ classdef Duobinary
|
||||
end
|
||||
|
||||
%make unipolar
|
||||
if I == 7 || I == 6
|
||||
if I == 3
|
||||
data = data .* 0.5;
|
||||
elseif I == 7 || I == 6
|
||||
data = data .* sqrt(2.5);
|
||||
elseif I == 11
|
||||
data = data .* sqrt(5.8);
|
||||
@@ -206,7 +216,9 @@ classdef Duobinary
|
||||
data = data .* 2;
|
||||
data = data - round(mean(data));
|
||||
|
||||
if M == 4
|
||||
if M == 2
|
||||
data = data;
|
||||
elseif M == 4
|
||||
data = data ./ sqrt(5);
|
||||
elseif M == 6
|
||||
data = data ./ sqrt(10);
|
||||
|
||||
@@ -101,6 +101,17 @@ classdef FFE < handle
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
|
||||
if training
|
||||
mask = ones(obj.order,1);
|
||||
else
|
||||
mask = zeros(obj.order,1);
|
||||
mask(900:end) = 1;
|
||||
mask(ceil(length(obj.e)/2)) = 1;
|
||||
end
|
||||
|
||||
mask = ones(obj.order,1);
|
||||
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
@@ -110,7 +121,7 @@ classdef FFE < handle
|
||||
|
||||
U = x(obj.order+sample-1:-1:sample);
|
||||
|
||||
y(symbol,1) = obj.e.' * U; % Calculating output of LMS __ * |
|
||||
y(symbol,1) = (obj.e.*mask).' * U; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
d_hat(symbol,1) = d(symbol);
|
||||
@@ -129,7 +140,6 @@ classdef FFE < handle
|
||||
normalizationfactor = (U.' * U);
|
||||
obj.e = obj.e - err(symbol) * U / normalizationfactor; % Weight update rule of NLMS
|
||||
end
|
||||
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@ classdef Postfilter < handle
|
||||
|
||||
if ~isnan(options.useBurg) && options.useBurg
|
||||
|
||||
disp('using burg alg')
|
||||
% disp('using burg alg')
|
||||
obj.coefficients = arburg(noiseclass_in.signal,obj.ncoeff);
|
||||
|
||||
elseif ~isempty(options.coefficients)
|
||||
|
||||
disp('using given taps')
|
||||
% disp('using given taps')
|
||||
obj.coefficients = options.coefficients;
|
||||
obj.useBurg = 0;
|
||||
|
||||
@@ -67,22 +67,29 @@ classdef Postfilter < handle
|
||||
|
||||
end
|
||||
|
||||
function showFilter(obj,noiseclass_in,options)
|
||||
function showFilter(obj,options)
|
||||
|
||||
arguments
|
||||
obj
|
||||
noiseclass_in
|
||||
options.noiseclass_in =[]
|
||||
options.fignum = 121
|
||||
options.color = []
|
||||
end
|
||||
|
||||
% noiseclass_in.spectrum('displayname','Noise PSD shifted to 0dBm','fignum',options.fignum,'normalizeTo0dB',1);
|
||||
|
||||
if ~isempty(options.noiseclass_in)
|
||||
len = length(options.noiseclass_in);
|
||||
fs = options.noiseclass_in.fs;
|
||||
else
|
||||
len = 1024;
|
||||
fs = 1;
|
||||
end
|
||||
|
||||
figure(options.fignum)
|
||||
[h,w] = freqz(1,obj.coefficients,length(noiseclass_in),"whole",noiseclass_in.fs);
|
||||
[h,w] = freqz(1,obj.coefficients,len,"whole",fs);
|
||||
h = h/max(abs(h));
|
||||
hold on
|
||||
w_ = (w - noiseclass_in.fs/2);
|
||||
w_ = (w - fs/2);
|
||||
|
||||
if isempty(options.color)
|
||||
plot(w_.*1e-9,20*log10(fftshift(abs(h))),'DisplayName',['Burg Coeffs: ', num2str(round(obj.coefficients,2)), ' '],'LineWidth',2);
|
||||
|
||||
@@ -70,7 +70,7 @@ classdef MLSE < handle
|
||||
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level
|
||||
constellation = unique(data_ref);
|
||||
decisionLevels = (constellation(1:end-1) + constellation(2:end)) / 2;
|
||||
tx_bits = PAMmapper(numel(constellation),0).demap(data_ref);
|
||||
tx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(data_ref);
|
||||
|
||||
|
||||
% impulse respnse i.e. [0.5, 1.0000]
|
||||
@@ -188,10 +188,50 @@ classdef MLSE < handle
|
||||
|
||||
end
|
||||
|
||||
% Compute soft output PAM4 stream from the metric_sym
|
||||
soft_output = zeros(length(data_in),1); % Expected symbol value per stage
|
||||
symbol_prob = zeros(length(data_in), length(states)); % Store full probability distribution (optional)
|
||||
llp = llp';
|
||||
for n = 1:length(data_in)
|
||||
metrics = llp(n, :); % A posteriori metric for each PAM4 candidate
|
||||
% For numerical stability, subtract the maximum metric before exponentiating
|
||||
maxMetric = min(metrics);
|
||||
expMetrics = exp(metrics - maxMetric);
|
||||
probs = expMetrics / sum(expMetrics); % Normalize to get probabilities
|
||||
symbol_prob(n, :) = probs; % (Optional) store distribution for analysis
|
||||
% Compute the soft output as the expected value of the PAM4 symbols
|
||||
soft_output(n) = sum(probs .* constellation');
|
||||
end
|
||||
|
||||
% Number of symbols and bits per symbol
|
||||
num_symbols = constellation;
|
||||
num_bits = 2; % 2 bits per symbol
|
||||
|
||||
bit_mapping = PAMmapper(4,0,"eth_style",1).showBitMapping; % Each row corresponds to the symbol above
|
||||
|
||||
% Initialize LLR storage
|
||||
llr = zeros(num_bits, length(data_in));
|
||||
|
||||
% Compute bit-wise LLRs
|
||||
for bit_idx = 1:num_bits
|
||||
% Find indices where bit is 0 and where it is 1
|
||||
idx_bit_0 = find(bit_mapping(:,bit_idx) == 0);
|
||||
idx_bit_1 = find(bit_mapping(:,bit_idx) == 1);
|
||||
|
||||
% Sum over log-probabilities (Max-Log approximation: using min instead of sum)
|
||||
llr(:,bit_idx) = min(llp(:,idx_bit_1), [], 1) - min(llp(:,idx_bit_0), [], 1);
|
||||
|
||||
end
|
||||
|
||||
% Convert LLR values to a hard-decision bit stream
|
||||
bit_stream = llr < 0;
|
||||
[~,~,ber_llr,~] = calc_ber(bit_stream',tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLR BER : %.2e \n',ber_llr);
|
||||
|
||||
% directly decide based on lowest LLP index
|
||||
[~,llp_based_state_seq]=min(llp);
|
||||
LLP_EST(1:length(data_in)) = constellation(llp_based_state_seq);
|
||||
rx_bits = PAMmapper(numel(constellation),0).demap(LLP_EST');
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(LLP_EST');
|
||||
[~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('LLP BER : %.2e \n',ber_llp);
|
||||
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
@@ -200,7 +240,7 @@ classdef MLSE < handle
|
||||
% fprintf('LLP BER -1: %.2e \n',ber_llp);
|
||||
|
||||
%%%% DECIDE based on Viterbi traceback
|
||||
rx_bits = PAMmapper(numel(constellation),0).demap(VITERBI_ESTIMATION_SYMBOLS');
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(VITERBI_ESTIMATION_SYMBOLS');
|
||||
[~,~,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('Viterbi BER: %.2e \n',ber_viterbi);
|
||||
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
@@ -209,14 +249,14 @@ classdef MLSE < handle
|
||||
% directly decide based on the FW path metrics
|
||||
[~,fw_direct_state_seq]=min(pm_survivor_fw);
|
||||
FW_EST(1:length(data_in)) = constellation(fw_direct_state_seq);
|
||||
rx_bits = PAMmapper(numel(constellation),0).demap(FW_EST');
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(FW_EST');
|
||||
[~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('FW BER: %.2e \n',ber_fw);
|
||||
|
||||
% directly decide based on the BW path metrics
|
||||
[~,bw_direct_state_seq]=min(pm_survivor_bw);
|
||||
BW_EST(1:length(data_in)) = constellation(bw_direct_state_seq(2:end));
|
||||
rx_bits = PAMmapper(numel(constellation),0).demap(BW_EST');
|
||||
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(BW_EST');
|
||||
[~,~,ber_bw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('BW BER: %.2e \n',ber_bw);
|
||||
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
@@ -224,6 +264,8 @@ classdef MLSE < handle
|
||||
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
% fprintf('BW BER: %.2e \n',ber_viterbi);
|
||||
|
||||
PAMmapper(4,0,"eth_style",1).showBitMapping
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
tx_symbolpos = zeros(numel(constellation),length(data_ref));
|
||||
|
||||
@@ -59,8 +59,19 @@ classdef TransmissionPerformance
|
||||
1.03e-2, 9.29e-3, 8.33e-3, 7.54e-3, 7.04e-3, 4.70e-3];
|
||||
|
||||
%% LUT for KP4-FEC and Inner Code https://grouper.ieee.org/groups/802/3/dj/public/23_03/patra_3dj_01b_2303.pdf
|
||||
|
||||
CODE_RATE_KP4_AND_INNER = [0.885799];
|
||||
BERTHRESHOLDS_KP4_AND_INNER = 4.85e-3;
|
||||
|
||||
% https://www.ieee802.org/3/bs/public/14_11/parthasarathy_3bs_01a_1114.pdf
|
||||
CODE_RATE_KP4 = [1/(1+0.052)];%Reed-Solomon RS(544, 514) == KP4
|
||||
BERTHRESHOLDS_KP4 = 2.2e-4;
|
||||
|
||||
CODE_RATE_HDFEC = [1/(1+0.067)]; %Beyond 300 Gbps Short-Reach Links Using TFLN MZMs With 500 mVpp and Linear Equalization
|
||||
BERTHRESHOLDS_HDFEC = 3.8e-3;
|
||||
|
||||
CODE_RATE_O_FEC = [1/(1+0.153)]; %Stefano im Meeting
|
||||
BERTHRESHOLDS_O_FEC = 2e-2;
|
||||
|
||||
|
||||
|
||||
@@ -151,6 +162,11 @@ classdef TransmissionPerformance
|
||||
netrates.KP4_hamming.NetRate = NaN(1, numMeasurements);
|
||||
netrates.KP4_hamming.CodeRate = NaN(1, numMeasurements);
|
||||
netrates.KP4_hamming.Threshold = NaN(1, numMeasurements);
|
||||
|
||||
netrates.O_FEC.GrossRate = NaN(1, numMeasurements);
|
||||
netrates.O_FEC.NetRate = NaN(1, numMeasurements);
|
||||
netrates.O_FEC.CodeRate = NaN(1, numMeasurements);
|
||||
netrates.O_FEC.Threshold = NaN(1, numMeasurements);
|
||||
end
|
||||
|
||||
% Process each measurement individually.
|
||||
@@ -204,6 +220,22 @@ classdef TransmissionPerformance
|
||||
netrates.KP4_hamming.Threshold(i) = obj.BERTHRESHOLDS_KP4_AND_INNER(idxBER);
|
||||
end
|
||||
|
||||
idxBER = [];
|
||||
for j = length(obj.BERTHRESHOLDS_O_FEC):-1:1
|
||||
if ber(i) <= obj.BERTHRESHOLDS_O_FEC(j)
|
||||
idxBER = j;
|
||||
break;
|
||||
end
|
||||
end
|
||||
if ~isempty(idxBER)
|
||||
codeRate = obj.CODE_RATE_O_FEC(idxBER);
|
||||
netrates.O_FEC.NetRate(i) = grossRate(i) * codeRate;
|
||||
netrates.O_FEC.GrossRate(i) = grossRate(i) ;
|
||||
netrates.O_FEC.CodeRate(i) = codeRate;
|
||||
netrates.O_FEC.Threshold(i) = obj.BERTHRESHOLDS_O_FEC(idxBER);
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -45,15 +45,15 @@ classdef DBHandler < handle
|
||||
else
|
||||
error('DB seems to be corrupt')
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
function obj = refresh(obj)
|
||||
% Get table names and the first rows of each table to understand the structure
|
||||
obj.getTableNames();
|
||||
obj.getTables();
|
||||
obj.getDistinctValues();
|
||||
% Get table names and the first rows of each table to understand the structure
|
||||
obj.getTableNames();
|
||||
obj.getTables();
|
||||
obj.getDistinctValues();
|
||||
end
|
||||
|
||||
function obj = getTableNames(obj)
|
||||
@@ -187,10 +187,11 @@ classdef DBHandler < handle
|
||||
if isstruct(newRow)
|
||||
fields = fieldnames(newRow);
|
||||
emptyFields = structfun(@isempty,newRow);
|
||||
if sum(emptyFields)>0
|
||||
newRow.(fields{emptyFields==1}) = NaN;
|
||||
disp(['In Table: ',tableName,': ',fields{emptyFields==1},' was empty, is now NaN ',newRow.(fields{emptyFields==1})])
|
||||
|
||||
if sum(emptyFields) > 0
|
||||
emptyFieldNames = fields(emptyFields); % use () to get a cell array
|
||||
for idx = 1:numel(emptyFieldNames)
|
||||
newRow.(emptyFieldNames{idx}) = NaN;
|
||||
end
|
||||
end
|
||||
newRow = struct2table(newRow);
|
||||
end
|
||||
@@ -274,87 +275,168 @@ classdef DBHandler < handle
|
||||
end
|
||||
end
|
||||
|
||||
function addBEREntry(obj, berValue, occurrence, runID, ffe, dfe, mlse, pf, eqType, ffe_order, dfe_order, len_tr, mu_ffe, mu_dfe, mu_dc, comment)
|
||||
% addBEREntry Adds a BER entry linked to an existing or new Equalizer entry.
|
||||
% Usage:
|
||||
% addBEREntry(runID, eq, ffe, dfe, mlse, pf, eqType, ffe_order, dfe_order, len_tr, mu_ffe, mu_dfe, mu_dc, berValue, comment)
|
||||
|
||||
if isempty(pf)
|
||||
postfilter_taps = [];
|
||||
function resultID = addProcessingResult(obj, run_id, resultData, eqParamsData)
|
||||
% addProcessingResult Adds a processing result and links it to an EqualizerParameters entry.
|
||||
%
|
||||
% Inputs:
|
||||
% run_id: A run_id from the main table to connect the BER with.
|
||||
%
|
||||
% resultData: A struct with fields corresponding to the ProcessingResults table.
|
||||
%
|
||||
% eqParamsData: A struct with fields corresponding to the EqualizerParameters table,
|
||||
% except 'eq_id' and 'config_hash'. These fields are used to compute
|
||||
% a hash and check for an existing configuration.
|
||||
%
|
||||
% Output:
|
||||
% resultID: The result_id of the newly inserted ProcessingResults entry.
|
||||
|
||||
% 1. Compute hash for equalizer parameters
|
||||
jsonStr = jsonencode(eqParamsData);
|
||||
md = java.security.MessageDigest.getInstance('MD5');
|
||||
md.update(uint8(jsonStr));
|
||||
hashBytes = typecast(md.digest, 'uint8');
|
||||
hashStr = lower(dec2hex(hashBytes)');
|
||||
hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string
|
||||
|
||||
% Add hash to equalizer parameters
|
||||
eqParamsData.config_hash = hashStr;
|
||||
|
||||
% 2. Check if an equalizer configuration with the same hash exists
|
||||
queryStr = sprintf('SELECT eq_id FROM EqualizerParameters WHERE config_hash = ''%s''', eqParamsData.config_hash);
|
||||
existingEntry = obj.fetch(queryStr);
|
||||
|
||||
if ~isempty(existingEntry)
|
||||
% Use existing eq_id
|
||||
eq_id = existingEntry{1,1};
|
||||
else
|
||||
postfilter_taps = pf.burg_coeff;
|
||||
% Insert the new equalizer configuration and get its eq_id
|
||||
eq_id = obj.appendToTable('EqualizerParameters', eqParamsData);
|
||||
end
|
||||
|
||||
% Create equalizer data struct for searching and adding if necessary
|
||||
equalizerData = struct( ...
|
||||
'ffe', jsonencode(ffe), ...
|
||||
'dfe', jsonencode(dfe), ...
|
||||
'mlse', jsonencode(mlse), ...
|
||||
'pf', jsonencode(pf), ...
|
||||
'eq_type', string(eqType), ...
|
||||
'ffe_order', jsonencode(ffe_order), ...
|
||||
'dfe_order', jsonencode(dfe_order), ...
|
||||
'postfilter_taps',jsonencode(postfilter_taps),...
|
||||
'len_tr', len_tr, ...
|
||||
'mu_ffe', jsonencode(mu_ffe), ...
|
||||
'mu_dfe', mu_dfe, ...
|
||||
'mu_dc', mu_dc, ...
|
||||
'comment', comment ...
|
||||
);
|
||||
% 3. Add the equalizer configuration reference and run_id to resultData
|
||||
resultData.eqParam_id = eq_id;
|
||||
resultData.run_id = run_id;
|
||||
|
||||
% Check if exact Equalizer and BER entries already exist in the DB ...
|
||||
selectedFields = {'Runs.run_id','BERs.ber_id','Equalizer.eq_id','BERs.ber',['BERs.occurrence' ...
|
||||
'']};
|
||||
filterParams = obj.tables;
|
||||
filterParams.Equalizer = equalizerData;
|
||||
[dataTable,sql_query] = obj.queryDB(filterParams, selectedFields);
|
||||
% 4. Compute hash for the processing result
|
||||
tempResultData = rmfield(resultData, 'date_of_processing');
|
||||
resultJsonStr = jsonencode(tempResultData);
|
||||
md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance
|
||||
md2.update(uint8(resultJsonStr));
|
||||
resultHashBytes = typecast(md2.digest, 'uint8');
|
||||
resultHashStr = lower(dec2hex(resultHashBytes)');
|
||||
resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string
|
||||
|
||||
% get or insert Equalizer
|
||||
if ~isempty(dataTable)
|
||||
% Equalizer entry already exists, use the existing eq_id
|
||||
cur_eq_id = dataTable.eq_id;
|
||||
else
|
||||
% Insert the new Equalizer entry
|
||||
cur_eq_id = obj.appendToTable('Equalizer', equalizerData);
|
||||
% Add the result hash to resultData
|
||||
resultData.result_hash = resultHashStr;
|
||||
|
||||
% 5. Check if an identical processing result already exists
|
||||
queryStr2 = sprintf('SELECT result_id FROM Results WHERE result_hash = ''%s''', resultData.result_hash);
|
||||
existingResult = obj.fetch(queryStr2);
|
||||
|
||||
if ~isempty(existingResult)
|
||||
% If the result exists, return its result_id without inserting a new row
|
||||
resultID = existingResult{1,1};
|
||||
warning(['Result already exists: ResultID: ',num2str(resultID),'| EQ ID: ',num2str(eq_id),' Run ID: ' num2str(run_id)])
|
||||
return;
|
||||
end
|
||||
|
||||
% skip if already here or insert BER entry
|
||||
if ~isempty(dataTable)
|
||||
% 6. Insert the processing result
|
||||
resultID = obj.appendToTable('Results', resultData);
|
||||
end
|
||||
|
||||
% A BER entry with the same eq_id, run_id, and occurrence already exists
|
||||
existingBERValue = dataTable.ber;
|
||||
function recalcHashes(obj)
|
||||
% recalcHashes Recalculate hashes for all rows in the Results and EqualizerParameters tables.
|
||||
%
|
||||
% For EqualizerParameters, the hash is computed from all fields except
|
||||
% 'eq_id' and 'config_hash'.
|
||||
%
|
||||
% For Results, the hash is computed from all fields except 'result_id',
|
||||
% 'result_hash', and 'date_of_processing'.
|
||||
|
||||
% Compare the existing BER value with the new BER value
|
||||
if existingBERValue == berValue
|
||||
fprintf('The BER entry %.2e || -- eq_id: %d -- run_id: %d -- occurrence: %d already exists. \n',berValue, cur_eq_id, runID, occurrence);
|
||||
else
|
||||
fprintf('Already found BER for EQ: %.2e ~= %.2e || -- eq_id: %d -- run_id: %d -- occurrence: %d already exists.\n', berValue, existingBERValue, cur_eq_id, runID, occurrence);
|
||||
% Recalculate hashes for EqualizerParameters
|
||||
eqParamsRows = obj.fetch('SELECT * FROM EqualizerParameters');
|
||||
for i = 1:height(eqParamsRows)
|
||||
rowStruct = table2struct(eqParamsRows(i,:)); % Convert the table row to a struct
|
||||
|
||||
% Remove fields not part of the hash computation
|
||||
if isfield(rowStruct, 'eq_id')
|
||||
rowStruct = rmfield(rowStruct, 'eq_id');
|
||||
end
|
||||
if isfield(rowStruct, 'config_hash')
|
||||
rowStruct = rmfield(rowStruct, 'config_hash');
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
% No such BER entry exists, insert the new BER entry
|
||||
berData = struct( ...
|
||||
'run_id', runID, ...
|
||||
'eq_id', cur_eq_id, ...
|
||||
'ber', berValue, ...
|
||||
'occurrence', occurrence ...
|
||||
);
|
||||
|
||||
obj.appendToTable('BERs', berData);
|
||||
% Compute MD5 hash from the JSON representation
|
||||
jsonStr = jsonencode(rowStruct);
|
||||
md = java.security.MessageDigest.getInstance('MD5');
|
||||
md.update(uint8(jsonStr));
|
||||
hashBytes = typecast(md.digest, 'uint8');
|
||||
hashStr = lower(dec2hex(hashBytes)');
|
||||
hashStr = lower(strtrim(hashStr(:)'));
|
||||
|
||||
% Update the config_hash field using the eq_id from the table row
|
||||
eq_id = eqParamsRows.eq_id(i);
|
||||
updateQuery = sprintf('UPDATE EqualizerParameters SET config_hash = ''%s'' WHERE eq_id = %d', hashStr, eq_id);
|
||||
obj.executeSQL(updateQuery);
|
||||
end
|
||||
|
||||
|
||||
% Fetch all rows from the Results table using queryDB with no filters
|
||||
Results = obj.tables.Results;
|
||||
|
||||
if isstruct(Results)
|
||||
newFields = {};
|
||||
fNames = fieldnames(Results);
|
||||
for i = 1:numel(fNames)
|
||||
newFields{end+1} = ['Results.' fNames{i}];
|
||||
end
|
||||
Results = newFields;
|
||||
end
|
||||
[resultsRows, ~] = obj.queryDB(obj.tables, Results);
|
||||
|
||||
for i = 1:height(resultsRows)
|
||||
rowStruct = table2struct(resultsRows(i, :)); % Convert the row to a struct
|
||||
|
||||
% Remove fields not used in the hash calculation
|
||||
if isfield(rowStruct, 'result_id')
|
||||
rowStruct = rmfield(rowStruct, 'result_id');
|
||||
end
|
||||
if isfield(rowStruct, 'result_hash')
|
||||
rowStruct = rmfield(rowStruct, 'result_hash');
|
||||
end
|
||||
if isfield(rowStruct, 'date_of_processing')
|
||||
rowStruct = rmfield(rowStruct, 'date_of_processing');
|
||||
end
|
||||
|
||||
% Compute MD5 hash from the JSON representation
|
||||
jsonStr = jsonencode(rowStruct);
|
||||
md = java.security.MessageDigest.getInstance('MD5');
|
||||
md.update(uint8(jsonStr));
|
||||
hashBytes = typecast(md.digest, 'uint8');
|
||||
hashStr = lower(dec2hex(hashBytes)');
|
||||
hashStr = lower(strtrim(hashStr(:)'));
|
||||
|
||||
% Access the result_id from the table row and update the hash
|
||||
result_id = resultsRows.result_id(i);
|
||||
updateQuery = sprintf('UPDATE Results SET result_hash = ''%s'' WHERE result_id = %d', ...
|
||||
hashStr, result_id);
|
||||
obj.executeSQL(updateQuery);
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function executeSQL(obj, query)
|
||||
% This method executes an SQL statement using MATLAB's execute function.
|
||||
execute(obj.conn, query);
|
||||
end
|
||||
|
||||
|
||||
function answer = fetch(obj,query)
|
||||
answer = fetch(obj.conn,query);
|
||||
end
|
||||
@@ -405,15 +487,46 @@ classdef DBHandler < handle
|
||||
|
||||
function query = constructSQLQuery(obj, filterParams, selectedFields)
|
||||
% constructSQLQuery Constructs the SQL query based on filter parameters and selected fields.
|
||||
%
|
||||
% If selectedFields is provided as a struct, it is converted to a cell array.
|
||||
% The conversion takes the field names and creates entries like:
|
||||
% {'selectedFields.fieldName'} for each field.
|
||||
|
||||
% Construct the SELECT clause dynamically based on user selection
|
||||
% Input check for selectedFields: if it's a struct, convert it to a cell array.
|
||||
if isstruct(selectedFields)
|
||||
newFields = {};
|
||||
tableNames = fieldnames(selectedFields);
|
||||
for t = 1:numel(tableNames)
|
||||
tableStruct = selectedFields.(tableNames{t});
|
||||
fieldNames = fieldnames(tableStruct);
|
||||
for f = 1:numel(fieldNames)
|
||||
if isequal(tableStruct.(fieldNames{f}), 1)
|
||||
newFields{end+1} = sprintf('%s.%s', tableNames{t}, fieldNames{f});
|
||||
end
|
||||
end
|
||||
end
|
||||
selectedFields = newFields;
|
||||
end
|
||||
|
||||
% Construct the SELECT clause dynamically based on user selection.
|
||||
% (Assuming that when provided as a cell array, each entry is of the form
|
||||
% 'TableName.fieldName' or, in our conversion case, 'selectedFields.fieldName'.)
|
||||
selectClause = 'SELECT DISTINCT ';
|
||||
for i = 1:numel(selectedFields)
|
||||
fieldParts = strsplit(selectedFields{i}, '.');
|
||||
tableName = fieldParts{1};
|
||||
fieldName = fieldParts{2};
|
||||
% If the field comes from the struct conversion, its first part is 'selectedFields'
|
||||
% and the actual field name is in the second part.
|
||||
if strcmp(fieldParts{1}, 'selectedFields')
|
||||
tableName = fieldParts{1}; % not used for type checking below
|
||||
fieldName = fieldParts{2};
|
||||
else
|
||||
tableName = fieldParts{1};
|
||||
fieldName = fieldParts{2};
|
||||
end
|
||||
|
||||
if isnumeric(obj.tables.(tableName).(fieldName))
|
||||
% Decide on COALESCE depending on the field type.
|
||||
% If the table is known in obj.tables and the field is numeric, use 'NaN'.
|
||||
if isfield(obj.tables, tableName) && isfield(obj.tables.(tableName), fieldName) && isnumeric(obj.tables.(tableName).(fieldName))
|
||||
selectClause = [selectClause, 'COALESCE(', selectedFields{i}, ', ''NaN'') AS ', fieldName];
|
||||
else
|
||||
selectClause = [selectClause, 'COALESCE(', selectedFields{i}, ', '''') AS ', fieldName];
|
||||
@@ -426,235 +539,300 @@ classdef DBHandler < handle
|
||||
end
|
||||
end
|
||||
|
||||
% Construct the FROM and WHERE clause
|
||||
baseQuery = [selectClause, 'FROM Runs ' ...
|
||||
'LEFT JOIN Configurations ON Runs.run_id = Configurations.run_id ' ...
|
||||
'LEFT JOIN Measurements ON Runs.run_id = Measurements.run_id ' ...
|
||||
'WHERE '];
|
||||
% 'LEFT JOIN BERs ON Runs.run_id = BERs.run_id ' ...
|
||||
% 'LEFT JOIN Equalizer ON BERs.eq_id = Equalizer.eq_id ' ...
|
||||
|
||||
% --- Adaptive FROM Clause ---
|
||||
% Use "Runs" as the main table and add LEFT JOINs for every other table in obj.tables
|
||||
% (except "sqlite_sequence") that has a run_id field.
|
||||
mainTable = 'Runs';
|
||||
fromClause = ['FROM ', mainTable, ' '];
|
||||
tableNamesAll = fieldnames(obj.tables);
|
||||
for t = 1:numel(tableNamesAll)
|
||||
tableName = tableNamesAll{t};
|
||||
if strcmpi(tableName, mainTable) || strcmpi(tableName, 'sqlite_sequence')
|
||||
continue;
|
||||
end
|
||||
|
||||
% Loop through each table in filterParams
|
||||
if isfield(obj.tables.(tableName), 'run_id')
|
||||
% most tables are directly linked to runs table
|
||||
fromClause = [fromClause, 'LEFT JOIN ', tableName, ' ON ', mainTable, '.run_id = ', tableName, '.run_id '];
|
||||
elseif isfield(obj.tables.(tableName), 'eq_id')
|
||||
% equalizer is only linked to results table
|
||||
fromClause = [fromClause, 'LEFT JOIN ', tableName, ' ON ', 'Results', '.eqParam_id = ', tableName, '.eq_id '];
|
||||
end
|
||||
end
|
||||
|
||||
% --- WHERE Clause Construction ---
|
||||
baseQuery = [selectClause, ' ', fromClause, 'WHERE '];
|
||||
filterClauses = [];
|
||||
tableNames_ = fieldnames(filterParams);
|
||||
|
||||
for t = 1:numel(tableNames_)
|
||||
tableName = tableNames_{t};
|
||||
tableParams = filterParams.(tableName);
|
||||
|
||||
% Loop through each parameter in the table
|
||||
fieldNames = fieldnames(tableParams);
|
||||
for i = 1:numel(fieldNames)
|
||||
fieldName = fieldNames{i};
|
||||
value = tableParams.(fieldName);
|
||||
|
||||
% Construct the full column name in the format "tableName.fieldName"
|
||||
fullName = sprintf('%s.%s', tableName, fieldName);
|
||||
|
||||
% Handle different types of values for SQL query construction
|
||||
% Handle various types of values for SQL query construction
|
||||
if isempty(value)
|
||||
% Skip this parameter if it is empty (include all values)
|
||||
continue;
|
||||
elseif isnumeric(value) && isnan(value)
|
||||
% If value is NaN, use IS NULL in SQL
|
||||
filterClause = sprintf('%s IS NULL', fullName);
|
||||
elseif isnumeric(value) && ~isEnumeration(value)
|
||||
filterClause = sprintf('%s = %f', fullName, value);
|
||||
elseif islogical(value) || (isnumeric(value) && ismember(value, [0, 1])) && ~isEnumeration(value)
|
||||
filterClause = sprintf('%s = %d', fullName, value);
|
||||
elseif ischar(value) || isstring(value)
|
||||
filterClause = sprintf('%s = ''%s''', fullName, char(value)); %nicht nach string suchen sondern nach chararray -> 'bla' statt "bla"
|
||||
filterClause = sprintf('%s = ''%s''', fullName, char(value));
|
||||
elseif isEnumeration(value)
|
||||
filterClause = sprintf('%s = ''%s''', fullName, value);
|
||||
else
|
||||
error('Unsupported data type for field "%s".', fullName);
|
||||
end
|
||||
|
||||
% Add the constructed filter clause to the list
|
||||
filterClauses = [filterClauses, filterClause, ' AND '];
|
||||
end
|
||||
end
|
||||
|
||||
% Remove trailing ' AND ' from the filter clauses if any filters were added
|
||||
% Remove trailing ' AND ' if any filters were added.
|
||||
if ~isempty(filterClauses)
|
||||
filterClauses = filterClauses(1:end-5);
|
||||
end
|
||||
|
||||
% Construct the final SQL query
|
||||
if isempty(filterClauses)
|
||||
query = [selectClause, 'FROM Runs ' ...
|
||||
'LEFT JOIN Configurations ON Runs.run_id = Configurations.run_id ' ...
|
||||
'LEFT JOIN Measurements ON Runs.run_id = Measurements.run_id ' ...
|
||||
'LEFT JOIN BERs ON Runs.run_id = BERs.run_id'];
|
||||
query = [selectClause, ' ', fromClause, 'WHERE ', filterClauses];
|
||||
else
|
||||
query = [baseQuery, filterClauses];
|
||||
query = [selectClause, ' ', fromClause];
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
function selectedFields = promptSelectFields(obj)
|
||||
% promptSelectFields Prompts the user to select fields from multiple tables to include in the SELECT statement using settingsdlg.
|
||||
% promptSelectFields Prompts the user to select fields from multiple tables
|
||||
% using a custom checkbox GUI with scrolling.
|
||||
%
|
||||
% The function builds a list of all fields (formatted as 'TableName.fieldName')
|
||||
% and displays each as a checkbox inside an inner container panel. The container's
|
||||
% height is set to accommodate all checkboxes, so the scrollable panel shows scrollbars.
|
||||
% When the user clicks the "Select" button, the selected fields are returned.
|
||||
% If none are selected, all fields are returned.
|
||||
|
||||
% Get all possible fields from all tables (excluding sqlite_sequence)
|
||||
% Get all possible tables (excluding sqlite_sequence)
|
||||
tableNames = fieldnames(obj.tables);
|
||||
tableNames = setdiff(tableNames, {'sqlite_sequence'}); % Remove sqlite_sequence
|
||||
|
||||
% Prepare the inputs for settingsdlg
|
||||
promptSettings = {};
|
||||
allFieldsFullName = {};
|
||||
convertedFieldNames = {};
|
||||
tableNames = setdiff(tableNames, {'sqlite_sequence'});
|
||||
|
||||
% Build a single cell array of all field names with table prefix.
|
||||
allFields = {};
|
||||
for i = 1:numel(tableNames)
|
||||
tableFields = fieldnames(obj.tables.(tableNames{i}));
|
||||
for j = 1:numel(tableFields)
|
||||
fieldName = tableFields{j};
|
||||
fullName = sprintf('%s.%s', tableNames{i}, fieldName);
|
||||
convertedName = strrep(fullName, '.', '_'); % Replace '.' with '_'
|
||||
|
||||
allFieldsFullName{end + 1} = fullName; % Add full name to the list
|
||||
convertedFieldNames{end + 1} = convertedName; % Store the converted name
|
||||
|
||||
% Add the field name and checkbox setting to the prompt
|
||||
promptSettings{end + 1} = {sprintf('Include %s', fullName), convertedName};
|
||||
promptSettings{end + 1} = false; % Default: not selected
|
||||
fields = fieldnames(obj.tables.(tableNames{i}));
|
||||
for j = 1:numel(fields)
|
||||
allFields{end+1} = sprintf('%s.%s', tableNames{i}, fields{j});
|
||||
end
|
||||
end
|
||||
numFields = numel(allFields);
|
||||
|
||||
% Create the settings dialog
|
||||
[settings, button] = settingsdlg(...
|
||||
'title', 'Select Fields for the SQL Query', ...
|
||||
'description', 'Check the boxes for the fields you want to include in the SELECT statement.', ...
|
||||
promptSettings{:} ...
|
||||
);
|
||||
% Create the main figure.
|
||||
fig = uifigure('Name', 'Select Fields', 'Position', [100, 100, 400, 600]);
|
||||
|
||||
% If the user cancels, default to selecting all fields
|
||||
if strcmp(button, 'cancel')
|
||||
selectedFields = allFieldsFullName;
|
||||
return;
|
||||
% Create a scrollable panel.
|
||||
scrollPanel = uipanel(fig, 'Position', [10, 60, 380, 530], 'Scrollable', 'on');
|
||||
|
||||
% Define checkbox dimensions.
|
||||
checkboxHeight = 30;
|
||||
spacing = 5;
|
||||
totalHeight = numFields * (checkboxHeight + spacing);
|
||||
|
||||
% Create an inner container panel with height larger than the scrollPanel's height.
|
||||
container = uipanel(scrollPanel, 'Position', [0, 0, scrollPanel.Position(3), totalHeight]);
|
||||
|
||||
% Create checkboxes using absolute positioning in the container.
|
||||
checkboxes = gobjects(numFields, 1);
|
||||
for i = 1:numFields
|
||||
% Calculate the vertical position.
|
||||
% The origin (0,0) is at the bottom left of the container.
|
||||
yPos = totalHeight - i*(checkboxHeight + spacing) + spacing;
|
||||
checkboxes(i) = uicheckbox(container, ...
|
||||
'Text', allFields{i}, ...
|
||||
'Value', false, ...
|
||||
'Position', [10, yPos, container.Position(3)-20, checkboxHeight]);
|
||||
end
|
||||
|
||||
% Parse user input into selectedFields
|
||||
% Create a "Select" button in the main figure.
|
||||
btn = uibutton(fig, 'Text', 'Select', ...
|
||||
'Position', [150, 10, 100, 30], ...
|
||||
'ButtonPushedFcn', @(btn, event) uiresume(fig));
|
||||
|
||||
% Wait for the user to click the button.
|
||||
uiwait(fig);
|
||||
|
||||
% Retrieve the selected fields.
|
||||
selectedFields = {};
|
||||
for i = 1:numel(allFieldsFullName)
|
||||
convertedName = convertedFieldNames{i};
|
||||
if isfield(settings, convertedName) && settings.(convertedName) % Add to selectedFields if the checkbox was selected
|
||||
selectedFields{end + 1} = allFieldsFullName{i}; %#ok<AGROW>
|
||||
for i = 1:numFields
|
||||
if checkboxes(i).Value
|
||||
selectedFields{end+1} = checkboxes(i).Text;
|
||||
end
|
||||
end
|
||||
|
||||
% If no fields are selected, default to selecting all fields
|
||||
% If no fields are selected, default to all fields.
|
||||
if isempty(selectedFields)
|
||||
selectedFields = allFieldsFullName;
|
||||
selectedFields = allFields;
|
||||
end
|
||||
|
||||
% Close the figure.
|
||||
delete(fig);
|
||||
end
|
||||
|
||||
function filterParams = promptFilterParameters(obj)
|
||||
% promptFilterParameters Prompts the user to enter filter parameters using the settingsdlg framework.
|
||||
% promptFilterParameters Prompts the user to enter filter parameters using a
|
||||
% custom scrollable UI with dropdowns.
|
||||
%
|
||||
% For each table (excluding 'sqlite_sequence'), each field that has distinct
|
||||
% values is displayed as a label and a dropdown. The dropdown items are built
|
||||
% from the distinct values (with "All" prepended). The output is a struct where,
|
||||
% for each table, each field is set to the chosen value (or [] if "All" is selected).
|
||||
|
||||
% Get all possible parameters from all tables (excluding sqlite_sequence)
|
||||
tableNames_ = fieldnames(obj.tables);
|
||||
tableNames_ = setdiff(tableNames_, {'sqlite_sequence'}); % Remove sqlite_sequence
|
||||
% Get all tables except 'sqlite_sequence'
|
||||
tableNames = fieldnames(obj.tables);
|
||||
tableNames = setdiff(tableNames, {'sqlite_sequence'});
|
||||
|
||||
% Prepare the inputs for settingsdlg with sections and separators
|
||||
promptSettings = {};
|
||||
allFieldsFullName = {};
|
||||
convertedFieldNames = {};
|
||||
% Precompute layout constants.
|
||||
heightPerTableLabel = 30;
|
||||
heightPerField = 40; % vertical space for a field (label + dropdown)
|
||||
spacing = 5;
|
||||
|
||||
for i = 1:numel(tableNames_)
|
||||
% Add a separator for each table section
|
||||
promptSettings{end + 1} = 'separator';
|
||||
promptSettings{end + 1} = tableNames_{i};
|
||||
|
||||
% Get all fields from the current table
|
||||
tableFields = fieldnames(obj.tables.(tableNames_{i}));
|
||||
|
||||
% Prepare each field to be added to the dialog
|
||||
% Compute total required height.
|
||||
totalHeight = 0;
|
||||
for i = 1:numel(tableNames)
|
||||
totalHeight = totalHeight + heightPerTableLabel;
|
||||
tableName = tableNames{i};
|
||||
tableFields = fieldnames(obj.tables.(tableName));
|
||||
for j = 1:numel(tableFields)
|
||||
fieldName = tableFields{j};
|
||||
fullName = sprintf('%s.%s', tableNames_{i}, fieldName);
|
||||
convertedName = strrep(fullName, '.', '_'); % Replace '.' with '_'
|
||||
|
||||
% Skip fields that do not have distinct values stored
|
||||
if ~isfield(obj.distinctValues.(tableNames_{i}), fieldName)
|
||||
continue;
|
||||
% Only include fields that have distinct values stored.
|
||||
if isfield(obj.distinctValues.(tableName), fieldName)
|
||||
totalHeight = totalHeight + heightPerField;
|
||||
end
|
||||
|
||||
% Get the distinct values for the field
|
||||
distinctValues_ = obj.distinctValues.(tableNames_{i}).(fieldName);
|
||||
|
||||
% Prepare distinct values for dropdown
|
||||
if isempty(distinctValues_)
|
||||
% If there are no distinct values, use only an "All" entry
|
||||
distinctValues_ = {'All'};
|
||||
else
|
||||
% Ensure distinctValues is a cell array of strings
|
||||
if isnumeric(distinctValues_)
|
||||
distinctValues_ = arrayfun(@(x) num2str(x), distinctValues_, 'UniformOutput', false);
|
||||
elseif isstring(distinctValues_)
|
||||
distinctValues_ = cellstr(distinctValues_);
|
||||
elseif iscell(distinctValues_) && ~iscellstr(distinctValues_)
|
||||
distinctValues_ = cellfun(@num2str, distinctValues_, 'UniformOutput', false);
|
||||
end
|
||||
|
||||
% Add an "All" option at the beginning of the distinct values list
|
||||
distinctValues_ = [{'All'}; distinctValues_];
|
||||
end
|
||||
|
||||
allFieldsFullName{end + 1} = fullName; % Add full name to the list
|
||||
convertedFieldNames{end + 1} = convertedName; % Store the converted name
|
||||
|
||||
% Add the field name and value setting to the prompt
|
||||
promptSettings{end + 1} = {sprintf('%s', fullName), convertedName};
|
||||
promptSettings{end + 1} = distinctValues_; % Add distinct values as dropdown options
|
||||
end
|
||||
end
|
||||
|
||||
% Create the settings dialog
|
||||
[settings, button] = settingsdlg(...
|
||||
'title', 'Input Parameters for Filtering', ...
|
||||
'description', 'Enter the values for each field to filter. Select "All" to include all values.', ...
|
||||
promptSettings{:} ...
|
||||
);
|
||||
% Create the main UI figure.
|
||||
fig = uifigure('Name', 'Input Parameters for Filtering', 'Position', [100, 100, 500, 600]);
|
||||
|
||||
% If the user cancels, return an empty struct
|
||||
if strcmp(button, 'cancel')
|
||||
% Set a CloseRequestFcn so that closing the figure calls uiresume.
|
||||
fig.CloseRequestFcn = @(src, event) uiresume(src);
|
||||
|
||||
% Create a scrollable panel inside the figure.
|
||||
scrollPanel = uipanel(fig, 'Position', [10, 60, 480, 530], 'Scrollable', 'on');
|
||||
|
||||
% Create an inner container panel with a height set to totalHeight.
|
||||
container = uipanel(scrollPanel, 'Position', [0, 0, scrollPanel.Position(3), totalHeight]);
|
||||
|
||||
% Prepare cell arrays to store dropdown handles and corresponding table/field names.
|
||||
dropdownHandles = {};
|
||||
dropdownTableNames = {};
|
||||
dropdownFieldNames = {};
|
||||
|
||||
% Set the starting Y coordinate (filling from top to bottom).
|
||||
currentY = totalHeight;
|
||||
|
||||
% Maximum number of dropdown items.
|
||||
maxItems = 100;
|
||||
|
||||
for i = 1:numel(tableNames)
|
||||
% Create a label for the table name.
|
||||
uilabel(container, ...
|
||||
'Text', tableNames{i}, ...
|
||||
'FontWeight', 'bold', ...
|
||||
'Position', [10, currentY - heightPerTableLabel + spacing, 200, heightPerTableLabel - spacing]);
|
||||
currentY = currentY - heightPerTableLabel;
|
||||
|
||||
tableName = tableNames{i};
|
||||
tableFields = fieldnames(obj.tables.(tableName));
|
||||
for j = 1:numel(tableFields)
|
||||
fieldName = tableFields{j};
|
||||
if ~isfield(obj.distinctValues.(tableName), fieldName)
|
||||
continue; % Skip if no distinct values are stored.
|
||||
end
|
||||
|
||||
% Retrieve distinct values for the field.
|
||||
distinctValues_ = obj.distinctValues.(tableName).(fieldName);
|
||||
if ~isempty(distinctValues_) && numel(distinctValues_) > maxItems
|
||||
distinctValues_ = distinctValues_(1:maxItems);
|
||||
end
|
||||
|
||||
if isempty(distinctValues_)
|
||||
items = {'All'};
|
||||
else
|
||||
if isnumeric(distinctValues_)
|
||||
items = cellfun(@num2str, num2cell(distinctValues_), 'UniformOutput', false);
|
||||
elseif isstring(distinctValues_)
|
||||
items = cellstr(distinctValues_);
|
||||
elseif iscell(distinctValues_) && ~iscellstr(distinctValues_)
|
||||
items = cellfun(@num2str, distinctValues_, 'UniformOutput', false);
|
||||
else
|
||||
items = distinctValues_;
|
||||
end
|
||||
items = items(:)'; % Ensure row vector
|
||||
items = [{'All'}, items];
|
||||
end
|
||||
|
||||
% Create a label for the field.
|
||||
uilabel(container, ...
|
||||
'Text', sprintf('%s:', fieldName), ...
|
||||
'HorizontalAlignment', 'right', ...
|
||||
'Position', [10, currentY - 25, 150, 25]);
|
||||
|
||||
% Create a dropdown for the field.
|
||||
dd = uidropdown(container, ...
|
||||
'Items', items, ...
|
||||
'Value', 'All', ...
|
||||
'Position', [170, currentY - 25, 200, 25]);
|
||||
|
||||
% Store the dropdown handle and its associated table/field.
|
||||
dropdownHandles{end+1} = dd;
|
||||
dropdownTableNames{end+1} = tableName;
|
||||
dropdownFieldNames{end+1} = fieldName;
|
||||
|
||||
currentY = currentY - heightPerField;
|
||||
end
|
||||
end
|
||||
|
||||
% Create a "Submit" button at the bottom of the figure.
|
||||
btn = uibutton(fig, 'Text', 'Submit', ...
|
||||
'Position', [200, 10, 100, 30], ...
|
||||
'ButtonPushedFcn', @(btn, event) uiresume(fig));
|
||||
|
||||
% Wait until the user clicks "Submit" or closes the figure.
|
||||
uiwait(fig);
|
||||
|
||||
% If the figure was closed (and thus no longer valid), return an empty struct.
|
||||
if ~isvalid(fig)
|
||||
filterParams = struct();
|
||||
return;
|
||||
end
|
||||
|
||||
% Parse user input into filterParams structure
|
||||
% Build the filterParams struct from the dropdown selections.
|
||||
filterParams = struct();
|
||||
|
||||
for i = 1:numel(allFieldsFullName)
|
||||
value = settings.(convertedFieldNames{i});
|
||||
|
||||
% Split full name to get table and field names
|
||||
fieldParts = strsplit(allFieldsFullName{i}, '.');
|
||||
tableName = fieldParts{1};
|
||||
fieldName = fieldParts{2};
|
||||
|
||||
% If the table does not exist in the filterParams struct, create it
|
||||
for k = 1:numel(dropdownHandles)
|
||||
tableName = dropdownTableNames{k};
|
||||
fieldName = dropdownFieldNames{k};
|
||||
value = dropdownHandles{k}.Value;
|
||||
if ~isfield(filterParams, tableName)
|
||||
filterParams.(tableName) = struct();
|
||||
end
|
||||
|
||||
% Assign values to the respective fields under each table
|
||||
% If "All" is selected, assign empty; otherwise, try converting to numeric.
|
||||
if strcmp(value, 'All')
|
||||
filterParams.(tableName).(fieldName) = []; % Set to empty to include all values
|
||||
elseif isnumeric(value) && isnan(value)
|
||||
filterParams.(tableName).(fieldName) = NaN; % Use NaN to handle as NULL
|
||||
filterParams.(tableName).(fieldName) = [];
|
||||
else
|
||||
filterParams.(tableName).(fieldName) = value; % Use the entered value
|
||||
numValue = str2double(value);
|
||||
if ~isnan(numValue)
|
||||
filterParams.(tableName).(fieldName) = numValue;
|
||||
else
|
||||
filterParams.(tableName).(fieldName) = value;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
% Close the figure.
|
||||
delete(fig);
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
180
Classes/Moveit_wrapper.m
Normal file
180
Classes/Moveit_wrapper.m
Normal file
@@ -0,0 +1,180 @@
|
||||
classdef Moveit_wrapper < handle
|
||||
%
|
||||
|
||||
properties(Access=public)
|
||||
moveit_function_name
|
||||
para
|
||||
state = struct()
|
||||
comment
|
||||
end
|
||||
|
||||
methods (Access=public)
|
||||
function obj = Moveit_wrapper(moveit_function_name,options)
|
||||
%
|
||||
|
||||
arguments
|
||||
moveit_function_name
|
||||
options.para
|
||||
end
|
||||
|
||||
obj.moveit_function_name = moveit_function_name;
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
try
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
end
|
||||
|
||||
% Ensure the function exists
|
||||
if ~exist(obj.moveit_function_name, 'file')
|
||||
error('Function "%s" does not exist.', obj.moveit_function_name);
|
||||
end
|
||||
|
||||
% Step 1: Get default parameters by calling moveit module
|
||||
if isempty(obj.para)
|
||||
global loop;
|
||||
loop = 0; % Request parameter structure
|
||||
[obj.para, obj.comment] = feval(obj.moveit_function_name);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function signal_out = process(obj,signal_in)
|
||||
|
||||
arguments
|
||||
obj
|
||||
signal_in = []
|
||||
|
||||
end
|
||||
|
||||
isSignalClass = 0;
|
||||
if isa(signal_in,"Signal")
|
||||
signal_out = signal_in;
|
||||
signal_in = signal_in.signal;
|
||||
isSignalClass = 1;
|
||||
end
|
||||
|
||||
% signal_in = signal_in.';
|
||||
|
||||
% INIT MOVEIT
|
||||
global loop;
|
||||
loop = 0;
|
||||
signal_out = obj.moveit_init(signal_in);
|
||||
|
||||
% RUN MOVEIT
|
||||
global loop;
|
||||
loop = 1;
|
||||
|
||||
signal_out = obj.moveit_init(signal_out);
|
||||
|
||||
if isSignalClass
|
||||
% append to logbook
|
||||
lbdesc = ['Logbookentry'];
|
||||
signal_out = signal_out.logbookentry(lbdesc);
|
||||
signal_out.signal = signal_out;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function open_settings(obj)
|
||||
% Opens a settings dialog for modifying Moveit parameters with appropriate input types.
|
||||
% Inserts separators when the level value changes.
|
||||
|
||||
fieldNames = fieldnames(obj.para);
|
||||
settingsCell = {};
|
||||
previousLevel = -inf; % Track previous level for separators
|
||||
|
||||
% Create input arguments for settingsdlg
|
||||
for i = 1:numel(fieldNames)
|
||||
fieldName = fieldNames{i};
|
||||
value = obj.para.(fieldName);
|
||||
|
||||
% Get description from comment structure if available
|
||||
if isfield(obj.comment, fieldName)
|
||||
description = obj.comment.(fieldName);
|
||||
else
|
||||
description = fieldName; % Default to field name if no description
|
||||
end
|
||||
|
||||
% Get level for this parameter
|
||||
if isfield(obj.comment.level, fieldName)
|
||||
currentLevel = obj.comment.level.(fieldName);
|
||||
else
|
||||
currentLevel = 1; % Default level
|
||||
end
|
||||
|
||||
% Insert separator if the level changes
|
||||
if currentLevel ~= previousLevel
|
||||
settingsCell{end+1} = 'separator';
|
||||
settingsCell{end+1} = ['Level ', num2str(currentLevel)];
|
||||
end
|
||||
previousLevel = currentLevel;
|
||||
|
||||
% Determine input type from obj.comment.type
|
||||
if 1
|
||||
inputType = obj.comment.type.(fieldName);
|
||||
else
|
||||
inputType = 'number'; % Default type
|
||||
end
|
||||
|
||||
% Add field description
|
||||
settingsCell{end+1} = {description; fieldName};
|
||||
|
||||
% Define the input type
|
||||
if strcmp(inputType, 'boolean')
|
||||
% Checkbox input
|
||||
settingsCell{end+1} = logical(value); % Enable checkbox
|
||||
elseif contains(inputType, '|')
|
||||
% Dropdown selection (options separated by '|')
|
||||
dropdownOptions = strsplit(inputType, '|');
|
||||
settingsCell{end+1} = dropdownOptions;
|
||||
else
|
||||
% Default: Numeric input
|
||||
settingsCell{end+1} = value;
|
||||
end
|
||||
end
|
||||
|
||||
% Open settings dialog
|
||||
[newValues, button] = settingsdlg(...
|
||||
'title', ['Settings: ', obj.moveit_function_name], ...
|
||||
'description', ['Adjust parameters for ', obj.moveit_function_name], ...
|
||||
settingsCell{:} ...
|
||||
);
|
||||
|
||||
% Update obj.para with new values only if "OK" was pressed
|
||||
if strcmp(button, 'OK')
|
||||
fieldNames = fieldnames(newValues);
|
||||
for i = 1:numel(fieldNames)
|
||||
if fieldNames{i}
|
||||
|
||||
end
|
||||
obj.para.(fieldNames{i}) = newValues.(fieldNames{i});
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
methods (Access=private)
|
||||
|
||||
function data_out = moveit_init(obj,data_in)
|
||||
arguments(Input)
|
||||
obj
|
||||
data_in double
|
||||
end
|
||||
% Step 2: Initialize the function
|
||||
[data_out, obj.state] = feval(obj.moveit_function_name, data_in, obj.state, obj.para);
|
||||
end
|
||||
|
||||
function data_out = moveit_simulation_computation(obj,data_in)
|
||||
arguments(Input)
|
||||
obj
|
||||
data_in double
|
||||
end
|
||||
% Step 3: Process input signal
|
||||
[data_out, obj.state] = feval(obj.moveit_function_name, data_in, obj.state, obj.para);
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -89,7 +89,7 @@ classdef DataStorage < handle
|
||||
for p = 1:numel(obj.fn)
|
||||
name = obj.fn(p);
|
||||
values = obj.inputParams.(name);
|
||||
obj.parameter.(name) = Parameter(name,values);
|
||||
obj.parameter.(name) = StorageParameter(name,values);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
classdef Parameter < handle
|
||||
classdef StorageParameter < handle
|
||||
%PARAMETER Summary of this class goes here
|
||||
% Detailed explanation goes here
|
||||
|
||||
@@ -12,7 +12,7 @@ classdef Parameter < handle
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = Parameter(name, values)
|
||||
function obj = StorageParameter(name, values)
|
||||
%PARAMETER Construct
|
||||
obj.name = name;
|
||||
obj.values = values;
|
||||
Reference in New Issue
Block a user