Many changes towards simulation of JLT and once again the evaluation of the Highspeed data from Lab experiments 2024

This commit is contained in:
Silas Oettinghaus
2025-07-09 10:50:53 +02:00
parent 9ce23c4a10
commit 2cff29f239
35 changed files with 1874 additions and 549 deletions

View File

@@ -5,9 +5,11 @@ arguments
options.append_to_db = 0;
options.max_occurences = 4;
options.parameters = struct();
options.database_path
options.database_name
options.database_type
options.dataBase
options.load_file_path = struct();
options.storage_path
options.mode
end
% Initialize output structures
@@ -17,34 +19,72 @@ output.vnle_package = {};
output.dbtgt_package = {};
output.dbenc_package = {};
% Initialize database connection
database = DBHandler("pathToDB", [options.database_path, options.database_name], "type", "sqlite");
dataTable = queryRunid(run_id, database);
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
duob_mode = db_mode(dataTable.db_mode);
if database.checkIfRunExists('Results','run_id',run_id)
disp(['Already got at least one reulst for run id: ',num2str(run_id),' '])
return
if options.mode == "load_run_id" || options.append_to_db
% Initialize database connection
database = DBHandler("dataBase", [options.dataBase], "type", options.database_type );
end
if options.mode == "load_run_id"
dataTable = queryRunid(run_id, database);
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
duob_mode = db_mode(strrep(dataTable.db_mode,'"',''));
% if database.checkIfRunExists('Results','run_id',run_id)
% disp(['Already got at least one reulst for run id: ',num2str(run_id),' '])
% return
% end
% Load and Sync signal data from DB
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, options);
elseif options.mode == "load_files"
Tx_bits = load(options.load_file_path.tx_bits_path);
Symbols = load(options.load_file_path.tx_symbols_path);
Scpe_sig_raw = load(options.load_file_path.rx_raw_path);
Tx_bits = Tx_bits.Bits;
Symbols = Symbols.Symbols;
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
fsym = Symbols.fs;
M = Symbols.logbook.ModifierCopy{1}.M;
duob_mode = Symbols.logbook.ModifierCopy{1}.duobinary_mode;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in", Scpe_sig_raw.fs, "fs_out", 2*fsym);
[~, Scpe_cell, ~, found_sync] = Scpe_sig_resampled.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
else
% Run quick Simulation
tx_simulation;
end
% Handle Settings and argument replacement
len_tr = 4096*2;
ffe_order = [50, 5, 5];
ffe_order = [50, 5, 5];
dfe_order = [0, 0, 0];
pf_ncoeffs = 1;
mu_ffe = [0.0001, 0.0008, 0.001];
mu_dfe = 0.0004;
mu_dc = 0.00;
mu_dc = 0.005;
dc_buffer_len = 1;
mu_tr = 0;
mu_dd = 0.05;
adaption= 1;
use_dd_mode = 1;
use_ffe = 1;
use_dfe = 1;
use_vnle_mlse = 1;
use_dbtgt = 1;
use_dbenc = 0;
use_dbenc = 1;
addProcessingResultToDatabase = 0;
@@ -60,74 +100,148 @@ if ~isempty(paramStruct)
end
% Configure equalizers
eq_lin = EQ("Ne",[50,0,0],"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);
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);
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);
% Duobinary signaling (db encoded)
mlse_db_enc = MLSE_viterbi("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
eq_db_enc = 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);
options.max_occurences = min(options.max_occurences,length(Scpe_cell));
for r = 1:options.max_occurences
%FFE
% eq_dfe = FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
% Load signal data
[Tx_bits, Symbols, Scpe_cell, ~] = loadSignalData(dataTable, options);
%
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);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_db_ = MLSE("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,"adaption_technique","lms");
% Duobinary signaling (db encoded)
mlse_db_enc = MLSE("DIR", [1,1], "duobinary_output", 0, "M", M, "trellis_states", PAMmapper(M,0).levels);
eq_db_enc = 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);
for r = 1:numel(Scpe_cell)
% Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{r}, Symbols, fsym);
if duob_mode ~= db_mode.db_encoded
if use_ffe
ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,...
ffe_order = [50, 0, 0];
eq_dfe = EQ("Ne",ffe_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);
dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',1,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
output.ffe_package{r} = ffe_results;
output.ffe_package{r} = dfe_results;
dfe_results.metrics.print;
if options.append_to_db
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config);
end
end
if use_dfe
ffe_order = [50, 5, 5];
eq_dfe = EQ("Ne",ffe_order,"Nb",[2,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);
dfe_results = ffe(eq_dfe,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
output.ffe_package{r} = dfe_results;
dfe_results.metrics.print;
if options.append_to_db
database.addProcessingResult(run_id, dfe_results.metrics, dfe_results.config);
end
end
if use_vnle_mlse
pf_ncoeffs = 1;
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);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 1, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
output.mlse_package{r} = mlse_results;
output.vnle_package{r} = ffe_results;
ffe_results.metrics.print;
mlse_results.metrics.print;
output.mlse_package{r} = mlse_results;
output.vnle_package{r} = ffe_results;
if options.append_to_db
database.addProcessingResult(run_id, mlse_results.metrics, mlse_results.config);
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
end
pf_ncoeffs = 2;
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);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
[ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode,...
'showAnalysis', 1, ...
"postFFE", [],...
"eth_style_symbol_mapping", 0);
ffe_results.metrics.print;
mlse_results.metrics.print;
output.mlse_package{r} = mlse_results;
output.vnle_package{r} = ffe_results;
if options.append_to_db
database.addProcessingResult(run_id, mlse_results.metrics, mlse_results.config);
database.addProcessingResult(run_id, ffe_results.metrics, ffe_results.config);
end
end
if use_dbtgt
dbt_results = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode, ...
'showAnalysis', 0,...
"postFFE", []);
output.dbtgt_package{r} = dbt_results;
output.dbtgt_package{r} = dbt_results;
if options.append_to_db
database.addProcessingResult(run_id, dbt_results.metrics, dbt_results.config);
end
end
end
if duob_mode == db_mode.db_encoded
db_results = duobinary_signaling(eq_db_enc, mlse_db_enc, M, Scpe_sig, Symbols, Tx_bits, "precode_mode",duob_mode, "showAnalysis",0,"postFFE",[]);
output.dbenc_package{r} = db_results;
if options.append_to_db
database.addProcessingResult(run_id, db_results.metrics, db_results.config);
end
end

