From 18ccaf8c124f0c2d1e285bacd88c51afe99b49bd Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Mon, 23 Feb 2026 09:17:26 +0100 Subject: [PATCH] duobinary cleanup (currently it is a mess) --- Classes/01_transmit/PAMmapper.m | 15 +- Classes/DataBaseHandler/Metricstruct.m | 10 +- Datatypes/db_decoder.m | 8 + Functions/EQ_structures/duobinary_target.m | 306 +++++++++++---------- projects/IMDD_base_system/imdd_it.m | 38 +-- projects/IMDD_base_system/imdd_model.m | 151 +++++----- projects/standard_system/freqresp_test.m | 2 +- test/duobinary_emulation_minimal_example.m | 10 +- test/memoryless_decoding.m | 75 +++++ 9 files changed, 364 insertions(+), 251 deletions(-) create mode 100644 Datatypes/db_decoder.m create mode 100644 test/memoryless_decoding.m diff --git a/Classes/01_transmit/PAMmapper.m b/Classes/01_transmit/PAMmapper.m index a44d8cd..f9d622b 100644 --- a/Classes/01_transmit/PAMmapper.m +++ b/Classes/01_transmit/PAMmapper.m @@ -462,10 +462,19 @@ classdef PAMmapper end - function [Signal_out] = quantize(obj,Signal_in) + function [Signal_out] = quantize(obj,Signal_in,options) + arguments + obj + Signal_in + options.custom_const = [] + end - constellation = obj.get_levels(); - constellation = constellation ./ obj.scaling; + if isempty(options.custom_const) + constellation = obj.get_levels(); + constellation = constellation ./ obj.scaling; + else + constellation = options.custom_const; + end issignalclass = 0; if isa(Signal_in,'Signal') diff --git a/Classes/DataBaseHandler/Metricstruct.m b/Classes/DataBaseHandler/Metricstruct.m index fe9a818..b5c5113 100644 --- a/Classes/DataBaseHandler/Metricstruct.m +++ b/Classes/DataBaseHandler/Metricstruct.m @@ -7,11 +7,11 @@ classdef Metricstruct eqParam_id (1,1) double {mustBeNumeric} = NaN date_of_processing (1,1) datetime = datetime('now') - numBits (1,1) double {mustBeInteger, mustBeNonnegative} = 0 - BER (1,1) double {mustBeNumeric, mustBeNonnegative} = 0 - numBitErr (1,1) double {mustBeInteger, mustBeNonnegative} = 0 - BER_precoded (1,1) double {mustBeNumeric, mustBeNonnegative} = 0 - numBitErr_precoded (1,1) double {mustBeInteger, mustBeNonnegative} = 0 + numBits (1,1) double = 0 + BER (1,1) double = 0 + numBitErr (1,1) double = 0 + BER_precoded (1,1) double = 0 + numBitErr_precoded (1,1) double = 0 SNR (1,1) double {mustBeNumeric} = NaN SNR_level (:,1) double {mustBeNumeric} = [] diff --git a/Datatypes/db_decoder.m b/Datatypes/db_decoder.m new file mode 100644 index 0000000..058a839 --- /dev/null +++ b/Datatypes/db_decoder.m @@ -0,0 +1,8 @@ +classdef db_decoder < int32 + + enumeration + sequencedetection (0) % use MLSE for decoding + memoryless (1) % use modulo + end + +end \ No newline at end of file diff --git a/Functions/EQ_structures/duobinary_target.m b/Functions/EQ_structures/duobinary_target.m index 36150dd..78c8d17 100644 --- a/Functions/EQ_structures/duobinary_target.m +++ b/Functions/EQ_structures/duobinary_target.m @@ -1,153 +1,171 @@ function [db_results] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options) -arguments - eq_ - mlse_ - M - rx_signal - tx_symbols - tx_bits - options.precode_mode db_mode - options.showAnalysis = 0; - options.eth_style_symbol_mapping = 0; - options.postFFE = []; -end + arguments + eq_ + 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.decoding_mode db_decoder = db_decoder.sequencedetection; + end + + + %Duobinary Targeting + db_ref_sequence = Duobinary().encode(tx_symbols); + db_ref_constellation = unique(db_ref_sequence.signal); + [eq_signal, eq_noise] = eq_.process(rx_signal,db_ref_sequence); + + if ~isempty(options.postFFE) + [eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence); + end + % + + switch options.decoding_mode + case db_decoder.sequencedetection %MLSE + mlse_.DIR = [1,1]; + if isa(mlse_,'MLSE_viterbi') + pam_sig_sd = mlse_.process(eq_signal); + else + [pam_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); + end + pam_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(pam_sig_sd); + case db_decoder.memoryless %DB Target FFE + % Hard decision on FFE output + eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal,'custom_const',db_ref_constellation.'); + eq_signal_hd = Duobinary().decode(eq_signal_hd); + pam_sig_hd = eq_signal_hd; + end + + % 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) + switch options.precode_mode + + case db_mode.no_db + % TX Data is not precoded: + + % A) Emulate diff precoding + if options.decoding_mode == db_decoder.sequencedetection + pam_sig_hd_precoded = Duobinary().encode(pam_sig_hd,"M",M); + pam_sig_hd_precoded = Duobinary().decode(pam_sig_hd_precoded,"M",M); + else + pam_sig_hd_precoded = pam_sig_hd; + end -%Duobinary Targeting -db_ref_sequence = Duobinary().encode(tx_symbols); -db_ref_constellation = unique(db_ref_sequence.signal); -[eq_signal, eq_noise] = eq_.process(rx_signal,db_ref_sequence); + tx_symbols_precoded = Duobinary().encode(tx_symbols); + tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); + tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); -if ~isempty(options.postFFE) - [eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence); -end + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(pam_sig_hd_precoded); + + [bits_db,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); + + %B) Just determine BER + if options.decoding_mode == db_decoder.sequencedetection + rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(pam_sig_hd); + tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols); + [bits_mlse,errors_mlse,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + else + ber_db = NaN; + errors_db = NaN; + end + + 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! + if options.decoding_mode == db_decoder.sequencedetection + pam_sig_hd_decoded = Duobinary().encode(pam_sig_hd,"M",M); + pam_sig_hd_decoded = Duobinary().decode(pam_sig_hd_decoded,"M",M); + else + pam_sig_hd_decoded = pam_sig_hd; + end -mlse_.DIR = [1,1]; -% + rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(pam_sig_hd_decoded); + [bits_db,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + burst_db_precoded = count_error_bursts(a, 40); -if isa(mlse_,'MLSE_viterbi') - mlse_sig_sd = mlse_.process(eq_signal); -else - [mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols); -end - -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) -switch options.precode_mode - - 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_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); - 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); - - %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 - - % 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,a] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_db_precoded = count_error_bursts(a, 40); - % 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,a] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - burst_db = count_error_bursts(a, 40); - - cols = linspecer(8); - figure();hold on; - stem(1:40,burst_db,'LineWidth',1,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder'); - stem(1:40,burst_db_precoded,'LineWidth',1,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','w diff. precoder'); - xlabel('Bit Error Burst Length') - ylabel('Occurence') - set(gca, 'yscale', 'log'); -end - -% M = numel(unique(tx_symbols.signal)); -rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); - -[bits_db,errors_db,ber_db,errorIndice_db] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); - - - - - -alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2); -alpha = alpha(2); -if isa(mlse_,'MLSE_viterbi') - gmi_mlse = NaN; - air_mlse = NaN; -else - gmi_mlse = GMI_MLSE; - air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); -end - -db_results = struct(); -db_results.metrics = Metricstruct; -db_results.metrics.result_id = NaN; -db_results.metrics.run_id = NaN; -db_results.metrics.eqParam_id = NaN; -db_results.metrics.date_of_processing = datetime('now'); -db_results.metrics.BER = ber_db; -db_results.metrics.numBits = bits_db; -db_results.metrics.numBitErr = errors_db; -db_results.metrics.BER_precoded = ber_db_diff_precoded; -db_results.metrics.numBitErr_precoded = errors_db_diff_precoded; -db_results.metrics.GMI = gmi_mlse; -db_results.metrics.AIR = air_mlse; -db_results.metrics.MLSE_dir = mlse_.DIR; -db_results.metrics.Alpha = alpha; - -% Create DB results structure -db_results.config = Equalizerstruct(); -eq_.e = []; -eq_.e2 = []; -eq_.e3 = []; -db_results.config.eq = jsonencode(eq_); -% mlse_.DIR = []; -db_results.config.mlse = jsonencode(mlse_); -db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse); -db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)'; - -if options.showAnalysis - - eq_signal.eye(eq_signal.fs,M,"fignum",249); - - - eq_noise = eq_noise - mean(eq_noise.signal); - - rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum"); - - Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",10,"displayname","DB encoded reference"); - - showEQNoisePSD(eq_noise,"fignum",250,"displayname",'Duobinary Target Noise after Equalization'); - - fprintf('DB tgt BER: %.2e \n',ber_db); - - figure(341); clf; - showLevelHistogram(eq_signal, db_ref_sequence, "fignum", 341); - -end + % B) Omit the Coding by comparing with demapped TX symbol sequence + if options.decoding_mode == db_decoder.sequencedetection + 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(pam_sig_hd); + [bits_db,errors_db,ber_db,a] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + burst_db = count_error_bursts(a, 40); + else + ber_db = NaN; + errors_db = NaN; + end + end + + + switch options.decoding_mode + case db_decoder.sequencedetection %MLSE + if isa(mlse_,'MLSE_viterbi') + gmi_mlse = NaN; + air_mlse = NaN; + else + gmi_mlse = GMI_MLSE; + air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M)); + end + case db_decoder.memoryless %DB Target FFE + % [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); + [gmi] = calc_ngmi(eq_signal,tx_symbols); + gmi_mlse = NaN; + air_mlse = NaN; + end + + db_results = struct(); + db_results.metrics = Metricstruct; + db_results.metrics.result_id = NaN; + db_results.metrics.run_id = NaN; + db_results.metrics.eqParam_id = NaN; + db_results.metrics.date_of_processing = datetime('now'); + db_results.metrics.BER = ber_db; + db_results.metrics.numBits = bits_db; + db_results.metrics.numBitErr = errors_db; + db_results.metrics.BER_precoded = ber_db_diff_precoded; + db_results.metrics.numBitErr_precoded = errors_db_diff_precoded; + db_results.metrics.GMI = gmi_mlse; + db_results.metrics.AIR = air_mlse; + db_results.metrics.MLSE_dir = mlse_.DIR; + db_results.metrics.Alpha = NaN; + + % Create DB results structure + db_results.config = Equalizerstruct(); + eq_.e = []; + eq_.e2 = []; + eq_.e3 = []; + db_results.config.eq = jsonencode(eq_); + % mlse_.DIR = []; + db_results.config.mlse = jsonencode(mlse_); + db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse); + db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)'; + + if options.showAnalysis + + eq_signal.eye(eq_signal.fs,M,"fignum",249); + + + eq_noise = eq_noise - mean(eq_noise.signal); + + rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum"); + + Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",10,"displayname","DB encoded reference"); + + showEQNoisePSD(eq_noise,"fignum",250,"displayname",'Duobinary Target Noise after Equalization'); + + fprintf('DB tgt BER: %.2e \n',ber_db); + + figure(341); clf; + showLevelHistogram(eq_signal, db_ref_sequence, "fignum", 341); + + end end \ No newline at end of file diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index a495d74..9ffc49b 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -4,11 +4,11 @@ if 1 uloops = struct; uloops.precomp = [1]; - uloops.bitrate = [300].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI + uloops.bitrate = [360].*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 = [1293]; + uloops.laser_wavelength = [1290]; uloops.M = [4]; - uloops.link_length = [0:2:10]; % 1,2,3,5,6,8,10 + uloops.link_length = [0]; % 1,2,3,5,6,8,10 uloops.alpha = [0]; wh = DataStorage(uloops); @@ -19,21 +19,23 @@ if 1 end %% -figure -hold on -for alpha = uloops.alpha - a=wh.getStoValue('ber',1, [300].*1e9 , 1293, 4, uloops.link_length,alpha); - ffe = cellfun(@(x) x.ffe_results.metrics.BER, a); - plot(uloops.link_length,ffe,'DisplayName',sprintf('Alpha: %d',alpha),'LineStyle','-','HandleVisibility','on'); -end - -set(gca, 'YScale', 'log'); -ylim([5e-5 0.4]); -yline([3.8e-3, 2e-2],'HandleVisibility','off'); -legend -beautifyBERplot() -ylabel('BER'); - +% figure +% hold on +% +% a=wh.getStoValue('ber',1, [360].*1e9 , 1290, 4, uloops.link_length,alpha); +% ffe = cellfun(@(x) x.ffe_results.metrics.BER, a); +% plot(uloops.link_length,ffe,'DisplayName',sprintf('FFE'),'LineStyle','-','HandleVisibility','on'); +% dbt = cellfun(@(x) x.dbt_results.metrics.BER, a); +% plot(uloops.link_length,dbt,'DisplayName',sprintf('DBt'),'LineStyle','-','HandleVisibility','on'); +% +% +% set(gca, 'YScale', 'log'); +% ylim([5e-5 0.4]); +% yline([3.8e-3, 2e-2],'HandleVisibility','off'); +% legend +% beautifyBERplot() +% ylabel('BER'); +% diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index 5a89483..4118a26 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -3,66 +3,59 @@ function [output] = imdd_model(varargin) simulation_mode = 1; %%% Change folder -curFolder = pwd; -funcFolder=fileparts(mfilename('fullpath')); -if ~isempty(funcFolder) - cd(funcFolder); -end +% curFolder = pwd; +% funcFolder=fileparts(mfilename('fullpath')); +% if ~isempty(funcFolder) +% cd(funcFolder); +% end %%% Run parameters -% TX -M = 4; -fsym = 180e9; -apply_pulsef = 1; -fdac = 256e9; -fadc = 256e9; +% --- TX Architecture --- +M = 4; % PAM order +fsym = 180e9; % Symbol rate +apply_pulsef = 1; % Pulse shaping flag +fdac = 256e9; % DAC sampling rate +fadc = 256e9; % ADC sampling rate random_key = 1; +rcalpha = 0.05; % Roll-off factor +kover = 16; % Oversampling factor +duob_mode = db_mode.db_precoded; -rcalpha = 0.05; -kover = 16; - +% --- TX Optics (EML / Laser) --- vbias_rel = 0.5; u_pi = 3; -vbias = -vbias_rel*u_pi; - +vbias = -vbias_rel * u_pi; laser_wavelength = 1293; laser_linewidth = 0; tx_bw_nyquist = 0.8; -% Channel +% --- Channel --- link_length = 1; +alpha = 0; -% RX -rop = -8; -rx_bw_nyquist = 0.8; +% --- RX & Equalization --- +rop = -2; +rx_bw_nyquist = 0.8; +len_tr = 4096 * 2; -vnle_order1 = 50; -vnle_order2 = 7; -vnle_order3 = 7; +% VNLE / FFE / DFE Orders +vnle_order1 = 50; +vnle_order2 = 7; +vnle_order3 = 7; +vnle_order = [vnle_order1, vnle_order2, vnle_order3]; +dfe_order = [0, 0, 0]; +dfe_ = sum(dfe_order) > 0; -vnle_order=[vnle_order1,vnle_order2,vnle_order3]; -dfe_order = [0 0 0]; +pf_ncoeffs = 1; -pf_ncoeffs = 1; - -alpha = 0; - -len_tr = 4096*2; - -mu_ffe1 = 0.0001; -mu_ffe2 = 0.0008; -mu_ffe3 = 0.001; -mu_dc = 0.005; -% mu_dc = 0; - -mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3]; -mu_dfe = 0.0004; - - -dfe_ = sum(dfe_order)>0; - -duob_mode = db_mode.no_db; +% Equalizer Step Sizes +mu_ffe1 = 0.0001; +mu_ffe2 = 0.0008; +mu_ffe3 = 0.001; +mu_ffe = [mu_ffe1, mu_ffe3, mu_ffe3]; +mu_dfe = 0.0004; +mu_dc = 0.005; %%% change specific parameter if given in varargin % Parse optional input arguments @@ -109,12 +102,12 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 'duobinary_mode',duob_mode,... "mrds_code",0,"mrds_blocklength",512).process(); -Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); +% Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); %%%%% AWG % El_sig = M8199A("kover",kover).process(Digi_sig); El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); -% El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); +El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',1); % El_sig = El_sig.setPower(0,"dBm"); %%%%% Low-pass el. components %%%%%% @@ -132,7 +125,7 @@ El_sig = El_sig .* scaling; %%%%% MODULATE E/O CONVERSION %%%%%% [Opt_sig] = EML("mode",eml_mode.im_cosinus,"power",3,"fsimu",El_sig.fs,"lambda",laser_wavelength,"bias",vbias,"u_pi",u_pi,"linewidth",laser_linewidth,"randomkey",random_key+1,"alpha",alpha).process(El_sig); -Opt_sig.spectrum("displayname",'Opt Spectrum','fignum',10,'normalizeTo0dB',1); +% Opt_sig.spectrum("displayname",'Opt Spectrum','fignum',10,'normalizeTo0dB',1); Opt_sig = Fiber("fsimu",Opt_sig.fs,"fiber_length",link_length,"alpha",0.3,"D",0,"lambda0",1310,"gamma",0,"Dslope",0.07).process(Opt_sig); @@ -150,7 +143,6 @@ Rx_sig = Filter('filtdegree',4,"f_cutoff",rx_bwl,"fs",fdac*kover,"filterType",fi % %%%%%% Low-pass Scope %%%%%% Lp_scpe = Filter('filtdegree',4,"f_cutoff",110e9,"fs",fadc,"filterType",filtertypes.butterworth,"active",true); -% Rx_sig.spectrum("displayname",'Analog Rx Spectrum','fignum',100,'normalizeTo0dB',1); %%%%%% Scope %%%%%% Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc,... @@ -165,15 +157,18 @@ Scpe_sig = Scpe_sig.resample("fs_out",2*fsym); Scpe_sig.signal = Scpe_sig.signal(1:2*length(Symbols)); %%%%%% Sync Rx signal with reference %%%%%% -[Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",1); +[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.spectrum("displayname",'Filtered Digital Spectrum','fignum',100,'normalizeTo0dB',1); + + %%% EQUALIZING - +if 0 % -------------------- FFE -------------------- ffe_order = [50, 0, 0]; eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0], ... @@ -186,7 +181,8 @@ output.ffe_results = ffe(eq_ffe,M,Scpe_sig,Symbols,Tx_bits, ... "eth_style_symbol_mapping",0); output.ffe_results.metrics.print - +end +if 0 % -------------------- DFE -------------------- eq_dfe = EQ("Ne",ffe_order,"Nb",[2,0,0], ... "training_length",len_tr,"training_loops",5,"dd_loops",5, ... @@ -198,33 +194,36 @@ output.dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits, ... "eth_style_symbol_mapping",0); output.dfe_results.metrics.print("description",'DFE'); +end +if 0 + % -------------------- VNLE + MLSE -------------------- + pf_ncoeffs = 1; + ffe_order3 = [50, 5, 5]; + eq_v = EQ("Ne",ffe_order3,"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("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); + + [output.vnle_results, output.mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); + output.mlse_results.metrics.print("description",'MLSE'); +end +if 1 -% -------------------- VNLE + MLSE -------------------- -pf_ncoeffs = 1; -ffe_order3 = [50, 5, 5]; -eq_v = EQ("Ne",ffe_order3,"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("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); - -[output.vnle_results, output.mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... - "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); - - -% -------------------- DB target -------------------- -mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); -ffe_order = [50, 5, 5]; -eq_ = EQ("Ne",ffe_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); -output.dbt_results = duobinary_target(eq_,mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... - "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", []); - -output.dbt_results.metrics.print("description",'Duobinary'); - + %% -------------------- DB target -------------------- + mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); + ffe_order = [50, 5, 5]; + eq_ = EQ("Ne",ffe_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,"DB_aim",0); + output.dbt_results = duobinary_target(eq_,mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ... + "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [],"decoding_mode","memoryless"); + + output.dbt_results.metrics.print("description",'Duobinary'); +end disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') fprintf('\n') diff --git a/projects/standard_system/freqresp_test.m b/projects/standard_system/freqresp_test.m index f0cab66..daf2293 100644 --- a/projects/standard_system/freqresp_test.m +++ b/projects/standard_system/freqresp_test.m @@ -4,7 +4,7 @@ freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9); % Digi_sig = freqresp.buildOFDM(); -Digi_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]); +Digi_sig.spectrum("fignum",1112); Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(Digi_sig); diff --git a/test/duobinary_emulation_minimal_example.m b/test/duobinary_emulation_minimal_example.m index 4bb75ca..52a5135 100644 --- a/test/duobinary_emulation_minimal_example.m +++ b/test/duobinary_emulation_minimal_example.m @@ -1,7 +1,7 @@ -M = 6; -apply_precode = 1; +M = 4; +apply_precode = 0; bitpattern = []; s = RandStream('twister','Seed',1); @@ -29,12 +29,12 @@ else end show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241); - + if apply_precode % Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6]) symbols_db = Duobinary().encode(symbols_tx); - + % Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4 symbols_rx = Duobinary().decode(symbols_db); else @@ -51,6 +51,8 @@ disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]); figure(200) clf hold on +start = 100; +burstwidth = 10; idxs = start-10:start+burstwidth+10; scatter(idxs,d.signal(idxs),'DisplayName',['Orig Signal'],'Marker','o'); scatter(idxs,d_burst.signal(idxs),'DisplayName',['Error Signal'],'Marker','x'); diff --git a/test/memoryless_decoding.m b/test/memoryless_decoding.m new file mode 100644 index 0000000..8288be9 --- /dev/null +++ b/test/memoryless_decoding.m @@ -0,0 +1,75 @@ +% einstellungen +M = 4; +apply_precode_at_tx = 0; +emulate_precode = 1; + +% daten erzeugen +bitpattern = []; +s = RandStream('twister','Seed',1); +for i = 1:log2(M) + N = 2^(17-1); %length of prbs + bitpattern(:,i) = randi(s,[0 1], N, 1); +end + +if M == 6 + bitpattern = reshape(bitpattern',[],1); + bitpattern = bitpattern(1:end-mod(length(bitpattern),5)); +end + +tx_bits = Informationsignal(bitpattern); +tx_symbols = PAMmapper(M,0).map(tx_bits); + +if apply_precode_at_tx + + tx_symbols = Duobinary().precode(tx_symbols); + + if emulate_precode + % ansatz 1B) Omit precode (einfach precoded empfangen, nichts weiter machen und normal prozessieren - möglich weil konstellation sich nicht ändert) + rx_symbols = tx_symbols; + bits_rx = PAMmapper(M,0).demap(rx_symbols); + % beachten, dass man die tx_bits anpassen muss, da man ja mit precoded symbolen vergleicht + tx_bits = PAMmapper(M,0).demap(tx_symbols); + else + % ansatz 1A) Precode normal: + % Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6]) + symbols_db = Duobinary().encode(tx_symbols); + % Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4 + rx_symbols = Duobinary().decode(symbols_db); + bits_rx = PAMmapper(M,0).demap(rx_symbols); + end + + [~,~,ber,~] = calc_ber(tx_bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); + disp(['BER: ',sprintf('%.1E',ber)]); + assert(ber == 0) + +else + + if emulate_precode + % emulate precode: + % Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6]) + symbols_db = Duobinary().encode(tx_symbols); % das wäre der FFE mit db target + + % Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4 + rx_symbols = Duobinary().decode(symbols_db); % modulo + bits_rx = PAMmapper(M,0).demap(rx_symbols); % demappen + + % ref symbole precoden, auch hier muss man wieder etwas fummeln wegen der emulation: + % encode + decode == remove precoding (mathematisch die gleiche operation) + tx_symbols_ref = Duobinary().encode(tx_symbols); + tx_symbols_ref = Duobinary().decode(tx_symbols_ref); + tx_bits = PAMmapper(M,0).demap(tx_symbols_ref); + + else + % normal detection without any precode stuff + symbols_db = Duobinary().encode(tx_symbols); + + rx_symbols = tx_symbols; + bits_rx = PAMmapper(M,0).demap(rx_symbols); + end + + + [~,~,ber,~] = calc_ber(tx_bits.signal,bits_rx.signal,"skip_front",1,"skip_end",1,"returnErrorLocation",1); + disp(['BER: ',sprintf('%.1E',ber)]); + assert(ber == 0) +end +