classdef Timing_Recovery_GPT < handle properties(Access=public) sps muGrid end methods(Access=public) function obj = Timing_Recovery_GPT(options) arguments(Input) options.sps = 2; options.muGrid = 0; end fn = fieldnames(options); for n = 1:numel(fn) obj.(fn{n}) = options.(fn{n}); end %obj-Initialization here% end function [data_out, mu_best, score] = process(obj, data_in) %MAXVARTIMINGSYNC Choose sampling phase mu that maximizes variance of downsampled symbols. % % data_in : matched-filtered samples (complex or real), length N % sps : samples per symbol (here typically 2) % muGrid : candidate fractional offsets in [0,1) % % data_out : symbol-rate samples (length floor(N/sps)) % mu_best: chosen fractional offset % score : variance score for each mu in muGrid data_out = data_in; x = data_in.signal(:); N = length(x); Ns = floor(N/obj.sps); if nargin < 3 || isempty(obj.muGrid) obj.muGrid = linspace(0, 0.99, 101); % 0..0.99 in ~0.01 steps end % Symbol indices (1-based sample positions) n0 = 1; % start sample index k = (0:Ns-1).'; tBase = n0 + k*obj.sps; % integer times (1, 1+sps, ...) score = zeros(numel(obj.muGrid),1); for m = 1:numel(obj.muGrid) mu = obj.muGrid(m); t = tBase + mu; % Linear fractional sampling y = interp1(1:N, x, t, 'linear', 'extrap'); % For PAM, maximize variance of real part (or abs if you prefer) yr = real(y); score(m) = var(yr, 1); % use population variance (normalization doesn't matter for argmax) end % Pick best mu [~, idx] = max(score); mu_best = obj.muGrid(idx); % Resample with best mu t = tBase + mu_best; data_out.signal = interp1(1:N, x, t, 'linear', 'extrap'); end end end