classdef Pulseformer_test < IMDDTestCase methods (Test, TestTags = {'unit', 'fast', 'transmit', 'pulseformer'}) function constructorStoresConfiguredProperties(testCase) pf = Pulseformer( ... "fdac", 64e9, ... "fsym", 16e9, ... "pulse", pulseform.rrc, ... "pulselength", 12, ... "alpha", 0.2, ... "matched", 1); testCase.verifyEqual(pf.fdac, 64e9); testCase.verifyEqual(pf.fsym, 16e9); testCase.verifyEqual(pf.pulse, pulseform.rrc); testCase.verifyEqual(pf.pulselength, 12); testCase.verifyEqual(pf.alpha, 0.2); testCase.verifyEqual(pf.matched, 1); end function processUsesSymbolRateFallbackAndUpdatesSignalMetadata(testCase) fsym = 16e9; fdac = 64e9; tx = makeInformationsignal([1; 0; -1; 1; 1; 0], []); pf = Pulseformer( ... "fdac", fdac, ... "fsym", fsym, ... "pulse", pulseform.rc, ... "pulselength", 8, ... "alpha", 0.35, ... "matched", 0); rx = pf.process(tx); expected = expectedPulseformerOutput(pf, tx); testCase.verifyClass(rx, 'Informationsignal'); testCase.verifyEqual(rx.signal, expected, "AbsTol", 1e-12); testCase.verifyEqual(rx.fs, fdac); testCase.verifyEqual(height(rx.logbook), height(tx.logbook) + 1); testCase.verifyEqual(string(rx.logbook.Description(end)), "Applied Pulseshaping"); end function matchedAndUnmatchedProcessingPreserveTheSameRealWaveform(testCase) tx = makeInformationsignal([1; 0; 0; -1; 1; -1; 0; 1], 16e9); unmatched = Pulseformer( ... "fdac", 32e9, ... "fsym", 16e9, ... "pulse", pulseform.rrc, ... "pulselength", 10, ... "alpha", 0.15, ... "matched", 0); matched = Pulseformer( ... "fdac", 32e9, ... "fsym", 16e9, ... "pulse", pulseform.rrc, ... "pulselength", 10, ... "alpha", 0.15, ... "matched", 1); rxUnmatched = unmatched.process(tx); rxMatched = matched.process(tx); testCase.verifyEqual(length(rxUnmatched.signal), length(rxMatched.signal)); testCase.verifyEqual(rxUnmatched.signal, rxMatched.signal, "AbsTol", 1e-12); testCase.verifyEqual(rxUnmatched.signal, expectedPulseformerOutput(unmatched, tx), "AbsTol", 1e-12); testCase.verifyEqual(rxMatched.signal, expectedPulseformerOutput(matched, tx), "AbsTol", 1e-12); end function processPreservesLengthForIntegerRateConversion(testCase) tx = makeInformationsignal((1:5).', []); pf = Pulseformer( ... "fdac", 20e9, ... "fsym", 5e9, ... "pulse", pulseform.rc, ... "pulselength", 6, ... "alpha", 0.25, ... "matched", 0); rx = pf.process(tx); testCase.verifyEqual(length(rx.signal), 20); testCase.verifyEqual(rx.fs, 20e9); end end end function sig = makeInformationsignal(values, fs) base = Signal(values); sig = Informationsignal(values, "fs", fs, "logbook", base.logbook); end function expected = expectedPulseformerOutput(pf, signalclass_in) if isprop(signalclass_in, 'fs') && ~isempty(signalclass_in.fs) f_in = signalclass_in.fs; else f_in = pf.fsym; end [p, q] = rat(pf.fdac / f_in); fs_intermediate = f_in * p; sps_filter = fs_intermediate / pf.fsym; if pf.pulse == pulseform.rc filtertype = 'normal'; else filtertype = 'sqrt'; end h = rcosdesign(pf.alpha, pf.pulselength, sps_filter, filtertype); if pf.matched h = conj(fliplr(h)); end data_out_ = upfirdn(signalclass_in.signal, h, p, q); delay_samples_intermediate = (length(h) - 1) / 2; delay_samples_out = delay_samples_intermediate / q; st = floor(delay_samples_out) + 1; len_out = ceil(length(signalclass_in.signal) * p / q); expected = data_out_(st : st + len_out - 1); end