Coder Social home page Coder Social logo

ble-indoor-positioning's Introduction

Travis GitHub release JitPack Codecov license

BLE Indoor Positioning

This repo contains a Java library that is capable of estimating locations based on advertising packets received from Bluetooth beacons. It also contains an Android app that uses this library to visualize beacon and location data.

Demo App Screen Recording

Usage

Integration

Gradle

Release artefacts are available through Bintray.

dependencies {
    compile 'com.nexenio.bleindoorpositioning:core:0.4.0'
}

If you want to use branch snapshots or specific commits, use JitPack.

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

dependencies {
    compile 'com.github.neXenio:BLE-Indoor-Positioning:dev-SNAPSHOT'
}

Maven

<dependency>
  <groupId>com.nexenio.bleindoorpositioning</groupId>
  <artifactId>core</artifactId>
  <version>0.4.0</version>
</dependency>

JAR

You can download the latest .jar files from GitHub or Bintray.

Bluetooth Scanning

You need to implement some sort of Bluetooth scanning in order to get the advertising data from nearby beacons. On Android, you can use the BluetoothAdapter or libraries like RxAndroidBle that wrap around the system APIs.

You'll get a scan result, which you can extract the beacon mac address and raw advertising data from. Forward that data to the BeaconManager singleton and it will take care of everything else.

private void processScanResult(ScanResult scanResult) {
    String macAddress = scanResult.getBleDevice().getMacAddress();
    byte[] advertisingData = scanResult.getScanRecord().getBytes();
    int rssi = scanResult.getRssi();
    BeaconManager.processAdvertisingData(macAddress, advertisingData, rssi);
}

The BeaconManager will create Beacon instances for you and hold them in memory. Each Beacon will hold a list of recent AdvertisingPackets. There are quite a few convenience methods for Eddystone and iBeacon available, too.

You can listen for beacon changes by registering a BeaconUpdateListener:

BeaconManager.registerBeaconUpdateListener(new BeaconUpdateListener() {
    @Override
    public void onBeaconUpdated(Beacon beacon) {
        // have fun with your beacon!
    }
});

For some more fine-tuned callbacks, you may want to use a FilteredBeaconUpdateListener, which will only emit updates when beacons match a BeaconFilter of your choice.

Distance Estimation

Based on the received AdvertisingPackets (which also keep track of the RSSIs), you can get an estimated distance (in meters) to each Beacon. Simply call beacon.getDistance() or directly use the BeaconDistanceCalculator, which operates using the Log-distance path loss model.

Location Estimation

Based on the estimated distances to nearby beacons, the IndoorPositioning singleton can calculate the current geo coordinates of the device that received the advertising data. It utilizes the Multilateration class for that, which solves a formulation of n-D space trilateration problem with a nonlinear least squares optimizer (using the Levenberg–Marquardt algorithm).

You can listen for location changes by registering a LocationListener:

IndoorPositioning.registerLocationListener(new LocationListener() {
    @Override
    public void onLocationUpdated(LocationProvider locationProvider, Location location) {
        // have fun with your location!
    }
});

The Location will contain latitude, longitude and altitude, as well as some convenience methods to get the distance or angle to a different location.

This assumes that the geo coordinates of the beacons are known. You can assign a BeaconLocationProvider to any beacon instance, which could read the geo coordinates from the advertising data or some external API.

ble-indoor-positioning's People

Contributors

hadiidbouk avatar jobhh avatar leonlowitzki avatar marvinmirtschin avatar steppschuh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ble-indoor-positioning's Issues

Provide indoor positioning beacon filter and update listener

public class IndoorPositioningBeaconFilter extends IBeaconFilter {

    public static final UUID INDOOR_POSITIONING_UUID = UUID.fromString("03253fdd-55cb-44c2-a1eb-80c8355f8291");

    public IndoorPositioningBeaconFilter() {
        setProximityUuid(INDOOR_POSITIONING_UUID);
    }
}
public abstract class IndoorPositioningBeaconUpdateListener extends FilteredBeaconUpdateListener {

    public IndoorPositioningBeaconUpdateListener() {
        super(new IndoorPositioningBeaconFilter());
    }

}

Inconsistent @NonNull annotation usage

Hello,

The registerLocationListener (unregisterLocationListener) method in AndroidLocationProvider are not nullable

registerLocationListener(@NonNull LocationListener locationListener)

But the same method in the IndoorPositioning (Which should be named IndoorPositioningProvider - i guess) are nullable.

registerLocationListener(LocationListener locationListener)

Can we follow the same convention in both methods ?

Apk file

How can i get .apk file of this project.

Sample app crash when using LocationProvider instead of minor

