183 lines
6.4 KiB
Matlab
183 lines
6.4 KiB
Matlab
classdef Optical_Multiplex < handle
|
||
% Takes a cell array of signals
|
||
% returns a total field signal
|
||
% WDM spacing is given in wavelength plan OR via delta_F
|
||
|
||
% The grid is stored in the output signal -> the demux will ideally
|
||
% look this up and use this as the demux frequencies...
|
||
|
||
% signal_cell = {Opt_sig_1, Opt_sig_2};
|
||
% Opt_sig_wdm = Optical_Multiplex("fs_in",Opt_sig.fs,"fs_out",4*Opt_sig.fs,...
|
||
% "lambda_center",1310,"random_key",0,"filtype",1,"B",200e9,"delta_f",400e9).process(signal_cell);
|
||
|
||
properties(Access=public)
|
||
fs_in
|
||
fs_out
|
||
lambda_center
|
||
delta_f
|
||
random_key
|
||
attenuation
|
||
B
|
||
mgauss
|
||
filtype
|
||
|
||
c = physconst('lightspeed')
|
||
f_center
|
||
f_T
|
||
lambda_T
|
||
df_T
|
||
end
|
||
|
||
methods (Access=public)
|
||
function obj = Optical_Multiplex(options)
|
||
%NAME Construct an instance of this class
|
||
% Detailed explanation goes here
|
||
|
||
arguments
|
||
options.fs_in
|
||
options.fs_out
|
||
options.lambda_center
|
||
options.B = 200e9
|
||
options.mgauss = 3
|
||
options.filtype = 2
|
||
|
||
options.delta_f = 0
|
||
options.random_key
|
||
options.attenuation = 0;
|
||
end
|
||
|
||
%
|
||
fn = fieldnames(options);
|
||
for n = 1:numel(fn)
|
||
try
|
||
obj.(fn{n}) = options.(fn{n});
|
||
end
|
||
end
|
||
|
||
|
||
end
|
||
|
||
function signalclass_out = process(obj,signalclasses_in)
|
||
|
||
% actual processing of the signal (steps 1. - 3.)
|
||
signalclass_out = obj.process_(signalclasses_in);
|
||
|
||
% append to logbook
|
||
lbdesc = ['Opt. Mux. '];
|
||
signalclass_out = signalclass_out.logbookentry(lbdesc);
|
||
|
||
end
|
||
|
||
function data_out = process_(obj,data_in)
|
||
%METHOD1 Summary of this method goes here
|
||
% Detailed explanation goes here
|
||
arguments(Input)
|
||
obj
|
||
data_in cell
|
||
end
|
||
|
||
% assert(data_in{1}.fs == obj.fs_in,'Sampling rate');
|
||
att = 1/10^(obj.attenuation/10);
|
||
N = numel(data_in);
|
||
w = obj.fs_out/data_in{1}.fs;
|
||
blocklen_in = length(data_in{1});
|
||
blocklen_out = w*blocklen_in;
|
||
freqaxis = linspace(-obj.fs_out/2, obj.fs_out/2, blocklen_out+1);
|
||
obj.f_center = obj.c/(obj.lambda_center.*1e-9);
|
||
|
||
if obj.random_key ~= 0
|
||
res = freqaxis(2)-freqaxis(1);
|
||
R = RandStream("twister","Seed",obj.random_key);
|
||
laser_frequency_imperfection = res .* round(R.randn(N,1)*10); %in mutliples of the fft resolution, i.e. the distance between two freq. bins
|
||
else
|
||
laser_frequency_imperfection = zeros(blocklen_in,1);
|
||
end
|
||
|
||
obj.f_T = [];
|
||
obj.df_T = [];
|
||
polrots = [];
|
||
for o = 1:N
|
||
|
||
if obj.delta_f ~= 0
|
||
% user defined a channel spacing in GHz. Build plan
|
||
% left and right from zero
|
||
obj.df_T(o) = (-length(data_in)/2-0.5+o) .* obj.delta_f;
|
||
obj.df_T(o) = obj.df_T(o)+ laser_frequency_imperfection(o);
|
||
|
||
obj.f_T = [obj.f_T obj.f_center+obj.df_T(o)];
|
||
else
|
||
%center frequencies of channels
|
||
obj.f_T = [obj.f_T obj.c/(data_in{o}.lambda)];
|
||
obj.lambda_T = [obj.lambda_T data_in{o}.lambda];
|
||
|
||
%difference between mid frequency of MUX and channels
|
||
obj.df_T = [obj.df_T obj.f_center - obj.f_T(o)];
|
||
end
|
||
|
||
% adapt frequency shifts to match the FFT grid! Find nearest grid point
|
||
[glitch(o),pos] = min(abs( freqaxis-obj.df_T(o) ));
|
||
obj.df_T(o) = freqaxis(pos);
|
||
|
||
polrots = [polrots, data_in{o}.polrot];
|
||
end
|
||
|
||
obj.lambda_T = obj.c ./ (obj.f_center-obj.df_T);
|
||
|
||
obj.B = 200e9; %200GHz
|
||
faxis = linspace(-obj.fs_out/2,obj.fs_out/2, blocklen_out+1);%generates arow vector faxis of blocklen+1 points linearly spaced between and including -para.fs/2 and para.fs/2
|
||
faxis = ifftshift(faxis(1:end-1));
|
||
|
||
switch obj.filtype
|
||
case 1
|
||
H =exp(-(faxis/obj.B).^(2*obj.mgauss)*log(2)*2^(2*obj.mgauss-1)).';
|
||
case 2
|
||
H=zeros(length(faxis),1);
|
||
H(find(abs(faxis)<=obj.B/2))=1;
|
||
case 3
|
||
H = 1;
|
||
end
|
||
|
||
x_envelopes = NaN([blocklen_out N]);
|
||
y_envelopes = x_envelopes;
|
||
|
||
% ---- OPTIMIZED: Pre-compute all LO phases as [blocklen_out × N] matrix ----
|
||
time_idx = (0:blocklen_out-1).'; % [blocklen_out × 1]
|
||
lo_phases = mod(2*pi * time_idx * obj.df_T / obj.fs_out, 2*pi); % [blocklen_out × N]
|
||
lo_all = cos(lo_phases) + 1i*sin(lo_phases); % [blocklen_out × N]
|
||
|
||
% Collect all resampled signals first (still requires loop due to cell array)
|
||
x_signals = zeros(blocklen_out, N);
|
||
y_signals = zeros(blocklen_out, N);
|
||
|
||
for o = 1:N
|
||
data_in_resampled = data_in{o}.resample("fs_out", obj.fs_out);
|
||
x_signals(:, o) = data_in_resampled.signal(:, 1);
|
||
y_signals(:, o) = data_in_resampled.signal(:, 2);
|
||
end
|
||
|
||
% ---- VECTORIZED: Batched FFT/IFFT for all channels ----
|
||
% Apply filter to all channels at once
|
||
x_filtered = ifft(fft(x_signals) .* H); % [blocklen_out × N]
|
||
y_filtered = ifft(fft(y_signals) .* H); % [blocklen_out × N]
|
||
|
||
% Apply attenuation and LO shift to all channels
|
||
x_envelopes = att .* x_filtered .* lo_all; % [blocklen_out × N]
|
||
y_envelopes = att .* y_filtered .* lo_all; % [blocklen_out × N]
|
||
|
||
data_out = data_in_resampled;
|
||
data_out.signal = [sum(x_envelopes,2), sum(y_envelopes,2)];
|
||
data_out.lambda = obj.lambda_T;
|
||
data_out.polrot = polrots;
|
||
|
||
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
|