package DataStructure; import java.io.Serializable; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; public class SingleBTS implements Comparable, Serializable { private static final long serialVersionUID = 1L; public int ARFCN; // BTS-Frequency public String name; // Name of BTS public Date time; // used to distinguish usedBTS; public LinkedList interpolated = new LinkedList(); // Is this BTS interpolated or measured? public boolean fullBTS; // Is this a fully Measured BTS? Does it have UL, TA // and so on... public int TA; // Timing-Advance public int BS_POWER; // BaseSation Power public int MS_TO; // MS_TO public int MS_PWR; // L1_MS_PWR // public GPScoordinate coord; // where was the BTS found? private ArrayList RXul = new ArrayList(); // Strength // Up-link private ArrayList RXdl = new ArrayList(); // Strength // Down-Link public int reason; // SMS, call, LocationUpdate... public double distance; // Distance to BTS in km public GPScoordinate coordinate; private double varDL; // Stores variance if this SingleBTS is // interpolated public double mode; // maximum probability density in dBm // public double ulQ = -1; public ArrayList dlQsub = new ArrayList(); // public ArrayList dlQfull = new ArrayList(); public ArrayList ulQsub = new ArrayList(); // public ArrayList ulQfull = new ArrayList(); // Constructor public SingleBTS(int ARFCN, double RXul, double RXdl, boolean interpolated, Date time, String name) { this.ARFCN = ARFCN; if (RXul != 0) this.addUl(RXul); this.addDl(RXdl); this.interpolated.add(interpolated); // this.interpolated = interpolated; this.time = time; this.name = name; } public SingleBTS(int ARFCN, double RXul, double RXdl, boolean interpolated, Date time, String name, double varDL) { this(ARFCN, RXul, RXdl, interpolated, time, name); this.varDL = varDL; } /* * // Constructor without boolean without time public SingleBTS(int ARFCN, * int RXul, int RXdl, int TA) { this(ARFCN, RXul, RXdl, TA, false, null); } * * // Constructor with date public SingleBTS(int ARFCN, int RXul, int RXdl, * int TA, Date time) { this(ARFCN, RXul, RXdl, TA, false, time); } */ public SingleBTS(int ARFCN, int RXul, int RXdl, boolean interpolated, Date timestamp, String name, int TA, int BS_POWER, int MS_TO, int mS_PWR) { this.ARFCN = ARFCN; this.addUl(RXul); this.addDl(RXdl); this.interpolated.add(interpolated); // this.interpolated = interpolated; this.time = timestamp; this.name = name; this.TA = TA; this.BS_POWER = BS_POWER; this.MS_TO = MS_TO; } // "empty" constructor to do averaging public SingleBTS(int ARFCN, String name) { this.ARFCN = ARFCN; this.name = name; this.reason = 2; time = new Date(); } public void addUl(double RXul) { // a Measurement of 0 will not happen! if (RXul != 0) { if (RXul > 0) { // RXul is in mW this.RXul.add(RXul); } else { // RXul is in dBmw double value = Math.pow(10, RXul / 10d); this.RXul.add(value); } // it gets automatically a full BTS fullBTS = true; } } public double getDLQsub() { if (dlQsub.size() == 0) { return -10000; } if (dlQsub.size() == 1) { return dlQsub.get(0); } int result = 0; for (int i = 0; i < dlQsub.size(); i++) { result += dlQsub.get(i); } return ((double) result / dlQsub.size()); } public double getULQsub() { if (ulQsub.size() == 0) { return -10000; } if (ulQsub.size() == 1) { return ulQsub.get(0); } int result = 0; for (int i = 0; i < ulQsub.size(); i++) { result += ulQsub.get(i); } return ((double) result / ulQsub.size()); } /* * public double getULQfull() { if (ulQfull.size() == 0) { return -10000; } * if (ulQfull.size() == 1) { return ulQfull.get(0); } * * int result = 0; for (int i = 0; i < ulQfull.size(); i++) { result += * ulQfull.get(i); } return ((double) result / ulQfull.size()); } */ /* * public double getDLQfull() { if (dlQfull.size() == 0) { return 9; } if * (dlQfull.size() == 1) { return dlQfull.get(0); } * * int result = 0; for (int i = 0; i < dlQfull.size(); i++) { result += * dlQfull.get(i); } return ((double) result / dlQfull.size()); } */ public double getUldB() { if (RXul.isEmpty()) return Double.NaN; double sum = 0; for (Double ul : RXul) { sum = sum + ul; } // average sum = sum / RXul.size(); // to dbm double dbm = 10 * Math.log10(sum); return (dbm); } public double getULmW() { if (RXul.isEmpty()) return 0; double sum = 0; for (Double ul : RXul) { sum = sum + ul; } return (sum / RXul.size()); } public double getDLmW() { if (RXdl.isEmpty()) return 0; double sum = 0; for (Double dl : RXdl) { sum = sum + dl; } return (sum / RXdl.size()); } public void addDl(double RXdl) { if (RXdl != 0) { if (RXdl > 0) { // RXdl is in mW this.RXdl.add(RXdl); } else { // RXul is in dBmW double value = Math.pow(10, RXdl / 10d); this.RXdl.add(value); } } } public double getDldB() { if (RXdl.isEmpty()) return Double.NaN; double sum = 0; for (Double dl : RXdl) { sum = sum + dl; } // average sum = sum / RXdl.size(); // to dB double dbm = 10 * Math.log10(sum); return (dbm); } /** * Returns average of dB-Values. No prior transform to mW before averaging. * Seems wrong! * * @return */ public double getStrictDLdBAverage() { if (mode == 0) { double sum = 0; for (Double x : RXdl) { sum += 10 * Math.log10(x); } sum = sum / getDLcount(); // mode = sum; return sum; } else return mode; } /** * Adds measurements from another SingleBTS. Provided BTS is only added if: * BTS is not null, ARFCN of BTS matches * * @param BTS * The BTS that should be added */ public void addBTSMeasure(SingleBTS BTS) { if (BTS != null && BTS.ARFCN == this.ARFCN) { addUl(BTS.getUldB()); addDl(BTS.getDldB()); // this.dlQsub.addAll(BTS.dlQsub); // this.dlQfull.addAll(BTS.dlQfull); this.ulQsub.addAll(BTS.ulQsub); // this.ulQfull.addAll(BTS.ulQfull); this.dlQsub.addAll(BTS.dlQsub); if (BTS.BS_POWER != 0) this.BS_POWER = BTS.BS_POWER; if (BTS.MS_PWR != 0) this.MS_PWR = BTS.MS_PWR; if (BTS.MS_TO != 0) this.MS_TO = BTS.MS_TO; if (BTS.TA != 0) this.TA = BTS.TA; if (BTS.time != null) this.time = BTS.time; if (!this.fullBTS) this.fullBTS = BTS.fullBTS; } } public double getVarianceULmW() { /* * Variance: calculate avarage calculate for each measurement x: * (x-average)² sum up and divide by number of measurements */ double average = getULmW(); double variance = 0; for (double xi : RXul) { variance += Math.pow(xi - average, 2); } return variance / getULcount(); } public double getVarianceDLmW() { /* * Variance: calculate avarage calculate for each measurement x: * (x-average)² sum up and divide by number of measurements */ double average = getDLmW(); double variance = 0; for (double xi : RXdl) { variance += Math.pow(xi - average, 2); } return variance / getDLcount(); } @SuppressWarnings("unused") public double getVarianceDLdB() { // HACK: always return sigma 5 (Variance 25 = 5^2)!!! if (true) { // return 8; // std of 4 if no user present // return 12.5; // std of 5 if a user present // was 2.5! // return 16; return 25; } // if (RXdl.isEmpty()) { // return 0; // } // if (isInterpolated()) { // return varDL; // } double variance = 0; // TODO: set average to getAverageDLdB(). Then calculate variance! // calculate average of dB values (no Tranform to mW and back) double average = 0; for (double value : RXdl) { average += 10 * Math.log10(value); } average = average / getDLcount(); for (double xi : RXdl) { variance += Math.pow(10 * Math.log10(xi) - average, 2); } variance = variance / getDLcount(); // double variance = 10 * Math.log10(getVarianceDLmW()); if (variance == Double.NaN) { System.out.println("NaN in Variance"); } return variance; } public double getVarianceULdB() { if (RXdl.isEmpty()) { return 0; } double variance = 0; // calculate simple average of dB values (no tranform to mW) double average = 0; for (double value : RXul) { average += 10 * Math.log10(value); } average = average / getULcount(); for (double xi : RXul) { variance += Math.pow(10 * Math.log10(xi) - average, 2); } variance = variance / getULcount(); // double variance = 10 * Math.log10(getVarianceDLmW()); return variance; } public double getTrueVarianceDLdB() { if (RXdl.isEmpty()) { return 5; } if (isInterpolated()) { return varDL; } double variance = 0; // TODO: set average to getAverageDLdB(). Then calculate variance! // calculate average of dB values (no Tranform to mW and back) double average = 0; for (double value : RXdl) { average += 10 * Math.log10(value); } average = average / getDLcount(); for (double xi : RXdl) { variance += Math.pow(10 * Math.log10(xi) - average, 2); } variance = variance / getDLcount(); // double variance = 10 * Math.log10(getVarianceDLmW()); if (variance == Double.NaN) { System.out.println("NaN in Variance"); } return variance; } public int getULcount() { return RXul.size(); } public int getDLcount() { return RXdl.size(); } public int minDLdB() { double minmW = minDLmW(); if (((int) (10 * Math.log10(minmW))) == 33) { System.out.print("min falsch"); } double minDLdB = (10 * Math.log10(minmW)); if (minDLdB == Double.NEGATIVE_INFINITY) { return -111; } return (int) minDLdB; } public double minDLmW() { if (RXdl.isEmpty()) { return 0; } double min = 2000; for (Double measure : RXdl) { if (measure < min) min = measure; } return min; } public int minULdB() { double minmW = minULmW(); double mindB = 10 * Math.log10(minmW); if (mindB == Double.NEGATIVE_INFINITY) { return -111; } return (int) (mindB); } public double minULmW() { if (RXul.isEmpty()) { return 0; } double min = 2000; for (Double measure : RXul) { if (measure < min) min = measure; } return min; } public int maxDLdB() { double maxmW = maxDLmW(); return (int) (10 * Math.log10(maxmW)); } public double maxDLmW() { double max = -2000; for (Double measure : RXdl) { if (measure > max) { max = measure; } } return max; } public int maxULdB() { double maxmW = maxULmW(); return (int) (10 * Math.log10(maxmW)); } public double maxULmW() { double max = -2000; for (Double measure : RXul) { if (measure > max) { max = measure; } } return max; } // returns "ARFCN;RXul;RXdl;TA" public String toString() { if (RXdl.size() == 1 && RXul.size() == 1) { return (name + ":" + ARFCN + "; UL:" + (int) (getUldB()) + "; DL:" + (int) (getDldB()) + " Mode:" + getStrictDLdBAverage() + " " + time.toString()); } else { String dl = getDLString(); String ul = getULString(); return (name + ": " + ARFCN + "; " + ul + " " + dl + " " + time .toString()); } } public String ULtoString() { return getULString() + " " + time.toString(); } public String DLtoString() { return getDLString() + " Mode: " + getStrictDLdBAverage() + ", " + time.toString(); } private String getULString() { NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(3); String ul = "UL(min:" + minULdB() + ",max:" + maxULdB() + ",varDL:" + nf.format(getVarianceULdB()) + "avg:" + nf.format(getUldB()) + "),#meas:" + RXul.size(); return ul; } public String getDLString() { NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(3); // String variance = Double.toString(getVarianceDLdB()); // String variancevariable = Double.toString(varDL); String dl = "DL(min:" + minDLdB() + ",max:" + maxDLdB() + ",varDL:" + nf.format(getVarianceDLdB()) + "avg:" + nf.format(getDldB()) + "),#meas:" + RXdl.size(); return dl; } // make SingleBTS comparable to SingleBTS! public int compareTo(SingleBTS comperator) { if (this.time == null || comperator.time == null) { throw new NullPointerException( "one of the SingleBTS objects is null!"); } if (this.time.getTime() > comperator.time.getTime()) { return 1; } else if (this.time.getTime() < comperator.time.getTime()) { return -1; } else return 0; } public int compareTo(Date timestamp) { if (this.time == null || timestamp == null) { throw new NullPointerException("objects is null!"); } if (this.time.getTime() > timestamp.getTime()) { return 1; } else if (this.time.getTime() < timestamp.getTime()) { return -1; } else return 0; } public boolean newlyInterpolated() { if (interpolated == null || interpolated.isEmpty()) return false; return interpolated.get(interpolated.size() - 1); } /** * remove outlier with Grubb's test. see * http://www.graphpad.com/articles/outlier.htm */ public void removeOutlier() { // if (RXul.size() < 4) // System.out.println("Too few data points: UL! " + RXul.size()); // if (RXdl.size() < 4) // System.out.println("Too few data points: DL! " + RXdl.size()); boolean outlier = true; // loop until no more outlier is found while (outlier) { outlier = false; double maxUL = maxULdB(); double minUL = minULdB(); double maxDL = maxDLdB(); double minDL = minDLdB(); // calculate Z for each. Use dBm values. They are in normal // distribution double maxULZ = Math.abs(getUldB() - maxUL) / Math.sqrt(getVarianceULdB()); double minULZ = Math.abs(getUldB() - minUL) / Math.sqrt(getVarianceULdB()); double maxDLZ = Math.abs(getStrictDLdBAverage() - maxDL) / Math.sqrt(getTrueVarianceDLdB()); double minDLZ = Math.abs(getStrictDLdBAverage() - minDL) / Math.sqrt(getTrueVarianceDLdB()); // check for probability < 95% that it is within the gauss curve if (maxULZ > getCritZ(getULcount()) && RXul.size() > 2) { RXul.remove((Object) maxULmW()); outlier = true; // start again from the beginning: continue continue; // System.out.println("Outlier UL removed: " + maxUL); } if (minULZ > getCritZ(getULcount()) && RXul.size() > 2) { RXul.remove((Object) minULmW()); outlier = true; continue; // System.out.println("Outlier UL removed: " + minUL); } if (maxDLZ > getCritZ(getDLcount()) && RXdl.size() > 2) { RXdl.remove((Object) maxDLmW()); outlier = true; continue; // System.out.println("Outlier DL removed: " + maxDL); } if (minDLZ > getCritZ(getDLcount()) && RXdl.size() > 2) { RXdl.remove((Object) minDLmW()); outlier = true; continue; // System.out.println("Outlier DL removed: " + minDL); } } } // search for arfcn! public int compareTo(int arfcn) { if (this.ARFCN > arfcn) return 1; if (this.ARFCN < arfcn) return -1; else return 0; } // resets UL and DL public void clear() { RXdl.clear(); RXul.clear(); } public boolean isInterpolated() { if (interpolated == null || interpolated.isEmpty()) { return false; } else { return interpolated.getLast(); // damit die Log-Interpolation // wieder // funktioniert, muss das hier auf // getFirst() stehen // return interpolated.getFirst(); } } // show critical values for Z private float getCritZ(int N) { if (N == 3) return 1.15f; if (N == 4) return 1.48f; if (N == 5) return 1.71f; if (N == 6) return 1.89f; if (N == 7) return 2.02f; if (N == 8) return 2.13f; if (N == 9) return 2.21f; if (N == 10) return 2.29f; if (N == 11) return 2.34f; if (N == 12) return 2.41f; if (N == 13) return 2.46f; if (N == 14) return 2.51f; if (N == 15) return 2.55f; if (N == 16) return 2.59f; if (N == 17) return 2.62f; if (N == 18) return 2.65f; if (N == 19) return 2.68f; if (N == 20) return 2.73f; if (N == 21) return 2.73f; if (N == 22) return 2.76f; if (N == 23) return 2.78f; if (N == 24) return 2.80f; if (N == 25) return 2.82f; if (N == 26) return 2.84f; if (N == 27) return 2.86f; if (N == 28) return 2.88f; if (N == 29) return 2.89f; if (N == 30) return 2.91f; if (N == 31) return 2.92f; if (N == 32) return 2.94f; if (N == 33) return 2.95f; if (N == 34) return 2.97f; if (N == 35) return 2.98f; if (N == 36) return 2.99f; if (N == 37) return 3.00f; if (N == 38) return 3.01f; if (N == 39) return 3.03f; if (N >= 40 && N < 50) return 3.04f; if (N >= 50 && N < 60) return 3.13f; if (N >= 60 && N < 70) return 3.20f; if (N >= 70 && N < 80) return 3.26f; if (N >= 80 && N < 90) return 3.31f; if (N >= 90 && N < 100) return 3.35f; if (N >= 100 && N < 110) return 3.38f; if (N >= 110 && N < 120) return 3.42f; if (N >= 120 && N < 130) return 3.44f; if (N >= 130 && N < 140) return 3.47f; if (N >= 140 && N < 150) return 3.49f; if (N >= 150 && N < 160) return 3.52f; else return 3.7f; } /** * Sets the Variance for DL in mW. Works only if this BTS is interpolated! * * @param varDL */ public void setVarDLdB(double var) { if (isInterpolated()) { this.varDL = var; } else { System.out.println("kann Variance nicht setzen!"); } } }