Skip to content

Instantly share code, notes, and snippets.

@japaric
Last active February 15, 2018 22:56
Show Gist options
  • Save japaric/d425b2bd342e26eb899370636b5fb480 to your computer and use it in GitHub Desktop.
Save japaric/d425b2bd342e26eb899370636b5fb480 to your computer and use it in GitHub Desktop.

LAB 2: Implementing traffic generators

This laboratory can be found in HTML rendered form here

Authors:

Table of contents

Objectives

The objective of this lab is to:

  • Understand three different Internet traffic types. Namely, constant bit rate (CBR), Normal or Gaussian traffic distribution, and Poisson traffic distribution.

  • Implement three traffic generators based on distributions mentioned above.

Sink

We implemented a Node subclass called Sink that logs the timestamp of each received packet to a file. The logged data was used to plot the distribution of the traffic and to extract traffic statistics.

Generators

When designing the generator classes, we decided that the user should only need to specify the time that the generator should run for, along with parameters specific to each generator type.

Like Sink each generator included functionality to log the timestamps of each sent packet to a file.

To make comparisons between different generators meaningful we ran all simulations for 10,000 seconds.

Generator_CBR

Generator_CBR is a subclass of the Node class that acts as a traffic generator that produces Constant Bit Rate (CBR) traffic. Generator_CBR takes a parameter k upon construction that denotes the number of packets to send per second. The behavior of the Generator_CBR is to send k packets every second; these k packets are sent at random times in an interval of 1 second. To elaborate on the last part: for a value k of 5 the generator will send five packets between time t = 2s and t = 3s. For example, the timestamps could be 2.3111, 2.6556, 2.4052, 2.1056, 2.0255.

Generator distribution

Below is shown the packet frequency distribution of a CBR generator with parameter k of 10 packets per second and that ran for 10,000 seconds.

cbr-generator.png

The above graph was produced by first computing the packet frequency at each one second time interval. We basically translated the log file which looks like this:

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9

Into packet frequency data:

10 # t = 0s
10 # t = 1s

And the proceeded to generate an histogram out of that data.

Sink distributions

Here we present the distributions observed at the receiver side (sink) for different types of links.

  • A lossless Link maintains the distribution observed at the sender so we have omitted the graph.

  • A LossyLink with 50% chance of dropping packets.

cbr-lossy.png

On the generator side we measured a mean packet frequency of 50.0 and a standard deviation of 0.0.

On the sink side we measured a mean of 25.5110 and a standard deviation of 3.5271.

We observed a drop in the mean of about 50% and an increase in the standard deviation, which can both be explained by the loss of packets in the link.

On the generator side we observed a distribution with a single packet frequency (single bin) and on the sink side we observed a normal distribution.

  • LossyLink with mean delay of 1.0 and jitter of 0.1.

cbr-delay.png

On the generator side we measured a mean packet frequency of 50.0 and a standard deviation of 0.0.

On the sink side we measured a mean of 49.9947 and a standard deviation of 2.2916.

Once again we observe a normal distribution on the sink node. This time the mean remains approximately the same since there's no packet loss in the link. The jitter produces a variation in the packet frequency as it causes some packets to arrive in a different (later) time frame.

  • LossyLink with 50% chance of dropping packets, mean delay of 1.0 and jitter of 0.1.

cbr-lossy-delay.png

On the generator side we measured a mean packet frequency of 50.0 and a standard deviation of 0.0.

On the sink side we measured a mean of 25.5240 and a standard deviation of 3.3702.

This run is similar to the first LossyLink simulation. We also observed a 50% decrease in the mean value due to packet loss. The standard deviation was a bit higher than in the first simulation; the higher deviation is probably caused by the jitter.

Generator_POISSON

Generator_POISSON is a subclass of the Node class that acts as a traffic generator that produces traffic that follows a Poisson distribution.

From wikipedia 1 we know that:

The Poisson distribution (..) is a discrete probability distribution that expresses the probability of a given number of events occurring in a fixed interval of time or space if these events occur with a known constant rate and independently of the time since the last event.

Generator_POISSON takes a parameter lambda that represents the number of expected occurrences. We'll re-use the approach we used to implement the Generator_CBR and have the Generator_POISSON produce k packets per second, but this time the value k will change every second; the value k will be drawn from a Poisson random number generator parameterized by the parameter lambda.