View File

@@ -36,20 +36,21 @@ if ~isempty(options.postFFE)
end
% Process through MLSE
eq_signal = mlse_.process(eq_signal);
% [mlse_signal] = mlse_.process(eq_signal);
[mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols);
% Apply duobinary encoding and decoding
eq_signal = Duobinary().encode(eq_signal);
eq_signal = Duobinary().decode(eq_signal);
mlse_signal = Duobinary().encode(mlse_signal);
mlse_signal = Duobinary().decode(mlse_signal);
% Demap symbols to bits
rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(eq_signal);
rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(mlse_signal);
%% Calculate BER and metrics
[bits_db, errors_db, ber_db, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
% Calculate performance metrics
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
% Calculate performance metrics after duobinary FFE!
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); %SNR of duobinary sequence - not directly comparable to
[gmi] = calc_air(eq_signal, tx_symbols, "skip_front", 10000, "skip_end", 10000);
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M));
[evm_total, evm_lvl] = calc_evm(eq_signal, tx_symbols);

View File

@@ -23,8 +23,8 @@ if ~isempty(options.postFFE)
end
mlse_.DIR = [1,1];
mlse_sig_sd = mlse_.process(eq_signal);
% mlse_sig_sd = mlse_.process(eq_signal);
[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols);
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
@@ -73,6 +73,10 @@ end
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);
gmi_mlse = GMI_MLSE;
air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M));
db_results = struct();
db_results.metrics = Metricstruct;
@@ -85,13 +89,10 @@ 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.SNR = NaN;
db_results.metrics.SNR_level = NaN;
db_results.metrics.GMI = NaN;
db_results.metrics.AIR = NaN;
db_results.metrics.EVM = NaN;
db_results.metrics.EVM_level = NaN;
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();
@@ -99,7 +100,7 @@ eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
db_results.config.eq = jsonencode(eq_);
mlse_.DIR = [];
% 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)';

