В этом примере показано, как использовать функции 5G Toolbox™, чтобы смоделировать 5G NR физический нисходящий канал совместно использованный канал (PDSCH) ссылка, включая все шаги от транспортной генерации блока до декодирования бита в конце приемника.
Эта схема показывает нисходящий канал, совместно использованный канал (DL-SCH) и PDSCH передают и получают цепь обработки.
В этом примере показано, как смоделировать эти элементы симуляции уровня ссылки.
Кодирование DL-SCH
Гибридный ARQ (HARQ) управление
Кодирование PDSCH
Несколько - вводят, несколько - выводят (MIMO) предварительное кодирование
Модуляция OFDM
Канал распространения и шумовое сложение
Временная синхронизация
Демодуляция OFDM
Оценка канала и эквализация
Декодирование PDSCH
Декодирование DL-SCH
Для примера того, как использовать симуляцию уровня ссылки, чтобы измерить пропускную способность, смотрите Пропускную способность NR PDSCH.
Задайте отношение сигнал-шум (SNR), количество пазов, чтобы симулировать, и усовершенствовать флаг оценки канала. Чтобы узнать больше об определении ОСШ, используемом в этом примере, см. Определение ОСШ, Используемое в Симуляциях Ссылки.
SNRdB = 10; % SNR in dB totalNoSlots = 20; % Number of slots to simulate perfectEstimation = false; % Perfect synchronization and channel estimation rng("default"); % Set default random number generator for repeatability
Создайте объект настройки несущей. Этот объект управляет нумерологией, такой как, расстояние между поднесущими, полоса пропускания и длина циклического префикса (CP). Этот пример использует набор по умолчанию свойств.
carrier = nrCarrierConfig
carrier = nrCarrierConfig with properties: NCellID: 1 SubcarrierSpacing: 15 CyclicPrefix: 'normal' NSizeGrid: 52 NStartGrid: 0 NSlot: 0 NFrame: 0 Read-only properties: SymbolsPerSlot: 14 SlotsPerSubframe: 1 SlotsPerFrame: 10
Создайте объект настройки PDSCH. Задайте (16-QAM) схему модуляции и количество слоев (2). Выделите все блоки ресурса (RBS) PDSCH (полное выделение полосы). Можно также задать другие параметры выделения времени и настройки (DM-RS) опорного сигнала демодуляции в этом объекте.
pdsch = nrPDSCHConfig; pdsch.Modulation = "16QAM"; pdsch.NumLayers = 2; pdsch.PRBSet = 0:carrier.NSizeGrid-1; % Full band allocation
Отобразите параметры PDSCH.
pdsch
pdsch = nrPDSCHConfig with properties: NSizeBWP: [] NStartBWP: [] ReservedPRB: {[1x1 nrPDSCHReservedConfig]} ReservedRE: [] Modulation: '16QAM' NumLayers: 2 MappingType: 'A' SymbolAllocation: [0 14] PRBSet: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... ] VRBToPRBInterleaving: 0 VRBBundleSize: 2 NID: [] RNTI: 1 DMRS: [1x1 nrPDSCHDMRSConfig] EnablePTRS: 0 PTRS: [1x1 nrPDSCHPTRSConfig] Read-only properties: NumCodewords: 1
Установите параметры DM-RS. Чтобы улучшить оценку канала, добавьте дополнительное положение DM-RS.
pdsch.DMRS.DMRSAdditionalPosition = 1;
Установите тип настройки DM-RS и длину DM-RS, которая определяет количество ортогональных последовательностей DM-RS или портов DM-RS.
DMRSConfigurationType = 1
поддержки до 4 портов DM-RS, когда DMRSLength = 1
.
DMRSConfigurationType = 1
поддержки до 8 портов DM-RS, когда DMRSLength = 2
.
DMRSConfigurationType = 2
поддержки до 6 портов DM-RS, когда DMRSLength = 1
. Это спроектировано для многопользовательского MIMO (MU-MIMO).
DMRSConfigurationType = 2
поддержки до 12 портов DM-RS, когда DMRSLength = 2
. Это спроектировано для MU-MIMO.
Максимальное количество слоев должно быть меньше чем или равно количеству портов DM-RS.
pdsch.DMRS.DMRSConfigurationType = 1;
pdsch.DMRS.DMRSLength = 2;
pdsch.DMRS % Display DM-RS properties
ans = nrPDSCHDMRSConfig with properties: DMRSConfigurationType: 1 DMRSReferencePoint: 'CRB0' DMRSTypeAPosition: 2 DMRSAdditionalPosition: 1 DMRSLength: 2 CustomSymbolSet: [] DMRSPortSet: [] NIDNSCID: [] NSCID: 0 NumCDMGroupsWithoutData: 2 Read-only properties: CDMGroups: [0 0] DeltaShifts: [0 0] FrequencyWeights: [2x2 double] TimeWeights: [2x2 double] DMRSSubcarrierLocations: [6x2 double] CDMLengths: [2 1]
Задайте скорость кода, количество процессов HARQ и значения последовательности версии сокращения (RV). Это управление последовательностью повторные передачи RV в случае ошибки. Чтобы отключить повторные передачи HARQ, можно установить rvSeq
к фиксированному значению (например, 0). Для получения дополнительной информации о том, как смоделировать транспортные каналы с HARQ, см. Модель 5G Транспортные Каналы NR с HARQ.
NHARQProcesses = 16; % Number of parallel HARQ processes
rvSeq = [0 2 3 1];
Учтите количество кодовых комбинаций при определении уровня кодирования. Количество кодовых комбинаций является свойством только для чтения объекта настройки PDSCH, который зависит от количества слоев.
1 кодовая комбинация максимум для 4 слоев
2 кодовых комбинации больше чем для 4 слоев
% Coding rate if pdsch.NumCodewords == 1 codeRate = 490/1024; else codeRate = [490 490]./1024; end
Создайте объекты энкодера и декодера DL-SCH. Чтобы использовать несколько процессов, установите MultipleHARQProcesses
свойство к true
для обоих объектов. Вы не должны задавать количество процессов HARQ. Объекты энкодера и декодера DL-SCH могут смоделировать до 16 процессов HARQ. Чтобы идентифицировать активный процесс HARQ при выполнении операций с объектами энкодера и декодера DL-SCH, используйте HARQprocessID
свойство объекта сущности HARQ, заданного в следующем разделе.
% Create DL-SCH encoder object encodeDLSCH = nrDLSCH; encodeDLSCH.MultipleHARQProcesses = true; encodeDLSCH.TargetCodeRate = codeRate; % Create DLSCH decoder object decodeDLSCH = nrDLSCHDecoder; decodeDLSCH.MultipleHARQProcesses = true; decodeDLSCH.TargetCodeRate = codeRate; decodeDLSCH.LDPCDecodingAlgorithm = "Normalized min-sum"; decodeDLSCH.MaximumLDPCIterationCount = 6;
Создайте объект сущности HARQ справиться с процессами HARQ и буферами энкодера и декодера DL-SCH. Для каждого процесса HARQ сущность HARQ хранит эти элементы:
Идентификационный номер HARQ.
RV.
Номер передачи, который указывает, сколько раз был передан определенный транспортный блок.
Отметьте, чтобы указать, требуются ли новые данные. Новые данные требуются, когда транспортный блок получен успешно или если тайм-аут последовательности произошел (все передачи RV перестали работать).
Отметьте, чтобы указать, произошел ли тайм-аут последовательности (все передачи RV перестали работать).
harqEntity = HARQEntity(0:NHARQProcesses-1,rvSeq,pdsch.NumCodewords);
Задайте количество передающих и приемных антенн.
nTxAnts = 8; nRxAnts = 8; % Check that the number of layers is valid for the number of antennas if pdsch.NumLayers > min(nTxAnts,nRxAnts) error("The number of layers ("+string(pdsch.NumLayers)+") must be smaller than min(nTxAnts,nRxAnts) ("+string(min(nTxAnts,nRxAnts))+")") end
Создайте объект канала.
channel = nrTDLChannel;
channel.DelayProfile = "TDL-C";
channel.NumTransmitAntennas = nTxAnts;
channel.NumReceiveAntennas = nRxAnts;
Установите частоту дискретизации канала на тот из сигнала OFDM. Чтобы получить частоту дискретизации сигнала OFDM, используйте nrOFDMInfo
функция.
ofdmInfo = nrOFDMInfo(carrier); channel.SampleRate = ofdmInfo.SampleRate;
Настройте цикл, чтобы симулировать передачу и прием пазов. Создайте comm.ConstellationDiagram
отобразить созвездие компенсируемого сигнала.
constPlot = comm.ConstellationDiagram; % Constellation diagram object constPlot.ReferenceConstellation = getConstellationRefPoints(pdsch.Modulation); % Reference constellation values constPlot.EnableMeasurements = 1; % Enable EVM measurements % Initial timing offset offset = 0; estChannelGrid = getInitialChannelEstimate(channel,carrier); newPrecodingWeight = getPrecodingMatrix(pdsch.PRBSet,pdsch.NumLayers,estChannelGrid); for nSlot = 0:totalNoSlots-1 % New slot carrier.NSlot = nSlot;
Транспортный размер блока является количеством битов, чтобы отправить к этапам кодирования канала. Это значение зависит от способности PDSCH. Чтобы вычислить транспортный размер блока, используйте nrTBS
функция.
% Generate PDSCH indices info, which is needed to calculate the transport % block size [pdschIndices,pdschInfo] = nrPDSCHIndices(carrier,pdsch); % Calculate transport block sizes Xoh_PDSCH = 0; trBlkSizes = nrTBS(pdsch.Modulation,pdsch.NumLayers,numel(pdsch.PRBSet),pdschInfo.NREPerPRB,codeRate,Xoh_PDSCH);
Этот раздел объясняет организацию буферизации данных в энкодере и декодере.
Буферы энкодера DL-SCH: Сгенерируйте новый транспортный блок, если новые данные требуются для активного процесса HARQ. Сохраните транспортный блок в соответствующем буфере. Если новые данные не требуются, энкодер DL-SCH использует свои буферизированные биты для повторной передачи.
Буферы декодера DL-SCH: мягкие буферы в приемнике хранят ранее полученные версии той же кодовой комбинации. Эти буферы очищены автоматически на успешный прием (никакая ошибка CRC). Однако, если концы последовательности RV без успешного декодирования, сбросьте буферы вручную при помощи resetSoftBuffer
объектная функция.
% Get new transport blocks and flush decoder soft buffer, as required for cwIdx = 1:pdsch.NumCodewords if harqEntity.NewData(cwIdx) % Create and store a new transport block for transmission trBlk = randi([0 1],trBlkSizes(cwIdx),1); setTransportBlock(encodeDLSCH,trBlk,cwIdx-1,harqEntity.HARQProcessID); % If the previous RV sequence ends without successful % decoding, flush the soft buffer if harqEntity.SequenceTimeout(cwIdx) resetSoftBuffer(decodeDLSCH,cwIdx-1,harqEntity.HARQProcessID); end end end
Закодируйте транспортные блоки. Транспортный блок уже хранится в одном из внутренних мягких буферов объекта энкодера DL-SCH.
codedTrBlock = encodeDLSCH(pdsch.Modulation,pdsch.NumLayers,pdschInfo.G,harqEntity.RedundancyVersion,harqEntity.HARQProcessID);
Сгенерируйте символы PDSCH от закодированных транспортных блоков.
pdschSymbols = nrPDSCH(carrier,pdsch,codedTrBlock);
Получите веса перед кодированием. Этот пример принимает знание канала для предварительного кодирования. (Для примера того, как использовать оценку канала в приемнике, чтобы вычислить веса, используемые для передачи в следующем пазе, смотрите Пропускную способность NR PDSCH.)
precodingWeights = newPrecodingWeight;
Предварительно закодируйте символы PDSCH.
pdschSymbolsPrecoded = pdschSymbols*precodingWeights;
Сгенерируйте символы DM-RS и индексы.
dmrsSymbols = nrPDSCHDMRS(carrier,pdsch); dmrsIndices = nrPDSCHDMRSIndices(carrier,pdsch);
Сгенерируйте пустую сетку ресурса. Эта сетка представляет паз.
pdschGrid = nrResourceGrid(carrier,nTxAnts);
nrPDSCHIndices
функция генерирует индексы, которые относятся к слоям и не антеннам. Этот формат полезен при отображении символов PDSCH непосредственно со слоями. В этом случае получившиеся сетки ресурса не предварительно закодированы. Этот рисунок показывает процесс отображения символов PDSCH к стольким же сеток ресурса сколько слои.
Поскольку этот пример применяет предварительное кодирование MIMO к символам PDSCH прежде, чем сопоставить их с сетками ресурса, ПРЕДВАРИТЕЛЬНО ЗАКОДИРОВАННЫЕ MIMO символы PDSCH относятся к антеннам и не слоям. Чтобы преобразовать индексы слоя в индексы антенны, используйте nrExtractResources
функция. Этот рисунок показывает процесс отображения символов MIMO-precoded к стольким же сеток ресурса сколько передающие антенны.
[~,pdschAntIndices] = nrExtractResources(pdschIndices,pdschGrid); pdschGrid(pdschAntIndices) = pdschSymbolsPrecoded;
MIMO-precode и карта символы DM-RS к сетке ресурса. Подобно индексам PDSCH индексы DM-RS относятся к слоям. Чтобы преобразовать эти индексы слоя в индексы антенны, используйте nrExtractResources
функционируйте снова.
% PDSCH DM-RS precoding and mapping for p = 1:size(dmrsSymbols,2) [~,dmrsAntIndices] = nrExtractResources(dmrsIndices(:,p),pdschGrid); pdschGrid(dmrsAntIndices) = pdschGrid(dmrsAntIndices) + dmrsSymbols(:,p)*precodingWeights(p,:); end
OFDM-модулируйте сетку ресурса.
[txWaveform,waveformInfo] = nrOFDMModulate(carrier,pdschGrid);
Канал распространения генерирует N выборки выхода для входа с выборками N. Однако блок N, выборки выхода включают канал, фильтрует переходный процесс (K выборки). Поскольку этап синхронизации удаляет этот начальный переходный процесс, если паз в канале, выход имеет выборки N, выборки N-K, остается после синхронизации. Выборок N-K недостаточно, чтобы декодировать ценность паза данных. Часть выборок паза находится в линии задержки фильтра канала и еще не сбрасывается. Чтобы сбросить все соответствующие выборки из фильтра канала, заполните входной сигнал нулями. Максимальная задержка, которую вводит фильтр канала, влияет на размер дополнения. Дополнение составляет задержку, введенную всеми многопутевыми компонентами и задержкой реализации фильтра канала. Этот рисунок показывает потребность в нуле, дополняющем, прежде чем форма волны введет канал.
Заполните входной сигнал достаточным количеством нулей, чтобы гарантировать, что сгенерированный сигнал сбрасывается из фильтра канала.
chInfo = info(channel); maxChDelay = ceil(max(chInfo.PathDelays*channel.SampleRate)) + chInfo.ChannelFilterDelay; txWaveform = [txWaveform; zeros(maxChDelay,size(txWaveform,2))];
Отправьте сигнал через канал и добавьте шум.
[rxWaveform,pathGains,sampleTimes] = channel(txWaveform); noise = generateAWGN(SNRdB,nRxAnts,waveformInfo.Nfft,size(rxWaveform)); rxWaveform = rxWaveform + noise;
Можно выполнить совершенную или практическую синхронизацию.
Идеальная синхронизация принимает знание канала (nrPerfectTimingEstimate
). Канал возвращает информацию об усилениях пути, и путь фильтрует импульсную характеристику. Можно использовать эту информацию, чтобы определить смещение, которое сопоставлено с самым сильным многопутевым компонентом через все снимки состояния канала и через все передающие и приемные антенны.
Практическая синхронизация выполняет взаимную корреляцию полученного сигнала с символами PDSCH DM-RS во временном интервале (nrTimingEstimate
). В некоторых неблагоприятных случаях эта взаимная корреляция может быть слабой из-за исчезновения или шума, приводящего к ошибочному смещению синхронизации. Функциональный hSkipWeakTimingOffset
проверяет величину взаимной корреляции mag
. Если взаимная корреляция слаба, функция игнорирует текущую оценку синхронизации и вместо этого использует предыдущую оценку (offset
).
Выполните совершенную или практическую оценку синхронизации и синхронизацию.
if perfectEstimation % Get path filters for perfect timing estimation pathFilters = getPathFilters(channel); [offset,mag] = nrPerfectTimingEstimate(pathGains,pathFilters); else [t,mag] = nrTimingEstimate(carrier,rxWaveform,dmrsIndices,dmrsSymbols); offset = hSkipWeakTimingOffset(offset,t,mag); end rxWaveform = rxWaveform(1+offset:end,:);
OFDM-демодулируйте синхронизируемый сигнал.
rxGrid = nrOFDMDemodulate(carrier,rxWaveform);
Оценка канала обеспечивает представление эффектов канала на элемент ресурса (RE). Эквалайзер использует эту информацию, чтобы компенсировать искажение, введенное каналом.
Можно выполнить совершенную или практическую оценку канала.
Совершенная оценка канала принимает знание канала (nrPerfectChannelEstimate
). Совершенная оценка канала представляет условия канала между передающими и приемными антеннами. Поскольку эквалайзер требует знания канала между слоями передачи и получить антеннами, необходимо применить предварительное кодирование к совершенной оценке канала.
Практическая оценка канала использует PDSCH DM-RS, чтобы оценить условия канала и использует усреднение шума и интерполяцию, чтобы получить оценку для всего REs в пазе. Поскольку DM-RSs задан на слой, получившаяся практическая оценка канала представляет условия канала между слоями передачи и получить антеннами. Практическая оценка канала включает эффект операции MIMO перед кодированием.
Этот рисунок показывает контрольные точки оценок канала в цепи обработки нисходящего канала.
Выполните совершенную или практическую оценку канала.
if perfectEstimation % Perform perfect channel estimation between transmit and receive % antennas. estChGridAnts = nrPerfectChannelEstimate(carrier,pathGains,pathFilters,offset,sampleTimes); % Get perfect noise estimate (from noise realization) noiseGrid = nrOFDMDemodulate(carrier,noise(1+offset:end ,:)); noiseEst = var(noiseGrid(:)); % Get precoding matrix for next slot newPrecodingWeight = getPrecodingMatrix(pdsch.PRBSet,pdsch.NumLayers,estChGridAnts); % Apply precoding to estChGridAnts. The resulting estimate is for % the channel estimate between layers and receive antennas. estChGridLayers = precodeChannelEstimate(estChGridAnts,precodingWeights.'); else % Perform practical channel estimation between layers and receive % antennas. [estChGridLayers,noiseEst] = nrChannelEstimate(carrier,rxGrid,dmrsIndices,dmrsSymbols,'CDMLengths',pdsch.DMRS.CDMLengths); % Remove precoding from estChannelGrid before precoding % matrix calculation estChGridAnts = precodeChannelEstimate(estChGridLayers,conj(precodingWeights)); % Get precoding matrix for next slot newPrecodingWeight = getPrecodingMatrix(pdsch.PRBSet,pdsch.NumLayers,estChGridAnts); end
Постройте оценку канала между первым слоем, и первые получают антенну.
mesh(abs(estChGridLayers(:,:,1,1)));
title('Channel Estimate'); xlabel('OFDM Symbol'); ylabel("Subcarrier"); zlabel("Magnitude");
На данном этапе можно использовать оценку канала, чтобы получить матрицу перед кодированием для передачи в следующем пазе. Поскольку этот пример принимает знание канала в передатчике, вы не должны вычислять матрицу перед кодированием в конце приемника. Для примера того, как вычислить матрицу перед кодированием для передачи данных на основе оценки канала в приемнике, смотрите Пропускную способность NR PDSCH.
Эквалайзер использует оценку канала, чтобы компенсировать искажение, введенное каналом.
Извлеките символы PDSCH из полученной сетки и сопоставленных оценок канала. csi
выведите имеет информацию о состоянии канала (CSI) для каждого из компенсируемых символов PDSCH. CSI является мерой условий канала для каждого символа PDSCH. Используйте CSI, чтобы взвесить декодируемые мягкие биты после того, как декодирование PDSCH, эффективно увеличивая важность символов, испытывающих лучше, образует канал условия.
[pdschRx,pdschHest] = nrExtractResources(pdschIndices,rxGrid,estChGridLayers); [pdschEq,csi] = nrEqualizeMMSE(pdschRx,pdschHest,noiseEst);
Постройте созвездие компенсируемых символов. График включает схемы созвездия для всех слоев.
constPlot.ChannelNames = "Layer "+(pdsch.NumLayers:-1:1); constPlot.ShowLegend = true; % Constellation for the first layer has a higher SNR than that for the % last layer. Flip the layers so that the constellations do not mask % each other. constPlot(fliplr(pdschEq));
Декодируйте компенсируемые символы PDSCH и получите мягкие битные кодовые комбинации.
[dlschLLRs,rxSymbols] = nrPDSCHDecode(carrier,pdsch,pdschEq,noiseEst);
Масштабируйте мягкие биты или отношения логарифмической правдоподобности (LLRs) CSI. Это масштабирование применяет больший вес к символам в REs с лучшими условиями канала.
% Scale LLRs by CSI csi = nrLayerDemap(csi); % CSI layer demapping for cwIdx = 1:pdsch.NumCodewords Qm = length(dlschLLRs{cwIdx})/length(rxSymbols{cwIdx}); % Bits per symbol csi{cwIdx} = repmat(csi{cwIdx}.',Qm,1); % Expand by each bit per symbol dlschLLRs{cwIdx} = dlschLLRs{cwIdx} .* csi{cwIdx}(:); % Scale end
Декодируйте LLRs и проверьте ошибки.
decodeDLSCH.TransportBlockLength = trBlkSizes;
[decbits,blkerr] = decodeDLSCH(dlschLLRs,pdsch.Modulation,pdsch.NumLayers, ...
harqEntity.RedundancyVersion,harqEntity.HARQProcessID);
Обновите текущий процесс HARQ с получившимся состоянием блочной ошибки, и затем перейдите к следующему процессу. Этот шаг обновляет информацию, связанную с активным процессом HARQ в сущности HARQ.
statusReport = updateAndAdvance(harqEntity,blkerr,trBlkSizes,pdschInfo.G);
Обобщите HARQ и информацию о декодировании для существующего паза.
disp("Slot "+(nSlot)+". "+statusReport);
Slot 0. HARQ Proc 0: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 1. HARQ Proc 1: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 2. HARQ Proc 2: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 3. HARQ Proc 3: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 4. HARQ Proc 4: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 5. HARQ Proc 5: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 6. HARQ Proc 6: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 7. HARQ Proc 7: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 8. HARQ Proc 8: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 9. HARQ Proc 9: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 10. HARQ Proc 10: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 11. HARQ Proc 11: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 12. HARQ Proc 12: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 13. HARQ Proc 13: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 14. HARQ Proc 14: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 15. HARQ Proc 15: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 16. HARQ Proc 0: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 17. HARQ Proc 1: CW0: Initial transmission passed (RV=0,CR=0.482212). Slot 18. HARQ Proc 2: CW0: Initial transmission passed (RV=0,CR=0.482212).
Slot 19. HARQ Proc 3: CW0: Initial transmission passed (RV=0,CR=0.482212).
end % for nSlot = 0:totalNoSlots
function noise = generateAWGN(SNRdB,nRxAnts,Nfft,sizeRxWaveform) % Generate AWGN for a given value of SNR in dB (SNRDB), which is the % receiver SNR per RE and antenna, assuming the channel does % not affect the power of the signal. NRXANTS is the number of receive % antennas. NFFT is the FFT size used in OFDM demodulation. SIZERXWAVEFORM % is the size of the receive waveform used to calculate the size of the % noise matrix. % Normalize noise power by the IFFT size used in OFDM modulation, as % the OFDM modulator applies this normalization to the transmitted % waveform. Also normalize by the number of receive antennas, as the % channel model applies this normalization to the received waveform by % default. The SNR is defined per RE for each receive antenna (TS % 38.101-4). SNR = 10^(SNRdB/10); % Calculate linear noise gain N0 = 1/sqrt(2.0*nRxAnts*double(Nfft)*SNR); noise = N0*complex(randn(sizeRxWaveform),randn(sizeRxWaveform)); end function wtx = getPrecodingMatrix(PRBSet,NLayers,hestGrid) % Calculate precoding matrix given an allocation and a channel estimate % Allocated subcarrier indices allocSc = (1:12)' + 12*PRBSet(:).'; allocSc = allocSc(:); % Average channel estimate [~,~,R,P] = size(hestGrid); estAllocGrid = hestGrid(allocSc,:,:,:); Hest = permute(mean(reshape(estAllocGrid,[],R,P)),[2 3 1]); % SVD decomposition [~,~,V] = svd(Hest); wtx = V(:,1:NLayers).'; wtx = wtx/sqrt(NLayers); % Normalize by NLayers end function estChannelGrid = getInitialChannelEstimate(channel,carrier) % Obtain an initial channel estimate for calculating the precoding matrix. % This function assumes a perfect channel estimate % Clone of the channel chClone = channel.clone(); chClone.release(); % No filtering needed to get channel path gains chClone.ChannelFiltering = false; % Get channel path gains [pathGains,sampleTimes] = chClone(); % Perfect timing synchronization pathFilters = getPathFilters(chClone); offset = nrPerfectTimingEstimate(pathGains,pathFilters); % Perfect channel estimate estChannelGrid = nrPerfectChannelEstimate(carrier,pathGains,pathFilters,offset,sampleTimes); end function refPoints = getConstellationRefPoints(mod) % Calculate the reference constellation points for a given modulation % scheme. switch mod case "QPSK" nPts = 4; case "16QAM" nPts = 16; case "64QAM" nPts = 64; case "256QAM" nPts = 256; end binaryValues = int2bit(0:nPts-1,log2(nPts)); refPoints = nrSymbolModulate(binaryValues(:),mod); end function estChannelGrid = precodeChannelEstimate(estChannelGrid,W) % Apply precoding matrix W to the last dimension of the channel estimate. % Linearize 4-D matrix and reshape after multiplication K = size(estChannelGrid,1); L = size(estChannelGrid,2); R = size(estChannelGrid,3); estChannelGrid = reshape(estChannelGrid,K*L*R,[]); estChannelGrid = estChannelGrid*W; estChannelGrid = reshape(estChannelGrid,K,L,R,[]); end