Adjusted frame statistics for the use with two devices. redundancy
authorFlorian Pose <fp@igh-essen.com>
Wed, 09 Nov 2011 14:53:33 +0100
branchredundancy
changeset 2158 69f2b2702336
parent 2157 8fa0571f9996
child 2159 72ac85ee3729
Adjusted frame statistics for the use with two devices.
master/cdev.c
master/device.c
master/device.h
master/ioctl.h
master/master.c
master/master.h
tool/CommandMaster.cpp
--- a/master/cdev.c	Wed Nov 09 12:58:09 2011 +0100
+++ b/master/cdev.c	Wed Nov 09 14:53:33 2011 +0100
@@ -213,13 +213,17 @@
     data.devices[0].tx_count = master->main_device.tx_count;
     data.devices[0].rx_count = master->main_device.rx_count;
     data.devices[0].tx_bytes = master->main_device.tx_bytes;
+    data.devices[0].rx_bytes = master->main_device.rx_bytes;
     data.devices[0].tx_errors = master->main_device.tx_errors;
     for (i = 0; i < EC_RATE_COUNT; i++) {
         data.devices[0].tx_frame_rates[i] =
             master->main_device.tx_frame_rates[i];
+        data.devices[0].rx_frame_rates[i] =
+            master->main_device.rx_frame_rates[i];
         data.devices[0].tx_byte_rates[i] =
             master->main_device.tx_byte_rates[i];
-        data.devices[0].loss_rates[i] = master->main_device.loss_rates[i];
+        data.devices[0].rx_byte_rates[i] =
+            master->main_device.rx_byte_rates[i];
     }
 
     if (master->backup_device.dev) {
@@ -233,13 +237,34 @@
     data.devices[1].tx_count = master->backup_device.tx_count;
     data.devices[1].rx_count = master->backup_device.rx_count;
     data.devices[1].tx_bytes = master->backup_device.tx_bytes;
+    data.devices[1].rx_bytes = master->backup_device.rx_bytes;
     data.devices[1].tx_errors = master->backup_device.tx_errors;
     for (i = 0; i < EC_RATE_COUNT; i++) {
         data.devices[1].tx_frame_rates[i] =
             master->backup_device.tx_frame_rates[i];
+        data.devices[1].rx_frame_rates[i] =
+            master->backup_device.rx_frame_rates[i];
         data.devices[1].tx_byte_rates[i] =
             master->backup_device.tx_byte_rates[i];
-        data.devices[1].loss_rates[i] = master->backup_device.loss_rates[i];
+        data.devices[1].rx_byte_rates[i] =
+            master->backup_device.rx_byte_rates[i];
+    }
+
+    data.tx_count = master->device_stats.tx_count;
+    data.rx_count = master->device_stats.rx_count;
+    data.tx_bytes = master->device_stats.tx_bytes;
+    data.rx_bytes = master->device_stats.rx_bytes;
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        data.tx_frame_rates[i] =
+            master->device_stats.tx_frame_rates[i];
+        data.rx_frame_rates[i] =
+            master->device_stats.rx_frame_rates[i];
+        data.tx_byte_rates[i] =
+            master->device_stats.tx_byte_rates[i];
+        data.rx_byte_rates[i] =
+            master->device_stats.rx_byte_rates[i];
+        data.loss_rates[i] =
+            master->device_stats.loss_rates[i];
     }
 
     up(&master->device_sem);
--- a/master/device.c	Wed Nov 09 12:58:09 2011 +0100
+++ b/master/device.c	Wed Nov 09 14:53:33 2011 +0100
@@ -54,12 +54,6 @@
     } while (0)
 #endif
 
