Many changes for 400G DSP

Minimal Example
...
This commit is contained in:
sioe
2024-12-17 16:17:58 +01:00
parent 397cfa61dd
commit e47a4dbbbe
68 changed files with 2749 additions and 2948 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -349,13 +349,13 @@ classdef Signal
% spectrum_plot(obj.signal,options.fsamp,options.figurename,options.displayname);
N = 2^(nextpow2(length(obj.signal))-2);
N = 2^(nextpow2(length(obj.signal))-9);
if options.normalizeToNyquist==0
[p_lin,w] = pwelch(obj.signal,hanning(N),N/2,N,obj.fs,"centered","power","mean");
[p_lin,w] = pwelch(obj.signal,hanning(N),N/2,N,obj.fs,"centered","psd","mean");
w=w.*1e-9;
else
[p_lin,w] = pwelch(obj.signal,hanning(N),N/2,N,"centered","power","mean");
[p_lin,w] = pwelch(obj.signal,hanning(N),N/2,N,"centered","psd","mean");
% p_lin = smooth(p_lin,0.05,'rloess');
end
@@ -365,7 +365,7 @@ classdef Signal
p_dbm = 10*log10(p_lin); %dB to dBm in case of "power"
ylab = "normalized to 0 dB";
else
p_dbm = 10*log10(p_lin)+30; %dB to dBm in case of "power"
p_dbm = 10*log10(p_lin); %dB to dBm in case of "power"
ylab = "Power (dBm)";
end
@@ -401,6 +401,125 @@ classdef Signal
end
function move_it_spectrum(obj,options)
arguments
obj
options.fignum
options.displayname = "";
options.color = [];
options.normalizeToNyquist = 0;
options.normalizeTo0dB = 0;
end
data_in = obj.signal;
if size(data_in,1) > size(data_in,2)
data_in = data_in';
end
for pol = 1:size(data_in,1)
%compute FFT of input
Data_in = fft( data_in(pol,:) );
%psd = Data_in.*conj(Data_in);
psd = Data_in;
%Use only magnitude of FFT (which was complex)
psd = abs(psd);
%Shift the spectrum to yield
psd = fftshift(psd);
%divide by N
psd = psd/length(data_in(pol,:));
psd_plot = 20*log10(psd);
% psd_plot = psd_plot - max(psd_plot);
%smoothing
% psd_smoothed = smooth(psd,1000);
%
% psd_smoothed = 10*log10(psd_smoothed);
% psd_smoothed = psd_smoothed - max(psd_smoothed);
carrier_power_time_dbm = 20*log10( mean(abs(data_in)) .^2 )+30; % dB -> +30 -> dBm
carrier_power_freq_dbm = max(psd_plot);
% psd_plot = psd_plot - max(psd_plot);
%% cspr
c = mean(data_in).^2;
s = mean(data_in.^2);
cspr = 10*log10(c / s);
testParseval = 1;
if testParseval == 1
E_FreqDomain =1/length(psd) * sum((psd.*length(psd)).^2);
%test parseval
E_TimeDomain = sum( (data_in(pol,:).^2) );
if isequal(round(E_FreqDomain,1),round(E_TimeDomain,1))
disp('Parseval is right!');
else
% disp('Something is wrong here?!');
end
end
figure(options.fignum); % If figure does not exist, create new figure
if 1
%Frequency Axis
freq_vec = linspace(-obj.fs/2,obj.fs/2,length(psd));
freq_vec = reshape(freq_vec,size(psd_plot));
if nargin == 4
p = plot(freq_vec*1e-9,psd_plot,'Linewidth',0.5,'DisplayName',options.displayname);
% plot(freq_vec*1e-9,psd_smoothed','Linewidth',1,'Color',[0 0 0],'DisplayName',[char(varargin{2}),' smoothed']);
else
p = plot(freq_vec*1e-9,psd_plot,'Linewidth',0.5);
% plot(freq_vec*1e-9,psd_smoothed','Linewidth',1,'Color',[1 1 1],'LineStyle',':');
end
%xlim([freq_vec(1)/1e9-2 freq_vec(end)/1e9+2])
xlabel('frequency [GHz]')
else
%Wavelength Axis
freq_vec = physconst('LightSpeed')*linspace(-obj.fs/2,obj.fs/2,length(psd))./((physconst('LightSpeed')/1310e-9)^2)*1e9;
freq_vec = freq_vec+1310;
if nargin == 4
plot(freq_vec',psd_plot,'Linewidth',0.5,'DisplayName',options.displayname)
else
plot(freq_vec,psd_plot','Linewidth',0.5);
end
%xlim([freq_vec(1)/1e9-2 freq_vec(end)/1e9+2])
xlabel('wavelength [nm]')
end
hold on
end
% xlim([-150 150])
% ylim([-100,0]);
ylabel('magnitude [dBm]')
legend
grid minor;
end
%% Power of signal
function pow = power(obj,options)
@@ -544,7 +663,7 @@ classdef Signal
%Cut occurences of ref signal from signal (only positive shifts)
S = {};
for c = shifts(shifts>0)
for c = shifts(shifts>=0)
sig = obj.delay(-c,'mode','samples');
sig.signal = sig.signal(1:length(b));
S{end+1,1} = sig;
@@ -557,7 +676,7 @@ classdef Signal
end
%return/keep the sinal with the highest correlation (only within positive shifts)
[~,idx]=max(pks(shifts>0));
[~,idx]=max(pks(shifts>=0));
obj.signal = S{idx}.signal;
%put signal with highest corr. to first index in S array
swap = S{1};

View File

@@ -8,6 +8,7 @@ classdef AWG < handle
upsampling_method
repetitions %repeat the signal to generate a longer sequence?
fdac %needed
precomp_sinc_rolloff = 1;
normalize2dac %want to normalize at first? either 0 or 1
bit_resolution %bit res. of quantizer (e.g. 5 bit)
dac_min
@@ -33,7 +34,8 @@ classdef AWG < handle
arguments
options.kover = 16;
options.upsampling_method upsampling_mode
options.upsampling_method upsampling_mode = upsampling_mode.samplehold;
options.precomp_sinc_rolloff = 1;
options.repetitions = 1;
options.normalize2dac = 1;
options.fdac = 92e9;
@@ -43,7 +45,7 @@ classdef AWG < handle
options.skew_active = 0;
options.awg_skew = 0;
options.lpf_active = 0;
options.lpf_type = 0;
options.lpf_type = filtertypes.butterworth;
options.f_cutoff = 32e9;
options.H_lpf Filter
@@ -95,7 +97,7 @@ classdef AWG < handle
signalclass_in = obj.H_lpf.process(signalclass_in);
else
%4.B) just use a standard filter
lpf = Filter('filtdegree',5,"f_cutoff",obj.f_cutoff,"fsamp",obj.kover*obj.fdac,"filterType",obj.lpf_type);
lpf = Filter('filtdegree',5,"f_cutoff",obj.f_cutoff,"fs",obj.kover*obj.fdac,"filterType",obj.lpf_type);
signalclass_in = lpf.process(signalclass_in);
end
end
@@ -133,7 +135,7 @@ classdef AWG < handle
obj.signal_length = length(data_in);
%%%%%%%%% PRECOMP SINC ROLLOFF %%%%%%%%%
if 1
if obj.precomp_sinc_rolloff
% X: design FIR filter for sinc precomp
% https://www.dsprelated.com/showarticle/1191.php
ntaps = 13;

View File

@@ -151,6 +151,12 @@ classdef PAMsource
sym_max = max(symbols.signal);
end
% symbols.move_it_spectrum("fignum",222,"displayname","Symbols only");
% symbols.spectrum("fignum",222,"displayname","Symbols only","normalizeTo0dB",1);
%%%%% Pulse-forming %%%%%%
if obj.applypulseform
digi_sig = obj.pulseformer.process(symbols);

View File

@@ -1,10 +1,9 @@
classdef Duobinary
%Duobinary Coding
% should work
% should work
properties(Access=public)
end
methods (Access=public)
@@ -12,6 +11,7 @@ classdef Duobinary
function obj = Duobinary()
%NAME Construct an instance of this class
end
function signal = precode(~,signal)
@@ -52,7 +52,7 @@ classdef Duobinary
% end
for k = 2:numel(data)
bk(k) =mod(data(k)-bk(k-1),M);
bk(k) = mod(data(k)-bk(k-1),M);
end
%make bipolar
@@ -80,6 +80,11 @@ classdef Duobinary
function signal = encode(~,signal)
arguments
~
signal
end
if isa(signal,'Signal')
data = signal.signal;
else
@@ -138,6 +143,8 @@ classdef Duobinary
data = data ./ sqrt(5.8);
elseif M == 8
data = data ./ sqrt(10.5); % 15-level constellation weighted with probability after DB code i.e.
else
errormsg("Error in: Duobinary encode > scale unipolar to bipolar > The data is not PAM4, PAM6 or PAM 8? ")
end
if isa(signal,'Signal')
@@ -169,13 +176,9 @@ classdef Duobinary
if I == 7
data = data .* sqrt(2.5);
elseif I == 11
%todo
data = data .* sqrt(5.8);
warning('Check db decode implementation, mapping and scaling is correct!')
elseif I == 15
data = data .* sqrt(10.5);
elseif I == 16
warning('Check db decode implementation, mapping and scaling is correct!')
end
data = round(data);
@@ -196,6 +199,8 @@ classdef Duobinary
data = data ./ sqrt(10);
elseif M == 8
data = data ./ sqrt(21);
else
errormsg("Error in: Duobinary decode > scale unipolar to bipolar > The data is not PAM4, PAM6 or PAM 8? ")
end
signalclass.signal = data;

View File

@@ -45,14 +45,14 @@ classdef Postfilter < handle
function showFilter(obj,noiseclass_in)
noiseclass_in.spectrum('displayname','Noise PSD shifted to 0dBm','fignum',123,'normalizeTo0dB',1);
noiseclass_in.spectrum('displayname','Noise PSD shifted to 0dBm','fignum',121,'normalizeTo0dB',1);
[h,w] = freqz(1,obj.burg_coeff,length(noiseclass_in),"whole",noiseclass_in.fs);
h = h/max(abs(h));
hold on
w_ = (w - noiseclass_in.fs/2);
plot(w_.*1e-9,20*log10(fftshift(h)),'DisplayName',['Burg Coeffs: ', num2str(obj.burg_coeff), ' ']);
plot(w_.*1e-9,20*log10(fftshift(abs(h))),'DisplayName',['Burg Coeffs: ', num2str(obj.burg_coeff), ' ']);
ylim([-30,2]);
end
end

View File

@@ -159,7 +159,7 @@ classdef DBHandler < handle
duplictae_sync = obj.fetch("SELECT COALESCE(Runs.rx_sync_path,'NaN') AS rx_sync_path, COUNT(*) AS occurrences FROM Runs GROUP BY rx_sync_path HAVING COUNT(*) > 1");
if size(duplictae_raw,2) > 1
for i = 1:size(duplictae_raw,2)-1
for i = 1:size(duplictae_raw,1)
fprintf('Raw Rx Paths: Found %d duplictaes of %s \n',duplictae_raw.occurrences(i),duplictae_raw.rx_raw_path(i));
end
end
@@ -349,6 +349,11 @@ classdef DBHandler < handle
function executeSQL(obj, query)
% This method executes an SQL statement using MATLAB's execute function.
execute(obj.conn, query);
end
function answer = fetch(obj,query)
answer = fetch(obj.conn,query);

View File

@@ -114,6 +114,12 @@ classdef DataStorage < handle
end
function addValueToStorageByLinIdx(obj, valueToStore ,storageVarName, lin_idx)
obj.sto.(storageVarName){lin_idx} = valueToStore;
end
% Access Value(s)
function value = getStoValue(obj,storageVarName, varargin)
@@ -270,7 +276,7 @@ classdef DataStorage < handle
function phys_indices = getPhysIndicesByLinIndex(obj, lin_idx)
function [phys_indices,param_name] = getPhysIndicesByLinIndex(obj, lin_idx)
% Converts a linear index into the corresponding physical parameter values
% Inputs:
% - lin_idx: The linear index within the storage array
@@ -285,8 +291,8 @@ classdef DataStorage < handle
% Map subscripts to physical values for each parameter
for i = 1:numel(obj.fn)
param_name = obj.fn(i);
phys_indices{i} = obj.parameter.(param_name).getPhysForIndex(subscripts{i});
param_name{i} = obj.fn(i);
phys_indices{i} = obj.parameter.(param_name{i}).getPhysForIndex(subscripts{i});
end
end

View File

@@ -1,114 +0,0 @@
classdef DataStorage2 < handle
% DATASTORAGE: Stores data with physical parameter mappings
properties
inputParams = struct;
parameter = struct;
fn = [];
dim = [];
sto = struct;
end
methods
function obj = DataStorage2(inputParams)
% Constructor to initialize the DataStorage object
if nargin > 0
obj.inputParams = inputParams;
obj.fn = string(fieldnames(inputParams));
obj = obj.buildParameter();
obj.dim = obj.getDimension();
obj.sto = struct;
else
error('Input parameters are required.');
end
end
function showInfo(obj)
% Displays information about the storage and its dimensions
disp("Data Structure with fields:");
fprintf('%-12s | %-8s | %-12s\n', 'Name', 'Dimension', 'Physical Values');
disp('-------------------------------------------------------');
for i = 1:numel(obj.fn)
fprintf('%-12s | %-8d | %-12s\n', ...
char(obj.fn(i)), obj.dim(i), ...
strjoin(string(obj.parameter.(obj.fn(i)).values), ', '));
end
disp('-------------------------------------------------------');
end
function dim = getDimension(obj)
% Get the dimensions based on the length of parameters
dim = zeros(1, numel(obj.fn));
for p = 1:numel(obj.fn)
dim(p) = obj.parameter.(obj.fn(p)).length;
end
end
function obj = buildParameter(obj)
% Build the Parameter objects for each input parameter
for p = 1:numel(obj.fn)
name = obj.fn(p);
values = obj.inputParams.(name);
obj.parameter.(name) = Parameter2(name, values);
end
end
function addStorage(obj, varName)
% Create an empty storage for a specific variable name
obj.sto.(string(varName)) = cell(obj.dim);
end
function addValueToStorage(obj, valueToStore, storageVarName, varargin)
% Add a value to the storage at the specified indices
if nargin - 3 == numel(obj.fn)
lin_idx = obj.getIndicesByPhys(varargin);
obj.sto.(storageVarName){lin_idx} = valueToStore;
else
error('Please provide all indices for the storage.');
end
end
function value = getStoValue(obj, storageVarName, varargin)
% Retrieve a value from storage based on physical parameters
if nargin - 2 == numel(obj.fn)
lin_idx = obj.getIndicesByPhys(varargin);
value = cell(1, numel(lin_idx));
for i = 1:numel(lin_idx)
value{i} = obj.sto.(storageVarName){lin_idx(i)};
end
value = value(~cellfun('isempty', value)); % Remove empty entries
else
error('Please provide all physical parameters.');
end
end
function lin_idx = getIndicesByPhys(obj, varargin)
% Unpack nested cell array if needed
if numel(varargin) == 1 && iscell(varargin{1})
varargin = varargin{1}; % Unpack if single cell array is passed
end
indices = cell(1, numel(obj.fn));
% Loop through each parameter (e.g., L, D)
for p = 1:numel(obj.fn)
% Unwrap if it's a cell
if iscell(varargin{p})
physVal = varargin{p}{1}; % Extract scalar from cell
else
physVal = varargin{p}; % It's already a scalar
end
paramName = obj.fn(p); % Get the parameter name (e.g., 'L' or 'D')
% Call getIndexByPhys on the corresponding Parameter2 object
indices{p} = obj.parameter.(paramName).getIndexByPhys(physVal);
end
% Convert subscript indices to a linear index
lin_idx = sub2ind(obj.dim, indices{:});
end
end
end

View File

@@ -1,51 +0,0 @@
classdef Parameter2 < handle
% PARAMETER2: Represents a physical parameter with mappings between values and indices
properties
name
values
length
physToIndexMap % Rename this from 'getPhysForIndex'
indexToPhysMap % Rename this from 'getIndexForPhys'
end
methods
function obj = Parameter2(name, values)
% Constructor to initialize the Parameter2 object
obj.name = name;
obj.values = values;
obj.length = numel(values);
% Initialize the mappings
obj.physToIndexMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
obj.indexToPhysMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
obj = obj.buildMappings();
end
function obj = buildMappings(obj)
% Build mappings between physical values and indices
for idx = 1:obj.length
obj.indexToPhysMap(idx) = obj.values(idx);
obj.physToIndexMap(obj.values(idx)) = idx;
end
end
function physVal = getPhysForIndex(obj, idx)
% Return the physical value corresponding to the index
if isKey(obj.indexToPhysMap, idx)
physVal = obj.indexToPhysMap(idx);
else
error('Index out of range for parameter %s', obj.name);
end
end
function idx = getIndexByPhys(obj, physVal)
% Return the index corresponding to the physical value
if isKey(obj.physToIndexMap, physVal)
idx = obj.physToIndexMap(physVal);
else
error('Physical value %g not found in parameter %s', physVal, obj.name);
end
end
end
end

View File

@@ -1,45 +0,0 @@
% Define input parameters for the DataStorage2
inputParams.L = [1, 2, 10, 80]; % Length in kilometers
inputParams.D = [16, 17, 18]; % Diameter in millimeters
% Create a DataStorage2 instance with the input parameters
dataStorage = DataStorage2(inputParams); % Using DataStorage2 class
% Display the current information about the data storage structure
dataStorage.showInfo();
% Add a storage variable named 'testStorage'
dataStorage.addStorage('testStorage');
% Add a value (e.g., 100) to the storage at specific physical parameter values
% For example, we store the value 100 at L = 10 km and D = 17 mm
dataStorage.addValueToStorage(100, 'testStorage', 10, 16);
dataStorage.addValueToStorage(100, 'testStorage', 10, 17);
dataStorage.addValueToStorage(100, 'testStorage', 10, 18);
% Retrieve the value from the storage at the same physical parameter values
storedValue = dataStorage.getStoValue('testStorage', 10, 16:18);
disp('Retrieved value from storage:');
disp(storedValue);
% Retrieve another value at a non-existent location (L = 2 km, D = 8 mm)
% This will show how the function handles empty storage entries
nonExistentValue = dataStorage.getStoValue('testStorage', 2, 8);
disp('Retrieved value from empty location:');
disp(nonExistentValue);
% Use the internal mappings to check how physical values map to indices
% Get the linear index for physical values L = 10 km and D = 17 mm
lin_idx = dataStorage.getIndicesByPhys(10, 17);
disp('Linear index for L=10 km and D=17 mm:');
disp(lin_idx);
% Check the reverse mapping: physical value for index 2 of parameter L
physValForIndex = dataStorage.parameter.L.getPhysForIndex(2);
disp('Physical value for index 2 of parameter L:');
disp(physValForIndex);
% Check the mapping: index for physical value D = 21 mm
indexForPhys = dataStorage.parameter.D.getIndexByPhys(21);
disp('Index for physical value D=21 mm:');
disp(indexForPhys);

Binary file not shown.