New MPI mitigation schemes // Duobinary // Start of FTN schemes

This commit is contained in:
Silas Oettinghaus
2024-09-06 08:10:04 +02:00
parent bb228ae2bd
commit 03bfd70470
30 changed files with 1598 additions and 560 deletions

View File

@@ -237,12 +237,16 @@ classdef Signal
arguments
obj Signal
options.fs_in double
options.fs_in double = obj.fs
options.fs_out double
options.n double = 10;
options.beta double = 5;
end
if options.fs_in ~= obj.fs
warning('The signals fs is different from the given fs_in while it should be the same.');
end
obj.signal = resample(obj.signal,options.fs_out,options.fs_in,options.n,options.beta);
desc = ['resample signal from ', num2str(options.fs_in*1e-9), ' GHz to ', num2str(options.fs_out*1e-9), ' GHz' ];
@@ -279,7 +283,7 @@ classdef Signal
edgetick = 2^(nextpow2(obj.fs*1e-9));
% xticks([-edgetick:16:edgetick]);
xlim([-244, 244])
ylim([-120,-0]);
ylim([-120,10]);
yticks([-200:10:10]);
legend
@@ -378,14 +382,23 @@ classdef Signal
end
%%
function [obj,delay_n] = delay(obj,options)
function [obj] = delay(obj,delay,options)
arguments
obj Signal
options.delay_samples = 0
delay double = 0
options.mode delay_mode = delay_mode.samples
end
obj.signal=delayseq(obj.signal,options.delay_samples);
if options.mode == delay_mode.samples
obj.signal=delayseq(obj.signal,delay);
elseif options.mode == delay_mode.time
obj.signal=delayseq(obj.signal,delay,obj.fs);
end
end
@@ -415,7 +428,7 @@ classdef Signal
end
% delay by lagging samples
obj = obj.delay("delay_samples",-D);
obj = obj.delay(-D,'mode','samples');
cuts = obj.length-(options.reference.length*q);
@@ -633,31 +646,7 @@ classdef Signal
hold on
xline(posxall)
hist_interest = plot_data(:,posxall);
hist_interest_smoth = smooth(hist_interest,20);
a = scatter(hist_interest_smoth+posxall,1:length(hist_interest_smoth),4,'.','MarkerEdgeColor','red');
[pk,loc] = findpeaks(hist_interest_smoth,"MinPeakDistance",40,"NPeaks",M,"MinPeakHeight",30);
scatter(posxall,loc,'red','Marker','x','LineWidth',2);
yline(loc,'Color','red','LineWidth',1,'LineStyle',':');
for i = 1:numel(loc)
ppeak(i) = maxA - (difference/histpoints*loc(i));
end
oma = false;
if isa(obj,'Opticalsignal')
er=10*log10(ppeak(1)/ppeak(end));
elseif isa(obj,'Electricalsignal')
if mean([ppeak(1),ppeak(end)]) < 1e-2
oma = true;
er=max(ppeak)-min(ppeak);
else
er=10*log10(ppeak(1)/ppeak(end));
end
else
er=10*log10(ppeak(1)/ppeak(end));
end
% Define properties
boxPosition = [0.15 0.86 0.2 0.05]; % Position for the first box [x y width height]
@@ -687,6 +676,33 @@ classdef Signal
'FontWeight', boxFontWeight, ...
'HorizontalAlignment', 'center');
try
hist_interest = plot_data(:,posxall);
hist_interest_smoth = smooth(hist_interest,20);
a = scatter(hist_interest_smoth+posxall,1:length(hist_interest_smoth),4,'.','MarkerEdgeColor','red');
[pk,loc] = findpeaks(hist_interest_smoth,"MinPeakDistance",40,"NPeaks",M,"MinPeakHeight",30);
scatter(posxall,loc,'red','Marker','x','LineWidth',2);
yline(loc,'Color','red','LineWidth',1,'LineStyle',':');
for i = 1:numel(loc)
ppeak(i) = maxA - (difference/histpoints*loc(i));
end
oma = false;
if isa(obj,'Opticalsignal')
er=10*log10(ppeak(1)/ppeak(end));
elseif isa(obj,'Electricalsignal')
if mean([ppeak(1),ppeak(end)]) < 1e-2
oma = true;
er=max(ppeak)-min(ppeak);
else
er=10*log10(ppeak(1)/ppeak(end));
end
else
er=10*log10(ppeak(1)/ppeak(end));
end
% Adjust position for the third box (slightly to the right)
boxPosition = [0.59 0.86 0.2 0.05]; % Adjusted position
@@ -697,6 +713,8 @@ classdef Signal
thirdboxstring = ['OMA outer:',num2str(er),' V'];
end
annotation('textbox', boxPosition, ...
'String',thirdboxstring , ...
'BackgroundColor', boxColor, ...
@@ -704,7 +722,7 @@ classdef Signal
'LineStyle', boxLineStyle, ...
'FontWeight', boxFontWeight, ...
'HorizontalAlignment', 'center');
end
yticks(linspace(0,histpoints,16));
y_tickstring = sprintfc('%.2f', y_tickstring);