-/** List of intervals for frame statistics [s].
- */
-static const unsigned int rate_intervals[] = {
-    1, 10, 60
-};
-
 /*****************************************************************************/
 
 /** Constructor.
@@ -309,30 +303,6 @@
 {
     struct sk_buff *skb = device->tx_skb[device->tx_ring_index];
 
-    // frame statistics
-    if (unlikely(jiffies - device->stats_jiffies >= HZ)) {
-        unsigned int i;
-        u32 tx_frame_rate =
-            (u32) (device->tx_count - device->last_tx_count) * 1000;
-        u32 tx_byte_rate =
-            (device->tx_bytes - device->last_tx_bytes);
-        u64 loss = device->tx_count - device->rx_count;
-        s32 loss_rate = (s32) (loss - device->last_loss) * 1000;
-        for (i = 0; i < EC_RATE_COUNT; i++) {
-            unsigned int n = rate_intervals[i];
-            device->tx_frame_rates[i] =
-                (device->tx_frame_rates[i] * (n - 1) + tx_frame_rate) / n;
-            device->tx_byte_rates[i] =
-                (device->tx_byte_rates[i] * (n - 1) + tx_byte_rate) / n;
-            device->loss_rates[i] =
-                (device->loss_rates[i] * (n - 1) + loss_rate) / n;
-        }
-        device->last_tx_count = device->tx_count;
-        device->last_tx_bytes = device->tx_bytes;
-        device->last_loss = loss;
-        device->stats_jiffies = jiffies;
-    }
-
     // set the right length for the data
     skb->len = ETH_HLEN + size;
 
@@ -350,7 +320,9 @@
 #endif
     {
         device->tx_count++;
+        device->master->device_stats.tx_count++;
         device->tx_bytes += ETH_HLEN + size;
+        device->master->device_stats.tx_bytes += ETH_HLEN + size;
 #ifdef EC_DEBUG_IF
         ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size);
 #endif
@@ -375,16 +347,20 @@
 
     // zero frame statistics
     device->tx_count = 0;
+    device->last_tx_count = 0;
     device->rx_count = 0;
+    device->last_rx_count = 0;
+    device->tx_bytes = 0;
+    device->last_tx_bytes = 0;
+    device->rx_bytes = 0;
+    device->last_rx_bytes = 0;
     device->tx_errors = 0;
-    device->tx_bytes = 0;
-    device->last_tx_count = 0;
-    device->last_tx_bytes = 0;
-    device->last_loss = 0;
+
     for (i = 0; i < EC_RATE_COUNT; i++) {
         device->tx_frame_rates[i] = 0;
+        device->rx_frame_rates[i] = 0;
         device->tx_byte_rates[i] = 0;
-        device->loss_rates[i] = 0;
+        device->rx_byte_rates[i] = 0;
     }
 }
 
@@ -479,6 +455,43 @@
     device->poll(device->dev);
 }
 
+/*****************************************************************************/
+
+/** Update device statistics.
+ */
+void ec_device_update_stats(
+        ec_device_t *device /**< EtherCAT device */
+        )
+{
+    unsigned int i;
+
+    u32 tx_frame_rate =
+        (u32) (device->tx_count - device->last_tx_count) * 1000;
+    u32 rx_frame_rate =
+        (u32) (device->rx_count - device->last_rx_count) * 1000;
+    u32 tx_byte_rate =
+        (device->tx_bytes - device->last_tx_bytes);
+    u32 rx_byte_rate =
+        (device->rx_bytes - device->last_rx_bytes);
+
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        unsigned int n = rate_intervals[i];
+        device->tx_frame_rates[i] =
+            (device->tx_frame_rates[i] * (n - 1) + tx_frame_rate) / n;
+        device->rx_frame_rates[i] =
+            (device->rx_frame_rates[i] * (n - 1) + rx_frame_rate) / n;
+        device->tx_byte_rates[i] =
+            (device->tx_byte_rates[i] * (n - 1) + tx_byte_rate) / n;
+        device->rx_byte_rates[i] =
+            (device->rx_byte_rates[i] * (n - 1) + rx_byte_rate) / n;
+    }
+
+    device->last_tx_count = device->tx_count;
+    device->last_rx_count = device->rx_count;
+    device->last_tx_bytes = device->tx_bytes;
+    device->last_rx_bytes = device->rx_bytes;
+}
+
 /******************************************************************************
  *  Device interface
  *****************************************************************************/
@@ -593,6 +606,9 @@
     }
 
     device->rx_count++;
