/**
   * Method to assign the states for all the bursts.
   *
   * @param analyzeBeginTime
   * @param analyseEndTime
   */
  private void analyzeBurstStat(TraceData.Analysis analysis) {
    Map<BurstCategory, Double> burstCategoryToEnergy =
        new EnumMap<BurstCategory, Double>(BurstCategory.class);
    Map<BurstCategory, Long> burstCategoryToPayload =
        new EnumMap<BurstCategory, Long>(BurstCategory.class);
    Map<BurstCategory, Double> burstCategoryToActive =
        new EnumMap<BurstCategory, Double>(BurstCategory.class);

    long totalPayload = 0;
    double totalAct = 0.0;
    double totalEnergy = 0.0;

    for (Burst b : burstCollection) {
      BurstCategory category = b.getBurstCategory();
      double energy = b.getEnergy();
      totalEnergy += energy;
      Double catEnergy = burstCategoryToEnergy.get(category);
      double d = catEnergy != null ? catEnergy.doubleValue() : 0.0;
      d += energy;
      burstCategoryToEnergy.put(category, d);

      int p1 = getPayloadLength(b, false);
      totalPayload += p1;
      Long payload = burstCategoryToPayload.get(category);
      long l = payload != null ? payload.longValue() : 0L;
      l += p1;
      burstCategoryToPayload.put(category, l);

      double activeTime = b.getActiveTime();
      totalAct += activeTime;
      Double catAct = burstCategoryToActive.get(category);
      d = catAct != null ? catAct.doubleValue() : 0.0;
      d += activeTime;
      burstCategoryToActive.put(category, d);
    }
    {
      long p1 = getPayloadLenBkg();
      burstCategoryToPayload.put(BurstCategory.BURSTCAT_BKG, p1);
      totalPayload += p1;
    }
    for (Map.Entry<BurstCategory, Double> entry : burstCategoryToEnergy.entrySet()) {
      BurstCategory categ = entry.getKey();
      long catPayload = burstCategoryToPayload.get(categ);
      double catEnergy = burstCategoryToEnergy.get(categ);
      double catActive = burstCategoryToActive.get(categ);

      Double jpkb = catPayload > 0 ? catEnergy / (catPayload * 8 / 1000.0f) : null;
      burstAnalysisInfo.add(
          new BurstAnalysisInfo(
              categ,
              catPayload,
              ((double) catPayload / totalPayload) * 100.0,
              catEnergy,
              (catEnergy / totalEnergy) * 100.0,
              catActive,
              ((catActive / totalAct) * 100.0),
              jpkb));
    }
  }
  /** Method to find the different periodic connection and periodic duration. */
  private void validatePeriodicConnections() {
    int burstSize = burstCollection.size();
    Burst lastPeriodicalBurst = null;
    int periodicCount = 0;
    double minimumRepeatTime = Double.MAX_VALUE;
    PacketInfo packetId = null;
    for (int i = 0; i < burstSize; i++) {
      Burst burst = burstCollection.get(i);
      if (burst.getBurstCategory() == BurstCategory.BURSTCAT_PERIODICAL) {
        if (periodicCount != 0) {
          double time = burst.getBeginTime() - lastPeriodicalBurst.getBeginTime();
          if (time < minimumRepeatTime) {
            minimumRepeatTime = time;
            packetId = burst.getFirstUplinkDataPacket();
            if (packetId == null) {
              packetId = burst.getBeginPacket();
            }
          }
        }
        lastPeriodicalBurst = burst;
        periodicCount++;
      }
    }

    if (packetId != null) {
      shortestPeriodPacketInfo = packetId;
      shortestPeriodTCPSession = packetId.getSession();
    }
    if (minimumRepeatTime != Double.MAX_VALUE) {
      minimumPeriodicRepeatTime = minimumRepeatTime;
    }
  }
  /** To Validate the simultaneous TCP connections */
  private void validateUnnecessaryConnections() {
    int setCount = 0;
    int maxCount = 0;
    Burst maxBurst = null;
    for (int i = 0; i < burstCollection.size(); ++i) {
      Burst burstInfo = burstCollection.get(i);
      if (burstInfo.getBurstCategory() == BurstCategory.BURSTCAT_USER
          || burstInfo.getBurstCategory() == BurstCategory.BURSTCAT_SCREEN_ROTATION) {
        continue;
      }
      double startTime = burstInfo.getBeginTime();
      double endTime = startTime + 60.0;
      int count = 1;
      for (int j = i + 1;
          j < burstCollection.size() && burstCollection.get(j).getEndTime() <= endTime;
          ++j) {
        if (burstCollection.get(j).getBurstCategory() != BurstCategory.BURSTCAT_USER
            || burstInfo.getBurstCategory() == BurstCategory.BURSTCAT_SCREEN_ROTATION) {
          ++count;
        }
      }
      if (count >= 4) {
        ++setCount;
        if (count > maxCount) {
          maxCount = count;
          maxBurst = burstInfo;
        }
        i = i + count;
      } else if (count == 3) {
        endTime = startTime + 15.0;
        count = 1;
        for (int j = i + 1;
            j < burstCollection.size() && burstCollection.get(j).getEndTime() <= endTime;
            ++j) {
          if (burstCollection.get(j).getBurstCategory() != BurstCategory.BURSTCAT_USER
              || burstInfo.getBurstCategory() == BurstCategory.BURSTCAT_SCREEN_ROTATION) {
            ++count;
          }
        }
        if (count >= 3) {
          ++setCount;
          if (count > maxCount) {
            maxCount = count;
            maxBurst = burstInfo;
          }
          i = i + count;
        }
      }
    }

    tightlyCoupledBurstCount = setCount;
    if (maxBurst != null) {
      tightlyCoupledBurstTime = maxBurst.getBeginTime();
    }
  }
 /**
  * Getter for getting the payload length for the provided burst.
  *
  * @param burst
  * @param bIncludeBkgApp
  * @return
  */
 private int getPayloadLength(Burst burst, boolean bIncludeBkgApp) {
   int r = 0;
   for (PacketInfo p : burst.getPackets()) {
     if (bIncludeBkgApp || p.getAppName() != null) {
       r += p.getPayloadLen();
     }
   }
   return r;
 }
  @Override
  protected List<IrStreamItem> process(int beg, int length) {
    List<IrStreamItem> items = new ArrayList<>(16);
    ParameterData data = new ParameterData(CHUNKSIZE);
    for (int i = beg; i < beg + length - 1; i += 2) {
      int noBitsLimit = params.getNoBitsLimit(noPayload);
      int mark = analyzer.getCleanedTime(i);
      int space = analyzer.getCleanedTime(i + 1);
      Burst burst = new Burst(mark, space);
      if (burst.equals(zero)) {
        data.update(0);
      } else if (burst.equals(one)) {
        data.update(1);
      } else if (burst.equals(two)) {
        data.update(2);
      } else if (burst.equals(three)) {
        data.update(3);
      } else {
        if (!data.isEmpty()) {
          saveParameter(data, items, params.getBitDirection());
          data = new ParameterData();
        }

        items.add(newFlash(mark));
        if (i == beg + length - 2 && params.isUseExtents())
          items.add(newExtent(analyzer.getTotalDuration(beg, length)));
        else items.add(newGap(space));
      }

      if (data.getNoBits() >= noBitsLimit) {
        saveParameter(data, items, params.getBitDirection());
        data = new ParameterData(CHUNKSIZE);
      }
    }
    if (!data.isEmpty()) saveParameter(data, items, params.getBitDirection());

    return items;
  }
  /** Burst data's analyzed to categorize the periodic bursts. */
  private void diagnosisPeriodicRequest() {

    Map<String, List<Double>> requestHost2tsList = new HashMap<String, List<Double>>();
    Map<String, List<Double>> requestObj2tsList = new HashMap<String, List<Double>>();
    Map<InetAddress, List<Double>> connIP2tsList = new HashMap<InetAddress, List<Double>>();
    Set<String> hostPeriodicInfoSet = new HashSet<String>();
    periodicCount = 0;
    diffPeriodicCount = 0;
    minimumPeriodicRepeatTime = 0.0;

    for (TCPSession b : analysis.getTcpSessions()) {

      // Get a list of timestamps of established sessions with each remote
      // IP
      PacketInfo p = b.getPackets().get(0);
      if (p.getTcpInfo() == TcpInfo.TCP_ESTABLISH) {
        List<Double> res = connIP2tsList.get(b.getRemoteIP());
        if (res == null) {
          res = new ArrayList<Double>();
          connIP2tsList.put(b.getRemoteIP(), res);
        }
        res.add(Double.valueOf(p.getTimeStamp()));
      }

      // Get a list of timestamps of HTTP requests to hosts/object names
      for (HttpRequestResponseInfo rr : b.getRequestResponseInfo()) {
        PacketInfo pkt = rr.getFirstDataPacket();
        if (rr.getDirection() == HttpRequestResponseInfo.Direction.REQUEST) {
          Double ts0 = Double.valueOf(pkt.getTimeStamp());
          if (rr.getHostName() != null) {
            List<Double> tempRequestHostEventList = requestHost2tsList.get(rr.getHostName());
            if (tempRequestHostEventList == null) {
              tempRequestHostEventList = new ArrayList<Double>();
              requestHost2tsList.put(rr.getHostName(), tempRequestHostEventList);
            }
            tempRequestHostEventList.add(ts0);
          }

          if (rr.getObjName() != null) {
            String objName = rr.getObjNameWithoutParams();
            List<Double> tempRequestObjEventList = requestObj2tsList.get(objName);

            if (tempRequestObjEventList == null) {
              tempRequestObjEventList = new ArrayList<Double>();
              requestObj2tsList.put(objName, tempRequestObjEventList);
            }
            tempRequestObjEventList.add(ts0);
          }
        }
      }
    }

    Set<String> hostList = new HashSet<String>();
    for (Map.Entry<String, List<Double>> iter : requestHost2tsList.entrySet()) {
      if (SelfCorr(iter.getValue())) {
        hostList.add(iter.getKey());
      }
    }

    Set<String> objList = new HashSet<String>();
    for (Map.Entry<String, List<Double>> iter : requestObj2tsList.entrySet()) {
      if (SelfCorr(iter.getValue())) {
        objList.add(iter.getKey());
      }
    }

    Set<InetAddress> ipList = new HashSet<InetAddress>();
    for (Map.Entry<InetAddress, List<Double>> iter : connIP2tsList.entrySet()) {
      if (SelfCorr(iter.getValue())) {
        ipList.add(iter.getKey());
      }
    }

    for (Burst burst : burstCollection) {
      if (!burst.getBurstInfos().contains(BurstInfo.BURST_CLIENT_DELAY)) {
        continue;
      }
      Packet beginPacket = burst.getBeginPacket().getPacket();
      if (beginPacket instanceof IPPacket) {
        IPPacket ip = (IPPacket) beginPacket;
        if (ipList.contains(ip.getDestinationIPAddress())
            || ipList.contains(ip.getSourceIPAddress())) {
          periodicCount++;
          burst.setBurstInfo(BurstInfo.BURST_PERIODICAL);
          if (ipList.contains(ip.getDestinationIPAddress())) {
            hostPeriodicInfoSet.add(ip.getDestinationIPAddress().toString());
          } else {
            hostPeriodicInfoSet.add(ip.getSourceIPAddress().toString());
          }
          continue;
        }
      }

      PacketInfo firstUplinkPayloadPacket = null;
      for (PacketInfo p : burst.getPackets()) {
        if (p.getDir() == Direction.UPLINK && p.getPayloadLen() > 0) {
          firstUplinkPayloadPacket = p;
          break;
        }
      }

      for (TCPSession session : analysis.getTcpSessions()) {
        for (HttpRequestResponseInfo rr : session.getRequestResponseInfo()) {
          if (rr.getDirection() == HttpRequestResponseInfo.Direction.REQUEST
              && (hostList.contains(rr.getHostName())
                  || objList.contains(rr.getObjNameWithoutParams()))) {
            if (rr.getFirstDataPacket() == firstUplinkPayloadPacket) {
              periodicCount++;
              burst.setBurstInfo(BurstInfo.BURST_PERIODICAL);
              burst.setFirstUplinkDataPacket(firstUplinkPayloadPacket);
              if (hostList.contains(rr.getHostName())) {
                hostPeriodicInfoSet.add(rr.getHostName());
              } else {
                hostPeriodicInfoSet.add(rr.getObjNameWithoutParams());
              }
              continue;
            }
          }
        }
      }
    }
    diffPeriodicCount = hostPeriodicInfoSet.size();
  }
  /** Method to assign the burst states. */
  private void analyzeBursts() {
    List<UserEvent> userEvents = analysis.getUserEvents();
    List<CpuActivity> cpuEvents = analysis.getCpuActivityList();
    int userEventsSize = userEvents.size();
    int cpuEventsSize = cpuEvents.size();
    int userEventPointer = 0;
    int cpuPointer = 0;
    // Analyze each burst
    Burst b = null;
    Burst lastBurst;
    for (Iterator<Burst> i = burstCollection.iterator(); i.hasNext(); ) {
      lastBurst = b;
      b = i.next();
      // Step 1: Remove background packets
      List<PacketInfo> pktIdx = new ArrayList<PacketInfo>(b.getPackets().size());
      int payloadLen = 0;
      Set<TcpInfo> tcpInfo = new HashSet<TcpInfo>();
      for (PacketInfo p : b.getPackets()) {
        if (p.getAppName() != null) {
          payloadLen += p.getPayloadLen();
          pktIdx.add(p);
          TcpInfo tcp = p.getTcpInfo();
          if (tcp != null) {
            tcpInfo.add(tcp);
          }
        }
      }
      if (pktIdx.size() == 0) {
        assert (payloadLen == 0);
        b.addBurstInfo(BurstInfo.BURST_BKG);
        continue;
      }
      PacketInfo pkt0 = pktIdx.get(0);
      TcpInfo info0 = pkt0.getTcpInfo();
      double time0 = pkt0.getTimeStamp();

      // Step 2: a long burst?
      if (b.getEndTime() - b.getBeginTime() > profile.getLargeBurstDuration()
          && payloadLen > profile.getLargeBurstSize()) {
        b.addBurstInfo(BurstInfo.BURST_LONG);
        ++longBurstCount;
        continue;
      }

      // Step 3: Contains no payload?
      if (payloadLen == 0) {
        if (tcpInfo.contains(TcpInfo.TCP_CLOSE)) {
          b.addBurstInfo(BurstInfo.BURST_FIN);
        }
        if (tcpInfo.contains(TcpInfo.TCP_ESTABLISH)) {
          b.addBurstInfo(BurstInfo.BURST_SYN);
        }
        if (tcpInfo.contains(TcpInfo.TCP_RESET)) {
          b.addBurstInfo(BurstInfo.BURST_RST);
        }
        if (tcpInfo.contains(TcpInfo.TCP_KEEP_ALIVE)
            || tcpInfo.contains(TcpInfo.TCP_KEEP_ALIVE_ACK)) {
          b.addBurstInfo(BurstInfo.BURST_KEEPALIVE);
        }
        if (tcpInfo.contains(TcpInfo.TCP_ZERO_WINDOW)) {
          b.addBurstInfo(BurstInfo.BURST_ZEROWIN);
        }
        if (tcpInfo.contains(TcpInfo.TCP_WINDOW_UPDATE)) {
          b.addBurstInfo(BurstInfo.BURST_WINUPDATE);
        }
        if (b.getBurstInfos().size() == 0
            && (info0 == TcpInfo.TCP_ACK_RECOVER || info0 == TcpInfo.TCP_ACK_DUP)) {
          b.addBurstInfo(BurstInfo.BURST_LOSS_RECOVER);
        }
        if (b.getBurstInfos().size() > 0) continue;
      }

      // Step 4: Server delay
      if (pkt0.getDir() == PacketInfo.Direction.DOWNLINK
          && (info0 == TcpInfo.TCP_DATA || info0 == TcpInfo.TCP_ACK)) {
        b.addBurstInfo(BurstInfo.BURST_SERVER_DELAY);
        continue;
      }

      // Step 5: Loss recover
      if (info0 == TcpInfo.TCP_ACK_DUP || info0 == TcpInfo.TCP_DATA_DUP) {
        b.addBurstInfo(BurstInfo.BURST_LOSS_DUP);
        continue;
      }

      if (info0 == TcpInfo.TCP_DATA_RECOVER || info0 == TcpInfo.TCP_ACK_RECOVER) {
        b.addBurstInfo(BurstInfo.BURST_LOSS_RECOVER);
        continue;
      }

      // Step 6: User triggered
      final double USER_EVENT_SMALL_TOLERATE = profile.getUserInputTh();
      if (payloadLen > 0) {
        UserEvent ue = null;
        while ((userEventPointer < userEventsSize)
            && ((ue = userEvents.get(userEventPointer)).getReleaseTime()
                < (time0 - USER_EVENT_TOLERATE))) ++userEventPointer;
        BurstInfo bi =
            ue != null
                ? ((ue.getEventType() == UserEventType.SCREEN_LANDSCAPE
                        || ue.getEventType() == UserEventType.SCREEN_PORTRAIT)
                    ? BurstInfo.BURST_SCREEN_ROTATION_INPUT
                    : BurstInfo.BURST_USER_INPUT)
                : null;
        int j = userEventPointer;
        double minGap = Double.MAX_VALUE;
        while (j < userEventsSize) {
          UserEvent uEvent = userEvents.get(j);
          if (withinTolerate(uEvent.getPressTime(), time0)) {
            double gap = time0 - uEvent.getPressTime();
            if (gap < minGap) {
              minGap = gap;
            }
          }
          if (withinTolerate(uEvent.getReleaseTime(), time0)) {
            double gap = time0 - uEvent.getReleaseTime();
            if (gap < minGap) {
              minGap = gap;
            }
          }
          if (uEvent.getPressTime() > time0) {
            break;
          }
          j++;
        }
        if (minGap < USER_EVENT_SMALL_TOLERATE) {
          b.addBurstInfo(bi);
          continue;
        } else if (minGap < USER_EVENT_TOLERATE
            && (lastBurst == null || lastBurst.getEndTime() < b.getBeginTime() - minGap)) {
          double cpuBegin = time0 - minGap;
          double cpuEnd = time0;
          // Check CPU usage
          while (cpuPointer < cpuEventsSize) {
            double t = cpuEvents.get(cpuPointer).getBeginTimeStamp();
            if (t < b.getBeginTime() - USER_EVENT_TOLERATE) {
              ++cpuPointer;
            } else {
              break;
            }
          }
          int k = cpuPointer;
          double s = 0.0f;
          int ns = 0;
          while (k < cpuEventsSize) {
            CpuActivity cpuAct = cpuEvents.get(k);
            double t = cpuAct.getBeginTimeStamp();
            if (t > cpuBegin && t < cpuEnd) {
              s += cpuAct.getUsage();
              ns++;
            }
            if (t >= cpuEnd) {
              break;
            }
            k++;
          }
          if (ns > 0 && (s / ns) > AVG_CPU_USAGE_THRESHOLD) {
            b.addBurstInfo(bi);
            b.addBurstInfo(BurstInfo.BURST_CPU_BUSY);
            continue;
          }
        }
      }

      // Step 7: Client delay
      if (b.getBurstInfos().size() == 0 && payloadLen == 0) {
        b.addBurstInfo(BurstInfo.BURST_UNKNOWN);
      } else {
        b.addBurstInfo(BurstInfo.BURST_CLIENT_DELAY);
      }
    }
  }
  /** Computes the total burst energy. */
  private void computeBurstEnergyRadioResource() {
    List<RrcStateRange> rrcCollection = analysis.getRrcStateMachine().getRRcStateRanges();
    int rrcCount = rrcCollection.size();
    if (rrcCount == 0) {
      return;
    }
    int p = 0;
    double time2 = -1;
    double totalEnergy = 0.0f;
    Iterator<Burst> iter = burstCollection.iterator();
    Burst currentBurst = iter.next();
    double time1 = rrcCollection.get(0).getBeginTime();
    while (true) {
      Burst nextBurst = iter.hasNext() ? iter.next() : null;
      time2 =
          nextBurst != null
              ? nextBurst.getBeginTime()
              : rrcCollection.get(rrcCount - 1).getEndTime();
      double e = 0.0f;
      double activeTime = 0.0f;
      while (p < rrcCount) {
        RrcStateRange rrCntrl = rrcCollection.get(p);
        if (rrCntrl.getEndTime() < time1) {
          p++;
        } else {
          if (time2 > rrCntrl.getEndTime()) {
            e +=
                profile.energy(
                    time1, rrCntrl.getEndTime(), rrCntrl.getState(), analysis.getPackets());
            if ((rrCntrl.getState() == RRCState.STATE_DCH
                    || rrCntrl.getState() == RRCState.TAIL_DCH)
                || (rrCntrl.getState() == RRCState.LTE_CONTINUOUS
                    || rrCntrl.getState() == RRCState.LTE_CR_TAIL)
                || (rrCntrl.getState() == RRCState.WIFI_ACTIVE
                    || rrCntrl.getState() == RRCState.WIFI_TAIL)) {
              activeTime += rrCntrl.getEndTime() - time1;
            }
            p++;
          }
          break;
        }
      }
      while (p < rrcCount) {
        RrcStateRange rrCntrl = rrcCollection.get(p);
        if (rrCntrl.getEndTime() < time2) {
          e +=
              profile.energy(
                  Math.max(rrCntrl.getBeginTime(), time1),
                  rrCntrl.getEndTime(),
                  rrCntrl.getState(),
                  analysis.getPackets());
          if ((rrCntrl.getState() == RRCState.STATE_DCH || rrCntrl.getState() == RRCState.TAIL_DCH)
              || (rrCntrl.getState() == RRCState.LTE_CONTINUOUS
                  || rrCntrl.getState() == RRCState.LTE_CR_TAIL)
              || (rrCntrl.getState() == RRCState.WIFI_ACTIVE
                  || rrCntrl.getState() == RRCState.WIFI_TAIL)) {
            activeTime += rrCntrl.getEndTime() - Math.max(rrCntrl.getBeginTime(), time1);
          }
          p++;
        } else {
          e +=
              profile.energy(
                  Math.max(rrCntrl.getBeginTime(), time1),
                  time2,
                  rrCntrl.getState(),
                  analysis.getPackets());
          if ((rrCntrl.getState() == RRCState.STATE_DCH || rrCntrl.getState() == RRCState.TAIL_DCH)
              || (rrCntrl.getState() == RRCState.LTE_CONTINUOUS
                  || rrCntrl.getState() == RRCState.LTE_CR_TAIL)
              || (rrCntrl.getState() == RRCState.WIFI_ACTIVE
                  || rrCntrl.getState() == RRCState.WIFI_TAIL)) {
            activeTime += time2 - Math.max(rrCntrl.getBeginTime(), time1);
          }
          break;
        }
      }
      currentBurst.setEnergy(e);
      totalEnergy += e;
      currentBurst.setActiveTime(activeTime);

      time1 = time2;
      if (nextBurst != null) {
        currentBurst = nextBurst;
      } else {
        break;
      }
    }
    this.totalEnergy = totalEnergy;
  }
  /** Groups packets into Burst Collections */
  private void groupIntoBursts() {
    // Validate that there are packets
    List<PacketInfo> packets = this.analysis.getPackets();
    if (packets.size() <= 0) {
      this.burstCollection = Collections.emptyList();
      return;
    }
    ArrayList<Burst> result = new ArrayList<Burst>();
    double burstThresh = profile.getBurstTh();
    double longBurstThresh = profile.getLongBurstTh();
    List<PacketInfo> burstPackets = new ArrayList<PacketInfo>();
    // Step 1: Build bursts using burst time threshold
    PacketInfo lastPacket = null;
    for (PacketInfo packet : packets) {
      if (lastPacket == null
          || (packet.getTimeStamp() - lastPacket.getTimeStamp() > burstThresh
              && !mss.contains(lastPacket.getPayloadLen()))) {
        if (burstPackets.size() > 0) {
          result.add(new Burst(burstPackets));
          burstPackets.clear();
        }
      }
      burstPackets.add(packet);
      lastPacket = packet;
    }
    result.add(new Burst(burstPackets));

    // Step 2: Remove promotion delays and merge bursts if possible
    Map<PacketInfo, Double> timestampList = normalizeCore(packets);
    List<Burst> newBurstColl = new ArrayList<Burst>(result.size());
    int n = result.size();
    Burst newBurst = result.get(0);
    for (int i = 0; i < n - 1; i++) {
      Burst bnext = result.get(i + 1);
      double time1 = timestampList.get(newBurst.getEndPacket());
      double time2 = timestampList.get(bnext.getBeginPacket());
      if ((time2 - time1) < burstThresh) {
        newBurst.merge(bnext);
      } else {
        newBurstColl.add(newBurst);
        newBurst = bnext;
      }
    }
    newBurstColl.add(newBurst);
    this.burstCollection = newBurstColl;

    // Step 3: compute burstID for each packet
    n = burstCollection.size();
    for (Burst b : burstCollection) {
      for (PacketInfo p : b.getPackets()) {
        p.setBurst(b);
      }
    }

    // Step 4: determine short/long IBTs
    n = burstCollection.size();
    for (int i = 0; i < n; i++) {
      Burst b = burstCollection.get(i);
      assert (b.getEndTime() >= b.getBeginTime());
      if (i < n - 1) {
        double ibt = burstCollection.get(i + 1).getBeginTime() - b.getEndTime();
        assert (ibt >= burstThresh);
        b.setbLong((ibt > longBurstThresh));
      } else {
        b.setbLong(true);
      }
    }
  }