Add new signal generator method that only creates the PRBS
This commit is contained in:
@@ -1,12 +1,63 @@
|
||||
classdef Signalgenerator
|
||||
%NAME Summary of this class goes here
|
||||
% Detailed explanation goes here
|
||||
%SIGNALGENERATOR Generate simple information-level test sequences.
|
||||
%
|
||||
% This class creates Informationsignal objects for quick simulations,
|
||||
% mapper checks, and theory scripts. Use the "form" option to select
|
||||
% the generated sequence type.
|
||||
%
|
||||
% Examples:
|
||||
%
|
||||
% % Sine wave
|
||||
% sig = Signalgenerator( ...
|
||||
% "form", signalform.sine, ...
|
||||
% "length", 1024, ...
|
||||
% "fs", 1000, ...
|
||||
% "fsig", 50).process();
|
||||
%
|
||||
% % Deterministic random bit matrix, 2 bits per row
|
||||
% bits = Signalgenerator( ...
|
||||
% "form", signalform.random, ...
|
||||
% "length", 1024, ...
|
||||
% "dimension", 2, ...
|
||||
% "randkey", 3).process();
|
||||
%
|
||||
% % PRMS bit matrix for PAM-4 mapping. If length is omitted, the
|
||||
% % output length is derived from order, as in the old PAMsource.
|
||||
% bits = Signalgenerator( ...
|
||||
% "form", signalform.prms, ...
|
||||
% "dimension", 2, ...
|
||||
% "order", 7).process();
|
||||
% symbols = PAMmapper(4, 0).map(bits);
|
||||
%
|
||||
% % PRMS for PAM-6: 5 bits map to 2 PAM-6 symbols
|
||||
% bits = Signalgenerator( ...
|
||||
% "form", signalform.prms, ...
|
||||
% "dimension", 5, ...
|
||||
% "order", 7).process();
|
||||
% bitVector = reshape(bits.signal.', [], 1);
|
||||
% symbols = PAMmapper(6, 0).map(bitVector);
|
||||
%
|
||||
% % Map, demap, and check BER
|
||||
% mapper = PAMmapper(8, 0);
|
||||
% bits = Signalgenerator( ...
|
||||
% "form", signalform.prms, ...
|
||||
% "length", 1024, ... % explicit output length override
|
||||
% "dimension", 3, ...
|
||||
% "order", 5).process();
|
||||
% symbols = mapper.map(bits);
|
||||
% rxBits = mapper.demap(symbols);
|
||||
% [checkedBits, errors, ber] = calc_ber(rxBits, bits.signal);
|
||||
|
||||
properties(Access=public)
|
||||
form
|
||||
length
|
||||
fs
|
||||
fsig
|
||||
dimension
|
||||
order
|
||||
randkey
|
||||
skip
|
||||
bruijn
|
||||
|
||||
end
|
||||
|
||||
@@ -17,9 +68,14 @@ classdef Signalgenerator
|
||||
|
||||
arguments
|
||||
options.form signalform = signalform.sine
|
||||
options.length double = 1024
|
||||
options.length double = []
|
||||
options.fs double = 1000 %Hz sampling
|
||||
options.fsig double = 50 % Hz fundamental frex e.g. of the sine or sawtooth
|
||||
options.dimension double = 1
|
||||
options.order double = 7
|
||||
options.randkey double = 0
|
||||
options.skip double = 0
|
||||
options.bruijn logical = false
|
||||
end
|
||||
|
||||
%
|
||||
@@ -30,6 +86,10 @@ classdef Signalgenerator
|
||||
end
|
||||
end
|
||||
|
||||
if isempty(obj.length)
|
||||
obj.length = obj.default_length();
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function signalclass_out = process(obj)
|
||||
@@ -69,7 +129,12 @@ classdef Signalgenerator
|
||||
% Generate sine wave
|
||||
signal = A * sin(2*pi*f*t);
|
||||
case signalform.noise
|
||||
|
||||
s = RandStream('twister','Seed',obj.randkey);
|
||||
signal = randn(s, 1, obj.length);
|
||||
case signalform.random
|
||||
signal = obj.build_random_data();
|
||||
case signalform.prms
|
||||
signal = obj.build_prms_data();
|
||||
end
|
||||
|
||||
|
||||
@@ -82,6 +147,317 @@ classdef Signalgenerator
|
||||
% Cant be seen from outside! So put all your functions here that can/
|
||||
% shall not be called from outside
|
||||
|
||||
function signal = build_random_data(obj)
|
||||
arguments(Input)
|
||||
obj
|
||||
end
|
||||
|
||||
s = RandStream('twister','Seed',obj.randkey);
|
||||
signal = randi(s, [0 1], obj.length, obj.dimension);
|
||||
end
|
||||
|
||||
%BUILD_PRMS_DATA Generate pseudo-random multi-level sequence bits.
|
||||
%
|
||||
% The output is a binary matrix with size:
|
||||
% obj.length x obj.dimension
|
||||
%
|
||||
% Each row is one generated bit group / symbol time. Each column is
|
||||
% one parallel PRMS bit stream. For conventional PAM formats this
|
||||
% means:
|
||||
% PAM-2: dimension = 1
|
||||
% PAM-4: dimension = 2
|
||||
% PAM-8: dimension = 3
|
||||
%
|
||||
% PAM-6 is special in this codebase: the mapper consumes 5 bits and
|
||||
% maps them to two PAM-6 symbols. Use dimension = 5, then flatten
|
||||
% the output before calling PAMmapper(6, ...).map(...).
|
||||
%
|
||||
% Relevant Signalgenerator options:
|
||||
% length Number of generated PRMS bit groups / rows.
|
||||
% dimension Number of parallel bit streams per row.
|
||||
% order User-facing sequence order, matching the old
|
||||
% PAMsource behavior. If length is omitted, it sets the
|
||||
% output length. Internally it is converted to the PRMS
|
||||
% order per stream.
|
||||
% Default output length is 2^(order-1), except for
|
||||
% PAM-6-style dimension = 5 where it is 2^(order-2).
|
||||
% skip Number of PRMS symbols to advance before output. This
|
||||
% is useful when different blocks should use shifted
|
||||
% sections of the same deterministic sequence.
|
||||
% bruijn If true, enables de Bruijn-style zero-symbol insertion
|
||||
% as in the legacy MOVE-IT generator.
|
||||
%
|
||||
% The implementation mirrors the legacy MOVE-IT/prms_c register
|
||||
% logic in plain MATLAB, so no mex build is required.
|
||||
|
||||
function signal = build_prms_data(obj)
|
||||
arguments(Input)
|
||||
obj
|
||||
end
|
||||
|
||||
obj.validate_prms_parameters();
|
||||
|
||||
state.dimension = obj.dimension;
|
||||
state.order = obj.internal_prms_order();
|
||||
state.periodicity = state.dimension * state.order;
|
||||
state.bl = obj.length;
|
||||
state.bruijn = obj.bruijn;
|
||||
state.bruijn_counter = -1;
|
||||
state.srgtaps = Signalgenerator.srgtap_masks(state.periodicity);
|
||||
mat_table = Signalgenerator.build_prms_mapping(state.dimension);
|
||||
state.mat_table = mat_table * 2.^(0:state.dimension-1).';
|
||||
state.reg_mask = 2^state.periodicity - 1;
|
||||
state.data_mask = 2^(state.periodicity - 1);
|
||||
|
||||
tmp_reg = 2^49 - 1;
|
||||
tmp_reg = Signalgenerator.advance_prbs_register(tmp_reg, state.srgtaps, 1, state.reg_mask);
|
||||
state.reg = zeros(state.dimension, 1);
|
||||
state.reg(1) = tmp_reg;
|
||||
|
||||
modulo_mask = 2^state.dimension - 1;
|
||||
offset = (2^state.periodicity - 1) / modulo_mask;
|
||||
for n = 2:state.dimension
|
||||
tmp_reg = Signalgenerator.advance_prbs_register(tmp_reg, state.srgtaps, offset, state.reg_mask);
|
||||
state.reg(n) = tmp_reg;
|
||||
end
|
||||
|
||||
if obj.skip > 0
|
||||
state = Signalgenerator.advance_prms_state(state, obj.skip);
|
||||
end
|
||||
|
||||
[data_out, ~] = Signalgenerator.generate_prms_block(state, state.bl);
|
||||
signal = data_out.';
|
||||
end
|
||||
|
||||
function validate_prms_parameters(obj)
|
||||
if obj.length < 1 || fix(obj.length) ~= obj.length
|
||||
error("Signalgenerator:InvalidLength", ...
|
||||
"PRMS length must be a positive integer.");
|
||||
end
|
||||
if obj.dimension < 1 || fix(obj.dimension) ~= obj.dimension
|
||||
error("Signalgenerator:InvalidDimension", ...
|
||||
"PRMS dimension must be a positive integer.");
|
||||
end
|
||||
if obj.order < 1 || fix(obj.order) ~= obj.order
|
||||
error("Signalgenerator:InvalidOrder", ...
|
||||
"PRMS order must be a positive integer.");
|
||||
end
|
||||
if obj.skip < 0 || fix(obj.skip) ~= obj.skip
|
||||
error("Signalgenerator:InvalidSkip", ...
|
||||
"PRMS skip must be a nonnegative integer.");
|
||||
end
|
||||
if obj.dimension * obj.internal_prms_order() > 48
|
||||
error("Signalgenerator:UnsupportedOrder", ...
|
||||
"PRMS dimension * internal PRMS order must be <= 48.");
|
||||
end
|
||||
end
|
||||
|
||||
function length = default_length(obj)
|
||||
switch obj.form
|
||||
case {signalform.random, signalform.prms}
|
||||
if obj.dimension == 5
|
||||
length = 2^max(0, obj.order - 2);
|
||||
else
|
||||
length = 2^max(0, obj.order - 1);
|
||||
end
|
||||
otherwise
|
||||
length = 1024;
|
||||
end
|
||||
end
|
||||
|
||||
function prms_order = internal_prms_order(obj)
|
||||
if 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
|
||||
|
||||
|
||||
end
|
||||
|
||||
methods (Static, Access=private)
|
||||
function state = advance_prms_state(state, number_symbols)
|
||||
[~, state] = Signalgenerator.generate_prms_block(state, number_symbols);
|
||||
end
|
||||
|
||||
function [data_out, state] = generate_prms_block(state, block_length)
|
||||
data_out = zeros(state.dimension, block_length);
|
||||
counter = 0;
|
||||
|
||||
while counter < block_length
|
||||
if state.bruijn == 2
|
||||
counter = counter + 1;
|
||||
state.bruijn = 1;
|
||||
continue;
|
||||
end
|
||||
|
||||
for n = 1:state.dimension
|
||||
prms_tmp_value = false;
|
||||
for m = 1:state.dimension
|
||||
prms_tmp_value = xor(prms_tmp_value, ...
|
||||
state.reg(m) >= state.data_mask && ...
|
||||
bitand(state.mat_table(m), 2^(n-1)) ~= 0);
|
||||
end
|
||||
data_out(n, counter + 1) = prms_tmp_value;
|
||||
end
|
||||
|
||||
for n = 1:state.dimension
|
||||
state.reg(n) = Signalgenerator.advance_prbs_register( ...
|
||||
state.reg(n), state.srgtaps, 1, state.reg_mask);
|
||||
end
|
||||
|
||||
counter = counter + 1;
|
||||
|
||||
if state.bruijn == 1
|
||||
if state.bruijn_counter < 0
|
||||
if any([~sum(data_out(:, counter)) state.order == 1])
|
||||
state.bruijn_counter = state.bruijn_counter - 1;
|
||||
if any([state.bruijn_counter == -state.order state.order == 1])
|
||||
state.bruijn_counter = 2^state.periodicity - 1;
|
||||
state.bruijn = 2;
|
||||
end
|
||||
else
|
||||
state.bruijn_counter = -1;
|
||||
end
|
||||
else
|
||||
state.bruijn_counter = state.bruijn_counter - 1;
|
||||
if state.bruijn_counter == 0
|
||||
state.bruijn_counter = 2^state.periodicity - 1;
|
||||
state.bruijn = 2;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function reg = advance_prbs_register(reg, tap_masks, number_steps, reg_mask)
|
||||
for idx = 1:number_steps
|
||||
feedback = false;
|
||||
for tap_idx = 1:numel(tap_masks)
|
||||
if tap_masks(tap_idx) ~= 0
|
||||
feedback = xor(feedback, bitand(reg, tap_masks(tap_idx)) ~= 0);
|
||||
end
|
||||
end
|
||||
|
||||
reg = bitand(reg * 2 + double(feedback), reg_mask);
|
||||
end
|
||||
end
|
||||
|
||||
function mat_table = build_prms_mapping(dimension)
|
||||
if dimension > 2
|
||||
bchpolynomial = zeros(1, dimension + 1);
|
||||
bchpolynomial([1 Signalgenerator.srgtaps(dimension) + 1]) = 1;
|
||||
betatable = fliplr(Signalgenerator.cyclgen_local(2^dimension - 1, bchpolynomial).');
|
||||
elseif dimension == 2
|
||||
betatable = [0 1; 1 0; 1 1];
|
||||
else
|
||||
betatable = 1;
|
||||
end
|
||||
|
||||
binvec = 2.^(dimension-1:-1:0);
|
||||
modulo_mask = 2^dimension - 1;
|
||||
betatable_sort_forward = sum(repmat(binvec, 2^dimension - 1, 1) .* betatable, 2);
|
||||
[~, betatable_sort_inverse] = sort(betatable_sort_forward);
|
||||
betatable_sort_inverse = betatable_sort_inverse - 1;
|
||||
|
||||
dividend = zeros(dimension + 1, 2);
|
||||
dividend([dimension - Signalgenerator.srgtaps(dimension) + 1 end], 1) = 1;
|
||||
|
||||
h = zeros(dimension, 2);
|
||||
for digit = 1:dimension
|
||||
h(digit, 1:2) = dividend(digit, 1:2);
|
||||
dividend(digit, 1:2) = [0 0];
|
||||
|
||||
newdiv_binary = xor( ...
|
||||
betatable(dividend(digit + 1, 2) + 1, :) * dividend(digit + 1, 1), ...
|
||||
betatable(rem(h(digit, 2) + 1, modulo_mask) + 1, :) * h(digit, 1));
|
||||
dividend(digit + 1, 1) = sum(newdiv_binary) > 0;
|
||||
|
||||
if (digit ~= dimension) && dividend(digit + 1, 1)
|
||||
dividend(digit + 1, 2) = dividend(digit + 1, 1) * ...
|
||||
betatable_sort_inverse(sum(newdiv_binary .* binvec));
|
||||
else
|
||||
dividend(digit + 1, 2) = 0;
|
||||
end
|
||||
end
|
||||
|
||||
mat_table = betatable(h(:, 2) + 1, :);
|
||||
end
|
||||
|
||||
function code = cyclgen_local(columns, polynomial)
|
||||
rows = log2(columns + 1);
|
||||
code = zeros(rows, columns);
|
||||
code(1, 1) = 1;
|
||||
|
||||
for s = 2:columns
|
||||
code(:, s) = [0; code(1:end-1, s-1)];
|
||||
|
||||
if code(end, s-1)
|
||||
code(:, s) = rem(code(:, s) + polynomial(1:end-1).', 2);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function taps = srgtaps(inx)
|
||||
data = {[1] ...
|
||||
[2 1] ...
|
||||
[3 1] ...
|
||||
[4 1] ...
|
||||
[5 2] ...
|
||||
[6 1] ...
|
||||
[7 1] ...
|
||||
[8 7 2 1] ...
|
||||
[9 4] ...
|
||||
[10 3] ...
|
||||
[11 2] ...
|
||||
[12 10 2 1] ...
|
||||
[13 8 5 3] ...
|
||||
[14 12 11 1] ...
|
||||
[15 1] ...
|
||||
[16 15 12 10] ...
|
||||
[17 3] ...
|
||||
[18 7] ...
|
||||
[19 10 9 3] ...
|
||||
[20 3] ...
|
||||
[21 2] ...
|
||||
[22 1] ...
|
||||
[23 5] ...
|
||||
[24 11 5 2] ...
|
||||
[25 3] ...
|
||||
[26 23 15 13] ...
|
||||
[27 23 22 17] ...
|
||||
[28 3] ...
|
||||
[29 2] ...
|
||||
[30 27 10 9] ...
|
||||
[31 3] ...
|
||||
[32 16 7 2] ...
|
||||
[33 13] ...
|
||||
[34 17 12 8] ...
|
||||
[35 2] ...
|
||||
[36 11] ...
|
||||
[37 22 14 2] ...
|
||||
[38 27 6 5] ...
|
||||
[39 4] ...
|
||||
[40 29 27 23] ...
|
||||
[41 3] ...
|
||||
[42 34 31 30] ...
|
||||
[43 27 22 5] ...
|
||||
[44 39 35 18] ...
|
||||
[45 39 28 4] ...
|
||||
[46 40 31 18] ...
|
||||
[47 5] ...
|
||||
[48 19 9 1]};
|
||||
|
||||
taps = data{inx};
|
||||
end
|
||||
|
||||
function masks = srgtap_masks(inx)
|
||||
taps = Signalgenerator.srgtaps(inx);
|
||||
masks = zeros(1, 4);
|
||||
masks(1:numel(taps)) = 2.^(taps - 1);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user