+    device->master->device_stats.rx_count++;
+    device->rx_bytes += size;
+    device->master->device_stats.rx_bytes += size;
 
     if (unlikely(device->master->debug_level > 1)) {
         EC_MASTER_DBG(device->master, 2, "Received frame:\n");
--- a/master/device.h	Wed Nov 09 12:58:09 2011 +0100
+++ b/master/device.h	Wed Nov 09 14:53:33 2011 +0100
@@ -101,19 +101,26 @@
     u64 tx_count; /**< Number of frames sent. */
     u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
     u64 rx_count; /**< Number of frames received. */
-    u64 tx_bytes; /**< Number of frames sent. */
+    u64 last_rx_count; /**< Number of frames received of last statistics
+                         cycle. */
+    u64 tx_bytes; /**< Number of bytes sent. */
     u64 last_tx_bytes; /**< Number of bytes sent of last statistics cycle. */
+    u64 rx_bytes; /**< Number of bytes received. */
+    u64 last_rx_bytes; /**< Number of bytes received of last statistics cycle.
+                        */
     u64 tx_errors; /**< Number of transmit errors. */
-    u64 last_loss; /**< Tx/Rx difference of last statistics cycle. */
     unsigned int tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in
                                                   frames/s for different
                                                   statistics cycle periods. */
+    unsigned int rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in
+                                                  frames/s for different
+                                                  statistics cycle periods. */
     unsigned int tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s
                                                  for different statistics
                                                  cycle periods. */
-    int loss_rates[EC_RATE_COUNT]; /**< Frame loss rates for different
-                                     statistics cycle periods. */
-    unsigned long stats_jiffies; /**< Jiffies of last statistic cycle. */
+    unsigned int rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s
+                                                 for different statistics
+                                                 cycle periods. */
 
 #ifdef EC_DEBUG_IF
     ec_debug_t dbg; /**< debug device */
@@ -141,6 +148,7 @@
 uint8_t *ec_device_tx_data(ec_device_t *);
 void ec_device_send(ec_device_t *, size_t);
 void ec_device_clear_stats(ec_device_t *);
+void ec_device_update_stats(ec_device_t *);
 
 #ifdef EC_DEBUG_RING
 void ec_device_debug_ring_append(ec_device_t *, ec_debug_frame_dir_t,
--- a/master/ioctl.h	Wed Nov 09 12:58:09 2011 +0100
+++ b/master/ioctl.h	Wed Nov 09 14:53:33 2011 +0100
@@ -56,7 +56,7 @@
  *
  * Increment this when changing the ioctl interface!
  */
-#define EC_IOCTL_VERSION_MAGIC 12
+#define EC_IOCTL_VERSION_MAGIC 14
 
 // Command-line tool
 #define EC_IOCTL_MODULE                EC_IOR(0x00, ec_ioctl_module_t)
@@ -168,11 +168,22 @@
         uint64_t tx_count;
         uint64_t rx_count;
         uint64_t tx_bytes;
+        uint64_t rx_bytes;
         uint64_t tx_errors;
         uint32_t tx_frame_rates[EC_RATE_COUNT];
+        uint32_t rx_frame_rates[EC_RATE_COUNT];
         uint32_t tx_byte_rates[EC_RATE_COUNT];
-        int32_t loss_rates[EC_RATE_COUNT];
+        uint32_t rx_byte_rates[EC_RATE_COUNT];
     } devices[2];
+    uint64_t tx_count;
+    uint64_t rx_count;
+    uint64_t tx_bytes;
+    uint64_t rx_bytes;
+    uint32_t tx_frame_rates[EC_RATE_COUNT];
+    uint32_t rx_frame_rates[EC_RATE_COUNT];
+    uint32_t tx_byte_rates[EC_RATE_COUNT];
+    uint32_t rx_byte_rates[EC_RATE_COUNT];
+    int32_t loss_rates[EC_RATE_COUNT];
     uint64_t app_time;
     uint16_t ref_clock;
 } ec_ioctl_master_t;
--- a/master/master.c	Wed Nov 09 12:58:09 2011 +0100
+++ b/master/master.c	Wed Nov 09 14:53:33 2011 +0100
@@ -82,6 +82,12 @@
 
 #endif
 
