System Monitor - Chart: Average value of a time range

The System Monitor features nice graphs—for network traffic, for example. It displays values ​​from the last minute (the time interval is configurable). It would be great if a graph showing the average values ​​for that same time span could also be displayed. This could be enabled via a simple checkbox in the settings. It’s a great feature idea, and one that shouldn’t be too difficult to implement.
Just a thought. Thanks for your attention.

hi, welcome.

unless the value you want is provided by hardware/firmware such as with a senor like Average CPU Frequency, it would have to be up to the display style (line, bar, pie, etc).

sounds like what you are asking for is a trend line such as you can add to a chart in a spreadsheet.

that would not be a directly display of sensor data but rather a running average which would require significantly more calculation than simply plotting the values as they come in.

it could be done, but it would not be something i would want to just turn on for all my sensors without understanding the demand that would put onto the system.

Hello skyfishgoo.

Needed values are delivered by /sys/class/net/$INTERFACE/statistics/rx_bytes and /sys/class/net/$INTERFACE/statistics/rx_bytes

sounds like what you are asking for is a trend line such as you can add to a chart in a spreadsheet.

Here is the exact algorithm for 1 measurement:

INTERFACE=“eth0”
INTERVAL=60  # measurement interval in seconds

#First measurement

RX1=$(cat /sys/class/net/$INTERFACE/statistics/rx_bytes)
TX1=$(cat /sys/class/net/$INTERFACE/statistics/tx_bytes)

#Wait $INTERVAL

#Second measurement

RX2=$(cat /sys/class/net/$INTERFACE/statistics/rx_bytes)
TX2=$(cat /sys/class/net/$INTERFACE/statistics/tx_bytes)

#Calculate difference (bytes)

RX_DIFF=$((RX2 - RX1))
TX_DIFF=$((TX2 - TX1))

#Calculate average rate (bytes per second)

RX_RATE_BPS=$(echo “scale=2; $RX_DIFF / $INTERVAL” | bc)
TX_RATE_BPS=$(echo “scale=2; $TX_DIFF / $INTERVAL” | bc)

#Convert to Megabytes per second (MB/s)

RX_RATE_MBPS=$(echo “scale=2; $RX_RATE_BPS / 1024 / 1024” | bc)
TX_RATE_MBPS=$(echo “scale=2; $TX_RATE_BPS / 1024 / 1024” | bc)

#Output

echo “Average download rate over last $INTERVAL seconds: $RX_RATE_MBPS MB/s”
echo “Average upload rate over last $INTERVAL seconds: $TX_RATE_MBPS MB/s”

This algorithm is executed every time the chart is updated.
If the chart is updated once per second and the displayed time span covers one minute, 60 measurements run in parallel; a corresponding number of variables is therefore required.
The currently calculated values ​​for $RX_RATE_MBPS and $TX_RATE_MBPS are displayed in the chart.

Over many days and nights of toil, I have written this wonderful c++ code:

// It measures the average upload and download rates over the last 60 seconds in KB/s.
// It outputs these values ​​every second to the console and to a log file.
// The log file contains only the numerical values ​​(upload and download in KB/s), separated by a space.

#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <deque>
#include <numeric>
#include <ctime>
#include <iomanip>
#include <sstream>

using namespace std;
using namespace std::chrono;

// Function to read bytes from the specified file
long long get_bytes(const string& file_path) {
    ifstream file(file_path);
    if (!file.is_open()) {
        cerr << "Error: Could not open file " << file_path << "." << endl;
        return -1;
    }
    long long bytes;
    file >> bytes;
    file.close();
    return bytes;
}

// Function to generate a timestamp for the log file
string get_timestamp() {
    auto now = system_clock::now();
    time_t now_time = system_clock::to_time_t(now);
    stringstream ss;
    ss << put_time(localtime(&now_time), "%Y-%m-%d_%H-%M-%S");
    return ss.str();
}

int main() {
    const string tx_file = "/sys/class/net/eth0/statistics/tx_bytes";
    const string rx_file = "/sys/class/net/eth0/statistics/rx_bytes";
    const string output_file = "network_speed_" + get_timestamp() + ".log";

    // Open file for writing
    ofstream log_file(output_file);
    if (!log_file.is_open()) {
        cerr << "Error: Could not open file " << output_file << " for writing." << endl;
        return 1;
    }

    // Deque to store the last 60 measurements (bytes per second)
    deque<long long> tx_bytes_history;
    deque<long long> rx_bytes_history;

    cout << "Network Speed Monitor (Average Upload/Download Rate over the last 60 seconds in KB/s)" << endl;
    cout << "Data is also being written to the file: '" << output_file << "'." << endl;
    cout << "Measurement started... (Press Ctrl+C to stop)" << endl;

    // Initial measurement (bytes at start)
    long long tx_bytes_start = get_bytes(tx_file);
    long long rx_bytes_start = get_bytes(rx_file);

    if (tx_bytes_start == -1 || rx_bytes_start == -1) {
        cerr << "Aborting due to error reading data." << endl;
        log_file.close();
        return 1;
    }

    while (true) {
        // Wait for 1 second
        this_thread::sleep_for(seconds(1));

        // Read current bytes
        long long tx_bytes_current = get_bytes(tx_file);
        long long rx_bytes_current = get_bytes(rx_file);

        if (tx_bytes_current == -1 || rx_bytes_current == -1) {
            cerr << "Aborting due to error reading data." << endl;
            log_file.close();
            return 1;
        }

        // Calculate difference from the previous measurement (bytes per second)
        long long tx_diff = tx_bytes_current - tx_bytes_start;
        long long rx_diff = rx_bytes_current - rx_bytes_start;

        // Add bytes to history (per second)
        tx_bytes_history.push_back(tx_diff);
        rx_bytes_history.push_back(rx_diff);

        // Keep only the last 60 measurements
        if (tx_bytes_history.size() > 60) {
            tx_bytes_history.pop_front();
            rx_bytes_history.pop_front();
        }

        // Calculate average rate over the last 60 seconds (in bytes per second)
        long long tx_avg_bytes = 0;
        long long rx_avg_bytes = 0;
        if (!tx_bytes_history.empty()) {
            tx_avg_bytes = accumulate(tx_bytes_history.begin(), tx_bytes_history.end(), 0LL) / tx_bytes_history.size();
            rx_avg_bytes = accumulate(rx_bytes_history.begin(), rx_bytes_history.end(), 0LL) / rx_bytes_history.size();
        }

        // Convert bytes/s to KB/s (without decimal places)
        long long tx_avg_kb = tx_avg_bytes / 1024;
        long long rx_avg_kb = rx_avg_bytes / 1024;

        // Output to console
        cout << "Upload: " << tx_avg_kb << " KB/s | Download: " << rx_avg_kb << " KB/s" << endl;

        // Output to log file (only the numbers, separated by a space)
        log_file << tx_avg_kb << " " << rx_avg_kb << endl;

        // Update start measurement for the next second
        tx_bytes_start = tx_bytes_current;
        rx_bytes_start = rx_bytes_current;
    }

    log_file.close();
    return 0;
}

This feature will grant the System Monitor the global dominance it rightfully deserves.

Thank you very much for your attention and admiration.