function showLevelHistogram(eq_signal,ref_symbols,options) arguments eq_signal ref_symbols options.fignum (1,1) double = NaN % Default to NaN if not provided options.displayname (1,:) char = '' % Default to an empty string if not provided options.ref_symbol_uncoded = [] options.nbins (1,1) double = 1000 end if isa(eq_signal,'Signal') eq_signal = eq_signal.signal; end if isa(ref_symbols,'Signal') ref_symbols = ref_symbols.signal; end if isa(options.ref_symbol_uncoded,'Signal') options.ref_symbol_uncoded = options.ref_symbol_uncoded.signal; end eq_signal = eq_signal(:); ref_symbols = ref_symbols(:); ref_symbol_uncoded = options.ref_symbol_uncoded(:); assert(numel(eq_signal) == numel(ref_symbols), ... 'showLevelHistogram:LengthMismatch', ... 'eq_signal and ref_symbols must have the same number of samples.'); assert(isempty(ref_symbol_uncoded) || numel(ref_symbol_uncoded) == numel(eq_signal), ... 'showLevelHistogram:LengthMismatch', ... 'options.ref_symbol_uncoded must have the same number of samples as eq_signal.'); % 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 eq_signal = max(min(eq_signal,3),-3); eq_signal = eq_signal .* -1; %%% histogram clf if isempty(ref_symbol_uncoded) % Normal mode: split the received signal by the actually % transmitted reference level. constellation = unique(ref_symbols); received_sd = NaN(numel(constellation),numel(ref_symbols)); lvlcol = linspecer(numel(constellation)); lvlcol = cbrewer2('RdBu',numel(constellation)+4); % remove 4 colors from the middle to avoid the bright central colors mid = ceil(size(lvlcol,1)/2); rm_idx = mid + (-1:2); % two before mid and two after (2x2 removal centered) rm_idx = max(1,min(size(lvlcol,1),rm_idx)); lvlcol(rm_idx,:) = []; lvlcol = lvlcol(1:numel(constellation),:); for lvl = 1:numel(constellation) class_mask = ref_symbols == constellation(lvl); received_sd(lvl,class_mask) = eq_signal(class_mask); end for lvl = 1:numel(constellation) intermediate = received_sd(lvl,:); cnt(lvl) = round(numel(intermediate(~isnan(intermediate)))./numel(eq_signal),3).*100; hold on warning off histogram(received_sd(lvl,:),options.nbins, ... "EdgeAlpha",0, ... "DisplayName",['Lvl ',num2str(lvl),' ; ',num2str(cnt(lvl)),' '], ... "FaceColor",lvlcol(lvl,:), ... "Normalization","pdf"); warning on end else % Plot p(y | x_n): y is the noisy observation and x_n is the % uncoded PAM class. For duobinary this naturally gives multi-modal % PDFs because one x_n class can map to multiple DB amplitudes. Each % DB lobe is drawn separately so the DB-level structure stays visible. db_constellation = unique(ref_symbols); classes = unique(ref_symbol_uncoded); % lvlcol = linspecer(numel(classes)); lvlcol = cbrewer2('RdBu',numel(classes)+4); % remove 4 colors from the middle to avoid the bright central colors mid = ceil(size(lvlcol,1)/2); rm_idx = mid + (-1:2); % two before mid and two after (2x2 removal centered) rm_idx = max(1,min(size(lvlcol,1),rm_idx)); lvlcol(rm_idx,:) = []; lvlcol = lvlcol(1:numel(sir_group_labels),:); for db_lvl = 1:numel(db_constellation) db_mask = ref_symbols == db_constellation(db_lvl); mapped_class = mode(ref_symbol_uncoded(db_mask)); [~,class_idx] = min(abs(classes - mapped_class)); cnt = round(nnz(ref_symbol_uncoded == mapped_class)./numel(eq_signal),3).*100; if db_lvl == findFirstMappedDbLevel(ref_symbols,ref_symbol_uncoded,db_constellation,mapped_class) display_name = ['p(y|x_',num2str(class_idx),') ; ',num2str(cnt),' ']; display_name = ['p(y|x_',num2str(class_idx),')']; handle_visibility = "on"; else display_name = ['p(y|x_',num2str(class_idx),') ; ',num2str(cnt),' ']; display_name = ['p(y|x_',num2str(class_idx),')']; handle_visibility = "off"; end weighted_lobe = NaN(size(eq_signal)); weighted_lobe(db_mask) = eq_signal(db_mask); hold on warning off histogram(weighted_lobe,options.nbins, ... "EdgeAlpha",0, ... "DisplayName",display_name, ... "FaceColor",lvlcol(class_idx,:), ... "HandleVisibility",handle_visibility, ... "Normalization","pdf"); warning on end end xlim([-3 3]); legend grid on % view([90 -90]); end function first_db_lvl = findFirstMappedDbLevel(ref_symbols,ref_symbol_uncoded,db_constellation,mapped_class) first_db_lvl = NaN; for db_lvl = 1:numel(db_constellation) db_mask = ref_symbols == db_constellation(db_lvl); if mode(ref_symbol_uncoded(db_mask)) == mapped_class first_db_lvl = db_lvl; return end end end