diff --git a/Classes/01_transmit/PAMmapper.m b/Classes/01_transmit/PAMmapper.m index 6c1276d..83287be 100644 --- a/Classes/01_transmit/PAMmapper.m +++ b/Classes/01_transmit/PAMmapper.m @@ -504,9 +504,118 @@ classdef PAMmapper end - function bitmap = showBitMapping(obj) + function [ax, mapping_table] = plotBitMapping(obj) + [constellation, bitmap] = obj.getConstellationBitMapping(); + constellation = constellation .* obj.get_scaling; + bit_labels = string(cellstr(char(bitmap + '0'))); - bitmap = obj.demap((obj.levels ./ obj.scaling)'); + if obj.M == 6 + [~, order] = sortrows([-constellation(:,2), constellation(:,1)]); + constellation = constellation(order, :); + bit_labels = bit_labels(order); + + mapping_table = table(constellation(:,1), constellation(:,2), bit_labels, ... + 'VariableNames', {'symbol_1', 'symbol_2', 'bits'}); + + fig = figure("Name", sprintf("PAM-%d Bit Mapping", obj.M)); + ax = axes(fig); + scatter(ax, constellation(:,1), constellation(:,2), 60, "filled"); + hold(ax, "on"); + grid(ax, "on"); + box(ax, "on"); + + x_step = min(diff(unique(constellation(:,1)))); + y_step = min(diff(unique(constellation(:,2)))); + text(ax, constellation(:,1), constellation(:,2) + 0.18*y_step, bit_labels, ... + "HorizontalAlignment", "center", "FontName", "Consolas"); + + xlabel(ax, "Symbol 1"); + ylabel(ax, "Symbol 2"); + title(ax, sprintf("PAM-%d Mapping", obj.M)); + axis(ax, "equal"); + xticks(ax, unique(constellation(:,1))); + yticks(ax, unique(constellation(:,2))); + xlim(ax, [min(constellation(:,1)) - 0.8*x_step, max(constellation(:,1)) + 1.4*x_step]); + ylim(ax, [min(constellation(:,2)) - 0.6*y_step, max(constellation(:,2)) + 0.9*y_step]); + else + constellation = constellation(:); + mapping_table = table(constellation, bit_labels, ... + 'VariableNames', {'symbol', 'bits'}); + + fig = figure("Name", sprintf("PAM-%d Bit Mapping", obj.M)); + ax = axes(fig); + scatter(ax, constellation, zeros(size(constellation)), 60, "filled"); + hold(ax, "on"); + grid(ax, "on"); + box(ax, "on"); + text(ax, constellation, 0.08*ones(size(constellation)), bit_labels, ... + "HorizontalAlignment", "center", "FontName", "Consolas"); + + xlabel(ax, "Symbol"); + title(ax, sprintf("PAM-%d Mapping", obj.M)); + yticks(ax, []); + ylim(ax, [-0.2 0.2]); + xticks(ax, constellation); + if numel(constellation) > 1 + x_step = min(diff(unique(constellation))); + else + x_step = 1; + end + xlim(ax, [min(constellation) - 0.8*x_step, max(constellation) + 0.8*x_step]); + end + + if nargout == 0 + disp(mapping_table); + end + end + + function bitmap = showBitMapping(obj) + [~, bitmap] = obj.getConstellationBitMapping(); + + end + + function [Gp, details] = graypenalty(obj) + [constellation, bit_labels] = obj.getConstellationBitMapping(); + + num_points = size(constellation, 1); + dist2 = inf(num_points); + + for idx = 1:num_points + delta = constellation - constellation(idx, :); + dist2(idx, :) = sum(delta.^2, 2).'; + end + + dist2(1:num_points+1:end) = inf; + min_dist2 = min(dist2(:)); + is_neighbor = abs(dist2 - min_dist2) < 1e-12; + + hamming_dist = zeros(num_points); + for idx = 1:num_points + hamming_dist(idx, :) = sum(bit_labels ~= bit_labels(idx, :), 2).'; + end + + Gp = sum(hamming_dist(is_neighbor)) / nnz(is_neighbor); + + if nargout > 1 + details = struct( ... + "constellation", constellation, ... + "bit_labels", bit_labels, ... + "neighbor_mask", is_neighbor, ... + "nearest_distance", sqrt(min_dist2)); + end + + end + + function [constellation, bitmap] = getConstellationBitMapping(obj) + + if obj.M == 6 + constellation = obj.thresholds ./ obj.scaling; + bitmap = obj.demap(reshape(constellation.', [], 1)); + bitmap = reshape(bitmap, 5, []).'; + else + constellation = (obj.levels ./ obj.scaling)'; + bitmap = obj.demap(constellation); + end end diff --git a/Functions/EQ_structures/dsp_scope_signal.m b/Functions/EQ_structures/dsp_scope_signal.m index dbe91df..0a6520a 100644 --- a/Functions/EQ_structures/dsp_scope_signal.m +++ b/Functions/EQ_structures/dsp_scope_signal.m @@ -97,7 +97,7 @@ function output = dsp_scope_signal(Scpe_sig_raw, Symbols, Tx_bits, options) ffe_results = ffe(eq_dfe, M, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", duob_mode, ... - 'showAnalysis', showAnalysis, ... + 'showAnalysis', options.debug_plots, ... "postFFE", [], ... "eth_style_symbol_mapping", 0); @@ -111,7 +111,7 @@ function output = dsp_scope_signal(Scpe_sig_raw, Symbols, Tx_bits, options) dfe_results = ffe(eq_dfe, M, Scpe_sig, Symbols, Tx_bits, ... "precode_mode", duob_mode, ... - 'showAnalysis', showAnalysis, ... + 'showAnalysis', options.debug_plots, ... "postFFE", [], ... "eth_style_symbol_mapping", 0); diff --git a/Functions/EQ_structures/ffe.m b/Functions/EQ_structures/ffe.m index c7e7c52..05acd96 100644 --- a/Functions/EQ_structures/ffe.m +++ b/Functions/EQ_structures/ffe.m @@ -180,6 +180,8 @@ function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, 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'); % @@ -188,6 +190,6 @@ function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M, % 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); + % eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105); end \ No newline at end of file diff --git a/Functions/EQ_visuals/show2Dconstellation.m b/Functions/EQ_visuals/show2Dconstellation.m index 8596676..1170d66 100644 --- a/Functions/EQ_visuals/show2Dconstellation.m +++ b/Functions/EQ_visuals/show2Dconstellation.m @@ -30,7 +30,7 @@ end rx_symbols = eq_signal; %./ rms(eq_signal); correct_symbols = ref_symbols; -col = cbrewer2('Paired',numel(unique(correct_symbols))*2); +col = cbrewer2('Paired',numel(unique(correct_symbols))*3); ccnt = -1; levels = unique(correct_symbols); diff --git a/Theory/Mapping_Coding/Gray_penalty.m b/Theory/Mapping_Coding/Gray_penalty.m new file mode 100644 index 0000000..68d8b89 --- /dev/null +++ b/Theory/Mapping_Coding/Gray_penalty.m @@ -0,0 +1,11 @@ +function [Gp, details] = Gray_penalty(use_eth_mapping) +%GRAY_PENALTY Average bit errors per nearest-neighbor PAM-6 symbol error. + +if nargin < 1 + use_eth_mapping = 1; +end + +mapper = PAMmapper(6, 0, "eth_style", use_eth_mapping); +mapper.plotBitMapping(); +[Gp, details] = mapper.graypenalty(); +end diff --git a/projects/IMDD_base_system/imdd_it.m b/projects/IMDD_base_system/imdd_it.m index ddce315..4195ee4 100644 --- a/projects/IMDD_base_system/imdd_it.m +++ b/projects/IMDD_base_system/imdd_it.m @@ -17,30 +17,42 @@ if 1 uloops = struct; uloops.precomp = [0]; - uloops.bitrate = [300].*1e9; %[300,330,360,390,420,450,480] [224,336,360,390,420,448] for MPI + uloops.fsym = 180e9; + % uloops.bitrate = 100e9; % uloops.laser_wavelength = [1293,1297.5,1302,1306.5,1310,1313.4,1318,1322.7,1327.4]; - uloops.laser_wavelength = [1293]; - uloops.M = [4]; + uloops.laser_wavelength = [1310]; + uloops.M = [6]; uloops.link_length = 1; % uloops.link_length = [0:2:10]; % 1,2,3,5,6,8,10 - uloops.channel_alpha = [0.5]; + uloops.channel_alpha = [0.8]; uloops.duob_mode = db_mode.no_db; uloops.decoding_mode = "memoryless"; - uloops.channel_mode = "awgn_alphad"; - uloops.channel_snr_dB = [20:-2:0]; + uloops.channel_mode = "awgn_alphad"; %awgn_alphad + uloops.channel_snr_dB = [20]; + uloops.rop = -6; wh = DataStorage(uloops); wh.addStorage("ber"); - wh = submit_handle(@imdd_model,wh,"parallel",1); + wh = submit_handle(@imdd_model,wh,"parallel",0); end %% figure hold on +if isfield(uloops, 'fsym') && isfield(uloops, 'bitrate') + error('Define only one of uloops.fsym or uloops.bitrate.'); +elseif isfield(uloops, 'fsym') + rateLookup = uloops.fsym; +elseif isfield(uloops, 'bitrate') + rateLookup = uloops.bitrate; +else + error('Define either uloops.fsym or uloops.bitrate.'); +end + for alpha = uloops.channel_alpha - a=wh.getStoValue('ber',0, [300].*1e9 , 1293, 4, uloops.link_length,alpha,uloops.duob_mode,uloops.decoding_mode,uloops.channel_mode,uloops.channel_snr_dB); + a=wh.getStoValue('ber',0, rateLookup, uloops.laser_wavelength, uloops.M, uloops.link_length,alpha,uloops.duob_mode,uloops.decoding_mode,uloops.channel_mode,uloops.channel_snr_dB); gmi_ffe = cellfun(@(x) x.ffe_results.metrics.GMI, a); alpha_ffe = cellfun(@(x) x.ffe_results.metrics.Alpha, a); diff --git a/projects/IMDD_base_system/imdd_model.m b/projects/IMDD_base_system/imdd_model.m index eb55538..7c3f4ea 100644 --- a/projects/IMDD_base_system/imdd_model.m +++ b/projects/IMDD_base_system/imdd_model.m @@ -12,11 +12,12 @@ end %%% Run parameters % TX M = 4; -fsym = 180e9; +fsym = 150e9; +bitrate = []; -apply_pulsef = 1; -fdac = 256e9; -fadc = 256e9; +apply_pulsef = 0; +fdac = 4*fsym;%256e9; +fadc = 4*fsym;%256e9; random_key = 1; rcalpha = 0.05; @@ -28,7 +29,7 @@ vbias = -vbias_rel*u_pi; laser_wavelength = 1293; laser_linewidth = 0; -tx_bw_nyquist = 0.8; +tx_bw_nyquist = 0.65; % Channel link_length = 1; @@ -38,7 +39,7 @@ channel_alpha = 0; channel_delay_symbols = 1; % RX -rop = -8; +rop = 2; rx_bw_nyquist = 0.8; vnle_order1 = 50; @@ -68,7 +69,7 @@ dfe_ = sum(dfe_order)>0; duob_mode = db_mode.no_db; decoding_mode = []; -bitrate = fsym * log2(M); +providedFields = string.empty; %%% change specific parameter if given in varargin % Parse optional input arguments @@ -76,28 +77,38 @@ if ~isempty(varargin) var_s = varargin{1}; if isstruct(var_s) fields = fieldnames(var_s); + providedFields = string(fields); for i = 1:numel(fields) - if isnumeric(fields{i}) - eval([fields{i}, ' = ', num2str( var_s.(fields{i}) ), ';']); - fprintf("%s <-- %.2f \n", fields{i}, var_s.(fields{i})); - else - eval([fields{i}, ' = ', 'var_s.(fields{',num2str(i),'})' , ';']); + fieldName = fields{i}; + if ~isvarname(fieldName) + error('Invalid parameter name "%s".', fieldName); end - + eval([fieldName, ' = var_s.(fieldName);']); end else error('Optional variables should be passed as a struct.'); end end +hasFsym = any(providedFields == "fsym"); +hasBitrate = any(providedFields == "bitrate"); -fsym_ = floor( bitrate*1e-9./log2(M) ).*1e9; - -if fsym_ ~= fsym - fsym = fsym_; - % fprintf('Adapted symbolrate to %d GBd, to match provided bitrate of %d GBit/s using PAM %d \n',fsym.*1e-9,bitrate.*1e-9, M); +if M == 6 + bitsPerSymbol = 2.5; +else + bitsPerSymbol = log2(M); + if abs(bitsPerSymbol - round(bitsPerSymbol)) > 1e-12 + error('Unsupported PAM order M=%d. Only powers of two and PAM-6 are supported.', M); + end end +if hasFsym && hasBitrate + error('either bitrate or symbolrate!') +elseif hasBitrate + fsym = bitrate / bitsPerSymbol; +else + bitrate = fsym * bitsPerSymbol; +end f_nyquist = fsym/2; @@ -114,7 +125,6 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1 Digi_sig.spectrum("displayname",'Digi Spectrum','fignum',10,'normalizeTo0dB',1); - channel_mode = lower(string(channel_mode)); switch channel_mode @@ -122,7 +132,7 @@ switch channel_mode %%%%% AWG % El_sig = M8199A("kover",kover).process(Digi_sig); - El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); + El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",1,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",0).process(Digi_sig); % El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); % El_sig = El_sig.setPower(0,"dBm"); @@ -199,7 +209,6 @@ dspParameters.use_vnle_mlse = 1; dspParameters.use_dbtgt = 0; dspParameters.use_dbenc = 0; dspParameters.use_ml_mlse = 0; -dspParameters.showAnalysis = 0; dspParameters.pf_ncoeffs = pf_ncoeffs; dspParameters.ffe_order_ffe = [50, 0, 0]; dspParameters.ffe_order_dfe = [50, 0, 0]; @@ -217,7 +226,7 @@ scopeOutput = dsp_scope_signal(Rx_sig, Symbols, Tx_bits, ... "parameters", dspParameters, ... "preprocess_mode", "auto", ... "tx_pulseformer", txPulseformer, ... - "debug_plots", false); + "debug_plots", true); output = struct(); output.ffe_results = scopeOutput.ffe_package; @@ -230,3 +239,4 @@ output.mlmlse_results = scopeOutput.mlmlse_package; disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') fprintf('\n') +end