+/** List of intervals for statistics [s].
+ */
+const unsigned int rate_intervals[] = {
+    1, 10, 60
+};
+
 /*****************************************************************************/
 
 void ec_master_clear_slave_configs(ec_master_t *);
@@ -92,6 +98,8 @@
 static int ec_master_eoe_thread(void *);
 #endif
 void ec_master_find_dc_ref_clock(ec_master_t *);
+void ec_master_clear_device_stats(ec_master_t *);
+void ec_master_update_device_stats(ec_master_t *);
 
 /*****************************************************************************/
 
@@ -134,6 +142,7 @@
 
     master->main_mac = main_mac;
     master->backup_mac = backup_mac;
+    ec_master_clear_device_stats(master);
 
     sema_init(&master->device_sem, 1);
 
@@ -1148,6 +1157,84 @@
     }
 }
 
+/*****************************************************************************/
+
+/** Clears the common device statistics.
+ */
+void ec_master_clear_device_stats(
+        ec_master_t *master /**< EtherCAT master */
+        )
+{
+    unsigned int i;
+
+    // zero frame statistics
+    master->device_stats.tx_count = 0;
+    master->device_stats.last_tx_count = 0;
+    master->device_stats.rx_count = 0;
+    master->device_stats.last_rx_count = 0;
+    master->device_stats.tx_bytes = 0;
+    master->device_stats.last_tx_bytes = 0;
+    master->device_stats.rx_bytes = 0;
+    master->device_stats.last_rx_bytes = 0;
+    master->device_stats.last_loss = 0;
+
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        master->device_stats.tx_frame_rates[i] = 0;
+        master->device_stats.tx_byte_rates[i] = 0;
+        master->device_stats.loss_rates[i] = 0;
+    }
+}
+
+/*****************************************************************************/
+
+/** Updates the common device statistics.
+ */
+void ec_master_update_device_stats(
+        ec_master_t *master /**< EtherCAT master */
+        )
+{
+    ec_device_stats_t *s = &master->device_stats;
+    u32 tx_frame_rate, rx_frame_rate, tx_byte_rate, rx_byte_rate;
+    u64 loss;
+    s32 loss_rate;
+    unsigned int i;
+
+    // frame statistics
+    if (likely(jiffies - s->jiffies < HZ)) {
+        return;
+    }
+
+    tx_frame_rate = (u32) (s->tx_count - s->last_tx_count) * 1000;
+    rx_frame_rate = (u32) (s->rx_count - s->last_rx_count) * 1000;
+    tx_byte_rate = (s->tx_bytes - s->last_tx_bytes);
+    rx_byte_rate = (s->rx_bytes - s->last_rx_bytes);
+    loss = s->tx_count - s->rx_count;
+    loss_rate = (s32) (loss - s->last_loss) * 1000;
+
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        unsigned int n = rate_intervals[i];
+        s->tx_frame_rates[i] =
+            (s->tx_frame_rates[i] * (n - 1) + tx_frame_rate) / n;
+        s->rx_frame_rates[i] =
+            (s->rx_frame_rates[i] * (n - 1) + rx_frame_rate) / n;
+        s->tx_byte_rates[i] =
+            (s->tx_byte_rates[i] * (n - 1) + tx_byte_rate) / n;
+        s->rx_byte_rates[i] =
+            (s->rx_byte_rates[i] * (n - 1) + rx_byte_rate) / n;
+        s->loss_rates[i] =
+            (s->loss_rates[i] * (n - 1) + loss_rate) / n;
+
+    }
+    s->last_tx_count = s->tx_count;
+    s->last_rx_count = s->rx_count;
+    s->last_tx_bytes = s->tx_bytes;
+    s->last_rx_bytes = s->rx_bytes;
+
+    ec_device_update_stats(&master->main_device);
+    ec_device_update_stats(&master->backup_device);
+
+    s->jiffies = jiffies;
+}
 
 /*****************************************************************************/
 
