diff --git a/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc b/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc index 7bba9ac89..4a47f47e7 100644 --- a/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc +++ b/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc @@ -280,49 +280,50 @@ ThreeGppSpectrumPropagationLossModel::CalcBeamformingGain( numRxPorts, isReverse); - // The precoding matrix is not set + NS_ASSERT_MSG(rxParams->psd->GetValuesN() == rxParams->spectrumChannelMatrix->GetNumPages(), + "RX PSD and the spectrum channel matrix should have the same number of RBs "); + + // Calculate RX PSD from the spectrum channel matrix H and + // the precoding matrix P as: PSD = (H*P)^h * (H*P) + MatrixBasedChannelModel::Complex3DVector psd; + MatrixBasedChannelModel::Complex3DVector p; if (!rxParams->precodingMatrix) { - // Update rxParams->Psd. - // Compute RX PSD from the channel matrix - auto vit = rxParams->psd->ValuesBegin(); // psd iterator - size_t rbIdx = 0; - while (vit != rxParams->psd->ValuesEnd()) + // When the precoding matrix P is not set, we create one with a single column + p = ComplexMatrixArray(rxParams->spectrumChannelMatrix->GetNumCols(), 1, 1); + // Initialize it to the inverse square of the number of txPorts + p.Elem(0, 0, 0) = 1.0 / sqrt(rxParams->spectrumChannelMatrix->GetNumCols()); + for (size_t rowI = 0; rowI < rxParams->spectrumChannelMatrix->GetNumCols(); rowI++) { - // Calculate PSD for the first antenna port (correct for SISO) - *vit = std::norm(rxParams->spectrumChannelMatrix->Elem(0, 0, rbIdx)); - vit++; - rbIdx++; + p.Elem(rowI, 0, 0) = p.Elem(0, 0, 0); } + // Replicate vector to match the number of RBGs + p = p.MakeNCopies(rxParams->spectrumChannelMatrix->GetNumPages()); } else { - NS_ASSERT_MSG(rxParams->psd->GetValuesN() == rxParams->spectrumChannelMatrix->GetNumPages(), - "RX PSD and the spectrum channel matrix should have the same number of RBs "); - // Calculate RX PSD from the spectrum channel matrix, H and - // the precoding matrix, P as: - // PSD = (H*P)^h * (H*P), - // where the dimensions are: - // H (rxPorts,txPorts,numRbs) x P (txPorts,txStreams, numRbs) = - // HxP (rxPorts,txStreams, numRbs) - MatrixBasedChannelModel::Complex3DVector hP = - *rxParams->spectrumChannelMatrix * (*rxParams->precodingMatrix); - // (HxP)^h dimensions are (txStreams, rxPorts, numRbs) - MatrixBasedChannelModel::Complex3DVector hPHerm = hP.HermitianTranspose(); + p = *rxParams->precodingMatrix; + } + // When we have the precoding matrix P, we first do + // H(rxPorts,txPorts,numRbs) x P(txPorts,txStreams,numRbs) = HxP(rxPorts,txStreams,numRbs) + MatrixBasedChannelModel::Complex3DVector hP = *rxParams->spectrumChannelMatrix * p; + // Then (HxP)^h dimensions are (txStreams, rxPorts, numRbs) + MatrixBasedChannelModel::Complex3DVector hPHerm = hP.HermitianTranspose(); - // Finally, (HxP)^h x (HxP) = PSD (txStreams, txStreams, numRbs) - MatrixBasedChannelModel::Complex3DVector psd = hPHerm * hP; - // Update rxParams->Psd - for (uint32_t rbIdx = 0; rbIdx < rxParams->psd->GetValuesN(); ++rbIdx) + // Finally, (HxP)^h x (HxP) = PSD(txStreams, txStreams, numRbs) + psd = hPHerm * hP; + + // Update rxParams->Psd + for (uint32_t rbIdx = 0; rbIdx < rxParams->psd->GetValuesN(); ++rbIdx) + { + (*rxParams->psd)[rbIdx] = 0.0; + + for (size_t txStream = 0; txStream < psd.GetNumRows(); ++txStream) { - (*rxParams->psd)[rbIdx] = 0.0; - - for (size_t txStream = 0; txStream < psd.GetNumRows(); ++txStream) - { - (*rxParams->psd)[rbIdx] += std::real(psd(txStream, txStream, rbIdx)); - } + (*rxParams->psd)[rbIdx] += std::real(psd(txStream, txStream, rbIdx)); } } + return rxParams; } diff --git a/src/spectrum/test/three-gpp-channel-test-suite.cc b/src/spectrum/test/three-gpp-channel-test-suite.cc index 62a444e02..001510dbf 100644 --- a/src/spectrum/test/three-gpp-channel-test-suite.cc +++ b/src/spectrum/test/three-gpp-channel-test-suite.cc @@ -934,9 +934,18 @@ ThreeGppSpectrumPropagationLossModelTest::DoRun() // 1) check that the rx PSD is equal for both the direct and the reverse channel auto rxParamsNew = lossModel->DoCalcRxPowerSpectralDensity(txParams, rxMob, txMob, rxAntenna, txAntenna); - NS_TEST_ASSERT_MSG_EQ((*rxParamsOld->psd == *rxParamsNew->psd), - true, - "The long term for the direct and the reverse channel are different"); + if ((rxParamsOld->spectrumChannelMatrix->GetNumCols() == + rxParamsNew->spectrumChannelMatrix->GetNumCols()) && + (rxParamsOld->spectrumChannelMatrix->GetNumRows() == + rxParamsNew->spectrumChannelMatrix->GetNumRows()) && + (rxParamsOld->spectrumChannelMatrix->GetNumCols() == 1) && + (rxParamsOld->spectrumChannelMatrix->GetNumRows() == 1)) + { + // this is only really true in case of SISO (1x1 non-polarized port array) + NS_TEST_ASSERT_MSG_EQ((*rxParamsOld->psd == *rxParamsNew->psd), + true, + "The long term for the direct and the reverse channel are different"); + } // 2) check if the long term is updated when changing the BF vector // change the position of the rx device and recompute the beamforming vectors