A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-tx-stats-helper.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024 Huazhong University of Science and Technology
3 * Copyright (c) 2024 University of Washington
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Authors: Muyuan Shen <muyuan@uw.edu>
8 * Hao Yin <haoyin@uw.edu>
9 */
10
12
13#include "ns3/frame-exchange-manager.h"
14#include "ns3/log.h"
15#include "ns3/net-device-container.h"
16#include "ns3/node-container.h"
17#include "ns3/wifi-mac-queue.h"
18#include "ns3/wifi-net-device.h"
19
20#include <functional>
21
22namespace ns3
23{
24
25NS_LOG_COMPONENT_DEFINE("WifiTxStatsHelper");
26
31
33 : m_startTime(startTime),
35{
36 NS_LOG_FUNCTION(this << startTime.As(Time::S) << stopTime.As(Time::S));
37 NS_ASSERT_MSG(startTime <= stopTime,
38 "Invalid Start: " << startTime << " and Stop: " << stopTime << " Time");
39}
40
41void
43{
44 NS_LOG_FUNCTION(this << nodes.GetN());
45 NetDeviceContainer netDevices;
46 for (uint32_t i = 0; i < nodes.GetN(); ++i)
47 {
48 for (uint32_t j = 0; j < nodes.Get(i)->GetNDevices(); ++j)
49 {
50 netDevices.Add(nodes.Get(i)->GetDevice(j));
51 }
52 }
53 Enable(netDevices);
54}
55
56void
58{
59 NS_LOG_FUNCTION(this << devices.GetN());
60
61 for (uint32_t j = 0; j < devices.GetN(); ++j)
62 {
63 auto device = DynamicCast<WifiNetDevice>(devices.Get(j));
64 if (!device)
65 {
66 NS_LOG_DEBUG("Ignoring deviceId: " << devices.Get(j)->GetIfIndex() << " on nodeId: "
67 << devices.Get(j)->GetNode()->GetId()
68 << " because it is not of type WifiNetDevice");
69 continue;
70 }
71 NS_LOG_INFO("Adding deviceId: " << devices.Get(j)->GetIfIndex()
72 << " on nodeId: " << devices.Get(j)->GetNode()->GetId());
73 const auto nodeId = devices.Get(j)->GetNode()->GetId();
74 const auto deviceId = devices.Get(j)->GetIfIndex();
75 if (device->GetMac()->GetQosSupported())
76 {
77 for (const auto& [ac, wifiAc] : wifiAcList)
78 {
79 // Trace enqueue for available ACs
80 device->GetMac()->GetTxopQueue(ac)->TraceConnectWithoutContext(
81 "Enqueue",
83 .Bind(nodeId, deviceId));
84 }
85 }
86 else
87 {
88 // Trace enqueue for Non-QoS AC
89 device->GetMac()
90 ->GetTxopQueue(AC_BE_NQOS)
91 ->TraceConnectWithoutContext(
92 "Enqueue",
94 .Bind(nodeId, deviceId));
95 }
96 device->GetMac()->TraceConnectWithoutContext(
97 "AckedMpdu",
98 MakeCallback(&WifiTxStatsHelper::NotifyAcked, this).Bind(nodeId, deviceId));
99 device->GetMac()->TraceConnectWithoutContext(
100 "DroppedMpdu",
101 MakeCallback(&WifiTxStatsHelper::NotifyMacDropped, this).Bind(nodeId, deviceId));
102 for (int i = 0; i < device->GetNPhys(); ++i)
103 {
104 device->GetPhy(i)->TraceConnectWithoutContext(
105 "PhyTxBegin",
106 MakeCallback(&WifiTxStatsHelper::NotifyTxStart, this).Bind(nodeId, deviceId));
107 }
108 }
109}
110
111void
113{
114 NS_LOG_FUNCTION(this << startTime.As(Time::S));
115 NS_ASSERT_MSG(startTime >= Now(), "Invalid Start: " << startTime << " less than current time");
116 NS_ASSERT_MSG(startTime <= m_stopTime,
117 "Invalid Start: " << startTime << " and Stop: " << m_stopTime << " Time");
118 m_startTime = startTime;
119}
120
121void
123{
124 NS_LOG_FUNCTION(this << stopTime.As(Time::S));
125 NS_ASSERT_MSG(stopTime >= Now(), "Invalid Stop: " << stopTime << " less than current time");
127 "Invalid Start: " << m_startTime << " and Stop: " << stopTime << " Time");
129}
130
131void
133{
134 NS_LOG_FUNCTION(this);
135 m_successMap.clear();
136 m_failureMap.clear();
137 m_startTime = Now();
138}
139
142{
144 for (const auto& [nodeDevTuple, records] : m_successMap)
145 {
146 results[{std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)}] = records.size();
147 }
148 return results;
149}
150
153{
155 for (const auto& [nodeDevTuple, records] : m_successMap)
156 {
157 for (const auto& record : records)
158 {
159 NS_ASSERT_MSG(!record.m_successLinkIdSet.empty(), "No LinkId set on MPDU");
160 if (type == FIRST_LINK_IN_SET)
161 {
162 const auto firstLinkId = *record.m_successLinkIdSet.begin();
163 const auto nodeDevLinkTuple =
164 std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
165 std::get<1>(nodeDevTuple),
166 firstLinkId);
167 if (auto countIt = results.find(nodeDevLinkTuple); countIt != results.end())
168 {
169 countIt->second++;
170 }
171 else
172 {
173 results[nodeDevLinkTuple] = 1;
174 }
175 }
176 else
177 {
178 for (const auto linkId : record.m_successLinkIdSet)
179 {
180 const auto nodeDevLinkTuple =
181 std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
182 std::get<1>(nodeDevTuple),
183 linkId);
184 if (auto countIt = results.find(nodeDevLinkTuple); countIt != results.end())
185 {
186 countIt->second++;
187 }
188 else
189 {
190 results[nodeDevLinkTuple] = 1;
191 }
192 }
193 }
194 }
195 }
196 return results;
197}
198
201{
203 for (const auto& [nodeDevTuple, records] : m_failureMap)
204 {
205 results[{std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)}] = records.size();
206 }
207 return results;
208}
209
212{
214 for (const auto& [nodeDevTuple, records] : m_failureMap)
215 {
216 for (const auto& record : records)
217 {
218 NS_ASSERT_MSG(record.m_dropTime.has_value() && record.m_dropReason.has_value(),
219 "Incomplete dropped MPDU record");
220 if (record.m_dropReason == reason)
221 {
222 if (auto countIt =
223 results.find({std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)});
224 countIt != results.end())
225 {
226 countIt->second++;
227 }
228 else
229 {
230 results[{std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)}] = 1;
231 }
232 }
233 }
234 }
235 return results;
236}
237
240{
242 for (const auto& [nodeDevLinkTuple, records] : m_successMap)
243 {
244 for (const auto& record : records)
245 {
246 if (auto countIt =
247 results.find({std::get<0>(nodeDevLinkTuple), std::get<1>(nodeDevLinkTuple)});
248 countIt != results.end())
249 {
250 countIt->second += record.m_retransmissions;
251 }
252 else
253 {
254 results[{std::get<0>(nodeDevLinkTuple), std::get<1>(nodeDevLinkTuple)}] =
255 record.m_retransmissions;
256 }
257 }
258 }
259 return results;
260}
261
262uint64_t
264{
265 uint64_t count{0};
266 for (const auto& [nodeDevLinkTuple, records] : m_successMap)
267 {
268 count += records.size();
269 }
270 return count;
271}
272
273uint64_t
275{
276 uint64_t count{0};
277 for (const auto& [nodeDevTuple, records] : m_failureMap)
278 {
279 count += records.size();
280 }
281 return count;
282}
283
284uint64_t
286{
287 uint64_t count{0};
288 for (const auto& [nodeDevTuple, records] : m_failureMap)
289 {
290 for (const auto& record : records)
291 {
292 NS_ASSERT_MSG(record.m_dropTime.has_value() && record.m_dropReason.has_value(),
293 "Incomplete dropped MPDU record");
294 if (record.m_dropReason == reason)
295 {
296 count++;
297 }
298 }
299 }
300 return count;
301}
302
303uint64_t
305{
306 uint64_t count{0};
307 for (const auto& [nodeDevTuple, records] : m_successMap)
308 {
309 for (const auto& record : records)
310 {
311 count += record.m_retransmissions;
312 }
313 }
314 return count;
315}
316
317Time
319{
320 return Now() - m_startTime;
321}
322
325{
327 for (const auto& [nodeDevTuple, records] : m_successMap)
328 {
329 for (const auto& record : records)
330 {
331 NS_ASSERT_MSG(!record.m_successLinkIdSet.empty(), "No LinkId set on MPDU");
332 if (type == FIRST_LINK_IN_SET)
333 {
334 const auto firstLinkId = *record.m_successLinkIdSet.begin();
335 const auto nodeDevLinkTuple =
336 std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
337 std::get<1>(nodeDevTuple),
338 firstLinkId);
339 results[nodeDevLinkTuple].emplace_back(record);
340 }
341 else
342 {
343 for (const auto linkId : record.m_successLinkIdSet)
344 {
345 const auto nodeDevLinkTuple =
346 std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
347 std::get<1>(nodeDevTuple),
348 linkId);
349 results[nodeDevLinkTuple].emplace_back(record);
350 }
351 }
352 }
353 }
354 return results;
355}
356
362
363void
365{
366 NS_LOG_FUNCTION(this << nodeId << deviceId << mpdu);
367 const auto now = Simulator::Now();
368 if (now > m_stopTime)
369 {
370 NS_LOG_DEBUG("Ignoring enqueue because helper is stopped");
371 return;
372 }
373 if (mpdu->GetHeader().IsData())
374 {
375 if (!mpdu->GetHeader().HasData())
376 {
377 // exclude Null frames
378 return;
379 }
380 MpduRecord record{.m_nodeId = nodeId, .m_deviceId = deviceId, .m_enqueueTime = now};
381 if (mpdu->GetHeader().IsQosData())
382 {
383 record.m_tid = mpdu->GetHeader().GetQosTid();
384 }
385 NS_LOG_INFO("Creating inflight record for packet UID "
386 << mpdu->GetPacket()->GetUid() << " node " << nodeId << " device " << deviceId
387 << " tid " << +record.m_tid);
388 m_inflightMap[mpdu->GetPacket()->GetUid()] = record;
389 }
390}
391
392void
394{
395 NS_LOG_FUNCTION(this << nodeId << deviceId << pkt);
396 const auto now = Now();
397 if (now > m_stopTime)
398 {
399 NS_LOG_DEBUG("Ignoring TxStart because helper is stopped");
400 return;
401 }
402 if (auto pktRecord = m_inflightMap.find(pkt->GetUid()); pktRecord != m_inflightMap.end())
403 {
404 auto& [uid, record] = *pktRecord;
405 NS_LOG_INFO("Packet UID " << uid << " started");
406 if (record.m_txStartTime.IsZero())
407 {
408 NS_LOG_INFO("TxStart called (first transmission) for inflight packet UID "
409 << pkt->GetUid());
410 record.m_txStartTime = now;
411 }
412 else
413 {
414 NS_LOG_INFO("TxStart called (retransmission) for inflight packet UID "
415 << pkt->GetUid());
416 record.m_retransmissions++;
417 }
418 }
419}
420
421void
423{
424 NS_LOG_FUNCTION(this << nodeId << deviceId << mpdu);
425 NS_ASSERT_MSG(!mpdu->GetInFlightLinkIds().empty(), "No LinkId set on MPDU");
426 const auto now = Now();
427 if (now <= m_startTime || now > m_stopTime)
428 {
429 if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
430 pktRecord != m_inflightMap.end())
431 {
432 m_inflightMap.erase(pktRecord);
433 }
434 NS_LOG_DEBUG("Ignoring acknowledgement because the time is out of range");
435 return;
436 }
437 // Get the set of in-flight link IDs
438 const auto linkIds = mpdu->GetInFlightLinkIds();
439 if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
440 pktRecord != m_inflightMap.end())
441 {
442 auto& [uid, record] = *pktRecord;
443 record.m_ackTime = now;
444 record.m_successLinkIdSet = linkIds;
445 record.m_mpduSeqNum = mpdu->GetHeader().GetSequenceNumber();
446 // Store record in success map and remove it below from inflight map
447 NS_LOG_INFO("Successful transmission logged of packet UID " << uid);
448 m_successMap[{nodeId, deviceId}].push_back(std::move(record));
449 NS_LOG_INFO("Erasing packet UID " << uid << " from inflight map due to success");
450 m_inflightMap.erase(pktRecord);
451 }
452}
453
454void
456 uint32_t deviceId,
457 WifiMacDropReason reason,
459{
460 NS_LOG_FUNCTION(this << nodeId << deviceId << +reason << mpdu);
461 const auto now = Now();
462 if (now <= m_startTime || now > m_stopTime)
463 {
464 if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
465 pktRecord != m_inflightMap.end())
466 {
467 m_inflightMap.erase(pktRecord);
468 }
469 NS_LOG_DEBUG("Ignoring drop because the time is out of range");
470 return;
471 }
472 if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
473 pktRecord != m_inflightMap.end())
474 {
475 auto& [uid, record] = *pktRecord;
476 NS_LOG_INFO("Packet UID " << uid << " dropped");
477 record.m_dropTime = now;
478 record.m_dropReason = reason;
479 record.m_mpduSeqNum = mpdu->GetHeader().GetSequenceNumber();
480 NS_LOG_INFO("Failed transmission logged of packet UID " << uid);
481 // Store record in failure map and remove it below from inflight map
482 m_failureMap[{nodeId, deviceId}].push_back(std::move(record));
483 NS_LOG_INFO("Erasing packet UID " << uid << " from inflight map due to failure");
484 m_inflightMap.erase(pktRecord);
485 }
486}
487
488} // namespace ns3
holds a vector of ns3::NetDevice pointers
void Add(NetDeviceContainer other)
Append the contents of another NetDeviceContainer to the end of this container.
keep track of a set of node pointers.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:408
@ S
second
Definition nstime.h:106
Time m_stopTime
The stop time for recording statistics.
MpduRecordsPerNodeDevice_t m_successMap
The nested map of successful MPDUs.
void Reset()
Clear the data structures for all completed successful and failed MpduRecords.
std::unordered_map< std::tuple< uint32_t, uint32_t >, std::list< MpduRecord >, TupleHash > MpduRecordsPerNodeDevice_t
std::unordered_map of {nodeId, deviceId} tuple to a list of MPDU records
std::unordered_map< std::tuple< uint32_t, uint32_t, uint8_t >, std::list< MpduRecord >, TupleHash > MpduRecordsPerNodeDeviceLink_t
std::unordered_map of {nodeId, deviceId, linkId} tuple to a list of MPDU records
std::map< uint64_t, MpduRecord > m_inflightMap
In-flight MPDUs; key is a Packet UID.
void NotifyAcked(uint32_t nodeId, uint32_t deviceId, Ptr< const WifiMpdu > mpdu)
Callback for the WifiMac::AckedMpdu trace.
CountPerNodeDeviceLink_t GetSuccessesByNodeDeviceLink(WifiTxStatsHelper::MultiLinkSuccessType type=FIRST_LINK_IN_SET) const
Return the counts of successful MPDU transmissions in a hash map.
MultiLinkSuccessType
When Multi-Link Operation (MLO) is used, it is possible for MPDUs to be sent on multiple links concur...
void Stop(Time stopTime)
Set the stop time for statistics collection.
void NotifyTxStart(uint32_t nodeId, uint32_t deviceId, Ptr< const Packet > pkt, Watt_u)
Callback for the WifiPhy::PhyTxBegin trace.
uint64_t GetFailures() const
Return the count of failed MPDU transmissions across all enabled devices.
WifiTxStatsHelper()
Default constructor; start time initialized to zero and stop time to Time::Max().
Time m_startTime
The start time for recording statistics.
void NotifyMacEnqueue(uint32_t nodeId, uint32_t deviceId, Ptr< const WifiMpdu > mpdu)
Callback for the WifiMacQueue::Enqueue trace.
CountPerNodeDevice_t GetFailuresByNodeDevice() const
Return the counts of failed MPDU transmissions in a hash map.
std::unordered_map< std::tuple< uint32_t, uint32_t >, uint64_t, TupleHash > CountPerNodeDevice_t
std::unordered_map of {nodeId, deviceId} tuple to counter value
const MpduRecordsPerNodeDeviceLink_t GetSuccessRecords(WifiTxStatsHelper::MultiLinkSuccessType type=FIRST_LINK_IN_SET) const
Return a hash map of successful MPDU records.
const MpduRecordsPerNodeDevice_t & GetFailureRecords() const
Return a hash map of MPDU records for failed transmissions.
CountPerNodeDevice_t GetSuccessesByNodeDevice() const
Return the counts of successful MPDU transmissions in a hash map.
MpduRecordsPerNodeDevice_t m_failureMap
The nested map of failed MPDUs.
uint64_t GetSuccesses() const
Return the count of successful MPDU transmissions across all enabled devices.
void NotifyMacDropped(uint32_t nodeId, uint32_t deviceId, WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Callback for the WifiMac::DroppedMpdu trace.
void Enable(const NodeContainer &nodes)
Enables trace collection for all nodes and WifiNetDevices in the specified NodeContainer.
CountPerNodeDevice_t GetRetransmissionsByNodeDevice() const
Return the counts of MPDU retransmissions in a hash map.
uint64_t GetRetransmissions() const
Return the count of MPDU retransmissions across all enabled devices.
void Start(Time startTime)
Set the start time for statistics collection.
Time GetDuration() const
Return the duration since the helper was started or reset.
std::unordered_map< std::tuple< uint32_t, uint32_t, uint8_t >, uint64_t, TupleHash > CountPerNodeDeviceLink_t
std::unordered_map of {nodeId, deviceId, linkId} tuple to counter value
Time stopTime
#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
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:690
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
#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:267
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition simulator.cc:288
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:71
@ AC_BE_NQOS
Non-QoS.
Definition qos-utils.h:73
NodeContainer nodes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:605
double Watt_u
Watt weak type.
Definition wifi-units.h:25
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition qos-utils.cc:115
Structure for recording information about an individual data MPDU transmission.
uint8_t m_tid
The TID for the MPDU.