From 952178341ebec2e45dec3b67e6cfd604e0c1470b Mon Sep 17 00:00:00 2001 From: Silas Oettinghaus Date: Thu, 23 Apr 2026 21:56:47 +0200 Subject: [PATCH] =?UTF-8?q?F=C3=BCge=20PRMS-Fehlerbehandlung=20hinzu=20und?= =?UTF-8?q?=20implementiere=20Partialresponse-Kodierung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Classes/01_transmit/Signalgenerator.m | 21 +- Classes/04_DSP/Coding/Partialresponse.m | 199 ++++++++++++++++++ .../partial_response/duobinary_back2back.m | 36 +++- 3 files changed, 244 insertions(+), 12 deletions(-) create mode 100644 Classes/04_DSP/Coding/Partialresponse.m diff --git a/Classes/01_transmit/Signalgenerator.m b/Classes/01_transmit/Signalgenerator.m index 4c9a5ad..63e2005 100644 --- a/Classes/01_transmit/Signalgenerator.m +++ b/Classes/01_transmit/Signalgenerator.m @@ -282,18 +282,29 @@ classdef Signalgenerator otherwise length = 1024; end + + if obj.form == signalform.prms && length > 2^20 + error("Signalgenerator:AutoLengthTooLarge", ... + "Auto-derived PRMS length would be %d samples, which is too large for an implicit default. Pass an explicit length if you really want a longer sequence.", ... + length); + end end function prms_order = internal_prms_order(obj) - if ~isempty(obj.M) + bits_per_symbol = obj.prms_bits_per_symbol(); + + prms_order = max(1, floor(obj.order / bits_per_symbol)); + end + + function bits_per_symbol = prms_bits_per_symbol(obj) + if (~isempty(obj.M) && obj.M == 6) || (isempty(obj.M) && obj.dimension == 5) + % PAM-6 mapping in this repo consumes 5 bits for 2 symbols. + bits_per_symbol = 2.5; + elseif ~isempty(obj.M) bits_per_symbol = log2(obj.M); - elseif obj.dimension == 5 - bits_per_symbol = log2(6); else bits_per_symbol = obj.dimension; end - - prms_order = max(1, floor(obj.order / bits_per_symbol)); end function dimension = mapper_bit_dimension(obj) diff --git a/Classes/04_DSP/Coding/Partialresponse.m b/Classes/04_DSP/Coding/Partialresponse.m new file mode 100644 index 0000000..4494973 --- /dev/null +++ b/Classes/04_DSP/Coding/Partialresponse.m @@ -0,0 +1,199 @@ +classdef Partialresponse + %PARTIALRESPONSE Generalized symbol-domain partial-response coding. + + properties + order = 1 + end + + methods + function obj = Partialresponse(options) + arguments + options.order (1,1) double = 1 + end + + obj.order = options.order; + end + + function signal = precode(obj, signal, options) + arguments + obj + signal + options.M = [] + end + + [data, issignal, signalclass] = obj.unpackSignal(signal); + M = obj.resolveM(data, options.M); + a = obj.amplitudeToIndex(data, M); + + h = arrayfun(@(k) nchoosek(obj.order, k), 0:obj.order); + u = zeros(size(a)); + state = zeros(1, obj.order); + + start_idx = 1; + if obj.order == 1 + % Match the legacy Duobinary class exactly: keep the first + % precoded symbol at zero state and start the recursion at k=2. + start_idx = 2; + end + + for k = start_idx:numel(a) + u(k) = mod(a(k) - sum(h(2:end).*state), M); + state = [u(k) state(1:end-1)]; + end + + data_out = obj.indexToPamAmplitude(u, M); + signal = obj.packSignal(data_out, issignal, signalclass); + end + + function signal = encode(obj, signal, options) + arguments + obj + signal + options.M = [] + end + + [data, issignal, signalclass] = obj.unpackSignal(signal); + M = obj.resolveM(data, options.M); + u = obj.amplitudeToIndex(data, M); + + h = arrayfun(@(k) nchoosek(obj.order, k), 0:obj.order); + y = zeros(size(u)); + state = zeros(1, obj.order); + center = ((M - 1) * sum(h)) / 2; + + for k = 1:numel(u) + pr_state = [u(k) state]; + y(k) = sum(h .* pr_state) - center; + state = pr_state(1:end-1); + end + + y = y ./ obj.encodedScaling(M, obj.order); + + signal = obj.packSignal(y, issignal, signalclass); + end + + function signal = decode(obj, signal, options) + arguments + obj + signal + options.M = [] + end + + [data, issignal, signalclass] = obj.unpackSignal(signal); + M = obj.resolveEncodedM(data, options.M); + + h = arrayfun(@(k) nchoosek(obj.order, k), 0:obj.order); + idx_to_amp = nan(1, (M-1)*sum(h) + 1); + center = ((M - 1) * sum(h)) / 2; + scaling = obj.encodedScaling(M, obj.order); + + for n = 0:(M^(obj.order+1)-1) + state = zeros(1, obj.order+1); + tmp = n; + for k = 1:numel(state) + state(k) = mod(tmp, M); + tmp = floor(tmp/M); + end + y_idx = sum(h .* state); + idx_to_amp(y_idx + 1) = (y_idx - center) / scaling; + end + + alphabet = unique(idx_to_amp); + a = zeros(size(data)); + + for k = 1:numel(data) + [~, pos] = min(abs(data(k) - alphabet)); + y_idx = find(idx_to_amp == alphabet(pos), 1) - 1; + a(k) = mod(y_idx, M); + end + + data_out = obj.indexToPamAmplitude(a, M); + signal = obj.packSignal(data_out, issignal, signalclass); + end + end + + methods (Access=private) + function [data, issignal, signalclass] = unpackSignal(~, signal) + issignal = isa(signal, 'Signal'); + if issignal + signalclass = signal; + data = signal.signal; + else + signalclass = []; + data = signal; + end + data = double(data(:)); + end + + function signal = packSignal(~, data, issignal, signalclass) + if issignal + signalclass.signal = data; + signal = signalclass; + else + signal = data; + end + end + + function M = resolveM(~, data, M) + if isempty(M) + M = numel(unique(round(data, 12))); + end + end + + function M = resolveEncodedM(obj, data, M) + if isempty(M) + I = numel(unique(round(data, 12))); + if obj.order == 1 + M = (I + 1) / 2; + else + error('Partialresponse:NeedM', ... + 'Specify M when decoding higher-order partial-response signals.'); + end + end + end + + function a = amplitudeToIndex(obj, data, M) + levels = obj.pamLevels(M); + scaling = obj.pamScaling(M); + amp = round(data(:) * scaling); + a = (amp + (M - 1)) / 2; + end + + function data = indexToPamAmplitude(obj, a, M) + scaling = obj.pamScaling(M); + data = (2*a(:) - (M - 1)) / scaling; + end + + function levels = pamLevels(~, M) + levels = -(M-1):2:(M-1); + end + + function scaling = pamScaling(~, M) + try + mapper = PAMmapper(M, 0); + scaling = mapper.scaling; + catch ME + error('Partialresponse:UnsupportedM', ... + 'Unsupported PAM order for Partialresponse: %s', ME.message); + end + end + + function scaling = encodedScaling(~, M, order) + h = arrayfun(@(k) nchoosek(order, k), 0:order); + center = ((M - 1) * sum(h)) / 2; + y = zeros(M^(order+1), 1); + + for n = 0:(numel(y)-1) + state = zeros(1, order+1); + tmp = n; + for k = 1:numel(state) + state(k) = mod(tmp, M); + tmp = floor(tmp/M); + end + y(n + 1) = sum(h .* state) - center; + end + + scaling = sqrt(mean(y.^2)); + end + end +end diff --git a/Theory/Mapping_Coding/partial_response/duobinary_back2back.m b/Theory/Mapping_Coding/partial_response/duobinary_back2back.m index d2b6e33..94cc818 100644 --- a/Theory/Mapping_Coding/partial_response/duobinary_back2back.m +++ b/Theory/Mapping_Coding/partial_response/duobinary_back2back.m @@ -1,9 +1,8 @@ - - for M = [2 4 8] bits = Signalgenerator("form", signalform.prms,"M", M,"order", 15).process(); - symbols = PAMmapper(M,0).map(bits); + mapper = PAMmapper(M,0); + symbols = mapper.map(bits); symbols_pre = Duobinary().precode(symbols); @@ -11,8 +10,31 @@ for M = [2 4 8] symbols_rx = Duobinary().decode(symbols_db); - % Vergleichen von b(n) und d_dec(n) bits_rx = PAMmapper(M,0).demap(symbols_rx); - [~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1); - disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]); -end \ No newline at end of file + [~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); + disp(['Class BER: ',sprintf('%.1E',ber),' - PAM-',num2str(M)]); + + symbols_pre_pr = Partialresponse("order",1).precode(symbols,"M",M); + symbols_db_pr = Partialresponse("order",1).encode(symbols_pre_pr,"M",M); + symbols_rx_pr = Partialresponse("order",1).decode(symbols_db_pr,"M",M); + + bits_rx_pr = mapper.demap(symbols_rx_pr); + [~,~,ber_pr,~] = calc_ber(bits.signal,bits_rx_pr.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1); + disp(['Partialresponse BER: ',sprintf('%.1E',ber_pr),' - PAM-',num2str(M)]); + + pre_match = isequaln(symbols_pre.signal, symbols_pre_pr.signal); + enc_match = isequaln(symbols_db.signal, symbols_db_pr.signal); + dec_match = isequaln(symbols_rx.signal, symbols_rx_pr.signal); + + pre_maxdiff = max(abs(symbols_pre.signal - symbols_pre_pr.signal)); + enc_maxdiff = max(abs(symbols_db.signal - symbols_db_pr.signal)); + dec_maxdiff = max(abs(symbols_rx.signal - symbols_rx_pr.signal)); + + disp(['Precode match: ',num2str(pre_match), ... + ' | max abs diff: ',sprintf('%.3g',pre_maxdiff)]); + disp(['Encode match: ',num2str(enc_match), ... + ' | max abs diff: ',sprintf('%.3g',enc_maxdiff)]); + disp(['Decode match: ',num2str(dec_match), ... + ' | max abs diff: ',sprintf('%.3g',dec_maxdiff)]); + disp('---'); +end