A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-spectrum-value-helper.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 CTTC
3 * Copyright (c) 2010 TELEMATICS LAB, DEE - Politecnico di Bari
4 * Copyright (c) 2017 Orange Labs
5 *
6 * SPDX-License-Identifier: GPL-2.0-only
7 *
8 * Authors: Nicola Baldo <nbaldo@cttc.es>
9 * Giuseppe Piro <g.piro@poliba.it>
10 * Rediet <getachew.redieteab@orange.com>
11 */
12
14
15#include "wifi-utils.h"
16
17#include "ns3/assert.h"
18#include "ns3/fatal-error.h"
19#include "ns3/log.h"
20
21#include <algorithm>
22#include <cmath>
23#include <iterator>
24#include <map>
25#include <numeric>
26#include <optional>
27#include <sstream>
28
29namespace
30{
31
32/**
33 * Lambda to print a vector of frequencies.
34 */
35auto printFrequencies = [](const std::vector<ns3::MHz_u>& v) {
36 std::stringstream ss;
37 for (const auto& centerFrequency : v)
38 {
39 ss << centerFrequency << " ";
40 }
41 return ss.str();
42};
43} // namespace
44
45namespace ns3
46{
47
48NS_LOG_COMPONENT_DEFINE("WifiSpectrumValueHelper");
49
50///< Wifi Spectrum Model structure
52{
53 std::vector<MHz_u> centerFrequencies; ///< center frequency per segment
54 MHz_u channelWidth; ///< channel width
55 Hz_u carrierSpacing; ///< carrier spacing
56 MHz_u guardBandwidth; ///< guard band width
57};
58
59/**
60 * Less than operator
61 * @param lhs the left hand side wifi spectrum to compare
62 * @param rhs the right hand side wifi spectrum to compare
63 * @returns true if the left hand side spectrum is less than the right hand side spectrum
64 */
65bool
67{
68 return std::tie(lhs.centerFrequencies,
69 lhs.channelWidth,
71 lhs.guardBandwidth) < std::tie(rhs.centerFrequencies,
72 rhs.channelWidth,
74 rhs.guardBandwidth);
75 // TODO: replace with default spaceship operator, but it seems currently not working with all
76 // compilers
77}
78
79static std::map<WifiSpectrumModelId, Ptr<SpectrumModel>>
80 g_wifiSpectrumModelMap; ///< static initializer for the class
81
83WifiSpectrumValueHelper::GetSpectrumModel(const std::vector<MHz_u>& centerFrequencies,
84 MHz_u channelWidth,
85 Hz_u carrierSpacing,
86 MHz_u guardBandwidth)
87{
88 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
89 << channelWidth << carrierSpacing << guardBandwidth);
90 NS_ASSERT_MSG(centerFrequencies.size() <= 2,
91 "Spectrum model does not support more than 2 segments");
92 if (centerFrequencies.size() != 1)
93 {
94 NS_ASSERT_MSG(centerFrequencies.front() != centerFrequencies.back(),
95 "Center frequency of each segment shall be different");
96 }
97 // assume all frequency segments have the same width, hence split the guard bandwidth
98 // accordingly over the segments
99 guardBandwidth /= centerFrequencies.size();
101 WifiSpectrumModelId key{centerFrequencies, channelWidth, carrierSpacing, guardBandwidth};
102 if (const auto it = g_wifiSpectrumModelMap.find(key); it != g_wifiSpectrumModelMap.cend())
103 {
104 ret = it->second;
105 }
106 else
107 {
108 Bands bands;
109 const auto [minCenterFrequency, maxCenterFrequency] =
110 std::minmax_element(centerFrequencies.cbegin(), centerFrequencies.cend());
111 const auto separationWidth =
112 (minCenterFrequency == maxCenterFrequency)
113 ? 0
114 : (*maxCenterFrequency - *minCenterFrequency - (channelWidth / 2));
115 NS_ASSERT(separationWidth == 0 || centerFrequencies.size() == 2);
116 Hz_u bandwidth = (channelWidth + (2 * guardBandwidth) + separationWidth) * 1e6;
117 // For OFDM, the center subcarrier is null (at center frequency)
118 uint32_t numBands = std::ceil(bandwidth / carrierSpacing);
119 NS_ASSERT(numBands > 0);
120 if (numBands % 2 == 0)
121 {
122 // round up to the nearest odd number of subbands so that bands
123 // are symmetric around center frequency
124 numBands += 1;
125 }
126 NS_ASSERT_MSG(numBands % 2 == 1, "Number of bands should be odd");
127 NS_LOG_DEBUG("Num bands " << numBands << " band bandwidth " << carrierSpacing);
128
129 // The lowest frequency is obtained from the minimum center frequency among the segment(s).
130 // Then, we subtract half the channel width to retrieve the starting frequency of the
131 // operating channel. If the channel is made of 2 segments, since the channel width is the
132 // total width, only a quarter of the channel width has to be subtracted. Finally, we
133 // remove the guard band width to get the center frequency of the first band and half the
134 // carrier spacing to get the effective starting frequency of the first band.
135 const auto startingFrequencyHz = *minCenterFrequency * 1e6 -
136 ((channelWidth * 1e6) / (2 * centerFrequencies.size())) -
137 (guardBandwidth * 1e6) - (carrierSpacing / 2);
138 for (size_t i = 0; i < numBands; i++)
139 {
140 BandInfo info;
141 auto f = startingFrequencyHz + (i * carrierSpacing);
142 info.fl = f;
143 f += carrierSpacing / 2;
144 info.fc = f;
145 f += carrierSpacing / 2;
146 info.fh = f;
147 NS_LOG_DEBUG("creating band " << i << " (" << info.fl << ":" << info.fc << ":"
148 << info.fh << ")");
149 bands.push_back(info);
150 }
151 ret = Create<SpectrumModel>(std::move(bands));
153 }
154 NS_LOG_LOGIC("returning SpectrumModel::GetUid () == " << ret->GetUid());
155 return ret;
156}
157
158// Power allocated to 71 center subbands out of 135 total subbands in the band
161 Watt_u txPower,
162 MHz_u guardBandwidth)
163{
164 NS_LOG_FUNCTION(centerFrequency << txPower << +guardBandwidth);
165 MHz_u channelWidth = 22; // DSSS channels are 22 MHz wide
166 Hz_u carrierSpacing = 312500;
168 GetSpectrumModel({centerFrequency}, channelWidth, carrierSpacing, guardBandwidth));
169 auto vit = c->ValuesBegin();
170 auto bit = c->ConstBandsBegin();
171 auto nGuardBands = static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
172 auto nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
173 NS_ASSERT(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1));
174 // Evenly spread power across 22 MHz
175 const auto txPowerPerBand = txPower / nAllocatedBands;
176 const auto psd = txPowerPerBand / (bit->fh - bit->fl);
177 for (size_t i = 0; i < c->GetSpectrumModel()->GetNumBands(); i++, vit++, bit++)
178 {
179 if ((i >= (nGuardBands / 2)) && (i <= ((nGuardBands / 2) + nAllocatedBands - 1)))
180 {
181 *vit = psd;
182 }
183 }
184 return c;
185}
186
189 MHz_u channelWidth,
190 Watt_u txPower,
191 MHz_u guardBandwidth,
192 dBr_u minInnerBand,
193 dBr_u minOuterBand,
194 dBr_u lowestPoint)
195{
196 NS_LOG_FUNCTION(centerFrequency << channelWidth << txPower << guardBandwidth << minInnerBand
197 << minOuterBand << lowestPoint);
198 Hz_u carrierSpacing = 0;
199 uint32_t innerSlopeWidth = 0;
200 switch (static_cast<uint16_t>(channelWidth))
201 {
202 case 20:
203 carrierSpacing = 312500; // Hz
204 innerSlopeWidth = static_cast<uint32_t>((2e6 / carrierSpacing) + 0.5); // [-11;-9] & [9;11]
205 break;
206 case 10:
207 carrierSpacing = 156250; // Hz
208 innerSlopeWidth =
209 static_cast<uint32_t>((1e6 / carrierSpacing) + 0.5); // [-5.5;-4.5] & [4.5;5.5]
210 break;
211 case 5:
212 carrierSpacing = 78125; // Hz
213 innerSlopeWidth =
214 static_cast<uint32_t>((5e5 / carrierSpacing) + 0.5); // [-2.75;-2.5] & [2.5;2.75]
215 break;
216 default:
217 NS_FATAL_ERROR("Channel width " << channelWidth << " should be correctly set.");
218 return nullptr;
219 }
220
221 auto c = Create<SpectrumValue>(
222 GetSpectrumModel({centerFrequency}, channelWidth, carrierSpacing, guardBandwidth));
223 auto nGuardBands = static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
224 auto nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
225 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1),
226 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
227 // 52 subcarriers (48 data + 4 pilot)
228 // skip guard band and 6 subbands, then place power in 26 subbands, then
229 // skip the center subband, then place power in 26 subbands, then skip
230 // the final 6 subbands and the guard band.
231 const auto txPowerPerBand = txPower / 52;
232 NS_LOG_DEBUG("Power per band " << txPowerPerBand << "W");
233 uint32_t start1 = (nGuardBands / 2) + 6;
234 uint32_t stop1 = start1 + 26 - 1;
235 uint32_t start2 = stop1 + 2;
236 uint32_t stop2 = start2 + 26 - 1;
237
238 // Build transmit spectrum mask
239 std::vector<WifiSpectrumBandIndices> subBands{
240 std::make_pair(start1, stop1),
241 std::make_pair(start2, stop2),
242 };
243 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands);
245 {subBands},
246 maskBand,
247 txPowerPerBand,
248 nGuardBands,
249 innerSlopeWidth,
250 minInnerBand,
251 minOuterBand,
252 lowestPoint);
253 NormalizeSpectrumMask(c, txPower);
254 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
255 return c;
256}
257
260 const std::vector<MHz_u>& centerFrequencies,
261 MHz_u channelWidth,
262 Watt_u txPower,
263 MHz_u guardBandwidth,
264 dBr_u minInnerBand,
265 dBr_u minOuterBand,
266 dBr_u lowestPoint,
267 const std::vector<bool>& puncturedSubchannels)
268{
269 NS_ASSERT_MSG(centerFrequencies.size() == 1 ||
270 (channelWidth == 160 && centerFrequencies.size() <= 2),
271 "PSD for non-contiguous channels is only possible when the total width is 160 "
272 "MHz and cannot be made of more than 2 segments");
273 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
274 << channelWidth << txPower << guardBandwidth << minInnerBand << minOuterBand
275 << lowestPoint);
276 const Hz_u carrierSpacing = 312500;
278 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
279 // assume all frequency segments have the same width, hence split the guard bandwidth
280 // accordingly over the segments
281 guardBandwidth /= centerFrequencies.size();
282 const auto nGuardBands =
283 static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
284 const auto nAllocatedBands =
285 static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
286 const auto separationWidth = std::abs(centerFrequencies.back() - centerFrequencies.front());
287 const auto unallocatedWidth = separationWidth > 0 ? (separationWidth - (channelWidth / 2)) : 0;
288 const auto nUnallocatedBands =
289 static_cast<uint32_t>(((unallocatedWidth * 1e6) / carrierSpacing) + 0.5);
290 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() ==
291 (nAllocatedBands + nGuardBands + nUnallocatedBands + 1),
292 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
293 std::size_t num20MhzBands = channelWidth / 20;
294 std::size_t numAllocatedSubcarriersPer20MHz = 52;
295 NS_ASSERT(puncturedSubchannels.empty() || (puncturedSubchannels.size() == num20MhzBands));
296 const auto txPowerPerBand = (txPower / numAllocatedSubcarriersPer20MHz) / num20MhzBands;
297 NS_LOG_DEBUG("Power per band " << txPowerPerBand << "W");
298
299 std::size_t numSubcarriersPer20MHz = (20 * 1e6) / carrierSpacing;
300 std::size_t numUnallocatedSubcarriersPer20MHz =
301 numSubcarriersPer20MHz - numAllocatedSubcarriersPer20MHz;
302 std::vector<std::vector<WifiSpectrumBandIndices>> subBandsPerSegment(
303 centerFrequencies.size()); // list of data/pilot-containing subBands (sent at 0dBr)
304 for (std::size_t i = 0; i < centerFrequencies.size(); ++i)
305 {
306 subBandsPerSegment.at(i).resize(
307 num20MhzBands * 2 / centerFrequencies.size()); // the center subcarrier is skipped,
308 // hence 2 subbands per 20 MHz subchannel
309 }
310 std::vector<std::vector<WifiSpectrumBandIndices>> puncturedBandsPerSegment;
311 uint32_t start = (nGuardBands / 2) + (numUnallocatedSubcarriersPer20MHz / 2);
312 uint32_t stop;
313 uint8_t index = 0;
314 for (auto& subBands : subBandsPerSegment)
315 {
316 puncturedBandsPerSegment.emplace_back();
317 for (auto it = subBands.begin(); it != subBands.end();)
318 {
319 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
320 *it = std::make_pair(start, stop);
321 ++it;
322 uint32_t puncturedStart = start;
323 start = stop + 2; // skip center subcarrier
324 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
325 *it = std::make_pair(start, stop);
326 ++it;
327 start = stop + numUnallocatedSubcarriersPer20MHz;
328 uint32_t puncturedStop = stop;
329 if (!puncturedSubchannels.empty() && puncturedSubchannels.at(index++))
330 {
331 puncturedBandsPerSegment.back().emplace_back(puncturedStart, puncturedStop);
332 }
333 }
334 start += nUnallocatedBands;
335 }
336
337 // Prepare spectrum mask specific variables
338 auto innerSlopeWidth = static_cast<uint32_t>(
339 (2e6 / carrierSpacing) +
340 0.5); // size in number of subcarriers of the 0dBr<->20dBr slope (2MHz for HT/VHT)
341 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands + nUnallocatedBands);
342 const auto puncturedSlopeWidth =
343 static_cast<uint32_t>((500e3 / carrierSpacing) +
344 0.5); // size in number of subcarriers of the punctured slope band
345
346 // Build transmit spectrum mask
348 subBandsPerSegment,
349 maskBand,
350 txPowerPerBand,
351 nGuardBands,
352 innerSlopeWidth,
353 minInnerBand,
354 minOuterBand,
355 lowestPoint,
356 puncturedBandsPerSegment,
357 puncturedSlopeWidth);
358 NormalizeSpectrumMask(c, txPower);
359 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
360 return c;
361}
362
365 const std::vector<MHz_u>& centerFrequencies,
366 MHz_u channelWidth,
367 Watt_u txPower,
368 MHz_u guardBandwidth,
369 dBr_u minInnerBand,
370 dBr_u minOuterBand,
371 dBr_u lowestPoint)
372{
373 NS_ASSERT_MSG(centerFrequencies.size() == 1 ||
374 (channelWidth == 160 && centerFrequencies.size() <= 2),
375 "PSD for non-contiguous channels is only possible when the total width is 160 "
376 "MHz and cannot be made of more than 2 segments");
377 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
378 << channelWidth << txPower << guardBandwidth << minInnerBand << minOuterBand
379 << lowestPoint);
380 const Hz_u carrierSpacing = 312500;
382 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
383 // assume all frequency segments have the same width, hence split the guard bandwidth
384 // accordingly over the segments
385 guardBandwidth /= centerFrequencies.size();
386 const auto nGuardBands =
387 static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
388 const auto nAllocatedBands =
389 static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
390 const auto separationWidth = std::abs(centerFrequencies.back() - centerFrequencies.front());
391 const auto unallocatedWidth = separationWidth > 0 ? (separationWidth - (channelWidth / 2)) : 0;
392 const auto nUnallocatedBands =
393 static_cast<uint32_t>(((unallocatedWidth * 1e6) / carrierSpacing) + 0.5);
394 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() ==
395 (nAllocatedBands + nGuardBands + nUnallocatedBands + 1),
396 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
397 std::size_t num20MhzBands = channelWidth / 20;
398 std::size_t numAllocatedSubcarriersPer20MHz = 56;
399 const auto txPowerPerBand = (txPower / numAllocatedSubcarriersPer20MHz) / num20MhzBands;
400 NS_LOG_DEBUG("Power per band " << txPowerPerBand << "W");
401
402 std::size_t numSubcarriersPer20MHz = (20 * 1e6) / carrierSpacing;
403 std::size_t numUnallocatedSubcarriersPer20MHz =
404 numSubcarriersPer20MHz - numAllocatedSubcarriersPer20MHz;
405 std::vector<std::vector<WifiSpectrumBandIndices>> subBandsPerSegment(
406 centerFrequencies.size()); // list of data/pilot-containing subBands (sent at 0dBr)
407 for (std::size_t i = 0; i < centerFrequencies.size(); ++i)
408 {
409 subBandsPerSegment.at(i).resize(
410 num20MhzBands * 2 / centerFrequencies.size()); // the center subcarrier is skipped,
411 // hence 2 subbands per 20 MHz subchannel
412 }
413 uint32_t start = (nGuardBands / 2) + (numUnallocatedSubcarriersPer20MHz / 2);
414 uint32_t stop;
415 for (auto& subBands : subBandsPerSegment)
416 {
417 for (auto it = subBands.begin(); it != subBands.end();)
418 {
419 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
420 *it = std::make_pair(start, stop);
421 ++it;
422 start = stop + 2; // skip center subcarrier
423 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
424 *it = std::make_pair(start, stop);
425 ++it;
426 start = stop + numUnallocatedSubcarriersPer20MHz;
427 }
428 start += nUnallocatedBands;
429 }
430
431 // Prepare spectrum mask specific variables
432 auto innerSlopeWidth = static_cast<uint32_t>(
433 (2e6 / carrierSpacing) +
434 0.5); // size in number of subcarriers of the inner band (2MHz for HT/VHT)
435 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands + nUnallocatedBands);
436
437 // Build transmit spectrum mask
439 subBandsPerSegment,
440 maskBand,
441 txPowerPerBand,
442 nGuardBands,
443 innerSlopeWidth,
444 minInnerBand,
445 minOuterBand,
446 lowestPoint);
447 NormalizeSpectrumMask(c, txPower);
448 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
449 return c;
450}
451
454 MHz_u centerFrequency,
455 MHz_u channelWidth,
456 Watt_u txPower,
457 MHz_u guardBandwidth,
458 dBr_u minInnerBand,
459 dBr_u minOuterBand,
460 dBr_u lowestPoint,
461 const std::vector<bool>& puncturedSubchannels)
462{
463 return CreateHeOfdmTxPowerSpectralDensity(std::vector<MHz_u>{centerFrequency},
464 channelWidth,
465 txPower,
466 guardBandwidth,
467 minInnerBand,
468 minOuterBand,
469 lowestPoint,
470 puncturedSubchannels);
471}
472
475 const std::vector<MHz_u>& centerFrequencies,
476 MHz_u channelWidth,
477 Watt_u txPower,
478 MHz_u guardBandwidth,
479 dBr_u minInnerBand,
480 dBr_u minOuterBand,
481 dBr_u lowestPoint,
482 const std::vector<bool>& puncturedSubchannels)
483{
485 centerFrequencies.size() == 1 || channelWidth == 160,
486 "PSD for non-contiguous channels is only possible when the total width is 160 MHz");
487 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
488 << channelWidth << txPower << guardBandwidth << minInnerBand << minOuterBand
489 << lowestPoint);
490 const Hz_u carrierSpacing = 78125;
492 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
493 // assume all frequency segments have the same width, hence split the guard bandwidth
494 // accordingly over the segments
495 guardBandwidth /= centerFrequencies.size();
496 const auto nGuardBands =
497 static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
498 const auto separationWidth = std::abs(centerFrequencies.back() - centerFrequencies.front());
499 const auto unallocatedWidth = separationWidth > 0 ? (separationWidth - (channelWidth / 2)) : 0;
500 const auto nUnallocatedBands =
501 static_cast<uint32_t>(((unallocatedWidth * 1e6) / carrierSpacing) + 0.5);
502 const auto nAllocatedBands =
503 static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
504 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() ==
505 (nAllocatedBands + nGuardBands + nUnallocatedBands + 1),
506 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
507 Watt_u txPowerPerBand = 0.0;
508 uint32_t start1;
509 uint32_t stop1;
510 uint32_t start2;
511 uint32_t stop2;
512 uint32_t start3;
513 uint32_t stop3;
514 uint32_t start4;
515 uint32_t stop4;
516 // Prepare spectrum mask specific variables
517 auto innerSlopeWidth = static_cast<uint32_t>(
518 (1e6 / carrierSpacing) + 0.5); // size in number of subcarriers of the inner band
519 std::vector<std::vector<WifiSpectrumBandIndices>> subBandsPerSegment(
520 centerFrequencies.size()); // list of data/pilot-containing subBands (sent at 0dBr)
521 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands + nUnallocatedBands);
522 switch (static_cast<uint16_t>(channelWidth))
523 {
524 case 20:
525 // 242 subcarriers (234 data + 8 pilot)
526 txPowerPerBand = txPower / 242;
527 innerSlopeWidth =
528 static_cast<uint32_t>((5e5 / carrierSpacing) + 0.5); // [-10.25;-9.75] & [9.75;10.25]
529 // skip the guard band and 6 subbands, then place power in 121 subbands, then
530 // skip 3 DC, then place power in 121 subbands, then skip
531 // the final 5 subbands and the guard band.
532 start1 = (nGuardBands / 2) + 6;
533 stop1 = start1 + 121 - 1;
534 start2 = stop1 + 4;
535 stop2 = start2 + 121 - 1;
536 subBandsPerSegment.at(0).emplace_back(start1, stop1);
537 subBandsPerSegment.at(0).emplace_back(start2, stop2);
538 break;
539 case 40:
540 // 484 subcarriers (468 data + 16 pilot)
541 txPowerPerBand = txPower / 484;
542 // skip the guard band and 12 subbands, then place power in 242 subbands, then
543 // skip 5 DC, then place power in 242 subbands, then skip
544 // the final 11 subbands and the guard band.
545 start1 = (nGuardBands / 2) + 12;
546 stop1 = start1 + 242 - 1;
547 start2 = stop1 + 6;
548 stop2 = start2 + 242 - 1;
549 subBandsPerSegment.at(0).emplace_back(start1, stop1);
550 subBandsPerSegment.at(0).emplace_back(start2, stop2);
551 break;
552 case 80:
553 // 996 subcarriers (980 data + 16 pilot)
554 txPowerPerBand = txPower / 996;
555 // skip the guard band and 12 subbands, then place power in 498 subbands, then
556 // skip 5 DC, then place power in 498 subbands, then skip
557 // the final 11 subbands and the guard band.
558 start1 = (nGuardBands / 2) + 12;
559 stop1 = start1 + 498 - 1;
560 start2 = stop1 + 6;
561 stop2 = start2 + 498 - 1;
562 subBandsPerSegment.at(0).emplace_back(start1, stop1);
563 subBandsPerSegment.at(0).emplace_back(start2, stop2);
564 break;
565 case 160: {
566 NS_ASSERT_MSG(centerFrequencies.size() <= 2,
567 "It is not possible to create a PSD made of more than 2 segments for a width "
568 "of 160 MHz");
569 // 2 x 996 subcarriers (2 x 80 MHZ bands)
570 txPowerPerBand = txPower / (2 * 996);
571 start1 = (nGuardBands / 2) + 12;
572 stop1 = start1 + 498 - 1;
573 start2 = stop1 + 6;
574 stop2 = start2 + 498 - 1;
575 start3 = stop2 + (2 * 12) + nUnallocatedBands;
576 stop3 = start3 + 498 - 1;
577 start4 = stop3 + 6;
578 stop4 = start4 + 498 - 1;
579 subBandsPerSegment.at(0).emplace_back(start1, stop1);
580 subBandsPerSegment.at(0).emplace_back(start2, stop2);
581 subBandsPerSegment.at(subBandsPerSegment.size() - 1).emplace_back(start3, stop3);
582 subBandsPerSegment.at(subBandsPerSegment.size() - 1).emplace_back(start4, stop4);
583 break;
584 }
585 default:
586 NS_FATAL_ERROR("ChannelWidth " << channelWidth << " unsupported");
587 break;
588 }
589
590 // Create punctured bands
591 auto puncturedSlopeWidth =
592 static_cast<uint32_t>((500e3 / carrierSpacing) +
593 0.5); // size in number of subcarriers of the punctured slope band
594 std::vector<std::vector<WifiSpectrumBandIndices>> puncturedBandsPerSegment;
595 std::size_t subcarriersPerSuband = (20 * 1e6 / carrierSpacing);
596 uint32_t start = (nGuardBands / 2);
597 uint32_t stop = start + subcarriersPerSuband - 1;
598 if (!puncturedSubchannels.empty())
599 {
600 for (std::size_t i = 0; i < subBandsPerSegment.size(); ++i)
601 {
602 puncturedBandsPerSegment.emplace_back();
603 }
604 }
605 std::size_t prevPsdIndex = 0;
606 for (std::size_t i = 0; i < puncturedSubchannels.size(); ++i)
607 {
608 std::size_t psdIndex = (puncturedBandsPerSegment.size() == 1)
609 ? 0
610 : ((i < (puncturedSubchannels.size() / 2)) ? 0 : 1);
611 if (psdIndex != prevPsdIndex)
612 {
613 start += nUnallocatedBands;
614 stop += nUnallocatedBands;
615 }
616 if (puncturedSubchannels.at(i))
617 {
618 puncturedBandsPerSegment.at(psdIndex).emplace_back(start, stop);
619 }
620 start = stop + 1;
621 stop = start + subcarriersPerSuband - 1;
622 prevPsdIndex = psdIndex;
623 }
624
625 // Build transmit spectrum mask
627 subBandsPerSegment,
628 maskBand,
629 txPowerPerBand,
630 nGuardBands,
631 innerSlopeWidth,
632 minInnerBand,
633 minOuterBand,
634 lowestPoint,
635 puncturedBandsPerSegment,
636 puncturedSlopeWidth);
637 NormalizeSpectrumMask(c, txPower);
638 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
639 return c;
640}
641
644 const std::vector<MHz_u>& centerFrequencies,
645 MHz_u channelWidth,
646 Watt_u txPower,
647 MHz_u guardBandwidth,
648 const std::vector<WifiSpectrumBandIndices>& ru)
649{
650 auto printRuIndices = [](const std::vector<WifiSpectrumBandIndices>& v) {
651 std::stringstream ss;
652 for (const auto& [start, stop] : v)
653 {
654 ss << start << "-" << stop << " ";
655 }
656 return ss.str();
657 };
658 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
659 << channelWidth << txPower << guardBandwidth << printRuIndices(ru));
660 const Hz_u carrierSpacing = 78125;
662 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
663
664 // Build spectrum mask
665 auto vit = c->ValuesBegin();
666 auto bit = c->ConstBandsBegin();
667 const auto numSubcarriers =
668 std::accumulate(ru.cbegin(), ru.cend(), 0, [](uint32_t sum, const auto& p) {
669 return sum + (p.second - p.first) + 1;
670 });
671 const auto txPowerPerBand = (txPower / numSubcarriers); // FIXME: null subcarriers
672 uint32_t numBands = c->GetSpectrumModel()->GetNumBands();
673 const auto psd = txPowerPerBand / (bit->fh - bit->fl);
674 for (size_t i = 0; i < numBands; i++, vit++, bit++)
675 {
676 const auto allocated = std::any_of(ru.cbegin(), ru.cend(), [i](const auto& p) {
677 return (i >= p.first && i <= p.second);
678 });
679 *vit = allocated ? psd : 0.0;
680 }
681
682 return c;
683}
684
685void
688 const std::vector<std::vector<WifiSpectrumBandIndices>>& allocatedSubBandsPerSegment,
689 const WifiSpectrumBandIndices& maskBand,
690 Watt_u txPowerPerBand,
691 uint32_t nGuardBands,
692 uint32_t innerSlopeWidth,
693 dBr_u minInnerBand,
694 dBr_u minOuterBand,
695 dBr_u lowestPoint,
696 const std::vector<std::vector<WifiSpectrumBandIndices>>& puncturedBandsPerSegment,
697 uint32_t puncturedSlopeWidth)
698{
699 NS_ASSERT_MSG(allocatedSubBandsPerSegment.size() <= 2,
700 "Only PSDs for up to 2 frequency segments are supported");
701 NS_ASSERT(puncturedBandsPerSegment.empty() ||
702 (puncturedBandsPerSegment.size() == allocatedSubBandsPerSegment.size()));
703 NS_LOG_FUNCTION(c << allocatedSubBandsPerSegment.front().front().first
704 << allocatedSubBandsPerSegment.front().back().second << maskBand.first
705 << maskBand.second << txPowerPerBand << nGuardBands << innerSlopeWidth
706 << minInnerBand << minOuterBand << lowestPoint << puncturedSlopeWidth);
707 uint32_t numSubBands = allocatedSubBandsPerSegment.front().size();
708 uint32_t numBands = c->GetSpectrumModel()->GetNumBands();
709 uint32_t numMaskBands = maskBand.second - maskBand.first + 1;
710 NS_ASSERT(numSubBands && numBands && numMaskBands);
711 NS_LOG_LOGIC("Power per band " << txPowerPerBand << "W");
712
713 // Different power levels
714 dBm_u txPowerRef = (10.0 * std::log10(txPowerPerBand * 1000.0));
715 dBm_u txPowerInnerBandMin = txPowerRef + minInnerBand;
716 dBm_u txPowerMiddleBandMin = txPowerRef + minOuterBand;
717 dBm_u txPowerOuterBandMin =
718 txPowerRef + lowestPoint; // TODO also take into account dBm/MHz constraints
719
720 // Different widths (in number of bands)
721 uint32_t outerSlopeWidth =
722 nGuardBands / 4; // nGuardBands is the total left+right guard band. The left/right outer
723 // part is half of the left/right guard band.
724 uint32_t middleSlopeWidth = outerSlopeWidth - (innerSlopeWidth / 2);
725
726 std::vector<WifiSpectrumBandIndices> outerBandsLeft;
727 std::vector<WifiSpectrumBandIndices> middleBandsLeft;
728 std::vector<WifiSpectrumBandIndices> flatJunctionsLeft;
729 std::vector<WifiSpectrumBandIndices> innerBandsLeft;
730 std::vector<WifiSpectrumBandIndices> allocatedSubBands;
731 std::vector<WifiSpectrumBandIndices> innerBandsRight;
732 std::vector<WifiSpectrumBandIndices> flatJunctionsRight;
733 std::vector<WifiSpectrumBandIndices> middleBandsRight;
734 std::vector<WifiSpectrumBandIndices> outerBandsRight;
735 std::optional<WifiSpectrumBandIndices> betweenPsdsBand;
736
737 allocatedSubBands.emplace_back(allocatedSubBandsPerSegment.front().front().first,
738 allocatedSubBandsPerSegment.front().back().second);
739 outerBandsLeft.emplace_back(maskBand.first, // to handle cases where allocated channel is
740 // under WifiPhy configured channel width.
741 maskBand.first + outerSlopeWidth - 1);
742 middleBandsLeft.emplace_back(outerBandsLeft.front().second + 1,
743 outerBandsLeft.front().second + middleSlopeWidth);
744 innerBandsLeft.emplace_back(allocatedSubBands.front().first - innerSlopeWidth,
745 allocatedSubBands.front().first -
746 1); // better to place slope based on allocated subcarriers
747 flatJunctionsLeft.emplace_back(middleBandsLeft.front().second + 1,
748 innerBandsLeft.front().first -
749 1); // in order to handle shift due to guard subcarriers
750 uint32_t flatJunctionWidth =
751 flatJunctionsLeft.front().second - flatJunctionsLeft.front().first + 1;
752 innerBandsRight.emplace_back(allocatedSubBands.front().second + 1,
753 allocatedSubBands.front().second + innerSlopeWidth);
754 flatJunctionsRight.emplace_back(innerBandsRight.front().second + 1,
755 innerBandsRight.front().second + flatJunctionWidth);
756 middleBandsRight.emplace_back(flatJunctionsRight.front().second + 1,
757 flatJunctionsRight.front().second + middleSlopeWidth);
758 outerBandsRight.emplace_back(middleBandsRight.front().second + 1,
759 middleBandsRight.front().second + outerSlopeWidth);
760
761 if (allocatedSubBandsPerSegment.size() > 1)
762 {
763 const auto offset = (((allocatedSubBandsPerSegment.front().back().second -
764 allocatedSubBandsPerSegment.front().front().first) /
765 2) +
766 (allocatedSubBandsPerSegment.back().front().first -
767 allocatedSubBandsPerSegment.front().back().second) +
768 ((allocatedSubBandsPerSegment.back().back().second -
769 allocatedSubBandsPerSegment.back().front().first) /
770 2));
771 outerBandsLeft.emplace_back(outerBandsLeft.front().first + offset,
772 outerBandsLeft.front().second + offset);
773 middleBandsLeft.emplace_back(middleBandsLeft.front().first + offset,
774 middleBandsLeft.front().second + offset);
775 flatJunctionsLeft.emplace_back(flatJunctionsLeft.front().first + offset,
776 flatJunctionsLeft.front().second + offset);
777 innerBandsLeft.emplace_back(innerBandsLeft.front().first + offset,
778 innerBandsLeft.front().second + offset);
779 allocatedSubBands.emplace_back(allocatedSubBands.front().first + offset,
780 allocatedSubBands.front().second + offset);
781 innerBandsRight.emplace_back(innerBandsRight.front().first + offset,
782 innerBandsRight.front().second + offset);
783 flatJunctionsRight.emplace_back(flatJunctionsRight.front().first + offset,
784 flatJunctionsRight.front().second + offset);
785 middleBandsRight.emplace_back(middleBandsRight.front().first + offset,
786 middleBandsRight.front().second + offset);
787 outerBandsRight.emplace_back(outerBandsRight.front().first + offset,
788 outerBandsRight.front().second + offset);
789 betweenPsdsBand.emplace(middleBandsRight.front().first, middleBandsLeft.back().second);
790 }
791
792 std::ostringstream ss;
793 for (std::size_t i = 0; i < allocatedSubBandsPerSegment.size(); ++i)
794 {
795 if (allocatedSubBandsPerSegment.size() > 1)
796 {
797 ss << "PSD" << i + 1 << ": ";
798 }
799 ss << "outerBandLeft=[" << outerBandsLeft.at(i).first << ";" << outerBandsLeft.at(i).second
800 << "] "
801 << "middleBandLeft=[" << middleBandsLeft.at(i).first << ";"
802 << middleBandsLeft.at(i).second << "] "
803 << "flatJunctionLeft=[" << flatJunctionsLeft.at(i).first << ";"
804 << flatJunctionsLeft.at(i).second << "] "
805 << "innerBandLeft=[" << innerBandsLeft.at(i).first << ";" << innerBandsLeft.at(i).second
806 << "] "
807 << "allocatedBand=[" << allocatedSubBands.at(i).first << ";"
808 << allocatedSubBands.at(i).second << "] ";
809 if (!puncturedBandsPerSegment.empty() && !puncturedBandsPerSegment.at(i).empty())
810 {
811 ss << "puncturedBands=[" << puncturedBandsPerSegment.at(i).front().first << ";"
812 << puncturedBandsPerSegment.at(i).back().second << "] ";
813 }
814 ss << "innerBandRight=[" << innerBandsRight.at(i).first << ";"
815 << innerBandsRight.at(i).second << "] "
816 << "flatJunctionRight=[" << flatJunctionsRight.at(i).first << ";"
817 << flatJunctionsRight.at(i).second << "] "
818 << "middleBandRight=[" << middleBandsRight.at(i).first << ";"
819 << middleBandsRight.at(i).second << "] "
820 << "outerBandRight=[" << outerBandsRight.at(i).first << ";"
821 << outerBandsRight.at(i).second << "] ";
822 }
823 if (allocatedSubBandsPerSegment.size() > 1)
824 {
825 ss << "=> PSD: "
826 << "outerBandLeft=[" << outerBandsLeft.front().first << ";"
827 << outerBandsLeft.front().second << "] "
828 << "middleBandLeft=[" << middleBandsLeft.front().first << ";"
829 << middleBandsLeft.front().second << "] "
830 << "flatJunctionLeft=[" << flatJunctionsLeft.front().first << ";"
831 << flatJunctionsLeft.front().second << "] "
832 << "innerBandLeft=[" << innerBandsLeft.front().first << ";"
833 << innerBandsLeft.front().second << "] "
834 << "allocatedBandInPsd1=[" << allocatedSubBands.front().first << ";"
835 << allocatedSubBands.front().second << "] ";
836 if (!puncturedBandsPerSegment.empty() && !puncturedBandsPerSegment.front().empty())
837 {
838 ss << "puncturedBandsInPsd1=[" << puncturedBandsPerSegment.front().front().first << ";"
839 << puncturedBandsPerSegment.front().back().second << "] ";
840 }
841 ss << "flatJunctionRightPsd1=[" << flatJunctionsRight.front().first << ";"
842 << flatJunctionsRight.front().second << "] "
843 << "linearSum=[" << betweenPsdsBand->first << ";" << betweenPsdsBand->second << "] "
844 << "flatJunctionLeftPsd2=[" << flatJunctionsLeft.back().first << ";"
845 << flatJunctionsLeft.back().second << "] "
846 << "innerBandLeftPsd2=[" << innerBandsLeft.back().first << ";"
847 << innerBandsLeft.back().second << "] "
848 << "allocatedBandInPsd2=[" << allocatedSubBands.back().first << ";"
849 << allocatedSubBands.back().second << "] ";
850 if (!puncturedBandsPerSegment.empty() && !puncturedBandsPerSegment.back().empty())
851 {
852 ss << "puncturedBandsInPsd2=[" << puncturedBandsPerSegment.back().front().first << ";"
853 << puncturedBandsPerSegment.back().back().second << "] ";
854 }
855 ss << "innerBandRight=[" << innerBandsRight.back().first << ";"
856 << innerBandsRight.back().second << "] "
857 << "flatJunctionRight=[" << flatJunctionsRight.back().first << ";"
858 << flatJunctionsRight.back().second << "] "
859 << "middleBandRight=[" << middleBandsRight.back().first << ";"
860 << middleBandsRight.back().second << "] "
861 << "outerBandRight=[" << outerBandsRight.back().first << ";"
862 << outerBandsRight.back().second << "] ";
863 }
864 NS_LOG_DEBUG(ss.str());
865 NS_ASSERT(maskBand.second == outerBandsRight.back().second);
866 NS_ASSERT(numMaskBands ==
867 ((allocatedSubBandsPerSegment.back().back().second -
868 allocatedSubBandsPerSegment.front().front().first +
869 1) // equivalent to allocatedBand (includes notches and DC)
870 + 2 * (innerSlopeWidth + middleSlopeWidth + outerSlopeWidth + flatJunctionWidth)));
871
872 // Different slopes
873 double innerSlope = (-1.0 * minInnerBand) / innerSlopeWidth;
874 double middleSlope = (-1.0 * (minOuterBand - minInnerBand)) / middleSlopeWidth;
875 double outerSlope = (txPowerMiddleBandMin - txPowerOuterBandMin) / outerSlopeWidth;
876 double puncturedSlope = (-1.0 * minInnerBand) / puncturedSlopeWidth;
877
878 // Build spectrum mask
879 Watt_u previousTxPower = 0.0;
880 std::vector<Watt_u> txPowerValues(numBands);
881 NS_ASSERT(txPowerValues.size() == numBands);
882 for (size_t i = 0; i < numBands; ++i)
883 {
884 size_t psdIndex =
885 (allocatedSubBandsPerSegment.size() == 1) ? 0 : ((i < (numBands / 2)) ? 0 : 1);
886 Watt_u txPower = 0.0;
887 if (i < maskBand.first || i > maskBand.second) // outside the spectrum mask
888 {
889 txPower = 0.0;
890 }
891 else if (betweenPsdsBand.has_value() &&
892 (i <= betweenPsdsBand->second && i >= betweenPsdsBand->first))
893 {
894 // value for PSD mask 1
895 std::vector<double> txPowerWPsds(2);
896 if (i <= middleBandsRight.at(0).second && i >= middleBandsRight.at(0).first)
897 {
898 txPowerWPsds.at(0) =
899 DbmToW(txPowerInnerBandMin -
900 ((i - middleBandsRight.at(0).first + 1) *
901 middleSlope)); // +1 so as to be symmetric with left slope
902 }
903 else if (i <= outerBandsRight.at(0).second && i >= outerBandsRight.at(0).first)
904 {
905 txPowerWPsds.at(0) =
906 DbmToW(txPowerMiddleBandMin -
907 ((i - outerBandsRight.at(0).first + 1) *
908 outerSlope)); // +1 so as to be symmetric with left slope
909 }
910 else if (i > outerBandsRight.at(0).second)
911 {
912 txPower = DbmToW(txPowerOuterBandMin);
913 }
914 else
915 {
916 NS_ASSERT(false);
917 }
918
919 // value for PSD mask 2
920 if (i < outerBandsLeft.at(1).first)
921 {
922 txPower = DbmToW(txPowerOuterBandMin);
923 }
924 else if (i <= outerBandsLeft.at(1).second && i >= outerBandsLeft.at(1).first)
925 {
926 txPowerWPsds.at(1) =
927 DbmToW(txPowerOuterBandMin + ((i - outerBandsLeft.at(1).first) * outerSlope));
928 }
929 else if (i <= middleBandsLeft.at(1).second && i >= middleBandsLeft.at(1).first)
930 {
931 txPowerWPsds.at(1) = DbmToW(txPowerMiddleBandMin +
932 ((i - middleBandsLeft.at(1).first) * middleSlope));
933 }
934 else
935 {
936 NS_ASSERT(false);
937 }
938
939 txPower = std::accumulate(txPowerWPsds.cbegin(), txPowerWPsds.cend(), 0.0);
940 txPower = std::max(DbmToW(txPowerRef - 25.0), txPower);
941 txPower = std::min(DbmToW(txPowerRef - 20.0), txPower);
942 }
943 else if (i <= outerBandsLeft.at(psdIndex).second &&
944 i >= outerBandsLeft.at(psdIndex)
945 .first) // better to put greater first (less computation)
946 {
947 txPower = DbmToW(txPowerOuterBandMin +
948 ((i - outerBandsLeft.at(psdIndex).first) * outerSlope));
949 }
950 else if (i <= middleBandsLeft.at(psdIndex).second &&
951 i >= middleBandsLeft.at(psdIndex).first)
952 {
953 txPower = DbmToW(txPowerMiddleBandMin +
954 ((i - middleBandsLeft.at(psdIndex).first) * middleSlope));
955 }
956 else if ((i <= flatJunctionsLeft.at(psdIndex).second &&
957 i >= flatJunctionsLeft.at(psdIndex).first) ||
958 (i <= flatJunctionsRight.at(psdIndex).second &&
959 i >= flatJunctionsRight.at(psdIndex).first))
960 {
961 txPower = DbmToW(txPowerInnerBandMin);
962 }
963 else if (i <= innerBandsLeft.at(psdIndex).second && i >= innerBandsLeft.at(psdIndex).first)
964 {
965 txPower = (!puncturedBandsPerSegment.empty() &&
966 !puncturedBandsPerSegment.at(psdIndex).empty() &&
967 (puncturedBandsPerSegment.at(psdIndex).front().first <=
968 allocatedSubBandsPerSegment.at(psdIndex).front().first))
969 ? DbmToW(txPowerInnerBandMin)
970 : // first 20 MHz band is punctured
971 DbmToW(txPowerInnerBandMin +
972 ((i - innerBandsLeft.at(psdIndex).first) * innerSlope));
973 }
974 else if ((i <= allocatedSubBandsPerSegment.at(psdIndex).back().second &&
975 i >= allocatedSubBandsPerSegment.at(psdIndex).front().first)) // roughly in
976 // allocated band
977 {
978 bool insideSubBand = false;
979 for (uint32_t j = 0; !insideSubBand && j < numSubBands;
980 j++) // continue until inside a sub-band
981 {
982 insideSubBand = (i <= allocatedSubBandsPerSegment.at(psdIndex)[j].second) &&
983 (i >= allocatedSubBandsPerSegment.at(psdIndex)[j].first);
984 }
985 if (insideSubBand)
986 {
987 bool insidePuncturedSubBand = false;
988 std::size_t j = 0;
989 std::size_t puncturedBandSize = !puncturedBandsPerSegment.empty()
990 ? puncturedBandsPerSegment.at(psdIndex).size()
991 : 0;
992 for (; !insidePuncturedSubBand && j < puncturedBandSize;
993 j++) // continue until inside a punctured sub-band
994 {
995 insidePuncturedSubBand =
996 (i <= puncturedBandsPerSegment.at(psdIndex).at(j).second) &&
997 (i >= puncturedBandsPerSegment.at(psdIndex).at(j).first);
998 }
999 if (insidePuncturedSubBand)
1000 {
1001 uint32_t startPuncturedSlope =
1002 (puncturedBandsPerSegment.at(psdIndex)
1003 .at(puncturedBandsPerSegment.at(psdIndex).size() - 1)
1004 .second -
1005 puncturedSlopeWidth); // only consecutive subchannels can be punctured
1006 if (i >= startPuncturedSlope)
1007 {
1008 txPower = DbmToW(txPowerInnerBandMin +
1009 ((i - startPuncturedSlope) * puncturedSlope));
1010 }
1011 else
1012 {
1013 txPower = std::max(
1014 DbmToW(txPowerInnerBandMin),
1015 DbmToW(txPowerRef -
1016 ((i - puncturedBandsPerSegment.at(psdIndex).at(0).first) *
1017 puncturedSlope)));
1018 }
1019 }
1020 else
1021 {
1022 txPower = txPowerPerBand;
1023 }
1024 }
1025 else
1026 {
1027 txPower = DbmToW(txPowerInnerBandMin);
1028 }
1029 }
1030 else if (i <= innerBandsRight.at(psdIndex).second &&
1031 i >= innerBandsRight.at(psdIndex).first)
1032 {
1033 // take min to handle the case where last 20 MHz band is punctured
1034 txPower = std::min(
1035 previousTxPower,
1036 DbmToW(txPowerRef - ((i - innerBandsRight.at(psdIndex).first + 1) *
1037 innerSlope))); // +1 so as to be symmetric with left slope
1038 }
1039 else if (i <= middleBandsRight.at(psdIndex).second &&
1040 i >= middleBandsRight.at(psdIndex).first)
1041 {
1042 txPower = DbmToW(txPowerInnerBandMin -
1043 ((i - middleBandsRight.at(psdIndex).first + 1) *
1044 middleSlope)); // +1 so as to be symmetric with left slope
1045 }
1046 else if (i <= outerBandsRight.at(psdIndex).second &&
1047 i >= outerBandsRight.at(psdIndex).first)
1048 {
1049 txPower = DbmToW(txPowerMiddleBandMin -
1050 ((i - outerBandsRight.at(psdIndex).first + 1) *
1051 outerSlope)); // +1 so as to be symmetric with left slope
1052 }
1053 else
1054 {
1055 NS_FATAL_ERROR("Should have handled all cases");
1056 }
1057 NS_LOG_LOGIC(i << " -> " << (10 * std::log10(txPower / txPowerPerBand)));
1058 previousTxPower = txPower;
1059 txPowerValues.at(i) = txPower;
1060 }
1061
1062 // fill in spectrum mask
1063 auto vit = c->ValuesBegin();
1064 auto bit = c->ConstBandsBegin();
1065 const auto invBandwidth = 1 / (bit->fh - bit->fl);
1066 for (auto txPowerValue : txPowerValues)
1067 {
1068 *vit = txPowerValue * invBandwidth;
1069 vit++;
1070 bit++;
1071 }
1072
1073 for (const auto& allocatedSubBands : allocatedSubBandsPerSegment)
1074 {
1075 NS_LOG_INFO("Added signal power to subbands " << allocatedSubBands.front().first << "-"
1076 << allocatedSubBands.back().second);
1077 }
1078}
1079
1080void
1082{
1083 NS_LOG_FUNCTION(c << txPower);
1084 // Normalize power so that total signal power equals transmit power
1085 Watt_u currentTxPower = Integral(*c);
1086 double normalizationRatio [[maybe_unused]] = currentTxPower / txPower;
1087 double invNormalizationRatio = txPower / currentTxPower;
1088 NS_LOG_LOGIC("Current power: " << currentTxPower << "W vs expected power: " << txPower << "W"
1089 << " -> ratio (C/E) = " << normalizationRatio);
1090 auto vit = c->ValuesBegin();
1091 for (size_t i = 0; i < c->GetSpectrumModel()->GetNumBands(); i++, vit++)
1092 {
1093 *vit = (*vit) * invNormalizationRatio;
1094 }
1095}
1096
1097Watt_u
1099 const std::vector<WifiSpectrumBandIndices>& segments)
1100{
1101 auto powerWattPerHertz{0.0};
1102 auto bandIt = psd->ConstBandsBegin() + segments.front().first; // all bands have same width
1103 const auto bandWidth = (bandIt->fh - bandIt->fl);
1104 NS_ASSERT_MSG(bandWidth >= 0.0,
1105 "Invalid width for subband [" << bandIt->fl << ";" << bandIt->fh << "]");
1106 for (const auto& [start, stop] : segments)
1107 {
1108 auto valueIt = psd->ConstValuesBegin() + start;
1109 auto end = psd->ConstValuesBegin() + stop;
1110 uint32_t index [[maybe_unused]] = 0;
1111 while (valueIt <= end)
1112 {
1113 NS_ASSERT_MSG(*valueIt >= 0.0,
1114 "Invalid power value " << *valueIt << " in subband " << index);
1115 powerWattPerHertz += *valueIt;
1116 ++valueIt;
1117 ++index;
1118 }
1119 }
1120 const Watt_u power = powerWattPerHertz * bandWidth;
1121 NS_ASSERT_MSG(power >= 0.0, "Invalid calculated power " << power);
1122 return power;
1123}
1124
1125bool
1126operator<(const FrequencyRange& left, const FrequencyRange& right)
1127{
1128 return left.minFrequency < right.minFrequency;
1129}
1130
1131bool
1132operator==(const FrequencyRange& left, const FrequencyRange& right)
1133{
1134 return (left.minFrequency == right.minFrequency) && (left.maxFrequency == right.maxFrequency);
1135}
1136
1137bool
1138operator!=(const FrequencyRange& left, const FrequencyRange& right)
1139{
1140 return !(left == right);
1141}
1142
1143std::ostream&
1144operator<<(std::ostream& os, const FrequencyRange& freqRange)
1145{
1146 os << "[" << freqRange.minFrequency << " MHz - " << freqRange.maxFrequency << " MHz]";
1147 return os;
1148}
1149
1150} // namespace ns3
Smart pointer class similar to boost::intrusive_ptr.
static Ptr< SpectrumValue > CreateOfdmTxPowerSpectralDensity(MHz_u centerFrequency, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=-20, dBr_u minOuterband=-28, dBr_u lowestPoint=-40)
Create a transmit power spectral density corresponding to OFDM (802.11a/g).
static Ptr< SpectrumValue > CreateDsssTxPowerSpectralDensity(MHz_u centerFrequency, Watt_u txPower, MHz_u guardBandwidth)
Create a transmit power spectral density corresponding to DSSS.
static void CreateSpectrumMaskForOfdm(Ptr< SpectrumValue > c, const std::vector< std::vector< WifiSpectrumBandIndices > > &allocatedSubBandsPerSegment, const WifiSpectrumBandIndices &maskBand, Watt_u txPowerPerBand, uint32_t nGuardBands, uint32_t innerSlopeWidth, dBr_u minInnerBand, dBr_u minOuterband, dBr_u lowestPoint, const std::vector< std::vector< WifiSpectrumBandIndices > > &puncturedSubBands={}, uint32_t puncturedSlopeWidth=0)
Create a transmit power spectral density corresponding to OFDM transmit spectrum mask requirements fo...
static Ptr< SpectrumModel > GetSpectrumModel(const std::vector< MHz_u > &centerFrequencies, MHz_u channelWidth, Hz_u carrierSpacing, MHz_u guardBandwidth)
Return a SpectrumModel instance corresponding to the center frequency and channel width.
static Watt_u GetBandPowerW(Ptr< SpectrumValue > psd, const std::vector< WifiSpectrumBandIndices > &segments)
Calculate the power of the specified band composed of uniformly-sized sub-bands.
static Ptr< SpectrumValue > CreateHtOfdmTxPowerSpectralDensity(const std::vector< MHz_u > &centerFrequencies, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=-20, dBr_u minOuterband=-28, dBr_u lowestPoint=-40)
Create a transmit power spectral density corresponding to OFDM High Throughput (HT) (802....
static Ptr< SpectrumValue > CreateHeMuOfdmTxPowerSpectralDensity(const std::vector< MHz_u > &centerFrequencies, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, const std::vector< WifiSpectrumBandIndices > &ru)
Create a transmit power spectral density corresponding to the OFDMA part of HE TB PPDUs for a given R...
static Ptr< SpectrumValue > CreateHeOfdmTxPowerSpectralDensity(MHz_u centerFrequency, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=-20, dBr_u minOuterband=-28, dBr_u lowestPoint=-40, const std::vector< bool > &puncturedSubchannels={})
Create a transmit power spectral density corresponding to OFDM High Efficiency (HE) (802....
static Ptr< SpectrumValue > CreateDuplicated20MhzTxPowerSpectralDensity(const std::vector< MHz_u > &centerFrequencies, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=-20, dBr_u minOuterband=-28, dBr_u lowestPoint=-40, const std::vector< bool > &puncturedSubchannels={})
Create a transmit power spectral density corresponding to OFDM duplicated over multiple 20 MHz subcha...
static void NormalizeSpectrumMask(Ptr< SpectrumValue > c, Watt_u txPower)
Normalize the transmit spectrum mask generated by CreateSpectrumMaskForOfdm so that the total transmi...
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
auto printFrequencies
Lambda to print a vector of frequencies.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
bool operator!=(Callback< R, Args... > a, Callback< R, Args... > b)
Inequality test.
Definition callback.h:658
bool operator==(const EventId &a, const EventId &b)
Definition event-id.h:155
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition angles.cc:148
double Integral(const SpectrumValue &arg)
std::vector< BandInfo > Bands
Container of BandInfo.
bool operator<(const EventId &a, const EventId &b)
Definition event-id.h:168
static std::map< WifiSpectrumModelId, Ptr< SpectrumModel > > g_wifiSpectrumModelMap
static initializer for the class
Watt_u DbmToW(dBm_u val)
Convert from dBm to Watts.
Definition wifi-utils.cc:31
double Watt_u
Watt weak type.
Definition wifi-units.h:25
std::pair< uint32_t, uint32_t > WifiSpectrumBandIndices
typedef for a pair of start and stop sub-band indices
The building block of a SpectrumModel.
double fc
center frequency
double fl
lower limit of subband
double fh
upper limit of subband
Struct defining a frequency range between minFrequency and maxFrequency.
MHz_u minFrequency
the minimum frequency
MHz_u maxFrequency
the maximum frequency
Wifi Spectrum Model structure.
std::vector< MHz_u > centerFrequencies
center frequency per segment