BCJR implementation

WDM code added (Pol Cont., Opt MUX/DEMUX, Opt Atten, DP_Fiber) -> the codebase is not optimized to always work with dp signals!
This commit is contained in:
Silas Oettinghaus
2025-09-17 13:58:58 +02:00
parent f4a22d23a2
commit 4099f6820f
37 changed files with 3643 additions and 593 deletions

View File

@@ -5,6 +5,7 @@ classdef Opticalsignal < Signal
properties
nase
lambda
polrot
end
@@ -18,6 +19,7 @@ classdef Opticalsignal < Signal
options.logbook
options.lambda
options.nase
options.polrot
end
obj = obj@Signal(signal);

View File

@@ -108,6 +108,7 @@ classdef Signal
options.logbook
options.nase
options.lambda
options.polrot
end
fn = fieldnames(options);
@@ -122,7 +123,7 @@ classdef Signal
%convert to optical
o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase);
o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase,"polrot",options.polrot);
elseif isa(obj,'Informationsignal')
@@ -141,7 +142,7 @@ classdef Signal
arguments
obj
options.fignum = []
options.fignum = randi(1000)
options.displayname = [];
options.timeframe = 0;
options.clear = 0;
@@ -187,7 +188,7 @@ classdef Signal
ylabel('Amplitude');
end
% Add legend if not already present
if isempty(get(gca, 'Legend'))
legend;
@@ -350,21 +351,30 @@ classdef Signal
options.normalizeTo0dB = 0;
options.max_num_lines = []; % Leave empty or omit to disable line rotation
options.fft_length = [];
% --- NEW options ---
options.useWavelengthAxis (1,1) logical = false % plot x-axis in wavelength
options.lambda0_nm (1,1) double = 1310 % center wavelength [nm]
end
if isempty(options.fft_length)
options.fft_length = 2^(nextpow2(length(obj.signal))-9);
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","power","mean");
w = w.*1e-9;
[p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ...
options.fft_length/2, options.fft_length, ...
obj.fs, "centered", "power", "mean");
f_GHz = f_Hz*1e-9; % keep frequency vector for frequency axis
else
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,"centered","power","mean");
[p_lin,f_rad] = pwelch(obj.signal, hanning(options.fft_length), ...
options.fft_length/2, options.fft_length, ...
"centered", "power", "mean");
% In normalized mode, pwelch returns rad/sample centered on 0.
% We'll keep f_rad for the x-axis in that mode.
end
if options.normalizeTo0dB
p_lin = p_lin./ max(p_lin);
p_lin = p_lin ./ max(p_lin);
p_dbm = 10*log10(p_lin); % normalized to 0 dB
ylab = "Normalized PSD";
else
@@ -372,36 +382,69 @@ classdef Signal
ylab = "Power (dB/Hz)";
end
% --- If requested, build wavelength axis from frequency offset ---
if options.useWavelengthAxis && options.normalizeToNyquist == 0
c = physconst('LightSpeed'); % [m/s]
lambda0_m = options.lambda0_nm*1e-9; % center wavelength [m]
f_c = c / lambda0_m; % carrier frequency [Hz]
% exact mapping
f_abs = f_c + f_Hz; % absolute frequency [Hz]
lambda_m = c ./ f_abs; % wavelength [m]
lambda_nm = lambda_m * 1e9; % wavelength [nm]
% assign axis
x_vec = lambda_nm(:);
x_label = "Wavelength [nm]";
% Sort to ensure axis is ascending
[x_vec, sortIdx] = sort(x_vec, 'ascend');
p_dbm = p_dbm(sortIdx, :);
else
% Frequency or normalized axes
if options.normalizeToNyquist == 0
x_vec = f_GHz;
x_label = "Frequency in GHz";
else
x_vec = f_rad; % normalized frequency in rad/sample
x_label = "Normalized Frequency";
end
end
figure(options.fignum);
ax = gca;
hold on
if isempty(options.color)
hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1);
else
hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1,'Color',options.color);
for s = 1:min(size(p_dbm))
if isempty(options.color)
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1);
else
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1, 'Color', options.color);
end
end
% If user wants to limit the number of lines, check and remove old lines
% Limit number of lines if requested
if ~isempty(options.max_num_lines) && options.max_num_lines > 0
allLines = findall(ax, 'Type', 'Line');
if length(allLines) > options.max_num_lines
% Sort lines by creation order. Usually, the oldest lines appear first in allLines.
% If needed, you can sort by UserData or other criteria.
numToRemove = length(allLines) - options.max_num_lines;
delete(allLines(1:numToRemove));
end
end
if options.normalizeToNyquist == 0
xlabel("Frequency in GHz");
edgetick = 2^(nextpow2(obj.fs*1e-9));
xticks(-edgetick:32:edgetick);
xlim([100*round(min(w)/100,1)-10, 100*round(max(w)/100,1)+10])
xlim([-128 128]);%256GSa/s
% Axis labels and limits
xlabel(x_label);
if options.useWavelengthAxis && options.normalizeToNyquist == 0
xlim([min(x_vec) max(x_vec)]);
else
xlabel("Normalized Frequency");
xlim([-pi, pi]);
if options.normalizeToNyquist == 0
% Keep your existing freq handling (you can fine-tune as needed)
% xlim([-128 128]); % example for 256 GSa/s if desired
xlim([min(x_vec) max(x_vec)]);
else
xlim([-pi, pi]);
end
end
ylabel(ylab);
@@ -409,24 +452,23 @@ 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([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
end
if options.normalizeTo0dB
% ylim([-40, 3]);
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
else
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
ylim([floor(min(p_dbm,[],'all'))-3, ceil(max(p_dbm,[],'all'))+3]);
end
yticks(-200:10:10);
grid on;% grid minor;
grid on;
legend
% legend('Interpreter','none');
end
function move_it_spectrum(obj,options)
arguments
obj
@@ -554,7 +596,7 @@ classdef Signal
options.unit power_notation = power_notation.dBm
end
pow = mean(abs(obj.signal).^2);
pow = sum(mean(abs(obj.signal).^2));
switch options.unit
case power_notation.dBm
@@ -669,9 +711,9 @@ classdef Signal
options.fs_ref = 0;
options.debug_plots = 0;
end
S = {};
inverted = -1;
sequenceFound = 0;
@@ -715,25 +757,25 @@ classdef Signal
findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend')
end
shifts = lags(pkpos);
sequenceStarts = shifts;
shifts = shifts(shifts>=0);
if numel(shifts) > 0
%Cut occurences of ref signal from signal (only positive shifts)
if all(sign(co(pkpos))==-1)
inverted = 1;
end
for c = shifts
sig = obj.delay(-c,'mode','samples');
sig.signal = sig.signal(1:length(b));% .* -inverted;
S{end+1,1} = sig;
end
% %return/keep the sinal with the highest correlation (only within positive shifts)
% [~,idx]=max(pks);
% obj.signal = S{idx}.signal;
@@ -741,7 +783,7 @@ classdef Signal
% swap = S{1};
% S{1} = S{idx};
% S{idx} = swap;
for c = 1:numel(shifts)
S{c}.logbook = [];
end
@@ -770,10 +812,6 @@ classdef Signal
end
%%
function obj = filter(obj,a,b)
@@ -934,8 +972,8 @@ classdef Signal
nn=histcounts(data_ind_y(n,:),1:histpoints+1);
hist_data(:,n)=flip(nn.'); %without flip, the eye is upside down :-(
end
plot_data = 20*log10(hist_data);
plot_data(plot_data==-Inf) = 0;
@@ -970,7 +1008,7 @@ classdef Signal
end
xlabel('Time in ps')
% add information
@@ -1091,7 +1129,7 @@ classdef Signal
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
xticklabels(x_tickstring);
grid off
grid off
end