diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index f75ddd4..edccd25 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -689,10 +689,17 @@ classdef Signal %estimate start pos of signal maxpeaknum = floor(length(a)/length(b)); - [pks,pkpos,w,p] = 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 @@ -700,6 +707,8 @@ classdef Signal findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend') end + + shifts = lags(pkpos); shifts = shifts(shifts>=0); @@ -752,6 +761,12 @@ classdef Signal end + + + + + + %% function obj = filter(obj,a,b) diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 32de7c5..d3bc066 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -677,8 +677,6 @@ classdef DBHandler < handle delete(fig); end - - function filterParams = promptFilterParameters(obj) % promptFilterParameters Prompts the user to enter filter parameters using a % custom scrollable UI with dropdowns. diff --git a/Datatypes/equalizer_structure.m b/Datatypes/equalizer_structure.m index c5bdeb2..7e338bd 100644 --- a/Datatypes/equalizer_structure.m +++ b/Datatypes/equalizer_structure.m @@ -5,8 +5,8 @@ classdef equalizer_structure < int32 vnle (1) vnle_pf_mlse (2) % db_precoded (3) - % db_encoded (4) vnle_db_mlse (3) + db_encoded (4) end end \ No newline at end of file diff --git a/Functions/EQ_structures/duobinary_signaling.m b/Functions/EQ_structures/duobinary_signaling.m index 73f131c..69c0449 100644 --- a/Functions/EQ_structures/duobinary_signaling.m +++ b/Functions/EQ_structures/duobinary_signaling.m @@ -1,18 +1,96 @@ -function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits) - %Duobinary Signaling +function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits,options) +%Duobinary Signaling +arguments + eq_ + mlse_ + M + rx_signal + tx_symbols + tx_bits + options.postFFE = []; +end - [eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols); - eq_signal = mlse_.process(eq_signal); +[eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols); - eq_signal = Duobinary().encode(eq_signal); - eq_signal = Duobinary().decode(eq_signal); +if ~isempty(options.postFFE) + [eq_signal,eq_noise] = options.postFFE.process(eq_signal,tx_symbols); +end - % M = numel(unique(eq_signal.signal)); - rx_bits = PAMmapper(M,0).demap(eq_signal); - [~,numErrors,ber,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); +eq_signal = mlse_.process(eq_signal); + +eq_signal = Duobinary().encode(eq_signal); +eq_signal = Duobinary().decode(eq_signal); + +% M = numel(unique(eq_signal.signal)); +rx_bits = PAMmapper(M,0).demap(eq_signal); + +[bits_db,errors_db,ber_db,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + +eq_package.ber = ber_db; + +resultsDBsignaling = struct( ... + 'result_id', NaN, ... % + 'run_id', NaN, ... % Beispielhafte Run-ID + 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle + 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit + 'numBits', bits_db, ... % Beispiel: 1.000.000 Bits + 'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler + 'BER_precoded', [], ... % BER = 120 / 1.000.000 + 'numBitErr_precoded', [], ... % Beispiel: 120 Bitfehler + 'BER', ber_db, ... % BER = 120 / 1.000.000 + 'SNR', [], ... % Beispielhafte SNR + 'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array + 'GMI', [], ... % Beispielhafter GMI-Wert + 'AIR', [], ... % Beispielhafter AIR-Wert + 'EVM', [], ... % Beispielhafte EVM + 'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array + 'Alpha', [] ... % Beispielhafter Alpha-Wert + ); + +if ~isempty(options.postFFE) + npostFFE = options.postFFE.order; +else + npostFFE = 0; +end + +equalizerConfigDBsignaling = struct( ... + 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt + 'equalizer_structure', int32(equalizer_structure.db_encoded), ... % Beispiel: 1 (z.B. für vnle) + 'M', M, ... % Ordnung der PAM-Konstellation + 'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String + 'db_target', 1, ... % 0 oder 1 + 'diff_precode', 1, ... % 0 oder 1 + 'postFFE', ~isempty(options.postFFE), ... % Beispielwert + 'NpostFFE', npostFFE, ... % Beispielwert + 'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung + 'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung + 'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung + 'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung + 'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung + 'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung + 'K', eq_.K, ... % Samples pro Symbol + 'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap + 'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) + 'training_length', eq_.training_length, ... % Anzahl Trainingssymbole + 'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe + 'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) + 'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung) + 'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung) + 'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD + 'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus + 'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung) + 'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung) + 'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung) + 'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD + 'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String + 'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert + 'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare + 'config_hash', NaN ... + ); + +eq_package.resultsDBsignaling = resultsDBsignaling; +eq_package.equalizerConfigDBsignaling = equalizerConfigDBsignaling; - eq_package.ber = ber; - end \ No newline at end of file diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index cbb880e..8d66efb 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -30,35 +30,42 @@ mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quanti % precoding to mitigate error propagation, most prominently used in % combination with duobinary signaling to avoid catastrophic error % behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) - -% takes: -% -> eq_signal_hd: hard decision signal after eq -% -> tx_symbols: that where used as reference for eq - switch options.precode_mode - case db_mode.db_emulate - mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M); - mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M); + case db_mode.no_db + % TX Data is not precoded: + + % A) Emulate diff precoding + mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M); + mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M); tx_symbols_precoded = Duobinary().encode(tx_symbols); tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); - tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); + tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); - case db_mode.db_discard + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded); + [~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % normal dsp for precoded sequence == discard/omit/ignore precode - tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); - - case db_mode.db_encoded - - % normal DB encoded data (only for 10KM) + %B) Just determine BER + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); case db_mode.db_precoded - mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M); - mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M); + % Daten SIND TATSÄCHLICH precoded auf TX Seite: + + % A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here! + mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M); + mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M); + rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded); + [~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + % B) Omit the Coding by comparing with demapped TX symbol sequence + + tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); + [bits_db,errors_db,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); end @@ -75,6 +82,8 @@ resultsDBtgt = struct( ... 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit 'numBits', bits_db, ... % Beispiel: 1.000.000 Bits 'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler + 'BER_precoded', ber_db_diff_precoded, ... % BER = 120 / 1.000.000 + 'numBitErr_precoded', errors_db_diff_precoded, ... % Beispiel: 120 Bitfehler 'BER', ber_db, ... % BER = 120 / 1.000.000 'SNR', [], ... % Beispielhafte SNR 'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array diff --git a/Functions/EQ_structures/vnle_postfilter_mlse.m b/Functions/EQ_structures/vnle_postfilter_mlse.m index aa5e716..35ea356 100644 --- a/Functions/EQ_structures/vnle_postfilter_mlse.m +++ b/Functions/EQ_structures/vnle_postfilter_mlse.m @@ -1,118 +1,128 @@ function [eq_package] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,rx_signal,tx_symbols,tx_bits,options) - + arguments - eq_ - pf_ - mlse_ - M - rx_signal - tx_symbols - tx_bits + eq_ + pf_ + mlse_ + M + rx_signal + tx_symbols + tx_bits options.precode_mode db_mode options.showAnalysis = 0; options.eth_style_symbol_mapping = 0; - options.postFFE = []; + options.postFFE = []; options.database = []; end - %FFE or VNLE - [eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); +%FFE or VNLE +[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); - if ~isempty(options.postFFE) - [eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols); - end +if ~isempty(options.postFFE) + [eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols); +end - eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd); +eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd); - mlse_sig_sd = pf_.process(eq_signal_sd,eq_noise); +mlse_sig_sd = pf_.process(eq_signal_sd,eq_noise); - mlse_.DIR = pf_.coefficients; - % [mlse_sig_hd,mlse_sig_sd] = mlse_.process(mlse_sig_sd,tx_symbols); - mlse_sig_sd = mlse_.process(mlse_sig_sd); +mlse_.DIR = pf_.coefficients; +% [mlse_sig_hd,mlse_sig_sd] = mlse_.process(mlse_sig_sd,tx_symbols); +mlse_sig_sd = mlse_.process(mlse_sig_sd); - mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); +mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); - % precoding to mitigate error propagation, most prominently used in - % combination with duobinary signaling to avoid catastrophic error - % behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) +% precoding to mitigate error propagation, most prominently used in +% combination with duobinary signaling to avoid catastrophic error +% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) - % takes: - % -> M - % -> eq_signal_hd: hard decision signal after eq - % -> tx_symbols: that where used as reference for eq +switch options.precode_mode - switch options.precode_mode - case db_mode.db_emulate - % re - eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M); - eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M); + case db_mode.no_db + % TX Data is not precoded: - mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M); - mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M); + % A) Emulate diff precoding + eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd,"M",M); + eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded,"M",M); + + mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M); + mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M); tx_symbols_precoded = Duobinary().encode(tx_symbols); tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); - tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); + tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); - case db_mode.db_discard + rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded); + [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded); + [~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + %B) Just determine BER + rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); + [bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + case db_mode.db_precoded + + % Daten SIND TATSÄCHLICH precoded auf TX Seite: + + % A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here! + eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd,"M",M); + eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded,"M",M); + rx_bits_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded); + [~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M); + mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M); + rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded); + [~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + % B) Omit the Coding by comparing with demapped TX symbol sequence - % normal dsp for precoded sequence == discard/omit/ignore precode tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); + rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); + [bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - case db_mode.db_encoded + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); + [bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - % normal DB encoded data (only for 10KM) +end - case db_mode.db_precoded +% METRICS OF VNLE SD Signal: +[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal); +[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000); +air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M)); +[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols); - eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M); - eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M); +% METRICS OF MLSE (HD-VITERBI) +pf_.ncoeff = 1; +pf_.process(eq_signal_sd,eq_noise); +alpha = pf_.coefficients(2); - mlse_sig_hd = Duobinary().encode(mlse_sig_hd,"M",M); - mlse_sig_hd = Duobinary().decode(mlse_sig_hd,"M",M); +eq_package.ber_mlse = ber_mlse; +eq_package.ber_vnle = ber_vnle; +eq_package.evm_vnle_total = evm_vnle_total; +eq_package.evm_vnle_lvl = evm_vnle_lvl; +eq_package.gmi = gmi_vnle; - end +eq_package.eq = eq_; +eq_package.pf = pf_; +eq_package.mlse = mlse_; - % METRICS OF VNLE % - rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd); - [bits_vnle,errors_vnle,ber_vnle,errorIndice_vnle] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - [snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal); - - % correct TUM implementation of AIR - [gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000); - gmi_vnle = min(max(abs(gmi_vnle),0.1),0); %set to zero if no convergence of gmi below 0.1 to avoid negative or any other "dumb" value - air_vnle = tx_symbols.fs .* floor(log2(8)*10)/10 .* gmi_vnle; - - [evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols); - - % METRICS OF MLSE (HD-VITERBI) - rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); - [bits_mlse,errors_mlse,ber_mlse,errorIndice_mlse]= calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - pf_.ncoeff = 1; - pf_.process(eq_signal_sd,eq_noise); - alpha = pf_.coefficients(2); - - - eq_package.ber_mlse = ber_mlse; - eq_package.ber_vnle = ber_vnle; - eq_package.evm_vnle_total = evm_vnle_total; - eq_package.evm_vnle_lvl = evm_vnle_lvl; - eq_package.gmi = gmi_vnle; - - eq_package.eq = eq_; - eq_package.pf = pf_; - eq_package.mlse = mlse_; - - resultsVNLE = struct( ... +resultsVNLE = struct( ... 'result_id', NaN, ... % 'run_id', NaN, ... % Beispielhafte Run-ID 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit + 'BER', ber_vnle, ... % BER = 120 / 1.000.000 'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits 'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler - 'BER', ber_vnle, ... % BER = 120 / 1.000.000 + 'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000 + 'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler 'SNR', snr_vnle, ... % Beispielhafte SNR 'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array 'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert @@ -120,15 +130,15 @@ end 'EVM', evm_vnle_total, ... % Beispielhafte EVM 'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array 'Alpha', [] ... % Beispielhafter Alpha-Wert -); + ); - if ~isempty(options.postFFE) - npostFFE = options.postFFE.order; - else - npostFFE = 0; - end +if ~isempty(options.postFFE) + npostFFE = options.postFFE.order; +else + npostFFE = 0; +end - equalizerConfigVNLE = struct( ... +equalizerConfigVNLE = struct( ... 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt 'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle) 'M', M, ... % Ordnung der PAM-Konstellation @@ -158,18 +168,20 @@ end 'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung) 'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD 'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare - 'config_hash', NaN ... -); + 'config_hash', NaN ... + ); - resultsMLSE = struct( ... +resultsMLSE = struct( ... 'result_id', NaN, ... % 'run_id', NaN, ... % Beispielhafte Run-ID 'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle 'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit + 'BER', ber_mlse, ... % BER = 120 / 1.000.000 'numBits', bits_mlse, ... % Beispiel: 1.000.000 Bits 'numBitErr', errors_mlse, ... % Beispiel: 120 Bitfehler - 'BER', ber_mlse, ... % BER = 120 / 1.000.000 + 'BER_precoded', ber_mlse_diff_precoded, ... % BER = 120 / 1.000.000 + 'numBitErr_precoded', errors_mlse_diff_precoded, ... % Beispiel: 120 Bitfehler 'SNR', [], ... % Beispielhafte SNR 'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array 'GMI', [], ... % Beispielhafter GMI-Wert @@ -178,11 +190,10 @@ end 'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array 'Alpha', alpha, ... % Beispielhafter Alpha-Wert 'MLSE_dir', jsonencode([mlse_.DIR])... -); + ); - - equalizerConfigMLSE = struct( ... +equalizerConfigMLSE = struct( ... 'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt 'equalizer_structure', int32(equalizer_structure.vnle_pf_mlse), ... % Beispiel: 1 (z.B. für vnle) 'M', M, ... % Ordnung der PAM-Konstellation @@ -214,43 +225,43 @@ end 'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String 'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert 'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare - 'config_hash', NaN ... -); - eq_package.resultsVNLE = resultsVNLE; - eq_package.resultsMLSE = resultsMLSE; - eq_package.equalizerConfigVNLE = equalizerConfigVNLE; - eq_package.equalizerConfigMLSE = equalizerConfigMLSE; + 'config_hash', NaN ... + ); +eq_package.resultsVNLE = resultsVNLE; +eq_package.resultsMLSE = resultsMLSE; +eq_package.equalizerConfigVNLE = equalizerConfigVNLE; +eq_package.equalizerConfigMLSE = equalizerConfigMLSE; - % eq_package.vnle_out = eq_signal_sd; - if options.showAnalysis +% eq_package.vnle_out = eq_signal_sd; +if options.showAnalysis - % fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl); + % fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl); - fprintf('VNLE BER: %.2e \n',ber_vnle); + fprintf('VNLE BER: %.2e \n',ber_vnle); - fprintf('MLSE BER: %.2e \n',ber_mlse); + fprintf('MLSE BER: %.2e \n',ber_mlse); - showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients); - + showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients); - rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal'); - tx_symbols.spectrum("normalizeTo0dB",1,"fignum",337,'displayname','Tx Signal'); + rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal'); - showLevelHistogram(eq_signal_sd,tx_symbols) - % showLevelHistogram(mlse_sig_sd,tx_symbols) + tx_symbols.spectrum("normalizeTo0dB",1,"fignum",337,'displayname','Tx Signal'); - showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients'); + showLevelHistogram(eq_signal_sd,tx_symbols) + % showLevelHistogram(mlse_sig_sd,tx_symbols) - showEQNoiseSNR(tx_symbols,eq_noise,"displayname",'vnle snr','fignum',101); + showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients'); - %%% EQ SNR Spectrum %230 - %snr - + showEQNoiseSNR(tx_symbols,eq_noise,"displayname",'vnle snr','fignum',101); - % showErrorBurstCount(eq_signal_sd,tx_symbols) - + %%% EQ SNR Spectrum %230 + %snr - end + + % showErrorBurstCount(eq_signal_sd,tx_symbols) + + +end end \ No newline at end of file diff --git a/Functions/Theory/modifiedGodardTimingRecovery.m b/Functions/Theory/modifiedGodardTimingRecovery.m new file mode 100644 index 0000000..d8a5214 --- /dev/null +++ b/Functions/Theory/modifiedGodardTimingRecovery.m @@ -0,0 +1,62 @@ +function tau_error = modifiedGodardTimingRecovery(rx, N, eta, beta) +% modifiedGodardTimingRecovery +% +% This function estimates the symbol timing error using the modified Godard +% approach in the frequency domain as described in: +% +% "Modified Godard Timing Recovery for Non-Integer Oversampling Receivers" +% Appl. Sci. 2017, 7, 655. :contentReference[oaicite:0]{index=0}​:contentReference[oaicite:1]{index=1} +% +% Inputs: +% rx - Received time-domain signal (vector) +% N - FFT size (should be an even integer) +% eta - Effective oversampling factor used for timing recovery (eta > 1) +% beta - Roll-off related parameter (0 < beta <= 1) +% +% Output: +% tau_error - Estimated timing error (in sample units) +% +% Implementation Notes: +% 1. The function computes an N-point FFT of the first N samples of rx. +% 2. It then determines an offset (Delta) defined as: +% offset = round((1 - 1/eta) * N) +% 3. To avoid index overflow, the summation is taken over indices k from 1 to +% floor(N/2) - offset. +% 4. The timing error is estimated as: +% tau_error = ( (1+beta)/(2*eta*N - 1) * sum(phase difference) ) / (2*pi) +% where the phase difference is (angle(R(k)) - angle(R(k+offset))) +% +% Make sure that the input signal rx contains at least N samples. + +% Check input length +if length(rx) < N + error('Input signal length must be at least N.'); +end + +% Compute the N-point FFT of the first N samples of rx +R = fft(rx(1:N), N); + +% Determine the offset based on the oversampling factor (eta) +offset = round((1 - 1/eta) * N); + +% Define the summation range to avoid index overflow +k_min = 1; +k_max = floor(N/2) - offset; +if k_max < k_min + error('Chosen parameters result in an empty summation range. Adjust N, eta, or beta.'); +end + +% Compute the sum of phase differences over the selected frequency bins +phase_diff_sum = 0; +for k = k_min:k_max + phase_k = angle(R(k)); + phase_k_offset = angle(R(k + offset)); + phase_diff_sum = phase_diff_sum + (phase_k - phase_k_offset); +end + +% Normalization factor as per the modified Godard algorithm +norm_factor = (1 + beta) / (2 * eta * N - 1); + +% Estimate the timing error in sample units +tau_error = (norm_factor * phase_diff_sum) / (2 * pi); +end diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/analyze_mpi.m b/projects/HighSpeedExperiment_2024/auswertung MPI/analyze_mpi.m index 7381091..eb7ca85 100644 --- a/projects/HighSpeedExperiment_2024/auswertung MPI/analyze_mpi.m +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/analyze_mpi.m @@ -6,10 +6,10 @@ if 1 uloops = struct; uloops.precomp = [0,1]; uloops.db_precode = [0,1]; - uloops.bitrate = [224,336,360,390,420,448].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI + uloops.bitrate = [420].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI % uloops.laser_wavelength = [1293,1297.5,1302,1306.5,1310,1313.4,1318,1322.7,1327.4]; uloops.laser_wavelength = [1310]; - uloops.M = [4,6,8]; + uloops.M = [4]; uloops.link_length = [1]; % 1,2,3,5,6,8,10 wh = DataStorage(uloops); wh.addStorage("ber"); diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi.m b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi.m index 1796127..8c6b5be 100644 --- a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi.m +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi.m @@ -203,7 +203,7 @@ for iatt = 1:numel(dataTable.interference_attenuation) if ~found Raw_signal = load([basePath, char(dataTable.rx_raw_path(1))]); Raw_signal = Raw_signal.Scpe_sig_raw; - [~,Scpe_cell,found] =Raw_signal.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",0); + [~,Scpe_cell,found] =Raw_signal.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",1); end if ~found @@ -217,16 +217,6 @@ for iatt = 1:numel(dataTable.interference_attenuation) end end - - % - % Raw_signal = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.55,"fs",Raw_signal.fs,"filterType",filtertypes.gaussian,"active",true).process(Raw_signal); - % - % Scpe_cell{1}.eye(fsym,M,"displayname",'eye','fignum',227); - % - % Raw_signal.spectrum("normalizeTo0dB",0,"fignum",11,"fft_length",2^12); - % Raw_signal.move_it_spectrum("fignum",334); - % Raw_signal.move_it_spectrum("fignum",334); - fsym = Symbols.fs; if db_precode @@ -249,13 +239,7 @@ for iatt = 1:numel(dataTable.interference_attenuation) Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); - % - % Pform = Pulseformer("fsym",Scpe_sig.fs,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha,"matched",0); - % - % Scpe_sig_matched = Pform.process(Scpe_sig); - % - % Scpe_sig.spectrum("normalizeTo0dB",0,"fignum",336,"displayname","scope "); - % Scpe_sig_matched.spectrum("normalizeTo0dB",0,"fignum",336,"displayname","matched"); + %%% EQUALIZING @@ -277,6 +261,7 @@ for iatt = 1:numel(dataTable.interference_attenuation) vnle_dfe_package{iatt,occ} = result; end + %%%%% VNLE + PF + MLSE %%%% if 1 @@ -299,7 +284,6 @@ for iatt = 1:numel(dataTable.interference_attenuation) end - %%%%% Duobinary Targeting %%%% if 1 diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m new file mode 100644 index 0000000..c34aa7d --- /dev/null +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_mpi_database.m @@ -0,0 +1,61 @@ + +% 1) Find RUN ID's + +basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +database = DBHandler("pathToDB",[basePath,'silas_labor.db']); + +filterParams = database.tables; +filterParams.Configurations = struct( ... + 'bitrate', [], ... %[224,336,360,390,420,448] + 'db_mode', [], ... + 'fiber_length', [], ... + 'interference_attenuation',[], ... + 'is_mpi', 0, ... + 'pam_level', [], ... + 'rop_attenuation', 0 ... + ); + +selectedFields = {'Runs.run_id',... + 'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength',... + 'Configurations.precomp_amp','Measurements.power_rop','Measurements.power_pd_in','Configurations.v_bias','Configurations.is_mpi',... + 'Configurations.interference_attenuation','Configurations.rop_attenuation'}; + +[dataTable,sql_query] = database.queryDB(filterParams, selectedFields); + +% only the rows without BER so far +% dataTable = dataTable(dataTable.BER == 0,:); +dataTable = dataTable((dataTable.fiber_length ~= 1),:); + +% Ensure a parallel pool is running +pool = gcp('nocreate'); +if isempty(pool) + pool = parpool; + % stop all forgotten or unfetched jobs from queue +elseif ~isempty(pool.FevalQueue.QueuedFutures) || ~isempty(pool.FevalQueue.RunningFutures) + oldq = length(pool.FevalQueue.QueuedFutures) + length(pool.FevalQueue.RunningFutures); + pool.FevalQueue.cancelAll + fprintf('Canceled %d unfetched jobs from old queue.', oldq); +end + +% Number of tasks to submit (one per run_id) +nTasks = height(dataTable); +futures = parallel.FevalFuture.empty(); + +% Submit each DSP run as a parallel task using parfeval +for i = 1:nTasks + % Extract the run_id (other parameters could be passed if needed) + runID = dataTable.run_id(i); + % Submit the function call to dsp_run_id (assuming it returns no output, hence 0 outputs) + futures(i) = parfeval(pool, @dsp_run_id, 0, runID, "max_occurences", 15, "append_to_db", 1); +end + +% Set up a waitbar to monitor progress +h = waitbar(0, 'Processing DSP runs...'); +while ~all(strcmp({futures.State}, 'finished')) + finishedCount = sum(strcmp({futures.State}, 'finished')); + waitbar(finishedCount / nTasks, h); + pause(0.1); +end +delete(h); + +fprintf('All DSP runs processed.\n'); diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m new file mode 100644 index 0000000..ebb7dfc --- /dev/null +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/dsp_run_id.m @@ -0,0 +1,194 @@ +function [output] = dsp_run_id(run_id,options) + +arguments + run_id + options.append_to_db = 0; + options.max_occurences = 4; + options.parameters = struct(); +end + +basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +database = DBHandler("pathToDB",[basePath,'silas_labor.db']); + +filterParams = database.tables; +filterParams.Configurations = struct('run_id', run_id); + +selectedFields = {'Runs.run_id','Runs.tx_bits_path', 'Runs.tx_symbols_path', 'Runs.rx_sync_path','Runs.rx_raw_path',... + 'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',... + 'Configurations.interference_attenuation'}; + +[dataTable,sql_query] = database.queryDB(filterParams, selectedFields); +[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices +dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each run_id + +fsym = dataTable.symbolrate; +M = double(dataTable.pam_level); +duob_mode = db_mode(dataTable.db_mode); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +len_tr = 4096*2; + +vnle_order1 = 50; +vnle_order2 = 5; +vnle_order3 = 5; + +dfe_order = [0 0 0]; + +pf_ncoeffs = 1; + +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_dfe = 0.0004; +mu_dc = 0.00; + +mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3]; +vnle_order=[vnle_order1,vnle_order2,vnle_order3]; + +% Overwrite default parameters if given in options.parameters +paramStruct = options.parameters; +if ~isempty(paramStruct) + paramNames = fieldnames(paramStruct); + for i = 1:numel(paramNames) + thisName = paramNames{i}; + thisValue = paramStruct.(thisName); + eval([thisName ' = thisValue;']); + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +eq_ = EQ("Ne",vnle_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); +pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); +mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); +mlse_db_ = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); +eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0); + +output = struct(); +vnle_pf_package = {}; +vnle_dfe_package = {}; +dbtgt_package = {}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Tx_bits = load([basePath, char(dataTable.tx_bits_path)]); +Tx_bits = Tx_bits.Bits; +Symbols_mapped = PAMmapper(M,0).map(Tx_bits); +Symbols_mapped.fs = dataTable.symbolrate; + +Symbols = load([basePath, char(dataTable.tx_symbols_path)]); +Symbols = Symbols.Symbols; + +Scpe_load = load([basePath, char(dataTable.rx_sync_path)]); +Scpe_cell = Scpe_load.S; +[~,~,found]=Scpe_cell{2}.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",0); + +if ~found + Raw_signal = load([basePath, char(dataTable.rx_raw_path(1))]); + Raw_signal = Raw_signal.Scpe_sig_raw; + [~,Scpe_cell,found] =Raw_signal.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",0); +end + +if ~found + if length(Symbols_mapped.signal) == sum(Symbols_mapped.signal == Symbols.signal) + warning('Could not synchronize the received signal with the stored symbols!') + else + [~,Scpe_cell,found] =Raw_signal.tsynch("reference",Symbols_mapped,"fs_ref",dataTable.symbolrate,"debug_plots",0); + end + if ~found + warning('Could not synchronize the received signal with the stored symbols!') + end +end + +proc_occ = min(options.max_occurences,length(Scpe_cell)); +for occ = 1:proc_occ + + Scpe_sig = Scpe_cell{occ}; + + %%%%%% Sample to 2x fsym %%%%%% + Scpe_sig = Scpe_sig.resample("fs_out",2*fsym); + + %%%%%% Sync Rx signal with reference %%%%%% + [Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",0); + + Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); + + Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); + + % Scpe_sig.plot("displayname",'Scope Signal','fignum',11); + % Scpe_sig.spectrum("displayname",'Raw Signal','fignum',20); + + if duob_mode ~= db_mode.db_encoded + + % %%%%% VNLE + DFE %%%% + if 0 + + eq_vnle_dfe = EQ("Ne",vnle_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0); + eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0); + + [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,"showAnalysis",1,"postFFE",[]); + vnle_dfe_package{occ} = result; + + end + + %%%%% VNLE + PF + MLSE %%%% + if 1 + + [result] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',0,"postFFE",[],"eth_style_symbol_mapping",0); + vnle_pf_package{occ} = result; + + if options.append_to_db + database.addProcessingResult(run_id,result.resultsMLSE, result.equalizerConfigMLSE); + database.addProcessingResult(run_id,result.resultsVNLE, result.equalizerConfigVNLE); + end + end + + %%%%% Duobinary Targeting %%%% + if 1 + [result] = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, "precode_mode", duob_mode,'showAnalysis',0,"postFFE",[]); + dbtgt_package{occ} = result; + + if options.append_to_db + database.addProcessingResult(run_id, result.resultsDBtgt, result.equalizerConfigDBtgt); + end + end + + fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e; BER DB tgt: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded,dbtgt_package{occ}.resultsDBtgt.BER,dbtgt_package{occ}.resultsDBtgt.BER_precoded) + % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded); + + + else + + %%%%%% %db signaling => db encoded %%%%% + if 1 + mlse_db_enc = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels); + eq_db_enc = EQ("Ne",vnle_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); + [result] = duobinary_signaling(eq_db_enc, mlse_db_enc,M, Scpe_sig ,Symbols, Tx_bits); + dbenc_package{occ} = result; + + if options.append_to_db + database.addProcessingResult(run_id, result.resultsDBsignaling, result.equalizerConfigDBsignaling); + end + end + + fprintf("BER DB: %.2e \n",dbenc_package{occ}.resultsDBsignaling.BER); + + + end + + + % autoArrangeFigures; + disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') + fprintf('\n') + + +end + + +output.dataTable = dataTable; +output.vnle_dfe_package = vnle_dfe_package; +output.vnle_pf_package = vnle_pf_package; +output.dbtgt_package = dbtgt_package; + +end \ No newline at end of file diff --git a/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m b/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m index 6eba0dd..3093427 100644 --- a/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m +++ b/projects/HighSpeedExperiment_2024/auswertung MPI/plot_from_database.m @@ -5,47 +5,67 @@ database = DBHandler("pathToDB",[basePath,'silas_labor.db']); filterParams = database.tables; filterParams.Configurations = struct( ... - 'bitrate', 336e9, ... - 'db_mode', 0, ... + 'bitrate', [], ... %[224,336,360,390,420,448] + 'db_mode', int32(db_mode.no_db), ... 'fiber_length', 1, ... 'interference_attenuation', [], ... 'interference_path_length', [], ... 'is_mpi', 1, ... 'pam_level', 4, ... - 'rop_attenuation', 0 ... + 'rop_attenuation', 0, ... + 'wavelength', 1310 ... ); -filterParams.EqualizerParameters.diff_precode = int32(db_mode.no_db); -filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); +% filterParams.EqualizerParameters.diff_precode = int32(db_mode.no_db); + filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); +% filterParams.EqualizerParameters.DCmu = 0.005; selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level' 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.SNR' 'Results.GMI' 'Results.Alpha'}; [dataTable,sql_query] = database.queryDB(filterParams, selectedFields); fixedVars = {'run_id','eq_id','bitrate'}; -resultTable = groupIt(fixedVars,dataTable); - +dataTableGrpd = groupIt(fixedVars,dataTable); % Create a new figure -figure(1); +figure(3); hold on -unique_rates = unique(resultTable.bitrate); +unique_rates = unique(dataTable.bitrate); +cols = linspecer(8); for i = 1:numel(unique_rates) + % Plot BER vs. interference_attenuation - plot(resultTable.power_mpi_signal(resultTable.bitrate==unique_rates(i),:)-resultTable.power_mpi_interference(resultTable.bitrate==unique_rates(i),:), resultTable.BER(resultTable.bitrate==unique_rates(i),:), 'o-', 'LineWidth', 1.5); + % plot(dataTableGrpd.power_mpi_signal(dataTableGrpd.bitrate==unique_rates(i),:)-dataTableGrpd.power_mpi_interference(dataTableGrpd.bitrate==unique_rates(i),:), dataTableGrpd.BER(dataTableGrpd.bitrate==unique_rates(i),:), '-', 'LineWidth', 0.5,'Color',cols(i,:)); + + if filterParams.Configurations.is_mpi + sir = dataTable.power_mpi_signal(dataTable.bitrate==unique_rates(i),:)-dataTable.power_mpi_interference(dataTable.bitrate==unique_rates(i),:); + ber = dataTable.BER(dataTable.bitrate==unique_rates(i),:); + sc=scatter(dataTable.power_mpi_signal(dataTable.bitrate==unique_rates(i),:)-dataTable.power_mpi_interference(dataTable.bitrate==unique_rates(i),:), dataTable.BER(dataTable.bitrate==unique_rates(i),:), 'LineWidth', 0.5,'Marker','.','MarkerEdgeColor',cols(i+1,:)); + pair_one = {'Run ID', dataTable.run_id(dataTable.bitrate==unique_rates(i),:)}; + pair_two = {'Rate', dataTable.bitrate(dataTable.bitrate==unique_rates(i),:)}; + addDatatips(sc, pair_one, pair_two); + else + sc=scatter(62*ones(size( dataTableGrpd.BER(dataTableGrpd.bitrate==unique_rates(i),:))), dataTableGrpd.BER(dataTableGrpd.bitrate==unique_rates(i),:), 'LineWidth', 0.5,'Marker','o','MarkerEdgeColor',cols(i,:),'MarkerFaceColor',cols(i,:)); + pair_one = {'Run ID', dataTableGrpd.run_id(dataTableGrpd.bitrate==unique_rates(i),:)}; + pair_two = {'Rate', dataTableGrpd.bitrate(dataTableGrpd.bitrate==unique_rates(i),:)}; + addDatatips(sc, pair_one, pair_two); + end + + + end % Label the axes and add a title -xlabel('Interference Attenuation'); +xlabel('SIR in dB'); ylabel('BER'); -title('BER vs. Interference Attenuation'); - +title('BER vs. Signal to Interference Ratio'); +yline(3.8e-3,'LineWidth',1,'LineStyle','--','HandleVisibility','off'); % Enable grid for better readability grid on; beautifyBERplot; - +ylim([1e-4 0.5]); @@ -70,7 +90,7 @@ function resultTable = groupIt(fixedVars,dataTable) colData = dataTable.(varNames{j}); if isnumeric(colData) % For numeric data, compute the mean. - aggData{i, j} = mean(colData(idx)); + aggData{i, j} = min(colData(idx)); else % For non-numeric data, take the first entry. if iscell(colData) @@ -88,4 +108,43 @@ function resultTable = groupIt(fixedVars,dataTable) % Append the group count as a new column. resultTable.nRows = groupCount; -end \ No newline at end of file +end + + +function addDatatips(sc, varargin) +% addDatatips Adds custom data tip rows to a scatter plot. +% +% addDatatips(sc, pair1, pair2, ...) adds one or more custom rows to the +% data tip display of the scatter plot identified by sc. +% +% Each pair should be provided as a 1x2 cell array: {label, value}. +% The value can be a scalar or a vector. If a vector is provided, its length +% must match the number of scatter plot points. +% +% Example: +% sc = scatter(x, y, 'LineWidth', 1.5, 'Marker', 'o'); +% pair_one = {'Attenuation', attenuationVector}; +% addDatatips(sc, pair_one); + + numPoints = numel(sc.XData); + + for k = 1:length(varargin) + pair = varargin{k}; + + if ~iscell(pair) || numel(pair) ~= 2 + error('Each pair must be a 1x2 cell array: {label, value}.'); + end + + label = pair{1}; + value = pair{2}; + + % If value is a vector, ensure its length is either 1 or equal to the number of scatter points. + if isvector(value) && numel(value) ~= 1 && numel(value) ~= numPoints + error('The vector for "%s" must be a scalar or have %d elements matching the scatter data points.', label, numPoints); + end + + % Create a new data tip row using the provided label and vector. + newRow = dataTipTextRow(label, value); + sc.DataTipTemplate.DataTipRows(end+1) = newRow; + end +end diff --git a/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m b/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m new file mode 100644 index 0000000..e53d7c6 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/db_auswertung/rate_vs_ber.m @@ -0,0 +1,186 @@ + + +basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +database = DBHandler("pathToDB",[basePath,'silas_labor.db']); + +filterParams = database.tables; +filterParams.Configurations = struct( ... + 'bitrate', [], ... %[224,336,360,390,420,448] + 'db_mode', int32(db_mode.db_encoded), ... + 'fiber_length', 10, ... + 'interference_attenuation', [], ... + 'interference_path_length', [], ... + 'is_mpi', 0, ... + 'pam_level', 4, ... + 'rop_attenuation', 0, ... + 'wavelength', 1310 ... + ); + +% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded); +% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); +filterParams.EqualizerParameters.DCmu = 0.00; + +selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level'... + 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' ... + 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ... + 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; + +[dataTable,sql_query] = database.queryDB(filterParams, selectedFields); + +fixedVars = {'eq_id','bitrate'}; +dataTableGrpd = groupIt(fixedVars,dataTable); + + +plotRealizations = 0; + +% Create a new figure +figure(); +hold on +unique_eq = unique(dataTable.eq_id); +cols = linspecer(8); +for i = 1:numel(unique_eq) + + idx = find(dataTableGrpd.eq_id == unique_eq(i), 1, 'first'); + equalizer_ = equalizer_structure(dataTableGrpd.equalizer_structure(idx)); + + + loop_filt = dataTableGrpd.eq_id==unique_eq(i); + + % Plot LINE: BER vs. interference_attenuation + switch equalizer_ + case equalizer_structure.vnle + bers = dataTableGrpd.BER(loop_filt,:); + case equalizer_structure.vnle_pf_mlse + bers = dataTableGrpd.BER(loop_filt,:); + case equalizer_structure.vnle_db_mlse + bers = dataTableGrpd.BER_precoded(loop_filt,:); + case equalizer_structure.db_encoded + bers = dataTableGrpd.BER(loop_filt,:); + end + + + name = sprintf('%s',equalizer_); + + p = plot(dataTableGrpd.bitrate(loop_filt,:).*1e-9, bers, '-', 'LineWidth', 0.5,'Color',cols(i,:),'DisplayName',name); + pair_one = {'Run ID', dataTableGrpd.run_id(loop_filt,:)}; + pair_two = {'Rate', dataTableGrpd.bitrate(loop_filt,:)}; + addDatatips(p, pair_one, pair_two); + xticks(unique(dataTableGrpd.bitrate(loop_filt,:).*1e-9)); + + % Plot SCATTERS: BER vs. interference_attenuation + + loop_filt = dataTable.eq_id==unique_eq(i); + if plotRealizations + switch equalizer_ + case equalizer_structure.vnle + bers = dataTable.BER(loop_filt,:); + case equalizer_structure.vnle_pf_mlse + bers = dataTable.BER(loop_filt,:); + case equalizer_structure.vnle_db_mlse + bers = dataTable.BER_precoded(loop_filt,:); + case equalizer_structure.db_encoded + bers = dataTable.BER(loop_filt,:); + end + + sc = scatter(dataTable.bitrate(loop_filt,:).*1e-9, bers, 'LineWidth', 0.5,'Marker','*','MarkerEdgeColor',cols(i,:),'HandleVisibility','off'); + pair_one = {'Run ID', dataTable.run_id(loop_filt,:)}; + pair_two = {'Rate', dataTable.bitrate(loop_filt,:)}; + addDatatips(sc, pair_one, pair_two); + xticks(unique(dataTable.bitrate(loop_filt,:).*1e-9)); + end + +end + + +% Label the axes and add a title +xlabel('Bitrate in Gbps'); +ylabel('BER'); +title('Line Rate vs. BER'); +yline(3.8e-3,'LineWidth',1,'LineStyle','--','HandleVisibility','off'); +% Enable grid for better readability +grid on; + +beautifyBERplot; +ylim([1e-4 0.5]); + + + +function resultTable = groupIt(fixedVars,dataTable) + + % Group by run_id and eq_id (adjust grouping keys as needed) + + [G, groupKeys] = findgroups(dataTable(:, fixedVars)); + + % Preallocate a cell array for aggregated data. + varNames = dataTable.Properties.VariableNames; + nVars = numel(varNames); + aggData = cell(height(groupKeys), nVars); + groupCount = zeros(height(groupKeys), 1); % To store the size of each group + + % Loop over each group. + for i = 1:height(groupKeys) + idx = (G == i); % Logical index for group i + groupCount(i) = sum(idx); % Count number of rows in this group + % For each variable in the table: + for j = 1:nVars + colData = dataTable.(varNames{j}); + if isnumeric(colData) + % For numeric data, compute the mean. + aggData{i, j} = min(colData(idx)); + else + % For non-numeric data, take the first entry. + if iscell(colData) + aggData{i, j} = colData{find(idx, 1)}; + else + aggData{i, j} = colData(find(idx, 1)); + end + end + end + end + + % Convert the aggregated cell array into a table. + resultTable = cell2table(aggData, 'VariableNames', varNames); + + % Append the group count as a new column. + resultTable.nRows = groupCount; + +end + + +function addDatatips(sc, varargin) +% addDatatips Adds custom data tip rows to a scatter plot. +% +% addDatatips(sc, pair1, pair2, ...) adds one or more custom rows to the +% data tip display of the scatter plot identified by sc. +% +% Each pair should be provided as a 1x2 cell array: {label, value}. +% The value can be a scalar or a vector. If a vector is provided, its length +% must match the number of scatter plot points. +% +% Example: +% sc = scatter(x, y, 'LineWidth', 1.5, 'Marker', 'o'); +% pair_one = {'Attenuation', attenuationVector}; +% addDatatips(sc, pair_one); + + numPoints = numel(sc.XData); + + for k = 1:length(varargin) + pair = varargin{k}; + + if ~iscell(pair) || numel(pair) ~= 2 + error('Each pair must be a 1x2 cell array: {label, value}.'); + end + + label = pair{1}; + value = pair{2}; + + % If value is a vector, ensure its length is either 1 or equal to the number of scatter points. + if isvector(value) && numel(value) ~= 1 && numel(value) ~= numPoints + error('The vector for "%s" must be a scalar or have %d elements matching the scatter data points.', label, numPoints); + end + + % Create a new data tip row using the provided label and vector. + newRow = dataTipTextRow(label, value); + sc.DataTipTemplate.DataTipRows(end+1) = newRow; + end +end diff --git a/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m b/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m new file mode 100644 index 0000000..b941680 --- /dev/null +++ b/projects/HighSpeedExperiment_2024/db_auswertung/realization_vs_ber.m @@ -0,0 +1,164 @@ + + +basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; +database = DBHandler("pathToDB",[basePath,'silas_labor.db']); + +filterParams = database.tables; +filterParams.Configurations = struct( ... + 'bitrate', 450e9, ... %[224,336,360,390,420,448] + 'db_mode', int32(db_mode.no_db), ... + 'fiber_length', 10, ... + 'interference_attenuation', [], ... + 'interference_path_length', [], ... + 'is_mpi', 0, ... + 'pam_level', 4, ... + 'rop_attenuation', 0, ... + 'wavelength', 1310 ... + ); + +% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded); +% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); +filterParams.EqualizerParameters.DCmu = 0.00; + +selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level'... + 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' ... + 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ... + 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; + +[dataTable,sql_query] = database.queryDB(filterParams, selectedFields); + +fixedVars = {'eq_id','bitrate'}; +dataTableGrpd = groupIt(fixedVars,dataTable); + + +plotRealizations = 1; + +% Create a new figure +figure(); +hold on +unique_eq = unique(dataTable.eq_id); +cols = linspecer(8); + +for i = 1:numel(unique_eq) + + idx = find(dataTableGrpd.eq_id == unique_eq(i), 1, 'first'); + equalizer_ = equalizer_structure(dataTableGrpd.equalizer_structure(idx)); + + % Plot SCATTERS: timestamp vs. interference_attenuation + + loop_filt = dataTable.eq_id==unique_eq(i); + if plotRealizations + switch equalizer_ + case equalizer_structure.vnle + bers = dataTable.BER(loop_filt,:); + case equalizer_structure.vnle_pf_mlse + bers = dataTable.BER(loop_filt,:); + case equalizer_structure.vnle_db_mlse + bers = dataTable.BER_precoded(loop_filt,:); + case equalizer_structure.db_encoded + bers = dataTable.BER(loop_filt,:); + end + date_of_proc = datetime(dataTable.date_of_processing(loop_filt,:)); + + sc = scatter(date_of_proc, bers, 'LineWidth', 0.5,'Marker','*','MarkerEdgeColor',cols(i,:),'HandleVisibility','off'); + pair_one = {'Run ID', dataTable.run_id(loop_filt,:)}; + pair_two = {'Rate', dataTable.bitrate(loop_filt,:)}; + addDatatips(sc, pair_one, pair_two); + xticks(date_of_proc); + end + +end + + +% Label the axes and add a title +xlabel('Time of Processing'); +ylabel('BER'); +title('Line Rate vs. BER'); +yline(3.8e-3,'LineWidth',1,'LineStyle','--','HandleVisibility','off'); +% Enable grid for better readability +grid on; + +beautifyBERplot; +ylim([1e-4 0.5]); + + + +function resultTable = groupIt(fixedVars,dataTable) + + % Group by run_id and eq_id (adjust grouping keys as needed) + + [G, groupKeys] = findgroups(dataTable(:, fixedVars)); + + % Preallocate a cell array for aggregated data. + varNames = dataTable.Properties.VariableNames; + nVars = numel(varNames); + aggData = cell(height(groupKeys), nVars); + groupCount = zeros(height(groupKeys), 1); % To store the size of each group + + % Loop over each group. + for i = 1:height(groupKeys) + idx = (G == i); % Logical index for group i + groupCount(i) = sum(idx); % Count number of rows in this group + % For each variable in the table: + for j = 1:nVars + colData = dataTable.(varNames{j}); + if isnumeric(colData) + % For numeric data, compute the mean. + aggData{i, j} = min(colData(idx)); + else + % For non-numeric data, take the first entry. + if iscell(colData) + aggData{i, j} = colData{find(idx, 1)}; + else + aggData{i, j} = colData(find(idx, 1)); + end + end + end + end + + % Convert the aggregated cell array into a table. + resultTable = cell2table(aggData, 'VariableNames', varNames); + + % Append the group count as a new column. + resultTable.nRows = groupCount; + +end + + +function addDatatips(sc, varargin) +% addDatatips Adds custom data tip rows to a scatter plot. +% +% addDatatips(sc, pair1, pair2, ...) adds one or more custom rows to the +% data tip display of the scatter plot identified by sc. +% +% Each pair should be provided as a 1x2 cell array: {label, value}. +% The value can be a scalar or a vector. If a vector is provided, its length +% must match the number of scatter plot points. +% +% Example: +% sc = scatter(x, y, 'LineWidth', 1.5, 'Marker', 'o'); +% pair_one = {'Attenuation', attenuationVector}; +% addDatatips(sc, pair_one); + + numPoints = numel(sc.XData); + + for k = 1:length(varargin) + pair = varargin{k}; + + if ~iscell(pair) || numel(pair) ~= 2 + error('Each pair must be a 1x2 cell array: {label, value}.'); + end + + label = pair{1}; + value = pair{2}; + + % If value is a vector, ensure its length is either 1 or equal to the number of scatter points. + if isvector(value) && numel(value) ~= 1 && numel(value) ~= numPoints + error('The vector for "%s" must be a scalar or have %d elements matching the scatter data points.', label, numPoints); + end + + % Create a new data tip row using the provided label and vector. + newRow = dataTipTextRow(label, value); + sc.DataTipTemplate.DataTipRows(end+1) = newRow; + end +end diff --git a/projects/Messung_Zürich/AIR_simp_new.fig b/projects/Messung_Zürich/AIR_simp_new.fig new file mode 100644 index 0000000..dbc22cc Binary files /dev/null and b/projects/Messung_Zürich/AIR_simp_new.fig differ diff --git a/projects/Messung_Zürich/BER_ief.fig b/projects/Messung_Zürich/BER_ief.fig new file mode 100644 index 0000000..51692ef Binary files /dev/null and b/projects/Messung_Zürich/BER_ief.fig differ diff --git a/projects/Messung_Zürich/BER_simp_new.fig b/projects/Messung_Zürich/BER_simp_new.fig new file mode 100644 index 0000000..5f7044f Binary files /dev/null and b/projects/Messung_Zürich/BER_simp_new.fig differ diff --git a/projects/Messung_Zürich/NDR_simp_new.fig b/projects/Messung_Zürich/NDR_simp_new.fig new file mode 100644 index 0000000..9ba7d17 Binary files /dev/null and b/projects/Messung_Zürich/NDR_simp_new.fig differ diff --git a/projects/Messung_Zürich/NGMI_ief.fig b/projects/Messung_Zürich/NGMI_ief.fig new file mode 100644 index 0000000..eab9017 Binary files /dev/null and b/projects/Messung_Zürich/NGMI_ief.fig differ diff --git a/projects/Messung_Zürich/NGMI_simp_new.fig b/projects/Messung_Zürich/NGMI_simp_new.fig new file mode 100644 index 0000000..8dff0cc Binary files /dev/null and b/projects/Messung_Zürich/NGMI_simp_new.fig differ