View File

@@ -36,6 +36,11 @@ if ~isempty(options.postFFE)
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);
@@ -66,10 +71,15 @@ end
% Create FFE results structure
ffe_results = struct();
try
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
eq_.b = [];
eq_.b2 = [];
eq_.b3 = [];
end
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
ffe_results.config = Equalizerstruct();
ffe_results.config.eq = jsonencode(eq_);
ffe_results.config.equalizer_structure = int32(equalizer_structure.ffe);
@@ -95,7 +105,7 @@ 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
@@ -140,24 +150,40 @@ end
end
function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, postFFE)
% Display analysis plots and metrics
figure(336);
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after FFE');
% Display analysis plots and metrics
if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
end
% 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);
showEQfilter(eq_.e, eq_signal_sd.fs.*2);
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
figure(341); clf;
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341);
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 102);
warning off
figure(400); clf;
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400);
showEQNoisePSD(eq_noise, "fignum", 103, "displayname", 'Residual Noise after FFE');
% Figure 2: Post-FFE coefficients (if available)
if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 104);
end
try
figure(339);
showEQfilter(eq_.e_tr, eq_signal_sd.fs.*2,"displayname",'training','fignum',339);
showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'dec. directed','fignum',339);
legend on
end
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);
figure(401); clf;
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
warning on
end

View File

@@ -45,9 +45,10 @@ end
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
% Process through postfilter and MLSE
mlse_sig_sd = pf_.process(eq_signal_sd, eq_noise);
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
mlse_.DIR = pf_.coefficients;
mlse_sig_sd = mlse_.process(mlse_sig_sd);
[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(mlse_sig_sd,tx_symbols);
mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
%% Calculate BER based on precoding mode
@@ -63,7 +64,17 @@ air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(dou
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
% MLSE metrics
alpha = pf_.coefficients(2);
alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2);
alpha = alpha(2);
gmi_mlse = GMI_MLSE;
air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M));
%% Display analysis if requested
if options.showAnalysis
displayAnalysis(eq_noise,whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE);
end
%% Prepare output structures
% Determine postFFE order
@@ -94,19 +105,21 @@ ffe_results.metrics.GMI = gmi_vnle;
ffe_results.metrics.AIR = air_vnle;
ffe_results.metrics.EVM = evm_vnle_total;
ffe_results.metrics.EVM_level = evm_vnle_lvl;
ffe_results.metrics.Alpha = [];
ffe_results.metrics.Alpha = alpha;
try
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
end
% Create FFE results structure
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
ffe_results.config = Equalizerstruct();
ffe_results.config.eq = jsonencode(eq_);
ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle);
ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part';
mlse_results = struct();
mlse_results.metrics = Metricstruct;
mlse_results.metrics.result_id = NaN;
@@ -118,12 +131,11 @@ mlse_results.metrics.numBits = numbits.mlse;
mlse_results.metrics.numBitErr = errors.mlse;
mlse_results.metrics.BER_precoded = bers.mlse_precoded;
mlse_results.metrics.numBitErr_precoded = errors.mlse_precoded;
mlse_results.metrics.SNR = NaN;
mlse_results.metrics.SNR_level = NaN;
mlse_results.metrics.GMI = NaN;
mlse_results.metrics.AIR = NaN;
mlse_results.metrics.EVM = NaN;
mlse_results.metrics.EVM_level = NaN;
% mlse_results.metrics.SNR = NaN;
mlse_results.metrics.GMI = gmi_mlse;
mlse_results.metrics.AIR = air_mlse;
% mlse_results.metrics.EVM = NaN;
% mlse_results.metrics.EVM_level = NaN;
mlse_results.metrics.Alpha = alpha;
mlse_results.metrics.MLSE_dir = mlse_.DIR;
@@ -131,15 +143,12 @@ mlse_results.metrics.MLSE_dir = mlse_.DIR;
mlse_results.config = Equalizerstruct();
mlse_results.config.eq = jsonencode(eq_);
mlse_.DIR = [];
mlse_.DIR = length(mlse_.DIR)-1;
mlse_results.config.mlse = jsonencode(mlse_);
mlse_results.config.equalizer_structure = int32(equalizer_structure.vnle_pf_mlse);
mlse_results.config.comment = 'function: vnle_postfilter_mlse - MLSE part';
%% Display analysis if requested
if options.showAnalysis
displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE);
end
end
@@ -206,7 +215,7 @@ end
end
function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE)
function displayAnalysis(eq_noise, whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE)
% Display analysis plots and metrics
figure(336);
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients);
@@ -228,4 +237,9 @@ showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400);
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
drawnow;
warning on
whitened_noise.spectrum("displayname",'after postfilter','fignum',342);
eq_noise.spectrum("displayname",'before postfilter','fignum',342);
end