Generator distribution

Below is shown the packet frequency distribution of a Poisson generator with parameter lambda of 10 packets per second and that ran for 10,000 seconds.

poisson-generator.png

Sink distributions

Here we present the distributions observed at the receiver side (sink) for different types of links.

  • A lossless Link maintains the distribution observed at the sender so we have omitted the graph.

  • A LossyLink with 50% chance of dropping packets.

poisson-lossy.png

On the generator side we measured a mean packet frequency of 9.9546 and a standard deviation of 3.1634.

On the sink side we measured a mean of 5.0802 and a standard deviation of 2.2468.

We observed that the mode (most common) value, the lambda parameter of the Poisson distribution, dropped to 5 on the sink side. This can be explained by the loss of packets in the link.

  • LossyLink with mean delay of 1.0 and jitter of 0.1.

poisson-delay.png

On the generator side we measured a mean packet frequency of 10.0406 and a standard deviation of 3.1635.

On the sink side we measured a mean of 10.0395 and a standard deviation of 3.1785.

We didn't observe much difference between the generator and the sink. The jitter effect doesn't seem to affect much the Poisson distribution.

  • LossyLink with 50% chance of dropping packets, mean delay of 1.0 and jitter of 0.1.

poisson-lossy-delay.png

On the generator side we measured a mean packet frequency of 9.9507 and a standard deviation of 3.1539.

On the sink side we measured a mean of 5.0825 and a standard deviation of 2.2541.

As in the first LossyLink simulation we observe that the most common frequency on the sink side dropped from 10 to 5.

Generator_NORMAL

Generator_NORMAL is a subclass of the Node class that acts as a traffic generator that produces traffic that follows a Normal (Gaussian) distribution 2.

Generator_NORMAL takes two parameters, mu and sigma, upon construction. These parameters denote the mean and standard deviation of the normal distribution used to generate traffic.

Generator_NORMAL has a very similar implementation to Generator_POISSON's; the main difference is that the parameter k will be drawn from a normal random number generator.

Generator distribution

Below is shown the packet frequency distribution of a Normal / Gaussian generator with a mean of 10 and a standard deviation of 2 that ran for 10,000 seconds.

normal-generator.png

When measuring the actual mean and standard deviation on the data we got: 9.5124 for the mean and 2.0295 for the standard deviation. These values are pretty close to the parameters of the generator.

Sink distributions

Here we present the distributions observed at the receiver side (sink) for different types of links.

  • A lossless Link maintains the distribution observed at the sender so we have omitted the graph.

  • A LossyLink with 50% chance of dropping packets.

normal-lossy.png

On the generator side we measured a mean packet frequency of 9.5124 and a standard deviation of 2.0295.

On the sink side we measured a mean of 4.6628 and a standard deviation of 1.8498.

The traffic distribution at the sink side also looks like a normal distribution but with a mean of about half the original one. This decrease in the mean can be explained by the packet loss in the link.

  • LossyLink with mean delay of 1.0 and jitter of 0.1.

normal-delay.png

On the generator side we measured a mean packet frequency of 9.5212 and a standard deviation of 2.0222.

On the sink side we measured a mean of 9.5193 and a standard deviation of 2.2162.

The traffic distribution on the sink side also looks like a normal distribution but with slightly larger standard deviation.

  • LossyLink with 50% chance of dropping packets, mean delay of 1.0 and jitter of 0.1.

normal-lossy-delay.png

On the generator side we measured a mean packet frequency of 9.4974 and a standard deviation of 2.0195.

On the sink side we measured a mean of 4.8567 and a standard deviation of 1.9262.

This run is similar to the first LossyLink simulation. We also observed a 50% decrease in the mean value due to packet loss. The standard deviation was a bit higher than in the first simulation; the higher deviation is probably caused by the jitter.

References

  1. Poisson distribution
  2. Normal distribution
