- bring FWM plots back to life!

- some dispersion plots with the great help of chatGPT :-D
This commit is contained in:
Silas Oettinghaus
2025-10-17 11:50:27 +02:00
parent 99898da519
commit 3ea6439947
13 changed files with 720 additions and 62 deletions

View File

@@ -1,14 +1,15 @@
% Gitter für lambda0 und S0
lambda0_vec = linspace(1300,1320,200);
lambda0_vec = linspace(1260,1360,200);
S0_vec = linspace(0.06,0.1,200);
[Lambda0, S0] = meshgrid(lambda0_vec, S0_vec);
% Festen Betriebsparameter
lambda = 1293; % nm
L = 10; % km
L = 1; % km
% Dispersion berechnen (lineare Näherung)
D = S0 .* ( lambda - Lambda0 ) * L;
% D = (S0./4) .* ( lambda - (Lambda0.^4)./(lambda^3) ) * L;
%% 2D-Konturplot nur mit Linien und Text
figure('Color','w');

View File

@@ -0,0 +1,146 @@
%% ------------------------------------------------------------
% Contour plot: λ_null as function of bandwidth (f_target) and reach (L)
% ------------------------------------------------------------
% Parameters
lambda0 = 1310e-9; % [m]
S0 = 0.08; % [ps/(nm²·km)]
c = physconst('lightspeed');
% Sweep dimensions
f_targets = linspace(50e9, 120e9, 100); % [Hz] (x-axis)
L_values = linspace(0.5e3, 10e3, 100); % [m] (y-axis)
lambda_surface = zeros(numel(L_values), numel(f_targets));
Dacc_surface = zeros(numel(L_values), numel(f_targets));
% Outer loop over fiber length (since L must be scalar)
for iL = 1:numel(L_values)
L = L_values(iL);
[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0);
lambda_vec = 2*abs(lambda0 - lambda_vec);
if 0
fprintf('\n- %d km ------------------------------------\n',L);
fprintf(' f_null [GHz] lambda [nm] Dacc [ps/nm]\n');
fprintf('----------------------------------------------\n');
fprintf('%10.1f %8.2f %+8.3f\n',[f_targets(:)/1e9, lambda_vec(:)*1e9, Dacc_vec(:)].');
fprintf('----------------------------------------------\n\n');
end
lambda_surface(iL, :) = lambda_vec; % λ for each f_target
Dacc_surface(iL, :) = Dacc_vec; % corresponding accumulated dispersion
end
% Convert for plotting
lambda_surface_nm = lambda_surface * 1e9; % [nm]
L_km = L_values / 1000; % [km]
f_GHz = f_targets / 1e9; % [GHz]
%% Contour plot
figure('Color','w');
% Define wavelength contour levels [nm]
lambda_levels = [1260:10:1290, 1290:5:1300, 1300:2.5:1310];
lambda_levels = [100:-20:50, 50:-10:30,30:-5:0];
% Contour plot
contour(f_GHz, L_km, lambda_surface_nm, lambda_levels, ...
'LineWidth', 1.5, ...
'ShowText', 'on', ...
'LabelFormat', '%.0f nm');
% Colormap and colorbar
colormap((cbrewer2('RdYlGn',100)));
colorbar;
clim([0 100]);
% Axis formatting
xlabel('Signal Bandwidth [GHz]');
ylabel('Fiber length [km]');
legend('$\Delta \lambda$')
% X-axis ticks at 56 : 16 : 150 GHz
xticks(56:8:150);
grid on; box on;
%% Optional: overlay accumulated-dispersion contours
if 0
hold on;
[CS, h] = contour(f_GHz, L_km, Dacc_surface, 10, 'k--', 'LineWidth', 0.8);
clabel(CS, h, 'Color','k', 'FontSize',8);
end
function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0)
% lambda_for_first_null_full (stable, single-branch + validity checks)
% --------------------------------------------------------------------
% Computes the wavelength(s) at which the first IM/DD fading null
% occurs at frequency/ies f_target using the full dispersion model:
%
% D(lambda) = (S0/4)*(lambda - lambda0^4 / lambda^3)
%
% Restricted to the NORMAL-dispersion branch (λ < λ0),
% and valid only in the O-band (12601360 nm).
%
% Inputs:
% f_target - scalar or vector of target null frequencies [Hz]
% L - fiber length [m]
% lambda0 - zero-dispersion wavelength (ZDW) [m]
% S0 - dispersion slope at ZDW [ps/(nm²·km)]
%
% Outputs:
% lambda_vec - wavelength(s) [m] where first null occurs (clamped to O-band)
% Dacc_vec - accumulated dispersion(s) [ps/nm] (NaN if out of valid range)
% --------------------------------------------------------------------
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³)
% Define O-band boundaries (in meters)
lambda_min = 1255e-9;
lambda_max = 1361e-9;
% Force column vector
f_target = f_target(:);
N = numel(f_target);
lambda_vec = NaN(N,1);
Dacc_vec = NaN(N,1);
for k = 1:N
RHS = c * 0.5 / (f_target(k)^2 * L);
% Normal-dispersion branch (λ < λ0)
fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS;
% Limit the search to [λ_min, λ0)
try
lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]);
catch
% If the zero is not within bounds, skip this point
lambda_sol = NaN;
end
% Validate solution
if isnan(lambda_sol) || lambda_sol < lambda_min || lambda_sol > lambda_max
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
continue
end
% Compute D(lambda) and accumulated dispersion
D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km)
Dacc_val = D_lambda * (L/1000); % ps/nm
% Sanity bound on dispersion (avoid unphysical > ±100 ps/nm)
if abs(Dacc_val) > 100
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
else
lambda_vec(k) = lambda_sol;
Dacc_vec(k) = Dacc_val;
end
end
end

