all 89 comments

[–]stinyg 36 points37 points  (20 children)

First time I see it and I must say it looks pretty cool.

Can you say something regarding the limitations. Would it for instance work in an office with multiple wifi senders (note I have quite limited knowledge of what's going on when I connect to wifi)?

[–]pvkooten[S] 33 points34 points  (19 children)

It does not actually connect. It only "scans" to find out how good the connection is to each access point. It's the step that happens when you switch on your laptop and the wifi starts scanning for which network/access point to connect to. We get the quality per network. The combination of a network being there (or not) and the quality (signal strength) are the only features we need (given there are usually many many access points around). It works for me not only at home with 7 access points, it also works at a bank with 100+ access points.

[–]folkrav 16 points17 points  (13 children)

With that in mind, would it work even better when there are more APs around you?

[–]p00b 9 points10 points  (10 children)

I would imagine it depends on how spread out they are. As an extreme example, I'd imagine if there were 100 APs stacked vertically they would not perform as well as 1 in the center of the room and 99 spread in a circle concentric with the room.

Depending on the internal logic, I'd also imagine that there's a point where the incremental benefit of adding another AP is canceled out by the computational (time/energy) cost in detecting/processing it.

[–]pvkooten[S] 11 points12 points  (9 children)

Actually, as I've written I believe in the README, it is amazing that vertical spread even seems to be a better discriminator than horizontal spread!

[–]p00b 4 points5 points  (8 children)

Wow, that's super interesting. Any theory as to why?

[–]pvkooten[S] 4 points5 points  (0 children)

It's all about how much signals are being "blocked". In open/horizontal space, signals are much more similar. That'd be my guess :-)

[–]w0lrah 2 points3 points  (3 children)

Most wireless APs use antennas that produce a signal pattern that roughly resembles a stretched donut. Directly above or below them the signal falls off a lot faster than to the sides.

As a general rule most "omnidirectional" antennas produce a similar pattern, the higher the gain the more horizontally stretched it'll be.

[–]p00b 0 points1 point  (2 children)

Cool, yeah I had that sense but didn't find any good source for a visualization of the signal. Do you know of anything that backs up what you're saying?

[–]w0lrah 0 points1 point  (1 child)


The first image is an example of typical directional and omnidirectional radiation patterns. Most antennas will emit a signal pattern resembling one of these two.

[–]p00b 0 points1 point  (0 children)

Cool, thank you!!

[–]larsga 0 points1 point  (2 children)


[–]p00b 0 points1 point  (1 child)

Rebuttal: Walls?

[–]larsga 0 points1 point  (0 children)

Usually a lot thinner, and a lot less dense, than floors.

[–]pvkooten[S] 2 points3 points  (1 child)

Certainly! It should. Though I still suspect there might be some wrong overfitting. Tomorrow I'll try again in a big organization and see if I can get a reliable 2 meter classification.

[–]FractalNerve 1 point2 points  (0 children)

You can add SVM based Accelerometer data and also Airpressure data to detect height, direction and identity and it increases indoor navigation precision too. I can provide you with a paper that actually does all that, if you're interested.

[–]derpderp3200An evil person 2 points3 points  (1 child)

What's the minimum it needs to work? I don't suppose 2 sources would be enough, would they?

[–]pvkooten[S] 2 points3 points  (0 children)

2 locations? Surely works. The kitchen vs bedroom example works. As for minimum amount of training examples, maybe 10, so 10 seconds of training on linux and on my macbook 30 seconds.

[–]LHoT10820 1 point2 points  (2 children)

This would probably work really well on 60GHz Wifi since signal reflection is much more predictable.

[–]pvkooten[S] 1 point2 points  (1 child)

Sounds interesting.... no experience with it!

[–]LHoT10820 1 point2 points  (0 children)

You need a shiny, new, expensive router for the non-wall-penetrating experience.


