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 = []; 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') mlse_sig_sd = mlse_.process(eq_signal); else [mlse_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(mlse_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; % tx_symbols = Duobinary().encode(tx_symbols); % tx_symbols = Duobinary().decode(tx_symbols.*db_const_scale_factor); 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 switch options.decoding_mode case db_decoder.sequencedetection %MLSE mlse_sig_hd_precoded = Duobinary().encode(pam_sig_hd); mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded); case db_decoder.memoryless %DB Target FFE % mlse_sig_hd_precoded = Duobinary().encode(pam_sig_hd); % mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded); mlse_sig_hd_precoded = pam_sig_hd; end 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(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); 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! switch options.decoding_mode case db_decoder.sequencedetection %MLSE mlse_sig_hd_decoded = Duobinary().encode(pam_sig_hd,"M",M); mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M); case db_decoder.memoryless %DB Target FFE mlse_sig_hd_decoded = pam_sig_hd; end tx_symbols_precoded = Duobinary().encode(tx_symbols,"M",M); tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded,"M",M); tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded); 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_precoded.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(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); 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(pam_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); 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 = max(gmi,0); air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ 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 end