View File

@@ -41,4 +41,7 @@ end
% Ensure a legend is displayed
legend('show');
end
xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]);
end

View File

@@ -53,8 +53,8 @@ function showEQcoefficients(options)
for i = 1:numSubplots
subplot(1, numSubplots, i);
stem(coeffs{i}, 'Color', options.color, 'LineWidth', 0.1, ...
'Marker', '.', 'MarkerSize', 1);
stem(coeffs{i}, 'Color', options.color, 'LineWidth', 1, ...
'Marker', '.', 'MarkerSize', 10);
title(titles{i});
ylim([-1, 1]); % Set y-axis limits to [-1, 1]
grid on;

View File

@@ -1,5 +1,12 @@
function showEQfilter(coefficients,fs)
function showEQfilter(coefficients,fs,options)
arguments
coefficients
fs
options.fignum (1,1) double = NaN % Default to NaN if not provided
options.displayname (1,:) char = '' % Default to an empty string if not provided
end
% Assuming that obj.e contains the final FFE filter coefficients.
% Set the number of frequency points and sampling frequency.
@@ -12,24 +19,28 @@ function showEQfilter(coefficients,fs)
half_nfft = floor(nfft/2) + 1;
f = f(1:half_nfft);
H = H(1:half_nfft);
% Plot the magnitude and phase responses.
figure(339);
% Determine the figure number to use or create a new figure
if isnan(options.fignum)
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
end
% Magnitude response (in dB)
subplot(2,1,1);
hold on
plot(f.*1e-9, 20*log10(abs(1./H)));
plot(f.*1e-9, 20*log10(abs(1./H)),'DisplayName',options.displayname);
title('(Inverted) Magnitude Response of FFE Filter');
xlabel('Frequency (Hz)');
xlabel('Frequency (GHz)');
ylabel('Magnitude (dB)');
grid on;
% Phase response
subplot(2,1,2);
plot(f.*1e-9, unwrap(angle(H)));
plot(f.*1e-9, unwrap(angle(H)),'DisplayName',options.displayname);
title('Phase Response of FFE Filter');
xlabel('Frequency (Hz)');
xlabel('Frequency (GHz)');
ylabel('Phase');
grid on;

View File

@@ -20,10 +20,14 @@ end
fig = figure(options.fignum); % Use the specified figure number
end
eq_signal = max(min(eq_signal,3),-3);
%%% Separate Classes
constellation = unique(ref_symbols);
received_sd = NaN(numel(constellation),length(ref_symbols));
lvlcol = cbrewer2('Set1',numel(constellation));
lvlcol = cbrewer2('Paired',numel(constellation)*2);
lvlcol = lvlcol(2:2:end,:);
% lvlcol = cbrewer2('Set1',numel(constellation));
for lvl = 1:numel(constellation)
%Separate the equalized signal into the
%respective levels based on the actually
@@ -43,6 +47,7 @@ end
histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' ; ',num2str(cnt(lvl)),' '],'FaceColor',lvlcol(lvl,:),'Normalization','pdf');
warning on
end
xlim([-3 3]);
legend
grid on

