content: package org.altbeacon.beacon.service;
  
  import android.os.SystemClock;
  
  import org.altbeacon.beacon.Beacon;
  import org.altbeacon.beacon.BeaconManager;
  import org.altbeacon.beacon.logging.LogManager;
  
  import java.io.Serializable;
  import java.lang.reflect.Constructor;
  
  public class RangedBeacon implements Serializable {
  
      private static final String TAG = "RangedBeacon";
      public static final long DEFAULT_MAX_TRACKING_AGE = 5000; /* 5 Seconds */
      public static long maxTrackingAge = DEFAULT_MAX_TRACKING_AGE; /* 5 Seconds */
      //kept here for backward compatibility
      public static final long DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS = 20000; /* 20 seconds */
      private static long sampleExpirationMilliseconds = DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS;
      private boolean mTracked = true;
      protected long lastTrackedTimeMillis = 0;
      Beacon mBeacon;
      protected transient RssiFilter mFilter = null;
      private int packetCount = 0;
      private long firstCycleDetectionTimestamp = 0;
      private long lastCycleDetectionTimestamp = 0;
  
      public RangedBeacon(Beacon beacon) {
          updateBeacon(beacon);
      }
  
      public void updateBeacon(Beacon beacon) {
          packetCount += 1;
          mBeacon = beacon;
          if(firstCycleDetectionTimestamp == 0) {
              firstCycleDetectionTimestamp = beacon.getFirstCycleDetectionTimestamp();
          }
          lastCycleDetectionTimestamp = beacon.getLastCycleDetectionTimestamp();
          addMeasurement(mBeacon.getRssi());
      }
  
      public boolean isTracked() {
          return mTracked;
      }
  
      public void setTracked(boolean tracked) {
          mTracked = tracked;
      }
  
      public Beacon getBeacon() {
          return mBeacon;
      }
  
      // Done at the end of each cycle before data are sent to the client
      public void commitMeasurements() {
           if (!getFilter().noMeasurementsAvailable()) {
               double runningAverage = getFilter().calculateRssi();
               mBeacon.setRunningAverageRssi(runningAverage);
               mBeacon.setRssiMeasurementCount(getFilter().getMeasurementCount());
               LogManager.d(TAG, "calculated new runningAverageRssi: %s", runningAverage);
          }
          else {
              LogManager.d(TAG, "No measurements available to calculate running average");
          }
          mBeacon.setPacketCount(packetCount);
          mBeacon.setFirstCycleDetectionTimestamp(firstCycleDetectionTimestamp);
          mBeacon.setLastCycleDetectionTimestamp(lastCycleDetectionTimestamp);
          packetCount = 0;
          firstCycleDetectionTimestamp = 0L;
          lastCycleDetectionTimestamp = 0L;
      }
  
      public void addMeasurement(Integer rssi) {
          // Filter out unreasonable values per
          // http://stackoverflow.com/questions/30118991/rssi-returned-by-altbeacon-library-127-messes-up-distance
          if (rssi != 127) {
              mTracked = true;
              lastTrackedTimeMillis = SystemClock.elapsedRealtime();
              getFilter().addMeasurement(rssi);
          }
      }
  
      //kept here for backward compatibility
      public static void setSampleExpirationMilliseconds(long milliseconds) {
          sampleExpirationMilliseconds = milliseconds;
          RunningAverageRssiFilter.setSampleExpirationMilliseconds(sampleExpirationMilliseconds);
      }
  
      public static void setMaxTrackinAge(int maxTrackinAge) {
          RangedBeacon.maxTrackingAge = maxTrackinAge;
      }
  
      public boolean noMeasurementsAvailable() {
          return getFilter().noMeasurementsAvailable();
      }
  
      public long getTrackingAge() {
          return SystemClock.elapsedRealtime() - lastTrackedTimeMillis;
      }
  
      public boolean isExpired() {
          return getTrackingAge() > maxTrackingAge;
      }
  
      private RssiFilter getFilter() {
          if (mFilter == null) {
              // Use custom RSSI filter
              if (BeaconManager.getRssiFilterImplClass() != null) {
                  try {
                      mFilter = (RssiFilter) BeaconManager.getRssiFilterImplClass().newInstance();
                  } catch (Exception e) {
                      LogManager.e(TAG, "Failed with exception %s", e.toString());
                      LogManager.e(TAG, "Could not construct class %s", BeaconManager.getRssiFilterImplClass().getName());
                      LogManager.e(TAG, "Will default to RunningAverageRssiFilter");
                  }
              }
              if (mFilter == null) {
                  // Use default rssi filter
                  mFilter = new RunningAverageRssiFilter();
              }
          }
          return mFilter;
      }
  }
  