@@ -2129,6 +2216,7 @@
     if (master->backup_device.dev) {
         ec_device_poll(&master->backup_device);
     }
+    ec_master_update_device_stats(master);
 
     // dequeue all datagrams that timed out
     list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
--- a/master/master.h	Wed Nov 09 12:58:09 2011 +0100
+++ b/master/master.h	Wed Nov 09 14:53:33 2011 +0100
@@ -137,6 +137,39 @@
 
 /*****************************************************************************/
 
+/** Device statistics.
+ */
+typedef struct {
+    u64 tx_count; /**< Number of frames sent. */
+    u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
+    u64 rx_count; /**< Number of frames received. */
+    u64 last_rx_count; /**< Number of frames received of last statistics
+                         cycle. */
+    u64 tx_bytes; /**< Number of bytes sent. */
+    u64 last_tx_bytes; /**< Number of bytes sent of last statistics cycle. */
+    u64 rx_bytes; /**< Number of bytes received. */
+    u64 last_rx_bytes; /**< Number of bytes received of last statistics cycle.
+                        */
+    u64 last_loss; /**< Tx/Rx difference of last statistics cycle. */
+    unsigned int tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in
+                                                  frames/s for different
+                                                  statistics cycle periods. */
+    unsigned int rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in
+                                                  frames/s for different
+                                                  statistics cycle periods. */
+    unsigned int tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s
+                                                 for different statistics
+                                                 cycle periods. */
+    unsigned int rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s
+                                                 for different statistics
+                                                 cycle periods. */
+    int loss_rates[EC_RATE_COUNT]; /**< Frame loss rates for different
+                                     statistics cycle periods. */
+    unsigned long jiffies; /**< Jiffies of last statistic cycle. */
+} ec_device_stats_t;
+
+/*****************************************************************************/
+
 /** EtherCAT master.
  *
  * Manages slaves, domains and IO.
@@ -159,6 +192,7 @@
     ec_device_t backup_device; /**< EtherCAT backup device. */
     const uint8_t *backup_mac; /**< MAC address of backup device. */
     struct semaphore device_sem; /**< Device semaphore. */
+    ec_device_stats_t device_stats; /**< Device statistics. */
 
     ec_fsm_master_t fsm; /**< Master state machine. */
     ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */
@@ -309,6 +343,8 @@
 void ec_master_internal_send_cb(void *);
 void ec_master_internal_receive_cb(void *);
 
-/*****************************************************************************/
-
-#endif
+extern const unsigned int rate_intervals[EC_RATE_COUNT]; // see master.c
+
+/*****************************************************************************/
+
+#endif
--- a/tool/CommandMaster.cpp	Wed Nov 09 12:58:09 2011 +0100
+++ b/tool/CommandMaster.cpp	Wed Nov 09 14:53:33 2011 +0100
@@ -114,12 +114,6 @@
                     && data.devices[i].address[5] == 0x00) {
                 cout << "None.";
             } else {
-                unsigned int lost =
-                    data.devices[i].tx_count - data.devices[i].rx_count;
-                if (lost == 1) {
-                    // allow one frame travelling
-                    lost = 0;
-                }
                 cout << hex << setfill('0')
                     << setw(2) << (unsigned int) data.devices[i].address[0]
                     << ":"
@@ -139,11 +133,12 @@
                     << (data.devices[i].link_state ? "UP" : "DOWN") << endl
                     << "      Tx frames:   "
                     << data.devices[i].tx_count << endl
+                    << "      Tx bytes:    "
+                    << data.devices[i].tx_bytes << endl
                     << "      Rx frames:   "
                     << data.devices[i].rx_count << endl
-                    << "      Lost frames: " << lost << endl
-                    << "      Tx bytes:    "
-                    << data.devices[i].tx_bytes << endl
+                    << "      Rx bytes:    "
+                    << data.devices[i].rx_bytes << endl
                     << "      Tx errors:   "
                     << data.devices[i].tx_errors << endl
                     << "      Tx frame rate [1/s]: "
@@ -166,33 +161,106 @@
                     }
                 }
                 cout << endl
