diff --git a/Classes/00_signals/Signal.m b/Classes/00_signals/Signal.m index e955679..8224f4b 100644 --- a/Classes/00_signals/Signal.m +++ b/Classes/00_signals/Signal.m @@ -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 @@ -284,7 +344,7 @@ classdef Signal N = 2^(nextpow2(length(obj.signal))-8); [p_lin,w] = pwelch(obj.signal,hanning(N),N/2,N,obj.fs,"centered","power","mean"); - normalize = 1; + normalize = 0; if normalize p_lin = p_lin./ max(p_lin); p_dbm = 10*log10(p_lin); %dB to dBm in case of "power" @@ -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)); diff --git a/Classes/01_transmit/ChannelFreqResp.m b/Classes/01_transmit/ChannelFreqResp.m index b8da77c..75960f3 100644 --- a/Classes/01_transmit/ChannelFreqResp.m +++ b/Classes/01_transmit/ChannelFreqResp.m @@ -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; diff --git a/Classes/01_transmit/PAMmapper.m b/Classes/01_transmit/PAMmapper.m index 9414d2e..64ef02f 100644 --- a/Classes/01_transmit/PAMmapper.m +++ b/Classes/01_transmit/PAMmapper.m @@ -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 diff --git a/Classes/01_transmit/PAMsource.m b/Classes/01_transmit/PAMsource.m index a4e1723..099b6be 100644 --- a/Classes/01_transmit/PAMsource.m +++ b/Classes/01_transmit/PAMsource.m @@ -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; diff --git a/Classes/02_etc/Amplifier.m b/Classes/02_etc/Amplifier.m index 4289061..31324e2 100644 --- a/Classes/02_etc/Amplifier.m +++ b/Classes/02_etc/Amplifier.m @@ -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; diff --git a/Classes/04_DSP/CIC_filter.m b/Classes/04_DSP/CIC_filter.m new file mode 100644 index 0000000..97c4db0 --- /dev/null +++ b/Classes/04_DSP/CIC_filter.m @@ -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) \ No newline at end of file diff --git a/Classes/04_DSP/Equalizer/FFE_adaptive_decision.m b/Classes/04_DSP/Equalizer/FFE_adaptive_decision.m index f1f9c9d..f1d9332 100644 --- a/Classes/04_DSP/Equalizer/FFE_adaptive_decision.m +++ b/Classes/04_DSP/Equalizer/FFE_adaptive_decision.m @@ -82,7 +82,7 @@ classdef FFE_adaptive_decision < handle end X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym lbdesc = [num2str(obj.order),' tap FFE']; - X = X.logbookentry(lbdesc); % append to logbook + X = X.logbookentry(lbdesc,obj); % append to logbook end @@ -106,6 +106,8 @@ classdef FFE_adaptive_decision < handle symbol = 0; % y_buffer = zeros(numel(obj.constellation),500); y_buffer = repmat(obj.constellation,1,obj.buffer_length); + adap_constellation = zeros(length(obj.constellation),length(x)); + cnt=0; for sample = 1 : obj.sps : N symbol = symbol+1; @@ -119,16 +121,24 @@ classdef FFE_adaptive_decision < handle d_hat(symbol,1) = d(symbol); else y_buffer(y_buffer==0) = NaN; - adap_constellation = mean(y_buffer,2,"omitnan"); - [~,symbol_idx] = min(abs(y(symbol) - adap_constellation)); % decision for closest constellation point + + adap_constellation(:,symbol) = mean(y_buffer,2,"omitnan"); + + [~,symbol_idx] = min(abs(y(symbol) - adap_constellation(:,symbol))); % decision for closest constellation point + d_hat(symbol,1) = obj.constellation(symbol_idx); end - y_buffer(symbol_idx,1) = y(symbol); y_buffer(symbol_idx,:) = circshift(y_buffer(symbol_idx,:),1); + + %y_(symbol) = y(symbol) - (adap_constellation(symbol_idx,symbol)-d_hat(symbol,1)); - err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error + if training + err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error + else + err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error + end if mio ~= 0 obj.e = obj.e - (mio * err(symbol) * U) ; % Weight update rule of LMS @@ -136,13 +146,13 @@ classdef FFE_adaptive_decision < handle normalizationfactor = (U.' * U); obj.e = obj.e - err(symbol) * U / normalizationfactor; % Weight update rule of NLMS end - obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error end end - + + %figure(1234);scatter(1:length(d_hat),y,1,'.');hold on;scatter(1:length(d_hat),y_,1,'.');plot(adap_constellation(1,1:length(d_hat)));plot(adap_constellation(2,1:length(d_hat)));plot(adap_constellation(3,1:length(d_hat)));plot(adap_constellation(4,1:length(d_hat))) end end diff --git a/Classes/05_Lab/Awg2Scope.m b/Classes/05_Lab/Awg2Scope.m new file mode 100644 index 0000000..765546c --- /dev/null +++ b/Classes/05_Lab/Awg2Scope.m @@ -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 diff --git a/Classes/05_Lab/AwgKeysight.m b/Classes/05_Lab/AwgKeysight.m index f02a7c5..0270ed4 100644 --- a/Classes/05_Lab/AwgKeysight.m +++ b/Classes/05_Lab/AwgKeysight.m @@ -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); diff --git a/Classes/05_Lab/DC_supply.m b/Classes/05_Lab/DC_supply.m index 5d79681..a04cd3e 100644 --- a/Classes/05_Lab/DC_supply.m +++ b/Classes/05_Lab/DC_supply.m @@ -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); diff --git a/Classes/05_Lab/OptAtten.m b/Classes/05_Lab/OptAtten.m index 5e0e320..84e98b5 100644 --- a/Classes/05_Lab/OptAtten.m +++ b/Classes/05_Lab/OptAtten.m @@ -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 diff --git a/Classes/05_Lab/ScopeKeysight.m b/Classes/05_Lab/ScopeKeysight.m index 9b33923..5578a32 100644 --- a/Classes/05_Lab/ScopeKeysight.m +++ b/Classes/05_Lab/ScopeKeysight.m @@ -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 diff --git a/Classes/Warehouse_class/classes/DataStorage.m b/Classes/Warehouse_class/classes/DataStorage.m index 5111ade..6fd1e97 100644 --- a/Classes/Warehouse_class/classes/DataStorage.m +++ b/Classes/Warehouse_class/classes/DataStorage.m @@ -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'); diff --git a/Classes/Warehouse_class/classes/DataStorage2.m b/Classes/Warehouse_class/classes/DataStorage2.m new file mode 100644 index 0000000..018f51c --- /dev/null +++ b/Classes/Warehouse_class/classes/DataStorage2.m @@ -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 diff --git a/Classes/Warehouse_class/classes/Parameter2.m b/Classes/Warehouse_class/classes/Parameter2.m new file mode 100644 index 0000000..84852ff --- /dev/null +++ b/Classes/Warehouse_class/classes/Parameter2.m @@ -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 diff --git a/Classes/Warehouse_class/classes/minimalExample_gen2.m b/Classes/Warehouse_class/classes/minimalExample_gen2.m new file mode 100644 index 0000000..c348197 --- /dev/null +++ b/Classes/Warehouse_class/classes/minimalExample_gen2.m @@ -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); diff --git a/Datatypes/scope_fadc.m b/Datatypes/scope_fadc.m index cc9c3c6..e4ab2c6 100644 --- a/Datatypes/scope_fadc.m +++ b/Datatypes/scope_fadc.m @@ -15,7 +15,7 @@ classdef scope_fadc < int32 methods function val = getValue(obj) - val = double(obj); % Convert int32 to double to get the numeric value + val = double(obj).*1e9; % Convert int32 to double and from GHz to Hz to get the numeric value end end diff --git a/projects/400G_FTN_setups/imdd_dsp_approaches.m b/projects/400G_FTN_setups/imdd_dsp_approaches.m index 8192c39..8ebf223 100644 --- a/projects/400G_FTN_setups/imdd_dsp_approaches.m +++ b/projects/400G_FTN_setups/imdd_dsp_approaches.m @@ -27,6 +27,7 @@ name = ['wh_',strrep(num2str(now),'.','')]; wh = DataStorage(params); +wh.addStorage("Rx_Bits"); wh.addStorage("ber_ffe"); %% Init Params @@ -200,7 +201,7 @@ for M = wh.parameter.M.values end end - Rx_bits = PAMmapper(M,0).demap(EQ_sig); + Rx_bits(i) = PAMmapper(M,0).demap(EQ_sig); [~,errors_bm,ber_ffe(i),errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); disp(['BER: ',sprintf('%.1E',ber_ffe(i)),' - - ROP: ',num2str(patten(i)),'dBm - - PAM-',num2str(M),' - - ',num2str(fsym*1e-9),' GBd']); @@ -210,12 +211,14 @@ for M = wh.parameter.M.values rop=wh.parameter.rop.values(i); wh.addValueToStorage(ber_ffe(i),'ber_ffe',M,datarate,rop); + wh.addValueToStorage(Rx_bits(i),'Rx_Bits',M,datarate,rop); end toc - % wh.save('C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\MPI_August\auswertung\') + filename = 'bla3'; + wh.save(['C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\MPI_August\auswertung\',filename]); end end diff --git a/projects/Lab_2024/lab_db_precode.m b/projects/Lab_2024/lab_db_precode.m new file mode 100644 index 0000000..1cdcd1a --- /dev/null +++ b/projects/Lab_2024/lab_db_precode.m @@ -0,0 +1,253 @@ + +folderpath = 'C:\Users\sioe\Nextcloud\Dokumente\02_Ablage_Office\Lab_Data_24\mpi_ofc_2024\'; +experiment_name = '10km_db_transmit_no_mpi_'; + +only_dsp = 0; + +ffe_only = 0; +postfilter_approach = 1; +db_channel_approach = 0; +db_coding_approach = 0; + +db_precode = db_coding_approach || db_channel_approach; + +%%% SIR Sweep for MPI Experiment %%% +params = struct; +params.i_atten = [40]; + +wh = DataStorage(params); + +wh.addStorage("ber"); +wh.addStorage("sir"); +wh.addStorage("pd_in"); +wh.addStorage("signals"); + +precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active +precomp_amp_max = 3; + +M = 4; +pn_key = 2; +usemrds = 0; +fdac = 92e9; +fsym = 92e9; +fadc = 160e9; +rrcalpha = 0.05; +v_bias = 2.25; +i_atten = params.i_atten(1); +pd_in_desired = 7; + +if ~only_dsp + + disp(['Start Measurement of ',num2str(prod(wh.dim)),' loops...']) + + %%%%% SET Volatges %%%%%% + dcs = DC_supply("active",[1,1],"voltage",[v_bias, 9]); + dcs.set("voltage",[v_bias, 9]); + + voa = OptAtten("active",[1,2,1,1],"value",[0,pd_in_desired,0,i_atten],"wavelength",[1310,1310,1310,1310]); + % voa.set('active',[1,2,1,1],'value',[0,pd_in_desired,0,i_atten]); + % voa.readvals(); + + SCP = ScopeKeysight("model","DSAZ634A",'autoscale',1,"fadc",'GSa_160',"channel",[1,0],"recordLen",2000000,"removeDC",1); + AWG = AwgKeysight("model","M8196A","fdac",fdac,"scaletodac",[1,1,1,1],"skews",[0,0,0,0],"voltages",[0,0,0,0.62]); + A2S = Awg2Scope(AWG,SCP,[0,0,0,1]); + + + if 1 + precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\standard_system\"; + precomp_fn = "lab_mpi_setup_2"; + else + precomp_path = "C:\Users\sioe\Documents\MATLAB\model-collection\sioe_models\Labor_2024\Lab_PAM4\"; + precomp_fn = "precomp_bla__loop1_1"; + end + + + %%%%% Symbol Generation %%%%%% + Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rrcalpha); + + [Digi_sig,Symbols,Bits] = PAMsource("fsym",fsym,"M",M,"order",18,"useprbs",1,... + "fs_out",fdac,"applyclipping",0,"clipfactor",1.7,... + "applypulseform",0,"pulseformer",Pform,"randkey",pn_key,... + "db_precode",db_precode,... + "mrds_code",usemrds,"mrds_blocklength",512,"db_encode",db_coding_approach).process(); + + if precomp_mode == 1 % measure channel + freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',Digi_sig.fs); + Digi_sig = freqresp.buildOFDM(); + elseif precomp_mode == 2 % apply precomp + freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',Digi_sig.fs); + Digi_sig = freqresp.precomp(Digi_sig,'maxampdb',precomp_amp_max,'loadPath',precomp_path,'fileName',precomp_fn); + end + + Digi_sig = Digi_sig.resample("fs_out",AWG.fdac); + + Digi_sig.spectrum("displayname","Normal Tx","fignum",10); + + % Digi_sig = Filter('filtdegree',1,"f_cutoff",45e9,"fs",Digi_sig.fs,"filterType",filtertypes.butterworth,"active",true).process(Digi_sig); + + % Digi_sig.spectrum("displayname","Lowpass Tx","fignum",999); + % Digi_sig.eye(fsym,M); + + + save([folderpath,[experiment_name,'bits']],"Bits"); + save([folderpath,[experiment_name,'symbols']],"Symbols"); + +end + +for i_atten = wh.parameter.i_atten.values + + if ~only_dsp + + %%%%% SET ATTENUATOR %%%%%% + voa.set('active',[1,2,1,1],'value',[0,7,0,i_atten]); + + %%%%% AWG --> Scope %%%%%% + [~,~,~,Scpe_sig] = A2S.process("signal4",Digi_sig); + + Scpe_sig.spectrum("displayname","Scope PSD","fignum",20); + + + + % Scpe_sig.spectrum("displayname",'Rx Signal','fignum',10); + + %%%%%% Sample to 2x fsym %%%%%% + Scpe_sig = Scpe_sig.resample("fs_in",160e9,"fs_out",2*fsym); + + if precomp_mode == 1 + freqresp.estimate(Scpe_sig,"save",true,"savePath",precomp_path,"fileName",precomp_fn); + freqresp.plot(); + end + + %%%%%% Sync Rx signal with reference %%%%%% + [Scpe_sig,S] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym); + save([folderpath,experiment_name,'rx_signal_iatten_',num2str(i_atten),''],"S"); + + average_signals = 0; + if average_signals + scope_mean = zeros(size(S{1}.signal)); + for n=1:numel(S) + scope_mean = scope_mean + S{n}.signal; + end + scope_mean = scope_mean ./ n; + Scpe_sig.signal = scope_mean; + end + + Scpe_sig.plot("displayname","Scope PSD","fignum",30); + Scpe_sig.eye(fsym,M,"fignum",40,"displayname",' after Scope'); + + + sir = voa.power_state(3)-voa.power_state(4); + pd_in = voa.power_state(2); + + end + + %%%%% EQUALIZE %%%%%% + Eq = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",50,"sps",2,"decide",0); + Eq = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",[50,7,7],"sps",2,"decide",1); + + if ffe_only %%%%%%%%%%%%%%%%%%%%%%%%%%% + + [EQ_sig] = Eq.process(Scpe_sig,Symbols); + + EQ_sig.plot("fignum",50,"displayname",'After EQ'); + + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + [~,errors_bm,ber,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + disp(['FFE: ',sprintf('%.1E',ber),'| SIR: ',num2str(sir),' dB | PD_in: ',num2str(pd_in),' dBm']); + + elseif postfilter_approach %%%%%%%%%%%%%%%%%%%%%%%%%%% + + [EQ_sig] = Eq.process(Scpe_sig,Symbols); + + EQ_sig.plot("fignum",50,"displayname",'After EQ','clear',1); + + Noi = EQ_sig-Symbols; + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + [~,errors_bm,ber_ffe_only,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + nc = 2; + burg_coeff = arburg(Noi.signal,nc); + + EQ_sig = EQ_sig.filter(burg_coeff,1); + + if 0 + Noi.spectrum('displayname','Noise PSD','fignum',123) + [h,w] = freqz(1,burg_coeff,length(Noi),"whole",Noi.fs); + h = h/max(abs(h)); + hold on + w_ = (w - Noi.fs/2); + plot(w_.*1e-9,20*log10(fftshift(h)),'DisplayName',['', num2str(nc), ' coefficients for burg alg.']); + end + + EQ_sig = MLSE("DIR",burg_coeff,"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig); + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + [~,errors_bm,ber,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + disp(['FFE: ',sprintf('%.1E',ber_ffe_only),' -> PF -> MLSE: ',sprintf('%.1E',ber),' | SIR: ',num2str(sir),' dB | PD_in: ',num2str(pd_in),' dBm']); + + elseif db_channel_approach %%%%%%%%%%%%%%%%%%%%%%%%%%% + + [EQ_sig, Noi] = Eq.process(Scpe_sig,Duobinary().encode(Symbols)); + + EQ_sig.plot("fignum",50,"displayname",'After EQ','clear',1); + + EQ_sig = MLSE("DIR",[1,1],"duobinary_output",1,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig); + EQ_sig = Duobinary().decode(EQ_sig); + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + [~,errors_bm,ber,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + disp([' DB Precode -> Channel -> FFE -> Decode/ Mod ',sprintf('%.1E',ber),' | SIR: ',num2str(sir),' dB | PD_in: ',num2str(pd_in),' dBm']); + + elseif db_coding_approach %%%%%%%%%%%%%%%%%%%%%%%%%%% + + [EQ_sig, Noi] = Eq.process(Scpe_sig,Symbols); + EQ_sig = MLSE("DIR",[1,1],"duobinary_output",1,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig); + EQ_sig = Duobinary().decode(EQ_sig); + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + [~,errors_bm,ber,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + EQ_sig.plot("fignum",50,"displayname",'After EQ'); + + disp([' DB Precode -> DB Code -> Channel -> FFE -> Decode/ Mod ',sprintf('%.1E',ber),' | SIR: ',num2str(sir),' dB | PD_in: ',num2str(pd_in),' dBm']); + + end + + + + wh.addValueToStorage(ber,'ber',i_atten); + wh.addValueToStorage(sir,'sir',i_atten); + wh.addValueToStorage(pd_in,'pd_in',i_atten); + wh.addValueToStorage(Rx_bits,'signals',i_atten); + +end + +wh.save([folderpath,experiment_name,'_wh']); + +cols = linspecer(8); + +sir_vals = wh.parameter.i_atten.values; +bers = wh.getStoValue('ber',sir_vals); + +figure(90); +a = gca; +hold on; % Retain the plot so new points can be added without complete redraw + +plot(sir_vals,bers,"LineWidth",0.5,"LineStyle","-","Marker",".","MarkerSize",15,"DisplayName",[experiment_name]); +yline(3.8e-3,'DisplayName','HD-FEC','LineStyle','--','HandleVisibility','off'); +xlabel('Received Optical Power (dBm)'); +ylabel('Bit Error Rate (BER)'); +title('Bit Error Rate vs. ROP'); +set(gca,'yscale','log'); +set(gca,'Box','on'); +grid on; +grid minor +legend('Interpreter','none') + +autoArrangeFigures(2,3,2) + diff --git a/projects/Lab_2024/lab_minimal.m b/projects/Lab_2024/lab_minimal.m index dbb690f..fe61cfa 100644 --- a/projects/Lab_2024/lab_minimal.m +++ b/projects/Lab_2024/lab_minimal.m @@ -1,14 +1,18 @@ + + +folderpath = 'C:\Users\sioe\Nextcloud\Dokumente\02_Ablage_Office\Lab_Data_24\mpi_ofc_2024\'; + %%% SIR Sweep for MPI Experiment %%% params = struct; -params.i_atten = 10; +params.i_atten = [40]; wh = DataStorage(params); wh.addStorage("ber"); +wh.addStorage("ber_pf"); wh.addStorage("sir"); wh.addStorage("pd_in"); - -playandrecord = 0; +wh.addStorage("signals"); precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active M = 4; @@ -18,8 +22,8 @@ fdac = 92e9; fsym = 92e9; fadc = 160e9; rrcalpha = 0.05; -v_bias = 2.27; -i_atten = 40; +v_bias = 2.25; +i_atten = params.i_atten(1); pd_in_desired = 7; disp(['Start Measurement of ',num2str(prod(wh.dim)),' loops...']) @@ -28,10 +32,13 @@ disp(['Start Measurement of ',num2str(prod(wh.dim)),' loops...']) dcs = DC_supply("active",[1,1],"voltage",[v_bias, 9]); dcs.set("voltage",[v_bias, 9]); -voa = OptAtten("active",[0,2,1,1],"value",[0,pd_in_desired,0,i_atten],"wavelength",[1310,1310,1310,1310]); -voa.set('active',[0,2,1,1],'value',[0,pd_in_desired,0,i_atten]); -voa.readvals(); -%%%%% SET Volatges %%%%%% +voa = OptAtten("active",[1,2,1,1],"value",[0,pd_in_desired,0,i_atten],"wavelength",[1310,1310,1310,1310]); +% voa.set('active',[1,2,1,1],'value',[0,pd_in_desired,0,i_atten]); +% voa.readvals(); + +SCP = ScopeKeysight("model","DSAZ634A",'autoscale',1,"fadc",'GSa_160',"channel",[1,0],"recordLen",2000000,"removeDC",1); +AWG = AwgKeysight("model","M8196A","fdac",fdac,"scaletodac",[1,1,1,1],"skews",[0,0,0,0],"voltages",[0,0,0,0.62]); +A2S = Awg2Scope(AWG,SCP,[0,0,0,1]); if 1 @@ -45,6 +52,7 @@ end %%%%% Symbol Generation %%%%%% Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rrcalpha); + [Digi_sig,Symbols,Bits] = PAMsource("fsym",fsym,"M",M,"order",18,"useprbs",1,... "fs_out",fdac,"applyclipping",0,"clipfactor",1.7,... "applypulseform",0,"pulseformer",Pform,"randkey",pn_key,... @@ -59,24 +67,23 @@ elseif precomp_mode == 2 % apply precomp Digi_sig = freqresp.precomp(Digi_sig,'maxampdb',3,'loadPath',precomp_path,'fileName',precomp_fn); end -%%%%% AWG %%%%%% -AWG = AwgKeysight("model","M8196A","fdac",fdac,"scaletodac",[1,1,1,1],"skews",[0,0,0,0],"voltages",[0,0,0,0.62]); -AWG.upload("signal4",Digi_sig); +Digi_sig = Digi_sig.resample("fs_out",AWG.fdac); + +save([folderpath,'bits'],"Bits"); +save([folderpath,'symbols'],"Symbols"); for i_atten = wh.parameter.i_atten.values %%%%% SET ATTENUATOR %%%%%% - voa.set('active',[0,2,1,1],'value',[0,7,0,i_atten]); + voa.set('active',[1,2,1,1],'value',[0,7,0,i_atten]); - %%%%% Scope %%%%%% - SCP = ScopeKeysight("model","DSAZ634A",'autoscale',1,"fadc",'GSa_160',"channel",[1],"recordLen",2000000,"removeDC",1); - Scpe_sig = SCP.read(); - Scpe_sig = Electricalsignal(Scpe_sig{1},"fs",fadc); + %%%%% AWG --> Scope %%%%%% + [~,~,~,Scpe_sig] = A2S.process("signal4",Digi_sig); % Scpe_sig.spectrum("displayname",'Rx Signal','fignum',10); %%%%%% Sample to 2x fsym %%%%%% - Scpe_sig = Scpe_sig.resample("fs_in",160e9,"fs_out",2*92e9); + Scpe_sig = Scpe_sig.resample("fs_in",160e9,"fs_out",2*fsym); if precomp_mode == 1 freqresp.estimate(Scpe_sig,"save",true,"savePath",precomp_path,"fileName",precomp_fn); @@ -85,44 +92,57 @@ for i_atten = wh.parameter.i_atten.values %%%%%% Sync Rx signal with reference %%%%%% [Scpe_sig,S] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym); + save([folderpath,'rx_iatten_',num2str(i_atten),''],"S"); Scpe_sig.eye(fsym,M); + Scpe_sig.spectrum("displayname","Scope PSD","fignum",1996); %%%%% EQUALIZE %%%%%% + + % Scpe_sig.signal = Scpe_sig.signal - movmean(Scpe_sig.signal,[100,0]); - Eq_ffe_only = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0); - ber_ffe_only = runEQ(Eq_ffe_only,Scpe_sig,Symbols,Bits,M); + Eq_ffe_only = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",50,"sps",2,"decide",0); + [ber_ffe_only,EQ_sig,Noi] = runEQ(Eq_ffe_only,Scpe_sig,Symbols,Bits,M); - Eq_move_it = EQ("Ne",[50,7,7],"Nb",[0,0,0],"training_length",4096*2,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.05,"DDmu",[0.0004 0.0004 0.0004 0.0004 ],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); - ber_move_it = runEQ(Eq_move_it,Scpe_sig,Symbols,Bits,M); + nc = 2; + burg_coeff = arburg(Noi.signal,nc); + + EQ_sig = EQ_sig.filter(burg_coeff,1); + + EQ_sig.plot("displayname",'After EQ','fignum',1112); + + if 0 + Noi.spectrum('displayname','Noise PSD','fignum',123) + [h,w] = freqz(1,burg_coeff,length(Noi),"whole",Noi.fs); + h = h/max(abs(h)); + hold on + w_ = (w - Noi.fs/2); + plot(w_.*1e-9,20*log10(fftshift(h)),'DisplayName',['', num2str(nc), ' coefficients for burg alg.']); + end + + % EQ_sig.spectrum("displayname","Signal Spectrum after Postfilter","fignum",1234); - Eq_nonlin = EQ("Ne",[50,7,7],"Nb",[0,0,0],"training_length",4096*2,"training_loops",5,"dd_loops",5,"K",2,"DCmu",0.0,"DDmu",[0.0004 0.0004 0.0004 0.0004 ],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1); - %VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",[50,7,7],"sps",2,"decide",0); - ber_nonlin = runEQ(Eq_nonlin,Scpe_sig,Symbols,Bits,M); - - Eq_adaptive_decision = FFE_adaptive_decision("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0,"buffer_length",80); - ber_adaptive_decision = runEQ(Eq_adaptive_decision,Scpe_sig,Symbols,Bits,M); - - FFE_FFDCAVG("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0,"mu_buff",128); - ber_ff_dcavg = runEQ(FFE_FFDCAVG,Scpe_sig,Symbols,Bits,M); - - Eq_feedback_removal = FFE_DCremoval("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",25,"sps",2,"decide",0,"mu_dc",0.05,"dc_buffer_len",1); - ber_feedback_removal = runEQ(Eq_feedback_removal,Scpe_sig,Symbols,Bits,M); - + EQ_sig = MLSE("DIR",burg_coeff,"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels).process(EQ_sig); + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + [~,errors_bm,ber_pf,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); sir = voa.power_state(3)-voa.power_state(4); pd_in = voa.power_state(2); - disp(['BER: ',sprintf('%.1E',ber_eq),' | SIR: ',num2str(sir),' dB | PD_in: ',num2str(pd_in),' dBm']); + disp(['FFE: ',sprintf('%.1E',ber_ffe_only),' -> PF -> MLSE: ',sprintf('%.1E',ber_pf),' | SIR: ',num2str(sir),' dB | PD_in: ',num2str(pd_in),' dBm']); - wh.addValueToStorage(ber_eq,'ber',i_atten); + wh.addValueToStorage(ber_ffe_only,'ber',i_atten); + wh.addValueToStorage(ber_pf,'ber_pf',i_atten); wh.addValueToStorage(sir,'sir',i_atten); wh.addValueToStorage(pd_in,'pd_in',i_atten); + wh.addValueToStorage(Rx_bits,'signals',i_atten); end - +name = ['mpidata_92GBd']; +wh.save([folderpath,name]); cols = linspecer(8); diff --git a/projects/Lab_2024/runEQ.m b/projects/Lab_2024/runEQ.m index 1fe66a8..da1661f 100644 --- a/projects/Lab_2024/runEQ.m +++ b/projects/Lab_2024/runEQ.m @@ -1,13 +1,25 @@ -function BER = runEQ(Eq_class,Scpe_sig,Symbols,Bits,M) - -[EQ_sig] = Eq_class.process(Scpe_sig,Symbols); - -error = EQ_sig-Symbols; -error.spectrum("displayname",['Error PSD after: ',inputname(1),' '],'fignum',564); - -Rx_bits = PAMmapper(M,0).demap(EQ_sig); - -[~,errors_bm,BER,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); +function [BER,EQ_sig,Noi] = runEQ(Eq_class,Scpe_sig,Symbols,Bits,M) + [EQ_sig] = Eq_class.process(Scpe_sig,Symbols); + + Noi = EQ_sig-Symbols; + + + Rx_bits = PAMmapper(M,0).demap(EQ_sig); + + [~,errors_bm,BER,errors] = calc_ber(Rx_bits.signal,Bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1); + + if 0 + figure(); + constellation = unique(Symbols.signal); + received = NaN(numel(constellation),length(Symbols)); + for lvl = 1:numel(constellation) + received(lvl,Symbols.signal==constellation(lvl)) = EQ_sig.signal(Symbols.signal==constellation(lvl)); + hold on + histogram(received(lvl,:),1000,"EdgeAlpha",0); + end + + Noi.spectrum("displayname",['Error PSD after: ',inputname(1),' '],'fignum',564); + end end \ No newline at end of file