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),
34 m_stopTime(stopTime)
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.
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
@ S
second
Definition nstime.h:105
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::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.
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
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.
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
Time GetDuration() const
Return the duration since the helper was started or reset.
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
#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_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
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition simulator.cc:294
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:71
@ AC_BE_NQOS
Non-QoS.
Definition qos-utils.h:72
NodeContainer nodes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
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:684
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
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.
uint32_t m_nodeId
The sending node ID.