View File

@@ -0,0 +1,406 @@
classdef ChannelFreqResp < handle
% Linear pre-compensation of Channel effects. This module has two modes:
% A) In acquire mode it sends a real OFDM singal with all subcarriers assigned in
% order to acquire later the system's frequency response after the
% signal passed the system...
% ChannelFreqResp.SendOFDM
% B) In the "not acquire" mode it loads the frequency response generated by the freq_res module,
% calculates the inverse frequency response with the right length matched
% with the signal length and distorts the signal before tranmission
% give channel indices for measure mode
%TYPICAL FLOW:
% referencesignal = ChannelFreqResp.buildOFDM()
% referencesignal -> SYSTEM -> measuredsignal
% ChannelFreqResp.estimate(measuredsignal)
properties(Access=public)
Nacq
Navg
Ncp
f_ref %fs of transmitted dmt sig
f_observe %fs of the observed signal
symlen
seqlen
refsig
H
H_all
H_apply
Nfft
df
faxis
end
methods (Access=public)
function obj = ChannelFreqResp(options)
%NAME Construct an instance of this class
% Detailed explanation goes here
arguments
options.Nacq = 4096;
options.Navg = 30;
options.Ncp = 100;
options.f_ref;
options.f_observe;
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
obj.Nfft = 2*obj.Nacq + 1 ;
obj.df = obj.f_ref/obj.Nfft ; % calculate frequency grid
obj.faxis = (0:obj.Nfft-1)*obj.df ;
end
function output = buildOFDM(obj)
% generate OFDM or DMT signal that is send over the channel
obj.symlen = (obj.Nacq*2+obj.Ncp+1);
obj.seqlen = obj.symlen*obj.Navg;
obj.refsig = obj.genRNDDMT(15)';
obj.refsig = Informationsignal(obj.refsig,"fs",obj.f_ref);
output = obj.refsig;
end
function output = estimate(obj, data_in, options)
arguments
obj
data_in
options.save logical = false
options.savePath = "";
options.fileName = "";
end
% use the transmitted DMT and the received DMT to get the transfer function
obj.Nfft = 2*obj.Nacq + 1 ;
%resample from fadc to fdac (fdac => fs of reference signal)
data_in = data_in.resample("fs_out",obj.f_ref);
%est from dmt
obj.H_all = obj.estHfromDMT(data_in.signal,obj.refsig.signal);
obj.H = mean(obj.H_all,1);
output = obj.H;
if options.save
obj.save('fileName',options.fileName,'savePath',options.savePath);
end
end
function Target = precomp(obj,Target,options)
% apply the freq response
arguments
obj
Target
options.fileName = ''
options.loadPath = ''
options.maxampdb
end
if isempty(obj.H)
obj.load("fileName",options.fileName,"loadPath",options.loadPath);
end
H_inv = 1./obj.H;
nH = find(~isnan(H_inv),1,'last'); %last value that is not nan
H_inv(nH:end)=H_inv(nH); %replace everything after first NaN with the last non-NaN value
fstarget = Target.fs;
% Build new frequencie axis (with current fs)
fnew = linspace(0,fstarget/2,length(Target)/2+1);
fnew = fnew(2:end-1);
% Old frequency axis (should be much coarser)
idx_old = find((obj.faxis > 0) .* (obj.faxis < fstarget/2)); %positions of all Frequencies smaller than fs/2
int_fold = obj.faxis(idx_old); %old frequencies from 0 to fs/2
idx_new = find(fnew <= int_fold(end)); %positions of all Frequencies smaller than fs/2
int_fnew = fnew(idx_new);%new frequencies from 0 to fs/2
% interpolate the frequency response that had a coarse frequency resolution (e.g. 256 bins) to the current frequency resolution (e.g. 21843 bins)
iH = interp1(int_fold, real(H_inv(idx_old)) ,fnew, 'linear') + 1i*interp1(int_fold, imag(H_inv(idx_old)) ,fnew, 'linear');
% set all NaN values to the fist/ last non-NaN value
nH = find(~isnan(iH),1,'first');
iH(1:nH)=iH(nH);
nH = find(~isnan(iH),1,'last');
iH(nH:end) =iH(nH);
%smoothing takes time and sometimes the result looks odd,
%however the performance is most of the time better
smoothing = 1;
if smoothing
iH = smooth(fnew,iH,0.1,'loess')';
end
% multiply the complex frequency responce with the phase
% angle (-pi,pi) at lowest frequency
iH = iH.*exp(-1j*angle(iH(1))); % to be checked (<- not from silas, so what needs to be checked?)
% Phase difference between lowest and hiughest frequency
% component -> but what is this for? dPhase is not used...
noncausal = 0;
if noncausal
dPhase = angle(iH(end))-angle(iH(1));
end
% normalize complex freq. resp. by magnitude at the first
% five frequencies -> should be the vaue at f=0=DC component?
iH = iH./mean(abs(iH(1:100))); %why 1:5??
% set maximum amplification
% set als values higher than hmax to hmax and keep the
% phase information by multiplication with respective
% corresponding phase angles
maxamp_lin = 10^(options.maxampdb/20);
iH(abs(iH)>maxamp_lin) = maxamp_lin.*exp(1j*angle(iH(abs(iH)>maxamp_lin)));
% it could be helpful to clip at linear 1 (=to keep comp from attenuating the signal)
% iH(abs(iH)<1) = 1.*exp(1j*angle(iH(abs(iH)<1)));
if 0
figure(7);hold on;plot(fnew,20*log10(abs(iH)))
end
H_inv = [iH(1) iH fliplr(conj(iH)) conj(iH(1))];
obj.H_apply = H_inv;
Target.signal = real((ifft( ( fft(real( Target.signal )) .* H_inv' ) )));
end
function plot(obj)
figure(55551);
clf;
Havg = obj.H;
%1)
subplot(4,1,1);hold all;box on;title('Magnitude Freq. Response');
plot(obj.faxis/1e9, 20*log10(abs(obj.H_all)),'linewidth',0.1,'LineStyle','-','Color','#808080') ;
xlim([0.2 .5*max(obj.faxis)*1e-9]);
plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2);
grid on;
%2)
subplot(4,1,2); hold all; box on; title('Phase Freq. Response');
plot(obj.faxis/1e9, (angle(obj.H_all)),'linewidth',0.1,'LineStyle','-','Color','#808080') ;
plot(obj.faxis/1e9, (angle(Havg)),'LineWidth',2) ;
xlim([0.2 .5*max(obj.faxis)*1e-9]);
grid on;
%normalize / remove attenuation
Havg = Havg./mean(Havg(2:10));
%3)
subplot(4,1,3); hold all; box on; title('Inverse Magnitude Freq. Response');
plot(obj.faxis/1e9, 20*log10(abs(1./Havg)),"LineWidth",2,"Color",[0.3467 0.5360 0.6907]) ;
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
ylim([-1 15]);
hold on;
yline(3,'LineWidth',2,'LineStyle','--');
%4)
subplot(4,1,4); hold all; box on; title('Inverse Phase Freq. Response');
plot(obj.faxis/1e9, (angle(1./Havg)),"LineWidth",2,"Color",[0.3467 0.5360 0.6907]) ;
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
end
function save(obj,options)
arguments
obj
options.fileName = ''
options.savePath = ''
end
% Check if the fileName was provided
if isempty(char(options.fileName))
% If no file name is provided, prompt the user to enter a file name
[options.fileName, filePath] = uiputfile('*.mat', 'Save As');
% If the user cancels the dialog, fileName and filePath will be 0
if isequal(options.fileName, 0) || isequal(filePath, 0)
disp('Save operation cancelled.');
return;
end
% Update the savePath with the directory chosen by the user
options.savePath = filePath;
end
% Check if the savePath was provided
if isempty(char(options.savePath))
% If no path is provided, open a UI window to select the path
options.savePath = uigetdir('', 'Select a folder to save the file');
% If the user cancels the dialog, savePath will be 0
if options.savePath == 0
disp('Save operation cancelled.');
return;
end
else
% If a path is provided, validate it
if ~isfolder(options.savePath)
error('The specified path does not exist.');
end
end
% Construct the full file path
fullFileName = fullfile(options.savePath, options.fileName);
% Save the data to the specified file
save(fullFileName, 'obj');
fprintf('Frequency response information successfully saved to %s\n', fullFileName);
end
function data = load(obj, options)
% Function to load data from a specified file and path.
arguments
obj
options.fileName = ''
options.loadPath = ''
end
% Check if the fileName was provided
if isempty(char(options.fileName))
% If no file name is provided, open a UI window to select the file
[options.fileName, options.loadPath] = uigetfile('*.mat', 'Select a file to load');
% If the user cancels the dialog, fileName and loadPath will be 0
if isequal(options.fileName, 0) || isequal(options.loadPath, 0)
disp('Load operation cancelled.');
data = [];
return;
end
end
% If loadPath is not provided or empty, use the current folder
if isempty(char(options.loadPath))
options.loadPath = pwd;
else
% Validate the path if provided
if ~isfolder(options.loadPath)
error('The specified path does not exist.');
end
end
% Construct the full file path
fullFileName = fullfile(options.loadPath, options.fileName);
% Check if the file exists
if ~isfile(fullFileName)
fullFileName = fullfile(options.loadPath, [char(options.fileName),'.mat']);
if ~isfile(fullFileName)
error('The specified file does not exist.');
end
end
% Load the data from the specified file
loadedData = load(fullFileName);
% Replace whole obj here.. is this save or unsave?!
fn = fieldnames(loadedData.obj);
for n = 1:numel(fn)
try
obj.(fn{n}) = loadedData.obj.(fn{n});
end
end
fprintf('Frequency response information successfully loaded from %s\n', fullFileName);
end
end
methods (Access=private)
% Cant be seen from outside! So put all your functions here that can/
% shall not be called from outside
function rOFDM = genRNDDMT(obj,randkey)
rOFDM = NaN(1,(2*obj.Nacq+obj.Ncp+1)*obj.Navg);
s = RandStream('mt19937ar','Seed',randkey,'NormalTransform','Polar');
ref = 2 * round(rand(s,obj.Nacq, obj.Navg)) - 1 ; % Navg Random BPSK sequences
ofdm = [ones(1, obj.Navg) ; ref ; conj(ref(end:-1:1,:)) ] ; % DMT
ofdm = ifft(ofdm) ; % Navg real OFDM sequences
ofdm = [ofdm(end-obj.Ncp+1 : end, :) ; ofdm] ; % Add cyclic prefix
rOFDM = reshape(ofdm, 1, size(ofdm,1)*size(ofdm,2)) ;
rOFDM = rOFDM./max(abs(rOFDM(:)));
end
function [rH] = estHfromDMT(obj, data_in, ref_in)
%! Dont (circ)shift the signal here as this would remove the phase information!
%tested with a butterworth filter this exactly reconstructs the
%phase and the magnitude. However, the option is here
estimatephase = 1;
if ~estimatephase
% 0. cross-correlate the received signal with its reference to extract the periods
corr = abs(ifft( fft(data_in(1:length(ref_in))).* conj(fft(ref_in)) )) ;
% find max
[~, peak] = max(corr) ;
peak=max(1,peak-1);
data_in = circshift(data_in,-peak) ;
%Y = data_in(peak:peak+(Nfft+obj.Ncp)*obj.Navg-1) ;
end
% 1. Reshape signal to a matrix to support noise averaging
Nfft = 2*obj.Nacq + 1 ;
Y = reshape(data_in, Nfft+obj.Ncp, obj.Navg).' ;
X = reshape(ref_in, Nfft + obj.Ncp, obj.Navg).' ;
% 2. Remove cyclic prefix and apply FFT transformation
Y = fft(Y(:, obj.Ncp+1 : obj.Ncp + Nfft), [], 2) ;
X = fft(X(:, obj.Ncp+1 : obj.Ncp + Nfft), [], 2) ;
% 4. Caclulate the frequency response using H = Y/X
rH = Y./X ;
end
end
end