# Plots the traffic distribution at both the generator and sink sides
#
# Usage
#
# $ # cbr-generator.txt is the traffic generator log file
# $ # cbr-sink.txt is the receiver side (sink) log file
# $ # 'cbr' indicates the generator used; this used to draw trend lines
# $ python plot.py cbr-generator.txt cbr-sink.txt 'cbr'
import scipy
import math as m
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import sys
# Style
sns.set()
# load generator data
g_timestamps = []
with open(sys.argv[1]) as f:
for line in f.readlines():
g_timestamps.append(float(line))
g_timestamps.sort()
# count the number of packets at each time interval
g_frequencies = []
i = 0
for t in range(0, m.ceil(g_timestamps[-1])):
frequency = 0
while True:
if i >= len(g_timestamps):
break
if g_timestamps[i] < t + 1:
i += 1
frequency += 1
else:
break
g_frequencies.append(frequency)
g_nbins = max(g_frequencies) - min(g_frequencies) + 1
# load sink data
s_timestamps = []
with open(sys.argv[2]) as f:
for line in f.readlines():
s_timestamps.append(float(line))
s_timestamps.sort()
# count the number of packets at each time interval
s_frequencies = []
i = 0
for t in range(0, m.ceil(s_timestamps[-1])):
frequency = 0
while True:
if i >= len(s_timestamps):
break
if s_timestamps[i] < t + 1:
i += 1
frequency += 1
else:
break
s_frequencies.append(frequency)
s_nbins = max(s_frequencies) - min(s_frequencies) + 1
ax = plt.subplot(121)
plt.hist(g_frequencies, g_nbins)
plt.title('Generator')
plt.xlabel('Packets per second')
plt.ylabel('Occurrences')
if sys.argv[3] == 'poisson':
lambda_ = scipy.stats.mode(g_frequencies).mode[0]
x = np.array(range(np.min(g_frequencies), np.max(g_frequencies)))
y = np.divide((lambda_ ** x) * np.exp(-lambda_), scipy.misc.factorial(x)) * len(g_frequencies)
x = x[y > 0]
y = y[y > 0]
plt.plot(x + 0.5, y, '--', label='Poisson fit')
plt.legend()
if sys.argv[3] == 'normal':
mu = np.mean(g_frequencies)
sigma = np.std(g_frequencies)
x = np.array(range(np.min(g_frequencies), np.max(g_frequencies))) + 0.5
y = scipy.stats.norm.pdf(x, mu, sigma) * len(g_frequencies)
plt.plot(x, y, '--', label='Gaussian fit')
plt.legend()
print('mean(G) = ', mu)
print('std(G) = ', sigma)
ax = plt.subplot(122)
plt.hist(s_frequencies, s_nbins)
plt.title('Sink')
plt.xlabel('Packets per second')
if sys.argv[3] == 'cbr':
mu = np.mean(s_frequencies)
sigma = np.std(s_frequencies)
x = np.array(range(np.min(s_frequencies), np.max(s_frequencies))) + 0.5
y = scipy.stats.norm.pdf(x, mu, sigma) * len(s_frequencies)
plt.plot(x, y, '--', label='Gaussian fit')
print('mean(G) = ', mu)
print('std(G) = ', sigma)
if sys.argv[3] == 'poisson':
lambda_ = scipy.stats.mode(s_frequencies).mode[0]
x = np.array(range(np.min(s_frequencies), np.max(s_frequencies)))
y = np.divide((lambda_ ** x) * np.exp(-lambda_), scipy.misc.factorial(x)) * len(s_frequencies)
x = x[y > 0]
y = y[y > 0]
plt.plot(x + 0.5, y, '--', label='Poisson fit')
if sys.argv[3] == 'normal':
mu = np.mean(s_frequencies)
sigma = np.std(s_frequencies)
x = np.array(range(np.min(s_frequencies), np.max(s_frequencies))) + 0.5
y = scipy.stats.norm.pdf(x, mu, sigma) * len(s_frequencies)
plt.plot(x, y, '--', label='Gaussian fit')
print('mean(S) = ', mu)
print('std(S) = ', sigma)
plt.legend()
plt.tight_layout()
f = plt.gcf()
f.set_size_inches(8, 4)
plt.savefig(sys.argv[1] + '.png')
package Sim;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Generator_CBR extends Node {
protected double _time = 0;
protected int _number_of_packages_per_second;
protected double time_limit = 0.0;
public Generator_CBR (int network, int node) {
super(node, node);
_id = new NetworkAddr(network, node);
}
// A function to help print timestamps to a file
private void log_time(String time, String file_name)
throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(file_name,true));
writer.write(time + "\n");
writer.close();
}
// Modified to only take a time limit and the ammount of packages per second.
public void StartSending(int network, int node, int number_of_packages_per_second, int time_limit)
{
this.time_limit = time_limit;
_number_of_packages_per_second = number_of_packages_per_second;
_toNetwork = network;
_toHost = node;
_seq = 1;
send(this, new TimerEvent(), 0);
System.out.println("Sending signal to start sending...");
}
// Override: Modified to send x number of package per second.
public void recv(SimEnt src, Event ev)
{
if (ev instanceof TimerEvent)
{
if (SimEngine.getTime() < time_limit){
double temp_time = 1.0/_number_of_packages_per_second;
int pkg_nr = 1; //Prints the number of any package in the loop bellow.
_time = 0;
for (int y = 0; y < _number_of_packages_per_second; y++) {
try{
log_time(Double.toString((SimEngine.getTime() + _time)), "CBR_Generator_Sending");
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
System.out.println("Time of sending package " + pkg_nr + " is: " + (SimEngine.getTime() + _time) + "\n");
_sentmsg++;
send(_peer, new Message(_id, new NetworkAddr(_toNetwork, _toHost),_seq), _time);
System.out.println("Node "+_id.networkId()+ "." + _id.nodeId() +" sent message with seq: "+_seq + " at time "+(SimEngine.getTime() + _time));
_seq++;
pkg_nr++;
_time += temp_time;
}
send(this, new TimerEvent(),1);
}
}
if (ev instanceof Message)
{
System.out.println("Node "+_id.networkId()+ "." + _id.nodeId() +" receives message with seq: "+((Message) ev).seq() + " at time "+SimEngine.getTime());
}
}
}
package Sim;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import java.lang.Math;
public class Generator_NORMAL extends Node {
protected double _time = 0;
protected Random randInt = new Random();
protected int _std_deviation;
protected int _mean;
protected double time_limit = 0.0;
public Generator_NORMAL (int network, int node) {
super(node, node);
_id = new NetworkAddr(network, node);
}
// A function to help print timestamps to a file
private void log_time(String time, String file_name)
throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(file_name,true));
writer.write(time + "\n");
writer.close();
}
// Modified to only take a time limit, diviation and mean.
public void StartSendingNormal(int network, int node, int std_diviation, int mean_pkg, double time_limit)
{
_toNetwork = network;
_std_deviation = std_diviation;
_mean = mean_pkg;
_toHost = node;
_seq = 1;
this.time_limit = time_limit;
send(this, new TimerEvent(), 0);
System.out.println("Sending signal to start sending...");
}
// Override: Modified to send packages as a normal distribution with a random gaussian number.
public void recv(SimEnt src, Event ev)
{
if (ev instanceof TimerEvent)
{
if (SimEngine.getTime() < time_limit)
{
int packages = (int)Math.abs(randInt.nextGaussian() * _std_deviation + _mean);
_time = SimEngine.getTime();
for (int i = 0; i < packages; i++)
{
double zeroOne = Math.random();
double thisTime = SimEngine.getTime() + zeroOne;
try
{
log_time(Double.toString(thisTime), "NORMAL_Generator_Sending");
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
System.out.println("Time of sending package is: " + thisTime);
_sentmsg++;
send(_peer, new Message(_id, new NetworkAddr(_toNetwork, _toHost), _seq), zeroOne);
System.out.println("Node "+_id.networkId()+ "." + _id.nodeId() +" sent message with seq: "+_seq + " at time "+ thisTime);
_seq++;
}
send(this, new TimerEvent(),1);
}
}
if (ev instanceof Message)
{
System.out.println("Node "+_id.networkId()+ "." + _id.nodeId() +" receives message with seq: "+((Message) ev).seq() + " at time "+SimEngine.getTime());
}
}
}
package Sim;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import java.lang.Math;
public class Generator_POISSON extends Node {
protected double _time = 0;
protected int _std_deviation;
protected int _mean;
protected double time_limit = 0.0;
protected double lambda = 0.0;
public Generator_POISSON (int network, int node) {
super(node, node);
_id = new NetworkAddr(network, node);
}
// A function to help print timestamps to a file
private void log_time(String time, String file_name)
throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(file_name,true));
writer.write(time + "\n");
writer.close();
}
// A function that takes a lambda and returns a k, used in recv function.
private static int getPoissonRandom(double mean) {
Random r = new Random();
double L = Math.exp(-mean);
int k = 0;
double p = 1.0;
do {
p = p * r.nextDouble();
k++;
} while (p > L);
return k - 1;
}
// Modified to only take a time limit and lambda.
public void StartSending(int network, int node, double time_limit, double lambda)
{
this.lambda = lambda;
_toNetwork = network;
_toHost = node;
_seq = 1;
this.time_limit = time_limit;
send(this, new TimerEvent(), 0);
System.out.println("Sending signal to start sending...");
}
// Override: Modified to send packages as a poisson distribution with calculated k value from function getPoissonRandom.
public void recv(SimEnt src, Event ev)
{
if (ev instanceof TimerEvent)
{
if (SimEngine.getTime() < time_limit)
{
int packages = getPoissonRandom(lambda);
_time = SimEngine.getTime();
for (int i = 0; i < packages; i++)
{
double zeroOne = Math.random();
double thisTime = SimEngine.getTime() + zeroOne;
try
{
log_time(Double.toString(thisTime), "POISSON_Generator_Sending");
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
System.out.println("Time of sending package is: " + thisTime);
_sentmsg++;
send(_peer, new Message(_id, new NetworkAddr(_toNetwork, _toHost), _seq), zeroOne);
System.out.println("Node "+_id.networkId()+ "." + _id.nodeId() +" sent message with seq: "+_seq + " at time "+ thisTime);
_seq++;
}
send(this, new TimerEvent(),1);
}
}
if (ev instanceof Message)
{
System.out.println("Node "+_id.networkId()+ "." + _id.nodeId() +" receives message with seq: "+((Message) ev).seq() + " at time "+SimEngine.getTime());
}
}
}
# Plots the packet frequency distribution
#
# Usage
#
# $ # cbr.txt is the traffic log file
# $ python plot.py cbr.txt
import math as m
import matplotlib.pyplot as plt
import numpy as np
import scipy
import seaborn as sns
import sys
# Style
sns.set()
timestamps = []
with open(sys.argv[1]) as f:
for line in f.readlines():
timestamps.append(float(line))
timestamps.sort()
# count the number of packets at each time interval
frequencies = []
i = 0
for t in range(0, m.ceil(timestamps[-1])):
frequency = 0
while True:
if i >= len(timestamps):
break
if timestamps[i] < t + 1:
i += 1
frequency += 1
else:
break
frequencies.append(frequency)
nbins = max(frequencies) - min(frequencies) + 1
plt.hist(frequencies, nbins)
plt.title('Generator')
plt.xlabel('Packets per second')
plt.ylabel('Occurrences')
if sys.argv[2] == 'poisson':
lambda_ = scipy.stats.mode(frequencies).mode[0]
x = np.array(range(np.min(frequencies), np.max(frequencies)))
y = np.divide((lambda_ ** x) * np.exp(-lambda_), scipy.misc.factorial(x)) * len(frequencies)
x = x[y > 0]
y = y[y > 0]
plt.plot(x + 0.5, y, '--', label='Poisson fit')
plt.legend()
if sys.argv[2] == 'normal':
mu = np.mean(frequencies)
sigma = np.std(frequencies)
x = np.array(range(np.min(frequencies), np.max(frequencies))) + 0.5
y = scipy.stats.norm.pdf(x, mu, sigma) * len(frequencies)
plt.plot(x, y, '--', label='Gaussian fit')
print('mean = ', mu)
print('std = ', sigma)
plt.tight_layout()
plt.savefig(sys.argv[1] + '.png')
package Sim;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Sink extends Node{
public Sink(int network, int node){
super(network, node);
}
private void log_time(String time, String file_name)
throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(file_name,true));
writer.write(time + "\n");
writer.close();
}
public void recv(SimEnt src, Event ev)
{
if (ev instanceof Message)
{
try {
log_time(Double.toString(SimEngine.getTime()), "Sink_Recieving");
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
System.out.println("SinkNode " + _id.networkId() + "." + _id.nodeId() + " receives message with seq: " + ((Message) ev).seq() + " at time " + SimEngine.getTime());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment