Optimize Profinet with statistical analysis

In this article, we’ll walk through a Lua script that not only calculates the minimum, maximum, and average time deltas between Profinet packets but also computes additional statistics to provide a deeper understanding of packet timing variations and network performance.

Analyzing the time delta between Profinet packets can provide critical insights into the performance and stability of real-time communication systems. By using Lua scripting in Wireshark, you can automate this analysis, extract detailed timing information between packets, and calculate advanced statistical metrics to assess network behavior.

In this article, we’ll walk through a Lua script that not only calculates the minimum, maximum, and average time deltas between Profinet packets but also computes additional statistics, such as median, variance, standard deviation, and maximum/minimum deviation from the average. These statistics provide a deeper understanding of packet timing variations and network performance.

Table of Contents

Setting Up the Lua Script in Wireshark

Before we dive into the script itself, let’s first ensure that the Lua environment is set up correctly.

Create the Lua Script:

  1. Open a text editor (e.g., Notepad++, Sublime Text, or Visual Studio Code).
  2. Save the file with a .lua extension. For example, you can name it profinet_time_delta_analysis.lua.

Place the Lua Script in the Correct Directory:

Wireshark looks for Lua scripts in specific plugin folders. To find the correct folder:

  • Open Wireshark.
  • Go to Help > About Wireshark > Folders.
  • Locate the “Personal Lua Plugins” folder.
  • Move the Lua script (profinet_time_delta_analysis.lua) into this folder.

Restart Wireshark:

After placing the Lua script in the correct folder, restart Wireshark so it can load the script. Once the script is placed in the correct folder and Wireshark is restarted, it will automatically load the Lua file, and the analysis will run when you capture or open a Profinet .pcap file. You can also reload the Lua scripts from within Wireshark by pressing Ctrl+Shift+L.

Wireshark Lua meny
You access the Lua console (scripts output window) from the Tools meny in Wireshark.

Code Walkthrough: Function by Function

1. Listener Initialization

We start by creating a listener that captures all Profinet traffic from a specific device.

				
					local profinet_listener = Listener.new("eth", "eth.src == b8:27:eb:f3:9a:b2 and pn_io")
				
			

Explanation:

  • Listener.new() creates a listener to capture Ethernet traffic.
  • The filter eth.src == b8:27:eb:f3:9a:b2 ensures that the script captures traffic only from the specified Profinet device, identified by its MAC address. Replace the sample MAC address b8:27:eb:f3:9a:b2 in the Lua code with the MAC address of your device.
  • The pn_io filter is used to ensure that only Profinet packets are processed.

2. Field Extraction

Next, we define the field frame.time_delta_displayed, which extracts the time delta between two consecutive packets.

				
					local time_delta_displayed = Field.new("frame.time_delta_displayed")
				
			

Explanation:

  • Field.new() defines a field from the packet capture that we want to extract.
  • frame.time_delta_displayed provides the time difference between the current packet and the previous packet, which is essential for calculating packet timing intervals.

3. Packet Processing

This function handles each packet captured by the listener, skips the first packet (since its time delta is always zero), and updates various statistics such as minimum, maximum, and average time deltas.

				
					local function packet_handler(pinfo, tvb)
    local time_delta = time_delta_displayed()

    if time_delta then
        -- Skip the first packet because its time delta is always 0
        if is_first_packet then
            is_first_packet = false
            return
        end

        -- Convert time delta to a number
        local delta = tonumber(tostring(time_delta))
        
        -- Update statistics
        total_time_delta = total_time_delta + delta
        packet_count = packet_count + 1

        -- Store the time delta for further analysis
        table.insert(time_deltas, delta)

        -- Set min and max time deltas after the first packet
        if not min_time_delta or delta < min_time_delta then
            min_time_delta = delta
        end
        if not max_time_delta or delta > max_time_delta then
            max_time_delta = delta
        end
    end
end

				
			

Explanation:

  • This function is invoked for every packet that matches the listener’s filter.
  • The script skips the first packet because it has no previous packet to compare, resulting in a zero time delta.
  • The extracted time delta is added to the time_deltas table for further analysis, and the running totals for minimum, maximum, and total time deltas are updated.

4. Additional Statistical Calculations

We include helper functions to calculate additional statistics: the median, variance, and standard deviation.

				
					local function calculate_median(values)
    table.sort(values)
    local len = #values
    if len % 2 == 1 then
        return values[(len + 1) // 2]
    else
        return (values[len // 2] + values[len // 2 + 1]) / 2
    end
end

local function calculate_variance_and_stddev(values, mean)
    local sum_sq_diff = 0
    for _, v in ipairs(values) do
        sum_sq_diff = sum_sq_diff + (v - mean) ^ 2
    end
    local variance = sum_sq_diff / #values
    local stddev = math.sqrt(variance)
    return variance, stddev
end
				
			

Explanation:

  • The calculate_median() function sorts the time deltas and returns the middle value (or the average of the two middle values if the number of values is even).
  • The calculate_variance_and_stddev() function calculates how far each time delta is from the average and computes the variance and standard deviation. Standard deviation gives a clearer view of how much time deltas vary from the average.

5. Initialization

The reset() function reinitializes the script’s variables whenever a new capture session starts or Wireshark is reset.

				
					function profinet_listener.reset()
    total_time_delta = 0.0
    time_deltas = {}
    min_time_delta = nil
    max_time_delta = nil
    packet_count = 0
    is_first_packet = true
    print("Profinet analysis started")
end
				
			

Explanation:

  • This function resets all statistics and variables at the start of a new capture.

6. Final Statistics Calculation

Once all packets have been processed, Wireshark calls the draw() function, which calculates and prints the statistics.

				
					function profinet_listener.draw()
    if packet_count > 0 then
        -- Calculate average time delta
        local avg_time_delta = total_time_delta / packet_count

        -- Calculate median time delta
        local median_time_delta = calculate_median(time_deltas)

        -- Calculate variance and standard deviation
        local variance, stddev = calculate_variance_and_stddev(time_deltas, avg_time_delta)

        -- Calculate maximum and minimum deviations from average
        local max_deviation = nil
        local min_deviation = nil
        for _, delta in ipairs(time_deltas) do
            local deviation = math.abs(delta - avg_time_delta)
            if not max_deviation or deviation > max_deviation then
                max_deviation = deviation
            end
            if not min_deviation or deviation < min_deviation then
                min_deviation = deviation
            end
        end

        -- Print out the statistics
        print("Profinet Time Delta Analysis Completed.")
        print("Total Packets: " .. packet_count)
        print("Minimum Time Delta: " .. min_time_delta .. " seconds")
        print("Maximum Time Delta: " .. max_time_delta .. " seconds")
        print("Average Time Delta: " .. avg_time_delta .. " seconds")
        print("Median Time Delta: " .. median_time_delta .. " seconds")
        print("Variance: " .. variance)
        print("Standard Deviation: " .. stddev)
        print("Maximum Deviation from Average: " .. max_deviation .. " seconds")
        print("Minimum Deviation from Average: " .. min_deviation .. " seconds")
    else
        print("No packets processed, unable to calculate time delta statistics.")
    end
end
				
			

Explanation:

This function calculates and prints the key statistics, including:

  • Median: The middle value in the sorted time deltas.
  • Variance and Standard Deviation: Measures of how much time deltas deviate from the average.
  • Maximum and Minimum Deviations from the Average: Shows the largest and smallest deviations from the average time delta.

Complete Lua Script

Below is the full Lua script that provides an extended analysis of the time deltas between Profinet packets:

				
					-- Listener to capture all Profinet traffic from the specified device
local profinet_listener = Listener.new("eth", "eth.src == b8:27:eb:f3:9a:b2 and pn_io")

-- Define the field to extract time delta between packets
local time_delta_displayed = Field.new("frame.time_delta_displayed")

-- Variables to store statistics
local total_time_delta = 0.0
local time_deltas = {}  -- Store each time delta for further statistical analysis
local min_time_delta = nil
local max_time_delta = nil
local packet_count = 0
local is_first_packet = true  -- Skip the first packet

-- Packet handler function
local function packet_handler(pinfo, tvb)
    local time_delta = time_delta_displayed()
    if time_delta then
        -- Skip the first packet
        if is_first_packet then
            is_first_packet = false
            return
        end

        local delta = tonumber(tostring(time_delta))
        total_time_delta = total_time_delta + delta
        packet_count = packet_count + 1
        table.insert(time_deltas, delta)

        if not min_time_delta or delta < min_time_delta then
            min_time_delta = delta
        end
        if not max_time_delta or delta > max_time_delta then
            max_time_delta = delta
        end
    end
end

-- Helper function to calculate the median
local function calculate_median(values)
    table.sort(values)
    local len = #values
    if len % 2 == 1 then
        return values[(len + 1) // 2]
    else
        return (values[len // 2] + values[len // 2 + 1]) / 2
    end
end

-- Helper function to calculate variance and standard deviation
local function calculate_variance_and_stddev(values, mean)
    local sum_sq_diff = 0
    for _, v in ipairs(values) do
        sum_sq_diff = sum_sq_diff + (v - mean) ^ 2
    end
    local variance = sum_sq_diff / #values
    return variance, math.sqrt(variance)
end

-- Reset function
function profinet_listener.reset()
    total_time_delta = 0.0
    time_deltas = {}
    min_time_delta = nil
    max_time_delta = nil
    packet_count = 0
    is_first_packet = true
    print("Profinet analysis started")
end

-- Draw function to calculate and print statistics
function profinet_listener.draw()
    if packet_count > 0 then
        local avg_time_delta = total_time_delta / packet_count
        local median_time_delta = calculate_median(time_deltas)
        local variance, stddev = calculate_variance_and_stddev(time_deltas, avg_time_delta)

        local max_deviation = nil
        local min_deviation = nil
        for _, delta in ipairs(time_deltas) do
            local deviation = math.abs(delta - avg_time_delta)
            if not max_deviation or deviation > max_deviation then
                max_deviation = deviation
            end
            if not min_deviation or deviation < min_deviation then
                min_deviation = deviation
            end
        end

        print("Profinet Time Delta Analysis Completed.")
        print("Total Packets: " .. packet_count)
        print("Minimum Time Delta: " .. min_time_delta .. " seconds")
        print("Maximum Time Delta: " .. max_time_delta .. " seconds")
        print("Average Time Delta: " .. avg_time_delta .. " seconds")
        print("Median Time Delta: " .. median_time_delta .. " seconds")
        print("Variance: " .. variance)
        print("Standard Deviation: " .. stddev)
        print("Maximum Deviation from Average: " .. max_deviation .. " seconds")
        print("Minimum Deviation from Average: " .. min_deviation .. " seconds")
    else
        print("No packets processed, unable to calculate time delta statistics.")
    end
end
				
			

Interpreting the Statistics

Here’s a brief explanation of the key metrics you will see when you run the script:

  • Minimum Time Delta: The smallest time difference between two consecutive packets, indicating the fastest packet delivery time.
  • Maximum Time Delta: The largest time difference, indicating the slowest packet delivery or largest delay.
  • Average Time Delta: The mean time difference between packets, showing the general packet rate.
  • Median Time Delta: The middle value of time deltas, showing where most of the packet timing is centered.
  • Variance and Standard Deviation: These give insights into how much the packet intervals vary from the average. Low variance and standard deviation indicate more consistent packet timing.
  • Maximum and Minimum Deviation from Average: These values show how much some packets deviate from the average packet timing.
Lua console window showing the output from the script.

Conclusion

This Lua script offers a detailed statistical analysis of Profinet traffic in Wireshark, going beyond simple time delta calculations to provide deeper insights into packet timing. The combination of variance, standard deviation, median, and deviations from the average allows you to diagnose any potential timing issues, ensuring the network performs reliably.

This script is flexible and can be extended further to track additional Profinet-specific metrics, making it a powerful tool for real-time traffic analysis.

Further reading

To deepen your understanding of Profinet and how to optimize its performance using statistical analysis, we recommend exploring the extensive technical documentation available for P-Net. P-Net is a full fledged Profinet stack designed for embedded systems, offering resources for developers looking to implement or enhance Profinet functionality in their projects.

  • P-Net Technical Documentation: Dive into detailed explanations of the Profinet protocol, implementation guides, and best practices for network optimization.

  • P-Net GitHub Repository: Access the source code, contribute to the project, and engage with a community of developers working with Profinet.

By exploring these resources, you can gain a deeper insight into Profinet networking, learn how to implement custom solutions, and further optimize your network’s performance using advanced techniques.