5G-LENA nr-v3.0-33-g7aea1e4
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-interference.cc
1/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2
3// Copyright (c) 2019 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
4//
5// SPDX-License-Identifier: GPL-2.0-only
6
7#include "nr-interference.h"
8
9#include "nr-mimo-chunk-processor.h"
10#include "nr-spectrum-signal-parameters.h"
11
12#include <ns3/log.h>
13#include <ns3/lte-chunk-processor.h>
14#include <ns3/simulator.h>
15
16#include <algorithm>
17
18NS_LOG_COMPONENT_DEFINE("NrInterference");
19
20namespace ns3
21{
22
24 : LteInterference(),
25 m_firstPower(0.0)
26{
27 NS_LOG_FUNCTION(this);
28}
29
31{
32 NS_LOG_FUNCTION(this);
33}
34
35void
37{
38 NS_LOG_FUNCTION(this);
39
40 m_mimoChunkProcessors.clear();
41 m_rxSignalsMimo.clear();
42 m_allSignalsMimo.clear();
43
44 LteInterference::DoDispose();
45}
46
47TypeId
49{
50 static TypeId tid =
51 TypeId("ns3::NrInterference")
52 .SetParent<Object>()
53 .AddTraceSource("SnrPerProcessedChunk",
54 "Snr per processed chunk.",
55 MakeTraceSourceAccessor(&NrInterference::m_snrPerProcessedChunk),
56 "ns3::SnrPerProcessedChunk::TracedCallback")
57 .AddTraceSource("RssiPerProcessedChunk",
58 "Rssi per processed chunk.",
59 MakeTraceSourceAccessor(&NrInterference::m_rssiPerProcessedChunk),
60 "ns3::RssiPerProcessedChunk::TracedCallback");
61 return tid;
62}
63
64void
65NrInterference::AddSignal(Ptr<const SpectrumValue> spd, Time duration)
66{
67 NS_LOG_FUNCTION(this << *spd << duration);
68
69 // Integrate over our receive bandwidth.
70 // Note that differently from wifi, we do not need to pass the
71 // signal through the filter. This is because
72 // before receiving the signal already passed through the
73 // spectrum converter, thus we will consider only the power over the
74 // spectrum that corresponds to the spectrum of the receiver.
75 // Also, differently from wifi we do not account here for the antenna gain,
76 // since this is already taken into account by the spectrum channel.
77 double rxPowerW = Integral(*spd);
78 // We are creating two events, one that adds the rxPowerW, and
79 // another that subtracts the rxPowerW at the endTime.
80 // These events will be used to determine if the channel is busy and
81 // for how long.
82 AppendEvent(Simulator::Now(), Simulator::Now() + duration, rxPowerW);
83
84 LteInterference::AddSignal(spd, duration);
85}
86
87void
88NrInterference::EndRx()
89{
90 NS_LOG_FUNCTION(this);
91 if (!m_receiving)
92 {
93 NS_LOG_INFO("EndRx was already evaluated or RX was aborted");
94 }
95 else
96 {
97 SpectrumValue snr = (*m_rxSignal) / (*m_noise);
98 double avgSnr = Sum(snr) / (snr.GetSpectrumModel()->GetNumBands());
100
101 NrInterference::ConditionallyEvaluateChunk();
102
103 m_receiving = false;
104 for (auto& it : m_rsPowerChunkProcessorList)
105 {
106 it->End();
107 }
108 for (auto& it : m_interfChunkProcessorList)
109 {
110 it->End();
111 }
112 for (auto& it : m_sinrChunkProcessorList)
113 {
114 it->End();
115 }
116
117 for (auto& cp : m_mimoChunkProcessors)
118 {
119 cp->End();
120 }
121 }
122}
123
124void
125NrInterference::ConditionallyEvaluateChunk()
126{
127 NS_LOG_FUNCTION(this);
128 if (m_receiving)
129 {
130 NS_LOG_DEBUG(this << " Receiving");
131 }
132 NS_LOG_DEBUG(this << " now " << Now() << " last " << m_lastChangeTime);
133 if (m_receiving && (Now() > m_lastChangeTime))
134 {
135 NS_LOG_LOGIC(this << " signal = " << *m_rxSignal << " allSignals = " << *m_allSignals
136 << " noise = " << *m_noise);
137 SpectrumValue interf = (*m_allSignals) - (*m_rxSignal) + (*m_noise);
138 SpectrumValue sinr = (*m_rxSignal) / interf;
139 double rbWidth = (*m_rxSignal).GetSpectrumModel()->Begin()->fh -
140 (*m_rxSignal).GetSpectrumModel()->Begin()->fl;
141 double rssidBm = 10 * log10(Sum((*m_noise + *m_allSignals) * rbWidth) * 1000);
143
144 NS_LOG_DEBUG("All signals: " << (*m_allSignals)[0] << ", rxSignal:" << (*m_rxSignal)[0]
145 << " , noise:" << (*m_noise)[0]);
146
147 Time duration = Now() - m_lastChangeTime;
148 for (auto& it : m_rsPowerChunkProcessorList)
149 {
150 it->EvaluateChunk(*m_rxSignal, duration);
151 }
152 for (auto& it : m_sinrChunkProcessorList)
153 {
154 it->EvaluateChunk(sinr, duration);
155 }
156
157 for (auto& cp : m_mimoChunkProcessors)
158 {
159 // Covariance matrix of noise plus out-of-cell interference
160 auto outOfCellInterfCov = CalcOutOfCellInterfCov();
161
162 // Compute the MIMO SINR separately for each received signal.
163 for (auto& rxSignal : m_rxSignalsMimo)
164 {
165 // Use the UE's RNTI to distinguish multiple received signals
166 auto nrRxSignal = DynamicCast<const NrSpectrumSignalParametersDataFrame>(rxSignal);
167 uint16_t rnti = nrRxSignal ? nrRxSignal->rnti : 0;
168
169 // MimoSinrChunk is used to store SINR and compute TBLER of the data transmission
170 auto sinrMatrix = ComputeSinr(outOfCellInterfCov, rxSignal);
171 MimoSinrChunk mimoSinr{sinrMatrix, rnti, duration};
172 cp->EvaluateChunk(mimoSinr);
173
174 // MimoSignalChunk is used to compute PMI feedback.
175 auto& chanSpct = *(rxSignal->spectrumChannelMatrix);
176 MimoSignalChunk mimoSignal{chanSpct, outOfCellInterfCov, rnti, duration};
177 cp->EvaluateChunk(mimoSignal);
178 }
179 }
180 m_lastChangeTime = Now();
181 }
182}
183
184/****************************************************************
185 * Class which records SNIR change events for a
186 * short period of time.
187 ****************************************************************/
188
189NrInterference::NiChange::NiChange(Time time, double delta)
190 : m_time(time),
191 m_delta(delta)
192{
193}
194
195Time
196NrInterference::NiChange::GetTime() const
197{
198 return m_time;
199}
200
201double
202NrInterference::NiChange::GetDelta() const
203{
204 return m_delta;
205}
206
207bool
208NrInterference::NiChange::operator<(const NrInterference::NiChange& o) const
209{
210 return (m_time < o.m_time);
211}
212
213bool
215{
216 double detectedPowerW = Integral(*m_allSignals);
217 double powerDbm = 10 * log10(detectedPowerW * 1000);
218
219 NS_LOG_INFO("IsChannelBusyNow detected power is: "
220 << powerDbm << " detectedPowerW: " << detectedPowerW << " length spectrum: "
221 << (*m_allSignals).GetValuesN() << " thresholdW:" << energyW);
222
223 if (detectedPowerW > energyW)
224 {
225 NS_LOG_INFO("Channel is BUSY.");
226 return true;
227 }
228 else
229 {
230 NS_LOG_INFO("Channel is IDLE.");
231 return false;
232 }
233}
234
235Time
237{
238 if (!IsChannelBusyNow(energyW))
239 {
240 return Seconds(0);
241 }
242
243 Time now = Simulator::Now();
244 double noiseInterferenceW = 0.0;
245 Time end = now;
246 noiseInterferenceW = m_firstPower;
247
248 NS_LOG_INFO("First power: " << m_firstPower);
249
250 for (const auto& i : m_niChanges)
251 {
252 noiseInterferenceW += i.GetDelta();
253 end = i.GetTime();
254 NS_LOG_INFO("Delta: " << i.GetDelta() << "time: " << i.GetDelta());
255 if (end < now)
256 {
257 continue;
258 }
259 if (noiseInterferenceW < energyW)
260 {
261 break;
262 }
263 }
264
265 NS_LOG_INFO("Future power dBm:" << 10 * log10(noiseInterferenceW * 1000)
266 << " W:" << noiseInterferenceW
267 << " and energy threshold in W is: " << energyW);
268
269 if (end > now)
270 {
271 NS_LOG_INFO("Channel BUSY until." << end);
272 }
273 else
274 {
275 NS_LOG_INFO("Channel IDLE.");
276 }
277
278 return end > now ? end - now : MicroSeconds(0);
279}
280
281void
283{
284 m_niChanges.clear();
285 m_firstPower = 0.0;
286}
287
288NrInterference::NiChanges::iterator
289NrInterference::GetPosition(Time moment)
290{
291 return std::upper_bound(m_niChanges.begin(), m_niChanges.end(), NiChange(moment, 0));
292}
293
294void
295NrInterference::AddNiChangeEvent(NiChange change)
296{
297 m_niChanges.insert(GetPosition(change.GetTime()), change);
298}
299
300void
301NrInterference::AppendEvent(Time startTime, Time endTime, double rxPowerW)
302{
303 Time now = Simulator::Now();
304
305 if (!m_receiving)
306 {
307 NiChanges::iterator nowIterator = GetPosition(now);
308 // We empty the list until the current moment. To do so we
309 // first we sum all the energies until the current moment
310 // and save it in m_firstPower.
311 for (auto& i : m_niChanges)
312 {
313 m_firstPower += i.GetDelta();
314 }
315 // then we remove all the events up to the current moment
316 m_niChanges.erase(m_niChanges.begin(), nowIterator);
317 // we create an event that represents the new energy
318 m_niChanges.insert(m_niChanges.begin(), NiChange(startTime, rxPowerW));
319 }
320 else
321 {
322 // for the startTime create the event that adds the energy
323 AddNiChangeEvent(NiChange(startTime, rxPowerW));
324 }
325
326 // for the endTime create event that will subtract energy
327 AddNiChangeEvent(NiChange(endTime, -rxPowerW));
328}
329
330void
331NrInterference::AddSignalMimo(Ptr<const SpectrumSignalParameters> params, const Time& duration)
332{
333 NS_LOG_FUNCTION(this << *params->psd << duration);
334 auto rxPowerW = Integral(*params->psd);
335
336 NS_LOG_FUNCTION(this << *params->psd << duration);
337 LteInterference::DoAddSignal(params->psd);
338 m_allSignalsMimo.push_back(params);
339 // Update signal ID to match signal ID in LteInterference
340 if (++m_lastSignalId == m_lastSignalIdBeforeReset)
341 {
342 m_lastSignalIdBeforeReset += NR_LTE_SIGNALID_INCR;
343 }
344 Simulator::Schedule(duration,
346 this,
347 params,
348 m_lastSignalId);
349
350 AppendEvent(Simulator::Now(), Simulator::Now() + duration, rxPowerW);
351}
352
353void
354NrInterference::StartRxMimo(Ptr<const SpectrumSignalParameters> params)
355{
356 auto rxPsd = params->psd;
357 if (!m_receiving)
358 {
359 // This must be the first receive signal, clear any lingering previous signals
360 m_rxSignalsMimo.clear();
361 }
362 m_rxSignalsMimo.push_back(params);
363 for (auto& cp : m_mimoChunkProcessors)
364 {
365 // Clear the list of stored chunks
366 cp->Start();
367 }
368 LteInterference::StartRx(rxPsd);
369}
370
371void
372NrInterference::DoSubtractSignalMimo(Ptr<const SpectrumSignalParameters> params, uint32_t signalId)
373{
374 DoSubtractSignal(params->psd, signalId);
375 auto numSignals = m_allSignalsMimo.size();
376 // In many instances the signal subtracted is the last signal. Check first for speedup.
377 if (m_allSignalsMimo.back() == params)
378 {
379 m_allSignalsMimo.pop_back();
380 }
381 else
382 {
383 m_allSignalsMimo.erase(
384 std::remove_if(m_allSignalsMimo.begin(),
385 m_allSignalsMimo.end(),
386 [params](Ptr<const SpectrumSignalParameters> p) { return p == params; }),
387 m_allSignalsMimo.end());
388 }
389 NS_ASSERT_MSG(m_allSignalsMimo.size() == (numSignals - 1),
390 "MIMO signal was not found for removal");
391}
392
393void
394NrInterference::AddMimoChunkProcessor(Ptr<NrMimoChunkProcessor> cp)
395{
396 NS_LOG_FUNCTION(this << cp);
397 m_mimoChunkProcessors.push_back(cp);
398}
399
401NrInterference::CalcOutOfCellInterfCov() const
402{
403 // Extract dimensions from first receive signal. Interference signals have equal dimensions
404 NS_ASSERT_MSG(!(m_rxSignalsMimo.empty()), "At least one receive signal is required");
405 const auto& firstSignal = m_rxSignalsMimo[0];
406 NS_ASSERT_MSG(firstSignal->spectrumChannelMatrix, "signal must have a channel matrix");
407 auto nRbs = firstSignal->spectrumChannelMatrix->GetNumPages();
408 auto nRxPorts = firstSignal->spectrumChannelMatrix->GetNumRows();
409
410 // Create white noise covariance matrix
411 auto allSignalsNoiseCov = NrCovMat{ComplexMatrixArray(nRxPorts, nRxPorts, nRbs)};
412 for (size_t iRb = 0; iRb < nRbs; iRb++)
413 {
414 for (size_t iRxPort = 0; iRxPort < nRxPorts; iRxPort++)
415 {
416 allSignalsNoiseCov(iRxPort, iRxPort, iRb) = m_noise->ValuesAt(iRb);
417 }
418 }
419
420 // Add all external interference signals to the covariance matrix
421 for (const auto& intfSignal : m_allSignalsMimo)
422 {
423 if (std::find(m_rxSignalsMimo.begin(), m_rxSignalsMimo.end(), intfSignal) !=
424 m_rxSignalsMimo.end())
425 {
426 // This is one of the signals in the current cell
427 continue;
428 }
429
430 AddInterference(allSignalsNoiseCov, intfSignal);
431 }
432 return allSignalsNoiseCov;
433}
434
435NrCovMat
436NrInterference::CalcCurrInterfCov(Ptr<const SpectrumSignalParameters> rxSignal,
437 const NrCovMat& outOfCellInterfCov) const
438{
439 // Add also the potential interfering signals intended for this device but belonging to other
440 // transmissions. This is required for a gNB receiving MU-MIMO UL signals from multiple UEs
441 auto interfNoiseCov = outOfCellInterfCov;
442 for (auto& otherSignal : m_rxSignalsMimo)
443 {
444 if (otherSignal == rxSignal)
445 {
446 continue; // this is the current receive signal of interest, do not add to interference
447 }
448 NS_ASSERT_MSG((std::find(m_allSignalsMimo.begin(), m_allSignalsMimo.end(), otherSignal) !=
449 m_allSignalsMimo.end()),
450 "RX signal already deleted from m_allSignalsMimo");
451
452 AddInterference(interfNoiseCov, otherSignal);
453 }
454 return interfNoiseCov;
455}
456
457void
458NrInterference::AddInterference(NrCovMat& covMat, Ptr<const SpectrumSignalParameters> signal) const
459{
460 const auto& chanSpct = *(signal->spectrumChannelMatrix);
461 if (signal->precodingMatrix)
462 {
463 auto& precMats = *(signal->precodingMatrix);
464 NS_ASSERT_MSG((precMats.GetNumPages() > 0) && (chanSpct.GetNumPages() > 0),
465 "precMats and channel cannot be empty");
466 NS_ASSERT_MSG(precMats.GetNumPages() == chanSpct.GetNumPages(),
467 "dim mismatch " << precMats.GetNumPages() << " vs "
468 << chanSpct.GetNumPages());
469 covMat.AddInterferenceSignal(chanSpct * precMats);
470 }
471 else
472 {
473 covMat.AddInterferenceSignal(chanSpct);
474 }
475}
476
477NrSinrMatrix
478NrInterference::ComputeSinr(NrCovMat& outOfCellInterfCov,
479 Ptr<const SpectrumSignalParameters> rxSignal) const
480{
481 // Calculate the interference+noise (I+N) covariance matrix for this signal,
482 // including interference from other RX signals
483 auto interfNoiseCov = CalcCurrInterfCov(rxSignal, outOfCellInterfCov);
484
485 // Interference whitening: normalize the signal such that interference + noise covariance matrix
486 // is the identity matrix
487 const auto& chanSpct = *(rxSignal->spectrumChannelMatrix);
488 auto intfNormChanMat = interfNoiseCov.CalcIntfNormChannel(chanSpct);
489
490 // Get the precoding matrix or create a dummy precoding matrix
491 ComplexMatrixArray precMat;
492 if (rxSignal->precodingMatrix)
493 {
494 precMat = *(rxSignal->precodingMatrix);
495 }
496 else
497 {
498 precMat = ComplexMatrixArray{chanSpct.GetNumCols(), 1, chanSpct.GetNumPages()};
499 for (size_t p = 0; p < chanSpct.GetNumPages(); p++)
500 {
501 precMat(0, 0, p) = 1.0;
502 }
503 }
504
505 return intfNormChanMat.ComputeSinrForPrecoding(precMat);
506}
507
508} // namespace ns3
NiChanges m_niChanges
List of events in which there is some change in the energy.
NrInterference()
NrInterference constructor.
virtual void AddMimoChunkProcessor(Ptr< NrMimoChunkProcessor > cp)
Add a chunk processor for MIMO signals.
TracedCallback< double > m_rssiPerProcessedChunk
! Trace for RSSI pre processed chunk.
virtual void DoSubtractSignalMimo(Ptr< const SpectrumSignalParameters > params, uint32_t signalId)
Notify that a signals transmission is ending. This means that the signal will be removed from the lis...
bool IsChannelBusyNow(double energyW)
Checks if the sum of the energy, including the energies that start at this moment is greater than pro...
~NrInterference() override
~NrInterference
void AppendEvent(Time startTime, Time endTime, double rxPowerW)
Crates events corresponding to the new energy. One event corresponds to the moment when the energy st...
Time GetEnergyDuration(double energyW)
Returns the duration of the energy that is above the energy provided detection threshold.
TracedCallback< double > m_snrPerProcessedChunk
! Trace for SNR per processed chunk.
virtual void StartRxMimo(Ptr< const SpectrumSignalParameters > params)
Notify the intended receiver that a new signal is being received. This method is to be called only fo...
virtual void AddSignalMimo(Ptr< const SpectrumSignalParameters > params, const Time &duration)
Notify that a new signal is being perceived in the medium. This method is to be called for all incomi...
void DoDispose() override
DoDispose method inherited from Object.
static TypeId GetTypeId()
Get the object TypeId.