function ax = plotRgaData(tbl, opts) %PLOTRGADATA Plot RGA partial pressures vs time (companion to plotCryoData). % % ax = plotRgaData(tbl) % ax = plotRgaData(tbl, TimeRange=[t1 t2], Masses=["m28" "m18"]) % % Plots every channel column of an rgaImportData table against its shared % datetime axis. If Masses is given, a second figure shows a stackedplot % of those ions over the same time window. Returns a struct with the % axes/chart handles. % % Name-value arguments (type "plotRgaData(tbl, " and tab for suggestions): % TimeRange (1x2 datetime) subset window [start end]. Default: full range. % Masses (string) mass labels for the stackedplot, with or without % the leading "m": "m28", "28", ["m28" "m18"]. Default: none, % i.e. the stackedplot is skipped unless masses are requested. % % By default the y-axis of the first plot is linear (RGA scans contain % small near-noise negatives that a log axis cannot show). To switch to % the classic semilog view afterwards: % set(ax.partial, 'YScale', 'log'); % (values <= 0 are simply not drawn on a log axis). % % EXAMPLE % tbl = rgaImportData('rga1_Jul_01_2026_08-23-06_AM.txt'); % ax = plotRgaData(tbl); % overlay only % ax = plotRgaData(tbl, TimeRange=[t1 t2]); % zoom to a window % ax = plotRgaData(tbl, Masses=["m28" "m18" "m44"]); % overlay + ion stack % % See also RGAIMPORTDATA, PLOTCRYODATA, STACKEDPLOT arguments tbl table opts.TimeRange (1,2) datetime = [NaT NaT] opts.Masses (1,:) string = string.empty end %--- Extract datetime column --- dt = tbl.datetime; %--- If a real time range was given, subset the table (shared by both plots) --- if ~any(isnat(opts.TimeRange)) mask = dt >= opts.TimeRange(1) & dt <= opts.TimeRange(2); tbl = tbl(mask, :); dt = dt(mask); end %--- Channel columns = everything except datetime & time_s --- chVars = setdiff(tbl.Properties.VariableNames, {'datetime','time_s'}, 'stable'); units = 'a.u.'; if isfield(tbl.Properties.UserData, 'units') units = tbl.Properties.UserData.units; end %--- Plot 1: all partial pressures overlaid --- figure; tl = tiledlayout(1, 1); tl.TileSpacing = 'compact'; tl.Padding = 'compact'; ax.partial = nexttile; hold on; for k = 1:numel(chVars) plot(dt, tbl.(chVars{k}), '-', 'LineWidth', 1, ... 'DisplayName', prettyName(chVars{k})); end grid on; ylabel(sprintf('Partial pressure [%s]', units)); xlabel('Date & Time'); title('RGA partial pressures'); legend('Location', 'best', 'Interpreter', 'none'); % Match plotCryoData's datetime tick style ax.partial.XAxis.TickLabelFormat = 'dd-MMM HH:mm'; %--- Plot 2: selected ions as a stackedplot (only if masses requested) --- ax.stacked = []; if ~isempty(opts.Masses) sel = selectIons(chVars, opts.Masses); if isempty(sel) warning('plotRgaData:noIonMatch', ... 'No channels matched the requested mass label(s): %s', ... strjoin(cellstr(opts.Masses), ', ')); else figure; ax.stacked = stackedplot(tbl, sel, 'XVariable', 'datetime'); ax.stacked.Title = sprintf('Selected ions [%s]', units); ax.stacked.XLabel = 'Date & Time'; ax.stacked.DisplayLabels = cellfun(@prettyName, sel, 'UniformOutput', false); ax.stacked.GridVisible = 'on'; end end end % ------------------------------------------------------------------------- function sel = selectIons(chVars, masses) % Return the channel names whose mass label matches any entry in masses. % masses may be given with or without the leading "m" ("m28", "28"). keep = false(1, numel(chVars)); for j = 1:numel(masses) lbl = masses(j); if ~startsWith(lbl, "m") lbl = "m" + lbl; end keep = keep | endsWith(string(chVars), "_" + lbl); end sel = chVars(keep); end % ------------------------------------------------------------------------- function s = prettyName(v) % "Water_m18" -> "Water (m18)" s = regexprep(v, '_m(\d+)$', ' (m$1)'); s = strrep(s, '_', ' '); end