-                    << "      Loss rate [1/s]:     "
-                    << setprecision(0) << fixed;
-                for (j = 0; j < EC_RATE_COUNT; j++) {
-                    cout << setw(ColWidth)
-                        << data.devices[i].loss_rates[j] / 1000.0;
+                    << "      Rx frame rate [1/s]: "
+                    << setfill(' ') << setprecision(0) << fixed;
+                for (j = 0; j < EC_RATE_COUNT; j++) {
+                    cout << setw(ColWidth)
+                        << data.devices[i].rx_frame_rates[j] / 1000.0;
                     if (j < EC_RATE_COUNT - 1) {
                         cout << " ";
                     }
                 }
                 cout << endl
-                    << "      Frame loss [%]:      "
+                    << "      Rx rate [KByte/s]:   "
                     << setprecision(1) << fixed;
                 for (j = 0; j < EC_RATE_COUNT; j++) {
-                    double perc = 0.0;
-                    if (data.devices[i].tx_frame_rates[j]) {
-                        perc = 100.0 * data.devices[i].loss_rates[j] /
-                            data.devices[i].tx_frame_rates[j];
-                    }
-                    cout << setw(ColWidth) << perc;
+                    cout << setw(ColWidth)
+                        << data.devices[i].rx_byte_rates[j] / 1024.0;
                     if (j < EC_RATE_COUNT - 1) {
                         cout << " ";
                     }
                 }
                 cout << setprecision(0) << endl;
             }
-            cout << endl;
-        }
+        }
+        unsigned int lost = data.tx_count - data.rx_count;
+        if (lost == 1) {
+            // allow one frame travelling
+            lost = 0;
+        }
+        cout << "    Common:" << endl
+            << "      Tx frames:   "
+            << data.tx_count << endl
+            << "      Tx bytes:    "
+            << data.tx_bytes << endl
+            << "      Rx frames:   "
+            << data.rx_count << endl
+            << "      Rx bytes:    "
+            << data.rx_bytes << endl
+            << "      Lost frames: " << lost << endl
+            << "      Tx frame rate [1/s]: "
+            << setfill(' ') << setprecision(0) << fixed;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            cout << setw(ColWidth)
+                << data.tx_frame_rates[j] / 1000.0;
+            if (j < EC_RATE_COUNT - 1) {
+                cout << " ";
+            }
+        }
+        cout << endl
+            << "      Tx rate [KByte/s]:   "
+            << setprecision(1) << fixed;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            cout << setw(ColWidth)
+                << data.tx_byte_rates[j] / 1024.0;
+            if (j < EC_RATE_COUNT - 1) {
+                cout << " ";
+            }
+        }
+        cout << endl
+            << "      Rx frame rate [1/s]: "
+            << setfill(' ') << setprecision(0) << fixed;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            cout << setw(ColWidth)
+                << data.rx_frame_rates[j] / 1000.0;
+            if (j < EC_RATE_COUNT - 1) {
+                cout << " ";
+            }
+        }
+        cout << endl
+            << "      Rx rate [KByte/s]:   "
+            << setprecision(1) << fixed;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            cout << setw(ColWidth)
+                << data.rx_byte_rates[j] / 1024.0;
+            if (j < EC_RATE_COUNT - 1) {
+                cout << " ";
+            }
+        }
+        cout << endl
+            << "      Loss rate [1/s]:     "
+            << setprecision(0) << fixed;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            cout << setw(ColWidth)
+                << data.loss_rates[j] / 1000.0;
+            if (j < EC_RATE_COUNT - 1) {
+                cout << " ";
+            }
+        }
+        cout << endl
+            << "      Frame loss [%]:      "
+            << setprecision(1) << fixed;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            double perc = 0.0;
+            if (data.tx_frame_rates[j]) {
+                perc = 100.0 * data.loss_rates[j] / data.tx_frame_rates[j];
+            }
+            cout << setw(ColWidth) << perc;
+            if (j < EC_RATE_COUNT - 1) {
+                cout << " ";
+            }
+        }
+        cout << setprecision(0) << endl;
 
         cout << "  Distributed clocks:" << endl
             << "    Reference clock: ";