function spur = generate_spur(SpurParams, t, baseSignal) %GENERATE_SPUR create spur signals based on a parameter structure % spur = GENERATE_SPUR(SpurParams, t, baseSignal) % t : time vector (row or column) % baseSignal : (optional) complex baseband signal used for some spur % mechanisms such as mixer nonlinearity. If omitted the % function will only generate independent tones. % % SpurParams is a struct that may contain any of the following fields % .lo : struct with fields amp, freq, phase (optional) - % LO-related spur added to phase of signal % .mixer : struct with fields alpha2, alpha3 - coefficients for % 2nd/3rd order nonlinearity. Requires baseSignal input. % .adc : struct with fields amp, freq, phase - tone added after % ADC (complex). Does not require baseSignal. % .switch : struct with fields amp, freq - square/pulse spur % .pulse : struct with fields amp, freq, duty (0–1), phase - % periodic gating/pulse train (lock) added to output % % Example: % SpurParams = struct(); % SpurParams.lo = struct('amp',0.01,'freq',1e6,'phase',0); % SpurParams.mixer= struct('alpha2',1e-4,'alpha3',1e-6); % SpurParams.adc = struct('amp',1e-3,'freq',2e6,'phase',0); % SpurParams.switch = struct('amp',5e-4,'freq',5e5); % SpurParams.pulse = struct('amp',0.02,'freq',500e3,'duty',0.1,'phase',0); % 위 설정은 LO 스퍼, 믹서 비선형, ADC 톤, 스위칭 % 잡음에 더해 10% duty pulse train 락킹까지 포함합니다. % % The returned spur vector has the same dimensions as t. If baseSignal % is supplied the mixer nonlinearity is computed element‑wise using that % signal; otherwise only independent spur terms are returned. if nargin < 3 baseSignal = []; end % ensure t is row for consistent operations t = t(:)'; spur = zeros(size(t)); if isfield(SpurParams, 'lo') p = SpurParams.lo; phase = 0; if isfield(p, 'phase'); phase = p.phase; end spur = spur + p.amp .* sin(2*pi*p.freq .* t + phase); end if isfield(SpurParams, 'mixer') && ~isempty(baseSignal) m = SpurParams.mixer; % apply polynomial nonlinearity to the provided base signal if isfield(m, 'alpha2') spur = spur + m.alpha2 .* (baseSignal.^2); end if isfield(m, 'alpha3') spur = spur + m.alpha3 .* (baseSignal.^3); end end if isfield(SpurParams, 'adc') a = SpurParams.adc; phase = 0; if isfield(a, 'phase'); phase = a.phase; end spur = spur + a.amp .* exp(1j*(2*pi*a.freq .* t + phase)); end if isfield(SpurParams, 'switch') s = SpurParams.switch; spur = spur + s.amp .* square(2*pi*s.freq .* t); end if isfield(SpurParams, 'pulse') p = SpurParams.pulse; % duty default 50% duty = 0.5; if isfield(p,'duty'); duty = p.duty; end phase = 0; if isfield(p,'phase'); phase = p.phase; end % generate pulse train (0/1) scaled by amp % square with duty cycle multiplied then shifted to [0,1] pulse_wave = (square(2*pi*p.freq .* t + phase, duty*100)+1)/2; spur = spur + p.amp .* pulse_wave; end end