Hello,
if someone use the LocationProvider to get the location from api or based on the mac address ( and not from the minor ) the sample app will crash when pressing the chart button because he may get a negative value of minor and he cannot get a color from the materialDesignXColors array.

 @ColorInt
    public static int getBeaconColor(Beacon beacon, @ColorUtil.ColoringMode int coloringMode, int beaconIndex) {
        int colorIndex = 0;
        switch (coloringMode) {
            case ColorUtil.COLORING_MODE_INSTANCES: {
                if (beacon instanceof IBeacon) {
                    colorIndex = ((IBeacon) beacon).getMinor();
                } else {
                    colorIndex = beaconIndex;
                }
                break;
            }
...

Incorrect bearing (angle) calculation

Calculation used in Location to get the rotation angle between 2 locations must convert the location values lat and long to radians before calculating Math.cos() and Math.sin().

App crash in updateLocation method

Hello,

I found that the App crash when the number of my beacons is 4 in updateLocation() method inside the IndoorPositioning class

No interface method sort(Ljava/util/Comparator;)V in class Ljava/util/List; or its super classes (declaration of 'java.util.List' appears in /system/framework/core-libart.jar)

1 -
screen shot 2018-06-20 at 10 26 23 am

2 -
screen shot 2018-06-20 at 10 26 55 am

3 -
screen shot 2018-06-20 at 10 27 31 am

Here is the 4 beacons from usableBeacons ArrayList :

1 -
screen shot 2018-06-20 at 10 34 17 am

2 -
screen shot 2018-06-20 at 10 34 36 am

3 -
screen shot 2018-06-20 at 10 34 45 am

4 -
screen shot 2018-06-20 at 10 34 54 am

PS: I am working with the dev branch
'implementation 'com.github.neXenio:BLE-Indoor-Positioning:dev-SNAPSHOT'

meetsSpecification not working with all ibeacons

Hello,

I am currently using 4 beacons supplied by Kontakt, (2 Pro and 2 standard).

The beacons (Pro), doesn't meet the specification of IBeaconAdvertisingPacket class.

I am getting different values of EXPECTED_TYPE and EXPECTED_BEACON_TYPE for the pro type of beacon. I don't know if Kontakt change something in these beacons or not.

I used also (aruba, estimote, averos, standard kontakt) and every beacon worked fine except the Kontakt pro beacon.

I guess the problem that some companies who provide beacons, change some bits from the advertising packets to add like battery life, led and sensors. So we cannot just test the ibeacon if it meets specification in a static way.

 public static boolean meetsSpecification(byte[] data) {
        if (data == null || data.length < 29) {
            return false;
        }
        if (getTypeBytes(data) != EXPECTED_TYPE) {
            return false;
        }
        if (!Arrays.equals(getFlagsBytes(data), EXPECTED_FLAGS)) {
            return false;
        }
        if (!Arrays.equals(getBeaconTypeBytes(data), EXPECTED_BEACON_TYPE)) {
            return false;
        }
        return true;
    }

Thank you

Deal with negative indices in ColorUtil

Use modulo instead of the remainder in:

@ColorInt
public static int getBeaconColor(int beaconIndex) {
    int colorIndex = 18 + (beaconIndex * 2);
    colorIndex %= MATERIAL_DESIGN_COLOR_RESOURCE_IDS.length;
    return getInstance().materialDesignDarkColors[colorIndex];
}

Related to #86

Beacons are passed to filters regardless of their generic types

07-16 13:49:37.565 W/NearbyDevicesManager: Device scanning failed
    java.lang.ClassCastException: com.nexenio.authenticator.accesscontrol.GateEntranceDetectionBeacon cannot be cast to com.nexenio.authenticator.accesscontrol.GateDetectionBeacon
        at com.nexenio.authenticator.accesscontrol.GateDetection$1.onMatchingBeaconUpdated(GateDetection.java:93)
        at com.nexenio.bleindoorpositioning.ble.beacon.FilteredBeaconUpdateListener.onBeaconUpdated(FilteredBeaconUpdateListener.java:25)
        at com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager.notifyBeaconUpdateListeners(BeaconManager.java:102)
        at com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager.processAdvertisingPacket(BeaconManager.java:79)
        at com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager.processAdvertisingData(BeaconManager.java:55)
        at com.nexenio.authenticator.networking.bluetooth.NearbyDevicesManager.onScanResult(NearbyDevicesManager.java:213)
        at com.nexenio.authenticator.networking.bluetooth.NearbyDevicesManager.lambda$startScanning$2(NearbyDevicesManager.java:147)
        at com.nexenio.authenticator.networking.bluetooth.-$$Lambda$NearbyDevicesManager$3z2AY2Ysie8a5mXIbx0mQPopEkY.accept(lambda)
        at io.reactivex.internal.observers.LambdaObserver.onNext(LambdaObserver.java:63)
        at io.reactivex.internal.operators.observable.ObservableDoFinally$DoFinallyObserver.onNext(ObservableDoFinally.java:82)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.tryEmit(ObservableFlatMap.java:266)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$InnerObserver.onNext(ObservableFlatMap.java:570)
        at io.reactivex.internal.operators.observable.ObservableMap$MapObserver.onNext(ObservableMap.java:64)
        at io.reactivex.internal.operators.observable.ObservableUnsubscribeOn$UnsubscribeObserver.onNext(ObservableUnsubscribeOn.java:60)
        at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onNext(ObservableCreate.java:67)
        at com.polidea.rxandroidble2.internal.serialization.FIFORunnableEntry$1.onNext(FIFORunnableEntry.java:60)
        at io.reactivex.internal.operators.observable.ObservableUnsubscribeOn$UnsubscribeObserver.onNext(ObservableUnsubscribeOn.java:60)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:58)
        at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onNext(ObservableCreate.java:67)
        at com.polidea.rxandroidble2.internal.operations.ScanOperationApi21$1.onScanResult(ScanOperationApi21.java:63)
        at android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper$1.run(BluetoothLeScanner.java:694)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6157)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:926)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:788)

