function [ffe_results,eq_signal_sd] = ffe(eq_, M, rx_signal, tx_symbols, tx_bits, options) % FFE Processes signals through FFE equalizer % % Inputs: % eq_ - Equalizer object % M - Modulation order % rx_signal - Received signal % tx_symbols - Transmitted symbols % tx_bits - Transmitted bits % options - Optional parameters % % Outputs: % ffe_results - Results from FFE processing arguments eq_ M rx_signal tx_symbols tx_bits options.precode_mode db_mode options.showAnalysis = 0; options.eth_style_symbol_mapping = 0; options.postFFE = []; options.database = []; end %% Process signals through equalizer % FFE or VNLE [eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols); % Apply post-FFE if provided if ~isempty(options.postFFE) tic [eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols); toc end try ch_coefficients = arburg(eq_noise.signal,1); channel_alpha = ch_coefficients(2); end % Hard decision on FFE output eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd); %% Calculate BER based on precoding mode [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping); %% Calculate performance metrics [snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); % [gmi] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000); [gmi] = calc_ngmi(eq_signal_sd,tx_symbols); gmi = max(gmi,0); air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M)); [evm_total, evm_lvl] = calc_evm(eq_signal_sd, tx_symbols); [std_total, std_lvl] = calc_std(eq_signal_sd, tx_symbols); [std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols); %% Display analysis if requested if options.showAnalysis displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, options.postFFE); end %% Prepare output structure % Determine postFFE order if ~isempty(options.postFFE) npostFFE = options.postFFE.order; else npostFFE = 0; end % Create FFE results structure ffe_results = struct(); try eq_.e = []; eq_.e2 = []; eq_.e3 = []; eq_.b = []; eq_.b2 = []; eq_.b3 = []; end try eq_.mu_optimization = []; eq_.dc_optimization = []; end ffe_results.config = Equalizerstruct(); ffe_results.config.eq = jsonencode(eq_); ffe_results.config.equalizer_structure = int32(equalizer_structure.ffe); ffe_results.config.comment = 'function: ffe'; ffe_results.metrics = Metricstruct; ffe_results.metrics.result_id = NaN; ffe_results.metrics.run_id = NaN; ffe_results.metrics.eqParam_id = NaN; ffe_results.metrics.date_of_processing = datetime('now'); ffe_results.metrics.BER = ber; ffe_results.metrics.numBits = bits; ffe_results.metrics.numBitErr = errors; ffe_results.metrics.BER_precoded = ber_precoded; ffe_results.metrics.numBitErr_precoded = errors_precoded; ffe_results.metrics.SNR = snr; ffe_results.metrics.SNR_level = snr_lvl; ffe_results.metrics.STD = std_total; ffe_results.metrics.STD_level = std_lvl; ffe_results.metrics.STDrx = std_rxraw_total; ffe_results.metrics.STDrx_level = std_rxraw_lvl; ffe_results.metrics.GMI = gmi; ffe_results.metrics.AIR = air; ffe_results.metrics.EVM = evm_total; ffe_results.metrics.EVM_level = evm_lvl; ffe_results.metrics.Alpha = channel_alpha; end %% Helper Functions function [bits, errors, ber, error_pos, errors_precoded, ber_precoded] = calculateBER(eq_signal_hd, tx_symbols, tx_bits, precode_mode, M, eth_style) % Calculate BER based on precoding mode mapper = PAMmapper(M, 0, "eth_style", eth_style); switch precode_mode case db_mode.no_db % TX Data is not precoded % 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); tx_symbols_precoded = Duobinary().encode(tx_symbols); tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded); tx_bits_precoded = mapper.demap(tx_symbols_precoded); rx_bits = mapper.demap(eq_signal_hd_precoded); [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits.signal, tx_bits_precoded.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); % B) Just determine BER rx_bits = mapper.demap(eq_signal_hd); [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); case db_mode.db_precoded % Data is precoded on TX side % A) Decode at Rx if no DB targeting was applied 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_decoded = mapper.demap(eq_signal_hd_decoded); [~, errors_precoded, ber_precoded, ~] = calc_ber(rx_bits_decoded.signal, tx_bits.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); % B) Omit the Coding by comparing with demapped TX symbol sequence tx_bits_demapped = mapper.demap(tx_symbols); rx_bits = mapper.demap(eq_signal_hd); [bits, errors, ber, error_pos] = calc_ber(rx_bits.signal, tx_bits_demapped.signal, "skip_front", 10, "skip_end", 10, "returnErrorLocation", 1); end end function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, postFFE) % Display analysis plots and metrics % Initialize figure handles % Corrected line - added tx_symbols as second positional argument % showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 100); warning off showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 101); figure(gcf);hold on; plot(((1:length(eq_noise.signal)) / eq_noise.fs) * 1e6,movmean(eq_noise.signal,2000,1), 'LineWidth',3,'Color','black') warning on showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 102); showEQNoisePSD(eq_noise, "fignum", 103, "displayname", 'Residual Noise after FFE'); % showEQcoefficients('n1', eq_.e, "displayname", 'Coefficients', 'fignum', 104); % Figure 2: Post-FFE coefficients (if available) if ~isempty(postFFE) showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 104); end try figure(339);hold on showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'training','fignum',339); legend on end show2Dconstellation(eq_signal_sd, tx_symbols,"displayname",'Visualization of symbol correlation','fignum',340); % try % figure(240); hold on; plot(pow2db(movmean(eq_.debug_struct.error_tr',100)));ylim([-30,3]);title('error training'); % % figure(241); hold on; plot(pow2db(movmean(eq_.debug_struct.update_tr',100)));title('update step training'); % % figure(242); hold on; plot(pow2db(movmean(eq_.debug_struct.update',1000)));title('update step dd'); % end % eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105); end