Source: measure.js

/**
 * Module which implements the class Measurement which includes the four
 * parameters that are measured in Q4S.
 * @module measure
 */

/** Measurement Class*/
class Measure {
  /**
   * Constructor for the Measurement.
   * @param {Number} latency - Latency in ms
   * @param {Number} jitter - Jitter in ms
   * @param {Number} bandwidth - bandwidth in ms
   * @param {Number} packetLoss - packetLoss

   */
  constructor(latency, jitter, bandwidth, packetLoss) {
    /**
     * Latency in ms
     * @member {Number}
     */
    this.latency = latency;
    /**
     * Jitter in ms
     * @member {Number}
     */
    this.jitter = jitter;
    /**
     * Bandwidth in kbps kilo bits per second
     * @member {Number}
     */
    this.bandwidth = bandwidth;
    /**
     * Packet Loss in probability 0 to 1
     * @member {Number}
     */
    this.packetLoss = packetLoss;
  }

  /**
   * Extract the text view of this object to be included in headers.
   * @return {String} The header representation of tose measurements.
   */
  toHeader() {
    let header = 'l=';
    if (typeof this.latency !== 'undefined') {
      header = header + this.latency;
    }
    header = header + ', j=';
    if (typeof this.jitter !== 'undefined') {
      header = header + this.jitter;
    }
    header = header + ', pl=';
    if (typeof this.packetLoss !== 'undefined') {
      header = header + this.packetLoss;
    }
    header = header + ', bw=';
    if (typeof this.bandwidth !== 'undefined') {
      header = header + this.bandwidth;
    }
    return header;
  }

  /**
   * Fill this object with contents from a text header.
   * @param {String} hdr - Header line to extract the text from.
   */
  fromHeader(hdr) {
    const latencyReg = /^l=\d+(\.\d+)?/;
    const jitterReg = /^j=\d+(\.\d+)?/;
    const packetLossReg = /^pl=[0-1]+(\.\d+)?/;
    const bandwidthReg = /^bw=\d+(\.\d+)?/;

    const parts = hdr.split(', ');

    parts.forEach((element) => {
      if (latencyReg.test(element)) {
        this.latency = parseFloat(element.substring(2));
      } else if (jitterReg.test(element)) {
        this.jitter = parseFloat(element.substring(2));
      } else if (packetLossReg.test(element)) {
        this.packetLoss = parseFloat(element.substring(3));
      } else if (bandwidthReg.test(element)) {
        this.bandwidth = parseFloat(element.substring(3));
      }
    });
  }

  /**
   * Fill the jitter attribute with an Array of dates. Those dates are the
   * arrival dates of packets. The array is expected to be filled with zeroes
   * in the empty spaces thata re not used. The lenght considered is up to the
   * last not zero value.
   * @param {Object[]} reqTime
   * @param {Number} reqTime.seq
   * @param {Date} reqTime.time
   */
  extractJitter(reqTime) {
    reqTime.sort((a, b) => {
      return a.seq - b.seq;
    });
    let acum = 0;
    let size = 0;
    let diff = 0;
    for (let i = 1; i < reqTime.length; i++) {
      if (reqTime[i].seq == reqTime[i - 1].seq + 1) {
        acum = acum + reqTime[i].time.getTime() - reqTime[i - 1].time.getTime();
        size++;
      }
    }
    const averageTime = acum / size;

    acum = 0;
    for (let i = 1; i < reqTime.length; i++) {
      if (reqTime[i].seq == reqTime[i - 1].seq + 1) {
        diff = reqTime[i].time.getTime() - reqTime[i - 1].time.getTime();
        acum = acum + Math.abs(diff - averageTime);
      }
    }
    if (size !== 0) {
      this.jitter = acum / size;
    }
  }

  /**
   * Fill the packet loss and latency attribute with an Array of dates. Those
   * dates are thedeparture of the packet and the time of ariva of the
   * corresponding response. The array is expected to be filled with zeroes
   * in the empty spaces thata re not used. The lenght considered is up to the
   * last not zero value.
   * @param {Object[]} departureTime
   * @param {Number} departureTime.seq
   * @param {Date} departureTime.time
   * @param {Object[]} arrivalTime
   * @param {Number} arrivalTime.seq
   * @param {Date} arrivalTime.time
   */
  extractLatency(departureTime, arrivalTime) {
    arrivalTime.sort((a, b) => {
      return a.seq - b.seq;
    });
    let acum = 0;
    let lost = 0;
    for (let i = 0; i < departureTime.length; i++) {
      let isFound = false;
      for (let j = i - lost; j < arrivalTime.length; j++) {
        if (departureTime[i].seq == arrivalTime[j].seq) {
          acum = acum +
          arrivalTime[j].time.getTime() -
          departureTime[i].time.getTime();
          isFound = true;
          break;
        }
      }
      if (!isFound) {
        lost++;
      }
    }
    this.latency = acum / arrivalTime.length;
    this.packetLoss = lost / departureTime.length;
  }

  /**
   * From recieved in bandwidth measurement.
   * @param {Number[]} arrivedMessages - The sending times
   * @param {Number} expectedBandwidth - The targeted bandwidth
   */
  extractBandwidth(arrivedMessages, expectedBandwidth) {
    const size = arrivedMessages.length;
    if (size > 0) {
      const maxSeq = arrivedMessages.reduce((prev, curr) => {
        return prev > curr ? prev : curr;
      });
      const recievedPercentage = size / (maxSeq + 1);
      this.packetLoss = 1 - recievedPercentage;
      this.bandwidth = expectedBandwidth * recievedPercentage;
    }
  }
}

module.exports = Measure;