TooManyEvaluationsException: illegal state: maximal count (1,000) exceeded: evaluations

2018-06-06 09:14:24.253 19160-19160/com.nexenio.bleindoorpositioning E/BluetoothClient: Bluetooth scanning error
    org.apache.commons.math3.exception.TooManyEvaluationsException: illegal state: maximal count (1,000) exceeded: evaluations
        at org.apache.commons.math3.optim.AbstractOptimizationProblem$MaxEvalCallback.trigger(AbstractOptimizationProblem.java:85)
        at org.apache.commons.math3.util.Incrementor.incrementCount(Incrementor.java:157)
        at org.apache.commons.math3.fitting.leastsquares.LevenbergMarquardtOptimizer.optimize(LevenbergMarquardtOptimizer.java:445)
        at com.lemmingapex.trilateration.NonLinearLeastSquaresSolver.solve(NonLinearLeastSquaresSolver.java:41)
        at com.lemmingapex.trilateration.NonLinearLeastSquaresSolver.solve(NonLinearLeastSquaresSolver.java:80)
        at com.lemmingapex.trilateration.NonLinearLeastSquaresSolver.solve(NonLinearLeastSquaresSolver.java:88)
        at com.nexenio.bleindoorpositioning.location.multilateration.Multilateration.findOptimum(Multilateration.java:65)
        at com.nexenio.bleindoorpositioning.location.multilateration.Multilateration.findOptimum(Multilateration.java:58)
        at com.nexenio.bleindoorpositioning.location.multilateration.Multilateration.getOptimum(Multilateration.java:123)
        at com.nexenio.bleindoorpositioning.location.multilateration.Multilateration.getLocation(Multilateration.java:109)
        at com.nexenio.bleindoorpositioning.IndoorPositioning.updateLocation(IndoorPositioning.java:93)
        at com.nexenio.bleindoorpositioning.IndoorPositioning.onBeaconUpdated(IndoorPositioning.java:73)
        at com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager.notifyBeaconUpdateListeners(BeaconManager.java:102)
        at com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager.processAdvertisingPacket(BeaconManager.java:79)
        at com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager.processAdvertisingData(BeaconManager.java:55)
        at com.nexenio.bleindoorpositioningdemo.bluetooth.BluetoothClient.processScanResult(BluetoothClient.java:126)
        at com.nexenio.bleindoorpositioningdemo.bluetooth.BluetoothClient.access$100(BluetoothClient.java:28)
        at com.nexenio.bleindoorpositioningdemo.bluetooth.BluetoothClient$1.onNext(BluetoothClient.java:92)
        at com.nexenio.bleindoorpositioningdemo.bluetooth.BluetoothClient$1.onNext(BluetoothClient.java:79)

Get rid of old beacons

Beacons that haven't been seen for a while should automatically be removed from BeaconManager.beaconMap to not fill up the memory.

Add maxAcceptableRssi when filtering usableBeacons and change rootMeanSquareThreshold type to double

The code below remove all the beacons with rssi < -70

 for (int beaconIndex = usableBeacons.size() - 1; beaconIndex >= 3; beaconIndex--) {
                if (usableBeacons.get(beaconIndex).getFilteredRssi() < -70) {
                    usableBeacons.remove(beaconIndex);
                }
}

