├── adsb.ground.station
├── src
│ ├── main
│ │ ├── assembly
│ │ │ ├── filter.properties
│ │ │ └── sources.xml
│ │ ├── resources
│ │ │ ├── application.properties
│ │ │ └── log4j.properties
│ │ └── java
│ │ │ └── com
│ │ │ └── ibm
│ │ │ └── iot
│ │ │ └── adsb
│ │ │ └── ground
│ │ │ └── station
│ │ │ ├── IotClient.java
│ │ │ └── Flight.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── ibm
│ │ └── iot
│ │ └── adsb
│ │ └── ground
│ │ └── station
│ │ └── AppTest.java
├── images
│ └── rpi_sdr_config.jpg
├── Dockerfile
├── pom.xml
├── README-ja.md
└── README.md
├── assets
├── arview-weather.png
├── mapview-weather.png
├── architecture_diagram.png
└── architecture_diagram_v2.png
├── .travis.yml
├── ARFlightTracker-iOS-Swift
├── close.png
├── compass.png
├── ARFlightTracker-iOS-Swift
│ ├── box.png
│ ├── plane.png
│ ├── radar.png
│ ├── bubble.png
│ ├── radar_dot.png
│ ├── ARKit
│ │ ├── ARKit.h
│ │ ├── View
│ │ │ ├── ARObjectView.h
│ │ │ ├── RadarView.h
│ │ │ ├── ARObjectView.m
│ │ │ └── RadarView.m
│ │ ├── LocalizationDelegate.h
│ │ ├── ARViewDelegate.h
│ │ ├── Model
│ │ │ ├── ARGeoCoordinate.h
│ │ │ └── ARGeoCoordinate.m
│ │ ├── LocalizationHelper.h
│ │ ├── ARKitConfig.h
│ │ ├── ARKitConfig.m
│ │ ├── ARKitEngine.h
│ │ └── LocalizationHelper.m
│ ├── ARFlightAnnotation.swift
│ ├── IBMFlightTracker-Bridging-Header.h
│ ├── MarkerView.h
│ ├── FlightCalloutView.swift
│ ├── ARAnnotation.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon-2.appiconset
│ │ │ └── Contents.json
│ │ ├── AppIcon-3.appiconset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── MQTTConnection.swift
│ ├── ARFlightAnnotationView.swift
│ ├── ARAnnotationView.swift
│ ├── FlightAnnotation.swift
│ ├── TestFlights.json
│ ├── Info.plist
│ ├── FlightInfo.swift
│ ├── AppDelegate.swift
│ ├── MarkerView.m
│ ├── RestCall.swift
│ ├── ARFlightAnnotationCustom.swift
│ ├── ARFlightConfiguration.swift
│ ├── FlightBubble.swift
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── ARViewHelper.swift
│ ├── ViewController.swift
│ ├── ARFlightViewController.swift
│ ├── FlightCalloutView.xib
│ ├── ARFlightViewController_v1.swift
│ └── FlightMapViewController.swift
├── ARFlightTracker-iOS-Swift.xcodeproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
├── ARFlightTracker-iOS-Swift.xcworkspace
│ └── contents.xcworkspacedata
├── FlightBubbleV1.swift
├── Podfile
├── ARFlightTracker-iOS-SwiftUITests
│ ├── Info.plist
│ └── IBMFlightTrackerUITests.swift
├── ARFlightTracker-iOS-SwiftTests
│ ├── Info.plist
│ └── IBMFlightTrackerTests.swift
├── LICENSE
├── Assets.xcassets
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── .gitignore
├── README-ja.md
├── README.md
└── FlightBubbleV1.xib
├── images
└── arch-iot-airtrafficcontrol-1024x878.png
├── .gitignore
├── CONTRIBUTING.md
├── pom.xml
├── README-cn.md
├── README-ja.md
├── MAINTAINERS.md
├── README.md
└── LICENSE
/adsb.ground.station/src/main/assembly/filter.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/arview-weather.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/assets/arview-weather.png
--------------------------------------------------------------------------------
/assets/mapview-weather.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/assets/mapview-weather.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: Java
2 | jdk:
3 | - oraclejdk8
4 | install: mvn -Pbootstrap verify
5 | script: mvn verify
6 |
7 |
--------------------------------------------------------------------------------
/assets/architecture_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/assets/architecture_diagram.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/close.png
--------------------------------------------------------------------------------
/assets/architecture_diagram_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/assets/architecture_diagram_v2.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/compass.png
--------------------------------------------------------------------------------
/adsb.ground.station/images/rpi_sdr_config.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/adsb.ground.station/images/rpi_sdr_config.jpg
--------------------------------------------------------------------------------
/images/arch-iot-airtrafficcontrol-1024x878.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/images/arch-iot-airtrafficcontrol-1024x878.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/box.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/plane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/plane.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/radar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/radar.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/bubble.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/bubble.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/radar_dot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/air-traffic-control/master/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/radar_dot.png
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/ARKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARKit.h
3 | // ARModule
4 | //
5 | // Created by Carlos on 06/06/11.
6 | // Copyright 2011 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import "ARKitEngine.h"
10 | #import "ARObjectView.h"
11 |
12 |
--------------------------------------------------------------------------------
/adsb.ground.station/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | ## Mandatory fields
2 | Authentication-Method=apikey
3 | Organization-ID=
4 | API-Key=
5 | Authentication-Token=
6 | ## Device on behalf of which the application needs to publish events.
7 | Device-Type=
8 | Device-ID=
9 |
10 | ## Optional fields
11 | Clean-Session=true
12 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/adsb.ground.station/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Set root logger level to DEBUG and its only appender to A1.
2 | log4j.rootLogger=INFO, A1
3 |
4 | # A1 is set to be a ConsoleAppender.
5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender
6 |
7 | # A1 uses PatternLayout.
8 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout
9 | log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
10 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARFlightAnnotation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARFlightAnnotation.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 12/5/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import MapKit
12 | import CoreLocation
13 |
14 | /// Defines POI with title and location.
15 | open class ARFlightAnnotation
16 | {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/View/ARObjectView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARObjectView.h
3 | // Santander
4 | //
5 | // Created by Carlos on 10/11/10.
6 | // Copyright 2010 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class ARKitEngine;
12 |
13 | @interface ARObjectView : UIView
14 |
15 | @property (nonatomic, weak) ARKitEngine *controller;
16 | @property (nonatomic) BOOL displayed;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/adsb.ground.station/src/main/assembly/sources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | sources
4 |
5 | zip
6 |
7 | false
8 |
9 |
10 |
11 |
12 | src/main/java
13 | /
14 |
15 | **/*
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/LocalizationDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // LocalizationDelegate.h
3 | // ARModule
4 | //
5 | // Created by Carlos on 06/06/11.
6 | // Copyright 2011 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @protocol LocalizationDelegate
13 |
14 | - (void) locationFound:(CLLocation *)location;
15 | - (void) headingFound:(CLHeading *)heading;
16 | - (void) locationUnavailable;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/FlightBubbleV1.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlightBubbleV1.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 2/15/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 |
13 | class FlightBubbleV1:UIView {
14 | @IBOutlet weak var distance: UILabel!
15 | @IBOutlet weak var altitude: UILabel!
16 |
17 | @IBOutlet weak var flightImage: UIImageView!
18 | @IBOutlet weak var flightName: UILabel!
19 |
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 |
14 | *~
15 | target
16 | GTAGS
17 | GRTAGS
18 | GPATH
19 | prop
20 | out
21 | .DS_Store
22 | .idea
23 | *.iml
24 | .project
25 | .classpath
26 | .settings
27 | *.sublime-project
28 | *.sublime-workspace
29 | build-local.properties
30 | .gradle
31 | .checkstyle
32 | dependency-reduced-pom.xml
33 |
34 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/IBMFlightTracker-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // IBMFlightTracker-Bridging-Header.h
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 1/5/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | #import "ARKit.h"
10 | #import "ARKitConfig.h"
11 | #import "ARKitEngine.h"
12 | #import "ARViewDelegate.h"
13 | #import "LocalizationDelegate.h"
14 | #import "LocalizationHelper.h"
15 | #import "ARGeoCoordinate.h"
16 | #import "ARObjectView.h"
17 | #import "RadarView.h"
18 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/ARViewDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARViewDelegate.h
3 | // ARModule
4 | //
5 | // Created by Carlos on 06/06/11.
6 | // Copyright 2011 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ARObjectView.h"
11 | #import "ARGeoCoordinate.h"
12 |
13 | @protocol ARViewDelegate
14 |
15 | - (ARObjectView *)viewForCoordinate:(ARGeoCoordinate *)coordinate floorLooking:(BOOL)floorLooking;
16 | - (void) itemTouchedWithIndex:(NSInteger) index;
17 | - (void) didChangeLooking:(BOOL)floorLooking;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This is an open source project, and we appreciate your help!
4 |
5 | We use the GitHub issue tracker to discuss new features and non-trivial bugs.
6 |
7 | In addition to the issue tracker, [#journeys on
8 | Slack](https://dwopen.slack.com) is the best way to get into contact with the
9 | project's maintainers.
10 |
11 | To contribute code, documentation, or tests, please submit a pull request to
12 | the GitHub repository. Generally, we expect two maintainers to review your pull
13 | request before it is approved for merging. For more details, see the
14 | [MAINTAINERS](MAINTAINERS.md) page.
15 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/View/RadarView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RadarView.h
3 | // Santander
4 | //
5 | // Created by Carlos on 22/11/10.
6 | // Copyright 2010 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ARGeoCoordinate.h"
11 |
12 | @interface RadarView : UIView {
13 | NSMutableArray *points;
14 | UIView *dotsView;
15 | double farthest;
16 | }
17 |
18 | @property (nonatomic) double farthest;
19 | @property (nonatomic, strong) NSMutableArray *points;
20 |
21 | - (id)initAtPoint:(CGPoint)middlePoint;
22 |
23 | - (void) updatePoints:(ARGeoCoordinate *)centerCoord;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '9.0'
3 |
4 | target 'ARFlightTracker-iOS-Swift' do
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | use_frameworks!
7 |
8 | # Pods for IBMFlightTracker
9 | pod 'CocoaMQTT'
10 | pod 'CocoaAsyncSocket'
11 | pod 'SwiftyJSON'
12 |
13 | target 'ARFlightTracker-iOS-SwiftTests' do
14 | inherit! :search_paths
15 | # Pods for testing
16 | end
17 |
18 | target 'ARFlightTracker-iOS-SwiftUITests' do
19 | inherit! :search_paths
20 | # Pods for testing
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/MarkerView.h:
--------------------------------------------------------------------------------
1 | ////
2 | //// MarkerView.h
3 | //// Around Me
4 | ////
5 | //// Created by jdistler on 11.02.13.
6 | //// Copyright (c) 2013 Jean-Pierre Distler. All rights reserved.
7 | ////
8 | //
9 | //#import
10 | //
11 | //@class ARGeoCoordinate;
12 | //@protocol MarkerViewDelegate;
13 | //
14 | //@interface MarkerView : UIView
15 | //
16 | //@property (nonatomic, strong) ARGeoCoordinate *coordinate;
17 | //@property (nonatomic, weak) id delegate;
18 | //- (id)initWithCoordinate:(ARGeoCoordinate *)coordinate delegate:(id)delegate;
19 | //
20 | //@end
21 | //
22 | //@protocol MarkerViewDelegate
23 | //
24 | //- (void)didTouchMarkerView:(MarkerView *)markerView;
25 | //
26 | //@end
27 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-SwiftUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/FlightCalloutView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlightCalloutView.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 11/23/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class FlightCalloutView:UIView {
13 |
14 | @IBOutlet weak var flightName: UILabel!
15 | @IBOutlet weak var flightImage: UIImageView!
16 | @IBOutlet weak var altitudeInMeters: UILabel!
17 | @IBOutlet weak var velocityInMetersPerSecond: UILabel!
18 | @IBOutlet weak var currentCity: UILabel!
19 | @IBOutlet weak var weatherIcon: UIImageView!
20 | @IBOutlet weak var currentTemprature: UILabel!
21 | @IBOutlet weak var weatherDescription: UILabel!
22 | public var calloutOpen: Bool = false
23 |
24 | }
25 |
26 |
27 |
--------------------------------------------------------------------------------
/adsb.ground.station/src/test/java/com/ibm/iot/adsb/ground/station/AppTest.java:
--------------------------------------------------------------------------------
1 | package com.ibm.iot.adsb.ground.station;
2 |
3 | import junit.framework.Test;
4 | import junit.framework.TestCase;
5 | import junit.framework.TestSuite;
6 |
7 | /**
8 | * Unit test for simple App.
9 | */
10 | public class AppTest
11 | extends TestCase
12 | {
13 | /**
14 | * Create the test case
15 | *
16 | * @param testName name of the test case
17 | */
18 | public AppTest( String testName )
19 | {
20 | super( testName );
21 | }
22 |
23 | /**
24 | * @return the suite of tests being tested
25 | */
26 | public static Test suite()
27 | {
28 | return new TestSuite( AppTest.class );
29 | }
30 |
31 | /**
32 | * Rigourous Test :-)
33 | */
34 | public void testApp()
35 | {
36 | assertTrue( true );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-SwiftTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSLocationWhenInUseUsageDescription
6 | App needs location service to start
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | BNDL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARAnnotation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARAnnotation.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 3/10/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreLocation
11 |
12 | /// Defines POI with title and location.
13 | open class ARAnnotation: NSObject
14 | {
15 | /// Title of annotation
16 | open var title: String?
17 |
18 | ///coordinate
19 | open var coordinate: CLLocationCoordinate2D!
20 |
21 | /// View for annotation. It is set inside ARViewController after fetching view from dataSource.
22 | internal(set) open var annotationView: ARAnnotationView?
23 |
24 | // Internal use only, do not set this properties
25 | internal(set) open var radialDistance: Double = 0
26 | internal(set) open var altitude: Double = 0
27 | internal(set) open var active: Bool = false
28 |
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/Model/ARGeoCoordinate.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARGeoCoordinate.h
3 | // ARKitDemo
4 | //
5 | // Created by Haseman on 8/1/09.
6 | // Copyright 2009 Zac White. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import
12 |
13 | #define degreesToRadians(x) (M_PI * x / 180.0)
14 | #define radiansToDegrees(x) (x * (180.0/M_PI))
15 |
16 | @interface ARGeoCoordinate : NSObject {
17 |
18 | }
19 |
20 | @property (nonatomic, strong) id dataObject;
21 | @property (nonatomic) double radialDistance;
22 | @property (nonatomic) double inclination;
23 | @property (nonatomic) double azimuth;
24 | @property (nonatomic, strong) CLLocation *geoLocation;
25 |
26 | + (ARGeoCoordinate *)coordinateWithLocation:(CLLocation *)location;
27 | - (void)calibrateUsingOrigin:(CLLocation *)origin useAltitude:(BOOL) useAltitude;
28 | - (NSUInteger)hash;
29 | - (BOOL)isEqual:(id)other;
30 | - (BOOL)isEqualToCoordinate:(ARGeoCoordinate *)otherCoordinate;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/Assets.xcassets/AppIcon-2.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "60x60"
7 | },
8 | {
9 | "idiom" : "ipad",
10 | "scale" : "1x",
11 | "size" : "76x76"
12 | },
13 | {
14 | "idiom" : "ipad",
15 | "scale" : "2x",
16 | "size" : "76x76"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "2x",
21 | "size" : "40x40"
22 | },
23 | {
24 | "idiom" : "ipad",
25 | "scale" : "1x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "ipad",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "29x29"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "scale" : "1x",
41 | "size" : "29x29"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "2x",
46 | "size" : "29x29"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/Assets.xcassets/AppIcon-3.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "60x60"
7 | },
8 | {
9 | "idiom" : "ipad",
10 | "scale" : "1x",
11 | "size" : "76x76"
12 | },
13 | {
14 | "idiom" : "ipad",
15 | "scale" : "2x",
16 | "size" : "76x76"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "2x",
21 | "size" : "40x40"
22 | },
23 | {
24 | "idiom" : "ipad",
25 | "scale" : "1x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "ipad",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "29x29"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "scale" : "1x",
41 | "size" : "29x29"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "2x",
46 | "size" : "29x29"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.ibm.iot
6 | air.traffic.control
7 | develop-SNAPSHOT
8 | pom
9 |
10 | Cloud-based Air Traffic Control
11 | https://github.com/ibm/air-traffic-control
12 |
13 |
14 | https://github.com/ibm/air-traffic-control
15 | scm:git:https://github.com/ibm/air-traffic-control.git
16 |
17 |
18 |
19 | UTF-8
20 | 4.12-beta-1
21 | 1.8
22 | 1.8
23 | 1.8+
24 |
25 |
26 |
27 | adsb.ground.station
28 |
29 |
30 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Sanjeev Ghimire
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/View/ARObjectView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ARObjectView.m
3 | // Santander
4 | //
5 | // Created by Carlos on 10/11/10.
6 | // Copyright 2010 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import "ARObjectView.h"
10 | #import "ARKitEngine.h"
11 |
12 | @implementation ARObjectView
13 |
14 | - (id)initWithFrame:(CGRect)frame {
15 | if ((self = [super initWithFrame:frame])) {
16 | self.clipsToBounds = YES;
17 | self.autoresizesSubviews = YES;
18 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
19 | self.userInteractionEnabled = YES;
20 | self.opaque = YES;
21 | _displayed = YES;
22 | }
23 | return self;
24 | }
25 |
26 | - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
27 | }
28 |
29 | - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
30 | }
31 |
32 | - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
33 | // TODO fix touch on objectviews at half side right
34 | [_controller viewTouched:self];
35 | }
36 |
37 | - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
38 | }
39 |
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/LocalizationHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // LocalizationHelper.h
3 | // ARModule
4 | //
5 | // Created by Carlos on 06/06/11.
6 | // Copyright 2011 __MyCompanyName__. All rights reserved.
7 | //
8 | #import "UIKit/UIKit.h"
9 | #import
10 | #import
11 | #import "LocalizationDelegate.h"
12 |
13 | typedef enum {
14 | kLocalizationUnknown,
15 | kLocalizationDisabled,
16 | kLocalizationEnabled
17 | } kLocalizationStatus;
18 |
19 | @interface LocalizationHelper : NSObject {
20 | CLLocationManager *locationManager;
21 | UIView *loadingView;
22 |
23 | kLocalizationStatus localizationStatus;
24 | BOOL isHeadingInfoAvailable;
25 |
26 | NSMutableArray *onceRegistered;
27 | NSMutableArray *registered;
28 |
29 | id locDelegate;
30 |
31 | CLLocation *lastLocation;
32 | }
33 |
34 | + (LocalizationHelper *) sharedHelper;
35 | - (BOOL) canReceiveHeadingUpdates;
36 | - (void) registerForUpdates:(id)receiver once:(BOOL)once;
37 | - (void) deregisterForUpdates:(id)receiver;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-SwiftTests/IBMFlightTrackerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IBMFlightTrackerTests.swift
3 | // IBMFlightTrackerTests
4 | //
5 | // Created by Sanjeev Ghimire on 11/14/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import IBMFlightTracker
11 |
12 | class IBMFlightTrackerTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "ipad",
6 | "minimum-system-version" : "7.0",
7 | "extent" : "full-screen",
8 | "scale" : "2x"
9 | },
10 | {
11 | "orientation" : "landscape",
12 | "idiom" : "ipad",
13 | "minimum-system-version" : "7.0",
14 | "extent" : "full-screen",
15 | "scale" : "1x"
16 | },
17 | {
18 | "orientation" : "landscape",
19 | "idiom" : "ipad",
20 | "minimum-system-version" : "7.0",
21 | "extent" : "full-screen",
22 | "scale" : "2x"
23 | },
24 | {
25 | "orientation" : "portrait",
26 | "idiom" : "iphone",
27 | "minimum-system-version" : "7.0",
28 | "scale" : "2x"
29 | },
30 | {
31 | "orientation" : "portrait",
32 | "idiom" : "iphone",
33 | "minimum-system-version" : "7.0",
34 | "subtype" : "retina4",
35 | "scale" : "2x"
36 | },
37 | {
38 | "orientation" : "portrait",
39 | "idiom" : "ipad",
40 | "minimum-system-version" : "7.0",
41 | "extent" : "full-screen",
42 | "scale" : "1x"
43 | }
44 | ],
45 | "info" : {
46 | "version" : 1,
47 | "author" : "xcode"
48 | }
49 | }
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/MQTTConnection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MQTTUtil.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 11/17/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CocoaMQTT
11 |
12 | class MQTTConnection{
13 |
14 | var mqtt: CocoaMQTT?
15 |
16 | //IOT device configuration
17 | public static let API_KEY = ""
18 | public static let API_TOKEN = ""
19 | public static let IOT_CLIENT = "a::Flights"
20 | public static let IOT_HOST = ".messaging.internetofthings.ibmcloud.com"
21 | public static let IOT_PORT = 1883
22 | public static let IOT_TOPIC = "iot-2/type//id//evt//fmt/json"
23 |
24 | func connectToMQTT(_delegate:CocoaMQTTDelegate){
25 | mqtt = CocoaMQTT(clientID: MQTTConnection.IOT_CLIENT, host: MQTTConnection.IOT_HOST, port: UInt16(MQTTConnection.IOT_PORT))
26 | if let mqtt = mqtt {
27 | mqtt.username = MQTTConnection.API_KEY
28 | mqtt.password = MQTTConnection.API_TOKEN
29 | mqtt.willMessage = CocoaMQTTWill(topic: MQTTConnection.IOT_TOPIC, message: "dieout")
30 | mqtt.keepAlive = 60
31 | }
32 |
33 | mqtt!.delegate = _delegate
34 | mqtt!.connect()
35 | }
36 |
37 | }
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/adsb.ground.station/Dockerfile:
--------------------------------------------------------------------------------
1 | # ADS-B/SDR image for armhf architecture. This will NOT work on x86 platforms.
2 | #
3 | # Build an image using this Dockerfile as shown below:
4 | #
5 | # $ docker build . -t
6 | #
7 | # Start Dump1090 Server in a container based on the newly created image as
8 | # shown below:
9 | #
10 | # $ docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb -p 30002:30002
11 | #
12 | # Attach to the container to see the ADS-B messages being received from SDR
13 | # as shown below:
14 | #
15 | # $ docker attach
16 | #
17 | # is generated when the image is run.
18 | #
19 | FROM resin/rpi-raspbian:jessie-20160831
20 |
21 | RUN apt-get update && \
22 | apt-get -qy install curl ca-certificates
23 | RUN apt-get upgrade && \
24 | apt-get dist-upgrade
25 | RUN apt-get install build-essential
26 | RUN apt-get install apt-utils
27 | RUN apt-get install usbutils
28 | RUN apt-get install pkg-config
29 | RUN apt-get install cmake
30 | RUN apt-get install libusb-1.0-0-dev
31 | RUN apt-get install git-core git
32 |
33 | RUN git clone git://git.osmocom.org/rtl-sdr.git /tmp/rtl-sdr
34 | WORKDIR /tmp/rtl-sdr
35 | RUN cmake ./ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
36 | RUN make
37 | RUN make install
38 | RUN ldconfig
39 |
40 | RUN git clone https://github.com/MalcolmRobb/dump1090 /tmp/dump1090
41 | WORKDIR /tmp/dump1090
42 | RUN make
43 |
44 | EXPOSE 30002
45 | CMD ["./dump1090", "--raw", "--net"]
46 |
47 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-SwiftUITests/IBMFlightTrackerUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IBMFlightTrackerUITests.swift
3 | // IBMFlightTrackerUITests
4 | //
5 | // Created by Sanjeev Ghimire on 11/14/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class IBMFlightTrackerUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 |
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 |
18 | // In UI tests it is usually best to stop immediately when a failure occurs.
19 | continueAfterFailure = false
20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
21 | XCUIApplication().launch()
22 |
23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
24 | }
25 |
26 | override func tearDown() {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | super.tearDown()
29 | }
30 |
31 | func testExample() {
32 | // Use recording to get started writing UI tests.
33 | // Use XCTAssert and related functions to verify your tests produce the correct results.
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARFlightAnnotationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARFlightAnnotationView.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 12/5/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | open class ARFlightAnnotationView: UIView
13 | {
14 | open weak var annotation: FlightAnnotation?
15 | fileprivate var initialized: Bool = false
16 |
17 | public init()
18 | {
19 | super.init(frame: CGRect.zero)
20 | self.initializeInternal()
21 | }
22 |
23 | public required init?(coder aDecoder: NSCoder)
24 | {
25 | super.init(coder: aDecoder)
26 | self.initializeInternal()
27 | }
28 |
29 | override init(frame: CGRect)
30 | {
31 | super.init(frame: frame)
32 | self.initializeInternal()
33 | }
34 |
35 | fileprivate func initializeInternal()
36 | {
37 | if self.initialized
38 | {
39 | return
40 | }
41 | self.initialized = true;
42 | self.initialize()
43 | }
44 |
45 | open override func awakeFromNib()
46 | {
47 | self.bindUi()
48 | }
49 |
50 | /// Will always be called once, no need to call super
51 | open func initialize()
52 | {
53 |
54 | }
55 |
56 | /// Called when distance/azimuth changes, intended to be used in subclasses
57 | open func bindUi()
58 | {
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARAnnotationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARAnnotationView.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 3/10/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// View for annotation. Subclass to customize. Annotation views should be lightweight,
12 | /// try to avoid xibs and autolayout.
13 | open class ARAnnotationView: UIView
14 | {
15 | open weak var annotation: ARAnnotation?
16 | fileprivate var initialized: Bool = false
17 |
18 | public init()
19 | {
20 | super.init(frame: CGRect.zero)
21 | self.initializeInternal()
22 | }
23 |
24 | public required init?(coder aDecoder: NSCoder)
25 | {
26 | super.init(coder: aDecoder)
27 | self.initializeInternal()
28 | }
29 |
30 | override init(frame: CGRect)
31 | {
32 | super.init(frame: frame)
33 | self.initializeInternal()
34 | }
35 |
36 | fileprivate func initializeInternal()
37 | {
38 | if self.initialized
39 | {
40 | return
41 | }
42 | self.initialized = true;
43 | self.initialize()
44 | }
45 |
46 | open override func awakeFromNib()
47 | {
48 | self.bindUi()
49 | }
50 |
51 | /// Will always be called once, no need to call super
52 | open func initialize()
53 | {
54 |
55 | }
56 |
57 | /// Called when distance/azimuth changes, intended to be used in subclasses
58 | open func bindUi()
59 | {
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/FlightAnnotation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlightAnnotation.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 11/15/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 | import Foundation
9 | import MapKit
10 | import UIKit
11 |
12 | open class FlightAnnotation: NSObject, MKAnnotation {
13 |
14 | public dynamic var coordinate: CLLocationCoordinate2D
15 | public var title: String?
16 | public var image: UIImage!
17 | public var speed: Double!
18 | public var altitude: Double!
19 |
20 | // use on map view
21 | var calloutView: FlightCalloutView!
22 | var calloutOpen: Bool = false
23 | //
24 | public var heading: Double!
25 | public var velocity: Double!
26 | public var callSign: String!
27 | public var createdInMillis: Int64!
28 | public var lastUpdatedInMillis:Int64!
29 |
30 | // to run in test mode. determines what value to show
31 | public var testFlight: Bool = false
32 | public var realAzimuth:Double! = 0.0
33 |
34 | /// View for annotation. It is set inside ARFlightViewController after fetching view from dataSource.
35 | internal(set) open var annotationView: ARFlightAnnotationView?
36 |
37 | // Internal use only, do not set this properties
38 | internal(set) open var distanceFromUser: Double = 0
39 | internal(set) open var azimuth: Double = 0
40 | internal(set) open var verticalLevel: Double = 0
41 | internal(set) open var active: Bool = false
42 |
43 | internal(set) open var annotationExpiryTimer: Timer?
44 |
45 | init(coordinate: CLLocationCoordinate2D) {
46 | self.coordinate=coordinate
47 | super.init()
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/ARKitConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARKitConfig.h
3 | // ARKit Example
4 | //
5 | // Created by Carlos on 21/10/13.
6 | //
7 | //
8 |
9 | #import
10 | #import "ARViewDelegate.h"
11 |
12 | @interface ARKitConfig : NSObject
13 |
14 | // Do we need to show different images when floor looking?
15 | @property (nonatomic) BOOL showsFloorImages;
16 |
17 | // Does the views need to be scaled based on distance?
18 | @property (nonatomic) BOOL scaleViewsBasedOnDistance;
19 |
20 | // If so, which is the minimum scale factor?
21 | @property (nonatomic) CGFloat minimumScaleFactor;
22 |
23 | // Does the views need to be rotated based on perspective?
24 | @property (nonatomic) BOOL rotateViewsBasedOnPerspective;
25 |
26 | // If so, which is the maximum rotation angle?
27 | @property (nonatomic) CGFloat maximumRotationAngle;
28 |
29 | // Which is the rendering update frequency?
30 | @property (nonatomic) CGFloat updateFrequency;
31 |
32 | // Do you want the engine to consider geopoints altitude?
33 | @property (nonatomic) BOOL useAltitude;
34 |
35 | // Do you want to use the debug mode?
36 | @property (nonatomic) BOOL debugMode;
37 |
38 | // In which orientation will the rendering be made?
39 | @property (nonatomic) UIInterfaceOrientation orientation;
40 |
41 | // The delegate to which notify touches and so
42 | @property (nonatomic, strong) id delegate;
43 |
44 | // Where is the point where you want to place the radar view?
45 | @property (nonatomic) CGPoint radarPoint;
46 |
47 | // Do you want to use a custom loading view or the default one? (nil = default)
48 | @property (nonatomic, strong) UIView *loadingView;
49 |
50 |
51 |
52 | + (ARKitConfig *) defaultConfigFor:(id) delegate;
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode artifacts
2 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
3 |
4 | ## Build generated
5 | build/
6 | DerivedData/
7 |
8 | ## Various settings
9 | *.pbxuser
10 | !default.pbxuser
11 | *.mode1v3
12 | !default.mode1v3
13 | *.mode2v3
14 | !default.mode2v3
15 | *.perspectivev3
16 | !default.perspectivev3
17 | xcuserdata/
18 |
19 | ## Other
20 | *.moved-aside
21 | *.xcuserstate
22 |
23 | ## Obj-C/Swift specific
24 | *.hmap
25 | *.ipa
26 | *.dSYM.zip
27 | *.dSYM
28 |
29 | ## Playgrounds
30 | timeline.xctimeline
31 | playground.xcworkspace
32 |
33 | # Swift Package Manager
34 | #
35 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
36 | Packages/
37 | .build/
38 |
39 | # CocoaPods
40 | #
41 | # We recommend against adding the Pods directory to your .gitignore. However
42 | # you should judge for yourself, the pros and cons are mentioned at:
43 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
44 | #
45 | Pods/
46 |
47 | # Carthage
48 | #
49 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
50 | Carthage/Checkouts
51 |
52 | Carthage/Build
53 |
54 | # fastlane
55 | #
56 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
57 | # screenshots whenever they are needed.
58 | # For more information about the recommended setup visit:
59 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
60 |
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 |
66 | #node modules
67 | node_modules/
68 |
69 | Podfile.lock
70 |
71 | .DS_Store
72 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/ARKitConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // ARKitConfig.m
3 | // ARKit Example
4 | //
5 | // Created by Carlos on 21/10/13.
6 | //
7 | //
8 |
9 | #import "ARKitConfig.h"
10 |
11 | static const BOOL DEFAULT_SHOWS_FLOOR_IMAGES_CONFIG = YES;
12 | static const BOOL DEFAULT_SCALE_VIEWS_BASED_ON_DISTANCE_CONFIG = YES;
13 | static const CGFloat DEFAULT_MINIMUM_SCALE_FACTOR_CONFIG = 0.5;
14 | static const BOOL DEFAULT_ROTATE_VIEWS_BASED_ON_PERSPECTIVE_CONFIG = YES;
15 | static const CGFloat DEFAULT_MAXIMUM_ROTATION_ANGLE_CONFIG = M_PI / 6.0;
16 | static const CGFloat DEFAULT_UPDATE_FREQUENCY_CONFIG = 1.0 / 20.0;
17 | static const BOOL DEFAULT_USE_ALTITUDE_CONFIG = NO;
18 | static const BOOL DEFAULT_DEBUG_MODE_CONFIG = NO;
19 | static const UIInterfaceOrientation DEFAULT_ORIENTATION_CONFIG = UIInterfaceOrientationPortrait;
20 |
21 | @implementation ARKitConfig
22 |
23 | + (ARKitConfig *) defaultConfigFor:(id) delegate {
24 |
25 | ARKitConfig *config = [[ARKitConfig alloc] init];
26 | config.showsFloorImages = DEFAULT_SHOWS_FLOOR_IMAGES_CONFIG;
27 | config.scaleViewsBasedOnDistance = DEFAULT_SCALE_VIEWS_BASED_ON_DISTANCE_CONFIG;
28 | config.minimumScaleFactor = DEFAULT_MINIMUM_SCALE_FACTOR_CONFIG;
29 | config.rotateViewsBasedOnPerspective = DEFAULT_ROTATE_VIEWS_BASED_ON_PERSPECTIVE_CONFIG;
30 | config.maximumRotationAngle = DEFAULT_MAXIMUM_ROTATION_ANGLE_CONFIG;
31 | config.updateFrequency = DEFAULT_UPDATE_FREQUENCY_CONFIG;
32 | config.useAltitude = DEFAULT_USE_ALTITUDE_CONFIG;
33 | config.debugMode = DEFAULT_DEBUG_MODE_CONFIG;
34 | config.orientation = DEFAULT_ORIENTATION_CONFIG;
35 | config.delegate = delegate;
36 | config.radarPoint = CGPointZero;
37 | config.loadingView = nil;
38 |
39 | return config;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/View/RadarView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RadarView.m
3 | // Santander
4 | //
5 | // Created by Carlos on 22/11/10.
6 | // Copyright 2010 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import "RadarView.h"
10 |
11 |
12 | @implementation RadarView
13 |
14 | @synthesize points, farthest;
15 |
16 | - (id)initAtPoint:(CGPoint)middlePoint {
17 | if ((self = [super initWithFrame:CGRectZero])) {
18 | // Initialization code
19 | UIImage *radarImg = [UIImage imageNamed:@"radar.png"];
20 | UIImageView *background = [[UIImageView alloc] initWithImage: radarImg];
21 | [self addSubview:background];
22 |
23 | self.frame = CGRectMake(middlePoint.x - radarImg.size.width / 2, middlePoint.y - radarImg.size.height / 2, radarImg.size.width, radarImg.size.height);
24 |
25 | dotsView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
26 | [self addSubview:dotsView];
27 | }
28 | return self;
29 | }
30 |
31 | - (void) updatePoints:(ARGeoCoordinate *)centerCoord {
32 | double centerAzimuth = centerCoord.azimuth + M_PI / 2;
33 | if (centerAzimuth < 0.0) {
34 | centerAzimuth = 2 * M_PI + centerAzimuth;
35 | }
36 |
37 | UIImage *dot = [UIImage imageNamed:@"radar_dot.png"];
38 |
39 | for (UIView *sub in dotsView.subviews) {
40 | [sub removeFromSuperview];
41 | }
42 |
43 | for (ARGeoCoordinate *coord in points) {
44 | double coordAzimuth = coord.azimuth - centerAzimuth;
45 | if (coordAzimuth < 0.0) {
46 | coordAzimuth = 2 * M_PI + coordAzimuth;
47 | }
48 | CGPoint pt;
49 |
50 | pt.x = dotsView.center.x + cos(coordAzimuth) * coord.radialDistance * self.frame.size.width / (2 * farthest);
51 | pt.y = dotsView.center.y + sin(coordAzimuth) * coord.radialDistance * self.frame.size.height / (2 * farthest);
52 |
53 | UIImageView *point = [[UIImageView alloc] initWithImage:dot];
54 | point.center = pt;
55 |
56 | [dotsView addSubview:point];
57 | }
58 | }
59 |
60 | @end
61 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/TestFlights.json:
--------------------------------------------------------------------------------
1 | {
2 | "flights": [
3 | {
4 | "icao": "LinkedIn office",
5 | "altitudeInMeters": 117,
6 | "callSign": "CPZ6051",
7 | "headingInDegrees": 132.56732841750284,
8 | "latitude": 37.786793,
9 | "longitude": -122.398263,
10 | "sensorMacAddress": "B8-27-EB-12-A5-E5",
11 | "type": "ar_flights_schema",
12 | "velocityInMetersPerSecond": 222.82611999540126,
13 | "createdInMillis": 1481673865141,
14 | "lastUpdatedInMillis": 1481674254888
15 | },
16 | {
17 | "icao": "Millenium Tower,SF",
18 | "altitudeInMeters": 198,
19 | "callSign": "CPZ6051",
20 | "headingInDegrees": 132.56732841750284,
21 | "latitude": 37.790457,
22 | "longitude": -122.396696,
23 | "sensorMacAddress": "B8-27-EB-12-A5-E5",
24 | "type": "ar_flights_schema",
25 | "velocityInMetersPerSecond": 222.82611999540126,
26 | "createdInMillis": 1481673865141,
27 | "lastUpdatedInMillis": 1481674254888
28 | },
29 | {
30 | "icao": "Transmerica Pyramid",
31 | "altitudeInMeters": 265,
32 | "callSign": "CPZ6051",
33 | "headingInDegrees": 132.56732841750284,
34 | "latitude": 37.795110,
35 | "longitude": -122.403260,
36 | "sensorMacAddress": "B8-27-EB-12-A5-E5",
37 | "type": "ar_flights_schema",
38 | "velocityInMetersPerSecond": 222.82611999540126,
39 | "createdInMillis": 1481673865141,
40 | "lastUpdatedInMillis": 1481674254888
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/README-cn.md:
--------------------------------------------------------------------------------
1 | *阅读本文的其他语言版本:[English](README.md)。*
2 | # 空中交通管制
3 | [](https://travis-ci.org/IBM/air-traffic-control)
4 |
5 | 这个存储库包含使用 IBM Cloud 构建基于云计算的现代空中交通管制系统的操作说明。
6 |
7 | Air Traffic Control 服务通过软件定义无线电 (SDR) 技术从 Raspberry Pi 支持的 ADS-B 地面接收站接收航班信息,以便直接从商业航班接收 ADS-B 消息,并将 MQTT 消息发布到正在 IBM Cloud 中运行的 IBM IoT Platform。它还包括一个基于 Swift 的 iOS 应用程序,该应用程序通过从 IoT Platform 接收 MQTT 消息,使用增强现实工具包来跟踪航班。该应用程序将显示接收器接收范围内的各个机场之间飞行的所有航班。
8 |
9 | 借助航空电子领域中的进步和容易获得的 Raspberry Pi (RPi) 等廉价计算资源,可以轻松地构建一个最先进的地面接收站。可以使用 Docker 等虚拟化技术轻松复制这些地面接收站,以覆盖大片地区。由 RPi 提供支持的地面接收站分散在全球各地,它们将实现以下功能:
10 | * 使用一个带天线的 SDR 接收器接收约 100-150 英里半径范围内的航班信息,具体范围取决于海拔高度和视野。
11 | * 充当联网的 IoT 设备,以消息队列遥测传输 (MQTT) 消息格式将航班信息发送到基于云的空中交通管制系统,该系统在一个可扩展、安全、可靠且开放的云基础架构中运行。
12 |
13 | 这个基于云的空中交通管制系统可使用 IBM 的 Cloud Platform As A Service (PaaS) 实现,后者采用 IBM 的 Open Cloud Architecture ,基于 CloudFoundry 开放技术和 SoftLayer 基础架构实现。因为将地面接收站建模为 IoT 设备,而且它们是联网设备,并以 MQTT 消息格式发送航班信息,所以使用 IBM Cloud 中的 Internet of Things (IoT) Platform 服务是合理的做法,因为该服务不仅能随着地面接收站数量增长而弹性扩展,还能作为汇集点来接收所有事件,以便可以使用航班数据创建分析应用程序、可视化仪表板等。
14 |
15 | IoT Platform 服务还能向所有与之相连的 iOS 设备提供航班信息。在飞机出现在用户上空之前,在 iOS 设备上运行的基于 Swift 的移动应用程序可以使用增强现实技术在屏幕上呈现飞往该方向的航班!
16 |
17 | ## 架构
18 | 下图显示了一个基于云计算的空中交通管制系统的总体架构,该系统依靠低廉的地面接收站来跟踪航班。
19 |
20 | 
21 |
22 | ## Application Workflow
23 | 
24 |
25 | 1. Raspberry Pi 将流量数据传输到物联网平台。
26 | 2. MQTT 将数据传递到物联网分析仪表板中进行分析。
27 | 3. 从天气服务 API 中获得的当前天气情况。
28 | 4. 向手机设备发送分析结果和天气数据。
29 |
30 | ## Raspberry Pi 支持的 ADS-B 地面接收站
31 |
32 | [这里](https://github.com/IBM/air-traffic-control/blob/master/adsb.ground.station/README.md) 提供了构建受 Raspberry Pi 支持的地面接收站的操作说明。
33 |
34 | ## 基于 Swift 的 iOS 应用程序
35 |
36 | [这里](https://github.com/IBM/air-traffic-control/blob/master/ARFlightTracker-iOS-Swift/README.md) 提供了使用基于 Swift 的 iOS 应用程序跟踪航班的操作说明。
37 |
38 | # 许可
39 |
40 | [Apache 2.0](LICENSE.md)
41 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ARFlightTracker-iOS-Swift
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIcons
12 |
13 | CFBundleIcons~ipad
14 |
15 | CFBundleIdentifier
16 | $(PRODUCT_BUNDLE_IDENTIFIER)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | $(PRODUCT_NAME)
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 1.0
25 | CFBundleVersion
26 | 1
27 | LSRequiresIPhoneOS
28 |
29 | NSCameraUsageDescription
30 | Camera is required for this app
31 | NSLocationAlwaysUsageDescription
32 | Location is required for this app
33 | NSLocationWhenInUseUsageDescription
34 | Location is required for this app
35 | UILaunchStoryboardName
36 | LaunchScreen
37 | UIMainStoryboardFile
38 | Main
39 | UIRequiredDeviceCapabilities
40 |
41 | armv7
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | UISupportedInterfaceOrientations~ipad
50 |
51 | UIInterfaceOrientationPortrait
52 | UIInterfaceOrientationPortraitUpsideDown
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/FlightInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlightInfo.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 11/15/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 | import Foundation
9 |
10 | import SwiftyJSON
11 |
12 | struct FlightInfo {
13 |
14 | let icao: String?
15 | let altitudeInMeters:Double
16 | let callSign: String
17 | let headingInDegrees: Double
18 | let latitude : Double
19 | let groundStationId : String
20 | let velocityInMetersPerSecond : Double
21 | let type: String
22 | let createdInMillis: Int64
23 | let lastUpdatedInMillis:Int64
24 | //let realAzimuth:Double!
25 |
26 | }
27 |
28 |
29 | extension FlightInfo{
30 | init?(json: JSON) {
31 | guard let icao = json["icao"].string,
32 | let altitudeInMeters = json["altitudeInMeters"].double,
33 | let callSign = json["callSign"].string,
34 | let headingInDegrees = json["headingInDegrees"].double,
35 | let latitude = json["latitude"].double,
36 | let longitude = json["longitude"].double,
37 | let groundStationId = json["groundStationId"].string,
38 | let velocityInMetersPerSecond = json["velocityInMetersPerSecond"].double,
39 | let type = json["type"].string,
40 | let createdInMillis = json["createdInMillis"].int64,
41 | let lastUpdatedInMillis = json["lastUpdatedInMillis"].int64//,
42 | //let realAzimuth = json["realAzimuth"].double
43 | else {
44 | return nil
45 | }
46 |
47 | self.icao = icao;
48 | self.altitudeInMeters=altitudeInMeters;
49 | self.callSign=callSign;
50 | self.headingInDegrees=headingInDegrees;
51 | self.latitude=latitude;
52 | self.longitude=longitude;
53 | self.groundStationId=groundStationId;
54 | self.velocityInMetersPerSecond=velocityInMetersPerSecond;
55 | self.type=type;
56 | self.createdInMillis=createdInMillis;
57 | self.lastUpdatedInMillis=lastUpdatedInMillis;
58 | //self.realAzimuth=realAzimuth;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 11/14/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/README-ja.md:
--------------------------------------------------------------------------------
1 | *他の言語で読む: [English](README.md).*
2 |
3 | # ARFlightTracker
4 |
5 | ARFlightTracker は、SDR/ADSB メッセージ受信局が MQTT サーバーを介してプッシュしたフライト情報を追跡するiOSベースのアプリです。
6 | アプリは、レシーバーの範囲内のポイントからポイントへ移動するすべてのフライトを表示します。
7 | ARFlightTracker アプリケーションは、IBM MQTT サーバーに接続し、新しい/更新されたフライト情報をトピックとして受け取り、それを地図ビューにレンダリングします。
8 | データは、SDR/ADSBメッセージレシーバによってトピックに供給されます。
9 | また地図上では、それぞれの目的地へのフライトが、行先の方向がわかるアニメーションとして表示されます。
10 | フライトの詳細ビューには、そのフライトの現在位置の天気に関するフライト情報が含まれています。
11 |
12 | ## 地図 (Map) ビュー
13 |
14 | 地図ビューでは、iOS デバイスに用意されている標準マップ上にすべてのフライトが表示されます。
15 | フライト方向は、ペイロード内の現在の heading 情報に基づいて調整されます
16 | アプリが MQTT メッセージを受信すると、そのフライト情報が目的地に向かって移動するのが見られます。
17 | 各フライトをタップすると、飛行番号、高度、距離などの詳細情報が表示されます。
18 | 下の図は、iOS デバイス上の Swift ベースのアプリケーションにおける、フライトを表示した地図ビューのレンダリングを示しています:
19 |
20 | 
21 |
22 | ## AR (Augmented Reality) ビュー
23 |
24 | ユーザーは、アプリの `AR View` タブをタップして、AR ベースのビューに切り替えることができます。
25 | このモードでは、アプリはカメラビューになり、利用者が表示されたフライトをポイントすることで、フライトの詳細情報が実際の風景の上にオーバレイで表示されます。
26 | 現実世界で飛行機は動いていますので、それに連動して情報を含む吹き出しがカメラビューのフライトと一緒に移動します。
27 | AR ビューには、デバイスの向きに基づくコンパスと、視野角内のすべてのフライトを表示するレーダービューも表示されます。
28 | 下の図は、iOS デバイス上の Swift ベースのアプリケーションにおける、フライトを表示した Augmented Reality ビューのレンダリングを示しています:
29 |
30 | 
31 |
32 | # 前提条件
33 |
34 | - Swift 3
35 | - Xcode 8.0+
36 | - CocoaPod - https://cocoapods.org/
37 | - iOS 10+
38 |
39 |
40 | # 依存する要素
41 |
42 | - CocoaMQTT - ノート: IBM の aphid client から移行
43 | - SwiftyJSON
44 | - ios-arkit for iphone - (コードベースの一部)
45 |
46 | # 手順:
47 |
48 | 1. ARFlightTracker-iOS-Swift ディレクトリに移動し、ARFlightTracker-iOS-Swift.xcworkspace を Xcode で開きます。
49 | 2. プロジェクトディレクトリから `pod install` を実行します。 これは `Podfile` に定義された依存関係をインストールします。
50 | 3. Xcodeエディタを使用して `util/MQTTConnection.swift` を更新します。資格情報を取得するには、IBM Cloud (Bluemix) コンソールで Internet of Things サービスを作成する必要があります。資格情報は次のようになります:
51 | ```
52 | API_KEY = ""
53 | API_TOKEN = ""
54 | IOT_CLIENT = "a::Flights"
55 | IOT_HOST = ".messaging.internetofthings.ibmcloud.com"
56 | IOT_PORT = 1883 (DEFAULT)
57 | IOT_TOPIC = "iot-2/type//id//evt/flight/fmt/json"
58 | ```
59 | 4. IBM Weather API の資格情報で `util/RestCall.swift` を更新します。IBM Cloud コンソールを使用して Weather API サービスを作成します:
60 | ```
61 | private static let WEATHER_API_USERNAME : String = ""
62 | private static let WEATHER_API_PASSWORD : String = ""
63 | ```
64 | 5. ビルドして実行します。
65 |
66 | # テストモード:
67 |
68 | テストモードでアプリを実行すると、IBM Cloud MQTTサーバーとは独立した状態になります。ViewController で、フラグを設定できます:
69 | ```
70 | flightTestMode = true
71 | ```
72 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/ARKitEngine.h:
--------------------------------------------------------------------------------
1 | //
2 | // ARKitEngine.h
3 | // ARModule
4 | //
5 | // Created by Carlos on 06/06/11.
6 | // Copyright 2011 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "LocalizationDelegate.h"
12 | #import "ARViewDelegate.h"
13 | #import "ARGeoCoordinate.h"
14 | #import "RadarView.h"
15 | #import "ARKitConfig.h"
16 |
17 | @class ARObjectView;
18 |
19 | typedef struct {
20 | CGFloat xOffset;
21 | CGFloat rotationAngle;
22 | UIInterfaceOrientation orientation;
23 | CGSize viewSize;
24 | } ARKitOrientationSupport;
25 |
26 | typedef enum {
27 | kFrontLookingType,
28 | kFloorLookingType
29 | } ARKitLookingType;
30 |
31 | @interface ARKitEngine : NSObject {
32 |
33 | @private
34 | NSMutableArray *ar_coordinates;
35 | NSMutableArray *ar_coordinateViews;
36 | NSMutableArray *ar_floorCoordinateViews;
37 |
38 | id delegate;
39 |
40 | UIImagePickerController *cameraController;
41 |
42 | RadarView *radar;
43 | UIView *ar_overlayView;
44 | UILabel *ar_debugView;
45 |
46 | NSTimer *_updateTimer;
47 |
48 | double maximumScaleDistance;
49 |
50 | BOOL showsFloorImages;
51 | BOOL scaleViewsBasedOnDistance;
52 | CGFloat minimumScaleFactor;
53 | BOOL rotateViewsBasedOnPerspective;
54 | CGFloat maximumRotationAngle;
55 | CGFloat updateFrequency;
56 | BOOL debugMode;
57 | BOOL useAltitude;
58 | ARKitLookingType lookingType;
59 |
60 | ARGeoCoordinate *centerCoordinate;
61 |
62 | CMMotionManager *motionManager;
63 |
64 | UIViewController *baseViewController;
65 |
66 | ARKitOrientationSupport orientationSupporter;
67 |
68 | UIView *loadingView;
69 | }
70 |
71 | - (id) initWithConfig:(ARKitConfig *) conf;
72 |
73 | - (void) addCoordinate:(ARGeoCoordinate *)coordinate;
74 | - (void) addCoordinates:(NSArray *)newCoordinates;
75 | - (void) removeCoordinate:(ARGeoCoordinate *)coordinate;
76 | - (void) removeCoordinates:(NSArray *)coordinates;
77 | - (void) removeAllCoordinates;
78 |
79 | - (void) addExtraView:(UIView *)extra;
80 |
81 | - (void) viewTouched:(ARObjectView *) view;
82 |
83 | - (void) startListening;
84 | - (void) hide;
85 |
86 | - (id) dataObjectWithIndex:(NSInteger)index;
87 | - (ARObjectView *) frontViewWithIndex:(NSInteger)index;
88 | - (ARObjectView *) floorViewWithIndex:(NSInteger)index;
89 |
90 |
91 | @end
92 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/MarkerView.m:
--------------------------------------------------------------------------------
1 | ////
2 | //// MarkerView.m
3 | //// Around Me
4 | ////
5 | //// Created by jdistler on 11.02.13.
6 | //// Copyright (c) 2013 Jean-Pierre Distler. All rights reserved.
7 | ////
8 | //
9 | //#import "MarkerView.h"
10 | //
11 | //#import "ARGeoCoordinate.h"
12 | //
13 | //const float kWidth = 200.0f;
14 | //const float kHeight = 100.0f;
15 | //
16 | //@interface MarkerView ()
17 | //
18 | //@property (nonatomic, strong) UILabel *lblDistance;
19 | //
20 | //@end
21 | //
22 | //
23 | //@implementation MarkerView
24 | //
25 | //- (id)initWithFrame:(CGRect)frame
26 | //{
27 | // self = [super initWithFrame:frame];
28 | // if (self) {
29 | // // Initialization code
30 | // }
31 | // return self;
32 | //}
33 | //
34 | //- (id)initWithCoordinate:(ARGeoCoordinate *)coordinate delegate:(id)delegate {
35 | // if((self = [super initWithFrame:CGRectMake(0.0f, 0.0f, kWidth, kHeight)])) {
36 | // _coordinate = coordinate;
37 | // _delegate = delegate;
38 | //
39 | // [self setUserInteractionEnabled:YES];
40 | //
41 | // UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, kWidth, 40.0f)];
42 | // [title setBackgroundColor:[UIColor colorWithWhite:0.3f alpha:0.7f]];
43 | // [title setTextColor:[UIColor whiteColor]];
44 | // [title setTextAlignment:NSTextAlignmentCenter];
45 | // [title setText:[coordinate title]];
46 | // [title sizeToFit];
47 | //
48 | // _lblDistance = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 45.0f, kWidth, 40.0f)];
49 | //
50 | // [_lblDistance setBackgroundColor:[UIColor colorWithWhite:0.3f alpha:0.7f]];
51 | // [_lblDistance setTextColor:[UIColor whiteColor]];
52 | // [_lblDistance setTextAlignment:NSTextAlignmentCenter];
53 | // [_lblDistance setText:[NSString stringWithFormat:@"%.2f km", [coordinate distanceFromOrigin] / 1000.0f]];
54 | // [_lblDistance sizeToFit];
55 | //
56 | // [self addSubview:title];
57 | // [self addSubview:_lblDistance];
58 | //
59 | // [self setBackgroundColor:[UIColor clearColor]];
60 | // }
61 | //
62 | // return self;
63 | //}
64 | //
65 | //- (void)drawRect:(CGRect)rect {
66 | // [super drawRect:rect];
67 | // [[self lblDistance] setText:[NSString stringWithFormat:@"%.2f km", [[self coordinate] distanceFromOrigin] / 1000.0f]];
68 | //}
69 | //
70 | //- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
71 | // if(_delegate && [_delegate conformsToProtocol:@protocol(MarkerViewDelegate)]) {
72 | // [_delegate didTouchMarkerView:self];
73 | // }
74 | //}
75 | //
76 | //- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
77 | //
78 | // CGRect theFrame = CGRectMake(0, 0, kWidth, kHeight);
79 | //
80 | // return CGRectContainsPoint(theFrame, point);
81 | //}
82 | //
83 | //@end
84 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/RestCall.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RestCall.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 3/14/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 |
12 |
13 | class RestCall {
14 |
15 |
16 | private static let WEATHER_API_USERNAME : String = ""
17 | private static let WEATHER_API_PASSWORD : String = ""
18 |
19 |
20 | func fetchWeatherBasedOnCurrentLocation(latitude: String, longitude: String, completion: @escaping (_ result: [String: Any]) -> Void){
21 |
22 | var weatherData : [String : Any] = [:]
23 |
24 | let url:String = self.getURL(latitude: latitude, longitude: longitude)
25 |
26 | guard let endpointURL = URL(string: url) else {
27 | print("Error: cannot create URL")
28 | return
29 | }
30 |
31 | let urlRequest = URLRequest(url: endpointURL)
32 |
33 | let session = URLSession.shared
34 |
35 | let task = session.dataTask(with: urlRequest) {
36 | (data, response, error) in
37 | // check for any errors
38 | guard error == nil else {
39 | print("error calling weather api")
40 | print(error!)
41 | return
42 | }
43 | // make sure we got data
44 | guard let responseData = data else {
45 | print("Error: did not receive data")
46 | return
47 | }
48 |
49 |
50 | let weatherJson = JSON(data: responseData)
51 |
52 | let observation : [String : Any] = weatherJson["observation"].dictionaryObject!
53 |
54 | weatherData["city"] = observation["obs_name"]
55 | weatherData["temperature"] = observation["temp"]
56 | weatherData["description"] = observation["wx_phrase"]
57 |
58 | let weatherIconUrl:String = "http://weather-company-data-demo.mybluemix.net/images/weathericons/icon\(observation["wx_icon"]!).png"
59 |
60 | weatherData["weatherIconUrl"] = weatherIconUrl
61 |
62 | completion(weatherData)
63 | }
64 | task.resume()
65 | }
66 |
67 |
68 |
69 | func getURL(latitude: String, longitude: String) -> String{
70 | let url: String = "https://\(RestCall.WEATHER_API_USERNAME):\(RestCall.WEATHER_API_PASSWORD)@twcservice.mybluemix.net/api/weather/v1/geocode/\(latitude)/\(longitude)/observations.json?units=e&language=en-US"
71 |
72 | return url
73 | }
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/README-ja.md:
--------------------------------------------------------------------------------
1 | *他の言語で読む: [English](README.md), [中国語](README-cn.md).*
2 |
3 | # air-traffic-control (航空交通管制)
4 | [](https://travis-ci.org/IBM/air-traffic-control)
5 |
6 | このレポジトリには、IBM Cloud を使用して最新の Cloud ベースの航空交通管制を構築するための手順が含まれています。
7 |
8 | > ノート: このコードパターンを完全に完成させるには、ラズベリーパイ、アンテナ付き SDR レシーバー、iOS デバイスが必要です。
9 |
10 | 航空交通管制サービスは、Software Defined Radio (SDR) 搭載の Raspberry Pi で構築した ADS-B 地上局を用いて、民間航空便から直接 ADS-B メッセージを受信します。
11 | そして得られたフライト情報を IBM Cloud で動作する IBM IoT プラットフォームに MQTT メッセージとして発行します。
12 | また、IoT プラットフォームから MQTT メッセージを受信し、Augmented Reality ツールキットを使用してフライトを追跡できる Swift ベースの iOS アプリケーションもサポートしています。
13 | このアプリは、レシーバーの範囲内をを移動するすべてのフライトを表示します。
14 |
15 | アビオニクス分野の進歩と Raspberry Pi (RPi) などの安価なコンピューティングリソースの利用により、最先端の地上局を簡単に構築できます。これらの地上局は、Docker などの仮想化技術を使用して複製でき、より大きな領域をカバーできるようになります。世界中に散在する RPi 搭載の地上局は、次のことを行います:
16 |
17 | * アンテナを備えたSDR受信機を使用して、高度と見通し線に応じて約 100-150 マイルの飛行に関する情報を受信します。
18 | * ネットワークに接続された IoT デバイスとして機能し、スケーラブルで安全で信頼性が高くオープンなクラウド環境で実行されるクラウドベースの航空交通管制に Message Queuing Telemetry Transport (MQTT) メッセージとしてフライト情報を発行します。
19 |
20 | クラウドベースの航空交通管制は、CloudFoundry オープンテクノロジーに基づく IBM の Open Cloud Architecture の実装であり、SoftLayer インフラストラクチャに基づく IBM の Cloud Platform-As-A-Service (PaaS) を使用して実装できます。
21 | 地上局はネットワークに接続され、MQTTメッセージとしてフライト情報を送信する IoT デバイスとしてモデル化されているため、IBM Cloud 内で Internet of Things(IoT) Platform サービスを使用することは理にかなっています。
22 | また、それは単に地上局の数をスケールさせるためだけではなく、フライトデータを使用して分析アプリケーション、視覚化ダッシュボードなどを構成できるように、すべてのイベントを受信するためのファネルポイントとして機能します。
23 |
24 | また、IoT プラットフォームサービスは、接続されているすべての iOS デバイスにフライト情報を提供することができます。
25 | iOS デバイス上で実行される Swift ベースのモバイルアプリは、拡張現実 (AR: Augmented Reality) を使用して、今見ている実際の景色に重ね合わせて、その方向に向かうフライトをレンダリングすることができます。
26 |
27 | ## アーキテクチャー
28 |
29 | 次の図は、安価な地上局を利用して航空便を追跡するクラウドベースの航空交通管制に関するハイレベルのアーキテクチャを示しています。
30 |
31 | 
32 |
33 |
34 | ## アプリケーションのワークフロー
35 |
36 | 
37 |
38 | 1. 地上局を表す、SDR レシーバー搭載の Raspberry PI が、民間飛行機からの ADS-B メッセージを受信してデコードし、JSON ペイロードを含む MQTT メッセージを IoT Platform にパブリッシュします。
39 | 2. 該当するデバイス・タイプとデバイス ID を持つ IoT Platform が MQTT メッセージを受信し、トピックの 1 つ上で使用できるようにします。
40 | 3. 必要に応じて、Streaming Analytics サービスが IoT Platform 内のトピックにサブスクライブしてメッセージを処理することもできます。
41 | 4. Streaming Analytics サービスからのデータを使用してダッシュボードが作成されます。
42 | 5. アプリは IoT Platform 内のトピックにサブスクライブし、Weather Company Data API を呼び出してフライト情報と気象情報を地図および拡張現実ビューにレンダリングします。
43 | 6. Weather Company Data Service では、座標を使用して気象データにアクセスするための API を公開しています。
44 |
45 | ## Raspberry Pi による ADS-B 地上局
46 |
47 | Raspberry Pi による ADS-B 地上局を建設するための指示は [こちら](adsb.ground.station/README-ja.md) にあります。
48 |
49 | ## Swift ベースの iOS アプリ
50 |
51 | Swift ベースの iOS アプリを使用してフライトを追跡する手順は [こちら](ARFlightTracker-iOS-Swift/README-ja.md) にあります。
52 |
53 | # ライセンス
54 |
55 | [Apache 2.0](LICENSE)
56 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/Model/ARGeoCoordinate.m:
--------------------------------------------------------------------------------
1 | //
2 | // ARGeoCoordinate.m
3 | // ARKitDemo
4 | //
5 | // Created by Haseman on 8/1/09.
6 | // Copyright 2009 Zac White. All rights reserved.
7 | //
8 |
9 | #import "ARGeoCoordinate.h"
10 |
11 |
12 | @implementation ARGeoCoordinate
13 |
14 | @synthesize geoLocation;
15 |
16 | @synthesize radialDistance, inclination, azimuth;
17 |
18 | @synthesize dataObject;
19 |
20 | - (float)angleFromCoordinate:(CLLocationCoordinate2D)first toCoordinate:(CLLocationCoordinate2D)second {
21 | float longitudinalDifference = second.longitude - first.longitude;
22 | float latitudinalDifference = second.latitude - first.latitude;
23 | float possibleAzimuth = (M_PI * .5f) - atan(latitudinalDifference / longitudinalDifference);
24 | if (longitudinalDifference > 0) return possibleAzimuth;
25 | else if (longitudinalDifference < 0) return possibleAzimuth + M_PI;
26 | else if (latitudinalDifference < 0) return M_PI;
27 |
28 | return 0.0f;
29 | }
30 |
31 | - (void)calibrateUsingOrigin:(CLLocation *)origin useAltitude:(BOOL) useAltitude {
32 |
33 | if (!self.geoLocation) return;
34 |
35 | double baseDistance = [origin distanceFromLocation:self.geoLocation];
36 |
37 | self.radialDistance = sqrt(pow(origin.altitude - self.geoLocation.altitude, 2) + pow(baseDistance, 2));
38 |
39 | float angle = sin(ABS(origin.altitude - self.geoLocation.altitude) / self.radialDistance);
40 |
41 | if (!useAltitude) {
42 | angle = 0;
43 | }
44 |
45 | if (origin.altitude > self.geoLocation.altitude) angle = -angle;
46 |
47 | self.inclination = angle;
48 | self.azimuth = [self angleFromCoordinate:origin.coordinate toCoordinate:self.geoLocation.coordinate];
49 | }
50 |
51 | + (ARGeoCoordinate *)coordinateWithLocation:(CLLocation *)location {
52 | ARGeoCoordinate *newCoordinate = [[ARGeoCoordinate alloc] init];
53 | newCoordinate.geoLocation = location;
54 |
55 | return newCoordinate;
56 | }
57 |
58 | - (NSUInteger)hash{
59 | return ([dataObject hash] + (int)(self.radialDistance + self.inclination + self.azimuth));
60 | }
61 |
62 | - (BOOL)isEqual:(id)other {
63 | if (other == self)
64 | return YES;
65 | if (!other || ![other isKindOfClass:[self class]])
66 | return NO;
67 | return [self isEqualToCoordinate:other];
68 | }
69 |
70 | - (BOOL)isEqualToCoordinate:(ARGeoCoordinate *)otherCoordinate {
71 | if (self == otherCoordinate) return YES;
72 |
73 | BOOL equal = self.radialDistance == otherCoordinate.radialDistance;
74 | equal &= self.inclination == otherCoordinate.inclination;
75 | equal &= self.azimuth == otherCoordinate.azimuth;
76 | equal &= self.dataObject == otherCoordinate.dataObject;
77 |
78 | return equal;
79 | }
80 |
81 | - (NSString *)description {
82 | return [NSString stringWithFormat:@"r: %.3fm φ: %.3f° θ: %.3f°", self.radialDistance, radiansToDegrees(self.azimuth), radiansToDegrees(self.inclination)];
83 | }
84 |
85 |
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/MAINTAINERS.md:
--------------------------------------------------------------------------------
1 | # Maintainers Guide
2 |
3 | This guide is intended for maintainers - anybody with commit access to one or
4 | more Code Pattern repositories.
5 |
6 | ## Methodology
7 |
8 | This repository does not have a traditional release management cycle, but
9 | should instead be maintained as as a useful, working, and polished reference at
10 | all times. While all work can therefore be focused on the master branch, the
11 | quality of this branch should never be compromised.
12 |
13 | The remainder of this document details how to merge pull requests to the
14 | repositories.
15 |
16 | ## Merge approval
17 |
18 | The project maintainers use LGTM (Looks Good To Me) in comments on the pull
19 | request to indicate acceptance prior to merging. A change requires LGTMs from
20 | two project maintainers. If the code is written by a maintainer, the change
21 | only requires one additional LGTM.
22 |
23 | ## Reviewing Pull Requests
24 |
25 | We recommend reviewing pull requests directly within GitHub. This allows a
26 | public commentary on changes, providing transparency for all users. When
27 | providing feedback be civil, courteous, and kind. Disagreement is fine, so long
28 | as the discourse is carried out politely. If we see a record of uncivil or
29 | abusive comments, we will revoke your commit privileges and invite you to leave
30 | the project.
31 |
32 | During your review, consider the following points:
33 |
34 | ### Does the change have positive impact?
35 |
36 | Some proposed changes may not represent a positive impact to the project. Ask
37 | whether or not the change will make understanding the code easier, or if it
38 | could simply be a personal preference on the part of the author (see
39 | [bikeshedding](https://en.wiktionary.org/wiki/bikeshedding)).
40 |
41 | Pull requests that do not have a clear positive impact should be closed without
42 | merging.
43 |
44 | ### Do the changes make sense?
45 |
46 | If you do not understand what the changes are or what they accomplish, ask the
47 | author for clarification. Ask the author to add comments and/or clarify test
48 | case names to make the intentions clear.
49 |
50 | At times, such clarification will reveal that the author may not be using the
51 | code correctly, or is unaware of features that accommodate their needs. If you
52 | feel this is the case, work up a code sample that would address the pull
53 | request for them, and feel free to close the pull request once they confirm.
54 |
55 | ### Does the change introduce a new feature?
56 |
57 | For any given pull request, ask yourself "is this a new feature?" If so, does
58 | the pull request (or associated issue) contain narrative indicating the need
59 | for the feature? If not, ask them to provide that information.
60 |
61 | Are new unit tests in place that test all new behaviors introduced? If not, do
62 | not merge the feature until they are! Is documentation in place for the new
63 | feature? (See the documentation guidelines). If not do not merge the feature
64 | until it is! Is the feature necessary for general use cases? Try and keep the
65 | scope of any given component narrow. If a proposed feature does not fit that
66 | scope, recommend to the user that they maintain the feature on their own, and
67 | close the request. You may also recommend that they see if the feature gains
68 | traction among other users, and suggest they re-submit when they can show such
69 | support.
70 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARFlightAnnotationCustom.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARFlightAnnotationCustom.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 12/6/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | open class ARFlightAnnotationViewCustom: ARFlightAnnotationView, UIGestureRecognizerDelegate
13 | {
14 | open var titleLabel: UILabel?
15 | open var infoButton: UIButton?
16 |
17 | override open func didMoveToSuperview()
18 | {
19 | super.didMoveToSuperview()
20 | if self.titleLabel == nil
21 | {
22 | self.loadUi()
23 | }
24 | }
25 |
26 | func loadUi()
27 | {
28 | // Title label
29 | self.titleLabel?.removeFromSuperview()
30 | let label = UILabel()
31 | label.font = UIFont.systemFont(ofSize: 10)
32 | label.numberOfLines = 0
33 | label.backgroundColor = UIColor.clear
34 | label.textColor = UIColor.white
35 | self.addSubview(label)
36 | self.titleLabel = label
37 |
38 | // Info button
39 | self.infoButton?.removeFromSuperview()
40 | let button = UIButton(type: UIButtonType.detailDisclosure)
41 | button.isUserInteractionEnabled = false // Whole view will be tappable, using it for appearance
42 | self.addSubview(button)
43 | self.infoButton = button
44 |
45 | // Gesture
46 | let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ARFlightAnnotationViewCustom.tapGesture))
47 | self.addGestureRecognizer(tapGesture)
48 |
49 | // Other
50 | self.backgroundColor = UIColor.black.withAlphaComponent(0.5)
51 | self.layer.cornerRadius = 5
52 |
53 | if self.annotation != nil
54 | {
55 | self.bindUi()
56 | }
57 | }
58 |
59 | func layoutUi()
60 | {
61 | let buttonWidth: CGFloat = 40
62 | let buttonHeight: CGFloat = 40
63 |
64 | self.titleLabel?.frame = CGRect(x: 10, y: 0, width: self.frame.size.width - buttonWidth - 5, height: self.frame.size.height);
65 | self.infoButton?.frame = CGRect(x: self.frame.size.width - buttonWidth, y: self.frame.size.height/2 - buttonHeight/2, width: buttonWidth, height: buttonHeight);
66 | }
67 |
68 | // This method is called whenever distance/azimuth is set
69 | override open func bindUi()
70 | {
71 | if let annotation = self.annotation, let title = annotation.title
72 | {
73 | let distance = annotation.distanceFromUser > 1000 ? String(format: "%.1fkm", annotation.distanceFromUser / 1000) : String(format:"%.0fm", annotation.distanceFromUser)
74 | self.titleLabel?.text = String(format: "%@\nAZ: %.0f°\nDST: %@", title, annotation.azimuth, distance)
75 | }
76 | }
77 |
78 | open override func layoutSubviews()
79 | {
80 | super.layoutSubviews()
81 | self.layoutUi()
82 | }
83 |
84 | open func tapGesture()
85 | {
86 | if let annotation = self.annotation
87 | {
88 | let alertView = UIAlertView(title: annotation.title, message: "Tapped", delegate: nil, cancelButtonTitle: "OK")
89 | alertView.show()
90 | }
91 | }
92 |
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARFlightConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARFlightConfiguration.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 12/5/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 | import UIKit
12 |
13 | let LAT_LON_FACTOR: CGFloat = 1.33975031663 // Used in azimuzh calculation, don't change
14 | //let LAT_LON_FACTOR: CGFloat = 1 // Used in azimuzh calculation, don't change
15 | let VERTICAL_SENS: CGFloat = 960
16 | let H_PIXELS_PER_DEGREE: CGFloat = 14 // How many pixels per degree
17 | //let H_PIXELS_PER_DEGREE: CGFloat = 1 // How many pixels per degree
18 | let OVERLAY_VIEW_WIDTH: CGFloat = 360 * H_PIXELS_PER_DEGREE // 360 degrees x sensitivity
19 |
20 | let MAX_VISIBLE_ANNOTATIONS: Int = 500 // Do not change, can affect performance
21 | let MAX_VERTICAL_LEVELS: Double = 10 // Do not change, can affect performance
22 |
23 | internal func radiansToDegrees(_ radians: Double) -> Double
24 | {
25 | return (radians) * (180.0 / M_PI)
26 | }
27 |
28 | internal func degreesToRadians(_ degrees: Double) -> Double
29 | {
30 | return (degrees) * (M_PI / 180.0)
31 | }
32 |
33 | /// Normalizes degree to 360
34 | internal func normalizeDegree(_ degree: Double) -> Double
35 | {
36 | var degreeNormalized = fmod(degree, 360)
37 | if degreeNormalized < 0
38 | {
39 | degreeNormalized = 360 + degreeNormalized
40 | }
41 | return degreeNormalized
42 | }
43 |
44 | /// Finds shortes angle distance between two angles. Angles must be normalized(0-360)
45 | internal func deltaAngle(_ angle1: Double, angle2: Double) -> Double
46 | {
47 | var deltaAngle = angle1 - angle2
48 |
49 | if deltaAngle > 180
50 | {
51 | deltaAngle -= 360
52 | }
53 | else if deltaAngle < -180
54 | {
55 | deltaAngle += 360
56 | }
57 | return deltaAngle
58 | }
59 |
60 | ///// DataSource provides the ARFlightViewController with the information needed to display annotations.
61 | //@objc public protocol ARDataSource : NSObjectProtocol
62 | //{
63 | // /// Asks the data source to provide annotation view for annotation. Annotation view must be subclass of ARFlightAnnotationView.
64 | // func ar(_ arViewController: ARFlightViewController, viewForAnnotation: FlightAnnotation) -> ARFlightAnnotationView
65 | //
66 | // /**
67 | // * READ BEFORE IMPLEMENTING
68 | // * ARFlightViewController tracks user movement and shows/hides annotations accordingly. But if there is huge amount
69 | // * of annotations or for some other reason annotations cannot be set all at once, this method can be used to
70 | // * set annotations part by part.
71 | // *
72 | // * Use ARFlightViewController.trackingManager.reloadDistanceFilter to change how often this is called.
73 | // *
74 | // * - parameter arViewController: ARFlightViewController instance
75 | // * - parameter location: Current location of the user
76 | // * - returns: Annotations to load, previous annotations are removed
77 | // */
78 | // @objc optional func ar(_ arViewController: ARFlightViewController, shouldReloadWithLocation location: CLLocation) -> [FlightAnnotation]
79 | //
80 | //}
81 |
82 |
83 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/README.md:
--------------------------------------------------------------------------------
1 | *Read this in other languages: [日本](README-ja.md).*
2 |
3 | # ARFlightTracker
4 | ARFlightTracker is an iOS based app which tracks flight pushed by SDR/ADSB message receiver through MQTT server. The app will display all the flights travelling point to point within the range of the receiver. AR Flight tracker app is connected to IBM MQTT server to a topic which receives new/updated flight information based on which is rendered into the map view. The data is fed to the topic by SDR/ADSB message receiver. The map also shows animated view of flights heading in a particular direction towards its destination. The callout view on the flight contains flight details with weather in current location of the flight.
5 |
6 | ## Map View
7 | Map View displays all the flights on a default map provided in the iOS device. The flight orientation is adjusted based on the current heading information in the payload. As the app receives MQTT messages, the flight will be seen moving in the direction towards its destination. A flight can be tapped to see more details such as such as flight number, altitude, distance etc. Figure below shows the rendering of the Map View with flights in the Swift-based app on an iOS device:
8 |
9 | 
10 |
11 | ## Augmented Reality View
12 | The user can tap the AR View tab in the app to switch to the AR-based View. In this mode, the app opens up a camera view where the user can point to a flight to be able to see the flight data on a callout that overlays on top of the flight in the real world. As the flight is moving in the real world, the callout with the information moves along with the flight in the camera view. The AR view also displays a compass based on the device heading and a radar view displaying all the flights within the viewing angle. Figure 5 below shows the rendering of the Augmented Reality View with flights in the Swift-based app on an iOS device.
13 |
14 | 
15 |
16 | # Pre-requisites
17 | - Swift 3
18 | - Xcode 8.0+
19 | - CocoaPod - https://cocoapods.org/
20 | - iOS 10+
21 |
22 |
23 | # Dependencies
24 | - CocoaMQTT - Note: moving to aphid client by IBM
25 | - SwiftyJSON
26 | - ios-arkit for iphone - (part of the code base)
27 |
28 | # Steps:
29 | 1. cd ARFlightTracker-iOS-Swift && open ARFlightTracker-iOS-Swift.xcworkspace using Xcode.
30 | 2. Run `pod install` from the project directory. This will install the dependencies define in `Podfile`
31 | 3. Update `util/MQTTConnection.swift` using Xcode editor. You have to create Internet of Things service in IBM Bluemix console to get the credentials. The credentials looks like as shown below:
32 | ```
33 | API_KEY = ""
34 | API_TOKEN = ""
35 | IOT_CLIENT = "a::Flights"
36 | IOT_HOST = ".messaging.internetofthings.ibmcloud.com"
37 | IOT_PORT = 1883 (DEFAULT)
38 | IOT_TOPIC = "iot-2/type//id//evt/flight/fmt/json"
39 | ```
40 | 4. Update `util/RestCall.swift` to with credentials for IBM Weather API. Create Weather API service using IBM Bluemix console:
41 | ```
42 | private static let WEATHER_API_USERNAME : String = ""
43 | private static let WEATHER_API_PASSWORD : String = ""
44 | ```
45 | 5. Build and Run
46 |
47 | # Test Mode:
48 | You can run the app in test mode to be independant of IBM Bluemix MQTT server. In ViewController you can set the flag
49 | `flightTestMode = true`
50 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/FlightBubble.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlightBubble.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 1/12/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 |
10 | import UIKit
11 |
12 | open class FlightBubble: ARAnnotationView, UIGestureRecognizerDelegate
13 | {
14 | open var titleLabel: UILabel?
15 | open var weatherImageView: UIImageView?
16 |
17 | override open func didMoveToSuperview()
18 | {
19 | super.didMoveToSuperview()
20 | if self.titleLabel == nil
21 | {
22 | self.loadUi()
23 | }
24 | }
25 |
26 | func loadUi()
27 | {
28 | // Title label
29 | self.titleLabel?.removeFromSuperview()
30 | let label = UILabel()
31 | label.font = UIFont.systemFont(ofSize: 10)
32 | label.numberOfLines = 0
33 | label.backgroundColor = UIColor.clear
34 | label.textColor = UIColor.white
35 | self.addSubview(label)
36 | self.titleLabel = label
37 |
38 | // weather image
39 | self.weatherImageView?.removeFromSuperview()
40 | let weatherImg = UIImageView()
41 | self.weatherImageView = weatherImg
42 | self.addSubview(weatherImg)
43 |
44 | // Other
45 | self.backgroundColor = UIColor.black.withAlphaComponent(0.5)
46 | self.layer.cornerRadius = 5
47 |
48 | if self.annotation != nil
49 | {
50 | self.bindUi()
51 | }
52 | }
53 |
54 | func layoutUi()
55 | {
56 | let buttonWidth: CGFloat = 40
57 | let buttonHeight: CGFloat = 40
58 |
59 | self.titleLabel?.frame = CGRect(x: 10, y: 0, width: self.frame.size.width - buttonWidth - 5, height: self.frame.size.height);
60 | self.weatherImageView?.frame = CGRect(x: self.frame.size.width - buttonWidth, y: self.frame.size.height/2 - buttonHeight/2, width: buttonWidth, height: buttonHeight);
61 | }
62 |
63 | // This method is called whenever distance/azimuth is set
64 | override open func bindUi()
65 | {
66 | if let annotation = self.annotation, let title = annotation.title
67 | {
68 | let distance = String(format:"%.0fm", annotation.radialDistance)
69 |
70 | var weatherMessage: String = ""
71 |
72 | RestCall().fetchWeatherBasedOnCurrentLocation(latitude: String(annotation.coordinate.latitude),longitude: String(annotation.coordinate.longitude)){
73 | (result: [String: Any]) in
74 |
75 | let weatherIconUrl: String = result["weatherIconUrl"] as! String
76 | let imageUrl = URL(string: weatherIconUrl)
77 | let data = try? Data(contentsOf: imageUrl!)
78 | let city = "\(result["city"]!)"
79 | let image = UIImage(data: data!)
80 | let currentTemp = "\(result["temperature"]!)°F"
81 | let weatherDesc = "\(result["description"]!)"
82 |
83 | weatherMessage = String(format: "\nCity:%@, %@ %@", city,currentTemp,weatherDesc)
84 |
85 | DispatchQueue.main.async(execute: { () -> Void in
86 | self.weatherImageView?.image = image
87 | self.titleLabel?.text?.append(weatherMessage)
88 | })
89 |
90 | }
91 |
92 | let text = String(format: "%@\nAlt: %.0f\nDst: %@", title, annotation.altitude, distance)
93 | self.titleLabel?.text = text
94 | }
95 | }
96 |
97 | open override func layoutSubviews()
98 | {
99 | super.layoutSubviews()
100 | self.layoutUi()
101 | }
102 |
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WARNING: This repository is no longer maintained :warning:
2 |
3 | > This content is no longer being updated or maintained. The content is provided “as is.” Given the rapid evolution of technology, some content, steps, or illustrations may have changed.
4 |
5 |
6 | *Read this in other languages: [中国](README-cn.md), [日本](README-ja.md).*
7 |
8 |
9 | # air-traffic-control
10 | [](https://travis-ci.org/IBM/air-traffic-control)
11 |
12 | This repository contains instructions to build modern Cloud-based Air Traffic Control using IBM Cloud.
13 |
14 | > Note: A Raspberry Pi, SDR receiver with antenna, and an iOS device is required to fully complete this Code Pattern.
15 |
16 | The Air Traffic Control Service receives flight information from a Raspberry Pi powered ADS-B Ground Stations with Software Defined Radio(SDR) to receive ADS-B messages directly from commercial flights and publish MQTT messages to the IBM IoT Platform running in IBM Cloud. It also supports a Swift-based iOS app to track flights using the Augmented Reality toolkit by receiving MQTT messages from the IoT Platform. The app will display all the flights traveling point to point within the range of the receiver.
17 |
18 | With the advances in the field of avionics and the availability of cheap computing resources such as Raspberry Pi (RPi), one can very easily build a state of the art Ground Station. These Ground Stations can be replicated trivially using virtualization technologies such as Docker to be able to cover large swathes of areas. The RPi-powered Ground Stations, scattered all over the world, will do the following:
19 | * Use a SDR receiver with an antenna to receive information about flights that are in approximately 100-150 miles radius depending on the altitude and the line of sight.
20 | * Act as network connected IoT devices to publish the flight information as Message Queuing Telemetry Transport (MQTT) messages to a Cloud-based Air Traffic Control running in scalable, secure, and, reliable, and open cloud infrastructure.
21 |
22 | The Cloud-based Air Traffic Control can be implemented using IBM's Cloud Platform-As-A-Service (PaaS) which is an implementation of IBM’s Open Cloud Architecture based on CloudFoundry open technology and based on SoftLayer infrastructure. Since the Ground Stations are modeled as IoT devices that are network connected and send flight information as MQTT messages, it makes sense to use the Internet of Things(IoT) Platform service within IBM Cloud as it can not only scale elastically with the number of Ground Stations but also serve as funneling point to receive all the events so that one can compose analytics applications, visualization dashboards, etc. using the flight data.
23 |
24 | IoT Platform service will also be able to serve the flight information to all the iOS devices that are connected to it. A Swift-based mobile app running on an iOS device can use Augmented Reality to render flights that are headed in that direction on the screen before they show up outside one’s window!
25 |
26 | ## Architecture
27 | Following figure shows the high-level architecture of a Cloud-based Air Traffic Control that relies on inexpensive Ground Stations to track flights
28 |
29 | 
30 |
31 |
32 | ## Application Workflow
33 | 
34 |
35 | 1. Raspberry Pi streams airtraffic data to IoT Platform
36 | 2. MQTT streams data to IoT Analytics dashboard for analysis
37 | 3. Current weather is pulled from the Weather Service API
38 | 4. Analytics and weather data are sent to phone device
39 |
40 |
41 | ## Raspberry Pi powered ADS-B Ground Station
42 |
43 | The instructions for building a Raspberry Pi powered Ground Station are [here](https://github.com/IBM/air-traffic-control/blob/master/adsb.ground.station/README.md).
44 |
45 | ## Swift-based iOS App
46 |
47 | The instructions for tracking flights using Swift-based iOS app are [here](https://github.com/IBM/air-traffic-control/blob/master/ARFlightTracker-iOS-Swift/README.md).
48 |
49 | # License
50 |
51 | This code pattern is licensed under the Apache Software License, Version 2. Separate third party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the [Developer Certificate of Origin, Version 1.1 (DCO)](https://developercertificate.org/) and the [Apache Software License, Version 2](http://www.apache.org/licenses/LICENSE-2.0.txt).
52 |
53 | [Apache Software License (ASL) FAQ](http://www.apache.org/foundation/license-faq.html#WhatDoesItMEAN)
54 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARViewHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARViewHelper.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 12/23/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MapKit
11 |
12 |
13 | class ARViewHelper {
14 |
15 | public static let OnePI = M_PI
16 | public static let TwoPI = M_PI * 2.0
17 | public static let HalfPI = M_PI / 2.0
18 | public static let RadiansConst = M_PI / 180.0
19 | public static let DegreesConst = M_PI * 180.0
20 |
21 |
22 | func GetScreenXCoordinate(annotationView: ARFlightAnnotationView, camera: CLLocation, yaw: Double, viewWidth: Double) -> CGFloat
23 | {
24 |
25 | //Result
26 | var screenX = 0 as CGFloat //Location of POI on screen
27 |
28 | var screenKoefX = 0.0
29 |
30 | let camera2D = camera.coordinate
31 | let annotation = annotationView.annotation!
32 | let poi2D = annotation.coordinate
33 |
34 | let rise = poi2D.latitude - camera2D.latitude
35 | let run = poi2D.longitude - camera2D.longitude
36 | //var inclinationXPOI = (float)Math.Atan(rise / run) - HalfPI;
37 | var inclinationXPOI = atan2(rise, run) - ARViewHelper.HalfPI
38 | if (run < 0.0){
39 | inclinationXPOI += ARViewHelper.OnePI;
40 | }
41 |
42 | if (inclinationXPOI < 0.0){
43 | inclinationXPOI += ARViewHelper.TwoPI
44 | }
45 |
46 | // Heading correction
47 | var inclinationX = 0.0
48 | if (yaw < 0)
49 | {
50 | inclinationX = yaw + ARViewHelper.TwoPI
51 | }
52 | else
53 | {
54 | inclinationX = yaw
55 | }
56 |
57 | //region Coordinate X
58 | //var distanceX = (float)Math.Sqrt((float)2 - (float)2.0 * (float)Math.Cos(inclinationX - inclinationXPOI));
59 | var distanceX = sqrt(2 - 2.0 * cos(inclinationX - inclinationXPOI))
60 | if (inclinationX < inclinationXPOI)
61 | {
62 | distanceX = -distanceX
63 | }
64 | if (inclinationX <= ARViewHelper.TwoPI && inclinationX >= (3 * ARViewHelper.HalfPI) && inclinationXPOI >= 0 && inclinationXPOI < (ARViewHelper.HalfPI))
65 | {
66 | distanceX = -distanceX
67 | }
68 |
69 | screenKoefX = distanceX
70 | screenX = CGFloat(viewWidth.multiplied(by: screenKoefX))
71 |
72 | return screenX
73 | //return CGFloat(viewWidth.divided(by: 2.0)) + screenX
74 | //endregion
75 | }
76 |
77 |
78 |
79 | func GetScreenYCoordinate(annotationView: ARFlightAnnotationView, camera: CLLocation, roll: Double, viewHeight: Double) -> CGFloat
80 | {
81 |
82 | var screenY = 0 as CGFloat
83 |
84 | let annotation = annotationView.annotation!
85 | let poi2D = annotation.coordinate
86 | //let distanceAB = MKGeometry.MetersBetweenMapPoints(camera, poi.LastLocation);
87 | let distanceAB = camera.distance(from: CLLocation.init(latitude:poi2D.latitude, longitude: poi2D.longitude))
88 |
89 | //POI
90 | let poiAltitude = annotation.altitude as Double
91 | //Camera location
92 | let altitude = camera.altitude as Double
93 | var screenKoefY = 0.0
94 |
95 |
96 | //var inclinationYPOI = (float)Math.Atan((float)(poiAltitude - altitude) / distanceAB);
97 | var inclinationYPOI = atan2(poiAltitude - altitude, distanceAB)
98 | if (inclinationYPOI <= 0.0){
99 | inclinationYPOI += ARViewHelper.TwoPI
100 | }
101 |
102 | //Heading correction
103 | var inclinationY = abs(roll) - ARViewHelper.HalfPI;
104 | if (inclinationY <= 0.0){
105 | inclinationY += ARViewHelper.TwoPI
106 | }
107 |
108 | screenKoefY = sqrt(2 - 2.0 * cos(inclinationYPOI - inclinationY));
109 | if (inclinationYPOI < inclinationY)
110 | {
111 | screenKoefY = -screenKoefY
112 | }
113 | if (inclinationYPOI <= ARViewHelper.TwoPI && inclinationYPOI >= (3 * ARViewHelper.HalfPI) && inclinationY >= 0 && inclinationY <= (ARViewHelper.HalfPI))
114 | {
115 | screenKoefY = -screenKoefY
116 | }
117 | if (inclinationY <= ARViewHelper.TwoPI && inclinationY >= (3 * ARViewHelper.HalfPI) && inclinationYPOI >= 0 && inclinationYPOI <= (ARViewHelper.HalfPI))
118 | {
119 | screenKoefY = -screenKoefY
120 | }
121 |
122 | screenY = CGFloat(viewHeight.multiplied(by: screenKoefY))
123 |
124 | return screenY
125 | //endregion
126 |
127 | //return CGFloat(viewHeight.divided(by: 2.0)) - screenY
128 |
129 |
130 | }
131 |
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/adsb.ground.station/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.ibm.iot
6 | adsb.ground.station
7 | develop-SNAPSHOT
8 | jar
9 |
10 | adsb.ground.station
11 | https://github.com/ibm/air-traffic-control/adsb.ground.station
12 |
13 |
14 | UTF-8
15 | 4.12-beta-1
16 | 1.8
17 | 1.8
18 | 1.8+
19 |
20 |
21 |
22 |
23 |
24 | org.opensky-network
25 | libadsb
26 | 2.0
27 |
28 |
29 | com.ibm.messaging
30 | watson-iot
31 | 0.2.2
32 |
33 |
34 | org.slf4j
35 | slf4j-api
36 | 1.7.21
37 |
38 |
39 | org.slf4j
40 | slf4j-log4j12
41 | 1.7.21
42 |
43 |
44 | junit
45 | junit
46 | ${junit.version}
47 | test
48 |
49 |
50 |
51 |
52 |
53 |
54 | org.apache.maven.plugins
55 | maven-shade-plugin
56 |
57 |
58 | package
59 |
60 | shade
61 |
62 |
63 |
64 |
65 | *:*
66 |
67 | META-INF/*.SF
68 | META-INF/*.DSA
69 | META-INF/*.RSA
70 |
71 |
72 |
73 |
74 |
75 | com.ibm.iot.adsb.ground.station.AdsbClient
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | org.apache.maven.plugins
84 | maven-jar-plugin
85 | 2.5
86 |
87 |
88 |
89 |
90 |
91 | true
92 | all-permissions
93 |
94 |
95 |
96 |
97 |
98 | org.apache.maven.plugins
99 | maven-dependency-plugin
100 |
101 |
102 |
103 | org.apache.maven.plugins
104 | maven-assembly-plugin
105 |
106 |
107 |
108 | src/main/assembly/filter.properties
109 |
110 |
111 |
112 |
113 | verify
114 |
115 | attached
116 |
117 |
118 |
119 | src/main/assembly/sources.xml
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARKit/LocalizationHelper.m:
--------------------------------------------------------------------------------
1 | //
2 | // LocalizationHelper.m
3 | // ARModule
4 | //
5 | // Created by Carlos on 06/06/11.
6 | // Copyright 2011 __MyCompanyName__. All rights reserved.
7 | //
8 | #import "UIKit/UIKit.h"
9 | #import "LocalizationHelper.h"
10 |
11 | static LocalizationHelper *sharedHelper;
12 |
13 | @implementation LocalizationHelper
14 |
15 | #pragma mark - Singleton Initialization
16 |
17 | + (LocalizationHelper *) sharedHelper {
18 | @synchronized ([LocalizationHelper class]) {
19 | if (!sharedHelper) {
20 | sharedHelper = [[LocalizationHelper alloc] init];
21 | }
22 | return sharedHelper;
23 | }
24 | return nil;
25 | }
26 |
27 | - (id) init {
28 | if ((self = [super init])) {
29 | locationManager = [[CLLocationManager alloc] init];
30 | locationManager.delegate = self;
31 | locationManager.headingFilter = kCLHeadingFilterNone;
32 | locationManager.desiredAccuracy = kCLLocationAccuracyBest;
33 | locationManager.distanceFilter = kCLDistanceFilterNone;
34 | [locationManager requestAlwaysAuthorization];
35 |
36 | isHeadingInfoAvailable = [CLLocationManager headingAvailable];
37 |
38 | onceRegistered = [[NSMutableArray alloc] init];
39 | registered = [[NSMutableArray alloc] init];
40 |
41 | localizationStatus = kLocalizationUnknown;
42 | }
43 | return self;
44 | }
45 |
46 | #pragma mark - Clients management
47 |
48 | - (void) registerForUpdates:(id)receiver once:(BOOL)once {
49 | if (localizationStatus == kLocalizationDisabled) {
50 | [self locationManager:locationManager didFailWithError:nil];
51 | } else {
52 | if (once) {
53 | if (lastLocation) {
54 | [self locationManager:locationManager didUpdateLocations:@[lastLocation]];
55 | } else {
56 | [onceRegistered addObject:receiver];
57 | }
58 | } else {
59 | [registered addObject:receiver];
60 | }
61 | if ([registered count] + [onceRegistered count] == 1) {
62 | [locationManager startUpdatingHeading];
63 | [locationManager startUpdatingLocation];
64 | }
65 | }
66 | }
67 |
68 | - (void) deregisterForUpdates:(id)receiver {
69 | if ([registered containsObject:receiver]) {
70 | [registered removeObject:receiver];
71 | }
72 | if ([onceRegistered containsObject:receiver]) {
73 | [onceRegistered removeObject:receiver];
74 | }
75 |
76 | if (![registered count] && ![onceRegistered count]) {
77 | [locationManager stopUpdatingHeading];
78 | [locationManager stopUpdatingLocation];
79 | }
80 | }
81 |
82 | - (BOOL) canReceiveHeadingUpdates {
83 | return isHeadingInfoAvailable;
84 | }
85 |
86 | #pragma mark - CLLocator delegate methods
87 |
88 | - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
89 | localizationStatus = kLocalizationEnabled;
90 | CLLocation *newLocation = [locations lastObject];
91 | NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
92 | if (locationAge > 5.0) {
93 | //Probably a cached result. Restart
94 | [manager stopUpdatingLocation];
95 | [manager startUpdatingLocation];
96 | return;
97 | }
98 |
99 | for (id delegate in registered) {
100 | [delegate locationFound:newLocation];
101 | }
102 | for (id delegate in onceRegistered) {
103 | [delegate locationFound:newLocation];
104 | }
105 | [onceRegistered removeAllObjects];
106 | if (![registered count]) {
107 | [manager stopUpdatingLocation];
108 | [manager stopUpdatingHeading];
109 | }
110 | }
111 |
112 | - (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager {
113 | return YES;
114 | }
115 |
116 | - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
117 | for (id delegate in registered) {
118 | [delegate headingFound:newHeading];
119 | }
120 | for (id delegate in onceRegistered) {
121 | [delegate headingFound:newHeading];
122 | }
123 | }
124 |
125 | - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
126 | [manager stopUpdatingLocation];
127 | [loadingView removeFromSuperview];
128 | localizationStatus = kLocalizationDisabled;
129 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@""
130 | message:NSLocalizedString(@"GPS_Unavailable", @"")
131 | delegate:nil
132 | cancelButtonTitle:NSLocalizedString(@"Ok", @"")
133 | otherButtonTitles:nil];
134 | [alert show];
135 | [locDelegate locationUnavailable];
136 | }
137 |
138 | @end
139 |
--------------------------------------------------------------------------------
/adsb.ground.station/src/main/java/com/ibm/iot/adsb/ground/station/IotClient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016, IBM Corporation. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.ibm.iot.adsb.ground.station;
17 |
18 | import java.io.IOException;
19 | import java.net.InetAddress;
20 | import java.net.NetworkInterface;
21 | import java.net.SocketException;
22 | import java.net.UnknownHostException;
23 | import java.util.Enumeration;
24 | import java.util.Properties;
25 |
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import com.google.gson.JsonObject;
30 | import com.ibm.iotf.client.app.ApplicationClient;
31 |
32 | public class IotClient {
33 | private static final String MAC_ADDRESS = computeMacAddress();
34 | private static final String APP_PROPERTIES_FILE_NAME = "/application.properties";
35 | private static final Properties APP_PROPERTIES = loadProperties(APP_PROPERTIES_FILE_NAME);
36 |
37 | private final Logger logger = LoggerFactory.getLogger(IotClient.class);
38 | private final String deviceType;
39 | private final String deviceId;
40 |
41 | private ApplicationClient applicationClient;
42 |
43 | public IotClient( ) {
44 | if (logger.isDebugEnabled()) {
45 | logger.debug(APP_PROPERTIES.toString());
46 | }
47 |
48 | deviceType = APP_PROPERTIES.getProperty("Device-Type");
49 | deviceId = APP_PROPERTIES.getProperty("Device-ID");
50 |
51 | // Specify unique "id" so that multiple instances of this app can concurrently publish MQTT
52 | // messages on behalf of the same IoT device without requiring a hardcoded entry in the
53 | // properties file.
54 | APP_PROPERTIES.setProperty("id", MAC_ADDRESS +
55 | "-" +
56 | String.valueOf(System.currentTimeMillis()));
57 | }
58 |
59 | public void connect() throws IOException {
60 | try {
61 | applicationClient = new ApplicationClient(APP_PROPERTIES);
62 | applicationClient.connect();
63 |
64 | if (logger.isDebugEnabled()) {
65 | logger.debug("Connected to Watson Iot Platform: " + applicationClient.isConnected());
66 | }
67 | } catch (Exception e) {
68 | logger.error("Failed to connect to Watson IoT Platform", e.getMessage());
69 | throw new IOException("Failed to connect to Watson Iot Platform", e);
70 | }
71 | }
72 |
73 | public void disconnect() {
74 | applicationClient.disconnect();
75 | applicationClient = null;
76 | }
77 |
78 | public boolean isConnected() {
79 | return (applicationClient != null) ? applicationClient.isConnected() : false;
80 | }
81 |
82 | public void publishEvent(Flight flight) throws IOException {
83 | if ((flight == null) || !flight.isReadyForTracking()) {
84 | return;
85 | }
86 |
87 | if ((applicationClient == null) || !applicationClient.isConnected()) {
88 | logger.info("Reconnecting to IBM Bluemix...");
89 | connect();
90 | logger.info("Reconnected successfully to IBM Bluemix...");
91 | }
92 |
93 | if (logger.isDebugEnabled()) {
94 | logger.debug(flight.toString());
95 | }
96 |
97 | JsonObject jsonObject = flight.getJsonObject();
98 | logger.info("Publishing MQTT message");
99 | applicationClient.publishEvent(deviceType, deviceId, "flight", jsonObject);
100 | logger.info("Published MQTT message");
101 | }
102 |
103 | public static String getMacAddress() {
104 | return MAC_ADDRESS;
105 | }
106 |
107 | private static Properties loadProperties(String propertiesFileName) {
108 | Properties props = new Properties();
109 | try {
110 | props.load(IotClient.class.getResourceAsStream(propertiesFileName));
111 | } catch (IOException ex) {
112 | System.out.println("Not able to read the properties file!");
113 | ex.printStackTrace();
114 | throw new RuntimeException(ex);
115 | }
116 |
117 | return props;
118 | }
119 |
120 | private static String computeMacAddress() {
121 | StringBuilder sb = new StringBuilder();
122 |
123 | try {
124 | InetAddress ip = InetAddress.getLocalHost();
125 | System.out.println("IP address : " + ip.getHostAddress());
126 |
127 | Enumeration networks = NetworkInterface.getNetworkInterfaces();
128 | while (networks.hasMoreElements()) {
129 | NetworkInterface network = networks.nextElement();
130 | byte[] mac = network.getHardwareAddress();
131 |
132 | if (mac != null) {
133 | System.out.print("MAC address : ");
134 | for (int i = 0; i < mac.length; i++) {
135 | sb.append(String.format("%02X", mac[i]));
136 | }
137 | System.out.println(sb.toString());
138 | return sb.toString();
139 | }
140 | }
141 | } catch (UnknownHostException | SocketException e) {
142 | e.printStackTrace();
143 | }
144 | return sb.toString();
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/FlightBubbleV1.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
32 |
39 |
46 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/adsb.ground.station/src/main/java/com/ibm/iot/adsb/ground/station/Flight.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016, IBM Corporation. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.ibm.iot.adsb.ground.station;
17 |
18 | import static java.lang.String.format;
19 |
20 | import org.opensky.libadsb.PositionDecoder;
21 |
22 | import com.google.gson.JsonObject;
23 |
24 | public class Flight {
25 | public static enum State {
26 | CACHED, NEW
27 | }
28 |
29 | private static final String SCHEMA_TYPE = "ar_flights_schema";
30 | private static final String PROPERTY_ICAO = "icao";
31 | private static final String PROPERTY_ALTITUDE = "altitudeInMeters";
32 | private static final String PROPERTY_CALLSIGN = "callSign";
33 | private static final String PROPERTY_HEADING = "headingInDegrees";
34 | private static final String PROPERTY_LATITUDE = "latitude";
35 | private static final String PROPERTY_LONGITUDE = "longitude";
36 | private static final String PROPERTY_SCHEMA_TYPE = "type";
37 | private static final String PROPERTY_VELOCITY = "velocityInMetersPerSecond";
38 | private static final String PROPERTY_CREATE_TIMESTAMP = "createdInMillis";
39 | private static final String PROPERTY_LAST_UPDATED_TIMESTAMP = "lastUpdatedInMillis";
40 | private static final String PROPERTY_GROUND_STATION_ID = "groundStationId";
41 |
42 | private static final String JSON_MESSAGE_FORMAT =
43 | "{ " +
44 | "\"icao\" : " + "\"%s\", " +
45 | "\"altitudeInMeters\" : " + "%f, " +
46 | "\"callSign\" : " + "\"%s\", " +
47 | "\"headingInDegrees\" : " + "%f, " +
48 | "\"latitude\" : " + "%f, " +
49 | "\"longitude\" : " + "%f, " +
50 | "\"groundStationId\" : " + "\"%s\", " +
51 | "\"type\" : " + "\"%s\", " +
52 | "\"velocityInMetersPerSecond\" : " + "%f, " +
53 | "\"createdInMillis\" : " + "%d, " +
54 | "\"lastUpdatedInMillis\" : " + "%d" +
55 | "}";
56 |
57 | private final String icao;
58 | private final long createdInMillis;
59 | private final PositionDecoder positionDecoder;
60 |
61 | private String callSign;
62 | private double altitudeInMeters;
63 | private double headingInDegrees;
64 | private double latitude;
65 | private double longitude;
66 | private double velocityInMetersPerSecond;
67 | private long lastUpdatedInMillis;
68 | private State state;
69 |
70 | public Flight(String icao) {
71 | if ((icao == null) || (icao.length() == 0)) {
72 | throw new NullPointerException(format("Invalid icao passed in: '%s'", icao));
73 | }
74 | this.icao = icao;
75 | this.createdInMillis = System.currentTimeMillis();
76 | this.positionDecoder = new PositionDecoder();
77 | this.latitude = 100; // Invalid latitude initialization
78 | this.longitude = 200; // Invalid longitude initialization
79 | }
80 |
81 | public String getIcao() {
82 | return icao;
83 | }
84 |
85 | public long getCreatedInMillis() {
86 | return createdInMillis;
87 | }
88 |
89 | public PositionDecoder getPositionDecoder() {
90 | return positionDecoder;
91 | }
92 |
93 | public String getType() {
94 | return SCHEMA_TYPE;
95 | }
96 |
97 | public String getCallSign() {
98 | return callSign;
99 | }
100 |
101 | public void setCallSign(String callSign) {
102 | this.callSign = callSign;
103 | }
104 |
105 | public double getAltitudeInMeters() {
106 | return altitudeInMeters;
107 | }
108 |
109 | public void setAltitudeInMeters(double altitudeInMeters) {
110 | this.altitudeInMeters = altitudeInMeters;
111 | }
112 |
113 | public double getHeadingInDegrees() {
114 | return headingInDegrees;
115 | }
116 |
117 | public void setHeadingInDegrees(double headingInDegrees) {
118 | this.headingInDegrees = headingInDegrees;
119 | }
120 |
121 | public double getLatitude() {
122 | return latitude;
123 | }
124 |
125 | public void setLatitude(double latitude) {
126 | this.latitude = latitude;
127 | }
128 |
129 | public double getLongitude() {
130 | return longitude;
131 | }
132 |
133 | public void setLongitude(double longitude) {
134 | this.longitude = longitude;
135 | }
136 |
137 | public double getVelocityInMetersPerSecond() {
138 | return velocityInMetersPerSecond;
139 | }
140 |
141 | public void setVelocityInMetersPerSecond(double velocityInMetersPerSecond) {
142 | this.velocityInMetersPerSecond = velocityInMetersPerSecond;
143 | }
144 |
145 | public long getLastUpdatedInMillis() {
146 | return lastUpdatedInMillis;
147 | }
148 |
149 | public void setLastUpdatedInMillis(long lastUpdatedInMillis) {
150 | this.lastUpdatedInMillis = lastUpdatedInMillis;
151 | }
152 |
153 | public boolean isReadyForTracking() {
154 | return ((latitude != 100) && (longitude != 200)) ? true : false;
155 | }
156 |
157 | public JsonObject getJsonObject() {
158 | assert isReadyForTracking();
159 |
160 | JsonObject jsonObject = new JsonObject();
161 | String cs = (callSign != null) ? callSign : icao;
162 |
163 | jsonObject.addProperty(PROPERTY_ICAO, icao);
164 | jsonObject.addProperty(PROPERTY_ALTITUDE, altitudeInMeters);
165 | jsonObject.addProperty(PROPERTY_CALLSIGN, cs);
166 | jsonObject.addProperty(PROPERTY_HEADING, headingInDegrees);
167 | jsonObject.addProperty(PROPERTY_LATITUDE, latitude);
168 | jsonObject.addProperty(PROPERTY_LONGITUDE, longitude);
169 | jsonObject.addProperty(PROPERTY_GROUND_STATION_ID, IotClient.getMacAddress());
170 | jsonObject.addProperty(PROPERTY_SCHEMA_TYPE, SCHEMA_TYPE);
171 | jsonObject.addProperty(PROPERTY_VELOCITY, velocityInMetersPerSecond);
172 | jsonObject.addProperty(PROPERTY_CREATE_TIMESTAMP, createdInMillis);
173 | jsonObject.addProperty(PROPERTY_LAST_UPDATED_TIMESTAMP, lastUpdatedInMillis);
174 |
175 | return jsonObject;
176 | }
177 |
178 | public String toJSON() {
179 | assert isReadyForTracking();
180 |
181 | String cs = (callSign != null) ? callSign : icao;
182 | String json = format(JSON_MESSAGE_FORMAT,
183 | icao,
184 | altitudeInMeters,
185 | cs,
186 | headingInDegrees,
187 | latitude,
188 | longitude,
189 | IotClient.getMacAddress(),
190 | SCHEMA_TYPE,
191 | velocityInMetersPerSecond,
192 | createdInMillis,
193 | lastUpdatedInMillis
194 | );
195 | return json;
196 | }
197 |
198 | public String toString() {
199 | return toJSON();
200 | }
201 |
202 | public State getState() {
203 | return state;
204 | }
205 |
206 | public void setState(State state) {
207 | this.state = state;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 11/14/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 | import Foundation
9 | import UIKit
10 | import CocoaMQTT
11 | import SwiftyJSON
12 | import CoreLocation
13 |
14 | open class ViewController: UIViewController, CocoaMQTTDelegate,CLLocationManagerDelegate{
15 |
16 | var flightTestMode : Bool = true
17 |
18 | var flightImage : [String : UIImage] = [:]
19 |
20 | var flightAnnotations : [String : FlightAnnotation] = [:]
21 |
22 | var flightAnnotationsGeo : [String : ARGeoCoordinate] = [:]
23 |
24 | let locationManager = CLLocationManager()
25 |
26 | var currentView : ViewController?
27 |
28 | var points : [ARGeoCoordinate] = []
29 |
30 | var arKitEngine : ARKitEngine? = nil
31 |
32 | open var userLocation: CLLocation?
33 |
34 | override open func viewDidLoad()
35 | {
36 | super.viewDidLoad()
37 |
38 | if(!self.flightTestMode){
39 | //connection to MQTT IoT
40 | MQTTConnection().connectToMQTT(_delegate: self)
41 | }else{
42 | print("Running in Test Mode")
43 | readTestFlights()
44 | currentView?.showTestFlights()
45 | }
46 | }
47 |
48 | public func mqtt(_ mqtt: CocoaMQTT, didConnect host: String, port: Int) {
49 | print("didConnect \(host):\(port)")
50 | }
51 |
52 | public func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) {
53 | print("didConnectAck: \(ack),rawValue: \(ack.rawValue)")
54 |
55 | if ack == .accept {
56 | mqtt.subscribe(MQTTConnection.IOT_TOPIC, qos: CocoaMQTTQOS.qos1)
57 | }
58 |
59 | }
60 |
61 | public func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) {
62 | print("didPublishMessage with message: \(message.string)")
63 | }
64 |
65 | public func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) {
66 | print("didPublishAck with id: \(id)")
67 | }
68 |
69 | public func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16 ) {
70 | //print("didReceivedMessage: \(message.string) with id \(id)")
71 |
72 | if let flightInfoString = message.string!.data(using: .utf8, allowLossyConversion: false) {
73 |
74 | let json = JSON(data: flightInfoString)
75 |
76 | let flight = FlightInfo(json: json)!
77 |
78 | let createdMillis: UnixTime = flight.createdInMillis
79 |
80 | let lastUpdatedInMIllis: UnixTime = flight.lastUpdatedInMillis
81 |
82 | print("flight: \(flight.icao) | Longitude: \(flight.longitude) | Latitude: \(flight.latitude) | createdTS: \((createdMillis/1000).toDayAndHour) | lastUpdateTS: \((lastUpdatedInMIllis/1000).toDayAndHour)")
83 |
84 | let flightAnnotation = FlightAnnotation(coordinate:CLLocationCoordinate2D(latitude: flight.latitude,longitude: flight.longitude))
85 | flightAnnotation.title=flight.icao
86 | flightAnnotation.coordinate=CLLocationCoordinate2D(latitude: flight.latitude,longitude: flight.longitude)
87 | // the logic of showing plane based on flight can be done here based on the icao code.
88 | flightAnnotation.image=UIImage(named:"plane.png")?.rotated(by: flight.headingInDegrees)
89 | flightAnnotation.altitude=flight.altitudeInMeters
90 | flightAnnotation.speed=flight.velocityInMetersPerSecond
91 | flightAnnotation.lastUpdatedInMillis=flight.lastUpdatedInMillis
92 | flightAnnotation.heading=flight.headingInDegrees
93 | flightAnnotation.callSign=flight.callSign
94 |
95 | //send the data to correspondign view
96 | currentView?.dataReceived(flightAnnotation: flightAnnotation)
97 |
98 |
99 | }
100 |
101 | }
102 |
103 |
104 | public func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopic topic: String) {
105 | print("didSubscribeTopic to \(topic)")
106 | }
107 |
108 | public func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopic topic: String) {
109 | print("didUnsubscribeTopic to \(topic)")
110 | }
111 |
112 | public func mqttDidPing(_ mqtt: CocoaMQTT) {
113 | print("didPing")
114 | }
115 |
116 | public func mqttDidReceivePong(_ mqtt: CocoaMQTT) {
117 | _console("didReceivePong")
118 | }
119 |
120 | public func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) {
121 | _console("mqttDidDisconnect")
122 | if((err) != nil){
123 | MQTTConnection().connectToMQTT(_delegate: self)
124 | }
125 |
126 | }
127 |
128 | func _console(_ info: String) {
129 | print("Delegate: \(info)")
130 | }
131 |
132 | // subclass will override this
133 | func dataReceived(flightAnnotation : FlightAnnotation){
134 |
135 | }
136 |
137 | //sub class will override this to display test flights
138 | func showTestFlights(){
139 |
140 | }
141 |
142 |
143 |
144 | func readTestFlights(){
145 | if let path = Bundle.main.path(forResource: "TestFlights", ofType: "json") {
146 | do {
147 | let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
148 | let flightJSONObjects = JSON(data: data)
149 | if flightJSONObjects != JSON.null {
150 | // print("jsonData:\(flightJSONObjects)")
151 |
152 | let flights = flightJSONObjects["flights"].arrayValue
153 | for flight in flights {
154 | let flightInfo = FlightInfo(json: flight)!
155 |
156 | let flightAnnotation = FlightAnnotation(coordinate:CLLocationCoordinate2D(latitude: flightInfo.latitude,longitude: flightInfo.longitude))
157 | flightAnnotation.title=flightInfo.icao
158 | flightAnnotation.coordinate=CLLocationCoordinate2D(latitude: flightInfo.latitude,longitude: flightInfo.longitude)
159 | // the logic of showing plane based on flight can be done here based on the icao code.
160 | flightAnnotation.image=UIImage(named:"plane.png")?.rotated(by: flightInfo.headingInDegrees)
161 | flightAnnotation.altitude=flightInfo.altitudeInMeters
162 | flightAnnotation.speed=flightInfo.velocityInMetersPerSecond
163 | flightAnnotation.lastUpdatedInMillis=flightInfo.lastUpdatedInMillis
164 | flightAnnotation.heading=flightInfo.headingInDegrees
165 | flightAnnotation.testFlight = true
166 | flightAnnotation.callSign=flightInfo.callSign
167 | flightAnnotations[flightInfo.icao!] = flightAnnotation
168 | }
169 | } else {
170 | print("Could not get json from file, make sure that file contains valid json.")
171 | }
172 | } catch let error {
173 | print(error.localizedDescription)
174 | }
175 | } else {
176 | print("Invalid filename/path.")
177 | }
178 |
179 | }
180 |
181 |
182 |
183 | }
184 |
185 | typealias UnixTime = Int64
186 |
187 | extension UnixTime {
188 | private func formatType(form: String) -> DateFormatter {
189 | let dateFormatter = DateFormatter()
190 | dateFormatter.locale = NSLocale(localeIdentifier: "en_US") as Locale!
191 | dateFormatter.dateFormat = form
192 | return dateFormatter
193 | }
194 | var dateFull: NSDate {
195 | return NSDate(timeIntervalSince1970: Double(self))
196 | }
197 | var toHour: String {
198 | return formatType(form: "hh:mm").string(from: dateFull as Date)
199 | }
200 | var toDay: String {
201 | return formatType(form: "MM/dd/yyyy").string(from: dateFull as Date)
202 | }
203 | var toDayAndHour: String {
204 | return formatType(form: "MM/dd/yyyy hh:mm").string(from: dateFull as Date)
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARFlightViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | //class ARFlightViewController: ViewController, ARLocationDelegate,ARDelegate,MarkerViewDelegate
3 | //{
4 | // public func didTouch(_ markerView: MarkerView!) {
5 | // //<#code#>
6 | // }
7 | //
8 | //
9 | //
10 | // public func geoLocations() -> NSMutableArray! {
11 | // return NSMutableArray(array: Array(flightAnnotationsGeo.values))
12 | // }
13 | //
14 | //
15 | // public func locationClicked(_ coordinate: ARGeoCoordinate!) {
16 | //
17 | // }
18 | //
19 | // fileprivate var didLayoutSubviews: Bool = false
20 | // fileprivate var annotations: [FlightAnnotation] = []
21 | // open var userLocation: CLLocation?
22 | // fileprivate var arController: AugmentedRealityController?
23 | // fileprivate var closeButton: UIButton?
24 | // fileprivate var compassImage: UIImageView!
25 | // open var closeButtonImage: UIImage?
26 | // {
27 | // didSet
28 | // {
29 | // closeButton?.setImage(self.closeButtonImage, for: UIControlState())
30 | // }
31 | // }
32 | //
33 | //
34 | //
35 | // init(){
36 | // super.init(nibName: nil, bundle: nil)
37 | // super.currentView = self
38 | // if(arController == nil) {
39 | // arController = AugmentedRealityController(view: self.view, parentViewController: self, withDelgate: self)
40 | // }
41 | // arController?.minimumScaleFactor=0.5
42 | // arController?.scaleViewsBasedOnDistance=true
43 | // arController?.rotateViewsBasedOnPerspective=true
44 | // arController?.debugMode=false
45 | // }
46 | //
47 | // required init?(coder aDecoder: NSCoder) {
48 | // super.init(coder: aDecoder)
49 | // }
50 | //
51 | //
52 | //// override func viewDidLoad()
53 | //// {
54 | ////
55 | //// super.viewDidLoad()
56 | //// }
57 | //
58 | // open override func viewWillAppear(_ animated: Bool)
59 | // {
60 | // super.viewWillAppear(animated)
61 | // onViewWillAppear() // Doing like this to prevent subclassing problems
62 | // }
63 | //
64 | // fileprivate func onViewWillAppear()
65 | // {
66 | // self.generateGeoCoordinatesFromFlightAnnotaiton()
67 | // }
68 | //
69 | // open override func viewDidAppear(_ animated: Bool)
70 | // {
71 | // super.viewDidAppear(animated)
72 | // onViewDidAppear() // Doing like this to prevent subclassing problems
73 | // }
74 | //
75 | // override func viewDidLayoutSubviews()
76 | // {
77 | // super.viewDidLayoutSubviews()
78 | // onViewDidLayoutSubviews()
79 | // }
80 | //
81 | // fileprivate func onViewDidAppear()
82 | // {
83 | // //self.generateGeoCoordinatesFromFlightAnnotaiton()
84 | //
85 | // }
86 | //
87 | // open override func viewDidDisappear(_ animated: Bool)
88 | // {
89 | // super.viewDidDisappear(animated)
90 | // onViewDidDisappear() // Doing like this to prevent subclassing problems
91 | // }
92 | //
93 | // fileprivate func onViewDidDisappear(){
94 | // arController?.removeCoordinates(Array(flightAnnotationsGeo.values))
95 | // arController?.stopListening()
96 | // }
97 | //
98 | // fileprivate func onViewDidLayoutSubviews()
99 | // {
100 | // // Executed only first time when everything is layouted
101 | // if !self.didLayoutSubviews
102 | // {
103 | // self.didLayoutSubviews = true
104 | //
105 | // // Close button
106 | // self.addCloseButton()
107 | //
108 | // //compass view
109 | // self.addCompassView()
110 | //
111 | // self.view.layoutIfNeeded()
112 | // }
113 | // }
114 | //
115 | //
116 | // @available(iOS 3.0, *)
117 | // public func didUpdate(_ newHeading: CLHeading!) {
118 | //
119 | // }
120 | //
121 | // @available(iOS 2.0, *)
122 | // public func didUpdate(_ newLocation: CLLocation!) {
123 | // self.userLocation = newLocation
124 | // }
125 | //
126 | // public func didUpdate(_ orientation: UIDeviceOrientation) {
127 | // }
128 | //
129 | //
130 | // func setFlightAnnotations(annotations: [FlightAnnotation]){
131 | // self.annotations = annotations
132 | // }
133 | //
134 | // open func generateGeoCoordinatesFromFlightAnnotaiton()
135 | // {
136 | // // Don't use annotations without valid location
137 | // for annotation in annotations
138 | // {
139 | // if CLLocationCoordinate2DIsValid(annotation.coordinate)
140 | // {
141 | // let timeInterval:TimeInterval=Double(annotation.lastUpdatedInMillis)/1000
142 | //
143 | // let arGeoCoordinate: ARGeoCoordinate = ARGeoCoordinate(
144 | // location: CLLocation.init(coordinate: annotation.coordinate, altitude: annotation.altitude, horizontalAccuracy: 0, verticalAccuracy: 0, course: annotation.heading , speed: annotation.speed, timestamp: NSDate(timeIntervalSinceNow: timeInterval) as Date),
145 | // locationTitle: annotation.title)
146 | //
147 | //// let arGeoCoordinate:ARGeoCoordinate = ARGeoCoordinate(location: CLLocation.init(latitude: annotation.coordinate.latitude, longitude: annotation.coordinate.longitude),locationTitle:annotation.title)
148 | // // create marker
149 | // let markerForFlights = MarkerView(coordinate: arGeoCoordinate, delegate: self)
150 | // arGeoCoordinate.displayView = markerForFlights
151 | // //calibrate using altitude
152 | // arGeoCoordinate.calibrate(usingOrigin: userLocation)
153 | //
154 | // arController?.addCoordinate(arGeoCoordinate)
155 | // //print("User Location: ",userLocation)
156 | // flightAnnotationsGeo[annotation.title!] = arGeoCoordinate
157 | // }
158 | // }
159 | // }
160 | //
161 | //
162 | // // display data as it received in the map.
163 | // override func dataReceived(flightAnnotation : FlightAnnotation){
164 | // flightAnnotations[flightAnnotation.title!] = flightAnnotation
165 | // self.setFlightAnnotations(annotations: Array(flightAnnotations.values))
166 | //
167 | // let timeInterval:TimeInterval=Double(flightAnnotation.lastUpdatedInMillis)/1000
168 | // let arGeoCoordinate: ARGeoCoordinate = ARGeoCoordinate(
169 | // location: CLLocation.init(coordinate: flightAnnotation.coordinate, altitude: flightAnnotation.altitude, horizontalAccuracy: 0, verticalAccuracy: 0, course: flightAnnotation.heading , speed: flightAnnotation.speed, timestamp: NSDate(timeIntervalSinceNow: timeInterval) as Date),
170 | // locationTitle: flightAnnotation.title)
171 | // let markerForFlights = MarkerView(coordinate: arGeoCoordinate, delegate: self)
172 | // arGeoCoordinate.displayView = markerForFlights
173 | // //calibrate using altitude
174 | // arGeoCoordinate.calibrate(usingOrigin: userLocation)
175 | //
176 | // if let geoCoordinate: ARGeoCoordinate = flightAnnotationsGeo[flightAnnotation.title!]{
177 | // //remove the coordinate.
178 | // geoCoordinate.displayView = nil
179 | // arController?.removeCoordinate(geoCoordinate)
180 | // }
181 | //
182 | // arController?.addCoordinate(arGeoCoordinate)
183 | // flightAnnotationsGeo[flightAnnotation.title!] = arGeoCoordinate
184 | // }
185 | //
186 | // // test flights
187 | // override func showTestFlights() {
188 | // self.setFlightAnnotations(annotations: Array(flightAnnotations.values))
189 | // for annotation in annotations {
190 | // self.dataReceived(flightAnnotation: annotation)
191 | // }
192 | // }
193 | //
194 | // func addCompassView(){
195 | //
196 | // let view = UIImageView()
197 | // view.frame = CGRect(x: 10, y: 10, width: 80, height: 78)
198 | // //view.backgroundColor = UIColor.white
199 | // view.image=UIImage(named: "compass.png")
200 | // self.view.addSubview(view)
201 | // self.compassImage=view
202 | //
203 | // }
204 | //
205 | //
206 | // //CLOSE button on AR flight view
207 | // func addCloseButton()
208 | // {
209 | // self.closeButton?.removeFromSuperview()
210 | //
211 | // if self.closeButtonImage == nil
212 | // {
213 | // let bundle = Bundle(for: ARFlightViewController.self)
214 | // let path = bundle.path(forResource: "close", ofType: "png")
215 | // if let path = path
216 | // {
217 | // self.closeButtonImage = UIImage(contentsOfFile: path)
218 | // }
219 | // }
220 | //
221 | // // Close button - make it customizable
222 | // let closeButton: UIButton = UIButton(type: UIButtonType.custom)
223 | // closeButton.setImage(closeButtonImage, for: UIControlState());
224 | // closeButton.frame = CGRect(x: self.view.bounds.size.width - 45, y: 5,width: 40,height: 40)
225 | // closeButton.addTarget(self, action: #selector(ARFlightViewController.closeButtonTap), for: UIControlEvents.touchUpInside)
226 | // closeButton.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleBottomMargin]
227 | // self.view.addSubview(closeButton)
228 | // self.closeButton = closeButton
229 | // }
230 | //
231 | // internal func closeButtonTap()
232 | // {
233 | // self.presentingViewController?.dismiss(animated: true, completion: nil)
234 | // }
235 | //
236 | //
237 | //
238 | //}
239 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/FlightCalloutView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
32 |
33 |
34 |
35 |
36 |
43 |
50 |
57 |
64 |
71 |
72 |
73 |
74 |
75 |
82 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/adsb.ground.station/README-ja.md:
--------------------------------------------------------------------------------
1 | *他の言語で読む: [English](README.md).*
2 |
3 | # adsb.ground.station (ADS-B 地上局)
4 |
5 | 以下を実施する商用フライトのための Raspberry Pi による ADS-B 地上局です:
6 |
7 | * _受信 (receives)_ : 1090MHz の周波数にチューニングされた Software-Defined Radio (SDR)デバイスを使用して、商用フライトによって放送される [Automatic Dependent Surveillance-Broadcast(ADS-B)](http://airfactsjournal.com/2013/01/ads-b-101-what-it-is-and-why-you-should-care/) メッセージを受信します。
8 | * _復号 (decodes)_ : 商用フライトからの ADS-B メッセージを復号します。
9 | * _発行 (publishes)_ : IBM Cloud (Bluemix) の [Internet of Things(IoT) Platform](https://console.ng.bluemix.net/catalog/services/internet-of-things-platform/?taxonomyNavigation=applications) に合致する [MQTT](http://mqtt.org/) メッセージを発行します。
10 |
11 | IBM Bluemix Cloud からの MQTT メッセージは、座標に基づいてデバイスに送信され、デバイスの近くのフライトを追跡することができます。
12 | 拡張現実 (AR: Augmented Reality) を使用した iOS のフライト追跡アプリの詳細は、こちらの [README](../ARFlightTracker-iOS-Swift/README-ja.md) を参照してください。
13 |
14 | USB2.0 インターフェースをサポートする [NooElec's RTL-SDR receiver set with antenna](http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html) などの SDR デバイスは、適切な半径内であれば、商用飛行機によって平文で放送されているADS-Bメッセージを受信するために、USBポートを備えた任意のデバイスから実用的に使用することができます。
15 |
16 | この練習の一環として、[NooElec's RTL-SDR receiver set with antenna](http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html) を接続した Raspberry Pi 3 は、商用フライトからの ADS-B メッセージを受信してデコードするだけでなく、対応する MQTT メッセージをIBM Cloud (Bluemix) IoT プラットフォームに公開するためにも使用されました。
17 |
18 | 以下のセクションでは、Raspberry Pi 3 をセットアップして、IBM Cloud上の商業フライトについての情報を公開できるようにする方法について詳しく説明します。このような Raspberry Pi による ADS-B 地上局は、世界中の航空機の 100-150 マイルの飛行を追跡することができます。IBM Cloud 上で先進的な航空交通管制を作成することで、現在の航空交通管制で使用されている時代遅れのレーダー技術を置き換えることが可能かもしれません。
19 |
20 | ## ハードウェア要件
21 |
22 | * Raspberry Pi 3 と 32GB 以上の容量の SD カード
23 | * [NooElec's RTL-SDR receiver set with antenna](http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html) のような SDR デバイス
24 |
25 | ## ソフトウェア要件
26 |
27 | 前述のハードウェアに加えて、`adsb.ground.station` には以下のソフトウェアが必要です:
28 |
29 | * RTL-SDR USB ドライバー
30 | * SDR デバイスを 1090MHz の周波数に同調させ、データを収集し、それをポート `30002` から利用可能にする [Dump1090](https://github.com/MalcolmRobb/dump1090) デコーダ
31 | * Java SE Development Kit (JDK) 8 以上
32 | * Maven 3.2.5 以上
33 |
34 | ## Raspberry Pi 3 を設定する
35 |
36 | 新しい Raspberry Pi 3 を設定するのに役立つ多くのリソースがあります。この練習では、[こちら](https://www.raspberrypi.org/documentation/installation/noobs.md) で説明されている、32GB SDカードに配置した _New Out Of Box Software_ (NOOBS) を使用しました。
37 |
38 | ## ハードウェアを設定する
39 |
40 | 「百聞は一見に如かず」なので、ここではハードウェアの設定方法を示す画像を示します:
41 |
42 | 
43 |
44 | 設定を完了するまでの手順は次のとおりです:
45 |
46 | * [NooElec's RTL-SDR receiver dongle](http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html) にアンテナを接続します。
47 | * NooElec's RTL-SDR receiver を Raspberry Pi の USB ポートに接続します。
48 | * マイクロ USB ポートを使用して Raspberry Pi 3 の電源を入れます。
49 |
50 | これでおしまい! RTL-SDR receiver から ADS-B メッセージを受信する前に、Raspberry Pi 3 に `Dump1090 Server` という専用のソフトウェアをビルド、インストール、実行する必要があります。
51 |
52 | `Dump1090 Server` をビルドして実行するには2つのオプションがあります。
53 | [Dockerfile](Dockerfile) を使って、Docker コンテナに `Dump1090 Server` をビルドして実行するのに必要なすべてのソフトウェアが入っている Docker イメージを作成することもできますし、`Dump1090 Server` をラズベリーパイ3に直接インストールすることもできます。
54 | 以下のセクションでは、これらのアプローチの両方の手順を示します。
55 |
56 | ## Raspberry Pi 3 上の Docker コンテナで Dump1090 Server をビルドし実行する
57 |
58 | このリポジトリの一部である [Dockerfile](Dockerfile) を使用すると、Raspberry Pi 3 上で動作する Docker コンテナ内で `Dump1090 Server` をビルドして実行するのに必要なすべてのソフトウェアを含む Docker イメージを作成できます。
59 |
60 | これを達成するための手順は次のとおりです:
61 |
62 | ### Raspbian 環境を最新にする
63 |
64 | ```
65 | $ sudo apt-get update
66 | $ sudo apt-get upgrade
67 | $ sudo apt-get dist-upgrade
68 | ```
69 |
70 | ### Raspberry Pi 3 に Docker をインストールする
71 |
72 | Docker を Raspberry Pi 3 にインストールして設定する手順は [こちら](http://blog.alexellis.io/getting-started-with-docker-on-raspberry-pi/)。
73 |
74 | ### Dump1090 Server のための Docker イメージを作成する
75 |
76 | ```
77 | $ git clone https://github.com/IBM/air-traffic-control
78 | $ cd air-traffic-control/adsb.ground.station
79 | $ docker build -t dump1090:1.0 .
80 | ```
81 |
82 | Docker イメージが準備できたら、次のコマンドを使って `dump1090` の `image-id` を取得できます:
83 |
84 | ```
85 | $ docker images dump1090
86 | ```
87 |
88 | ### Docker コンテナと Dump1090 Server を開始する
89 |
90 | `Dump1090 Server` の Docker イメージが作成されたら、Docker コンテナを起動することができます。
91 | Docker コンテナは内部的に `Dump1090 Server` を起動します。
92 | 前のステップで取得した `image-id` を使ってコンテナを起動する手順は次のとおりです:
93 |
94 | ```
95 | $ docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb -p 30002:30002
96 | ```
97 | このコマンドは指定されたイメージを使用してコンテナのプロセスを開始し、`container_id` をターミナルに表示します。
98 |
99 | 商用フライトから放送され、SDR から `Dump1090 Server` によって受信されている ADS-B メッセージのログを見るために、次のコマンドを使ってコンテナに接続することができます:
100 |
101 | ```
102 | $ docker attach
103 | ```
104 |
105 | ## Raspberry Pi 3 上で直接 Dump1090 Server をビルドし実行する
106 |
107 | このセクションでは、Docker を使用したくない場合に、必要なドライバと他のソフトウェアを Raspberry Pi 3 に直接インストールし、商用フライトから ADS-B メッセージを SDR で受信するための `Dump1090 Server` をビルドして実行する手順を詳しく説明します。
108 |
109 | Raspberry Pi 3 には HDMI ポートがあり、ディスプレイに接続することができます。
110 | ヘッドレスモードでも、他のマシンからの `ssh` で使うことができます。
111 | どちらのオプションを選択しても、実行する手順は次のとおりです。
112 |
113 | ### Raspbian 環境を最新にする
114 | ```
115 | $ sudo apt-get update
116 | $ sudo apt-get upgrade
117 | $ sudo apt-get dist-upgrade
118 | ```
119 |
120 | ### Build Essentials をインストールする
121 | ```
122 | $ sudo apt-get install build-essential
123 | ```
124 |
125 | ### Utils をインストールする
126 | ```
127 | $ sudo apt-get install apt-utils
128 | $ sudo apt-get install usbutils
129 | $ sudo apt-get install pkg-config
130 | ```
131 |
132 | ### `cmake` をインストールする
133 | ```
134 | $ sudo apt-get install cmake
135 | ```
136 |
137 | ### ドライバーのビルドに必要な USB ライブラリをインストールする
138 | ```
139 | $ sudo apt-get install libusb-1.0-0-dev
140 | ```
141 |
142 | ### git をインストールする
143 | ```
144 | $ sudo apt-get install git-core git
145 | ```
146 |
147 | ### RTL-SDR ドライバーをビルドしてインストールする
148 | ```
149 | $ cd ~
150 | $ git clone git://git.osmocom.org/rtl-sdr.git
151 | $ cd ~/rtl-sdr
152 | $ cmake ./ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
153 | $ make
154 | $ sudo make install
155 | $ sudo ldconfig
156 | ```
157 |
158 | ### [Dump1090 Server](https://github.com/MalcolmRobb/dump1090) をビルドする
159 | ```
160 | $ cd ~
161 | $ git clone https://github.com/MalcolmRobb/dump1090
162 | $ cd ~/dump1090
163 | $ make
164 | ```
165 |
166 | Raspberry Pi 3 は、ADS-B地上局になる準備が整いました!
167 | 以下に示すように、USBポートに接続された SDR デバイスが Raspberry Pi 3 によって認識されているかどうかを確認します。
168 |
169 | ```
170 | $ lsusb
171 | Bus 001 Device 005: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T
172 | Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
173 | Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
174 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
175 | ```
176 |
177 | ### Dump1090 Server を実行する
178 |
179 | `Dump1090 Server` は以下のように開始できます:
180 |
181 | ```
182 | $ cd ~/dump1090
183 | $ ./dump1090 --raw --net
184 | ```
185 |
186 | デフォルトでは、`Dump1090 Server` は SDR を 1090MHz の周波数にチューニングし、`30002` ポートでクライアントからの TCP 接続を待ち受けます。
187 | 接続されたすべてのクライアントは、生の ADS-B メッセージを受信します。
188 |
189 | `adsb.ground.station` は、`Dump1090 Server` に接続して生の ADS-B メッセージを受信し、ADS-B メッセージを復号し、対応する MQTT メッセージを IBM Cloud の IoT プラットフォームに発行する TCP クライアントとして動作します。
190 |
191 | ## IBM IoT プラットフォームへのデバイス登録と API キーの生成
192 |
193 | このプロジェクトは IBM Cloud で実行されている IoT プラットフォームに MQTT メッセージを送信するので、IoTプラットフォームのダッシュボードから、 [レシピ](https://developer.ibm.com/recipes/tutorials/how-to-register-devices-in-ibm-iot-foundation/) に従って、次のことを実施します:
194 |
195 | * デバイスタイプとデバイス ID を登録する
196 | * アプリケーションの API キーと対応する認証トークンを生成する
197 |
198 | 登録されたデバイスには、独自の認証トークンが割り当てられます。
199 | ただしこの練習では、アプリの認証トークンを使用する **のみ** になります。
200 |
201 | 前述の手順を完了したら、次の情報が必要です:
202 |
203 | * Organization-ID=[あなたの組織 ID]
204 | * API-Key=[あなたのアプリの API キー]
205 | * Authentication-Token=**[あなたのアプリの認証トークン]**
206 | * Device-Type=[あなたのデバイスタイプ]
207 | * Device-ID=[あなたのデバイス ID]
208 |
209 | この情報は、このプロジェクト/リポジトリを使用して実行可能な jar をビルドする **前に**、 `application.properties` ファイルを更新する、次の一連のステップで使用されます。
210 |
211 | ## このプロジェクトをビルドする
212 |
213 | このプロジェクトをビルドするための最小要件は次のとおりです:
214 |
215 | * Java SE Development Kit (JDK) 8 以上: Raspbian イメージの一部として標準でインストールされます
216 | * Maven 3.2.5 以上
217 |
218 | このプロジェクトは Raspberry Pi 3 上で直接ビルドすることもできますが、通常のラップトップ/デスクトップ上で構築し、実行可能な jar ファイルを Raspberry Pi 3 にコピーすることをお勧めします。
219 | これは Raspberry Pi 3 ではリソースが限られているからです。
220 | 通常のラップトップ/デスクトップでこのプロジェクトをビルドすることで、Maven リポジトリを作成します。
221 |
222 | ## このプロジェクトをビルドする手順
223 | ```
224 | $ cd ~
225 | $ git clone --recursive https://github.com/IBM/air-traffic-control
226 | $ cd air-traffic-control
227 | ```
228 | 前の手順で取得した情報で adsb.ground.station/src/main/resources/application.properties ファイルを更新します。
229 | これを行わないと、起動時に java.io.IOException が発生します。
230 | ```
231 | $ mvn clean install
232 | ```
233 |
234 | `adsb.ground.station/target` フォルダには、`src/main/resources/application.properties` ファイルに詳細を記載した IoT プラットフォームサービスを対象として設定された、実行可能な jar ファイル `adsb.ground.station-develop-SNAPSHOT.jar` が生成されています。
235 |
236 | ## `adsb.ground.station` クライアントを実行する
237 |
238 | ### Raspberry Pi 3 上の Dump1090 Server と共存させる
239 |
240 | 実行可能な jar がいったんビルドされたら、Raspberry Pi 3 で実行するのは非常に簡単です:
241 |
242 | ```
243 | $ cd air-traffic-control/adsb.ground.station/target
244 | $ java -cp . -jar adsb.ground.station-develop-SNAPSHOT.jar
245 | ```
246 |
247 | このコマンドを実行する前に `Dump1090 Server` が起動していることを確認しておいてください。
248 |
249 | ### クライアントとサーバーを別のホストで実行する
250 |
251 | `Dump1090 Server` and `adsb.ground.station` client can run on a separate hosts. For example, `Dump1090 Server` can run on Raspberry Pi 3 and the `adsb.ground.station` client can run on your laptop with additional command-line parameters as shown below:
252 |
253 | `Dump1090 Server` と `adsb.ground.station` クライアントは、別々のホスト上で実行させることができます。たとえば、 `Dump1090 Server` は Raspberry Pi 3 上で動作させ、`adsb.ground.station` クライアントはラップトップ上で以下のような追加のコマンドラインパラメータを使って実行できます:
254 |
255 | ```
256 | $ cd air-traffic-control/adsb.ground.station/target
257 | $ java -cp . -jar adsb.ground.station-develop-SNAPSHOT.jar --host 192.168.1.10 --port 30002
258 | ```
259 |
260 | 192.168.1.10 は `Dump1090 Server` が動作している Raspberry Pi 3 のIPアドレスです。
261 |
262 | このコマンドを実行する前に `Dump1090 Server` が起動していることを確認しておいてください。
263 |
264 | ### SDR をシミュレートする
265 |
266 | SDR や Raspberry Pi を所有していない場合は、ラップトップから `adsb.ground.station` クライアントを実行して、フライト追跡の仕組みを確認することができます。クライアントは、ADS-B メッセージをキャッシュしており、このメッセージは San Jose 地区で1時間の間にキャプチャされたもので、継続的に再生されます。`Dump1090 Server` を使わずにクライアントを走らせるには、以下のようにします:
267 |
268 | ```
269 | $ cd air-traffic-control/adsb.ground.station/target
270 | $ java -cp . -jar adsb.ground.station-develop-SNAPSHOT.jar --simulate-sdr
271 | ```
272 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/ARFlightViewController_v1.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ARFlightViewController_v1.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 1/9/17.
6 | // Copyright © 2017 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ARFlightViewControllerV1 : ViewController, ARViewDelegate,LocalizationDelegate
12 | {
13 | public func locationUnavailable() {
14 | //
15 | }
16 |
17 | @available(iOS 2.0, *)
18 | public func locationFound(_ location: CLLocation!) {
19 | super.userLocation = location
20 | }
21 |
22 | @available(iOS 3.0, *)
23 | public func headingFound(_ heading: CLHeading!) {
24 | self.rotateCompass(newHeading: fmod(heading.trueHeading, 360.0))
25 | }
26 |
27 |
28 |
29 | fileprivate var closeButton: UIButton?
30 | fileprivate var compassImage: UIImageView!
31 | open var closeButtonImage: UIImage?
32 | {
33 | didSet
34 | {
35 | closeButton?.setImage(self.closeButtonImage, for: UIControlState())
36 | }
37 | }
38 |
39 | init(){
40 | super.init(nibName: nil, bundle: nil)
41 | super.currentView = self
42 | LocalizationHelper.shared().register(forUpdates: self, once: false)
43 | }
44 |
45 | required init?(coder aDecoder: NSCoder) {
46 | super.init(coder: aDecoder)
47 | }
48 |
49 | open func startFlightARView(){
50 | super.currentView = self
51 | let config = ARKitConfig.defaultConfig(for: self)
52 | config?.useAltitude = true
53 | config?.orientation=UIApplication.shared.statusBarOrientation
54 | config?.scaleViewsBasedOnDistance = false
55 |
56 | // for radar display
57 | let s = UIScreen.main.bounds.size
58 | if UIApplication.shared.statusBarOrientation.isPortrait{
59 | config?.radarPoint = CGPoint(x: s.width - 50, y: s.height - 50)
60 | }else{
61 | config?.radarPoint = CGPoint(x: s.height - 50, y: s.width - 50)
62 | }
63 |
64 | arKitEngine = ARKitEngine.init(config: config)
65 | arKitEngine?.addCoordinates(points)
66 | self.addCompassView()
67 | self.addCloseButton()
68 | //
69 | arKitEngine?.startListening()
70 | }
71 |
72 |
73 |
74 | public func didChangeLooking(_ floorLooking: Bool) {
75 | if (floorLooking) {
76 | // The user has began looking at the floor
77 | print("floor looking")
78 | } else {
79 | // The user has began looking front
80 | print("not floor looking")
81 | }
82 | }
83 |
84 | public func itemTouched(with index: Int) {
85 |
86 | }
87 |
88 |
89 | public func view(for coordinate: ARGeoCoordinate!, floorLooking: Bool) -> ARObjectView! {
90 | var arObjectView : ARObjectView? = nil
91 |
92 | let annotation = coordinate.dataObject as! FlightAnnotation
93 |
94 | if floorLooking {
95 | arObjectView = ARObjectView.init()
96 | arObjectView?.displayed = false
97 | }else{
98 |
99 | let flightBubbleView = FlightBubble()
100 | flightBubbleView.frame = CGRect(x: 0,y: 0,width: 170,height: 50)
101 | let arAnnotation = ARAnnotation()
102 | arAnnotation.title = annotation.title
103 | arAnnotation.altitude = annotation.altitude
104 | arAnnotation.coordinate = annotation.coordinate
105 | arAnnotation.radialDistance = coordinate.radialDistance.multiplied(by: 0.00062)
106 |
107 | flightBubbleView.annotation = arAnnotation
108 | flightBubbleView.annotation?.annotationView = flightBubbleView
109 | //flightBubbleView.loadUi()
110 |
111 | arObjectView = ARObjectView.init(frame: flightBubbleView.frame)
112 | arObjectView?.addSubview(flightBubbleView)
113 |
114 | }
115 | arObjectView?.sizeToFit()
116 | return arObjectView
117 |
118 | }
119 |
120 |
121 | func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
122 | self.rotateCompass(newHeading: fmod(newHeading.trueHeading, 360.0))
123 | }
124 |
125 |
126 | open func generateGeoCoordinatesFromFlightAnnotaiton(annotations: [FlightAnnotation])
127 | {
128 | // Don't use annotations without valid location
129 | for annotation in annotations
130 | {
131 | if CLLocationCoordinate2DIsValid(annotation.coordinate)
132 | {
133 | let timeInterval:TimeInterval=Double(annotation.lastUpdatedInMillis)/1000
134 |
135 | let arGeoCoordinate: ARGeoCoordinate = ARGeoCoordinate(
136 | location: CLLocation.init(coordinate: annotation.coordinate, altitude: annotation.altitude, horizontalAccuracy: 0, verticalAccuracy: 0, course: annotation.heading , speed: annotation.speed, timestamp: NSDate(timeIntervalSinceNow: timeInterval) as Date))
137 | arGeoCoordinate.dataObject = annotation
138 | arGeoCoordinate.calibrate(usingOrigin: userLocation, useAltitude: true)
139 |
140 | self.flightAnnotationsGeo[annotation.title!] = arGeoCoordinate
141 |
142 | self.points.append(arGeoCoordinate)
143 | }
144 | }
145 | }
146 |
147 |
148 |
149 | // test flights
150 | override func showTestFlights() {
151 | // self.setFlightAnnotations(annotations: Array(flightAnnotations.values))
152 | // for annotation in annotations {
153 | // self.dataReceived(flightAnnotation: annotation)
154 | // }
155 | }
156 |
157 |
158 | override func dataReceived(flightAnnotation : FlightAnnotation){
159 | let timeInterval:TimeInterval=Double(flightAnnotation.lastUpdatedInMillis)/1000
160 |
161 | let arGeoCoordinate: ARGeoCoordinate = ARGeoCoordinate(
162 | location: CLLocation.init(coordinate: flightAnnotation.coordinate, altitude: flightAnnotation.altitude, horizontalAccuracy: 0, verticalAccuracy: 0, course: flightAnnotation.heading , speed: flightAnnotation.speed, timestamp: NSDate(timeIntervalSinceNow: timeInterval) as Date))
163 | arGeoCoordinate.dataObject = flightAnnotation
164 | arGeoCoordinate.calibrate(usingOrigin: userLocation, useAltitude: true)
165 |
166 | if let annGeo = flightAnnotationsGeo[flightAnnotation.title!]{
167 | arKitEngine?.remove(annGeo)
168 | }
169 |
170 | // if let ann = flightAnnotations[flightAnnotation.title!] {
171 | // ann.annotationExpiryTimer?.invalidate()
172 | // }
173 | //
174 | // flightAnnotation.annotationExpiryTimer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(self.removeAnnotation), userInfo: flightAnnotation, repeats: true)
175 | //
176 | // flightAnnotations[flightAnnotation.title!] = flightAnnotation
177 | flightAnnotationsGeo[flightAnnotation.title!] = arGeoCoordinate
178 | arKitEngine?.add(arGeoCoordinate)
179 | }
180 |
181 | //runs every 30seconds
182 | @objc func removeAnnotation(timer: Timer) {
183 | let flightAnnotation = timer.userInfo as! FlightAnnotation
184 | if flightAnnotation.lastUpdatedInMillis != nil {
185 | let diff = Int64(Date().timeIntervalSince1970*1000) - flightAnnotation.lastUpdatedInMillis
186 | if(diff >= 5000) {
187 | flightAnnotation.annotationExpiryTimer?.invalidate()
188 | let timeInterval:TimeInterval=Double(flightAnnotation.lastUpdatedInMillis)/1000
189 | let arGeoCoordinate: ARGeoCoordinate = ARGeoCoordinate(
190 | location: CLLocation.init(coordinate: flightAnnotation.coordinate, altitude: flightAnnotation.altitude, horizontalAccuracy: 0, verticalAccuracy: 0, course: flightAnnotation.heading , speed: flightAnnotation.speed, timestamp: NSDate(timeIntervalSinceNow: timeInterval) as Date))
191 | arGeoCoordinate.dataObject = flightAnnotation.callSign
192 |
193 | arKitEngine?.remove(arGeoCoordinate)
194 | }
195 | }
196 | }
197 |
198 | func addCompassView(){
199 | let view = UIImageView()
200 | view.frame = CGRect(x: 10, y: 10, width: 80, height: 78)
201 | //view.backgroundColor = UIColor.white
202 | view.image=UIImage(named: "compass.png")
203 | self.compassImage=view
204 |
205 | self.arKitEngine?.addExtraView(view)
206 |
207 | }
208 |
209 | internal func rotateCompass(newHeading: Double)
210 | {
211 | self.compassImage.transform = CGAffineTransform(rotationAngle: newHeading.toRadians())
212 | }
213 |
214 |
215 | //CLOSE button on AR flight view
216 | func addCloseButton()
217 | {
218 | self.closeButton?.removeFromSuperview()
219 |
220 | if self.closeButtonImage == nil
221 | {
222 | let bundle = Bundle(for: ARFlightViewControllerV1.self)
223 | let path = bundle.path(forResource: "close", ofType: "png")
224 | if let path = path
225 | {
226 | self.closeButtonImage = UIImage(contentsOfFile: path)
227 | }
228 | }
229 |
230 | // Close button - make it customizable
231 | let closeButton: UIButton = UIButton(type: UIButtonType.custom)
232 | closeButton.setImage(closeButtonImage, for: UIControlState());
233 | closeButton.frame = CGRect(x: self.view.bounds.size.width - 45, y: 5,width: 40,height: 40)
234 | closeButton.addTarget(self, action: #selector(ARFlightViewControllerV1.closeAR), for: UIControlEvents.touchUpInside)
235 | closeButton.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleBottomMargin]
236 | self.closeButton=closeButton
237 | self.arKitEngine?.addExtraView(closeButton)
238 | }
239 |
240 | internal func closeButtonTap()
241 | {
242 | self.presentingViewController?.dismiss(animated: true, completion: nil)
243 | }
244 |
245 | func closeAR() {
246 | arKitEngine?.hide()
247 | }
248 |
249 | /*
250 | * returning bubble view for AR
251 | */
252 | func createCalloutView() -> FlightBubbleV1 {
253 | let views = Bundle.main.loadNibNamed("FlightBubbleV1", owner: nil, options: nil)
254 | let bubbleView = views?[0] as! FlightBubbleV1
255 | return bubbleView
256 | }
257 |
258 | }
259 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/ARFlightTracker-iOS-Swift/ARFlightTracker-iOS-Swift/FlightMapViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FlightMapViewController.swift
3 | // IBMFlightTracker
4 | //
5 | // Created by Sanjeev Ghimire on 12/5/16.
6 | // Copyright © 2016 Sanjeev Ghimire. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import MapKit
12 | import SwiftyJSON
13 |
14 | class FlightMapViewController: ViewController, MKMapViewDelegate {
15 |
16 | @IBOutlet weak var mapView: MKMapView!
17 |
18 | @IBOutlet weak var mapARSegmentControl: UISegmentedControl!
19 |
20 | let annotationIdentifier = "AnnotationIdentifier"
21 |
22 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
23 | {
24 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
25 | }
26 |
27 | required public init?(coder aDecoder: NSCoder)
28 | {
29 | super.init(coder: aDecoder)
30 | }
31 |
32 | fileprivate func onViewWillAppear()
33 | {
34 | self.mapARSegmentControl.selectedSegmentIndex = 0
35 | }
36 |
37 |
38 | override func viewDidLoad()
39 | {
40 | super.currentView = self
41 | super.viewDidLoad()
42 |
43 | //self.locationManager.delegate = self
44 | self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
45 | self.locationManager.requestWhenInUseAuthorization()
46 | self.locationManager.startUpdatingLocation()
47 | self.mapView.delegate = self
48 | self.mapView.showsCompass = true
49 | self.mapView.showsUserLocation = true
50 | }
51 |
52 | open override func viewDidAppear(_ animated: Bool)
53 | {
54 | self.mapARSegmentControl.selectedSegmentIndex = 0
55 | super.currentView = self
56 | self.locationManager.delegate = self
57 | self.reloadAnnotations()
58 | super.viewDidAppear(animated)
59 | }
60 |
61 | //redraw all annotation again
62 | func reloadAnnotations(){
63 | let allAnnotations = self.mapView.annotations
64 | self.mapView.removeAnnotations(allAnnotations)
65 |
66 | for annotation in flightAnnotations.values {
67 | self.mapView.addAnnotation(annotation)
68 | }
69 | }
70 |
71 |
72 | open override func viewDidDisappear(_ animated: Bool)
73 | {
74 | super.viewDidDisappear(animated)
75 | }
76 |
77 | // display data as it received in the map.
78 | override func dataReceived(flightAnnotation : FlightAnnotation){
79 | if let ann = flightAnnotations[flightAnnotation.title!]{
80 | UIView.animate(withDuration: 0.01) {
81 | ann.annotationExpiryTimer?.invalidate()
82 | self.mapView.removeAnnotation(ann)
83 | if(ann.calloutOpen){
84 | self.addFlightAnnotationWithCallout(flightAnnotation: ann)
85 | ann.calloutOpen=true
86 | }
87 | flightAnnotation.annotationExpiryTimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.removeAnnotation), userInfo: flightAnnotation, repeats: true)
88 |
89 | self.mapView.addAnnotation(flightAnnotation)
90 | }
91 | } else {
92 | self.mapView.addAnnotation(flightAnnotation)
93 | }
94 | flightAnnotations[flightAnnotation.title!]=flightAnnotation
95 | }
96 |
97 |
98 |
99 | //runs every 30seconds
100 | @objc private func removeAnnotation(timer: Timer) {
101 | let flightAnnotation = timer.userInfo as! FlightAnnotation
102 | if flightAnnotation.lastUpdatedInMillis != nil {
103 | let diff = Int64(Date().timeIntervalSince1970*1000) - flightAnnotation.lastUpdatedInMillis
104 | if(diff >= 5000) {
105 | flightAnnotation.annotationExpiryTimer?.invalidate()
106 | flightAnnotations.removeValue(forKey: flightAnnotation.title!)
107 | self.mapView.removeAnnotation(flightAnnotation)
108 | }
109 | }
110 | }
111 |
112 |
113 | override func showTestFlights(){
114 | for flightAnnotation in Array(flightAnnotations.values) {
115 | self.mapView.addAnnotation(flightAnnotation)
116 | }
117 | }
118 |
119 |
120 | // swich between AR and map view
121 | @IBAction func ARMapViewLoader(_ sender: UISegmentedControl) {
122 | if(sender.selectedSegmentIndex == 0){
123 | showMapViewController()
124 | }else{
125 | //load ar view controller.
126 | let arFlightViewController = ARFlightViewControllerV1()
127 | arFlightViewController.generateGeoCoordinatesFromFlightAnnotaiton(annotations: Array(flightAnnotations.values))
128 | arFlightViewController.startFlightARView()
129 | }
130 | }
131 |
132 | func showMapViewController(){
133 | let bundle = Bundle(for: FlightMapViewController.self)
134 | let mapViewController = FlightMapViewController(nibName: "FlightMapViewController", bundle: bundle)
135 | self.present(mapViewController, animated: true, completion: nil)
136 | }
137 |
138 | func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
139 | {
140 | let location = locations.last
141 |
142 | let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
143 |
144 | let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
145 |
146 | self.mapView.setRegion(region, animated: true)
147 | self.mapView.regionThatFits(region)
148 |
149 | super.userLocation = location
150 |
151 | // stop updating current location
152 | self.locationManager.stopUpdatingLocation()
153 |
154 | }
155 |
156 | func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
157 | if !(annotation is FlightAnnotation) {
158 | return nil
159 | }
160 |
161 | var annotationView: MKAnnotationView?
162 | if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
163 | annotationView = dequeuedAnnotationView
164 | annotationView?.annotation = annotation
165 | }
166 | else {
167 | annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
168 | annotationView?.canShowCallout=false
169 | }
170 |
171 | let flightAnnotation = annotation as! FlightAnnotation
172 |
173 | if let annotationView = annotationView {
174 | // Configure your annotation view here
175 | annotationView.canShowCallout = false
176 | annotationView.image = flightAnnotation.image
177 | }
178 |
179 | return annotationView
180 | }
181 |
182 | func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)
183 | {
184 | // 1
185 | if view.annotation is MKUserLocation
186 | {
187 | // Don't proceed with custom callout
188 | return
189 | }
190 | // 2
191 | let flightAnnotation = view.annotation as! FlightAnnotation
192 |
193 | let calloutView = self.createCalloutView(flightAnnotation: flightAnnotation)
194 |
195 | //saving the callout view
196 | flightAnnotation.calloutView=calloutView
197 | flightAnnotation.calloutOpen=true
198 | // update the flight annotations map
199 | flightAnnotations[flightAnnotation.title!]=flightAnnotation
200 |
201 |
202 | //weather data
203 |
204 | RestCall().fetchWeatherBasedOnCurrentLocation(latitude: String(flightAnnotation.coordinate.latitude),longitude: String(flightAnnotation.coordinate.longitude)){
205 | (result: [String: Any]) in
206 |
207 | let weatherIconUrl: String = result["weatherIconUrl"] as! String
208 | let imageUrl = URL(string: weatherIconUrl)
209 | let data = try? Data(contentsOf: imageUrl!)
210 |
211 | DispatchQueue.main.async(execute: { () -> Void in
212 | calloutView.currentCity.text = "\(result["city"]!)"
213 | calloutView.weatherIcon.image = UIImage(data: data!)
214 | calloutView.currentTemprature.text = "\(result["temperature"]!)°F"
215 | calloutView.weatherDescription.text = "\(result["description"]!)"
216 | })
217 |
218 |
219 | }
220 |
221 |
222 | calloutView.center = CGPoint(x: view.bounds.size.width / 2, y: -calloutView.bounds.size.height*0.52)
223 | view.addSubview(calloutView)
224 | mapView.setCenter((view.annotation?.coordinate)!, animated: true)
225 |
226 |
227 | }
228 |
229 | func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
230 | if view.isKind(of: MKAnnotationView.self)
231 | {
232 | for subview in view.subviews
233 | {
234 | subview.removeFromSuperview()
235 | }
236 | }
237 | }
238 |
239 |
240 | func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
241 | {
242 | print("Errors: " + error.localizedDescription)
243 | }
244 |
245 | override func didReceiveMemoryWarning() {
246 | super.didReceiveMemoryWarning()
247 | // Dispose of any resources that can be recreated.
248 | }
249 |
250 | /// This method is called by ARViewController, make sure to set dataSource property.
251 | func ar(_ arViewController: ARFlightViewControllerV1, viewForAnnotation: FlightAnnotation) -> ARFlightAnnotationView
252 | {
253 | // Annotation views should be lightweight views, try to avoid xibs and autolayout all together.
254 | let annotationView = ARFlightAnnotationViewCustom()
255 | annotationView.frame = CGRect(x: 0,y: 0,width: 150,height: 50)
256 | return annotationView;
257 | }
258 |
259 |
260 | func addFlightAnnotationWithCallout(flightAnnotation: FlightAnnotation){
261 | let view = MKAnnotationView(annotation: flightAnnotation, reuseIdentifier: annotationIdentifier)
262 | view.canShowCallout=false
263 | var fcView = flightAnnotation.calloutView as FlightCalloutView?
264 | if(fcView == nil){
265 | fcView = self.createCalloutView(flightAnnotation: flightAnnotation)
266 | }
267 | fcView!.center = CGPoint(x: view.bounds.size.width / 2, y: -fcView!.bounds.size.height*0.52)
268 | view.addSubview(fcView!)
269 | self.mapView.setCenter((view.annotation?.coordinate)!, animated: true)
270 | }
271 |
272 | func createCalloutView(flightAnnotation: FlightAnnotation) -> FlightCalloutView {
273 | let views = Bundle.main.loadNibNamed("FlightCalloutView", owner: nil, options: nil)
274 | let calloutView = views?[0] as! FlightCalloutView
275 | calloutView.flightName.text = flightAnnotation.callSign
276 | calloutView.altitudeInMeters.text = "\(flightAnnotation.altitude.roundTo(places: 2)) m"
277 | calloutView.velocityInMetersPerSecond.text = "\(flightAnnotation.speed.roundTo(places: 2)) m/s"
278 | calloutView.flightImage.image=flightAnnotation.image
279 |
280 | return calloutView;
281 | }
282 |
283 |
284 | }
285 |
286 | func showActivityIndicatory(uiView: UIView, actInd: UIActivityIndicatorView) {
287 | actInd.frame = CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0);
288 | actInd.center = uiView.center
289 | actInd.hidesWhenStopped = true
290 | actInd.activityIndicatorViewStyle = .gray
291 | uiView.addSubview(actInd)
292 | actInd.startAnimating()
293 | }
294 |
295 | extension Double {
296 | func toRadians() -> CGFloat {
297 | return CGFloat(self * .pi / 180.0)
298 | }
299 |
300 | /// Rounds the double to decimal places value
301 | func roundTo(places:Int) -> Double {
302 | let divisor = pow(10.0, Double(places))
303 | return (self * divisor).rounded() / divisor
304 | }
305 | }
306 |
307 | extension UIImage {
308 | func rotated(by degrees: Double, flipped: Bool = false) -> UIImage? {
309 | guard let cgImage = self.cgImage else { return nil }
310 |
311 | let transform = CGAffineTransform(rotationAngle: degrees.toRadians())
312 | var rect = CGRect(origin: .zero, size: self.size).applying(transform)
313 | rect.origin = .zero
314 |
315 | let renderer = UIGraphicsImageRenderer(size: rect.size)
316 | return renderer.image { renderContext in
317 | renderContext.cgContext.translateBy(x: rect.midX, y: rect.midY)
318 | renderContext.cgContext.rotate(by: degrees.toRadians())
319 | renderContext.cgContext.scaleBy(x: flipped ? -1.0 : 1.0, y: -1.0)
320 |
321 | let drawRect = CGRect(origin: CGPoint(x: -self.size.width/2, y: -self.size.height/2), size: self.size)
322 | renderContext.cgContext.draw(cgImage, in: drawRect)
323 | }
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/adsb.ground.station/README.md:
--------------------------------------------------------------------------------
1 | *Read this in other languages: [日本](README-ja.md).*
2 |
3 | # adsb.ground.station
4 |
5 | Raspberry Pi powered Ground Station for commercial flights that does the following:
6 |
7 | * _receives_ [Automatic Dependent Surveillance-Broadcast(ADS-B)]
8 | (http://airfactsjournal.com/2013/01/ads-b-101-what-it-is-and-why-you-should-care/) messages
9 | broadcasted by commercial flights using a Software-Defined Radio(SDR) device tuned to 1090 MHz
10 | frequency
11 | * _decodes_ ADS-B messages from commercial flights
12 | * _publishes_ corresponding [MQTT](http://mqtt.org/) messages to [Bluemix Internet of Things(IoT) Platform]
13 | (https://console.ng.bluemix.net/catalog/services/internet-of-things-platform/?taxonomyNavigation=applications)
14 | in IBM's Bluemix Cloud.
15 |
16 | The MQTT messages from the IBM Bluemix Cloud can then be transmitted over to devices, based on their
17 | coordinates, to be able to track flights in the vicinity of the device. Details of iOS flight tracking
18 | app using Augmented Reality are in it's own [README.md](../ARFlightTracker-iOS-Swift/README.md).
19 |
20 | SDR devices such as [NooElec's RTL-SDR receiver set with antenna]
21 | (http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html), which support USB 2.0
22 | interface, can be used to receive ADS-B messages that are being broadcasted in clear text by
23 | commercial flights, within a decent radius, on practically any device with a USB port.
24 |
25 | As part of this exercise, a Raspberry Pi 3 along with [NooElec's RTL-SDR receiver set with antenna]
26 | (http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html) were used to not only
27 | receive ADS-B messages from commercial flights and decode them but also to publish corresponding
28 | MQTT messages to IBM Bluemix IoT Platform.
29 |
30 | The following sections describe in great detail how to set up Raspberry Pi 3 so that it can publish
31 | information about commercial flights in it's vicinity over IBM Bluemix. One can imagine a whole bunch
32 | of such Raspberry Pi powered ADS-B Ground Stations that can track aircraft in 100-150mile radius
33 | scattered all over the world publishing messages to create modern Air Traffic Controls in
34 | IBM Bluemix and perhaps replacing the out-dated radar technology that is currently used by the
35 | conventional Air Traffic Controls.
36 |
37 | ## Hardware Requirements
38 | * Raspberry Pi 3 with at least 32GB SD card
39 | * SDR device such as [NooElec's RTL-SDR receiver set with antenna]
40 | (http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html)
41 |
42 | ## Software Requirements
43 | In addition to the aforementioned hardware, `adsb.ground.station` requires the following software:
44 |
45 | * RTL-SDR USB driver
46 | * [Dump1090](https://github.com/MalcolmRobb/dump1090) decoder that tunes the SDR device to 1090MHz
47 | frequency, collects the data, and makes it available on port `30002`.
48 | * Java SE Development Kit (JDK) 8 or higher
49 | * Maven 3.2.5 higher
50 |
51 | ## Getting Raspberry Pi 3 Ready
52 | There are a lot of resources that can help you with getting your brand new Raspberry Pi 3 ready. For
53 | this exercise, the _New Out Of Box Software_(NOOBS) on a 32GB SD card was used as described
54 | [here](https://www.raspberrypi.org/documentation/installation/noobs.md).
55 |
56 | ## Configuring the Hardware
57 | Since a picture is worth a thousand words, here is the picture that shows how the hardware needs to
58 | be configured:
59 |
60 | 
61 |
62 | Here are the steps to accomplish the configuration:
63 |
64 | * Attach the antenna to the [NooElec's RTL-SDR receiver dongle]
65 | (http://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini-2-plus.html).
66 | * Plug in NooElec's RTL-SDR receiver into the Raspberry Pi's USB port.
67 | * Power up Raspberry Pi 3 using the micro-USB port.
68 |
69 | That's it! Before we can receive ADS-B messages from RTL-SDR receiver, we have to build, install,
70 | and run specialized software called `Dump1090 Server` on Raspberry Pi 3.
71 |
72 | There are two options to build and run `Dump1090 Server`. You can either use the
73 | [Dockerfile](Dockerfile) to create a Docker image that would in turn contain all the necessary
74 | software to build and run `Dump1090 Server` in a Docker container or you can install the software
75 | that let's you build and run `Dump1090 Server` directly on your Raspberry Pi 3. The following
76 | sections provide steps for both of these approaches.
77 |
78 | ## Using Docker on Raspberry Pi 3 to build and run Dump1090 Server
79 |
80 | Using the [Dockerfile](Dockerfile) that is part of this repository, you can create a Docker image
81 | that would contain all the necessary software to build and run `Dump1090 Server` from within the
82 | Docker container running on Raspberry Pi 3.
83 |
84 | Here are the steps to accomplish this:
85 |
86 | ### Upgrade to the latest Raspbian image
87 | ```
88 | $ sudo apt-get update
89 | $ sudo apt-get upgrade
90 | $ sudo apt-get dist-upgrade
91 | ```
92 |
93 | ### Install Docker on Raspberry Pi 3
94 | Instructions to install and configure Docker on Raspberry Pi 3 are
95 | [here](http://blog.alexellis.io/getting-started-with-docker-on-raspberry-pi/).
96 |
97 | ### Create Docker Image for Dump1090 Server
98 |
99 | ```
100 | $ git clone https://github.com/IBM/air-traffic-control
101 | $ cd air-traffic-control/adsb.ground.station
102 | $ docker build -t dump1090:1.0 .
103 | ```
104 |
105 | Once the Docker image is ready, you can retrieve the `image-id` for `dump1090`, using the following command:
106 |
107 | ```
108 | $ docker images dump1090
109 | ```
110 |
111 | ### Start Docker Container and Dump1090 Server
112 |
113 | Once the Docker image for `Dump1090 Server` has been created, you can start a Docker container. The
114 | Docker container will launch `Dump1090 Server` internally. Here is the step to start the container using `image-id` from the previous command:
115 |
116 | ```
117 | $ docker run -d --privileged -v /dev/bus/usb:/dev/bus/usb -p 30002:30002
118 | ```
119 | This command will use the specified image to start a container process and print the `container_id`
120 | in the Terminal.
121 |
122 | In order to see the log of ADS-B messages broadcasted by the commercial flights that are being
123 | received by the `Dump1090 Server` from the SDR, you can attach to the container using the following
124 | command:
125 |
126 | ```
127 | $ docker attach
128 | ```
129 |
130 | ## Building and Running Dump1090 Server directly on Raspberry Pi 3
131 |
132 | If you don't want to use Docker, then this section describes in detail all the steps that you should
133 | perform to install the necessary drivers and other software directly on Raspberry Pi 3 to be able to
134 | build and run `Dump1090 Server` that would allow the SDR to receive ADS-B messages from the commercial
135 | flights.
136 |
137 | Raspberry Pi 3 comes with a HDMI port that can be used to connect a display. You can also use it in
138 | headless mode and `ssh` from your other machine. Regardless of which option you choose, here are the
139 | sequence of steps that you should perform:
140 |
141 |
142 | ### Upgrade to the latest Raspbian image
143 | ```
144 | $ sudo apt-get update
145 | $ sudo apt-get upgrade
146 | $ sudo apt-get dist-upgrade
147 | ```
148 |
149 | ### Install Build Essentials
150 | ```
151 | $ sudo apt-get install build-essential
152 | ```
153 |
154 | ### Install Utils
155 | ```
156 | $ sudo apt-get install apt-utils
157 | $ sudo apt-get install usbutils
158 | $ sudo apt-get install pkg-config
159 | ```
160 |
161 | ### Install `cmake`
162 | ```
163 | $ sudo apt-get install cmake
164 | ```
165 |
166 | ### Install USB library needed to build drivers
167 | ```
168 | $ sudo apt-get install libusb-1.0-0-dev
169 | ```
170 |
171 | ### Install git
172 | ```
173 | $ sudo apt-get install git-core git
174 | ```
175 |
176 | ### Build and Install RTL-SDR Drivers
177 | ```
178 | $ cd ~
179 | $ git clone git://git.osmocom.org/rtl-sdr.git
180 | $ cd ~/rtl-sdr
181 | $ cmake ./ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
182 | $ make
183 | $ sudo make install
184 | $ sudo ldconfig
185 | ```
186 |
187 | ### Build [Dump1090 Server](https://github.com/MalcolmRobb/dump1090)
188 | ```
189 | $ cd ~
190 | $ git clone https://github.com/MalcolmRobb/dump1090
191 | $ cd ~/dump1090
192 | $ make
193 | ```
194 |
195 | Now your Raspberry Pi 3 is ready to be a ADS-B Ground Station! You can confirm whether the SDR
196 | device connected to the USB port is being recognized by Raspberry Pi 3 as shown below:
197 |
198 | ```
199 | $ lsusb
200 | Bus 001 Device 005: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T
201 | Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
202 | Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
203 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
204 | ```
205 |
206 | ### Run Dump1090 Server
207 |
208 | `Dump1090 Server` can be started as shown below:
209 |
210 | ```
211 | $ cd ~/dump1090
212 | $ ./dump1090 --raw --net
213 | ```
214 |
215 | By default, `Dump1090 Server` will tune the SDR to 1090MHz frequency and listen for client TCP
216 | connections on port `30002`. All the connected clients will receive the raw ADS-B messages.
217 |
218 | `adsb.ground.station` will act as TCP client that will connect to the `Dump1090 Server` to
219 | receive raw ADS-B messages, decode the ADS-B messages, and publish corresponding MQTT messages
220 | to IoT Platform in IBM Bluemix Cloud.
221 |
222 | ## Registering Device and Generating API Key with IBM IoT Platform
223 | Since this project will be sending MQTT messages to the IoT Platform running in IBM Bluemix Cloud,
224 | follow the steps in this
225 | [recipe](https://developer.ibm.com/recipes/tutorials/how-to-register-devices-in-ibm-iot-foundation/)
226 | from within the IoT Platform's dashboard to do the following:
227 | * Register a device-type and device-id
228 | * Generate API Key and the corresponding Authentication Token for the app
229 |
230 | Note that the registered device will have it's own Authentication Token. However, in this exercise,
231 | we will be **only** using the the Authentication Token of the app.
232 |
233 | You should have the following information when you are done with the aforementioned steps:
234 |
235 | * Organization-ID=[Your Organization ID]
236 | * API-Key=[Your Apps' API-Key]
237 | * Authentication-Token=**[Your Apps' Authentication Token]**
238 | * Device-Type=[Your Device Type]
239 | * Device-ID=[Your Device ID]
240 |
241 | This information will be used in the next set of steps when updating `application.properties` file
242 | **before** building the executable jar using this project/repo.
243 |
244 | ## Building this project
245 | Minimum requirements for building this project are:
246 |
247 | * Java SE Development Kit (JDK) 8 or higher: Installed by default as part of Raspbian image
248 | * Maven 3.2.5 or higher
249 |
250 | Even though this project can be built directly on Raspberry Pi 3, it is advisible to build it on a
251 | regular laptop/desktop and then copy over the executable jar to Raspberry Pi 3. This is because
252 | Raspberry Pi 3 has limited resources and you can avoid creating the Maven repository on it by
253 | building this project on a regular laptop/desktop.
254 |
255 | ## Steps for building this project
256 | ```
257 | $ cd ~
258 | $ git clone --recursive https://github.com/IBM/air-traffic-control
259 | $ cd air-traffic-control
260 | Update adsb.ground.station/src/main/resources/application.properties file using the information obtained from the
261 | earlier step. Failure to do this will result in java.io.IOException during startup.
262 | $ mvn clean install
263 | ```
264 |
265 | The `adsb.ground.station/target` folder will contain the executable jar
266 | `adsb.ground.station-develop-SNAPSHOT.jar` specifically targeted for the IoT Platform service
267 | and the device whose details are specified in `src/main/resources/application.properties` file.
268 |
269 | ## Running `adsb.ground.station` Client
270 |
271 | ### Co-located with Dump1090 Server on Raspberry Pi 3
272 |
273 | Once the executable jar is built, running it on Raspberry Pi 3 is very simple:
274 |
275 | ```
276 | $ cd air-traffic-control/adsb.ground.station/target
277 | $ java -cp . -jar adsb.ground.station-develop-SNAPSHOT.jar
278 | ```
279 |
280 | Make sure that the `Dump1090 Server` is up and running before running the aforementioned command.
281 |
282 | ### Client and Server Running on Separate Hosts
283 |
284 | `Dump1090 Server` and `adsb.ground.station` client can run on a separate hosts. For example,
285 | `Dump1090 Server` can run on Raspberry Pi 3 and the `adsb.ground.station` client can run on your
286 | laptop with additional command-line parameters as shown below:
287 |
288 | ```
289 | $ cd air-traffic-control/adsb.ground.station/target
290 | $ java -cp . -jar adsb.ground.station-develop-SNAPSHOT.jar --host 192.168.1.10 --port 30002
291 | ```
292 |
293 | where 192.168.1.10 is the IP address of the Raspberry Pi 3 on which `Dump1090 Server` is running.
294 |
295 | Make sure that the `Dump1090 Server` is up and running before running the aforementioned command.
296 |
297 | ### Simulate SDR
298 |
299 | If you don't have a SDR or a Raspberry Pi, you can still run `adsb.ground.station` client from your
300 | laptop to see how the flight tracking works. The client has cached ADS-B messages, which were
301 | captured over a period of an hour from San Jose area, and plays them continually. To run the client
302 | without `Dump1090 Server`, you can do the following:
303 |
304 | ```
305 | $ cd air-traffic-control/adsb.ground.station/target
306 | $ java -cp . -jar adsb.ground.station-develop-SNAPSHOT.jar --simulate-sdr
307 | ```
308 |
--------------------------------------------------------------------------------