- demystifyied dataset - matched filter - timing sync (whole sig, not symbol based) - EQ is worse, I guess due to symbol timing recovery Pulsef: - can be used as matched filter now, the only thing I really changed was the sampling behavior - before it assumed that the input is always fsym, now it can be anything... fdac is output frequency for both methods
237 lines
7.5 KiB
Matlab
237 lines
7.5 KiB
Matlab
classdef Pulseformer
|
|
%Pulseformer Summary of this class goes here
|
|
% Detailed explanation goes here
|
|
|
|
properties(Access=public)
|
|
|
|
end
|
|
properties(Access=public)
|
|
fdac
|
|
fsym
|
|
pulse
|
|
pulselength
|
|
alpha
|
|
matched
|
|
end
|
|
|
|
methods (Access=public)
|
|
function obj = Pulseformer(options)
|
|
%NAME Construct an instance of this class
|
|
% Detailed explanation goes here
|
|
|
|
arguments
|
|
options.fdac double
|
|
options.fsym double
|
|
options.pulse pulseform = pulseform.rc
|
|
options.pulselength double {mustBeInteger} = 32
|
|
options.alpha double = 0.05
|
|
options.matched = 0;
|
|
end
|
|
|
|
%
|
|
fn = fieldnames(options);
|
|
for n = 1:numel(fn)
|
|
try
|
|
obj.(fn{n}) = options.(fn{n});
|
|
end
|
|
end
|
|
|
|
% do more stuff
|
|
|
|
end
|
|
|
|
function signalclass_out = process(obj,signalclass_in)
|
|
|
|
% actual processing of the signal (steps 1. - 3.)
|
|
signalclass_in.signal = obj.process_(signalclass_in);
|
|
|
|
% append to logbook
|
|
lbdesc = 'Applied Pulseshaping';
|
|
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
|
|
|
% write fs to signal
|
|
signalclass_in.fs = obj.fdac;%.* (obj.fdac./obj.fsym);
|
|
|
|
% write to output
|
|
signalclass_out = signalclass_in;
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
methods (Access=private)
|
|
% Cant be seen from outside! So put all your functions here that can/
|
|
% shall not be called from outside
|
|
|
|
function data_out = process_(obj, data_in_signal)
|
|
% Extract the incoming sampling rate
|
|
% Safety check: If fs is missing (e.g. raw symbols), assume it is fsym
|
|
if isprop(data_in_signal, 'fs') && ~isempty(data_in_signal.fs)
|
|
f_in = data_in_signal.fs;
|
|
else
|
|
f_in = obj.fsym;
|
|
end
|
|
|
|
f_out = obj.fdac;
|
|
|
|
% 1. Calculate Resampling Factors (P and Q)
|
|
% We need rational approximation: f_out/f_in = p/q
|
|
[p, q] = rat(f_out / f_in);
|
|
|
|
% 2. Calculate SPS for the Filter Design
|
|
% The filter operates at the INTERMEDIATE rate (f_in * p).
|
|
% We need to know how many samples represent one symbol AT THAT RATE.
|
|
fs_intermediate = f_in * p;
|
|
sps_filter = fs_intermediate / obj.fsym;
|
|
|
|
% 3. Filter Design
|
|
if obj.pulse == pulseform.rc
|
|
filtertype = 'normal';
|
|
elseif obj.pulse == pulseform.rrc % assuming enum logic holds
|
|
filtertype = 'sqrt';
|
|
end
|
|
|
|
% Standard RRC Design
|
|
% Note: rcosdesign sps must be integer? Usually yes, but for polyphase it can handle it.
|
|
% If sps_filter is not integer, rcosdesign might complain.
|
|
% For your setup (powers of 2), it will likely be integer.
|
|
racos_len = obj.pulselength; % span in symbols
|
|
h = rcosdesign(obj.alpha, racos_len, sps_filter, filtertype);
|
|
|
|
% Matched Filter Flip (Complex Conjugate Time Reversal)
|
|
if obj.matched
|
|
h = conj(fliplr(h));
|
|
end
|
|
|
|
% 4. Processing
|
|
% Apply upfirdn using the calculated P and Q
|
|
data_out_ = upfirdn(data_in_signal.signal, h, p, q);
|
|
|
|
% 5. Trim Tail (Group Delay Correction)
|
|
% The delay of linear phase filter is (N-1)/2 samples @ intermediate rate
|
|
delay_samples_intermediate = (length(h) - 1) / 2;
|
|
|
|
% Convert delay to output samples
|
|
delay_samples_out = delay_samples_intermediate / q;
|
|
|
|
% We usually want to trim the "start" transient
|
|
st = floor(delay_samples_out) + 1;
|
|
|
|
% Calculate expected output length
|
|
len_out = ceil(length(data_in_signal.signal) * p / q);
|
|
|
|
% Cut
|
|
data_out = data_out_(st : st + len_out - 1);
|
|
end
|
|
|
|
%
|
|
% function data_out = process_(obj,data_in)
|
|
% %METHOD1 Summary of this method goes here
|
|
% % Detailed explanation goes here
|
|
% arguments(Input)
|
|
% obj
|
|
% data_in
|
|
% end
|
|
%
|
|
% arguments(Output)
|
|
% data_out
|
|
% end
|
|
%
|
|
% if ~rem(obj.fdac,obj.fsym)
|
|
% %ist ein Vielfaches
|
|
% sps = obj.fdac / obj.fsym;
|
|
% p = sps;
|
|
% q = 1;
|
|
% else
|
|
% %ist kein Vielfaches
|
|
% p = obj.fsym / gcd(obj.fdac, obj.fsym); %upsampling p->->->
|
|
% q = obj.fdac/ gcd(obj.fdac, obj.fsym); %downsampling <-q
|
|
% sps= q; %sps während dem pulse shaping
|
|
% end
|
|
%
|
|
% if obj.pulse == pulseform.rc
|
|
% filtertype = 'normal';
|
|
% elseif pulseform.rrc
|
|
% filtertype = 'sqrt';
|
|
% end
|
|
%
|
|
% %Bau das Filter (hier rc)
|
|
% racos_len = obj.pulselength*2;
|
|
% h = rcosdesign(obj.alpha,racos_len,sps,filtertype);
|
|
% % h = h./ max(h);
|
|
%
|
|
% if obj.matched
|
|
% h = conj(fliplr(h));
|
|
% end
|
|
%
|
|
% manual_cyclic_convolution = 0;
|
|
% upfirdn_convolution = 1;
|
|
%
|
|
% if manual_cyclic_convolution
|
|
%
|
|
% % Apply filter the long way (from move_it)
|
|
% data_in = data_in';
|
|
% blen = length(data_in)*sps;
|
|
%
|
|
% % oversample symbol sequence
|
|
% symbolov=zeros(size(data_in,1),blen);
|
|
% symbolov(:,1:sps:blen-sps+1)=data_in;
|
|
% H=fft(h,blen);
|
|
%
|
|
% % Convolution of Bit sequence with impulse response
|
|
% data_out=ifft( fft(symbolov.') .* repmat( H,size(data_in,1),1 ).' ).';
|
|
% data_out = circshift(data_out,[0 -(obj.pulselength*sps)]);
|
|
%
|
|
% if rem(obj.fdac,obj.fsym)
|
|
% data_out = data_out(1:q:end);
|
|
% end
|
|
%
|
|
% end
|
|
%
|
|
% if upfirdn_convolution
|
|
%
|
|
% %Apply Filter using Matlab build in fctn.
|
|
%
|
|
% data_out_ = upfirdn(data_in,h,p,q);
|
|
%
|
|
% %cut signal, which is longer due to fir filter
|
|
% st = round(p/q*racos_len/2); %we need to cut y_out
|
|
% en = round(st + (length(data_in)*p/q));
|
|
% data_out = data_out_(st:en);
|
|
%
|
|
% end
|
|
%
|
|
% if upfirdn_convolution && manual_cyclic_convolution
|
|
% figure()
|
|
% subplot(2,1,1)
|
|
% title("Convolution vs. Upfirdn and Cut")
|
|
% hold on
|
|
% % plot(data_out_(1:200),'DisplayName','Matlab upfirdn');
|
|
% plot(data_out(1:200),'DisplayName','By Hand cyclic convolution')
|
|
% subplot(2,1,2)
|
|
% hold on
|
|
% plot(data_out(1:2000),'DisplayName','OUT');
|
|
% plot(data_in(1:2000),'DisplayName','IN');
|
|
% end
|
|
%
|
|
% %scaling?! see pulsef module line 696
|
|
% % scale = max(max([abs(real(data_out)) abs(imag(data_out))])); %find max value from real and imag part
|
|
% % data_out = data_out./scale;
|
|
%
|
|
% % data_out = data_out';
|
|
%
|
|
% %Check output integrity
|
|
% if abs(round(p/q * length(data_in)) - length(data_out)) > 4
|
|
% warning('Check signal length after pulse shaping');
|
|
% %disp('Check signal length after pulse shaping');
|
|
% end
|
|
%
|
|
%
|
|
% end
|
|
|
|
|
|
end
|
|
end
|