Merge branch 'main' of cau-git.rz.uni-kiel.de:nt/mitarbeiter/silas/imdd_simulation

# Conflicts:
#	Classes/04_DSP/Equalizer/FFE_adaptive_decision.m
This commit is contained in:
sioe
2024-10-15 10:23:04 +02:00
34 changed files with 3329 additions and 94 deletions

View File

@@ -6,6 +6,10 @@ classdef Signal
signal
logbook
fs
gitSHA
gitStatus
gitPatch
end
methods
@@ -17,18 +21,26 @@ classdef Signal
options.fs = [];
end
obj.signal = signal;
obj.signal = obj.signal;
obj.fs = options.fs;
obj.signal = signal;
obj.signal = obj.signal;
obj.fs = options.fs;
[~,obj.gitSHA] = system('git rev-parse HEAD');
[~,obj.gitStatus] = system('git status --porcelain');
[~,obj.gitPatch] = system('git diff');
%%% Stuff for Logbook %%%
SignalType = [];
TimeStamp = [];
Length = [];
SignalPower = [];
Nase = [];
SignalCopy = [];
ModifierName = [];
ModifierCopy= {};
Description = [];
obj.logbook = table(SignalType,TimeStamp,Length,SignalPower,Nase,Description);
obj.logbook = table(SignalType,TimeStamp,Length,SignalPower,Nase,SignalCopy,ModifierName, ModifierCopy, Description);
end
@@ -131,9 +143,13 @@ classdef Signal
options.fignum
options.displayname = [];
options.timeframe = 0;
options.clear = 0;
end
figure(options.fignum); % If figure does not exist, create new figure
if options.clear
clf
end
% 2) Plot into the figure handle found or created in one
t = (0:length(obj.signal)-1) / obj.fs; % time vector
@@ -227,24 +243,58 @@ classdef Signal
%% Write Logbook Entry
function obj = logbookentry(obj,varargin)
if nargin > 1
if nargin == 2
Description = varargin{1};
CallingModifier = evalin('caller','obj');
elseif nargin == 3
Description = varargin{1};
CallingModifier = varargin{2};
else
Description = "";
CallingModifier = evalin('caller','obj');
end
CallingModifierStruct = obj.objToStructFilteredRecursive(CallingModifier);
SignalType = [string(class(obj))];
TimeStamp = [(datetime('now','TimeZone','local','Format','HH:mm:ss'))];
Length = num2str(obj.length, ['%' sprintf('.%df', 0)]);%[obj.length];
SignalPower = [obj.power];
Nase = [0];
SignalCopy = obj.signal;
ModifierName = class(CallingModifier);
ModifierCopy = {CallingModifierStruct};
cell = {SignalType , TimeStamp , Length , SignalPower(1) , Nase, Description};
cell = {SignalType , TimeStamp , Length , SignalPower(1) , Nase, SignalCopy, ModifierName, ModifierCopy, Description};
obj.logbook = [obj.logbook;cell];
obj.logbook = [obj.logbook; cell];
end
function s = objToStructFilteredRecursive(~,obj)
% Convert the object to a structure using 'struct' and catch warnings
warnState = warning('off', 'MATLAB:structOnObject');
s = struct(obj); % Convert to struct
warning(warnState); % Restore previous warning state
% Get all field names of the struct
fields = fieldnames(s);
% Loop over each field and handle filtering
for i = 1:numel(fields)
fieldData = s.(fields{i});
if isstruct(fieldData) % If the field is a struct, call recursively
s.(fields{i}) = obj.objToStructFilteredRecursive(fieldData);
elseif numel(fieldData) > 1000 % Remove field if it has more than 1000 elements
%s = rmfield(s, fields{i});
s.(fields{i}) = [];
elseif isa(fieldData,'table')
s = rmfield(s, fields{i});
end
end
end
%% Resample Signal
function obj = resample(obj,options)
@@ -260,13 +310,23 @@ classdef Signal
warning('The signals fs is different from the given fs_in while it should be the same.');
end
obj.signal = resample(obj.signal,options.fs_out,options.fs_in,options.n,options.beta);
if options.fs_in == options.fs_out
desc = ['resample signal from ', num2str(options.fs_in*1e-9), ' GHz to ', num2str(options.fs_out*1e-9), ' GHz' ];
desc = ['No need to resample signal from ', num2str(options.fs_in*1e-9), ' GHz to ', num2str(options.fs_out*1e-9), ' GHz' ];
obj = obj.logbookentry(desc,obj);
obj = obj.logbookentry(desc);
else
obj.fs = options.fs_out;
obj.signal = resample(obj.signal,options.fs_out,options.fs_in,options.n,options.beta);
desc = ['resample signal from ', num2str(options.fs_in*1e-9), ' GHz to ', num2str(options.fs_out*1e-9), ' GHz' ];
obj = obj.logbookentry(desc,obj);
obj.fs = options.fs_out;
end
end
@@ -295,6 +355,7 @@ classdef Signal
end
figure(options.fignum); % If figure does not exist, create new figure
ax = gca;
hold on
plot(w.*1e-9,p_dbm,'DisplayName',options.displayname,'LineWidth',1);
xlabel("Frequency in GHz");
@@ -304,11 +365,11 @@ classdef Signal
edgetick = 2^(nextpow2(obj.fs*1e-9));
% xticks([-edgetick:16:edgetick]);
xlim([100*round( min(w.*1e-9)/100,1)-10,100*round( max(w.*1e-9)/100,1)+10])
ylim([100*round( min(p_dbm)/100,1)-3,100*round( max(p_dbm)/100,1)+3]);
ylim([min(floor( min(p_dbm))-3 , ax.YLim(1)), max(ceil( max(p_dbm) )+(3), ax.YLim(2))]);
yticks([-200:10:10]);
grid on
grid minor
legend
legend('Interpreter','none');
end
@@ -451,13 +512,21 @@ classdef Signal
[pks,pkpos] = findpeaks(co./max(co),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum);
shifts = lags(pkpos);
%Cut occurences of ref signal from signal
%Cut occurences of ref signal from signal (only positive shifts)
S = {};
for c = shifts
for c = shifts(shifts>0)
sig = obj.delay(-c,'mode','samples');
sig.signal = sig.signal(1:length(b));
S{end+1,1} = sig;
end
%return/keep the sinal with the highest correlation (only within positive shifts)
[~,idx]=max(pks(shifts>0));
obj.signal = S{idx}.signal;
for c = 1:numel(shifts(shifts>0))
S{c}.logbook = [];
end
%plot all synced signals and the ref signal
debug = 0;
@@ -469,14 +538,16 @@ classdef Signal
end
end
%return the sinal with the highest correlation...
[~,idx]=max(pks);
obj.signal = S{idx}.signal;
end
function obj = filter(obj,a,b)
lbdesc = ['Filtering signal with H = a: ',num2str(a),' / b: ',num2str(b)];
obj = obj.logbookentry(lbdesc,obj);
obj.signal = filter(a,b,obj.signal);
end
@@ -560,7 +631,15 @@ classdef Signal
end
function eye(obj,fsym,M)
function eye(obj,fsym,M,options)
arguments
obj
fsym
M
options.fignum = 100;
options.displayname = "";
end
mode = 1;
@@ -587,7 +666,7 @@ classdef Signal
eye_mat = reshape(x(1:end-mod(length(x),histpoints_horizontal)),histpoints_horizontal,floor(length(x)/histpoints_horizontal)); %% reshape signal into 256 rows each row has the histogram(eye data of all symbols)
figure(922)
figure(options.fignum)
clf
if mode == 2
% generate "intuitive eye diagram" by drawing lines on top over
@@ -633,19 +712,19 @@ classdef Signal
colormap(cbrewer2("Blues",4096));
if isa(obj,'Opticalsignal')
title("Optical Eye")
title(['Optical Eye ',options.displayname])
ylabel("Power in mW");
y_tickstring = string(linspace(maxA.*1e3,minA.*1e3,16));
min_ = min(abs(obj.signal(100:end-100)).^2);
max_ = abs(max(obj.signal(100:end-100)).^2);
elseif isa(obj,'Electricalsignal')
title("Electrical Eye")
title(['Electrical Eye ',options.displayname])
ylabel("Voltage in V");
y_tickstring = string(linspace(maxA,minA,16));
min_ = min(obj.signal(100:end-100));
max_ = abs(max(obj.signal(100:end-100)));
else
title("Digital Eye")
title(['Digital Eye ',options.displayname])
ylabel("Digital Signal Amplitude");
y_tickstring = string(linspace(maxA,minA,16));
min_ = min(obj.signal(100:end-100));

View File

@@ -184,9 +184,12 @@ classdef ChannelFreqResp < handle
end
% iH(1) is DC ---> iH(end) is High Freq.
H_inv = [iH(1) iH fliplr(conj(iH)) conj(iH(1))];
H_inv = [iH(1) iH 0 fliplr(conj(iH))];
if mod(length(Target.signal),2) %ungerade
H_inv = [iH(1) iH iH(end) fliplr(conj(iH)) conj(iH(1))];
else
H_inv = [iH(1) iH 0 fliplr(conj(iH))];
end
% H_inv = [iH(1) iH iH(end) fliplr(conj(iH)) conj(iH(1))];
obj.H_apply = H_inv;

View File

@@ -32,8 +32,8 @@ classdef PAMmapper
signal_in.signal = obj.map_(signal_in.signal);
% signal_in = signal_in.normalize("mode","rms");
signal_in = signal_in.logbookentry();
lbdesc = ['Map bat stream to PAM ',num2str(obj.M),' symbols'];
signal_in = signal_in.logbookentry(lbdesc,obj);
out = signal_in;
else
out = signal_in;
@@ -43,7 +43,8 @@ classdef PAMmapper
function signalclass_out = demap(obj,signalclass_in)
signalclass_in.signal = obj.demap_(signalclass_in.signal);
signalclass_in = signalclass_in.logbookentry();
lbdesc = ['Demap PAM ',num2str(obj.M),' symbols to bit stream'];
signalclass_in = signalclass_in.logbookentry(lbdesc,obj);
signalclass_out = signalclass_in;
end

