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 ? MHz_u{0}
114 : (*maxCenterFrequency - *minCenterFrequency - (channelWidth / 2));
115 NS_ASSERT(separationWidth == MHz_u{0} || centerFrequencies.size() == 2);
116 const auto bandwidth = MHzToHz((channelWidth + (2 * guardBandwidth) + separationWidth));
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 startingFrequency = MHzToHz(*minCenterFrequency) -
136 (MHzToHz(channelWidth) / (2 * centerFrequencies.size())) -
137 MHzToHz(guardBandwidth) - (carrierSpacing / 2);
138 for (size_t i = 0; i < numBands; i++)
139 {
140 BandInfo info;
141 auto f = startingFrequency + (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 =
172 static_cast<uint32_t>(((MHzToHz(2 * guardBandwidth)) / carrierSpacing) + 0.5);
173 auto nAllocatedBands = static_cast<uint32_t>((MHzToHz(channelWidth) / carrierSpacing) + 0.5);
174 NS_ASSERT(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1));
175 // Evenly spread power across 22 MHz
176 const auto txPowerPerBand = txPower / nAllocatedBands;
177 const auto psd = txPowerPerBand / (bit->fh - bit->fl);
178 for (size_t i = 0; i < c->GetSpectrumModel()->GetNumBands(); i++, vit++, bit++)
179 {
180 if ((i >= (nGuardBands / 2)) && (i <= ((nGuardBands / 2) + nAllocatedBands - 1)))
181 {
182 *vit = psd;
183 }
184 }
185 return c;
186}
187
190 MHz_u channelWidth,
191 Watt_u txPower,
192 MHz_u guardBandwidth,
193 dBr_u minInnerBand,
194 dBr_u minOuterBand,
195 dBr_u lowestPoint)
196{
197 NS_LOG_FUNCTION(centerFrequency << channelWidth << txPower << guardBandwidth << minInnerBand
198 << minOuterBand << lowestPoint);
199 Hz_u carrierSpacing{0};
200 uint32_t innerSlopeWidth = 0;
201 switch (static_cast<uint16_t>(channelWidth))
202 {
203 case 20:
204 carrierSpacing = Hz_u{312500};
205 innerSlopeWidth =
206 static_cast<uint32_t>((Hz_u{2e6} / carrierSpacing) + 0.5); // [-11;-9] & [9;11]
207 break;
208 case 10:
209 carrierSpacing = Hz_u{156250};
210 innerSlopeWidth =
211 static_cast<uint32_t>((Hz_u{1e6} / carrierSpacing) + 0.5); // [-5.5;-4.5] & [4.5;5.5]
212 break;
213 case 5:
214 carrierSpacing = Hz_u{78125};
215 innerSlopeWidth =
216 static_cast<uint32_t>((Hz_u{5e5} / carrierSpacing) + 0.5); // [-2.75;-2.5] & [2.5;2.75]
217 break;
218 default:
219 NS_FATAL_ERROR("Channel width " << channelWidth << " should be correctly set.");
220 return nullptr;
221 }
222
223 auto c = Create<SpectrumValue>(
224 GetSpectrumModel({centerFrequency}, channelWidth, carrierSpacing, guardBandwidth));
225 auto nGuardBands =
226 static_cast<uint32_t>(((MHzToHz(2 * guardBandwidth)) / carrierSpacing) + 0.5);
227 auto nAllocatedBands = static_cast<uint32_t>((MHzToHz(channelWidth) / carrierSpacing) + 0.5);
228 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1),
229 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
230 // 52 subcarriers (48 data + 4 pilot)
231 // skip guard band and 6 subbands, then place power in 26 subbands, then
232 // skip the center subband, then place power in 26 subbands, then skip
233 // the final 6 subbands and the guard band.
234 const auto txPowerPerBand = txPower / 52;
235 NS_LOG_DEBUG("Power per band " << txPowerPerBand << "W");
236 uint32_t start1 = (nGuardBands / 2) + 6;
237 uint32_t stop1 = start1 + 26 - 1;
238 uint32_t start2 = stop1 + 2;
239 uint32_t stop2 = start2 + 26 - 1;
240
241 // Build transmit spectrum mask
242 std::vector<WifiSpectrumBandIndices> subBands{
243 std::make_pair(start1, stop1),
244 std::make_pair(start2, stop2),
245 };
246 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands);
248 {subBands},
249 maskBand,
250 txPowerPerBand,
251 nGuardBands,
252 innerSlopeWidth,
253 minInnerBand,
254 minOuterBand,
255 lowestPoint);
256 NormalizeSpectrumMask(c, txPower);
257 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
258 return c;
259}
260
263 const std::vector<MHz_u>& centerFrequencies,
264 MHz_u channelWidth,
265 Watt_u txPower,
266 MHz_u guardBandwidth,
267 dBr_u minInnerBand,
268 dBr_u minOuterBand,
269 dBr_u lowestPoint,
270 const std::vector<bool>& puncturedSubchannels)
271{
272 NS_ASSERT_MSG(centerFrequencies.size() == 1 ||
273 (channelWidth == MHz_u{160} && centerFrequencies.size() <= 2),
274 "PSD for non-contiguous channels is only possible when the total width is 160 "
275 "MHz and cannot be made of more than 2 segments");
276 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
277 << channelWidth << txPower << guardBandwidth << minInnerBand << minOuterBand
278 << lowestPoint);
279 const Hz_u carrierSpacing{312500};
281 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
282 // assume all frequency segments have the same width, hence split the guard bandwidth
283 // accordingly over the segments
284 guardBandwidth /= centerFrequencies.size();
285 const auto nGuardBands =
286 static_cast<uint32_t>(((MHzToHz(2 * guardBandwidth)) / carrierSpacing) + 0.5);
287 const auto nAllocatedBands =
288 static_cast<uint32_t>((MHzToHz(channelWidth) / carrierSpacing) + 0.5);
289 const auto separationWidth = std::abs(centerFrequencies.back() - centerFrequencies.front());
290 const auto unallocatedWidth =
291 separationWidth > Hz_u{0} ? (separationWidth - (channelWidth / 2)) : 0;
292 const auto nUnallocatedBands =
293 static_cast<uint32_t>((MHzToHz(unallocatedWidth) / carrierSpacing) + 0.5);
294 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() ==
295 (nAllocatedBands + nGuardBands + nUnallocatedBands + 1),
296 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
297 auto num20MhzBands = Count20MHzSubchannels(channelWidth);
298 std::size_t numAllocatedSubcarriersPer20MHz = 52;
299 NS_ASSERT(puncturedSubchannels.empty() || (puncturedSubchannels.size() == num20MhzBands));
300 const auto txPowerPerBand = (txPower / numAllocatedSubcarriersPer20MHz) / num20MhzBands;
301 NS_LOG_DEBUG("Power per band " << txPowerPerBand << "W");
302
303 std::size_t numSubcarriersPer20MHz = MHzToHz(MHz_u{20}) / carrierSpacing;
304 std::size_t numUnallocatedSubcarriersPer20MHz =
305 numSubcarriersPer20MHz - numAllocatedSubcarriersPer20MHz;
306 std::vector<std::vector<WifiSpectrumBandIndices>> subBandsPerSegment(
307 centerFrequencies.size()); // list of data/pilot-containing subBands (sent at 0dBr)
308 for (std::size_t i = 0; i < centerFrequencies.size(); ++i)
309 {
310 subBandsPerSegment.at(i).resize(
311 num20MhzBands * 2 / centerFrequencies.size()); // the center subcarrier is skipped,
312 // hence 2 subbands per 20 MHz subchannel
313 }
314 std::vector<std::vector<WifiSpectrumBandIndices>> puncturedBandsPerSegment;
315 uint32_t start = (nGuardBands / 2) + (numUnallocatedSubcarriersPer20MHz / 2);
316 uint32_t stop;
317 uint8_t index = 0;
318 for (auto& subBands : subBandsPerSegment)
319 {
320 puncturedBandsPerSegment.emplace_back();
321 for (auto it = subBands.begin(); it != subBands.end();)
322 {
323 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
324 *it = std::make_pair(start, stop);
325 ++it;
326 uint32_t puncturedStart = start;
327 start = stop + 2; // skip center subcarrier
328 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
329 *it = std::make_pair(start, stop);
330 ++it;
331 start = stop + numUnallocatedSubcarriersPer20MHz;
332 uint32_t puncturedStop = stop;
333 if (!puncturedSubchannels.empty() && puncturedSubchannels.at(index++))
334 {
335 puncturedBandsPerSegment.back().emplace_back(puncturedStart, puncturedStop);
336 }
337 }
338 start += nUnallocatedBands;
339 }
340
341 // Prepare spectrum mask specific variables
342 auto innerSlopeWidth = static_cast<uint32_t>(
343 (Hz_u{2e6} / carrierSpacing) +
344 0.5); // size in number of subcarriers of the 0dBr<->20dBr slope (2MHz for HT/VHT)
345 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands + nUnallocatedBands);
346 const auto puncturedSlopeWidth =
347 static_cast<uint32_t>((Hz_u{500e3} / carrierSpacing) +
348 0.5); // size in number of subcarriers of the punctured slope band
349
350 // Build transmit spectrum mask
352 subBandsPerSegment,
353 maskBand,
354 txPowerPerBand,
355 nGuardBands,
356 innerSlopeWidth,
357 minInnerBand,
358 minOuterBand,
359 lowestPoint,
360 puncturedBandsPerSegment,
361 puncturedSlopeWidth);
362 NormalizeSpectrumMask(c, txPower);
363 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
364 return c;
365}
366
369 const std::vector<MHz_u>& centerFrequencies,
370 MHz_u channelWidth,
371 Watt_u txPower,
372 MHz_u guardBandwidth,
373 dBr_u minInnerBand,
374 dBr_u minOuterBand,
375 dBr_u lowestPoint)
376{
377 NS_ASSERT_MSG(centerFrequencies.size() == 1 ||
378 (channelWidth == MHz_u{160} && centerFrequencies.size() <= 2),
379 "PSD for non-contiguous channels is only possible when the total width is 160 "
380 "MHz and cannot be made of more than 2 segments");
381 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
382 << channelWidth << txPower << guardBandwidth << minInnerBand << minOuterBand
383 << lowestPoint);
384 const Hz_u carrierSpacing{312500};
386 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
387 // assume all frequency segments have the same width, hence split the guard bandwidth
388 // accordingly over the segments
389 guardBandwidth /= centerFrequencies.size();
390 const auto nGuardBands =
391 static_cast<uint32_t>(((MHzToHz(2 * guardBandwidth)) / carrierSpacing) + 0.5);
392 const auto nAllocatedBands =
393 static_cast<uint32_t>((MHzToHz(channelWidth) / carrierSpacing) + 0.5);
394 const auto separationWidth = std::abs(centerFrequencies.back() - centerFrequencies.front());
395 const auto unallocatedWidth =
396 separationWidth > Hz_u{0} ? (separationWidth - (channelWidth / 2)) : 0;
397 const auto nUnallocatedBands =
398 static_cast<uint32_t>((MHzToHz(unallocatedWidth) / carrierSpacing) + 0.5);
399 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() ==
400 (nAllocatedBands + nGuardBands + nUnallocatedBands + 1),
401 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
402 auto num20MhzBands = Count20MHzSubchannels(channelWidth);
403 std::size_t numAllocatedSubcarriersPer20MHz = 56;
404 const auto txPowerPerBand = (txPower / numAllocatedSubcarriersPer20MHz) / num20MhzBands;
405 NS_LOG_DEBUG("Power per band " << txPowerPerBand << "W");
406
407 std::size_t numSubcarriersPer20MHz = MHzToHz(MHz_u{20}) / carrierSpacing;
408 std::size_t numUnallocatedSubcarriersPer20MHz =
409 numSubcarriersPer20MHz - numAllocatedSubcarriersPer20MHz;
410 std::vector<std::vector<WifiSpectrumBandIndices>> subBandsPerSegment(
411 centerFrequencies.size()); // list of data/pilot-containing subBands (sent at 0dBr)
412 for (std::size_t i = 0; i < centerFrequencies.size(); ++i)
413 {
414 subBandsPerSegment.at(i).resize(
415 num20MhzBands * 2 / centerFrequencies.size()); // the center subcarrier is skipped,
416 // hence 2 subbands per 20 MHz subchannel
417 }
418 uint32_t start = (nGuardBands / 2) + (numUnallocatedSubcarriersPer20MHz / 2);
419 uint32_t stop;
420 for (auto& subBands : subBandsPerSegment)
421 {
422 for (auto it = subBands.begin(); it != subBands.end();)
423 {
424 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
425 *it = std::make_pair(start, stop);
426 ++it;
427 start = stop + 2; // skip center subcarrier
428 stop = start + (numAllocatedSubcarriersPer20MHz / 2) - 1;
429 *it = std::make_pair(start, stop);
430 ++it;
431 start = stop + numUnallocatedSubcarriersPer20MHz;
432 }
433 start += nUnallocatedBands;
434 }
435
436 // Prepare spectrum mask specific variables
437 auto innerSlopeWidth = static_cast<uint32_t>(
438 (Hz_u{2e6} / carrierSpacing) +
439 0.5); // size in number of subcarriers of the inner band (2MHz for HT/VHT)
440 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands + nUnallocatedBands);
441
442 // Build transmit spectrum mask
444 subBandsPerSegment,
445 maskBand,
446 txPowerPerBand,
447 nGuardBands,
448 innerSlopeWidth,
449 minInnerBand,
450 minOuterBand,
451 lowestPoint);
452 NormalizeSpectrumMask(c, txPower);
453 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
454 return c;
455}
456
459 MHz_u centerFrequency,
460 MHz_u channelWidth,
461 Watt_u txPower,
462 MHz_u guardBandwidth,
463 dBr_u minInnerBand,
464 dBr_u minOuterBand,
465 dBr_u lowestPoint,
466 const std::vector<bool>& puncturedSubchannels)
467{
468 return CreateHeOfdmTxPowerSpectralDensity(std::vector<MHz_u>{centerFrequency},
469 channelWidth,
470 txPower,
471 guardBandwidth,
472 minInnerBand,
473 minOuterBand,
474 lowestPoint,
475 puncturedSubchannels);
476}
477
480 const std::vector<MHz_u>& centerFrequencies,
481 MHz_u channelWidth,
482 Watt_u txPower,
483 MHz_u guardBandwidth,
484 dBr_u minInnerBand,
485 dBr_u minOuterBand,
486 dBr_u lowestPoint,
487 const std::vector<bool>& puncturedSubchannels)
488{
490 centerFrequencies.size() == 1 || channelWidth == MHz_u{160},
491 "PSD for non-contiguous channels is only possible when the total width is 160 MHz");
492 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
493 << channelWidth << txPower << guardBandwidth << minInnerBand << minOuterBand
494 << lowestPoint);
495 const Hz_u carrierSpacing{78125};
497 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
498 // assume all frequency segments have the same width, hence split the guard bandwidth
499 // accordingly over the segments
500 guardBandwidth /= centerFrequencies.size();
501 const auto nGuardBands =
502 static_cast<uint32_t>(((MHzToHz(2 * guardBandwidth)) / carrierSpacing) + 0.5);
503 const auto separationWidth = std::abs(centerFrequencies.back() - centerFrequencies.front());
504 const auto unallocatedWidth =
505 separationWidth > Hz_u{0} ? (separationWidth - (channelWidth / 2)) : 0;
506 const auto nUnallocatedBands =
507 static_cast<uint32_t>((MHzToHz(unallocatedWidth) / carrierSpacing) + 0.5);
508 const auto nAllocatedBands =
509 static_cast<uint32_t>((MHzToHz(channelWidth) / carrierSpacing) + 0.5);
510 NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() ==
511 (nAllocatedBands + nGuardBands + nUnallocatedBands + 1),
512 "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
513 Watt_u txPowerPerBand{0.0};
514 uint32_t start1;
515 uint32_t stop1;
516 uint32_t start2;
517 uint32_t stop2;
518 uint32_t start3;
519 uint32_t stop3;
520 uint32_t start4;
521 uint32_t stop4;
522 // Prepare spectrum mask specific variables
523 auto innerSlopeWidth =
524 static_cast<uint32_t>((MHzToHz(MHz_u{1}) / carrierSpacing) +
525 0.5); // size in number of subcarriers of the inner band
526 std::vector<std::vector<WifiSpectrumBandIndices>> subBandsPerSegment(
527 centerFrequencies.size()); // list of data/pilot-containing subBands (sent at 0dBr)
528 WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands + nUnallocatedBands);
529 switch (static_cast<uint16_t>(channelWidth))
530 {
531 case 20:
532 // 242 subcarriers (234 data + 8 pilot)
533 txPowerPerBand = txPower / 242;
534 innerSlopeWidth = static_cast<uint32_t>((Hz_u{5e5} / carrierSpacing) +
535 0.5); // [-10.25;-9.75] & [9.75;10.25]
536 // skip the guard band and 6 subbands, then place power in 121 subbands, then
537 // skip 3 DC, then place power in 121 subbands, then skip
538 // the final 5 subbands and the guard band.
539 start1 = (nGuardBands / 2) + 6;
540 stop1 = start1 + 121 - 1;
541 start2 = stop1 + 4;
542 stop2 = start2 + 121 - 1;
543 subBandsPerSegment.at(0).emplace_back(start1, stop1);
544 subBandsPerSegment.at(0).emplace_back(start2, stop2);
545 break;
546 case 40:
547 // 484 subcarriers (468 data + 16 pilot)
548 txPowerPerBand = txPower / 484;
549 // skip the guard band and 12 subbands, then place power in 242 subbands, then
550 // skip 5 DC, then place power in 242 subbands, then skip
551 // the final 11 subbands and the guard band.
552 start1 = (nGuardBands / 2) + 12;
553 stop1 = start1 + 242 - 1;
554 start2 = stop1 + 6;
555 stop2 = start2 + 242 - 1;
556 subBandsPerSegment.at(0).emplace_back(start1, stop1);
557 subBandsPerSegment.at(0).emplace_back(start2, stop2);
558 break;
559 case 80:
560 // 996 subcarriers (980 data + 16 pilot)
561 txPowerPerBand = txPower / 996;
562 // skip the guard band and 12 subbands, then place power in 498 subbands, then
563 // skip 5 DC, then place power in 498 subbands, then skip
564 // the final 11 subbands and the guard band.
565 start1 = (nGuardBands / 2) + 12;
566 stop1 = start1 + 498 - 1;
567 start2 = stop1 + 6;
568 stop2 = start2 + 498 - 1;
569 subBandsPerSegment.at(0).emplace_back(start1, stop1);
570 subBandsPerSegment.at(0).emplace_back(start2, stop2);
571 break;
572 case 160: {
573 NS_ASSERT_MSG(centerFrequencies.size() <= 2,
574 "It is not possible to create a PSD made of more than 2 segments for a width "
575 "of 160 MHz");
576 // 2 x 996 subcarriers (2 x 80 MHZ bands)
577 txPowerPerBand = txPower / (2 * 996);
578 start1 = (nGuardBands / 2) + 12;
579 stop1 = start1 + 498 - 1;
580 start2 = stop1 + 6;
581 stop2 = start2 + 498 - 1;
582 start3 = stop2 + (2 * 12) + nUnallocatedBands;
583 stop3 = start3 + 498 - 1;
584 start4 = stop3 + 6;
585 stop4 = start4 + 498 - 1;
586 subBandsPerSegment.at(0).emplace_back(start1, stop1);
587 subBandsPerSegment.at(0).emplace_back(start2, stop2);
588 subBandsPerSegment.at(subBandsPerSegment.size() - 1).emplace_back(start3, stop3);
589 subBandsPerSegment.at(subBandsPerSegment.size() - 1).emplace_back(start4, stop4);
590 break;
591 }
592 default:
593 NS_FATAL_ERROR("ChannelWidth " << channelWidth << " unsupported");
594 break;
595 }
596
597 // Create punctured bands
598 auto puncturedSlopeWidth =
599 static_cast<uint32_t>((Hz_u{500e3} / carrierSpacing) +
600 0.5); // size in number of subcarriers of the punctured slope band
601 std::vector<std::vector<WifiSpectrumBandIndices>> puncturedBandsPerSegment;
602 std::size_t subcarriersPerSuband = (MHzToHz(MHz_u{20}) / carrierSpacing);
603 uint32_t start = (nGuardBands / 2);
604 uint32_t stop = start + subcarriersPerSuband - 1;
605 if (!puncturedSubchannels.empty())
606 {
607 for (std::size_t i = 0; i < subBandsPerSegment.size(); ++i)
608 {
609 puncturedBandsPerSegment.emplace_back();
610 }
611 }
612 std::size_t prevPsdIndex = 0;
613 for (std::size_t i = 0; i < puncturedSubchannels.size(); ++i)
614 {
615 std::size_t psdIndex = (puncturedBandsPerSegment.size() == 1)
616 ? 0
617 : ((i < (puncturedSubchannels.size() / 2)) ? 0 : 1);
618 if (psdIndex != prevPsdIndex)
619 {
620 start += nUnallocatedBands;
621 stop += nUnallocatedBands;
622 }
623 if (puncturedSubchannels.at(i))
624 {
625 puncturedBandsPerSegment.at(psdIndex).emplace_back(start, stop);
626 }
627 start = stop + 1;
628 stop = start + subcarriersPerSuband - 1;
629 prevPsdIndex = psdIndex;
630 }
631
632 // Build transmit spectrum mask
634 subBandsPerSegment,
635 maskBand,
636 txPowerPerBand,
637 nGuardBands,
638 innerSlopeWidth,
639 minInnerBand,
640 minOuterBand,
641 lowestPoint,
642 puncturedBandsPerSegment,
643 puncturedSlopeWidth);
644 NormalizeSpectrumMask(c, txPower);
645 NS_ASSERT_MSG(std::abs(txPower - Integral(*c)) < 1e-6, "Power allocation failed");
646 return c;
647}
648
651 const std::vector<MHz_u>& centerFrequencies,
652 MHz_u channelWidth,
653 Watt_u txPower,
654 MHz_u guardBandwidth,
655 const std::vector<WifiSpectrumBandIndices>& ru)
656{
657 auto printRuIndices = [](const std::vector<WifiSpectrumBandIndices>& v) {
658 std::stringstream ss;
659 for (const auto& [start, stop] : v)
660 {
661 ss << start << "-" << stop << " ";
662 }
663 return ss.str();
664 };
665 NS_LOG_FUNCTION(printFrequencies(centerFrequencies)
666 << channelWidth << txPower << guardBandwidth << printRuIndices(ru));
667 const Hz_u carrierSpacing{78125};
669 GetSpectrumModel(centerFrequencies, channelWidth, carrierSpacing, guardBandwidth));
670
671 // Build spectrum mask
672 auto vit = c->ValuesBegin();
673 auto bit = c->ConstBandsBegin();
674 const auto numSubcarriers =
675 std::accumulate(ru.cbegin(), ru.cend(), 0, [](uint32_t sum, const auto& p) {
676 return sum + (p.second - p.first) + 1;
677 });
678 const auto txPowerPerBand = (txPower / numSubcarriers); // FIXME: null subcarriers
679 uint32_t numBands = c->GetSpectrumModel()->GetNumBands();
680 const auto psd = txPowerPerBand / (bit->fh - bit->fl);
681 for (size_t i = 0; i < numBands; i++, vit++, bit++)
682 {
683 const auto allocated = std::any_of(ru.cbegin(), ru.cend(), [i](const auto& p) {
684 return (i >= p.first && i <= p.second);
685 });
686 *vit = allocated ? psd : 0.0;
687 }
688
689 return c;
690}
691
692void
695 const std::vector<std::vector<WifiSpectrumBandIndices>>& allocatedSubBandsPerSegment,
696 const WifiSpectrumBandIndices& maskBand,
697 Watt_u txPowerPerBand,
698 uint32_t nGuardBands,
699 uint32_t innerSlopeWidth,
700 dBr_u minInnerBand,
701 dBr_u minOuterBand,
702 dBr_u lowestPoint,
703 const std::vector<std::vector<WifiSpectrumBandIndices>>& puncturedBandsPerSegment,
704 uint32_t puncturedSlopeWidth)
705{
706 NS_ASSERT_MSG(allocatedSubBandsPerSegment.size() <= 2,
707 "Only PSDs for up to 2 frequency segments are supported");
708 NS_ASSERT(puncturedBandsPerSegment.empty() ||
709 (puncturedBandsPerSegment.size() == allocatedSubBandsPerSegment.size()));
710 NS_LOG_FUNCTION(c << allocatedSubBandsPerSegment.front().front().first
711 << allocatedSubBandsPerSegment.front().back().second << maskBand.first
712 << maskBand.second << txPowerPerBand << nGuardBands << innerSlopeWidth
713 << minInnerBand << minOuterBand << lowestPoint << puncturedSlopeWidth);
714 uint32_t numSubBands = allocatedSubBandsPerSegment.front().size();
715 uint32_t numBands = c->GetSpectrumModel()->GetNumBands();
716 uint32_t numMaskBands = maskBand.second - maskBand.first + 1;
717 NS_ASSERT(numSubBands && numBands && numMaskBands);
718 NS_LOG_LOGIC("Power per band " << txPowerPerBand << "W");
719
720 // Different power levels
721 dBm_u txPowerRef{10.0 * std::log10(txPowerPerBand * 1000.0)};
722 dBm_u txPowerInnerBandMin{txPowerRef + minInnerBand};
723 dBm_u txPowerMiddleBandMin{txPowerRef + minOuterBand};
724 dBm_u txPowerOuterBandMin{txPowerRef +
725 lowestPoint}; // TODO also take into account dBm/MHz constraints
726
727 // Different widths (in number of bands)
728 uint32_t outerSlopeWidth =
729 nGuardBands / 4; // nGuardBands is the total left+right guard band. The left/right outer
730 // part is half of the left/right guard band.
731 uint32_t middleSlopeWidth = outerSlopeWidth - (innerSlopeWidth / 2);
732
733 std::vector<WifiSpectrumBandIndices> outerBandsLeft;
734 std::vector<WifiSpectrumBandIndices> middleBandsLeft;
735 std::vector<WifiSpectrumBandIndices> flatJunctionsLeft;
736 std::vector<WifiSpectrumBandIndices> innerBandsLeft;
737 std::vector<WifiSpectrumBandIndices> allocatedSubBands;
738 std::vector<WifiSpectrumBandIndices> innerBandsRight;
739 std::vector<WifiSpectrumBandIndices> flatJunctionsRight;
740 std::vector<WifiSpectrumBandIndices> middleBandsRight;
741 std::vector<WifiSpectrumBandIndices> outerBandsRight;
742 std::optional<WifiSpectrumBandIndices> betweenPsdsBand;
743
744 allocatedSubBands.emplace_back(allocatedSubBandsPerSegment.front().front().first,
745 allocatedSubBandsPerSegment.front().back().second);
746 outerBandsLeft.emplace_back(maskBand.first, // to handle cases where allocated channel is
747 // under WifiPhy configured channel width.
748 maskBand.first + outerSlopeWidth - 1);
749 middleBandsLeft.emplace_back(outerBandsLeft.front().second + 1,
750 outerBandsLeft.front().second + middleSlopeWidth);
751 innerBandsLeft.emplace_back(allocatedSubBands.front().first - innerSlopeWidth,
752 allocatedSubBands.front().first -
753 1); // better to place slope based on allocated subcarriers
754 flatJunctionsLeft.emplace_back(middleBandsLeft.front().second + 1,
755 innerBandsLeft.front().first -
756 1); // in order to handle shift due to guard subcarriers
757 uint32_t flatJunctionWidth =
758 flatJunctionsLeft.front().second - flatJunctionsLeft.front().first + 1;
759 innerBandsRight.emplace_back(allocatedSubBands.front().second + 1,
760 allocatedSubBands.front().second + innerSlopeWidth);
761 flatJunctionsRight.emplace_back(innerBandsRight.front().second + 1,
762 innerBandsRight.front().second + flatJunctionWidth);
763 middleBandsRight.emplace_back(flatJunctionsRight.front().second + 1,
764 flatJunctionsRight.front().second + middleSlopeWidth);
765 outerBandsRight.emplace_back(middleBandsRight.front().second + 1,
766 middleBandsRight.front().second + outerSlopeWidth);
767
768 if (allocatedSubBandsPerSegment.size() > 1)
769 {
770 const auto offset = (((allocatedSubBandsPerSegment.front().back().second -
771 allocatedSubBandsPerSegment.front().front().first) /
772 2) +
773 (allocatedSubBandsPerSegment.back().front().first -
774 allocatedSubBandsPerSegment.front().back().second) +
775 ((allocatedSubBandsPerSegment.back().back().second -
776 allocatedSubBandsPerSegment.back().front().first) /
777 2));
778 outerBandsLeft.emplace_back(outerBandsLeft.front().first + offset,
779 outerBandsLeft.front().second + offset);
780 middleBandsLeft.emplace_back(middleBandsLeft.front().first + offset,
781 middleBandsLeft.front().second + offset);
782 flatJunctionsLeft.emplace_back(flatJunctionsLeft.front().first + offset,
783 flatJunctionsLeft.front().second + offset);
784 innerBandsLeft.emplace_back(innerBandsLeft.front().first + offset,
785 innerBandsLeft.front().second + offset);
786 allocatedSubBands.emplace_back(allocatedSubBands.front().first + offset,
787 allocatedSubBands.front().second + offset);
788 innerBandsRight.emplace_back(innerBandsRight.front().first + offset,
789 innerBandsRight.front().second + offset);
790 flatJunctionsRight.emplace_back(flatJunctionsRight.front().first + offset,
791 flatJunctionsRight.front().second + offset);
792 middleBandsRight.emplace_back(middleBandsRight.front().first + offset,
793 middleBandsRight.front().second + offset);
794 outerBandsRight.emplace_back(outerBandsRight.front().first + offset,
795 outerBandsRight.front().second + offset);
796 betweenPsdsBand.emplace(middleBandsRight.front().first, middleBandsLeft.back().second);
797 }
798
799 std::ostringstream ss;
800 for (std::size_t i = 0; i < allocatedSubBandsPerSegment.size(); ++i)
801 {
802 if (allocatedSubBandsPerSegment.size() > 1)
803 {
804 ss << "PSD" << i + 1 << ": ";
805 }
806 ss << "outerBandLeft=[" << outerBandsLeft.at(i).first << ";" << outerBandsLeft.at(i).second
807 << "] "
808 << "middleBandLeft=[" << middleBandsLeft.at(i).first << ";"
809 << middleBandsLeft.at(i).second << "] "
810 << "flatJunctionLeft=[" << flatJunctionsLeft.at(i).first << ";"
811 << flatJunctionsLeft.at(i).second << "] "
812 << "innerBandLeft=[" << innerBandsLeft.at(i).first << ";" << innerBandsLeft.at(i).second
813 << "] "
814 << "allocatedBand=[" << allocatedSubBands.at(i).first << ";"
815 << allocatedSubBands.at(i).second << "] ";
816 if (!puncturedBandsPerSegment.empty() && !puncturedBandsPerSegment.at(i).empty())
817 {
818 ss << "puncturedBands=[" << puncturedBandsPerSegment.at(i).front().first << ";"
819 << puncturedBandsPerSegment.at(i).back().second << "] ";
820 }
821 ss << "innerBandRight=[" << innerBandsRight.at(i).first << ";"
822 << innerBandsRight.at(i).second << "] "
823 << "flatJunctionRight=[" << flatJunctionsRight.at(i).first << ";"
824 << flatJunctionsRight.at(i).second << "] "
825 << "middleBandRight=[" << middleBandsRight.at(i).first << ";"
826 << middleBandsRight.at(i).second << "] "
827 << "outerBandRight=[" << outerBandsRight.at(i).first << ";"
828 << outerBandsRight.at(i).second << "] ";
829 }
830 if (allocatedSubBandsPerSegment.size() > 1)
831 {
832 ss << "=> PSD: "
833 << "outerBandLeft=[" << outerBandsLeft.front().first << ";"
834 << outerBandsLeft.front().second << "] "
835 << "middleBandLeft=[" << middleBandsLeft.front().first << ";"
836 << middleBandsLeft.front().second << "] "
837 << "flatJunctionLeft=[" << flatJunctionsLeft.front().first << ";"
838 << flatJunctionsLeft.front().second << "] "
839 << "innerBandLeft=[" << innerBandsLeft.front().first << ";"
840 << innerBandsLeft.front().second << "] "
841 << "allocatedBandInPsd1=[" << allocatedSubBands.front().first << ";"
842 << allocatedSubBands.front().second << "] ";
843 if (!puncturedBandsPerSegment.empty() && !puncturedBandsPerSegment.front().empty())
844 {
845 ss << "puncturedBandsInPsd1=[" << puncturedBandsPerSegment.front().front().first << ";"
846 << puncturedBandsPerSegment.front().back().second << "] ";
847 }
848 ss << "flatJunctionRightPsd1=[" << flatJunctionsRight.front().first << ";"
849 << flatJunctionsRight.front().second << "] "
850 << "linearSum=[" << betweenPsdsBand->first << ";" << betweenPsdsBand->second << "] "
851 << "flatJunctionLeftPsd2=[" << flatJunctionsLeft.back().first << ";"
852 << flatJunctionsLeft.back().second << "] "
853 << "innerBandLeftPsd2=[" << innerBandsLeft.back().first << ";"
854 << innerBandsLeft.back().second << "] "
855 << "allocatedBandInPsd2=[" << allocatedSubBands.back().first << ";"
856 << allocatedSubBands.back().second << "] ";
857 if (!puncturedBandsPerSegment.empty() && !puncturedBandsPerSegment.back().empty())
858 {
859 ss << "puncturedBandsInPsd2=[" << puncturedBandsPerSegment.back().front().first << ";"
860 << puncturedBandsPerSegment.back().back().second << "] ";
861 }
862 ss << "innerBandRight=[" << innerBandsRight.back().first << ";"
863 << innerBandsRight.back().second << "] "
864 << "flatJunctionRight=[" << flatJunctionsRight.back().first << ";"
865 << flatJunctionsRight.back().second << "] "
866 << "middleBandRight=[" << middleBandsRight.back().first << ";"
867 << middleBandsRight.back().second << "] "
868 << "outerBandRight=[" << outerBandsRight.back().first << ";"
869 << outerBandsRight.back().second << "] ";
870 }
871 NS_LOG_DEBUG(ss.str());
872 NS_ASSERT(maskBand.second == outerBandsRight.back().second);
873 NS_ASSERT(numMaskBands ==
874 ((allocatedSubBandsPerSegment.back().back().second -
875 allocatedSubBandsPerSegment.front().front().first +
876 1) // equivalent to allocatedBand (includes notches and DC)
877 + 2 * (innerSlopeWidth + middleSlopeWidth + outerSlopeWidth + flatJunctionWidth)));
878
879 // Different slopes
880 double innerSlope = (-1.0 * minInnerBand) / innerSlopeWidth;
881 double middleSlope = (-1.0 * (minOuterBand - minInnerBand)) / middleSlopeWidth;
882 double outerSlope = (txPowerMiddleBandMin - txPowerOuterBandMin) / outerSlopeWidth;
883 double puncturedSlope = (-1.0 * minInnerBand) / puncturedSlopeWidth;
884
885 // Build spectrum mask
886 Watt_u previousTxPower{0.0};
887 std::vector<Watt_u> txPowerValues(numBands);
888 NS_ASSERT(txPowerValues.size() == numBands);
889 for (size_t i = 0; i < numBands; ++i)
890 {
891 size_t psdIndex =
892 (allocatedSubBandsPerSegment.size() == 1) ? 0 : ((i < (numBands / 2)) ? 0 : 1);
893 Watt_u txPower{0.0};
894 if (i < maskBand.first || i > maskBand.second) // outside the spectrum mask
895 {
896 txPower = Watt_u{0.0};
897 }
898 else if (betweenPsdsBand.has_value() &&
899 (i <= betweenPsdsBand->second && i >= betweenPsdsBand->first))
900 {
901 // value for PSD mask 1
902 std::vector<Watt_u> txPowerWPsds(2);
903 if (i <= middleBandsRight.at(0).second && i >= middleBandsRight.at(0).first)
904 {
905 txPowerWPsds.at(0) =
906 DbmToW(txPowerInnerBandMin -
907 ((i - middleBandsRight.at(0).first + 1) *
908 middleSlope)); // +1 so as to be symmetric with left slope
909 }
910 else if (i <= outerBandsRight.at(0).second && i >= outerBandsRight.at(0).first)
911 {
912 txPowerWPsds.at(0) =
913 DbmToW(txPowerMiddleBandMin -
914 ((i - outerBandsRight.at(0).first + 1) *
915 outerSlope)); // +1 so as to be symmetric with left slope
916 }
917 else if (i > outerBandsRight.at(0).second)
918 {
919 txPower = DbmToW(txPowerOuterBandMin);
920 }
921 else
922 {
923 NS_ASSERT(false);
924 }
925
926 // value for PSD mask 2
927 if (i < outerBandsLeft.at(1).first)
928 {
929 txPower = DbmToW(txPowerOuterBandMin);
930 }
931 else if (i <= outerBandsLeft.at(1).second && i >= outerBandsLeft.at(1).first)
932 {
933 txPowerWPsds.at(1) =
934 DbmToW(txPowerOuterBandMin + ((i - outerBandsLeft.at(1).first) * outerSlope));
935 }
936 else if (i <= middleBandsLeft.at(1).second && i >= middleBandsLeft.at(1).first)
937 {
938 txPowerWPsds.at(1) = DbmToW(txPowerMiddleBandMin +
939 ((i - middleBandsLeft.at(1).first) * middleSlope));
940 }
941 else
942 {
943 NS_ASSERT(false);
944 }
945
946 txPower = std::accumulate(txPowerWPsds.cbegin(), txPowerWPsds.cend(), Watt_u{0.0});
947 txPower = std::max(DbmToW(txPowerRef - 25.0), txPower);
948 txPower = std::min(DbmToW(txPowerRef - 20.0), txPower);
949 }
950 else if (i <= outerBandsLeft.at(psdIndex).second &&
951 i >= outerBandsLeft.at(psdIndex)
952 .first) // better to put greater first (less computation)
953 {
954 txPower = DbmToW(txPowerOuterBandMin +
955 ((i - outerBandsLeft.at(psdIndex).first) * outerSlope));
956 }
957 else if (i <= middleBandsLeft.at(psdIndex).second &&
958 i >= middleBandsLeft.at(psdIndex).first)
959 {
960 txPower = DbmToW(txPowerMiddleBandMin +
961 ((i - middleBandsLeft.at(psdIndex).first) * middleSlope));
962 }
963 else if ((i <= flatJunctionsLeft.at(psdIndex).second &&
964 i >= flatJunctionsLeft.at(psdIndex).first) ||
965 (i <= flatJunctionsRight.at(psdIndex).second &&
966 i >= flatJunctionsRight.at(psdIndex).first))
967 {
968 txPower = DbmToW(txPowerInnerBandMin);
969 }
970 else if (i <= innerBandsLeft.at(psdIndex).second && i >= innerBandsLeft.at(psdIndex).first)
971 {
972 txPower = (!puncturedBandsPerSegment.empty() &&
973 !puncturedBandsPerSegment.at(psdIndex).empty() &&
974 (puncturedBandsPerSegment.at(psdIndex).front().first <=
975 allocatedSubBandsPerSegment.at(psdIndex).front().first))
976 ? DbmToW(txPowerInnerBandMin)
977 : // first 20 MHz band is punctured
978 DbmToW(txPowerInnerBandMin +
979 ((i - innerBandsLeft.at(psdIndex).first) * innerSlope));
980 }
981 else if ((i <= allocatedSubBandsPerSegment.at(psdIndex).back().second &&
982 i >= allocatedSubBandsPerSegment.at(psdIndex).front().first)) // roughly in
983 // allocated band
984 {
985 bool insideSubBand = false;
986 for (uint32_t j = 0; !insideSubBand && j < numSubBands;
987 j++) // continue until inside a sub-band
988 {
989 insideSubBand = (i <= allocatedSubBandsPerSegment.at(psdIndex)[j].second) &&
990 (i >= allocatedSubBandsPerSegment.at(psdIndex)[j].first);
991 }
992 if (insideSubBand)
993 {
994 bool insidePuncturedSubBand = false;
995 std::size_t j = 0;
996 std::size_t puncturedBandSize = !puncturedBandsPerSegment.empty()
997 ? puncturedBandsPerSegment.at(psdIndex).size()
998 : 0;
999 for (; !insidePuncturedSubBand && j < puncturedBandSize;
1000 j++) // continue until inside a punctured sub-band
1001 {
1002 insidePuncturedSubBand =
1003 (i <= puncturedBandsPerSegment.at(psdIndex).at(j).second) &&
1004 (i >= puncturedBandsPerSegment.at(psdIndex).at(j).first);
1005 }
1006 if (insidePuncturedSubBand)
1007 {
1008 uint32_t startPuncturedSlope =
1009 (puncturedBandsPerSegment.at(psdIndex)
1010 .at(puncturedBandsPerSegment.at(psdIndex).size() - 1)
1011 .second -
1012 puncturedSlopeWidth); // only consecutive subchannels can be punctured
1013 if (i >= startPuncturedSlope)
1014 {
1015 txPower = DbmToW(txPowerInnerBandMin +
1016 ((i - startPuncturedSlope) * puncturedSlope));
1017 }
1018 else
1019 {
1020 txPower = std::max(
1021 DbmToW(txPowerInnerBandMin),
1022 DbmToW(txPowerRef -
1023 ((i - puncturedBandsPerSegment.at(psdIndex).at(0).first) *
1024 puncturedSlope)));
1025 }
1026 }
1027 else
1028 {
1029 txPower = txPowerPerBand;
1030 }
1031 }
1032 else
1033 {
1034 txPower = DbmToW(txPowerInnerBandMin);
1035 }
1036 }
1037 else if (i <= innerBandsRight.at(psdIndex).second &&
1038 i >= innerBandsRight.at(psdIndex).first)
1039 {
1040 // take min to handle the case where last 20 MHz band is punctured
1041 txPower = std::min(
1042 previousTxPower,
1043 DbmToW(txPowerRef - ((i - innerBandsRight.at(psdIndex).first + 1) *
1044 innerSlope))); // +1 so as to be symmetric with left slope
1045 }
1046 else if (i <= middleBandsRight.at(psdIndex).second &&
1047 i >= middleBandsRight.at(psdIndex).first)
1048 {
1049 txPower = DbmToW(txPowerInnerBandMin -
1050 ((i - middleBandsRight.at(psdIndex).first + 1) *
1051 middleSlope)); // +1 so as to be symmetric with left slope
1052 }
1053 else if (i <= outerBandsRight.at(psdIndex).second &&
1054 i >= outerBandsRight.at(psdIndex).first)
1055 {
1056 txPower = DbmToW(txPowerMiddleBandMin -
1057 ((i - outerBandsRight.at(psdIndex).first + 1) *
1058 outerSlope)); // +1 so as to be symmetric with left slope
1059 }
1060 else
1061 {
1062 NS_FATAL_ERROR("Should have handled all cases");
1063 }
1064 NS_LOG_LOGIC(i << " -> " << (10 * std::log10(txPower / txPowerPerBand)));
1065 previousTxPower = txPower;
1066 txPowerValues.at(i) = txPower;
1067 }
1068
1069 // fill in spectrum mask
1070 auto vit = c->ValuesBegin();
1071 auto bit = c->ConstBandsBegin();
1072 const auto invBandwidth = 1 / (bit->fh - bit->fl);
1073 for (auto txPowerValue : txPowerValues)
1074 {
1075 *vit = txPowerValue * invBandwidth;
1076 vit++;
1077 bit++;
1078 }
1079
1080 for (const auto& allocatedSubBands : allocatedSubBandsPerSegment)
1081 {
1082 NS_LOG_INFO("Added signal power to subbands " << allocatedSubBands.front().first << "-"
1083 << allocatedSubBands.back().second);
1084 }
1085}
1086
1087void
1089{
1090 NS_LOG_FUNCTION(c << txPower);
1091 // Normalize power so that total signal power equals transmit power
1092 Watt_u currentTxPower{Integral(*c)};
1093 double normalizationRatio [[maybe_unused]] = currentTxPower / txPower;
1094 double invNormalizationRatio = txPower / currentTxPower;
1095 NS_LOG_LOGIC("Current power: " << currentTxPower << "W vs expected power: " << txPower << "W"
1096 << " -> ratio (C/E) = " << normalizationRatio);
1097 auto vit = c->ValuesBegin();
1098 for (size_t i = 0; i < c->GetSpectrumModel()->GetNumBands(); i++, vit++)
1099 {
1100 *vit = (*vit) * invNormalizationRatio;
1101 }
1102}
1103
1104Watt_u
1106 const std::vector<WifiSpectrumBandIndices>& segments)
1107{
1108 auto powerWattPerHertz{0.0};
1109 auto bandIt = psd->ConstBandsBegin() + segments.front().first; // all bands have same width
1110 const auto bandWidth = (bandIt->fh - bandIt->fl);
1111 NS_ASSERT_MSG(bandWidth >= 0.0,
1112 "Invalid width for subband [" << bandIt->fl << ";" << bandIt->fh << "]");
1113 for (const auto& [start, stop] : segments)
1114 {
1115 auto valueIt = psd->ConstValuesBegin() + start;
1116 auto end = psd->ConstValuesBegin() + stop;
1117 uint32_t index [[maybe_unused]] = 0;
1118 while (valueIt <= end)
1119 {
1120 NS_ASSERT_MSG(*valueIt >= 0.0,
1121 "Invalid power value " << *valueIt << " in subband " << index);
1122 powerWattPerHertz += *valueIt;
1123 ++valueIt;
1124 ++index;
1125 }
1126 }
1127 const Watt_u power{powerWattPerHertz * bandWidth};
1128 NS_ASSERT_MSG(power >= 0.0, "Invalid calculated power " << power);
1129 return power;
1130}
1131
1132bool
1133operator<(const FrequencyRange& left, const FrequencyRange& right)
1134{
1135 return left.minFrequency < right.minFrequency;
1136}
1137
1138bool
1139operator==(const FrequencyRange& left, const FrequencyRange& right)
1140{
1141 return (left.minFrequency == right.minFrequency) && (left.maxFrequency == right.maxFrequency);
1142}
1143
1144bool
1145operator!=(const FrequencyRange& left, const FrequencyRange& right)
1146{
1147 return !(left == right);
1148}
1149
1150std::ostream&
1151operator<<(std::ostream& os, const FrequencyRange& freqRange)
1152{
1153 os << "[" << freqRange.minFrequency << " MHz - " << freqRange.maxFrequency << " MHz]";
1154 return os;
1155}
1156
1157} // namespace ns3
Smart pointer class similar to boost::intrusive_ptr.
static Ptr< SpectrumValue > CreateHtOfdmTxPowerSpectralDensity(const std::vector< MHz_u > &centerFrequencies, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=dBr_u{-20}, dBr_u minOuterband=dBr_u{-28}, dBr_u lowestPoint=dBr_u{-40})
Create a transmit power spectral density corresponding to OFDM High Throughput (HT) (802....
static Ptr< SpectrumValue > CreateDsssTxPowerSpectralDensity(MHz_u centerFrequency, Watt_u txPower, MHz_u guardBandwidth)
Create a transmit power spectral density corresponding to DSSS.
static Ptr< SpectrumValue > CreateDuplicated20MhzTxPowerSpectralDensity(const std::vector< MHz_u > &centerFrequencies, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=dBr_u{-20}, dBr_u minOuterband=dBr_u{-28}, dBr_u lowestPoint=dBr_u{-40}, const std::vector< bool > &puncturedSubchannels={})
Create a transmit power spectral density corresponding to OFDM duplicated over multiple 20 MHz subcha...
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 > 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 > CreateOfdmTxPowerSpectralDensity(MHz_u centerFrequency, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=dBr_u{-20}, dBr_u minOuterband=dBr_u{-28}, dBr_u lowestPoint=dBr_u{-40})
Create a transmit power spectral density corresponding to OFDM (802.11a/g).
static Ptr< SpectrumValue > CreateHeOfdmTxPowerSpectralDensity(MHz_u centerFrequency, MHz_u channelWidth, Watt_u txPower, MHz_u guardBandwidth, dBr_u minInnerBand=dBr_u{-20}, dBr_u minOuterband=dBr_u{-28}, dBr_u lowestPoint=dBr_u{-40}, const std::vector< bool > &puncturedSubchannels={})
Create a transmit power spectral density corresponding to OFDM High Efficiency (HE) (802....
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.
std::size_t Count20MHzSubchannels(MHz_u channelWidth)
Return the number of 20 MHz subchannels covering the channel width.
Definition wifi-utils.h:134
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
Hz_u MHzToHz(MHz_u val)
Convert from MHz to Hz.
Definition wifi-utils.h:109
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