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