I have added maxAcceptableRssi variable with getter and setter in case the developer want to test another value.

Also in the same file rootMeanSquareThreshold should be double since multilateration.getRMS() return a double value.

Check this PR.

Snapshot building failed after merge

/home/jitpack/build/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/location/distance/BeaconDistanceCalculator.java:16: error: no tag name after @
* @ see
^

Parse Advertising Data is error

I saw IBeaconAdvertisingPacket class. I detected wrong parsing data such as: getTypeBytes return data[4], but I try parsing return data[1]

Using MacAddress instead of minor to get location

Hello,

There is a TODO to get the location from advertising packets, can you explain how you guys can make that happen if iBeacon broadcasts only one advertising packet which has a unique ID number comprising of three parts - UUID, Major, and Minor.

The sample shows that every beacon get the location depending on the minor, but the value of the minor can be changed from any smartphone.

Isn't better to use the Mac Address instead of the minor for now ? Like a HashMap.

public class IBeaconLocationProvider<B extends IBeacon> extends BeaconLocationProvider<B> {

    public IBeaconLocationProvider(B beacon) {
        super(beacon);
    }

    @Override
    public void updateLocation() {
        //AdvertisingPacket advertisingPacket = beacon.getLatestAdvertisingPacket();
        //location = new Location();
        // TODO: get location from advertising packets
    }
}

Location Accuracy Issue

Hello,

Thank you, for supplying us with this open source indoor position solution.

We have been working on your solution for a few days and were able to reach the users locations using the beacons; however, there seems to be some accuracy issues, as we are getting many location responses that are varying in location even though we are not moving.

We are currently using 4 beacons supplied by Kontakt, (2 Pro and 2 standard). Each beacon has been set with a TX power of -8dBm.

Also note that we have set the scanning mode of the BLE to SCAN_MODE_LOW_POWER.

See below image, which is a screenshot of our region surrounded by beacons (A, B, C, and D) which are mounted on columns at a height of about 2.5m, also note that the items in the middle are benches found in the area and not columns that might possibly interfere with the beacons signals.

We have set the beacons locations exactly from our maps coordinates, that have also been used in our code.

  • 4 beacons, 6.5 meters distance between beacons.
  • TxPower: -8 dBm.
  • IndoorPositioning / UPDATE_INTERVAL_MEDIUM
  • Using our own custom map.
  • Testing with Samsung Galaxy S8

screenshot_20180605-092001_kaindoorlocationsdkandroid

This is a screenshot showing the right recordings for our location by the beacons.

20180605_095208

We have also attached a video screen recording of our sample application showing the location error, and dots (our location) vastly varying in the whole map.

Thank you in advance for your help.

Unable to plot points

I am not able to get locations.

Only Getting the following values :
AndroidLocationProvider: Received location result with 1 locations
Last known location set to : Latitude: 30.722795 Longitude: 76.7677966

Stabilize multilateration results

Implement a solution to make the multilateration results more stable while keeping the delay low.
Position changes should be realistic (not 'jumping around') and probably consider the movement speed.

Add getter and setter for ROOT_MEAN_SQUARE_THRESHOLD

The current code inside updateLocation() is like this

 if (multilateration.getRMS() < ROOT_MEAN_SQUARE_THRESHOLD_STRICT) {
            locationPredictor.addLocation(location);
            onLocationUpdated(getMeanLocation(2, TimeUnit.SECONDS));
        }

The user should choose between threshold : ROOT_MEAN_SQUARE_THRESHOLD_STRICT, ROOT_MEAN_SQUARE_THRESHOLD_MEDIUM, ROOT_MEAN_SQUARE_THRESHOLD_LIGHT

Check the PR here

Add altitude relative to floor to beacons

Currently, in Beacon.calculateDistanceWithoutAltitudeDeltaToFloor(), the location altitude is used. However, that property should hold the elevation above sea level, not above floor level.

App crash first time location appear

Before having this bug, i was handling all the erros in this function of RxBle library :

override fun onError(e: Throwable?) {
    Log.e(e?.message.toString())
}

Now i have removed this function, and i am subscribing only on the onNext() function of the observable, so all the errors will stop the app ( there is no error handling ).

That's why this problem hasn't appear before, it only appeared in the Logcat.

It seems that the app crash the first time the location is calculated, the library try to get the mean of last 2 seconds locations, but there is one location only so the returned value is null.

I am receiving the error showed below, the location is null so there is no .getAltitude() method => no distance calculated.

I have fixed this error by checking the meanLocation if null or not before pass it to the listener, check this PR.

