Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to Prototype an Airport

How to Prototype an Airport

I transformed my office building into an airport using a combination of Ruby, Objective-C, and a number of hardware hacks. The goal was to prototype a better in-airport experience using a mixture of GPS and iBeacon technology to establish precise indoor geolocation.

This talk discusses outdoor and indoor location, and how the iPhone (and Android) devices currently handle location and proximity. I’ll also discuss how I built a robust Ruby backend, and how I hacked some hardware together to create iBeacons.

Barrett Clark

March 20, 2014
Tweet

More Decks by Barrett Clark

Other Decks in Programming

Transcript

  1. Airport Day of Travel Experience Breaks / Possible Sensor /

    Biometric / Tap-In Points 0.0 Home 1.0 Exterior Baggage Check 1.1 Personal Checkin Line 1.2 Personal Checkin Counter 1.3 Machine Checkin 2.0 Security Line Beginning 2.1 Security Line Check 3.0 Post Security 3.1 Concessionaires 3.2 Flight Boards 4.0 Gate Counter 4.1 Gate Seating 4.2 Boarding 5.0 On Plane 6.0 Gate Exit 2.0 2.1 3.0 1.1 1.3 4.1 3.1 5.0 0.0 1.0 1.3 1.2 2.0 2.1 3.0 3.1 3.2 4.2 4.0 5.0 6.0 1.0 1.3 1.2 2.1 3.1 4.2 4.0 5.0 6.0
  2. –You, every time you fly How long will it take

    to get through security? When should I leave for the airport?
  3. Geolocation • Accurate within feet • Store and query easily

    • Not difficult to setup/host • Combination of GPS, Cellular, and WiFi Access Point http://www.flickr.com/photos/trooper3d/707407034/
  4. Indoor Location • WiFi AP (satellite not visible) • Third

    Axis (elevation) important • Android can get more information from access points without connecting
  5. Bluetooth Buzzwords • Bluetooth v4.0/v4.1 • The latest version of

    the standard • Bluetooth Low Energy (BLE) • A subset of v4.0 • iBeacon • An implementation of BLE
  6. #pragma mark Location Manager Delegate methods - (void)startStandardUpdates { //

    Create the location manager if this object does not // already have one. if (nil == locationManager) locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; locationManager.desiredAccuracy = kCLLocationAccuracyBest; // Set a movement threshold for new events. locationManager.distanceFilter = 10; // meters [locationManager startUpdatingLocation]; } Register For Location Updates
  7. - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { ! CLLocation* location =

    [locations lastObject]; NSDate* eventDate = location.timestamp; // Post to service (snipped) ! NSLog(@"lat %+.6f, lng %+.6f h accuracy %+.6f v accuracy %+.6f\n", location.coordinate.latitude, location.coordinate.longitude, location.horizontalAccuracy, location.verticalAccuracy); } Receive Location Updates
  8. #pragma mark iBeacon - (void) initializeBeacon { self.locationManager = [[CLLocationManager

    alloc] init]; self.locationManager.delegate = self; NSString *uuid = @"B8EF40B0-82D1-11E3-BAA7-0800200C9A66"; NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:uuid]; self.region = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID major:0 identifier:@"Sabre Airport"]; ! self.region.notifyEntryStateOnDisplay = NO; self.region.notifyOnEntry = NO; self.region.notifyOnExit = NO; [self.locationManager startMonitoringForRegion:_region]; } Look For Beacons
  9. - (void) locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region { switch (state)

    { case CLRegionStateInside: // state == 1 // NOTE: The given proximityUUID is visible [self.locationManager startRangingBeaconsInRegion:self.region]; break; case CLRegionStateOutside: // state == 2 case CLRegionStateUnknown: default: ; // stop ranging beacons, etc [self.locationManager stopRangingBeaconsInRegion:self.region]; } } Range Beacons
  10. - (void) locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region { //

    A proximity value of 1 or 2 is considered "IN" the given area if ([beacons count] > 0) { CLBeacon *beacon; for (CLBeacon *b in beacons) { // Top secret business logic } ! // Post to service (snipped) ! NSLog(@"Major: %@, Minor: %@, Proximity: %d, Acc: %f, RSSI: %ld", beacon.major, beacon.minor, beacon.proximity, beacon.accuracy, (long)beacon.rssi); } } You are here near
  11. Reading Collection Server • Write heavy • No enrichment •

    Return reading ID to native app • Publish reading (MQTT)
  12. class ReadingsController < ApplicationController skip_before_filter :verify_authenticity_token, :only => [:create] !

    def index render nothing: true end ! def create reading = Reading.create(reading_params) rescue nil if reading render json: reading.id else render nothing: true, status: 500 end end ! def show json_string = Rails.cache.read(params[:id]) || Reading.find(params[:id]).to_cache.to_json rescue nil render text: json_string end ! private ! def reading_params # list of the fields (snipped) params.require(:reading).permit(snipped) end end
  13. class Reading < ActiveRecord::Base validates_presence_of :latitude, :longitude, :accuracy, :unless =>

    :beacon_proximity_uuid? validates_presence_of :beacon_proximity_uuid, :beacon_major, :beacon_minor, :unless => :latitude? ! after_create :cache_reading after_create :publish_reading ! def to_cache { # Hash of the fields (snipped) } end ! private ! def cache_reading Rails.cache.write(self.id, self.to_cache.to_json) end ! def publish_reading $mqtt.publish('reading', self.to_cache.to_json) end end
  14. Location Server • PostGIS enabled • Subscribe to reading topic

    • REST call as backup to pub/sub • Tell native app what the reading means (locations)
  15. class Zone < ActiveRecord::Base has_many :zone_location_categories has_many :location_categories, :through =>

    :zone_location_categories ! def self.find_all_by_longitude_latitude(lng, lat) all. select([:id, :name]). where("ST_CONTAINS(fence,ST_GeomFromText('POINT(#{lng} #{lat})', 4326))") end ! def self.find_all_by_point(point) # SELECT * FROM my_spatial_table WHERE ST_Intersects(lonlat, polygon); all.select([:id, :name]).where(:fence => point) end ! def self.find_all_by_beacon(beacon_proximity_uuid, beacon_major, beacon_minor) all. select([:id, :name]). where(:beacon_proximity_uuid => beacon_proximity_uuid). where(:beacon_major => beacon_major). where(:beacon_minor => beacon_minor) end ! # snip end
  16. uri = URI.parse ENV['CLOUDMQTT_URL'] || 'mqtt://localhost:1883' conn_opts = { remote_host:

    uri.host, remote_port: uri.port, username: uri.user, password: uri.password, } ! Thread.new do MQTT::Client.connect(conn_opts) do |c| # The block will be called when a message arrives for the topic c.get('reading') do |topic, message| json = JSON.parse(message) reading = Reading.create!( # attributes (snipped) ) Rails.logger.debug "#{topic}: #{reading.id} : #{message}" end end end
  17. Prototyping The Airport • Geolocation for “where in the world

    am I?” • Proximity (iBeacon) for “where in the building am I?” • Rails backend to decipher what it all means • And most importantly….