FSO data:

- 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
This commit is contained in:
silas (home)
2026-01-08 13:20:35 +01:00
parent 5c39a7f98a
commit 062c5dfbaf
6 changed files with 313 additions and 117 deletions

View File

@@ -43,7 +43,7 @@ classdef Pulseformer
function signalclass_out = process(obj,signalclass_in)
% actual processing of the signal (steps 1. - 3.)
signalclass_in.signal = obj.process_(signalclass_in.signal);
signalclass_in.signal = obj.process_(signalclass_in);
% append to logbook
lbdesc = 'Applied Pulseshaping';
@@ -65,109 +65,171 @@ classdef Pulseformer
% 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)
%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)]);
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
if rem(obj.fdac,obj.fsym)
data_out = data_out(1:q:end);
end
f_out = obj.fdac;
end
% 1. Calculate Resampling Factors (P and Q)
% We need rational approximation: f_out/f_in = p/q
[p, q] = rat(f_out / f_in);
if upfirdn_convolution
% 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;
%Apply Filter using Matlab build in fctn.
% 3. Filter Design
if obj.pulse == pulseform.rc
filtertype = 'normal';
elseif obj.pulse == pulseform.rrc % assuming enum logic holds
filtertype = 'sqrt';
end
data_out_ = upfirdn(data_in,h,p,q);
% 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);
%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);
% Matched Filter Flip (Complex Conjugate Time Reversal)
if obj.matched
h = conj(fliplr(h));
end
end
% 4. Processing
% Apply upfirdn using the calculated P and Q
data_out_ = upfirdn(data_in_signal.signal, h, p, q);
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
% 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
%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
%
% 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