View File

@@ -0,0 +1,38 @@
%% ------------------------------------------------------------
% Plot: Maximum usable IM/DD bandwidth vs wavelength
% ------------------------------------------------------------
% Fiber and dispersion parameters
lambda0 = 1310e-9; % [m]
S0 = 0.08; % [ps/(nm²·km)]
L = 10000; % [m]
c = physconst('lightspeed');
% Wavelength range around ZDW
lambda_vec = linspace(1250e-9, 1350e-9, 200); % [m]
% Compute D(lambda) using full model
lambda_nm = lambda_vec * 1e9;
lambda0_nm = lambda0 * 1e9;
D_lambda = (S0/4) .* (lambda_nm - (lambda0_nm.^4) ./ (lambda_nm.^3)); % [ps/(nm·km)]
% Convert D to [s/m²]
D_si = D_lambda * 1e-6;
% Compute first null frequency (f) for each wavelength
f_null = sqrt(c*(0.5) ./ (abs(D_si).*lambda_vec.^2*L)); % [Hz]
% Plot
figure('Color','w');
plot(lambda_vec*1e9, f_null/1e9, 'LineWidth', 1.6);
grid on; box on;
xlabel('Wavelength [nm]');
ylabel('First Fading Null Frequency [GHz]');
title(sprintf('IM/DD Bandwidth Limit vs. Wavelength (L = %.1f km)', L/1000));
% Highlight useful bandwidth thresholds
yline(25, '--', '25 GHz','Color',[0.4 0.4 0.4],'LabelHorizontalAlignment','left');
yline(50, '--', '50 GHz','Color',[0.2 0.6 0.2],'LabelHorizontalAlignment','left');
yline(100,'--', '100 GHz','Color',[0.6 0.2 0.2],'LabelHorizontalAlignment','left');
legend('First fading notch (f_{null})','Location','best');

View File

