function Scpe_sig = preprocessSignal(Scpe_sig, Symbols, fsym, options) % PREPROCESSSIGNAL Performs standard preprocessing on a signal % % Inputs: % Scpe_sig - Input signal % Symbols - Reference symbols for synchronization % fsym - Symbol frequency % % Outputs: % Scpe_sig - Preprocessed signal arguments Scpe_sig Symbols fsym options.mode string = "auto" options.tx_pulseformer = [] options.debug_plots (1,1) logical = false options.apply_gaussian_filter (1,1) logical = true options.gaussian_cutoff_factor (1,1) double = 0.52 end preprocessMode = resolvePreprocessMode(options.mode, options.tx_pulseformer, Symbols); targetFs = 2 * fsym; switch preprocessMode case "matched_filter" matchedPulseformer = buildMatchedPulseformer(options.tx_pulseformer, Symbols, fsym, targetFs); Scpe_sig = matchedPulseformer.process(Scpe_sig); case "resample" Scpe_sig = Scpe_sig.resample("fs_out", targetFs); otherwise error('preprocessSignal:InvalidMode', 'Unsupported preprocessing mode "%s".', preprocessMode); end [Scpe_sig, Scpe_cell] = Scpe_sig.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", options.debug_plots); % Scpe_sig = Scpe_cell{1}; % Apply Gaussian filter if options.apply_gaussian_filter Scpe_sig = Filter('filtdegree', 8, "f_cutoff", Symbols.fs.*options.gaussian_cutoff_factor, ... "fs", Scpe_sig.fs, "filterType", filtertypes.gaussian, ... "active", true).process(Scpe_sig); end %Remove DC offset Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); end function preprocessMode = resolvePreprocessMode(requestedMode, txPulseformer, Symbols) requestedMode = string(requestedMode); if requestedMode ~= "auto" preprocessMode = requestedMode; return end pulseformer = txPulseformer; if isempty(pulseformer) pulseformer = findSignalPulseformer(Symbols); end if isRootRaisedCosinePulseformer(pulseformer) preprocessMode = "matched_filter"; else preprocessMode = "resample"; end end function tf = isRootRaisedCosinePulseformer(pulseformer) tf = false; if isempty(pulseformer) return end pulseValue = readPulseformerField(pulseformer, 'pulse', []); if isempty(pulseValue) return end pulseValue = normalizePulseValue(pulseValue); tf = pulseValue == pulseform.rrc; end function matchedPulseformer = buildMatchedPulseformer(txPulseformer, Symbols, fsym, targetFs) pulseformerMeta = txPulseformer; if isempty(pulseformerMeta) pulseformerMeta = findSignalPulseformer(Symbols); end pulseValue = normalizePulseValue(readPulseformerField(pulseformerMeta, 'pulse', pulseform.rrc)); alphaValue = readPulseformerField(pulseformerMeta, 'alpha', 0.05); pulseLengthValue = readPulseformerField(pulseformerMeta, 'pulselength', 16); if isempty(alphaValue) alphaValue = 0.05; end matchedPulseformer = Pulseformer( ... "fsym", fsym, ... "fdac", targetFs, ... "pulse", pulseValue, ... "pulselength", pulseLengthValue, ... "alpha", alphaValue, ... "matched", 1); end function value = readPulseformerField(pulseformerMeta, fieldName, defaultValue) value = defaultValue; if isempty(pulseformerMeta) return end if isstruct(pulseformerMeta) && isfield(pulseformerMeta, fieldName) candidate = pulseformerMeta.(fieldName); if ~isempty(candidate) value = candidate; end return end if isobject(pulseformerMeta) && isprop(pulseformerMeta, fieldName) candidate = pulseformerMeta.(fieldName); if ~isempty(candidate) value = candidate; end end end function pulseValue = normalizePulseValue(rawValue) if isa(rawValue, 'pulseform') pulseValue = rawValue; return end if iscell(rawValue) rawValue = rawValue{1}; end if isstring(rawValue) || ischar(rawValue) pulseValue = pulseform.(char(string(rawValue))); return end pulseValue = pulseform(rawValue); end