Files
imdd_silas/Classes/04_DSP/Coding/Partialresponse.m
Silas Oettinghaus 56b48095db PR stuff
2026-04-29 10:17:09 +02:00

211 lines
6.3 KiB
Matlab

classdef Partialresponse
%PARTIALRESPONSE Generalized symbol-domain partial-response coding.
properties
M = []
order = 1
end
methods
function obj = Partialresponse(options)
arguments
options.M = [];
options.order = 1;
end
fn = fieldnames(options);
for n = 1:numel(fn)
obj.(fn{n}) = options.(fn{n});
end
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(obj, data, M)
if isempty(M)
M = obj.M;
end
if isempty(M)
M = numel(unique(round(data, 12)));
end
end
function M = resolveEncodedM(obj, data, M)
if isempty(M)
M = obj.M;
end
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