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') 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 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(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 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); % 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