@@ -0,0 +1,165 @@
%% Chromatic Dispersion Power Fading Demonstration
% ------------------------------------------------------------
% This script computes and visualizes power fading after
% photodiode detection caused by chromatic dispersion in IM/DD links.
%
% It also determines the wavelength λ that produces the first
% fading null at a specified RF frequency f_target using the
% full physical dispersion model:
%
% D(λ) = (S0/4) * (λ - λ0^4 / λ^3)
%
% and compares the analytic null frequency with simulation.
% ------------------------------------------------------------
% clear; close all; clc;
%% Fiber and wavelength parameters
lambda0 = 1310e-9; % Zero-dispersion wavelength (ZDW) [m]
S0 = 0.08; % Dispersion slope at ZDW [ps/(nm^2·km)]
L = 10000; % Fiber length [m]
alpha_dB = 0; % Attenuation [dB/m] (ignored here)
%% Target null frequency
f_targets = linspace(55e9,58e9,10);
f_targets = 56e9;
% f_targets = 80e9;
% Compute wavelength that gives the first null at f_target
[lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_targets, L, lambda0, S0);
% lambda_vec = 1293e-9;
fprintf('\n----------------------------------------------\n');
fprintf(' f_null [GHz] lambda [nm] Dacc [ps/nm]\n');
fprintf('----------------------------------------------\n');
fprintf('%10.1f %8.2f %+8.3f\n',[f_targets(:)/1e9, lambda_vec(:)*1e9, Dacc_vec(:)].');
fprintf('----------------------------------------------\n\n');
%% Frequency grid
f_simu = 500e9; % Simulation bandwidth [Hz]
N_freq = 500000;
faxis = linspace(-f_simu/2, f_simu/2, N_freq);
%% Derived fiber parameters
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/m³
% Convert wavelengths to nm for the D(lambda) model
lambda_nm = lambda_vec(end) * 1e9;
lambda0_nm = lambda0 * 1e9;
% Dispersion parameter [ps/(nm·km)]
D_lambda = (S0/4) * (lambda_nm - (lambda0_nm^4)/(lambda_nm^3));
% Convert to [s/m²]
D_si = D_lambda * 1e-6;
% β2 in [s²/m]
b2 = -D_si * lambda_vec(end)^2 / (2*pi*c);
%% IM/DD intensity response (simulation)
phi = 2*pi^2*b2*faxis.^2*L;
H_field_pos = exp(-1j*phi); % +f sideband
H_field_neg = exp(+1j*phi); % -f sideband
H_intensity = 0.5 * (H_field_pos + H_field_neg); % PD beating term
H_sim = abs(H_intensity);
%% Theoretical analytical IM/DD response
phi = 2*pi^2 * abs(b2) * faxis.^2 * L;
H_theoretical = abs(cos(phi));
%% Analytic first null (for verification)
f_null_analytic = sqrt(c*(0.5)/(abs(D_si)*lambda_vec(end)^2*L));
fprintf('Analytic first null from D,λ,L: %.2f GHz\n\n', f_null_analytic/1e9);
%% Plot
cols = linspecer(5);
figure('Color','w'); hold on; grid on; box on;
plot(faxis*1e-9, 10*log10(H_sim), 'DisplayName','$|H_{sim}|$ (IM/DD simulation)','Color',cols(1,:));
plot(faxis*1e-9, 10*log10(H_theoretical), 'DisplayName','|cos($\phi$)| (theory)','Color',cols(2,:),'LineStyle','--');
xline(f_targets(end)/1e9,'k:','LineWidth',1.2,'DisplayName','Target null (56 GHz)');
xline(f_null_analytic/1e9,'Color',[0.2 0.6 0.2],'LineStyle','-.','LineWidth',1.2,'DisplayName','Analytic null');
xlabel('Frequency [GHz]');
ylabel('Magnitude [dB]');
title(sprintf('Power Fading for %.2f nm, L = %.1f km',lambda_nm,L/1000));
legend('Location','best'); ylim([-30 0]);
%% Plot Bandwidth vs Lambda max
figure();
hold on;
plot(lambda_vec.*1e6,f_targets.*1e-9)
xlabel('wavelength');
ylabel('max. Bandwidth')
function [lambda_vec, Dacc_vec] = lambda_for_first_null_full(f_target, L, lambda0, S0)
% lambda_for_first_null_full (stable, single-branch + validity checks)
% --------------------------------------------------------------------
% Computes the wavelength(s) at which the first IM/DD fading null
% occurs at frequency/ies f_target using the full dispersion model:
%
% D(lambda) = (S0/4)*(lambda - lambda0^4 / lambda^3)
%
% Restricted to the NORMAL-dispersion branch (λ < λ0),
% and valid only in the O-band (12601360 nm).
%
% Inputs:
% f_target - scalar or vector of target null frequencies [Hz]
% L - fiber length [m]
% lambda0 - zero-dispersion wavelength (ZDW) [m]
% S0 - dispersion slope at ZDW [ps/(nm²·km)]
%
% Outputs:
% lambda_vec - wavelength(s) [m] where first null occurs (clamped to O-band)
% Dacc_vec - accumulated dispersion(s) [ps/nm] (NaN if out of valid range)
% --------------------------------------------------------------------
c = physconst('lightspeed');
S0_si = S0 * 1e3; % ps/(nm²·km) -> s/(m³)
% Define O-band boundaries (in meters)
lambda_min = 1255e-9;
lambda_max = 1361e-9;
% Force column vector
f_target = f_target(:);
N = numel(f_target);
lambda_vec = NaN(N,1);
Dacc_vec = NaN(N,1);
for k = 1:N
RHS = c * 0.5 / (f_target(k)^2 * L);
% Normal-dispersion branch (λ < λ0)
fun = @(lambda) -(S0_si/4).*(lambda - (lambda0^4)./(lambda.^3)).*lambda.^2 - RHS;
% Limit the search to [λ_min, λ0)
try
lambda_sol = fzero(fun, [lambda_min, lambda0 * 0.999]);
catch
% If the zero is not within bounds, skip this point
lambda_sol = NaN;
end
% Validate solution
if isnan(lambda_sol) || lambda_sol < lambda_min || lambda_sol > lambda_max
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
continue
end
% Compute D(lambda) and accumulated dispersion
D_lambda = (S0_si/4) * (lambda_sol - (lambda0^4)/(lambda_sol^3)) / 1e-6; % ps/(nm·km)
Dacc_val = D_lambda * (L/1000); % ps/nm
% Sanity bound on dispersion (avoid unphysical > ±100 ps/nm)
if abs(Dacc_val) > 100
lambda_vec(k) = NaN;
Dacc_vec(k) = NaN;
else
lambda_vec(k) = lambda_sol;
Dacc_vec(k) = Dacc_val;
end
end
end

View File

@@ -0,0 +1,120 @@
%% Matched Filter SNR Demonstration (Correct Timing)
% clear; close all; clc;
%% Parameters
M = 4; % QPSK
numSymbols = 1e6;
sps = 25; % samples per symbol
rolloff = 0.5;
EbNo_dB = 10;
%% Generate random data
data = randi([0 M-1], numSymbols, 1);
txSym = qammod(data, M, 'UnitAveragePower', true);
%% Root Raised Cosine filters
span = 64; % filter span in symbols
rrcTx = rcosdesign(rolloff, span, sps, 'sqrt');
rrcRx = rrcTx; % matched filter
txSignal2 = ifft(fft(rrcTx).*fft(txSym));
%% Transmit filtering (includes upsampling)
txSignal = upfirdn(txSym, rrcTx, sps, 1);
%% AWGN channel
rxSignal = awgn(txSignal, EbNo_dB + 10*log10(sps), 'measured');
%% Receiver matched filter
rxFilt = conv(rxSignal, rrcRx, 'same');
%% Symbol timing (group delay compensation)
delay = span * sps / 2; % total delay per filter is span*sps/2
rxAligned = rxFilt(delay+1 : end-delay);
%% Downsample to symbol rate
rxSampled = rxAligned(1:sps:end);
%% Align lengths
L = min(length(rxSampled), length(txSym));
rxSampled = rxSampled(1:L);
txSym = txSym(1:L);
%% Decision and BER
rxSym = qamdemod(rxSampled, M, 'UnitAveragePower', true);
[~, ber] = biterr(data(1:L), rxSym);
%% Compute effective SNR
snr_meas = 10*log10(mean(abs(txSym).^2) / mean(abs(txSym - rxSampled).^2));
fprintf('Measured BER: %.3e | Effective SNR: %.2f dB\n', ber, snr_meas);
%% Eye diagrams
eyediagram(rxSignal(1:4000), 2*sps);
title('Received Signal (Before Matched Filter)');
eyediagram(rxFilt(1:4000), 2*sps);
title('After Matched Filter (RRC)');
%% --------------------------------------------------------------
%% Spectrum analysis of shaped and filtered signals
%% --------------------------------------------------------------
Fs = sps; % normalized sample rate (symbol rate = 1)
Nfft = 2^16; % FFT size for high resolution
f = (-Nfft/2:Nfft/2-1)/Nfft * Fs; % normalized frequency axis (symbol-rate units)
% Spectra
S_tx = 20*log10(abs(fftshift(fft(txSignal, Nfft)))/max(abs(fft(txSignal, Nfft))));
S_rx = 20*log10(abs(fftshift(fft(rxFilt, Nfft)))/max(abs(fft(rxFilt, Nfft))));
% Unshaped (rectangular pulse) for comparison
txRect_unf = upfirdn(txSym, ones(1, sps), sps, 1);
S_rect = 20*log10(abs(fftshift(fft(txRect_unf, Nfft)))/max(abs(fft(txRect_unf, Nfft))));
% Plot
figure('Name','Spectrum after Pulse Shaping');
plot(f, S_rect, '--', 'DisplayName','Rectangular pulse');
hold on;
plot(f, S_tx, 'LineWidth',1.4, 'DisplayName','RRC (TX)');
plot(f, S_rx, 'LineWidth',1.4, 'DisplayName','After Matched Filter');
grid on;
xlabel('Normalized frequency (× symbol rate)');
ylabel('Magnitude [dB]');
title('Spectra Before and After RRC Pulse Shaping');
legend('Location','best');
xlim([-1.5 1.5]);
ylim([-60 0]);
%% --------------------------------------------------------------
%% Visualization: RRC and Raised-Cosine Frequency Responses
%% --------------------------------------------------------------
% Frequency axis for plotting (normalized to symbol rate)
Nfft = 4096;
H_rrc = fftshift(fft(rrcTx, Nfft));
H_rc = H_rrc .* H_rrc; % cascade of TX and RX RRC = full RC
f = linspace(-0.5, 0.5, Nfft); % normalized frequency (symbol-rate units)
figure('Name','Raised Cosine Filter Characteristics');
subplot(2,1,1);
plot(f, 20*log10(abs(H_rrc)/max(abs(H_rrc))), 'LineWidth', 1.5);
hold on;
plot(f, 20*log10(abs(H_rc)/max(abs(H_rc))), '--', 'LineWidth', 1.5);
grid on;
xlabel('Normalized frequency (× symbol rate)');
ylabel('Magnitude [dB]');
title(sprintf('RRC (rolloff = %.2f) and Full RC Spectrum', rolloff));
legend('Root Raised Cosine','Raised Cosine (TX×RX)','Location','best');
ylim([-60 5]);
subplot(2,1,2);
t = (-span*sps/2 : span*sps/2) / sps; % time axis in symbol durations
plot(t, rrcTx, 'LineWidth', 1.5);
grid on;
xlabel('Time [symbols]');
ylabel('Amplitude');
title('RRC Impulse Response');