View File

@@ -93,6 +93,7 @@ classdef PAMsource
end
bits = Informationsignal(bitpattern);
bits = bits.logbookentry(['Generate bit stream with size: ', num2str(size(bitpattern))]);
symbols = PAMmapper(obj.M,0).map(bits);
symbols.fs = obj.fsym;

View File

@@ -40,8 +40,8 @@ classdef Amplifier
signalclass_in = obj.process_(signalclass_in);
% append to logbook
lbdesc = ['Amp '];
signalclass_in = signalclass_in.logbookentry(lbdesc);
lbdesc = ['Optical Amplifier '];
signalclass_in = signalclass_in.logbookentry(lbdesc,obj);
% write to output
signalclass_out = signalclass_in;

View File

@@ -0,0 +1,59 @@
% Moving Average filter
N = 7;
xn = sin(2*pi*[0:.1:10]);
hn = ones(1,N);
y1n = conv(xn,hn) .* 1/N;
% transfer function of Moving Average filter
figure()
hF = fft(hn,1024);
plot([-512:511]/1024, abs(fftshift(hF)));
xlabel('Normalized frequency')
ylabel('Amplitude')
title('frequency response of Moving average filter')
% Implementing Cascaded Integrator Comb filter with the
% comb section following the integrator stage
N = 10;
delayBuffer = zeros(1,N);
intOut = 0;
xn = sin(2*pi*[0:.1:10]);
for ii = 1:length(xn)
% comb section
combOut = xn(ii) - delayBuffer(end);
delayBuffer(2:end) = delayBuffer(1:end-1);
delayBuffer(1) = xn(ii);
% integrator
intOut = intOut + combOut;
y2n(ii) = intOut;
end
err12 = y1n(1:length(xn)) - y2n;
err12dB = 10*log10(err12*err12'/length(err12)); % identical outputs
% Implementing Cascaded Integrator Comb filter with the
% integrator section following the comb stage
N = 10;
delayBuffer = zeros(1,N);
intOut = 0;
xn = sin(2*pi*[0:.1:10]);
for ii = 1:length(xn)
% integrator
intOut = intOut + xn(ii);
% comb section
combOut = intOut - delayBuffer(end);
delayBuffer(2:end) = delayBuffer(1:end-1);
delayBuffer(1) = intOut;
y3n(ii) = combOut;
end
err13 = y1n(1:length(xn)) - y3n;
err13dB = 10*log10(err13*err13'/length(err13)); % identical outputs
figure()
hold on
plot(xn)
plot(y1n)

View File

@@ -108,7 +108,7 @@ classdef Duobinary
data = data - b;
data = data ./ 2;
assert(isequal((0:M-1)',unique(data)),'Check Duobinary Precoding'); %seems the signal is not unipolar
% assert(isequal((0:M-1)',unique(data)),'Check Duobinary Precoding'); %seems the signal is not unipolar
% duobinary coding (1+D)
% coeff = [1,1];

109
Classes/05_Lab/Awg2Scope.m Normal file
View File

@@ -0,0 +1,109 @@
classdef Awg2Scope
%NAME Summary of this class goes here
% Detailed explanation goes here
properties(Access=public)
Awg
Scope
mapping;
end
methods (Access=public)
function obj = Awg2Scope(Awg,Scope,mapping)
%Simple class to call the Awg and Scope and map the signals
%accordingly in the correct formats with correct l
% ogbook
%entries...
arguments
Awg
Scope
mapping
end
obj.Awg = Awg;
obj.Scope = Scope;
obj.mapping = mapping; % AWG CH [1,2,3,4] -> Scope CH [0,0,0,1]
end
function [S1,S2,S3,S4] = process(obj,channels)
arguments
obj
% leave this as it is! Important for further handling/ parsing
channels.signal1 Informationsignal = Informationsignal([])
channels.signal2 Informationsignal = Informationsignal([])
channels.signal3 Informationsignal = Informationsignal([])
channels.signal4 Informationsignal = Informationsignal([])
% add new optional arguments here
end
[S1,S2,S3,S4]=obj.Awg.upload("signal1",channels.signal1,...
"signal2",channels.signal2,...
"signal3",channels.signal3,...
"signal4",channels.signal4...
);
scpe_sig_cell = obj.Scope.read();
% Map Scope measurement to output signal
% mapping index is the AWG chanel and mapping number is the
% respective Scope channel
% mapping=[1 2 3 4] means that AWG chann 1 is mapped to Scope ch 1 and so on
% mapping=[0 0 2 1] means that
% AWG chann 3 is mapped to Scope ch 2
% AWG chann 4 is mapped to Scope ch 1
lbdesc = ['Scope Record'];
try
S1 = Electricalsignal(S1,"fs",S1.fs,"logbook",S1.logbook);
S1.signal = scpe_sig_cell{obj.mapping(1)}.signal;
S1.fs = scpe_sig_cell{obj.mapping(1)}.fs;
S1 = S1.logbookentry(lbdesc,obj);
catch
% S1.signal = scpe_sig_cell{1};
end
try
S2 = Electricalsignal(S2,"fs",S2.fs,"logbook",S2.logbook);
S2.signal = scpe_sig_cell{obj.mapping(2)}.signal;
S2.fs = scpe_sig_cell{obj.mapping(2)}.fs;
S2 = S2.logbookentry(lbdesc,obj);
catch
% S2.signal = scpe_sig_cell{2};
S2 = S2.logbookentry(lbdesc,obj);
end
try
S3 = Electricalsignal(S3,"fs",S3.fs,"logbook",S3.logbook);
S3.signal = scpe_sig_cell{obj.mapping(3)}.signal;
S3.fs = scpe_sig_cell{obj.mapping(3)}.fs;
S3 = S3.logbookentry(lbdesc,obj);
catch
% S3.signal = scpe_sig_cell{3};
S3 = S3.logbookentry(lbdesc,obj);
end
try
S4 = Electricalsignal(S4,"fs",S4.fs,"logbook",S4.logbook);
S4.signal = scpe_sig_cell{obj.mapping(4)}.signal;
S4.fs = scpe_sig_cell{obj.mapping(4)}.fs;
S4 = S4.logbookentry(lbdesc,obj);
catch
% S4.signal = scpe_sig_cell{4};
S4 = S4.logbookentry(lbdesc,obj);
end
end
end
end

View File

@@ -24,9 +24,9 @@ classdef AwgKeysight
properties(Access=protected)
model awg_model
fdac double
%fdac double
skews double
voltages double
%voltages double
scaletodac logical
numChannels double
@@ -36,6 +36,11 @@ classdef AwgKeysight
numProvidedSignals double
end
properties(Access=public)
fdac double
voltages double
end
methods (Access=public)
function obj = AwgKeysight(options)
@@ -78,7 +83,7 @@ classdef AwgKeysight
end
function upload(obj,channels,options)
function [signal1,signal2,signal3,signal4] = upload(obj,channels,options)
arguments
obj
@@ -116,7 +121,18 @@ classdef AwgKeysight
success = obj.upload_(unpackedSignals);
for s = 1:obj.numChannels
lbdesc = ['Upload to Awg'];
obj = channels.(fn{s}).logbookentry(lbdesc,obj);
end
signal1 = channels.signal1;
signal2 = channels.signal2;
signal3 = channels.signal3;
signal4 = channels.signal4;
end
end
@@ -203,8 +219,11 @@ classdef AwgKeysight
v = visadev('TCPIP0::localhost::hislip0::INSTR');
end
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
%check if channel config matches
writeline(v,'*opt?');
aw=readline(v);

View File

@@ -47,7 +47,10 @@ classdef DC_supply
%connect to device
v = visadev("GPIB1::19::INSTR");
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
cmd = 'INST:SEL?';
writeline(v, cmd);

View File

@@ -0,0 +1,74 @@
classdef Exfo_laser
properties(Access=public)
wavelength
power
end
methods (Access=public)
function obj = Exfo_laser(options)
arguments
options.wavelength = 1310; %dbm
options.power = -10; %dbm
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
end
function success = set(obj,options)
arguments
obj
options.wavelength = obj.wavelength; %dbm
options.power = obj.power; %dbm
end
% Connect to the laser
o = serialport("COM8", 9600);
configureTerminator(o, "CR"); % Set the terminator to carriage return (CR)
writeline(o, "*IDN?");
pause(1);
if o.NumBytesAvailable ~= 0
disp(['Laser Mainframe: ', readline(o)]);
else
error('No connection to the mainframe');
clear o;
end
end
% Function to set the wavelength of the laser
function setLaserWavelength(~,serialObj, channel, wavelength)
command = ['CH', num2str(channel), ':L=', num2str(wavelength)];
writeline(serialObj, command);
pause(0.5); % Allow time for the wavelength to change
writeline(serialObj, ['CH', num2str(channel), ':L?']); % Query current wavelength
current_wavelen = readline(serialObj);
disp(['Current Wavelength: ', current_wavelen]);
end
% Function to set the laser power
function setLaserPower(~,serialObj, channel, power_dBm)
command = ['CH', num2str(channel), ':P=', num2str(power_dBm)];
writeline(serialObj, command);
pause(0.2); % Allow time for power to adjust
writeline(serialObj, ['CH', num2str(channel), ':P?']); % Query current power
current_power = readline(serialObj);
disp(['Current Power: ', current_power, ' dBm']);
end
end
end

View File

@@ -55,7 +55,10 @@ classdef OptAtten < handle
%connect to device
v = visadev('TCPIP::134.245.243.248::INSTR');
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
%Keysight Technologies, N7764A, MY49A00696, 1.13.1

View File

@@ -79,8 +79,11 @@ classdef ScopeKeysight
end
end
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
% Check if Scope is ready to go
opdone = 0;
acqdone = 0;
@@ -202,6 +205,11 @@ classdef ScopeKeysight
procdone = sscanf(obj.writeReceiveCheck(v,'pder?'), '%f');
pause(0.005);
end
% arrange to Electrcalsinal class as output %%
for ch = 1:numel(obj.channel)
recordedSignals{ch} = Electricalsignal(recordedSignals{ch},"fs",obj.fadc.getValue);%fs is a enum and requires get function
end
end

View File

@@ -48,6 +48,14 @@ classdef DataStorage < handle
end
function save(obj,path)
try
save(path,"obj");
catch
end
end
function showInfo(obj)
disp("Data Structure with fields:");
fprintf('%-12s', 'Name'); fprintf('%1s', '| '); fprintf('%0s ', 'Dimension'); fprintf('%4s', '| '); fprintf('%0s ', 'Physical Values'); fprintf('\n');
@@ -120,7 +128,21 @@ classdef DataStorage < handle
try
tmp = obj.sto.(storageVarName){lin_idx(i)};
if ~isempty(tmp)
value(i,:) = tmp ;
if isa(tmp,'Signal')
if i == 1
value = {};
end
value{i} = tmp ;
elseif isa(tmp,'cell')
if isa(tmp{1},'Signal')
if i == 1
value = {};
end
value{i} = tmp{1} ;
end
else
value(i,:) = tmp ;
end
else
errcnt = errcnt+1;

View File

@@ -0,0 +1,114 @@
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

@@ -0,0 +1,51 @@
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

@@ -0,0 +1,45 @@
% 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);