|
| 1 | +%srsPRSGeneratorUnittest Unit tests for PRS generator functions. |
| 2 | +% This class implements unit tests for the PRS generator functions using the |
| 3 | +% matlab.unittest framework. The simplest use consists in creating an object with |
| 4 | +% testCase = srsPRSGeneratorUnittest |
| 5 | +% and then running all the tests with |
| 6 | +% testResults = testCase.run |
| 7 | +% |
| 8 | +% srsPRSGeneratorUnittest Properties (Constant): |
| 9 | +% |
| 10 | +% srsBlock - The tested block (i.e., 'ptrs_pdsch_generator'). |
| 11 | +% srsBlockType - The type of the tested block, including layer |
| 12 | +% (i.e., 'phy/upper/signal_processors/prs'). |
| 13 | +% |
| 14 | +% srsPRSGeneratorUnittest Properties (ClassSetupParameter): |
| 15 | +% |
| 16 | +% outputPath - Path to the folder where the test results are stored. |
| 17 | +% |
| 18 | +% srsPRSGeneratorUnittest Properties (TestParameter): |
| 19 | +% |
| 20 | +% Numerology - Defines the subcarrier spacing (0, 1). |
| 21 | +% DurationAndCombSize - Combinations of duration and comb size. |
| 22 | +% |
| 23 | +% srsPRSGeneratorUnittest Methods (TestTags = {'testvector'}): |
| 24 | +% |
| 25 | +% testvectorGenerationCases - Generates a test vector according to the provided |
| 26 | +% parameters. |
| 27 | +% |
| 28 | +% srsPRSGeneratorUnittest Methods (Access = protected): |
| 29 | +% |
| 30 | +% addTestIncludesToHeaderFile - Adds include directives to the test header file. |
| 31 | +% addTestDefinitionToHeaderFile - Adds details (e.g., type/variable declarations) |
| 32 | +% to the test header file. |
| 33 | +% |
| 34 | +% See also matlab.unittest. |
| 35 | + |
| 36 | +% Copyright 2021-2024 Software Radio Systems Limited |
| 37 | +% |
| 38 | +% This file is part of srsRAN-matlab. |
| 39 | +% |
| 40 | +% srsRAN-matlab is free software: you can redistribute it and/or |
| 41 | +% modify it under the terms of the BSD 2-Clause License. |
| 42 | +% |
| 43 | +% srsRAN-matlab is distributed in the hope that it will be useful, |
| 44 | +% but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 45 | +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 46 | +% BSD 2-Clause License for more details. |
| 47 | +% |
| 48 | +% A copy of the BSD 2-Clause License can be found in the LICENSE |
| 49 | +% file in the top-level directory of this distribution. |
| 50 | + |
| 51 | +classdef srsPRSGeneratorUnittest < srsTest.srsBlockUnittest |
| 52 | + properties (Constant) |
| 53 | + %Name of the tested block. |
| 54 | + srsBlock = 'prs_generator' |
| 55 | + |
| 56 | + %Type of the tested block. |
| 57 | + srsBlockType = 'phy/upper/signal_processors/prs' |
| 58 | + |
| 59 | + %Minimum bandwidth in PRB. Given in TS38.214 Section 5.1.6.5. |
| 60 | + MinNumRB = 24; |
| 61 | + |
| 62 | + %Maximum bandwidth in PRB. Given in TS38.214 Section 5.1.6.5. |
| 63 | + MaxNumRB = 272; |
| 64 | + |
| 65 | + %Bandwidth granularity in PRB. Given in TS38.214 Section 5.1.6.5. |
| 66 | + ResNumRB = 4; |
| 67 | + |
| 68 | + end |
| 69 | + |
| 70 | + properties (Hidden) |
| 71 | + randomizeTestvector |
| 72 | + end |
| 73 | + |
| 74 | + properties (ClassSetupParameter) |
| 75 | + %Path to results folder (old 'ptrs_pdsch_generator' tests will be erased). |
| 76 | + outputPath = {['testPRS', char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss'))]} |
| 77 | + end |
| 78 | + |
| 79 | + properties (TestParameter) |
| 80 | + %Numerology, defines the subcarrier spacing (15kHz and 30kHz). |
| 81 | + Numerology = {0, 1} |
| 82 | + |
| 83 | + %Valid combinations of comb size and duration. Valid combinations |
| 84 | + %are given in TS38.211 Section 7.4.1.7.3. |
| 85 | + DurationAndCombSize = {[2, 2], [4, 2], [6, 2], [12, 2], [4, 4], [12, 4], [6, 6], [12, 6], [12, 12]} |
| 86 | + end |
| 87 | + |
| 88 | + methods (Access = protected) |
| 89 | + function addTestIncludesToHeaderFile(~, fileID) |
| 90 | + %addTestIncludesToHeaderFile Adds include directives to the test header file. |
| 91 | + fprintf(fileID, '#include "srsran/phy/upper/signal_processors/prs/prs_generator.h"\n'); |
| 92 | + fprintf(fileID, '#include "srsran/phy/upper/signal_processors/prs/prs_generator_configuration.h"\n'); |
| 93 | + fprintf(fileID, '#include "srsran/support/file_vector.h"\n'); |
| 94 | + fprintf(fileID, '#include "../../../support/resource_grid_test_doubles.h"\n'); |
| 95 | + fprintf(fileID, '#include "srsran/ran/precoding/precoding_codebooks.h"\n'); |
| 96 | + end |
| 97 | + |
| 98 | + function addTestDefinitionToHeaderFile(~, fileID) |
| 99 | + %addTestDetailsToHeaderFile Adds details (e.g., type/variable declarations) to the test header file. |
| 100 | + fprintf(fileID, 'struct test_case_t {\n'); |
| 101 | + fprintf(fileID, ' prs_generator_configuration config;\n'); |
| 102 | + fprintf(fileID, ' file_vector<resource_grid_writer_spy::expected_entry_t> symbols;\n'); |
| 103 | + fprintf(fileID, '};\n'); |
| 104 | + end |
| 105 | + |
| 106 | + function initializeClassImpl(obj) |
| 107 | + obj.randomizeTestvector = randperm(1008); |
| 108 | + end |
| 109 | + end % of methods (Access = protected) |
| 110 | + |
| 111 | + methods (Test, TestTags = {'testvector'}) |
| 112 | + function testvectorGenerationCases(testCase, Numerology, DurationAndCombSize) |
| 113 | + %testvectorGenerationCases Generates a test vector for the given Numerology, |
| 114 | + % NumLayers, FrequencyDensity, TimeDensity, and |
| 115 | + % REOffset. Other parameters are selected randomly. |
| 116 | + |
| 117 | + import srsTest.helpers.cellarray2str |
| 118 | + import srsTest.helpers.writeResourceGridEntryFile |
| 119 | + import srsTest.helpers.symbolAllocationMask2string |
| 120 | + import srsTest.helpers.RBallocationMask2string |
| 121 | + |
| 122 | + % Derive test parameters. |
| 123 | + subcarrierSpacing = 15 * pow2(Numerology); |
| 124 | + numPRSSymbols = DurationAndCombSize(1); |
| 125 | + combSize = DurationAndCombSize(2); |
| 126 | + |
| 127 | + % Generate a unique test ID. |
| 128 | + testID = testCase.generateTestID; |
| 129 | + |
| 130 | + % Grid size, use maximum. |
| 131 | + nSizeGrid = 272; |
| 132 | + |
| 133 | + % Select random parameters. |
| 134 | + NCellID = randi([0, 504]); |
| 135 | + REOffset = randi([0 combSize - 1]); |
| 136 | + numRB = randi([testCase.MinNumRB / testCase.ResNumRB, ... |
| 137 | + testCase.MaxNumRB / testCase.ResNumRB]) * testCase.ResNumRB; |
| 138 | + symbolStart = randi([0, 14 - numPRSSymbols]); |
| 139 | + RBOffset = randi([0, nSizeGrid - numRB]); |
| 140 | + NPRSID = randi([0 4095]); |
| 141 | + nSlot = randi([0 (10 * pow2(Numerology) - 1)]); |
| 142 | + nFrame = randi([0 1023]); |
| 143 | + amplitude = 10 * (rand() + 1); |
| 144 | + nStartGrid = randi([0, 2176 - numRB - RBOffset]); |
| 145 | + |
| 146 | + % Fix parameters. |
| 147 | + cyclicPrefix = 'normal'; |
| 148 | + |
| 149 | + % Prepare carrier configuration. |
| 150 | + carrier = nrCarrierConfig( ... |
| 151 | + NCellID=NCellID,... |
| 152 | + SubcarrierSpacing=subcarrierSpacing,... |
| 153 | + CyclicPrefix=cyclicPrefix,... |
| 154 | + NSizeGrid=nSizeGrid,... |
| 155 | + NStartGrid=nStartGrid,... |
| 156 | + NSlot=nSlot,... |
| 157 | + NFrame=nFrame); |
| 158 | + |
| 159 | + % Prepare PRS configuration. |
| 160 | + PRS = nrPRSConfig(... |
| 161 | + NumPRSSymbols=numPRSSymbols,... |
| 162 | + SymbolStart=symbolStart,... |
| 163 | + NumRB=numRB,... |
| 164 | + RBOffset=RBOffset,... |
| 165 | + CombSize=combSize,... |
| 166 | + REOffset=REOffset,... |
| 167 | + NPRSID=NPRSID); |
| 168 | + |
| 169 | + % Call the PRS symbol processor MATLAB functions. |
| 170 | + symbols = nrPRS(carrier, PRS); |
| 171 | + indices = nrPRSIndices(carrier, PRS, ... |
| 172 | + IndexStyle='subscript', IndexBase='0based'); |
| 173 | + |
| 174 | + % Write each complex symbol along with their associated indices to |
| 175 | + % a binary file. |
| 176 | + testCase.saveDataFile('_test_output', testID, ... |
| 177 | + @writeResourceGridEntryFile, amplitude * symbols, indices); |
| 178 | + |
| 179 | + % Convert parameters to srsRAN types. |
| 180 | + slotPointStr = cellarray2str({Numerology, nFrame, ... |
| 181 | + floor(nSlot / carrier.SlotsPerSubframe), ... |
| 182 | + rem(nSlot, carrier.SlotsPerSubframe)}, true); |
| 183 | + cyclicPrefixStr = ['cyclic_prefix::' upper(cyclicPrefix)]; |
| 184 | + combSizeStr = ['static_cast<prs_comb_size>(' num2str(combSize) ')']; |
| 185 | + durationStr = ['static_cast<prs_num_symbols>(' num2str(numPRSSymbols) ')']; |
| 186 | + rbStart = carrier.NStartGrid + PRS.RBOffset; |
| 187 | + freqAllocConfig = cellarray2str({PRS.RBOffset, PRS.RBOffset + PRS.NumRB}, true); |
| 188 | + powerOffsetdB = 20 * log10(amplitude); |
| 189 | + precodingStr = 'precoding_configuration::make_wideband(make_identity(1))'; |
| 190 | + |
| 191 | + configCell = {... |
| 192 | + slotPointStr, ... % slot |
| 193 | + cyclicPrefixStr, ... % cyclic_prefix |
| 194 | + PRS.NPRSID, ... % n_id_prs |
| 195 | + combSizeStr, ... % comb_size |
| 196 | + REOffset, ... % comb_offset |
| 197 | + durationStr, ... % duration |
| 198 | + symbolStart, ... % start_symbol |
| 199 | + rbStart, ... % prb_start |
| 200 | + freqAllocConfig, ... % freq_alloc |
| 201 | + powerOffsetdB, ... % power_offset_dB |
| 202 | + precodingStr ... % precoding |
| 203 | + }; |
| 204 | + |
| 205 | + % Generate the test case entry. |
| 206 | + testCaseString = testCase.testCaseToString(testID, configCell, ... |
| 207 | + true, '_test_output'); |
| 208 | + |
| 209 | + % Add the test to the file header. |
| 210 | + testCase.addTestToHeaderFile(testCase.headerFileID, testCaseString); |
| 211 | + end % of function testvectorGenerationCases |
| 212 | + end % of methods (Test, TestTags = {'testvector'}) |
| 213 | +end % of classdef srsPRSGeneratorUnittest |
0 commit comments