[–]chipaca 23 points24 points  (9 children)

  1. it depends on scipy and numpy, and pip install whereami doesn't pull those in. Add them to your dependencies?

  2. this:

    $ whereami
    Traceback (most recent call last):
      File "/home/john/src/whereami/env/bin/whereami", line 11, in <module>
      File "/home/john/src/whereami/env/lib/python3.5/site-packages/whereami/__main__.py", line 9, in main
        if "predict_proba" == sys.argv[1]:
    IndexError: list index out of range
  3. this:

    $ whereami predict_proba
    Traceback (most recent call last):
      File "/home/john/src/whereami/env/lib/python3.5/site-packages/whereami/predict.py", line 9, in predict_proba
        print({x: y for x, y in zip(lp.clf.classes_, lp.predict_proba(sample())[0])})
    AttributeError: 'Pipeline' object has no attribute 'clf'
  4. if you resize the terminal while it's learning, you get

    $ whereami learn desk 20
     40%|███████████████████████▏                                  | 8/20 [00:10<00
     45%|██████████████████████████                                | 9/20 [00:14<00
     50%|████████████████████████████▌                            | 10/20 [00:15<00
     55%|███████████████████████████████▎                         | 11/20 [00:15<00
     60%|██████████████████████████████████▏                      | 12/20 [00:16<00
     65%|█████████████████████████████████████                    | 13/20 [00:17<00
     70%|███████████████████████████████████████▉                 | 14/20 [00:21<00
     75%|██████████████████████████████████████████▊              | 15/20 [00:22<00
     80%|█████████████████████████████████████████████▌           | 16/20 [00:22<00
     85%|████████████████████████████████████████████████▍        | 17/20 [00:23<00
    100%|█████████████████████████████████████████████████████████| 20/20 [00:29<00:00,  1.57s/it]

[–]pvkooten[S] 5 points6 points  (2 children)

Thanks once more! I fixed predict_proba. Try pip install -U --no-cache whereami and please try again :)

[–]chipaca 4 points5 points  (1 child)

better! almost there:

    $ whereami predict_proba
    {'desk': 0.46999999999999997, 'bed': 0.53000000000000003}
    $ whereami crossval
    Traceback (most recent call last):
      File "bin/whereami", line 11, in <module>
      File "lib/python3.5/site-packages/whereami/__main__.py", line 36, in main
      File "lib/python3.5/site-packages/whereami/predict.py", line 18, in crossval
        X, y = get_train_data()
      File "lib/python3.5/site-packages/whereami/get_data.py", line 27, in get_train_data
    ValueError: I/O operation on closed file.

[–]pvkooten[S] 7 points8 points  (0 children)

Pull request seemed okay, but there was an issue. Now fixed. pip install -U --no-cache whereami and it should be solved. Actually, you might have to check ~/.whereami/bed.txt and ~/.whereami/desk.txt to see if some newlines are missing. Or just to delete these files and train from scratch. My apologies!

[–]pvkooten[S] 6 points7 points  (5 children)

  1. I think numpy and scipy are included as requirements of scikit-learn? Did they not automatically get pulled in?

  2. Heh, yea I guess "whereami" without anything should probably call "whereami predict" or show help. I guess the latter is better?

  3. This one is new to me. Did you whereami learn the model before? Update: I noticed it crashed now! I will fix it asap.

  4. I guess this is a problem with tqdm. I haven't noticed it. Perhaps post an issue on tqdm?

[–]Gagaro 8 points9 points  (4 children)

You should add them as requirements if you import them yourself in your project. Don't rely on others to do it for you.

The minimum is to show usage information when invoking a command without arguments.

[–]pvkooten[S] 5 points6 points  (0 children)

I added the argument information using argparse. It was on the todo list :) Please let me know if you feel like something else is missing.

[–]pvkooten[S] 1 point2 points  (2 children)

I added them in the requirements.txt, not sure if it has any effect though.

[–]TheBB 4 points5 points  (1 child)

They should be in your setup.py file.


Ouch, yea now it clicked. Though still somehow I would have expected them to be pulled in automatically :S

[–]GrabAHamLincoln 14 points15 points  (7 children)

Using signal strengths of public wifi points for localization is actually a pretty clever idea. It wouldn't work well in the middle of a forest, but for most home applications, it'll be faster and more reliable than GPS or image processing.

[–]monkeybreath 21 points22 points  (6 children)

That's what smartphones do. It is why the OS recommends you have wifi on for location services: it can get a rough fix immediately, whereas it takes several seconds to get a GPS fix. The OS keeps a cache of local access points, created by companies like Skyhook, who drive around cities getting the info.

[–]Eurynom0s 3 points4 points  (0 children)

IIRC it's longer than a few seconds for GPS to get you down to an exact location. So using wifi location is speeding things up by way more than a few seconds. (I think it may also be used to spare your battery life from turning the GPS on if you're just using functions like "what stores are nearby" that don't require the same level of accuracy as driving navigation.)

[–]Zouden 0 points1 point  (3 children)

Smartphones just look at the nearby access point names, they don't take it further and use signal strength like this does.

[–]monkeybreath 2 points3 points  (2 children)

iOS uses signal strength to determine position. That's why location services work indoors. They get their list with positions of access points from Skyhook. Just having a list of names does you nothing.

[–]Zouden 0 points1 point  (1 child)

Oh TIL. Is that a new thing? I don't think Android does that. The wifi location is only accurate to 30m or so.

[–]monkeybreath 0 points1 point  (0 children)

Since the first or second generation of iPhone, I think. Though I don't think Apple was first to use it. Wifi signals vary widely as you move around, so it won't be very accurate, unless you are doing detailed mapping like OP is. But it gets you in the ballpark, which is helpful for finding local businesses , or to jumpstart the calculations for GPS, such as predicting which satellites should be visible.

Edit: iOS 1.1.3 to 3.1 used Google and Skyhook services, but took it in house in 2010. https://techcrunch.com/2010/07/29/apple-location/

[–]GrabAHamLincoln 0 points1 point  (0 children)

I knew cell phones use cell tower signals for localization, but I didn't know they also used wifi signals. Interesting.

[–]Whoopska 10 points11 points  (2 children)

Code review!

  1. Any reason to use pickle instead of joblib for the models? I see it in a few places, but here is one.

  2. Doesn't this train the model on the test data after the first loop? If you look at sklearn.cross_val_sore you'll see it clones the predictor. I think you could get away with just return np.mean(cross_val_score(pipeline, cv=n))

  3. looks like just print(dict(zip(...))) can be used here

  4. json.dump can take a file pointer here

  5. I don't think get_model and get_pipeline are good logical distinctions. They both return the same thing, one trained and stored on disk and the other untrained. A model and pipeline deliberately have similar interfaces so you don't have to care which one you're working with. Maybe something like make_model and load_model would explain what the functions do more.

Very cool project! I'm definitely going to be using this with something like arbtt if the cross_val_score doesn't end up being a big deal to the performance. Thanks for citing your sources too!

[–]pvkooten[S] 5 points6 points  (1 child)

  1. I'm not sure, I thought joblib might be an external module. And "because I've always used pickle". But yea, maybe it's a good moment to switch.

  2. The fit simply refits the whole model, so actually it starts from scratch. Thus, I don't believe there is any leakage. I'll look into cross_val_score and perhaps the accuracy metric of sklearn, it should indeed be nicer code (and no need for numpy import)!

  3. Awesome! Yea, I don't get any chance to refactor anything with you guys pointing out everything :D Thanks!

  4. Hah. good point! You could have just pull requested it, even those small ones I'd be happy to include.

  5. Very good point.

And I'm going to check out arbtt. Thank you very much for the code review, they're great points!

[–]Whoopska 1 point2 points  (0 children)

2. Ah neat. I had stepped through the clone before and figured it was to reset the data, but I guess it's probably for parallelization safety

[–][deleted]  (3 children)


    [–]m0skit0d3lt4 3 points4 points  (2 children)

    You should be able to port it to android

    [–]NasKe 1 point2 points  (1 child)

    I might try it with Kivy later on this week.


    Have a look at https://gitter.im/schollz/find, he already has an app. As I mention in my previous post, the author of that framework started in Python (I did a few commits) but he switched to Go and made it a big project. I just wanted to make sure we also have a version just in python.

    [–]TotesMessenger 7 points8 points  (0 children)



    [–]Burner842 14 points15 points  (0 children)

    Nice try, FBI.

    [–]wickerwaka 2 points3 points  (1 child)

    Is the prediction data usable across devices or do I need to train it for each device to account for antenna and other hardware differences?


    I noticed my phone did get a bit different data. But I did not actually test whether it is in a "biased" way: it could still be that even though the data is a bit different, it will still be correctly categorized. The author of schollz/find claims that he can train on one device and use it on others, so I assume it "just" works.

    [–]minorsecond 2 points3 points  (2 children)


    whereami learn -l office -n 100


    ValueError: Found array with 0 feature(s) (shape=(100, 0)) while a minimum of 1 is required.

    [–]pvkooten[S] 1 point2 points  (0 children)

    Do you only have 1 access point? access_points -n. Please make a github issue :)

    [–]mafrasi2 0 points1 point  (0 children)

    access_points needs the command iwlist, which is in the package wireless_tools on Arch. If you're using Arch, maybe try installing this.

    [–]Gr1pp717 1 point2 points  (1 child)

    Any plans/ways to add a method other than polling? It would be neat if as I moved it changed where it thought I was. Could be useful for tying my phone to an internet of things. e.g. turn on lights as I approach a room with the phone.

    [–]pvkooten[S] 3 points4 points  (0 children)

    You could just add it in a cron job so that you have it test every 1-5 minutes. It's not possible for it to detect motion; to do that it would have to be constantly scanning. Maybe we can use another sensor. But indeed, I hope people will use it for home automation :)

    [–]thesamovar 1 point2 points  (4 children)

    Great idea! Sadly, it makes me realise my apartment is too small: always picks the same choice (because of very low crossval scores I guess).

    [–]loftykoala 1 point2 points  (0 children)

    I'm curious, what are your crossval scores, and how many networks are being picked up?

    I'm in a 2 BR apt and I'm seeing ~22 networks picked up, and I'm getting a crossval score of 0.95+.

    However, the prediction still seems biased towards either my kitchen or my bedroom. The other two locations I trained are my bathroom and living room, with each spot 3+ meters away from each other.

    Given the comment in the README.md about distinguishing between couch 1 and couch 2, I was hoping it would learn those four rooms...


    run access_points -n, how many are there? also try it on several different occassions and more samples, it might still end up working!

    [–]thesamovar 0 points1 point  (1 child)

    9 APs which feels like it ought to be enough? I used -n 100 as in the docs - what would be a better number?

    [–]pvkooten[S] 0 points1 point  (0 children)

    Well, try switching location and take another few times 100 samples. Experiment. I'd be surprised if it wouldn't work. If it doesn't work, feel free to send me the data so I could try to figure out what is wrong.

    [–]sensual_rustle 1 point2 points  (1 child)

    I was hoping this was r/outside


    When the title says indoors ;P?

    [–][deleted]  (1 child)



      Probably not as wifi is something that needs native capabilities (or cordova). I've seen cordova being used though to do this in an ionic app.

      [–]Kohonen 1 point2 points  (0 children)

      Wow, this is really neat. Congratulations!

      [–]FirstKitchen 1 point2 points  (0 children)

      This could be nice for finding where reports are coming from. Say for safety reasons.

      [–]stickystyle 1 point2 points  (1 child)

      Well damn, now my day is ruined as I'll be out in our warehouse looking like the crazy IT guy taking measurements everywhere instead of doing QA for our new software release.

      I hope it's worth it OP ;-)


      Haha, I certainly hope so too :-) Do let me know! Taking measurements surely "sounds" important.

      [–]cyanydeez 2 points3 points  (5 children)

      what'd be nice is a client/server architecture that could tell me where anyone of my devices is.

      [–]Ran4 15 points16 points  (1 child)

      That really shouldn't be part of this program though. Do one thing and do it well... and that precisely seem to be what this program is doing :)

      Really cool OP.


      Thanks! Indeed. I went with the unix philosophy by splitting access_points. It feels right :)

      [–]fisadev 2 points3 points  (2 children)

      If you are using linux in both devices (the lost device and the other device from where you want to query the location), you already have that. You only need to activate ssh access, and then you can do:

      ssh lost_device whereami predict

      [–]chaos777b 2 points3 points  (0 children)

      Reversing the logic if you had multiple passive sensors you could peridot the location of the connecting device with out accessing it.

      [–]crazyfreak316 1 point2 points  (0 children)

      To add to that you can use ansible (or saltstack) and do it on multiple devices very easily.

      [–]TonyF66 4 points5 points  (7 children)

      It is clever that it works - and does stats analysis to predict the answer - but in general i tend to know which room i have carried my laptop too ;)


      It isn't useful for humans, but software. Some sort of portable device could always be polling and communicate room change events to other devices.

      [–]w_t 3 points4 points  (0 children)

      Also future applications like 911 calls in buildings (what floor\room are you calling from?), targeted location info in shopping malls, etc. Indoor location is a huge, burgeoning software problem.

      [–]soawesomejohn 13 points14 points  (2 children)

      Put this on a raspberry pi on top of a roomba. :D


      I like your spirit :- )


      I'm not denying this has a big factor of "because we can use machine learning" :D But indeed, it's so our computer will know. Imagine home automation systems being connected to your laptop or something.

      [–]TonyF66 0 points1 point  (0 children)

      I agree with all the points - I was being a bit flippant - see the wink emoticon at the end of my post.

      [–]Vulpius 0 points1 point  (7 children)

      This is for predicting where you are given the fact that you're holding the device collecting the quality strengths (at prediction-time), right? It's not for training a immobile device based on e.g. fluctuations in WiFi fields?

      Second: given the monotonic nature of the features, limited interactions effects and the fact that they're all numeric, have you tried using a simpler model such as a logistic regression (or even a linear svm would work well I guess)?

      Neat work!

      [–]pvkooten[S] 1 point2 points  (6 children)

      Hey, I don't exactly understand the first part of your question. Is there a way to rephrase it? Second: I indeed tried a simple logistic regression, and the quality was much much worse! 0.95 vs 0.99. An SVM might work quite well, but as I got 99% with a very quick randomforest, I did not search further.

      [–]Vulpius 0 points1 point  (3 children)

      Cool, thanks! The accuracy drop is not too low (in a business setting, I'd go for the logistic regression one as it's a lot more white box and also gives me a bit more ease-of-mind that I'd not be overfitting), but it makes sense here to go for the best one.

      Regarding my first question:

      What you are doing is training the model to take your device (a laptop, say), sit on couch 1, collect 100 samples, sit on couch 2, collect 100 samples, and so on. If you want to predict where I am, I need to be holding the laptop on couch X (X being where I am), right?

      There have been studies on trying to predict the location of objects based on fluctuations in signal patterns. I know the answer (I know that this is not what you're trying to do here), but at first I thought you had developed a model to predict my location based on these fluctuations, by using one (or theoretically more but one would be enough) base station. To train, I'd sit in couch 1, and the base station would take 100 samples, I'd sit in couch 2, and the base station would sample again. Based on some features (I wouldn't know how to do this) obtained by inspecting fluctuations, you could then use this to predict where I am.

      However, it might be that the fluctuations caused by my position is enough the influence the quality strenghts you're getting from base stations, so that you're approach would continue to work even if the laptop (the measurer) stays fixed. Basically, my question boils down to: do I have to hold the laptop? If not, that would be very cool given your high accuracy scores...


      I'm with you. Just "RandomForest" sounds a lot cooler than logistic regression ;) I still had to read a couple of times what you meant, but I think I get it. You think the laptop is in a fixed spot, and whenever I move around, by the way I obstruct the wifi, the station would know where I am. Actually, that's a very cool idea. I think that if we would have a lot of training data and perhaps 4 laptops/stations producing this data continuously, then it might be possible to understand where I am. Obstructions have a very strong effect. But I guess it would all be very dependent on where the access points are. The stations have to be well placed. I think a phone uniquely representing someone might be the easier way.

      [–]Vulpius 0 points1 point  (1 child)

      That's exactly what I mean, yup. The approach you use sounds like this paper: https://www.researchgate.net/publication/220625904_An_Analysis_of_Wi-Fi_Based_Indoor_Positioning_Accuracy

      Whereas the alternative is more like this:


      Would be cool to try if you say obstruction does have a strong effect...


      Btw, I think there came many more access points over the years, so I guess that's very beneficial.

      [–]loftykoala 0 points1 point  (1 child)

      Would be interesting to see how different models perform in different situations (e.g., office with dense wifi coverage vs house with sparse wifi coverage). If I am able to carve out some time perhaps I'll start with logistic or SVM then send you a pull request.


      I'd be very welcoming.

      [–]GrabAHamLincoln 0 points1 point  (1 child)

      I just tested this, and a big caveat is sample size. To get a reliable result, you need to sample each region equally.

      I first sample my livingroom with 100 iterations, but that took forever, so I sampled other places with only 10 iterations. However, the random forest algorithm then thought all locations looked like my livingroom. When I resampled everywhere with 100 iterations, then accuracy was better, but still not great, with crossval only showing 0.41.

      Looking at my data files in ~/.whereami, most of the lines are:


      It looks like of the 100 samples taken, only about 10% actually successfully read wifi scans. Is that normal?


      That's very strange. Could you perhaps do some manual samples and see what happens?