View File

@@ -7,7 +7,7 @@ arguments
options.f_sym = [];
end
plot_shit = 0;
plot_shit = 1;
if isa(eq_signal,'Signal')
options.f_sym = eq_signal.fs;
@@ -25,6 +25,7 @@ if plot_shit
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
clf;
end
end
@@ -127,5 +128,6 @@ if plot_shit
yline(levels);
xlabel('Time in $\mu$s');
ylabel('Normalized Amplitude');
ylim([-3 3]);
end
end

View File

@@ -1,4 +1,4 @@
function [Bits, Symbols, Scpe_cell, found_sync] = loadSignalData(dataTable, options)
function [Bits, Symbols, Scpe_cell, found_sync] = loadAndSyncSignalDataFromDb(dataTable, options)
% LOADSIGNALDATA Loads and synchronizes signal data from storage
%
% Inputs:
@@ -12,22 +12,30 @@ function [Bits, Symbols, Scpe_cell, found_sync] = loadSignalData(dataTable, opti
% found_sync - Boolean indicating if synchronization was successful
found_sync = 0;
tempLocalStorage = 1;
tempLocalStorage = 0;
% Define the fixed storage directory relative to the user's MATLAB preferences directory
storage_dir = fullfile(prefdir, 'temp_sync_data');
% Part A: Check and load from local storage if available
if tempLocalStorage == 1
local_filename = sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id));
local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)));
if exist(local_filename, 'file')
% Load from local storage and return
load(local_filename, 'Bits', 'Symbols', 'Scpe_cell');
found_sync = 1;
try
load(local_filename, 'Bits', 'Symbols', 'Scpe_cell');
found_sync = 1;
return
catch
delete(local_filename);
end
end
end
% If not locally saved, load from storage
if ~found_sync
% Load transmitted bits
Bits = load([options.storage_path, char(dataTable.tx_bits_path)]);
Bits = load(fullfile([options.storage_path, char(dataTable.tx_bits_path)]));
Bits = Bits.Bits;
% Map bits to symbols
@@ -37,7 +45,7 @@ if ~found_sync
Symbols_mapped.fs = fsym;
% Load original symbols
Symbols = load([options.storage_path, char(dataTable.tx_symbols_path)]);
Symbols = load(fullfile([options.storage_path, char(dataTable.tx_symbols_path)]));
Symbols = Symbols.Symbols;
found_sync = 0;
@@ -45,7 +53,7 @@ if ~found_sync
% Try to load pre-synchronized data
try
Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]);
Scpe_load = load(fullfile([options.storage_path, char(dataTable.rx_sync_path)]));
Scpe_cell = Scpe_load.S;
[~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1);
catch
@@ -74,21 +82,21 @@ end
% Part B: Save to local storage if data was loaded and synced
if tempLocalStorage == 1 && found_sync
local_filename = sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id));
% Create directory if it doesn't exist
if ~exist('temp_sync_data', 'dir')
mkdir('temp_sync_data');
if ~exist(storage_dir, 'dir')
mkdir(storage_dir);
end
% local_filename = fullfile(storage_dir, sprintf('sync_data_run_%s.mat', num2str(dataTable.run_id)));
% List existing files and remove oldest if more than N
max_local_files = 10; % Store up to N files
files = dir('sync_data_run_*.mat');
files = dir(fullfile(storage_dir, 'sync_data_run_*.mat'));
if length(files) >= max_local_files
% Sort by date
[~, idx] = sort([files.datenum]);
% Delete oldest file
delete(files(idx(1)).name);
delete(fullfile(storage_dir, files(idx(1)).name));
end
% Save current data

View File