screen shot 2018-07-16 at 12 50 57 pm

screen shot 2018-07-16 at 12 48 28 pm

ConcurrentModificationException in WindowFilter

Caused by: java.util.ConcurrentModificationException
   at java.util.ArrayList$Itr.next(ArrayList.java:831)
   at com.nexenio.bleindoorpositioning.ble.beacon.Beacon.getAdvertisingPacketsBetween(Beacon.java:73)
   at com.nexenio.bleindoorpositioning.ble.beacon.signal.WindowFilter.getRecentAdvertisingPackets(WindowFilter.java:46)
   at com.nexenio.bleindoorpositioning.ble.beacon.signal.KalmanFilter.filter(KalmanFilter.java:57)
   at com.nexenio.bleindoorpositioning.ble.beacon.Beacon.getRssi(Beacon.java:143)
   at com.nexenio.bleindoorpositioning.ble.beacon.Beacon.getDistance(Beacon.java:155)
   at com.nexenio.bleindoorpositioning.ble.beacon.Beacon.getDistance(Beacon.java:151)

Optimize Beacon.getAdvertisingPacketsBetween()

During performance profiling, I noticed that one of the few methods with high impact (besides beacon update listeners) is Beacon.getAdvertisingPacketsBetween().

The current implementation can certainly be improved, e.g. by avoiding all the 'ArrayList.add()' calls:

public List<P> getAdvertisingPacketsBetween(long startTimestamp, long endTimestamp) {
    List<P> matchingAdvertisingPackets = new ArrayList<>();
    for (P advertisingPacket : new ArrayList<>(advertisingPackets)) {
        if (advertisingPacket.getTimestamp() < startTimestamp) {
            continue;
        }
        if (advertisingPacket.getTimestamp() >= endTimestamp) {
            continue;
        }
        matchingAdvertisingPackets.add(advertisingPacket);
    }
    return matchingAdvertisingPackets;
}

profiling-screenshot

Current Location Accuracy

Hi, firstly thank you so much for your effort for such a good project. We all have chance to see the implementations of several calculations and algorithms all together.

I have pretty tested the library and app in a real office environment with the beacons installed. Here I have some test results and comments;

Although it was quite difficult to determine the coordinates of the beacons, In the first scenario I have tested with TxPower=0 dBm which is maximum range advertising ~50 m. Each beacon has 7 m distance and 12 beacons installed in total. Mostly showing the right location when I move but sometimes current location dot moves out of the region or to other corner etc. I assumed maybe it shows wrong location on the view but also checked the locations on multilateration result and saw some deviations. Another point that caught my attention is possible wrong calculation of edge locations.

In the second scenario, I have tested by setting TxPower=-20 dBm which supposed to advertise in 4 m range but test is not really successful maybe because of some other needed configurations. Also no good result when I played with deviceAdvertisingRange parameter in BeaconMap(Even the same when I set 50 m for the first scenario).

However, I see that you convert cartesian coordinates to spherical coordinates. Do you confirm that the convert is totally gives the same successful location calculation according to cartesian?

Additionally, I appreciate if you have any other configuration suggestions?

Thank you,
Alper

Ignore locations outside the route

Hello,

Below an image from google, let's suppose that the black line is our route, is there any plan to filter the coming locations based on the route lines ?

In this way we will get an exact location with less errors, if we have a tree in a mall or an airport, i don't wanna show to the user that he is on top of the tree instead i want to show only locations on that line.

Thank you in advance

image

Add pathLossParameter getter and setter

Hello,

The below code doesn't allow us to change the path loss parameter, the building type is

Office with hard partition

by default.

public static float calculateDistanceTo(Beacon beacon, float rssi) {
        return calculateDistance(rssi, beacon.getCalibratedRssi(), beacon.getCalibratedDistance(), PATH_LOSS_PARAMETER_OFFICE_HARD_PARTITION);
}
Building Type Frequency of Transmission γ {\displaystyle \gamma } \gamma σ {\displaystyle \sigma } \sigma [dB]
Vacuum, infinite space 2.0 0
Retail store 914 MHz 2.2 8.7
Grocery store 914 MHz 1.8 5.2
Office with hard partition 1.5 GHz 3.0 7
Office with soft partition 900 MHz 2.4 9.6
Office with soft partition 1.9 GHz 2.6 14.1
Textile or chemical 1.3 GHz 2.0 3.0
Textile or chemical 4 GHz 2.1 7.0, 9.7
Office 60 GHz 2.2 3.92
Commercial 60 GHz 1.7 7.9

I added getter and setter methods to BeaconDistanceCalculator class, check this PR

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.