1 /* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Authors: Ghada Badawy <gbadawy@rim.com>
19  *          Sébastien Deronne <sebastien.deronne@gmail.com>
20  */
21 
22 #include "ht-capabilities.h"
23 
24 namespace ns3 {
25 
HtCapabilities()26 HtCapabilities::HtCapabilities ()
27   : m_ldpc (0),
28     m_supportedChannelWidth (0),
29     m_smPowerSave (0),
30     m_greenField (0),
31     m_shortGuardInterval20 (0),
32     m_shortGuardInterval40 (0),
33     m_txStbc (0),
34     m_rxStbc (0),
35     m_htDelayedBlockAck (0),
36     m_maxAmsduLength (0),
37     m_dssMode40 (0),
38     m_psmpSupport (0),
39     m_fortyMhzIntolerant (0),
40     m_lsigProtectionSupport (0),
41     m_maxAmpduLengthExponent (0),
42     m_minMpduStartSpace (0),
43     m_ampduReserved (0),
44     m_reservedMcsSet1 (0),
45     m_rxHighestSupportedDataRate (0),
46     m_reservedMcsSet2 (0),
47     m_txMcsSetDefined (0),
48     m_txRxMcsSetUnequal (0),
49     m_txMaxNSpatialStreams (0),
50     m_txUnequalModulation (0),
51     m_reservedMcsSet3 (0),
52     m_pco (0),
53     m_pcoTransitionTime (0),
54     m_reservedExtendedCapabilities (0),
55     m_mcsFeedback (0),
56     m_htcSupport (0),
57     m_reverseDirectionResponder (0),
58     m_reservedExtendedCapabilities2 (0),
59     m_implicitRxBfCapable (0),
60     m_rxStaggeredSoundingCapable (0),
61     m_txStaggeredSoundingCapable (0),
62     m_rxNdpCapable (0),
63     m_txNdpCapable (0),
64     m_implicitTxBfCapable (0),
65     m_calibration (0),
66     m_explicitCsiTxBfCapable (0),
67     m_explicitNoncompressedSteeringCapable (0),
68     m_explicitCompressedSteeringCapable (0),
69     m_explicitTxBfCsiFeedback (0),
70     m_explicitNoncompressedBfFeedbackCapable (0),
71     m_explicitCompressedBfFeedbackCapable (0),
72     m_minimalGrouping (0),
73     m_csiNBfAntennasSupported (0),
74     m_noncompressedSteeringNBfAntennasSupported (0),
75     m_compressedSteeringNBfAntennasSupported (0),
76     m_csiMaxNRowsBfSupported (0),
77     m_channelEstimationCapability (0),
78     m_reservedTxBf (0),
79     m_antennaSelectionCapability (0),
80     m_explicitCsiFeedbackBasedTxASelCapable (0),
81     m_antennaIndicesFeedbackBasedTxASelCapable (0),
82     m_explicitCsiFeedbackCapable (0),
83     m_antennaIndicesFeedbackCapable (0),
84     m_rxASelCapable (0),
85     m_txSoundingPpdusCapable (0),
86     m_reservedASel (0),
87     m_htSupported (0)
88 {
89   for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
90     {
91       m_rxMcsBitmask[i] = 0;
92     }
93 }
94 
95 WifiInformationElementId
ElementId() const96 HtCapabilities::ElementId () const
97 {
98   return IE_HT_CAPABILITIES;
99 }
100 
101 void
SetHtSupported(uint8_t htSupported)102 HtCapabilities::SetHtSupported (uint8_t htSupported)
103 {
104   m_htSupported = htSupported;
105 }
106 
107 void
SetLdpc(uint8_t ldpc)108 HtCapabilities::SetLdpc (uint8_t ldpc)
109 {
110   m_ldpc = ldpc;
111 }
112 
113 void
SetSupportedChannelWidth(uint8_t supportedChannelWidth)114 HtCapabilities::SetSupportedChannelWidth (uint8_t supportedChannelWidth)
115 {
116   m_supportedChannelWidth = supportedChannelWidth;
117 }
118 
119 void
SetShortGuardInterval20(uint8_t shortGuardInterval)120 HtCapabilities::SetShortGuardInterval20 (uint8_t shortGuardInterval)
121 {
122   m_shortGuardInterval20 = shortGuardInterval;
123 }
124 
125 void
SetShortGuardInterval40(uint8_t shortGuardInterval)126 HtCapabilities::SetShortGuardInterval40 (uint8_t shortGuardInterval)
127 {
128   m_shortGuardInterval40 = shortGuardInterval;
129 }
130 
131 void
SetMaxAmsduLength(uint16_t maxAmsduLength)132 HtCapabilities::SetMaxAmsduLength (uint16_t maxAmsduLength)
133 {
134   NS_ABORT_MSG_IF (maxAmsduLength != 3839 && maxAmsduLength != 7935,
135                    "Invalid A-MSDU Max Length value");
136   m_maxAmsduLength = (maxAmsduLength == 3839 ? 0 : 1);
137 }
138 
139 void
SetLSigProtectionSupport(uint8_t lSigProtection)140 HtCapabilities::SetLSigProtectionSupport (uint8_t lSigProtection)
141 {
142   m_lsigProtectionSupport = lSigProtection;
143 }
144 
145 void
SetMaxAmpduLength(uint32_t maxAmpduLength)146 HtCapabilities::SetMaxAmpduLength (uint32_t maxAmpduLength)
147 {
148   for (uint8_t i = 0; i <= 3; i++)
149     {
150       if ((1ul << (13 + i)) - 1 == maxAmpduLength)
151         {
152           m_maxAmpduLengthExponent = i;
153           return;
154         }
155     }
156   NS_ABORT_MSG ("Invalid A-MPDU Max Length value");
157 }
158 
159 void
SetRxMcsBitmask(uint8_t index)160 HtCapabilities::SetRxMcsBitmask (uint8_t index)
161 {
162   m_rxMcsBitmask[index] = 1;
163 }
164 
165 void
SetRxHighestSupportedDataRate(uint16_t maxSupportedRate)166 HtCapabilities::SetRxHighestSupportedDataRate (uint16_t maxSupportedRate)
167 {
168   m_rxHighestSupportedDataRate = maxSupportedRate;
169 }
170 
171 void
SetTxMcsSetDefined(uint8_t txMcsSetDefined)172 HtCapabilities::SetTxMcsSetDefined (uint8_t txMcsSetDefined)
173 {
174   m_txMcsSetDefined = txMcsSetDefined;
175 }
176 
177 void
SetTxRxMcsSetUnequal(uint8_t txRxMcsSetUnequal)178 HtCapabilities::SetTxRxMcsSetUnequal (uint8_t txRxMcsSetUnequal)
179 {
180   m_txRxMcsSetUnequal = txRxMcsSetUnequal;
181 }
182 
183 void
SetTxMaxNSpatialStreams(uint8_t maxTxSpatialStreams)184 HtCapabilities::SetTxMaxNSpatialStreams (uint8_t maxTxSpatialStreams)
185 {
186   m_txMaxNSpatialStreams = maxTxSpatialStreams - 1; //0 for 1 SS, 1 for 2 SSs, etc
187 }
188 
189 void
SetTxUnequalModulation(uint8_t txUnequalModulation)190 HtCapabilities::SetTxUnequalModulation (uint8_t txUnequalModulation)
191 {
192   m_txUnequalModulation = txUnequalModulation;
193 }
194 
195 uint8_t
GetLdpc(void) const196 HtCapabilities::GetLdpc (void) const
197 {
198   return m_ldpc;
199 }
200 
201 uint8_t
GetSupportedChannelWidth(void) const202 HtCapabilities::GetSupportedChannelWidth (void) const
203 {
204   return m_supportedChannelWidth;
205 }
206 
207 uint8_t
GetShortGuardInterval20(void) const208 HtCapabilities::GetShortGuardInterval20 (void) const
209 {
210   return m_shortGuardInterval20;
211 }
212 
213 uint16_t
GetMaxAmsduLength(void) const214 HtCapabilities::GetMaxAmsduLength (void) const
215 {
216   if (m_maxAmsduLength == 0)
217     {
218       return 3839;
219     }
220   return 7935;
221 }
222 
223 uint32_t
GetMaxAmpduLength(void) const224 HtCapabilities::GetMaxAmpduLength (void) const
225 {
226   return (1ul << (13 + m_maxAmpduLengthExponent)) - 1;
227 }
228 
229 bool
IsSupportedMcs(uint8_t mcs) const230 HtCapabilities::IsSupportedMcs (uint8_t mcs) const
231 {
232   if (m_rxMcsBitmask[mcs] == 1)
233     {
234       return true;
235     }
236   return false;
237 }
238 
239 uint8_t
GetRxHighestSupportedAntennas(void) const240 HtCapabilities::GetRxHighestSupportedAntennas (void) const
241 {
242   for (uint8_t nRx = 2; nRx <= 4; nRx++)
243     {
244       for (uint8_t mcs = (nRx - 1) * 8; mcs <= ((7 * nRx) + (nRx - 1)); mcs++)
245         {
246           if (IsSupportedMcs (mcs) == false)
247             {
248               return (nRx - 1);
249             }
250         }
251     }
252   return 4;
253 }
254 
255 uint8_t
GetInformationFieldSize() const256 HtCapabilities::GetInformationFieldSize () const
257 {
258   //we should not be here if HT is not supported
259   NS_ASSERT (m_htSupported > 0);
260   return 26;
261 }
262 
263 Buffer::Iterator
Serialize(Buffer::Iterator i) const264 HtCapabilities::Serialize (Buffer::Iterator i) const
265 {
266   if (m_htSupported < 1)
267     {
268       return i;
269     }
270   return WifiInformationElement::Serialize (i);
271 }
272 
273 uint16_t
GetSerializedSize() const274 HtCapabilities::GetSerializedSize () const
275 {
276   if (m_htSupported < 1)
277     {
278       return 0;
279     }
280   return WifiInformationElement::GetSerializedSize ();
281 }
282 
283 uint16_t
GetHtCapabilitiesInfo(void) const284 HtCapabilities::GetHtCapabilitiesInfo (void) const
285 {
286   uint16_t val = 0;
287   val |= m_ldpc & 0x01;
288   val |= (m_supportedChannelWidth & 0x01) << 1;
289   val |= (m_smPowerSave & 0x03) << 2;
290   val |= (m_greenField & 0x01) << 4;
291   val |= (m_shortGuardInterval20 & 0x01) << 5;
292   val |= (m_shortGuardInterval40 & 0x01) << 6;
293   val |= (m_txStbc & 0x01) << 7;
294   val |= (m_rxStbc & 0x03) << 8;
295   val |= (m_htDelayedBlockAck & 0x01) << 10;
296   val |= (m_maxAmsduLength & 0x01) << 11;
297   val |= (m_dssMode40 & 0x01) << 12;
298   val |= (m_psmpSupport & 0x01) << 13;
299   val |= (m_fortyMhzIntolerant & 0x01) << 14;
300   val |= (m_lsigProtectionSupport & 0x01) << 15;
301   return val;
302 }
303 
304 void
SetHtCapabilitiesInfo(uint16_t ctrl)305 HtCapabilities::SetHtCapabilitiesInfo (uint16_t ctrl)
306 {
307   m_ldpc = ctrl & 0x01;
308   m_supportedChannelWidth = (ctrl >> 1) & 0x01;
309   m_smPowerSave = (ctrl >> 2) & 0x03;
310   m_greenField = (ctrl >> 4) & 0x01;
311   m_shortGuardInterval20 = (ctrl >> 5) & 0x01;
312   m_shortGuardInterval40 = (ctrl >> 6) & 0x01;
313   m_txStbc = (ctrl >> 7) & 0x01;
314   m_rxStbc = (ctrl >> 8) & 0x03;
315   m_htDelayedBlockAck = (ctrl >> 10) & 0x01;
316   m_maxAmsduLength = (ctrl >> 11) & 0x01;
317   m_dssMode40 = (ctrl >> 12) & 0x01;
318   m_psmpSupport = (ctrl >> 13) & 0x01;
319   m_fortyMhzIntolerant = (ctrl >> 14) & 0x01;
320   m_lsigProtectionSupport = (ctrl >> 15) & 0x01;
321 }
322 
323 void
SetAmpduParameters(uint8_t ctrl)324 HtCapabilities::SetAmpduParameters (uint8_t ctrl)
325 {
326   m_maxAmpduLengthExponent = ctrl & 0x03;
327   m_minMpduStartSpace = (ctrl >> 2) & 0x1b;
328   m_ampduReserved = (ctrl >> 5) & 0xe0;
329 }
330 
331 uint8_t
GetAmpduParameters(void) const332 HtCapabilities::GetAmpduParameters (void) const
333 {
334   uint8_t val = 0;
335   val |=  m_maxAmpduLengthExponent & 0x03;
336   val |= (m_minMpduStartSpace & 0x1b) << 2;
337   val |= (m_ampduReserved & 0xe0) << 5;
338   return val;
339 }
340 
341 void
SetSupportedMcsSet(uint64_t ctrl1,uint64_t ctrl2)342 HtCapabilities::SetSupportedMcsSet (uint64_t ctrl1, uint64_t ctrl2)
343 {
344   for (uint64_t i = 0; i < 77; i++)
345     {
346       if (i < 64)
347         {
348           m_rxMcsBitmask[i] = (ctrl1 >> i) & 0x01;
349         }
350       else
351         {
352           m_rxMcsBitmask[i] = (ctrl2 >> (i - 64)) & 0x01;
353         }
354     }
355   m_reservedMcsSet1 = (ctrl2 >> 13) & 0x07;
356   m_rxHighestSupportedDataRate = (ctrl2 >> 16) & 0x03ff;
357   m_reservedMcsSet2 = (ctrl2 >> 26) & 0x3f;
358   m_txMcsSetDefined = (ctrl2 >> 32) & 0x01;
359   m_txRxMcsSetUnequal = (ctrl2 >> 33) & 0x01;
360   m_txMaxNSpatialStreams = (ctrl2 >> 34) & 0x03;
361   m_txUnequalModulation = (ctrl2 >> 36) & 0x01;
362   m_reservedMcsSet3 = (ctrl2 >> 37) & 0x07ffffff;
363 }
364 
365 uint64_t
GetSupportedMcsSet1(void) const366 HtCapabilities::GetSupportedMcsSet1 (void) const
367 {
368   uint64_t val = 0;
369   for (uint64_t i = 63; i > 0; i--)
370     {
371       val = (val << 1) | (m_rxMcsBitmask[i] & 0x01);
372     }
373   val = (val << 1) | (m_rxMcsBitmask[0] & 0x01);
374   return val;
375 }
376 
377 uint64_t
GetSupportedMcsSet2(void) const378 HtCapabilities::GetSupportedMcsSet2 (void) const
379 {
380   uint64_t val = 0;
381   val = val | (m_reservedMcsSet3 & 0x07ffffff);
382   val = (val << 1) | (m_txUnequalModulation & 0x01);
383   val = (val << 2) | (m_txMaxNSpatialStreams & 0x03);
384   val = (val << 1) | (m_txRxMcsSetUnequal & 0x01);
385   val = (val << 1) | (m_txMcsSetDefined & 0x01);
386   val = (val << 6) | (m_reservedMcsSet2 & 0x3f);
387   val = (val << 10) | (m_rxHighestSupportedDataRate & 0x3ff);
388   val = (val << 3) | (m_reservedMcsSet1 & 0x07);
389 
390   for (uint64_t i = 13; i > 0; i--)
391     {
392       val = (val << 1) | ( m_rxMcsBitmask[i + 63] & 0x01);
393     }
394   return val;
395 }
396 
397 uint16_t
GetExtendedHtCapabilities(void) const398 HtCapabilities::GetExtendedHtCapabilities (void) const
399 {
400   uint16_t val = 0;
401   val |= m_pco & 0x01;
402   val |= (m_pcoTransitionTime & 0x03) << 1;
403   val |= (m_reservedExtendedCapabilities & 0x1f) << 3;
404   val |= (m_mcsFeedback & 0x03) << 8;
405   val |= (m_htcSupport & 0x01) << 10;
406   val |= (m_reverseDirectionResponder & 0x01) << 11;
407   val |= (m_reservedExtendedCapabilities2 & 0x0f) << 12;
408   return val;
409 }
410 
411 void
SetExtendedHtCapabilities(uint16_t ctrl)412 HtCapabilities::SetExtendedHtCapabilities (uint16_t ctrl)
413 {
414   m_pco = ctrl & 0x01;
415   m_pcoTransitionTime = (ctrl >> 1) & 0x03;
416   m_reservedExtendedCapabilities = (ctrl >> 3) & 0x1f;
417   m_mcsFeedback = (ctrl >> 8) & 0x03;
418   m_htcSupport = (ctrl >> 10) & 0x01;
419   m_reverseDirectionResponder = (ctrl >> 11) & 0x01;
420   m_reservedExtendedCapabilities2 = (ctrl >> 12) & 0x0f;
421 }
422 
423 uint32_t
GetTxBfCapabilities(void) const424 HtCapabilities::GetTxBfCapabilities (void) const
425 {
426   uint32_t val = 0;
427   val |= m_implicitRxBfCapable & 0x01;
428   val |= (m_rxStaggeredSoundingCapable & 0x01) << 1;
429   val |= (m_txStaggeredSoundingCapable & 0x01) << 2;
430   val |= (m_rxNdpCapable & 0x01) << 3;
431   val |= (m_txNdpCapable & 0x01) << 4;
432   val |= (m_implicitTxBfCapable & 0x01) << 5;
433   val |= (m_calibration & 0x03) << 6;
434   val |= (m_explicitCsiTxBfCapable & 0x01) << 8;
435   val |= (m_explicitNoncompressedSteeringCapable & 0x01) << 9;
436   val |= (m_explicitCompressedSteeringCapable & 0x01) << 10;
437   val |= (m_explicitTxBfCsiFeedback & 0x03) << 11;
438   val |= (m_explicitNoncompressedBfFeedbackCapable & 0x03) << 13;
439   val |= (m_explicitCompressedBfFeedbackCapable & 0x03) << 15;
440   val |= (m_minimalGrouping & 0x03) << 17;
441   val |= (m_csiNBfAntennasSupported & 0x03) << 19;
442   val |= (m_noncompressedSteeringNBfAntennasSupported & 0x03) << 21;
443   val |= (m_compressedSteeringNBfAntennasSupported & 0x03) << 23;
444   val |= (m_csiMaxNRowsBfSupported & 0x03) << 25;
445   val |= (m_channelEstimationCapability & 0x03) << 27;
446   val |= (m_reservedTxBf & 0x07) << 29;
447   return val;
448 }
449 
450 void
SetTxBfCapabilities(uint32_t ctrl)451 HtCapabilities::SetTxBfCapabilities (uint32_t ctrl)
452 {
453   m_implicitRxBfCapable = ctrl & 0x01;
454   m_rxStaggeredSoundingCapable = (ctrl >> 1) & 0x01;
455   m_txStaggeredSoundingCapable = (ctrl >> 2) & 0x01;
456   m_rxNdpCapable = (ctrl >> 3) & 0x01;
457   m_txNdpCapable = (ctrl >> 4) & 0x01;
458   m_implicitTxBfCapable = (ctrl >> 5) & 0x01;
459   m_calibration = (ctrl >> 6) & 0x03;
460   m_explicitCsiTxBfCapable = (ctrl >> 8) & 0x01;
461   m_explicitNoncompressedSteeringCapable = (ctrl >> 9) & 0x01;
462   m_explicitCompressedSteeringCapable = (ctrl >> 10) & 0x01;
463   m_explicitTxBfCsiFeedback = (ctrl >> 11) & 0x03;
464   m_explicitNoncompressedBfFeedbackCapable = (ctrl >> 13) & 0x03;
465   m_explicitCompressedBfFeedbackCapable = (ctrl >> 15) & 0x03;
466   m_minimalGrouping = (ctrl >> 17) & 0x03;
467   m_csiNBfAntennasSupported = (ctrl >> 19) & 0x03;
468   m_noncompressedSteeringNBfAntennasSupported = (ctrl >> 21) & 0x03;
469   m_compressedSteeringNBfAntennasSupported = (ctrl >> 23) & 0x03;
470   m_csiMaxNRowsBfSupported = (ctrl >> 25) & 0x03;
471   m_channelEstimationCapability = (ctrl >> 27) & 0x03;
472   m_reservedTxBf = (ctrl >> 29) & 0x07;
473 }
474 
475 uint8_t
GetAntennaSelectionCapabilities(void) const476 HtCapabilities::GetAntennaSelectionCapabilities (void) const
477 {
478   uint8_t val = 0;
479   val |= m_antennaSelectionCapability & 0x01;
480   val |= (m_explicitCsiFeedbackBasedTxASelCapable & 0x01) << 1;
481   val |= (m_antennaIndicesFeedbackBasedTxASelCapable & 0x01) << 2;
482   val |= (m_explicitCsiFeedbackCapable & 0x01) << 3;
483   val |= (m_antennaIndicesFeedbackCapable & 0x01) << 4;
484   val |= (m_rxASelCapable & 0x01) << 5;
485   val |= (m_txSoundingPpdusCapable & 0x01) << 6;
486   val |= (m_reservedASel & 0x01) << 7;
487   return val;
488 }
489 
490 void
SetAntennaSelectionCapabilities(uint8_t ctrl)491 HtCapabilities::SetAntennaSelectionCapabilities (uint8_t ctrl)
492 {
493   m_antennaSelectionCapability = ctrl & 0x01;
494   m_explicitCsiFeedbackBasedTxASelCapable = (ctrl >> 1) & 0x01;
495   m_antennaIndicesFeedbackBasedTxASelCapable = (ctrl >> 2) & 0x01;
496   m_explicitCsiFeedbackCapable = (ctrl >> 3) & 0x01;
497   m_antennaIndicesFeedbackCapable = (ctrl >> 4) & 0x01;
498   m_rxASelCapable = (ctrl >> 5) & 0x01;
499   m_txSoundingPpdusCapable = (ctrl >> 6) & 0x01;
500   m_reservedASel = (ctrl >> 7) & 0x01;
501 }
502 
503 void
SerializeInformationField(Buffer::Iterator start) const504 HtCapabilities::SerializeInformationField (Buffer::Iterator start) const
505 {
506   if (m_htSupported == 1)
507     {
508       //write the corresponding value for each bit
509       start.WriteHtolsbU16 (GetHtCapabilitiesInfo ());
510       start.WriteU8 (GetAmpduParameters ());
511       start.WriteHtolsbU64 (GetSupportedMcsSet1 ());
512       start.WriteHtolsbU64 (GetSupportedMcsSet2 ());
513       start.WriteU16 (GetExtendedHtCapabilities ());
514       start.WriteU32 (GetTxBfCapabilities ());
515       start.WriteU8 (GetAntennaSelectionCapabilities ());
516     }
517 }
518 
519 uint8_t
DeserializeInformationField(Buffer::Iterator start,uint8_t length)520 HtCapabilities::DeserializeInformationField (Buffer::Iterator start,
521                                              uint8_t length)
522 {
523   Buffer::Iterator i = start;
524   uint16_t htinfo = i.ReadLsbtohU16 ();
525   uint8_t ampduparam = i.ReadU8 ();
526   uint64_t mcsset1 = i.ReadLsbtohU64 ();
527   uint64_t mcsset2 = i.ReadLsbtohU64 ();
528   uint16_t extendedcapabilities = i.ReadU16 ();
529   uint32_t txbfcapabilities = i.ReadU32 ();
530   uint8_t aselcapabilities = i.ReadU8 ();
531   SetHtCapabilitiesInfo (htinfo);
532   SetAmpduParameters (ampduparam);
533   SetSupportedMcsSet (mcsset1, mcsset2);
534   SetExtendedHtCapabilities (extendedcapabilities);
535   SetTxBfCapabilities (txbfcapabilities);
536   SetAntennaSelectionCapabilities (aselcapabilities);
537   return length;
538 }
539 
540 std::ostream &
operator <<(std::ostream & os,const HtCapabilities & htcapabilities)541 operator << (std::ostream &os, const HtCapabilities &htcapabilities)
542 {
543   os << bool (htcapabilities.GetLdpc ())
544      << "|" << bool (htcapabilities.GetSupportedChannelWidth ())
545      << "|" << bool (htcapabilities.GetShortGuardInterval20 ()) << "|";
546   for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
547     {
548       os << htcapabilities.IsSupportedMcs (i) << " ";
549     }
550   return os;
551 }
552 
553 } //namespace ns3
554