A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tgax-voip-traffic-test-suite.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024 DERONNE SOFTWARE ENGINEERING
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Sébastien Deronne <sebastien.deronne@gmail.com>
7 */
8
9#include "ns3/application-container.h"
10#include "ns3/application-helper.h"
11#include "ns3/config.h"
12#include "ns3/double.h"
13#include "ns3/internet-stack-helper.h"
14#include "ns3/node-container.h"
15#include "ns3/node.h"
16#include "ns3/nstime.h"
17#include "ns3/packet-sink-helper.h"
18#include "ns3/packet-sink.h"
19#include "ns3/packet-socket-address.h"
20#include "ns3/simple-net-device-helper.h"
21#include "ns3/test.h"
22#include "ns3/tgax-voip-traffic.h"
23#include "ns3/traced-callback.h"
24
25#include <algorithm>
26#include <map>
27#include <numeric>
28#include <optional>
29#include <string>
30#include <utility>
31#include <vector>
32
33using namespace ns3;
34
35NS_LOG_COMPONENT_DEFINE("TgaxVoipTrafficTest");
36
37namespace
38{
39const uint32_t voicePayloadSize = 33; ///< payload size of voice packets in bytes
40const uint32_t silencePayloadSize = 7; ///< payload size of silence packets in bytes
41const uint32_t compressedProtocolHeader = 3; ///< size of compressed protocol header (assumes IPv4)
42const double tol = 0.1; ///< some tolerance for floating point comparisons
43} // namespace
44
45/**
46 * @ingroup applications-test
47 * @ingroup tests
48 *
49 * TGax voice-over-IP traffic test.
50 */
52{
53 public:
54 /// Information about VoIP parameters
56 {
57 Time meanActiveStateDuration; //!< Mean duration of active/talking state
58 Time meanInactiveStateDuration; //!< Mean duration of inactive/silence state
59 double
60 voiceToSilenceProbability; //!< Probability to transition from active to inactive state
61 double
62 silenceToVoiceProbability; //!< Probability to transition from inactive to active state
63 Time scaleDelayJitter; //!< Scale of laplacian distribution used for delay jitter
64 Time boundDelayJitter; //!< Bound of laplacian distribution used for delay jitter
65 };
66
67 /**
68 * Constructor
69 * @param name the name of the test to run
70 * @param params the VoIP parameters to use for the test, default parameters are used if not
71 * provided
72 */
73 TgaxVoipTrafficTestCase(const std::string& name, std::optional<VoipParams> params = {});
74
75 private:
76 void DoSetup() override;
77 void DoRun() override;
78
79 /**
80 * Record a packets successfully sent
81 * @param packet the transmitted packet
82 * @param jitter the delay jitter applied to the packet
83 */
84 void PacketSent(Ptr<const Packet> packet, Time jitter);
85
86 /**
87 * Record a packet successfully received
88 * @param context the context
89 * @param p the packet
90 * @param addr the sender's address
91 */
92 void ReceiveRx(std::string context, Ptr<const Packet> p, const Address& addr);
93
94 /**
95 * Record a change in VoIP voice activity state
96 * @param state the new voice activity state
97 * @param duration the expected duration of the state
98 */
100
101 /// Information about transmitted packet
102 struct TxInfo
103 {
104 /**
105 * Constructor
106 * @param s the size of the packet in bytes
107 * @param t the timestamp at which the packet is transmitted
108 * @param j the delay jitter applied to the packet
109 */
111 : size{s},
112 tstamp{t},
113 jitter{j}
114 {
115 }
116
117 uint32_t size{}; //!< size of the packet in bytes
118 Time tstamp{}; //!< timestamp at which the packet is transmitted
119 Time jitter{}; //!< delay jitter applied to the packet
120 };
121
122 std::map<uint64_t, TxInfo> m_sent; //!< transmitted VoIP packets
123 uint64_t m_received{0}; //!< number of bytes received
124 std::vector<std::pair<TgaxVoipTraffic::VoiceActivityState, Time>>
125 m_states; //!< Hold voice activity states and the time at which it started
126
127 std::optional<VoipParams> m_params; //!< VoIP parameters
128};
129
131 std::optional<VoipParams> params)
132 : TestCase(name),
133 m_params(params)
134{
135}
136
137void
139{
140 NS_LOG_FUNCTION(this << packet << packet->GetSize() << packet->GetUid() << jitter);
141 if (!m_sent.empty() && m_params && m_params->boundDelayJitter.IsZero())
142 {
143 NS_TEST_ASSERT_MSG_EQ(packet->GetUid(),
144 (m_sent.rbegin()->first + 1),
145 "Packets should arrive in order if there is no jitter");
146 }
147 m_sent.try_emplace(packet->GetUid(),
148 packet->GetSize() - compressedProtocolHeader,
150 jitter);
151}
152
153void
155{
156 NS_LOG_FUNCTION(this << state << duration);
157 m_states.emplace_back(state, Simulator::Now());
158}
159
160void
162{
163 NS_LOG_FUNCTION(this << p << addr << p->GetSize());
164 m_received += (p->GetSize() - compressedProtocolHeader);
165}
166
167void
169{
170 NS_LOG_FUNCTION(this);
171
172 const auto simulationTime{Seconds(300)};
173
174 auto sender = CreateObject<Node>();
175 auto receiver = CreateObject<Node>();
176
178 nodes.Add(sender);
179 nodes.Add(receiver);
180
181 SimpleNetDeviceHelper simpleHelper;
182 auto devices = simpleHelper.Install(nodes);
183
184 InternetStackHelper internet;
185 internet.Install(nodes);
186
187 PacketSocketAddress socketAddress;
188 socketAddress.SetSingleDevice(devices.Get(0)->GetIfIndex());
189 socketAddress.SetPhysicalAddress(devices.Get(1)->GetAddress());
190 socketAddress.SetProtocol(1);
191
193 sourceHelper.SetAttribute("Remote", AddressValue(socketAddress));
194 sourceHelper.SetAttribute("ActivePacketPayloadSize",
196 sourceHelper.SetAttribute("SilencePacketPayloadSize",
198 if (m_params)
199 {
200 sourceHelper.SetAttribute("MeanActiveStateDuration",
201 TimeValue(m_params->meanActiveStateDuration));
202 sourceHelper.SetAttribute("MeanInactiveStateDuration",
203 TimeValue(m_params->meanInactiveStateDuration));
204 sourceHelper.SetAttribute("VoiceToSilenceProbability",
205 DoubleValue(m_params->voiceToSilenceProbability));
206 sourceHelper.SetAttribute("SilenceToVoiceProbability",
207 DoubleValue(m_params->silenceToVoiceProbability));
208 sourceHelper.SetAttribute("ScaleDelayJitter", TimeValue(m_params->scaleDelayJitter));
209 sourceHelper.SetAttribute("BoundDelayJitter", TimeValue(m_params->boundDelayJitter));
210 }
211 auto sourceApp = sourceHelper.Install(sender);
212 const auto startAppTime = Seconds(1.0);
213 sourceApp.Start(startAppTime);
214 sourceApp.Stop(startAppTime + simulationTime);
216
217 PacketSinkHelper sinkHelper("ns3::PacketSocketFactory", socketAddress);
218 auto sinkApp = sinkHelper.Install(receiver);
219 sinkApp.Start(Seconds(0.0));
220 sinkApp.Stop(Seconds(2.0) + simulationTime);
221
222 int64_t streamNumber = 10;
223 sourceHelper.AssignStreams(nodes, streamNumber);
224
226 "/NodeList/*/$ns3::Node/ApplicationList/*/$ns3::TgaxVoipTraffic/StateUpdate",
228
230 "/NodeList/*/$ns3::Node/ApplicationList/*/$ns3::TgaxVoipTraffic/TxWithJitter",
232
233 Config::Connect("/NodeList/*/ApplicationList/*/$ns3::PacketSink/Rx",
235}
236
237void
239{
242
243 const auto totalTx =
244 std::accumulate(m_sent.cbegin(), m_sent.cend(), 0ULL, [](auto sum, const auto& elem) {
245 return sum + elem.second.size;
246 });
247 NS_TEST_ASSERT_MSG_EQ(totalTx, m_received, "Did not receive all transmitted voip packets");
248
249 const auto offset = m_params
250 ? m_params->boundDelayJitter
251 : MilliSeconds(80); // an offset is applied in the model to guarantee
252 // positive time for scheduling packets
253 std::optional<TxInfo> prevTx{};
254 for (const auto& [id, info] : m_sent)
255 {
256 auto stateIt =
257 std::upper_bound(m_states.begin(),
258 m_states.end(),
259 info.tstamp - info.jitter - offset,
260 [](Time value, const auto& state) { return value <= state.second; });
261 stateIt--;
262 auto expectedSize =
266 NS_TEST_ASSERT_MSG_EQ(info.size, expectedSize, "Unexpected packet size");
267
268 if (!prevTx)
269 {
270 // first TX, no delta with previous
271 prevTx = info;
272 continue;
273 }
274
275 const auto interval = info.tstamp - prevTx->tstamp;
276 const auto jitterCorrection = info.jitter - prevTx->jitter;
277 const auto expectedInterval =
279 ? MilliSeconds(160)
280 : MilliSeconds(20);
281 NS_TEST_ASSERT_MSG_EQ(interval,
282 expectedInterval + jitterCorrection,
283 "Unexpected encoder frame interval");
284
285 prevTx = info;
286 }
287
288 std::vector<Time> inactiveDurations;
289 std::transform(m_states.cbegin(),
290 m_states.cend() - 1,
291 m_states.cbegin() + 1,
292 std::back_inserter(inactiveDurations),
293 [](const auto& lhs, const auto& rhs) {
294 return (lhs.first == TgaxVoipTraffic::VoiceActivityState::INACTIVE_SILENCE)
295 ? (rhs.second - lhs.second)
296 : Seconds(0);
297 });
298 auto noZeroEnd = std::remove_if(inactiveDurations.begin(),
299 inactiveDurations.end(),
300 [&](const auto& t) { return t.IsZero(); });
301 inactiveDurations.erase(noZeroEnd, inactiveDurations.end());
302 const auto totalInactiveDuration =
303 std::accumulate(inactiveDurations.cbegin(),
304 inactiveDurations.cend(),
305 Time(),
306 [](auto sum, const auto t) { return sum + t; });
307
308 std::vector<Time> activeDurations;
309 std::transform(m_states.cbegin(),
310 m_states.cend() - 1,
311 m_states.cbegin() + 1,
312 std::back_inserter(activeDurations),
313 [](const auto& lhs, const auto& rhs) {
314 return (lhs.first == TgaxVoipTraffic::VoiceActivityState::ACTIVE_TALKING)
315 ? (rhs.second - lhs.second)
316 : Seconds(0);
317 });
318 noZeroEnd = std::remove_if(activeDurations.begin(), activeDurations.end(), [&](const auto& t) {
319 return t.IsZero();
320 });
321 activeDurations.erase(noZeroEnd, activeDurations.end());
322 const auto totalActiveDuration =
323 std::accumulate(activeDurations.cbegin(),
324 activeDurations.cend(),
325 Time(),
326 [](auto sum, const auto t) { return sum + t; });
327
328 const auto averageActiveDuration = totalActiveDuration / activeDurations.size();
329 const auto expectedAverageActiveStateDurationMs =
330 m_params ? m_params->meanActiveStateDuration.GetMilliSeconds() : 1250;
331 NS_TEST_EXPECT_MSG_EQ_TOL(averageActiveDuration.GetMilliSeconds(),
332 expectedAverageActiveStateDurationMs,
333 tol * expectedAverageActiveStateDurationMs,
334 "Unexpected average active state duration");
335
336 const auto averageInactiveDuration = totalInactiveDuration / inactiveDurations.size();
337 const auto expectedAverageInactiveStateDurationMs =
338 m_params ? m_params->meanInactiveStateDuration.GetMilliSeconds() : 1250;
339 NS_TEST_EXPECT_MSG_EQ_TOL(averageInactiveDuration.GetMilliSeconds(),
340 expectedAverageInactiveStateDurationMs,
341 tol * expectedAverageInactiveStateDurationMs,
342 "Unexpected average inactive state duration");
343
344 const auto totalDuration = totalInactiveDuration + totalActiveDuration;
345 const auto voiceActivityFactor = static_cast<double>(totalActiveDuration.GetMicroSeconds()) /
346 totalDuration.GetMicroSeconds();
347 const auto expectedVoiceActivityFactor =
348 m_params ? (m_params->silenceToVoiceProbability /
349 (m_params->silenceToVoiceProbability + m_params->voiceToSilenceProbability))
350 : 0.5; // default is 50%
351 NS_TEST_EXPECT_MSG_EQ_TOL(voiceActivityFactor,
352 expectedVoiceActivityFactor,
353 tol,
354 "Unexpected voice activity factor");
355
356 const double totalJitterUs =
357 std::accumulate(m_sent.cbegin(), m_sent.cend(), 0.0, [](double sum, const auto& elem) {
358 return sum + elem.second.jitter.GetMicroSeconds();
359 });
360 const auto avgJitterMs = (totalJitterUs / m_sent.size()) / 1000;
361 NS_TEST_EXPECT_MSG_EQ_TOL(avgJitterMs, 0.0, tol, "Unexpected average jitter");
362}
363
364/**
365 * @ingroup applications-test
366 * @ingroup tests
367 *
368 * @brief TgaxVoipTraffic TestSuite
369 */
371{
372 public:
374};
375
377 : TestSuite("applications-tgax-voip-traffic", Type::UNIT)
378{
379 AddTestCase(new TgaxVoipTrafficTestCase("VoIP traffic with default parameters"),
381 AddTestCase(new TgaxVoipTrafficTestCase("VoIP traffic without jitter",
383 MilliSeconds(1250),
384 0.016,
385 0.016,
386 Time(),
387 Time()}),
389 AddTestCase(new TgaxVoipTrafficTestCase("VoIP traffic with custom parameters",
391 MilliSeconds(1500),
392 0.0200,
393 0.0133,
394 MicroSeconds(5000),
395 MilliSeconds(60)}),
397}
398
400 g_TgaxVoipTrafficTestSuite; //!< Static variable for test initialization
TGax voice-over-IP traffic test.
void StateUpdated(TgaxVoipTraffic::VoiceActivityState state, Time duration)
Record a change in VoIP voice activity state.
uint64_t m_received
number of bytes received
std::vector< std::pair< TgaxVoipTraffic::VoiceActivityState, Time > > m_states
Hold voice activity states and the time at which it started.
TgaxVoipTrafficTestCase(const std::string &name, std::optional< VoipParams > params={})
Constructor.
void ReceiveRx(std::string context, Ptr< const Packet > p, const Address &addr)
Record a packet successfully received.
std::optional< VoipParams > m_params
VoIP parameters.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void PacketSent(Ptr< const Packet > packet, Time jitter)
Record a packets successfully sent.
void DoRun() override
Implementation to actually run this TestCase.
std::map< uint64_t, TxInfo > m_sent
transmitted VoIP packets
a polymophic address class
Definition address.h:114
AttributeValue implementation for Address.
Definition address.h:329
void Start(Time start) const
Start all of the Applications in this container at the start time given as a parameter.
A helper to make it easier to instantiate an application on a set of nodes.
ApplicationContainer Install(NodeContainer c)
Install an application on each node of the input container configured with all the attributes set wit...
int64_t AssignStreams(NodeContainer c, int64_t stream)
Assigns a unique (monotonically increasing) stream number to all applications that match the configur...
void SetAttribute(const std::string &name, const AttributeValue &value)
Helper function used to set the underlying application attributes.
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition double.h:31
aggregate IP/TCP/UDP functionality to existing Nodes.
keep track of a set of node pointers.
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes.
an address for a packet socket
void SetProtocol(uint16_t protocol)
Set the protocol.
void SetPhysicalAddress(const Address address)
Set the destination address.
void SetSingleDevice(uint32_t device)
Set the address to match only a specified NetDevice.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
build a set of SimpleNetDevice objects
NetDeviceContainer Install(Ptr< Node > node) const
This method creates an ns3::SimpleChannel with the attributes configured by SimpleNetDeviceHelper::Se...
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:125
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
static void Run()
Run the simulation.
Definition simulator.cc:161
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:296
@ QUICK
Fast test.
Definition test.h:1057
TestCase(const TestCase &)=delete
Caller graph was not generated because of its size.
Type
Type of test.
Definition test.h:1271
TestSuite(std::string name, Type type=Type::UNIT)
Construct a new test suite.
Definition test.cc:494
VoiceActivityState
Voice activity states.
static TypeId GetTypeId()
Get the type ID.
Simulation virtual time values and global simulation resolution.
Definition nstime.h:95
AttributeValue implementation for Time.
Definition nstime.h:1375
Hold an unsigned integer type.
Definition uinteger.h:34
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
void Connect(std::string path, const CallbackBase &cb)
Definition config.cc:970
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition config.cc:946
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:627
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:133
#define NS_TEST_EXPECT_MSG_EQ_TOL(actual, limit, tol, msg)
Test that actual and expected (limit) values are equal to plus or minus some tolerance and report if ...
Definition test.h:499
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1307
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1290
NodeContainer nodes
const double tol
some tolerance for floating point comparisons
const uint32_t voicePayloadSize
payload size of voice packets in bytes
const uint32_t compressedProtocolHeader
size of compressed protocol header (assumes IPv4)
const uint32_t silencePayloadSize
payload size of silence packets in bytes
Every class exported by the ns3 library is enclosed in the ns3 namespace.
uint32_t size
size of the packet in bytes
Time jitter
delay jitter applied to the packet
TxInfo(uint32_t s, Time t, Time j)
Constructor.
Time tstamp
timestamp at which the packet is transmitted
double voiceToSilenceProbability
Probability to transition from active to inactive state.
Time scaleDelayJitter
Scale of laplacian distribution used for delay jitter.
Time meanActiveStateDuration
Mean duration of active/talking state.
double silenceToVoiceProbability
Probability to transition from inactive to active state.
Time boundDelayJitter
Bound of laplacian distribution used for delay jitter.
Time meanInactiveStateDuration
Mean duration of inactive/silence state.
static TgaxVoipTrafficTestSuite g_TgaxVoipTrafficTestSuite
Static variable for test initialization.