View File

@@ -24,7 +24,9 @@ classdef PAMmapper
if isa(signal_in,'Signal')
signal_in.signal = obj.map_(signal_in.signal);
signal_in = signal_in.normalize("mode","rms");
% signal_in = signal_in.normalize("mode","rms");
signal_in = signal_in.logbookentry();
out = signal_in;
else
@@ -42,7 +44,7 @@ classdef PAMmapper
function pam_sig = map_(obj,bitpattern)
switch obj.M
case 1
case 2
% 2-ASK: BPSK / OOK
pam_sig=bitpattern(:,1);
@@ -87,6 +89,7 @@ classdef PAMmapper
end
pam_sig = pam_sig/sqrt(21);
case 16
% 16-ASK:
x1 = bitpattern(:,1);

View File

@@ -9,6 +9,8 @@ classdef PAMsource
fsym
randkey
db_precode
mrds_code
mrds_blocklength
@@ -33,6 +35,8 @@ classdef PAMsource
options.fsym = 112e9;
options.randkey = 0;
options.db_precode = 0;
options.mrds_code = 0;
options.mrds_blocklength = 512;
@@ -91,6 +95,11 @@ classdef PAMsource
symbols = PAMmapper(obj.M,0).map(bits);
symbols.fs = obj.fsym;
if obj.db_precode
symbols = Duobinary().precode(symbols);
symbols = Duobinary().encode(symbols);
end
if obj.mrds_code
symbols = MRDS_coding("blocklength",obj.mrds_blocklength).encode(symbols);
end

