Skip to content

Commit 3fd624e

Browse files
committed
Initial commit
1 parent 5471840 commit 3fd624e

File tree

7 files changed

+207
-0
lines changed

7 files changed

+207
-0
lines changed

MavADSB2.iml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="JAVA_MODULE" version="4">
3+
<component name="NewModuleRootManager" inherit-compiler-output="true">
4+
<exclude-output />
5+
<content url="file://$MODULE_DIR$">
6+
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
7+
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
8+
<sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
9+
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
10+
</content>
11+
<orderEntry type="inheritedJdk" />
12+
<orderEntry type="sourceFolder" forTests="false" />
13+
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
14+
<orderEntry type="library" name="squareup.okhttp3.okhttp" level="project" />
15+
<orderEntry type="library" name="google.code.gson" level="project" />
16+
</component>
17+
</module>
Binary file not shown.

src/.DS_Store

6 KB
Binary file not shown.

src/main/.DS_Store

6 KB
Binary file not shown.

src/main/kotlin/Main.kt

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import com.google.gson.Gson
2+
import okhttp3.OkHttpClient
3+
import okhttp3.Request
4+
import java.util.*
5+
6+
private const val BASE_URL = "https://api.adsb.one/v2/"
7+
private val server: SBSServer = SBSServer()
8+
9+
fun main(args: Array<String>) {
10+
val lat = args[0]
11+
val lon = args[1]
12+
println("Querying ADSB 250nm around: $lat, $lon")
13+
server.start()
14+
val client = OkHttpClient()
15+
val request = Request.Builder().url("${BASE_URL}point/$lat/$lon/250").build()
16+
Timer().schedule(object : TimerTask() {
17+
override fun run() {
18+
try {
19+
client.newCall(request).execute().body?.let { server.sendData(Gson().fromJson(it.string(), ADSBData::class.java)) }
20+
} catch (e: Exception) {
21+
println("Failed to query ADSB data: ${e.message}")
22+
}
23+
}
24+
}, 0, 1010)
25+
}
26+
27+
data class ADSBData(
28+
val msg: String = "",
29+
val total: Int = 0,
30+
val ac: List<Aircraft>
31+
)
32+
33+
data class Aircraft(
34+
val hex: String = "",
35+
val type: String = "",
36+
val flight: String = "",
37+
val r: String = "",
38+
val t: String = "",
39+
val desc: String = "Unknown",
40+
val ownOp: String = "",
41+
val year: String = "",
42+
val alt_baro: String = "",
43+
val alt_geom: Int = 0,
44+
val gs: Double = 0.0,
45+
val track: Double = 0.0,
46+
val baro_rate: Int = 0,
47+
val squawk: String = "",
48+
val emergency: String = "",
49+
val category: String = "",
50+
val nav_qnh: Double = 0.0,
51+
val nav_altitude_mcp: Int = 0,
52+
val nav_heading: Double = 0.0,
53+
val lat: Double = 0.0,
54+
val lon: Double = 0.0,
55+
val nic: Int = 0,
56+
val rc: Int = 0,
57+
val seen_pos: Double = 0.0,
58+
val version: Int = 0,
59+
val nic_baro: Int = 0,
60+
val nac_p: Int = 0,
61+
val nac_v: Int = 0,
62+
val sil: Int = 0,
63+
val sil_type: String = "",
64+
val gva: Int = 0,
65+
val sda: Int = 0,
66+
val alert: Int = 0,
67+
val spi: Int = 0,
68+
val messages: Int = 0,
69+
val seen: Double = 0.0,
70+
val rssi: Double = 0.0,
71+
val dst: Double = 0.0,
72+
val dir: Double = 0.0
73+
) {
74+
fun getAltitude() = alt_baro.toIntOrNull() ?: 0
75+
}

src/main/kotlin/SBSServer.kt

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import java.io.PrintWriter
2+
import java.net.ServerSocket
3+
import java.net.Socket
4+
import java.text.SimpleDateFormat
5+
import java.util.*
6+
7+
class SBSServer {
8+
9+
private val clients = mutableListOf<Socket>()
10+
11+
fun start() {
12+
val serverSocket = ServerSocket(30003)
13+
Thread {
14+
while (true) {
15+
val socket = serverSocket.accept()
16+
clients.add(socket)
17+
}
18+
}.start()
19+
}
20+
21+
fun sendData(data: ADSBData) {
22+
Thread {
23+
val iterator = clients.iterator()
24+
while (iterator.hasNext()) {
25+
val socket = iterator.next()
26+
try {
27+
val outputStream = socket.getOutputStream()
28+
val writer = PrintWriter(outputStream)
29+
data.ac.forEachIndexed { id, ac ->
30+
if (ac.alt_baro.toIntOrNull() != null) {
31+
writer.println(
32+
createMessage(
33+
"MSG",
34+
3,
35+
5,
36+
0,
37+
ac.hex,
38+
ac.flight,
39+
ac.flight,
40+
ac.getAltitude(),
41+
ac.gs,
42+
ac.track,
43+
ac.lat,
44+
ac.lon,
45+
ac.squawk
46+
)
47+
)
48+
writer.flush()
49+
}
50+
}
51+
} catch (e: Exception) {
52+
// we don't care
53+
}
54+
}
55+
}.start()
56+
}
57+
58+
/**
59+
* DOCS: http://woodair.net/SBS/Article/Barebones42_Socket_Data.htm
60+
* Example: MSG,3,5,211,4CA2D6,10057,2008/11/28,14:53:50.594,2008/11/28,14:58:51.153,,37000,,,51.45735,-1.02826,,,0,0,0,0
61+
*/
62+
private fun createMessage(
63+
messageType: String = "MSG",
64+
transmissionType: Int = 3,
65+
sessionId: Int = 5,
66+
aircraftId: Int = 0,
67+
hex: String = "",
68+
flightId: String = "",
69+
callSign: String = "",
70+
altitude: Int,
71+
groundSpeed: Double,
72+
track: Double,
73+
lat: Double,
74+
lon: Double,
75+
squawk: String
76+
): String {
77+
val builder = StringBuilder()
78+
// The below basic data fields are standard for all messages (Field 2 used only for MSG)
79+
builder.append("$messageType,")
80+
builder.append("$transmissionType,")
81+
builder.append("$sessionId,")
82+
builder.append("$aircraftId,")
83+
builder.append("$hex,")
84+
builder.append("$flightId,")
85+
builder.append("${getDate()},")
86+
builder.append("${getTime()},")
87+
builder.append("${getDate()},")
88+
builder.append("${getTime()},")
89+
// The fields below contain specific aircraft information
90+
builder.append("${callSign},")
91+
builder.append("${altitude},")
92+
builder.append("${groundSpeed},")
93+
builder.append("${track},")
94+
builder.append("${formatCoordinate(lat)},")
95+
builder.append("${formatCoordinate(lon)},")
96+
builder.append(",") // vertical rate
97+
builder.append("${squawk},")
98+
builder.append(",") // alert
99+
builder.append(",") // emergency
100+
builder.append(",") // SPI
101+
builder.append("0") // isOnGround
102+
103+
val message = builder.toString()
104+
println(message)
105+
return message
106+
}
107+
108+
private fun getDate() = SimpleDateFormat("yyyy/MM/dd").format(Calendar.getInstance().time)
109+
110+
private fun getTime() = SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().time)
111+
112+
private fun formatCoordinate(coordinate: Double): String {
113+
return String.format("%.5f", coordinate)
114+
}
115+
}

src/test/.DS_Store

6 KB
Binary file not shown.

0 commit comments

Comments
 (0)