@@ -89,11 +89,13 @@ if submit_mode == processingMode.parallel
% Submit job with proper arguments
futures{jobCounter} = parfeval(@dsp_runid, 1, run_ids(r), ...
"database_name", dsp_options.database_name, ...
"database_type", dsp_options.database_type,...
"dataBase", dsp_options.dataBase, ...
"append_to_db", dsp_options.append_to_db, ...
"database_path", dsp_options.database_path, ...
"load_file_path", dsp_options.load_file_path, ...
"max_occurences", dsp_options.max_occurences, ...
"storage_path", dsp_options.storage_path, ...
"mode", dsp_options.mode,...
"parameters", optionalVars);
fprintf('[RunID %d, Job %d] Submitted to pool.\n', run_ids(r), k);
@@ -132,11 +134,13 @@ elseif submit_mode == processingMode.serial
fprintf('[RunID %d, Job %d] Running in linear mode...\n', run_ids(r), k);
results{k,r} = dsp_runid(run_ids(r),...
"database_name", dsp_options.database_name,...
"database_type", dsp_options.database_type,...
"dataBase", dsp_options.dataBase,...
"append_to_db", dsp_options.append_to_db,...
"database_path", dsp_options.database_path,...
"load_file_path", dsp_options.load_file_path,...
"max_occurences", dsp_options.max_occurences,...
"storage_path", dsp_options.storage_path,...
"mode", dsp_options.mode,...
"parameters", optionalVars);
fprintf('[RunID %d, Job %d] Completed successfully.\n', run_ids(r), k);
@@ -172,7 +176,7 @@ wh = submit_options.wh;
function setupParallelPool()
curpool = gcp('nocreate');
if isempty(curpool)
parpool;
parpool(10,"IdleTimeout",300);
else
if ~isempty(curpool.FevalQueue.QueuedFutures) || ~isempty(curpool.FevalQueue.RunningFutures)
oldq = length(curpool.FevalQueue.QueuedFutures) + length(curpool.FevalQueue.RunningFutures);

View File

@@ -23,8 +23,6 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
levels = unique(tx_signal);
% Preallocate an array to store the SNR for each unique level
snr_per_level = zeros(size(levels));
% Loop over each unique level to compute the SNR for that level
for i = 1:length(levels)
% Find indices where tx_signal equals the current level
@@ -32,5 +30,7 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
% Compute the SNR for these indices
snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx));
histogram(eq_noise(idx));
end
end

View File

@@ -0,0 +1,64 @@
% Parameters
N_values = [10 100 1000 4096]; % Different filter lengths to analyze
fs = 112e9;
% Create figure
figure;
% Plot frequency responses
subplot(211)
hold on
grid on
ylabel('Magnitude (dB)')
title('Frequency Response')
yline(-3,'--r')
ylim([-40 5])
subplot(212)
hold on
grid on
xlabel('Frequency (GHz)')
ylabel('Phase (rad)')
title('Phase Response')
% Color map for different lines
colors = cbrewer2('Set1',length(N_values));
% Loop through different filter lengths
for i = 1:length(N_values)
N = N_values(i);
% Filter coefficients
b = ones(1,N)/N;
a = 1;
% Frequency response
[h,w] = freqz(b,a,4096*8);
freq = (w/(2*pi))*fs;
h_db = 20*log10(abs(h));
% Plot magnitude response
subplot(211)
plot(freq/1e9, h_db, 'Color', colors(i,:), 'DisplayName', sprintf('N=%d', N),'LineWidth',0.1)
% Plot phase response
subplot(212)
plot(freq/1e9, unwrap(angle(h)), 'Color', colors(i,:), 'DisplayName', sprintf('N=%d', N),'LineWidth',0.1)
% Find -3dB frequency
cutoff_idx = find(h_db <= -3, 1);
f_cutoff = freq(cutoff_idx)/1e9;
fprintf('N=%d: Cutoff frequency (-3dB point): %.2f GHz\n', N, f_cutoff)
end
% Add legend and adjust axes
subplot(211)
legend('show')
xlim([0 16]) % Adjust x-axis limit to better see the differences
subplot(212)
legend('show')
xlim([0 16]) % Adjust x-axis limit to better see the differences
% Analytical approximation
f_3db_approx = 0.443 * fs./N_values ./ 1e9;