View File

@@ -1,23 +0,0 @@
classdef A1_scheme
%A1_SCHEME Summary of this class goes here
% Detailed explanation goes here
properties
Property1
end
methods
function obj = A1_scheme(inputArg1,inputArg2)
%A1_SCHEME Construct an instance of this class
% Detailed explanation goes here
obj.Property1 = inputArg1 + inputArg2;
end
function outputArg = method1(obj,inputArg)
%METHOD1 Summary of this method goes here
% Detailed explanation goes here
outputArg = obj.Property1 + inputArg;
end
end
end

View File

@@ -0,0 +1,245 @@
classdef Duobinary
%Duobinary Coding
% should work
properties(Access=public)
end
methods (Access=public)
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)
data = signalclass.signal;
u = unique(data);
M = numel(u);
%make unipolar
if M == 4
data = data .* sqrt(5);
elseif M == 6
data = data .* sqrt(10);
elseif M == 8
data = data .* sqrt(21);
elseif M == 16
data = data .* sqrt(85);
warning('Check if PAM16 implementation, mapping and scaling is correct!')
end
data = round(data);
b = min(data);
data = data - b;
data = data ./ 2;
assert(isequal((0:M-1)',unique(data)),'Check Duobinary Precoding'); %seems the signal is not unipolar
% Pre coding
bk = zeros(numel(data),1);
for k = 1:numel(data)-1
bk(k+1) =mod(data(k)-bk(k),M);
end
%make bipolar
bk = bk .* 2;
bk = bk + b;
if M == 4
bk = bk ./ sqrt(5);
elseif M == 6
bk = bk ./ sqrt(10);
elseif M == 8
bk = bk ./ sqrt(21);
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;
u = unique(data);
M = numel(u);
%make unipolar
if M == 4
data = data .* sqrt(5);
elseif M == 6
data = data .* sqrt(10);
elseif M == 8
data = data .* sqrt(21);
elseif M == 16
data = data .* sqrt(85);
warning('Check if PAM16 implementation, mapping and scaling is correct!')
end
data = round(data);
b = min(data);
data = data - b;
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];
data = conv(data,coeff,"same");
%make bipolar
data = (data-round(mean(data),1));
if 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 M == 6
data = data ./ sqrt(5.8);
elseif M == 8
data = data ./ sqrt(10.5); % 15-level constellation weighted with probability after DB code i.e.
end
signalclass.signal = data;
% Code to evaluate the s in sqrt(s):
% M=6;
% c = (0:(2*M)-2) - ((2*M)-2)/2;
% s = 0;
% for i = 0:(2*M)-2
% p(i+1) = M-abs(i-(M-1));
% s = s+p(i+1)*c(i+1).^2;
% end
% s = s/M^2;
end
function signalclass = decode(~,signalclass)
data = signalclass.signal;
u = unique(data);
I = numel(u); %number of duobinary coded const. points
M = (I+1)/2; %PAM-M order
%make unipolar
if I == 7
data = data .* sqrt(2.5);
elseif I == 11
%todo
data = data .* sqrt(5.8);
warning('Check if PAM16 implementation, mapping and scaling is correct!')
elseif I == 15
data = data .* sqrt(10.5);
elseif I == 16
warning('Check if PAM16 implementation, mapping and scaling is correct!')
end
data = round(data);
b = min(data);
data = data - b;
data = round(data);
data = mod(data,M);
%make bipolar
data = data .* 2;
data = data - round(mean(data));
if M == 4
data = data ./ sqrt(5);
elseif M == 6
data = data ./ sqrt(10);
elseif M == 8
data = data ./ sqrt(21);
end
signalclass.signal = data;
end
function signalclass = feedbackdetector(~,signalclass)
data = signalclass.signal;
u = unique(data);
I = numel(u); %number of duobinary coded const. points
M = (I+1)/2; %PAM-M order
%make unipolar
if I == 7
data = data .* sqrt(2.5);
elseif I == 11
%todo
data = data .* sqrt(5.8);
warning('Check if PAM16 implementation, mapping and scaling is correct!')
elseif I == 15
data = data .* sqrt(10.5);
elseif I == 16
warning('Check if PAM16 implementation, mapping and scaling is correct!')
end
data = round(data);
b = min(data);
data = data - b;
data = round(data);
data_out = zeros(length(data),1);
const = 0:M-1;
%%FALSCH!
for k = 1:length(data)-1
d_ = data(k+1) - data_out(k);
[~,b] = min(abs(d_-[0:M-1]));
data_out(k+1) = const(b);
end
%make bipolar
data_out = data_out .* 2;
data_out = data_out - round(mean(data_out));
if M == 4
data_out = data_out ./ sqrt(5);
elseif M == 6
data_out = data_out ./ sqrt(10);
elseif M == 8
data_out = data_out ./ sqrt(21);
end
signalclass.signal = data_out;
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

8
Datatypes/delay_mode.m Normal file
View File

@@ -0,0 +1,8 @@
classdef delay_mode < int32
enumeration
samples (1)
time (2)
end
end

View File

@@ -10,6 +10,8 @@ end
options.skip_end = abs(options.skip_end);
options.skip_front = abs(options.skip_front);
assert((options.skip_end+options.skip_front)<length(data_in),"You can not skip more bits than overall length of data! Set skip_front or skip_end to lower value or check data_in");
bits = 0;
errors= 0;
ber= 0;
@@ -29,7 +31,7 @@ if length(data_ref) == length(data_in)
else
errorIndice = sum(data_in ~= data_ref,1);
errors = sum(errorIndice ,"all" );
[~,errorIndice] = find(errorIndice==1);
[~,errorIndice] = find(errorIndice~=0);
end
catch

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,28 @@
measure = 0;
if measure
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9);
%
Digi_sig = freqresp.buildOFDM();
else
[Digi_sig,Symbols,Bits] = PAMsource("fsym",fsym,"M",M,"order",18,"useprbs",0,...
"fs_out",M8199.fdac,"applyclipping",1,"clipfactor",1.7,"applypulseform",1,"pulseformer",Pform,"randkey",pn_key,"mrds_code",usemrds,"mrds_blocklength",512).process();
end
Digi_sig.spectrum("fignum",1112,"displayname",['Signal']);
maxamp = -1;
El_sig = freqresp.precomp(Digi_sig,"maxampdb",maxamp);
El_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]);
El_sig = Filter('filtdegree',2,"f_cutoff",60e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
if measure
freqresp.estimate(El_sig,"fileName",'','save',false);
end
El_sig.spectrum("fignum",1112,"displayname",['after filter; maxamp:',num2str(maxamp)]);

View File

@@ -0,0 +1,197 @@
%% Parameter to simulate and save
params = struct;
params.M = [4];
params.datarate = [440];
params.rop = [0];
precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active
precomp_path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\standard_system\";
precomp_fn = "400G_simulative_setup";
usemrds = 0;
name = ['wh_',strrep(num2str(now),'.','')];
wh = DataStorage(params);
wh.addStorage("ber_ffe");
%% Init Params
link_length = 0; %meter
pn_key = 2;
laser_linewidth = 0;
endcnt = prod(wh.dim);
cnt=0;
disp(['Start Simulation of ',num2str(endcnt),' loops...'])
tic
for M = wh.parameter.M.values
for datarate = wh.parameter.datarate.values
% SETUP HERE: %%
kover = 16;
M8199 = M8199B("kover",kover);
fdac = M8199.fdac;
fsym = round(datarate / log2(M)) * 1e9;
rrcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rrcalpha);
% MAIN SIGNAL
%%%%% Symbol Generation %%%%%%
[Digi_sig,Symbols,Bits] = PAMsource("fsym",fsym,"M",M,"order",18,"useprbs",0,...
"fs_out",M8199.fdac,"applyclipping",1,"clipfactor",1.7,...
"applypulseform",0,"pulseformer",Pform,"randkey",pn_key,...
"db_precode",1,...
"mrds_code",usemrds,"mrds_blocklength",512).process();
Digi_sig.eye(fsym,M);
Digi_sig.spectrum("fignum",11,"displayname",'before precomp');
if precomp_mode == 1
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',Digi_sig.fs);
Digi_sig = freqresp.buildOFDM();
elseif precomp_mode == 2
Digi_sig = freqresp.precomp(Digi_sig,'maxampdb',3,'loadPath',precomp_path,'fileName',precomp_fn);
Digi_sig.spectrum("fignum",11,"displayname",'after precomp');
end
%%%%% AWG %%%%%%
El_sig = M8199.process(Digi_sig);
% El_sig.signal = awgn(El_sig.signal,-3,'measured',pn_key);
%%%%% Lowpass el. components %%%%%%
El_sig = Filter('filtdegree',2,"f_cutoff",60e9,"fs",fdac*kover,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
%%%%% Electrical Driver Amplifier %%%%%%
El_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","gain","amplification_db",9).process(El_sig);
% El_sig = El_sig.setPower(6,"dBm");
fprintf('Driver output power: %s dBm\n', num2str(El_sig.power));
fprintf('Driver output peak voltage: %s Vpp \n', num2str(max(El_sig.signal)-min(El_sig.signal)));
% MAIN SIGNAL
%%%%% MODULATE E/O CONVERSION %%%%%%
vbias_rel = 0.5;
u_pi = 2.9;
vbias = -vbias_rel*u_pi;
[Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",1290,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",pn_key).process(El_sig);
% Opt_sig.eye(fsym,M);
% %
% figure(10)
% hold on
% scatter(El_sig.signal(1:100000)+vbias,(abs(Opt_sig.signal(1:100000)).^2)*1e3,0.1,'.','DisplayName','Modulator TF')
% ylim([0 4]);
% xlim([-u_pi/2, u_pi/2]+vbias);
% xlabel('Input in V')
% ylabel('abs(Output) in mW')
Optfilter = Filter('filtdegree',6,"f_cutoff",fsym.*0.7,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true);
Opt_sig = Optfilter.process(Opt_sig);
Opt_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",0).process(Opt_sig);
i_ = wh.parameter.rop.length;
ber_ffe=zeros(i_);
patten=zeros(i_);
%%%%% Interference Signal Fiber Prop %%%%%%
Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length/1000,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig);
% Receiver ROP curve
for i = 1:i_
rop=wh.parameter.rop.values(i);
% Set ROP
Rx_sig = Amplifier("amp_mode","ideal_no_noise","gain_mode","output_power","amplification_db",rop).process(Opt_sig);
patten(i) = Rx_sig.power;
%%%%%% Square Law %%%%%%
Rx_sig = Photodiode("fsimu",fdac*kover,"dark_current",2e-08,"responsivity",1,"temperature",20,"nep",1.8e-11).process(Rx_sig);
%%%%%% Lowpass PhDiode %%%%%%
Rx_sig = Filter('filtdegree',2,"f_cutoff",70e9,"fs",fdac*kover,"filterType",filtertypes.gaussian,"active",true).process(Rx_sig);
%%%%%% Scope %%%%%%
fadc = 256e9;
Lp_scpe = Filter('filtdegree',4,"f_cutoff",100e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true);
Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc,...
"delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth,...
"samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0,...
"adcresolution",10,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(Rx_sig);
if precomp_mode == 1
freqresp.estimate(Scpe_sig,"save",true,"savePath",precomp_path,"fileName",precomp_fn);
freqresp.plot();
end
%%%%%% Sample to 2x fsym %%%%%%
Scpe_sig = Scpe_sig.resample("fs_in",fadc,"fs_out",2*fsym);
%%%%%% Sync Rx signal with reference %%%%%%
[Scpe_sig,D,cuts] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym);
%%%%% EQUALIZE %%%%%%
%
Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",1);
%Eq = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",[50,7,7],"sps",2,"decide",1);
[EQ_sig] = Eq.process(Scpe_sig,Symbols);
Rx_bits = PAMmapper(M,0).demap(EQ_sig);
[~,errors_bm,ber_ffe(i),errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber_ffe(i)),' - - ROP: ',num2str(patten(i)),'dBm - - PAM-',num2str(M),' - - ',num2str(fsym*1e-9),' GBd']);
end
for i = 1:i_
rop=wh.parameter.rop.values(i);
wh.addValueToStorage(ber_ffe(i) ,'ber_ffe',M,datarate,rop);
end
toc
disp(['Simulated: ',num2str(cnt/endcnt*100),' %']);
wh.save('C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\MPI_August\auswertung\')
end
end
cols = linspecer(8);
%cnt = cnt+1;
ber_ffe = wh.getStoValue('ber_ffe',M,datarate,wh.parameter.rop.values);
% Create the initial plot
figure(44);
a = gca;
hold on; % Retain the plot so new points can be added without complete redraw
plot(wh.parameter.rop.values,ber_ffe,"LineWidth",0.5,"LineStyle","-","Marker",".","MarkerSize",15,"DisplayName","FFE only");
yline(3.8e-3,'DisplayName','HD-FEC','LineStyle','--','HandleVisibility','off');
xlabel('Received Optical Power (dBm)');
ylabel('Bit Error Rate (BER)');
title('Bit Error Rate vs. ROP');
set(gca,'yscale','log');
set(gca,'Box','on');
grid on;
grid minor
legend

View File

@@ -0,0 +1,54 @@
M = wh.parameter.M.values(1);
datarates = wh.parameter.datarate.values;
rop = wh.parameter.rop.values;
cnt = 1;
for dr = datarates
ber_ffe = wh.getStoValue('ber_ffe',M,dr,rop);
sens_ffe(cnt) = getIntersection(ber_ffe,rop);
figure(112)
hold on
plot(rop,ber_ffe,"LineWidth",0.5,"LineStyle","-","Marker",".","MarkerSize",15,"DisplayName",['FFE only; ',num2str(dr), ' Gbps']);
yline(3.8e-3,'DisplayName','HD-FEC','LineStyle','--','HandleVisibility','off');
xlabel('Received Optical Power (dBm)');
ylabel('Bit Error Rate (BER)');
title('Bit Error Rate vs. ROP');
set(gca,'yscale','log');
set(gca,'Box','on');
grid on;
grid minor
legend
cnt = cnt+1;
end
figure(223);
hold on; % Retain the plot so new points can be added without complete redraw
plot(datarates,sens_ffe,"LineWidth",1,"LineStyle","-","Marker",".","MarkerSize",10,"DisplayName",['Linewidth: ',num2str(laser_linewidth.*1e-6),' MHz']);
xlabel('Signal to Interference Ratio (dB)');
ylabel('Receiver Sensitivity');
title(['Bit Error Rate vs. SIR']);
grid on;
legend
function i = getIntersection(ber,rop)
%get intersection between rop curve and hd-fec limit
hdfec = 3.8e-3 .* ones(size(ber));
i = InterX([rop;hdfec'],[rop;ber']);
if isempty(i)
i = NaN;
else
i = i(1);
end
end

View File

@@ -0,0 +1,36 @@
% Define parameters
n = 1; % Define the memory length n+1 (e.g., 1 for duobinary, 2 for tribinary, 3 for tetrabinary)
M = 4; % Modulation order (e.g., 4 for QPSK)
d = randi([0 M-1], 1, 100); % Example input sequence d(k)
% Pre-coding: bPR(k) = (d(k) - bPR(k-1)) mod M
bPR = zeros(size(d));
%bPR(1) = d(1); % Initial condition for pre-coding
for k = 1:numel(d)-1
bPR(k+1) = mod( d(k) - bPR(k), M );
end
% Polybinary coding: Convolution with (1 + z^-1)^n
% Generate coefficients for the polybinary filter (1 + z^-1)^n
coeff = ones(1, n+1); % Initialize coefficients with ones
for i = 2:n+1
coeff(i) = nchoosek(n, i-1); % Binomial coefficient for expansion
end
% Apply convolution to get the polybinary coded sequence
polybinary_data = conv(bPR, coeff, "same");
% Modulo operation for decoding to stay within M levels
d_reconstructed = mod(polybinary_data, M);
% Check if reconstruction is correct
reconstruction_success = isequal(d, d_reconstructed);
figure(111)
hold on
stairs(d_reconstructed)
stairs(d)
disp(['Reconstruction success: ', num2str(reconstruction_success)]);

Binary file not shown.

View File

@@ -0,0 +1,55 @@
useprbs = 1;
M = 8;
randkey = 1;
%%%%% PRBS Generation in correct shape for Modulation Format %%%%%%
O = 18; %order of prbs
N = 2^(O-1); %length of prbs
[~,seed] = prbs(O,1); %initialize first seed of prbs
bitpattern=[];
if useprbs
for i = 1:log2(M)
[bitpattern(:,i),seed] = prbs(O,N,seed);
end
else
s = RandStream('twister','Seed',randkey);
for i = 1:log2(M)
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
end
if M == 6
bitpattern = reshape(bitpattern,[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
Tx_bits = Informationsignal(bitpattern);
Symbols_tx = PAMmapper(M,0).map(Tx_bits);
Symbols_tx.fs = fsym;
% Symbols = Duobinary().precode(Symbols_tx);
Symbols = Duobinary().encode(Symbols_tx);
% Symbols.spectrum("displayname","db","fignum",12)
Symbols = Duobinary().feedbackdetector(Symbols);
% Symbols.eye(fsym,M);
% Symbols = Duobinary().decode(Symbols);
Rx_bits = PAMmapper(M,0).demap(Symbols);
[~,error_num,ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",0,"skip_end",1,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);
%
figure;hold on
stairs(Rx_bits.signal(1:100,1),'LineWidth',2)
stairs(Tx_bits.signal(1:100,1),'LineStyle',':','LineWidth',2);
figure;hold on
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2)
stairs(Symbols.signal(1:100,1),'LineStyle',':','LineWidth',2);