diff --git a/Classes/05_Lab/AwgKeysight.m b/Classes/05_Lab/AwgKeysight.m index ec82430..68e34f0 100644 --- a/Classes/05_Lab/AwgKeysight.m +++ b/Classes/05_Lab/AwgKeysight.m @@ -226,7 +226,7 @@ classdef AwgKeysight case awg_model.M8199A v = visadev('TCPIP0::Zizou::hislip2::INSTR'); case awg_model.M8199A_ILV - v = visadev('TCPIP0::Zizou::hislip2::INSTR'); + v = visadev('TCPIP0::Zizou::hislip0::INSTR'); end debug = 0; diff --git a/Classes/DataBaseHandler/DBHandler.m b/Classes/DataBaseHandler/DBHandler.m index 6f90ca7..368dd2a 100644 --- a/Classes/DataBaseHandler/DBHandler.m +++ b/Classes/DataBaseHandler/DBHandler.m @@ -9,6 +9,7 @@ classdef DBHandler < handle tableNames % Cell array containing names of all tables in the database tables = struct(); % Structure containing MATLAB tables for each database table distinctValues + type end methods @@ -21,6 +22,7 @@ classdef DBHandler < handle arguments options.pathToDB = ""; % Default value for pathToDB if not provided + options.type = "mysql"; end % Assign values to class properties based on input arguments @@ -33,7 +35,22 @@ classdef DBHandler < handle % Establish a connection to the SQLite database try - obj.conn = sqlite(obj.pathToDB); + + if options.type == "sqlite" + obj.conn = sqlite(obj.pathToDB); + elseif options.type == "mysql" + datasource = "jdbc:mysql://134.245.243.254:3306/labor"; + + obj.conn = database( ... + "labor", ... % Database name + "silas", ... % Username + "silas", ... % Password (or getSecret) + "Vendor", "MySQL", ... + "Server", "134.245.243.254", ... + "PortNumber", 3306, ... + "JDBCDriverLocation", "C:\Users\sioe\Documents\mysql-connector-j-9.3.0\mysql-connector-j-9.3.0.jar"); + end + catch e error('Failed to connect to the database: %s', e.message); end @@ -59,8 +76,14 @@ classdef DBHandler < handle function obj = getTableNames(obj) % Get all table names from the database try - result = fetch(obj.conn, 'SELECT name FROM sqlite_master WHERE type="table"'); - obj.tableNames = result.name; + if obj.type == "mysql" + result = fetch(obj.conn, 'SHOW TABLES;'); + obj.tableNames = result.Variables; + elseif obj.type == "sqlite" + result = fetch(obj.conn, 'SELECT name FROM sqlite_master WHERE type="table"'); + obj.tableNames = result.name; + end + catch e error('Failed to retrieve table names: %s', e.message); end @@ -213,12 +236,16 @@ classdef DBHandler < handle elseif ischar(value) newRow.(colName{1}) = string(value); + elseif isdatetime(value) + + % newRow.(colName{1}) = string(value); + end end % Parameters for retry logic - maxRetries = 5; + maxRetries = 50; basePause = 0.05; % seconds attempt = 0; @@ -226,6 +253,9 @@ classdef DBHandler < handle while ~success && attempt <= maxRetries try + if obj.type == "mysql" + newRow_ = obj.convertTableToCellStrings(newRow); + end sqlwrite(obj.conn, tableName, newRow); success = true; % Write successful catch e @@ -245,7 +275,14 @@ classdef DBHandler < handle end % Retrieve the measurement_id of the newly inserted row for linking other tables - result = fetch(obj.conn, 'SELECT last_insert_rowid()'); + + if obj.type == "mysql" + queue = "SELECT LAST_INSERT_ID()"; + elseif obj.type == "sqlite" + queue = "SELECT last_insert_rowid()"; + end + + result = fetch(obj.conn, queue); lastID = result{1, 1}; % Access the value directly from the table end @@ -502,8 +539,87 @@ classdef DBHandler < handle % Step 4: Execute the query and handle results result = obj.fetch(query); + result = obj.normalizeMySQLTable(result); end + function cleanedTable = normalizeMySQLTable(~,result) + cleanedTable = result; + varNames = result.Properties.VariableNames; + + for i = 1:numel(varNames) + col = result.(varNames{i}); + + % Only process if column is a cell array of strings or chars + if iscell(col) && all(cellfun(@(x) ischar(x) || isstring(x), col)) + % Try converting to numeric if possible + numCol = str2double(col); + if all(~isnan(numCol) | strcmpi(col, 'NaN')) + % It's numeric (with possible NaNs) + cleanedTable.(varNames{i}) = numCol; + else + % Clean double-quoted SQL literals (e.g., ""no_db"") + cleanedTable.(varNames{i}) = strrep(string(col), '""', '"'); + end + end + end + end + + function cellTable = convertTableToCellStrings(~,tbl) + % Converts all variables in a MATLAB table to cell arrays of strings (like JDBC fetch from MySQL) + % + % Example: + % jdbcFormatted = convertTableToCellStrings(myTable); + + cellTable = tbl; + varNames = tbl.Properties.VariableNames; + + for i = 1:numel(varNames) + col = tbl.(varNames{i}); + + if isnumeric(col) + % Convert numeric values to strings + cellTable.(varNames{i}) = arrayfun(@(x) num2str(x, '%.15g'), col, 'UniformOutput', false); + + elseif isstring(col) || ischar(col) + % Ensure cell array of strings + cellTable.(varNames{i}) = cellstr(col); + + elseif iscell(col) + % Convert each cell entry to string + cellTable.(varNames{i}) = cellfun(@convertToString, col, 'UniformOutput', false); + + elseif islogical(col) + % Convert logicals to '0' or '1' + cellTable.(varNames{i}) = cellstr(string(double(col))); + + elseif isdatetime(col) + % Convert datetimes to formatted string + cellTable.(varNames{i}) = cellstr(string(col)); + + else + warning('Column "%s" has unsupported type. Converting using string().', varNames{i}); + cellTable.(varNames{i}) = cellstr(string(col)); + end + end + end + + function out = convertToString(~,val) + if ischar(val) + out = val; + elseif isstring(val) + out = char(val); + elseif isnumeric(val) + out = num2str(val, '%.15g'); + elseif islogical(val) + out = num2str(double(val)); + elseif isdatetime(val) + out = datestr(val, 'yyyy-mm-dd HH:MM:SS'); + else + out = char(string(val)); % fallback + end + end + + function query = constructSQLQuery(obj, filterParams, selectedFields) % constructSQLQuery Constructs the SQL query based on filter parameters and selected fields. % diff --git a/Functions/Lab_helper/holdAndShowValue.m b/Functions/Lab_helper/holdAndShowValue.m index 1740aaa..5efe6ae 100644 --- a/Functions/Lab_helper/holdAndShowValue.m +++ b/Functions/Lab_helper/holdAndShowValue.m @@ -15,7 +15,7 @@ function holdAndShowValue() 'ButtonPushedFcn', @(src, event) closeWindow()); % Initialize the timer - updateTimer = timer('ExecutionMode', 'fixedRate', 'Period', 0.1, ... + updateTimer = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, ... 'TimerFcn', @(src, event) updatePowerValues()); % Start the timer diff --git a/projects/ECOC_2025/dsp_run_id.m b/projects/ECOC_2025/dsp_run_id.m index fb9f689..7b02af6 100644 --- a/projects/ECOC_2025/dsp_run_id.m +++ b/projects/ECOC_2025/dsp_run_id.m @@ -5,14 +5,14 @@ arguments options.append_to_db = 0; options.max_occurences = 4; options.parameters = struct(); - + options.database_path - options.database_name + options.database_name options.storage_path end -database = DBHandler("pathToDB",[options.database_path,options.database_name]); +database = DBHandler("pathToDB",[options.database_path,options.database_name],"type",'mysql'); filterParams = database.tables; filterParams.Configurations = struct('run_id', run_id); @@ -74,6 +74,9 @@ vnle_pf_package = {}; vnle_dfe_package = {}; dbtgt_package = {}; +vnle_pf =1; +db_tgt = 0; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Tx_signal = load([options.storage_path, char(dataTable.tx_signal_path)]); @@ -129,76 +132,80 @@ for occ = 1:record_realizations Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); if duob_mode ~= db_mode.db_encoded - + % %%%%% VNLE + DFE %%%% if 0 - + eq_vnle_dfe = EQ("Ne",vnle_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); 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); - + [result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,"showAnalysis",1,"postFFE",[]); vnle_dfe_package{occ} = result; - + end - + %%%%% VNLE + PF + MLSE %%%% - if 1 - + if vnle_pf + [result] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0); vnle_pf_package{occ} = result; - + if options.append_to_db database.addProcessingResult(run_id,result.resultsMLSE, result.equalizerConfigMLSE); database.addProcessingResult(run_id,result.resultsVNLE, result.equalizerConfigVNLE); end end - + %%%%% Duobinary Targeting %%%% - if 1 + if db_tgt [result] = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, "precode_mode", duob_mode,'showAnalysis',0,"postFFE",[]); dbtgt_package{occ} = result; - + if options.append_to_db database.addProcessingResult(run_id, result.resultsDBtgt, result.equalizerConfigDBtgt); end end - % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e; BER DB tgt: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded,dbtgt_package{occ}.resultsDBtgt.BER,dbtgt_package{occ}.resultsDBtgt.BER_precoded) - % % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded); + % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e; BER DB tgt: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded,dbtgt_package{occ}.resultsDBtgt.BER,dbtgt_package{occ}.resultsDBtgt.BER_precoded) + % % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded); - occ = 1; % or whatever your loop index is + occ = 1; % or whatever your loop index is - % Extract VNLE results for readability - vnle = vnle_pf_package{occ}.resultsVNLE; - mlse = vnle_pf_package{occ}.resultsMLSE; - dbtgt = dbtgt_package{occ}.resultsDBtgt; - + if vnle_pf + % Extract VNLE results for readability + vnle = vnle_pf_package{occ}.resultsVNLE; + mlse = vnle_pf_package{occ}.resultsMLSE; + end + if db_tgt + dbtgt = dbtgt_package{occ}.resultsDBtgt; + end % Print header fprintf("==== EQUALIZATION RUN-ID %d | PAM-%d | %.2f GBd ====\n\n", run_id, M, Symbols.fs.*1e-9); - - % VNLE Results - fprintf(">> VNLE Results:\n"); - fprintf(" BER %.2e\n", vnle.BER); - fprintf(" BER (pre-code) %.2e\n", vnle.BER_precoded); - fprintf(" SNR: %.2f dB\n", vnle.SNR); - fprintf(" GMI: %.4f\n", vnle.GMI); - fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9); - fprintf(" AIR: %.2f Gbps\n", vnle.AIR.*1e-9); - fprintf("\n"); - - % MLSE Results - fprintf(">> MLSE Results:\n"); - fprintf(" BER : %.2e\n", mlse.BER); - fprintf(" BER (pre-code): %.2e\n", mlse.BER_precoded); - fprintf(" Channel Alpha : %.2f\n", mlse.Alpha); - fprintf("\n"); - - % DB Target Results - fprintf(">> DB Target Results:\n"); - fprintf(" BER: %.2e\n", dbtgt.BER); - fprintf(" BER (pre-code): %.2e\n", dbtgt.BER_precoded); - fprintf("\n"); - + if vnle_pf + % VNLE Results + fprintf(">> VNLE Results:\n"); + fprintf(" BER %.2e\n", vnle.BER); + fprintf(" BER (pre-code) %.2e\n", vnle.BER_precoded); + fprintf(" SNR: %.2f dB\n", vnle.SNR); + fprintf(" GMI: %.4f\n", vnle.GMI); + fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9); + fprintf(" AIR: %.2f Gbps\n", vnle.AIR.*1e-9); + fprintf("\n"); + + % MLSE Results + fprintf(">> MLSE Results:\n"); + fprintf(" BER : %.2e\n", mlse.BER); + fprintf(" BER (pre-code): %.2e\n", mlse.BER_precoded); + fprintf(" Channel Alpha : %.2f\n", mlse.Alpha); + fprintf("\n"); + end + if db_tgt + % DB Target Results + fprintf(">> DB Target Results:\n"); + fprintf(" BER: %.2e\n", dbtgt.BER); + fprintf(" BER (pre-code): %.2e\n", dbtgt.BER_precoded); + fprintf("\n"); + end else diff --git a/projects/ECOC_2025/dsp_standalone.m b/projects/ECOC_2025/dsp_standalone.m index 6424498..e393135 100644 --- a/projects/ECOC_2025/dsp_standalone.m +++ b/projects/ECOC_2025/dsp_standalone.m @@ -1,10 +1,10 @@ - -databasePath = 'Z:\2025\ECOC Silas\'; +savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; +databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; database_name = 'ecoc2025_loops.db'; -db = DBHandler("pathToDB", [databasePath, database_name]); + num_occ = 1; run_par = false; -run_id = 562; -[out, ~] = submit_dsp(run_id, basePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ); +run_id = 2589; +[out, ~] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ); diff --git a/projects/ECOC_2025/measure_run_script.m b/projects/ECOC_2025/measure_run_script.m index 15405f8..4abd8eb 100644 --- a/projects/ECOC_2025/measure_run_script.m +++ b/projects/ECOC_2025/measure_run_script.m @@ -1,7 +1,5 @@ -savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; - fullFolderPath = fullfile(savePath, current_folder); if ~exist(fullFolderPath, 'dir') mkdir(fullFolderPath); @@ -19,19 +17,19 @@ referenceFields = fieldnames(db.tables.Configurations); assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!'); - %% Lab Automation if run_lab_automation % DC Source - if confPrev.v_bias ~= conf.v_bias + if (any(confPrev.v_bias ~= conf.v_bias)) || (~exist('dcs','var')) || (isempty(confPrev.v_bias)) dcs = DC_supply("active",[1,1],"voltage",[conf.v_bias, 5]); dcs.set("voltage",[conf.v_bias, 5]); [v_bias_meas,i_bias_meas]=dcs.readVals(); end % Laser - if confPrev.laser_power ~= conf.laser_power + + if (any(confPrev.laser_power ~= conf.laser_power)) || (~exist('laser','var')) || (isempty(confPrev.laser_power)) laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib'); laser.setWavelength(conf.wavelength); laser.setPower(conf.laser_power); @@ -39,16 +37,24 @@ if run_lab_automation end % Optical Attenuator - voa = OptAtten("active",[0,2,1,1],"value",[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation],"wavelength",[conf.wavelength,conf.wavelength,conf.wavelength,conf.wavelength]); + if ~exist('voa','var') + voa = OptAtten("active",[1,2,1,1],"value",[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation],"wavelength",[conf.wavelength,conf.wavelength,conf.wavelength,conf.wavelength]); + end voa.set('value',[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation]); + if voa.power_state(3) > -24 || voa.power_state(1)+ voa.atten_state(4) > -38 + holdAndShowValue + end + % PDFA - pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15'); + if ~exist('pdfa','var') + pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15'); + end % Construct AWG and Scope Modules %%%%%% fdac = 224e9; fadc = 160e9; - Scp = ScopeKeysight("model","DSAZ634A",'autoscale',1,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",5000000,"removeDC",1,"extRef",1); + Scp = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",recordlen,"removeDC",1,"extRef",1); Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]); A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0); @@ -56,12 +62,16 @@ end %% Signal generation and transmission -if 0% awg_upload_required || isempty(loop_id) +if awg_upload_required || isempty(loop_id) %%%%% Symbol Generation %%%%%% Pform = Pulseformer("fsym",conf.symbolrate,"fdac",8*conf.symbolrate,"pulse","rc","pulselength",16,"alpha",conf.pulsef_alpha); - + + if conf.symbolrate < 64e9 + sequence_order = min(18,sequence_order); + end + Pamsource = PAMsource(... - "fsym",conf.symbolrate,"M",conf.pam_level,"order",19,"useprbs",1,... + "fsym",conf.symbolrate,"M",conf.pam_level,"order",sequence_order,"useprbs",1,... "fs_out",fdac,... "applyclipping",0,... "clipfactor",4,... @@ -69,13 +79,13 @@ if 0% awg_upload_required || isempty(loop_id) "pulseformer",Pform,... "randkey",1,... "duobinary_mode", conf.db_mode); - + conf.pam_source = Pamsource; confPrev = conf; - + [Digi_sig,Symbols,Bits] = Pamsource.process(); - - + + %%%%% Precompensation Routine %%%%%% if precomp_mode == 1 % measure channel precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',fdac); @@ -84,12 +94,12 @@ if 0% awg_upload_required || isempty(loop_id) precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs); Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',conf.precomp_amp,'loadPath',precomp_path,'fileName',precomp_fn); end - + % Digi_sig.spectrum("displayname",'Tx Spectrum','fignum',10,'normalizeTo0dB',0); - + %%%%% Resample to DAC rate %%%%%% Digi_sig = Digi_sig.resample("fs_out",Awg.fdac); - + % [~,~,Scpe_sig_raw,~] = A2S.process("signal2",Digi_sig); Awg.upload("signal2",Digi_sig); @@ -104,7 +114,6 @@ if precomp_mode == 1 return; % End routine if precomp mode is active end - %% Save static TX data (only once) currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss'); timeStr = char(currentTime); @@ -119,8 +128,6 @@ save([savePath, tx_symbols_path], "Symbols"); tx_signal_path = [filesep, current_folder, filesep, base_filename, '_signal']; save([savePath, tx_signal_path], "Digi_sig"); -n_recording = 2; % Or any number you want - for recIdx = 1:n_recording fprintf('Recording %d / %d\n', recIdx, n_recording); @@ -167,7 +174,7 @@ for recIdx = 1:n_recording newRun.date_of_run = datetime(currentTime, 'InputFormat', 'yyyyMMdd_HHmmss'); newRun.tx_bits_path = tx_bits_path; newRun.tx_symbols_path = tx_symbols_path; - newRun.rx_sync_path = []; % Leave empty for now + newRun.rx_sync_path = ""; % Leave empty for now newRun.rx_raw_path = rx_raw_path; newRun.filename = filename; newRun.tx_signal_path = tx_signal_path; @@ -197,10 +204,12 @@ for recIdx = 1:n_recording db.appendToTable('Measurements', meas); - %% Submit DSP Routine - - % [out, ~] = submit_dsp(run_id, basePath, database_name, savePath, ... - % "parallel", parallel_dsp, "max_occurences", max_occurences); + %% Submit DSP Routine + + if subimt_DSP + [out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ... + "parallel", parallel_dsp, "max_occurences", max_occurences); + end end diff --git a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m index e737228..26a85c6 100644 --- a/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m +++ b/projects/ECOC_2025/plots_from_database/plot_mpi_trial.m @@ -1,8 +1,20 @@ +local = 1; + +if local + databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; +else + databasePath = '\\ntserver.tf.uni-kiel.de\scratch\sioe\ECOC_2025\'; +end -basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; database_name = 'ecoc2025_loops.db'; -database = DBHandler("pathToDB", [basePath, database_name]); +database = DBHandler("pathToDB", [databasePath, database_name]); + +figure(); +plotBoundaries = 1; +plotRealizations = 1; +cols = linspecer(3); % Ensure color count matches +% cols = cols(8,:); filterParams = database.tables; filterParams.Configurations = struct( ... @@ -10,7 +22,7 @@ filterParams.Configurations = struct( ... 'fiber_length', 0, ... 'db_mode', '"no_db"', ... 'interference_attenuation', [], ... - 'interference_path_length', 50, ... + 'interference_path_length', 10, ... 'is_mpi', 1, ... 'pam_level', 4, ... 'wavelength', 1310, ... @@ -23,8 +35,8 @@ filterParams.Configurations = struct( ... % filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded); % filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); -selectedFields = {'Configurations.run_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.v_bias' 'Configurations.v_awg' 'Configurations.precomp_amp' 'Configurations.symbolrate' 'Configurations.pam_level'... - 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'Configurations.signal_attenuation' ... +selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.v_bias' 'Configurations.v_awg' 'Configurations.precomp_amp' 'Configurations.symbolrate' 'Configurations.pam_level'... + 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'Configurations.interference_path_length' 'Configurations.signal_attenuation' ... 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ... 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'}; @@ -32,14 +44,18 @@ selectedFields = {'Configurations.run_id' 'Runs.date_of_run' 'Runs.rx_raw_path' dataTable.SIR = round(-6 - dataTable.power_mpi_interference); dataTable = cleanUpTable(dataTable); -% Filter by time -startTime = datetime('2025-04-12 18:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); -stopTime = datetime('2025-04-13 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); -dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); -dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); -dataTable = dataTable(dataTable.date_of_processing > startTime, :); -dataTable = dataTable(dataTable.date_of_processing < stopTime, :); +dataTable(dataTable.eq_id==0,:) = []; +% Filter by time +filter_by_time = 0; +if filter_by_time + startTime = datetime('2025-04-14 13:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + stopTime = datetime('2025-04-14 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); + dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); + dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS'); + dataTable = dataTable(dataTable.date_of_processing > startTime, :); + dataTable = dataTable(dataTable.date_of_processing < stopTime, :); +end % Group by smth y_var = 'BER'; x_var = 'SIR'; @@ -53,16 +69,14 @@ dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean); dataTableGrpd_min = groupIt(fixedVars, dataTable, @min); dataTableGrpd_max = groupIt(fixedVars, dataTable, @max); -plotRealizations = 1; + % Create a new figure mkr = '.'; -figure(); hold on unique_loop_var = unique(dataTable.(loop_var)); -cols = linspecer(numel(unique_loop_var)); % Ensure color count matches -for i = 1:2%1:numel(unique_loop_var) +for i = 1%:numel(unique_loop_var) % Prepare filtered data for this loop variable loopValue = unique_loop_var(i); @@ -86,31 +100,38 @@ for i = 1:2%1:numel(unique_loop_var) % Display name (optional) idx = find(dataTable.(loop_var) == loopValue, 1, 'first'); dispname = equalizer_structure(dataTable.equalizer_structure(idx)); + dispname = [char(dispname),'; ',num2str(unique(dataTable.interference_path_length)),' m']; - % Plot bounded line - [hl, hp] = boundedline(x_values, y_mean, y_bounds, ... - 'alpha', 'transparency', 0.2, ... - 'cmap', cols(i,:), ... - 'nan', 'fill', ... - 'orientation', 'vert'); + if plotBoundaries + % Plot bounded line + [hl, hp] = boundedline(x_values, y_mean, y_bounds, ... + 'alpha', 'transparency', 0.2, ... + 'cmap', cols(i,:), ... + 'nan', 'fill', ... + 'orientation', 'vert'); + + % Style the main line: thinnest, dotted, no marker + set(hl, 'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... + 'Color', cols(i,:), 'DisplayName', string(dispname)); + + % Hide patch (shaded area) from legend + set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none'); + + % Add invisible scatter for DataTips + plt = scatter(x_values, y_mean, ... + 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... + 'HandleVisibility', 'off', 'PickableParts', 'all'); + else + plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... + 'Color', cols(i,:), 'DisplayName', string(dispname)); + end + % Add data tips to the invisible scatter + pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)}; + pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9}; + pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)}; + addDatatips(plt, pair_one, pair_two, pair_three); - % Style the main line: thinnest, dotted, no marker - set(hl, 'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... - 'Color', cols(i,:), 'DisplayName', string(dispname)); - - % Hide patch (shaded area) from legend - set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none'); - - % Add invisible scatter for DataTips - sc_fake = scatter(x_values, y_mean, ... - 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... - 'HandleVisibility', 'off', 'PickableParts', 'all'); - - % Add data tips to the invisible scatter - pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)}; - pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9}; - pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)}; - addDatatips(sc_fake, pair_one, pair_two, pair_three); + % Optionally: outline bounds for better visibility (optional) % hnew = outlinebounds(hl, hp); @@ -145,6 +166,8 @@ if y_var == 'BER' ylim([1e-5, 0.1]); end +xlim([15,50]); + % Enable grid and beautify grid on; beautifyBERplot; diff --git a/projects/run_loop.m b/projects/run_loop.m index 35a7178..4eedbe8 100644 --- a/projects/run_loop.m +++ b/projects/run_loop.m @@ -2,23 +2,36 @@ % === Prepare base configuration === % databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; -databasePath = 'Z:\2025\ECOC Silas\'; -database_name = 'ecoc2025_loops.db'; -db = DBHandler("pathToDB", [databasePath, database_name]); -conf = db.tables.Configurations; -confPrev = db.tables.Configurations; +local = 1; +if local + databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; + savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; +else + databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; + savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; +end -awg_upload_required = 0; +database_name = 'ecoc2025_loops.db'; + +db = DBHandler("pathToDB", [databasePath, database_name],"type",'mysql'); + +conf = db.tables.Configurations; +if ~exist('confPrev','var') + confPrev = db.tables.Configurations; +end + +awg_upload_required = 1; %still uploads every first round +subimt_DSP = 1; +recordlen = 5000000; +sequence_order = 17; +n_recording = 1; % Or any number you want % Define vector parameters directly in conf: conf.unique_elab_id = "20250408-842b0a3078172b48dd032795226fbe683190afc4"; -conf.symbolrate = 112e9; -conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10; -conf.pam_level = 4; conf.db_mode = db_mode.no_db; conf.pulsef_alpha = 0.2; -conf.v_bias = 2.5; -conf.v_awg = 0.95; +conf.v_bias = 2.65; +conf.v_awg = 0.9; conf.precomp_amp = -64; %-64 conf.wavelength = 1310; conf.laser_power = 11; @@ -26,8 +39,11 @@ conf.fiber_length = 0; conf.pd_in_desired = 9; conf.is_mpi = 1; conf.signal_attenuation = 0; -conf.interference_path_length = 1000; %m -conf.interference_attenuation = [40,20:-1:0]; +conf.interference_path_length = 0; %m +conf.interference_attenuation = 40;%[0:2:30,40]; +conf.pam_level = 4;%[4,6,8]; +conf.symbolrate = 56e9;%[56,72,96,112].*1e9; +conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10; conf.pam_source = []; if conf.is_mpi diff --git a/test/database_mysql_test.m b/test/database_mysql_test.m new file mode 100644 index 0000000..a33e57c --- /dev/null +++ b/test/database_mysql_test.m @@ -0,0 +1,17 @@ + +db = DBHandler("type","mysql"); + +db.tableNames +currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss'); +newRun = db.tables.Runs; +newRun.run_id = NaN; +newRun.loop_id = 82; +newRun.date_of_run = datetime(currentTime, 'InputFormat', 'yyyyMMdd_HHmmss'); +newRun.tx_bits_path = 'pathtohell'; +newRun.tx_symbols_path = 'pathtohell2'; +newRun.rx_sync_path = ""; % Leave empty for now +newRun.rx_raw_path = 'pathtohell4'; +newRun.filename = 'filenametohell'; +newRun.tx_signal_path = 'pathtohell'; + +run_id = db.appendToTable('Runs', newRun); \ No newline at end of file