diff --git a/app/build.gradle b/app/build.gradle index f924908f..07116392 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -71,10 +71,6 @@ dependencies { implementation "com.google.dagger:hilt-android:$hilt_version" kapt "com.google.dagger:hilt-android-compiler:$hilt_version" - implementation "com.squareup.okhttp3:okhttp:$okhttp_version" - implementation "com.squareup.retrofit2:retrofit:$retrofit_version" - implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" - implementation "org.osmdroid:osmdroid-android:$osmdroid_version" implementation "com.jakewharton.timber:timber:$timber_version" diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/DataMapper.kt b/app/src/main/java/com/rtbishop/look4sat/framework/DataMapper.kt index b678c1bb..d21a8420 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/DataMapper.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/DataMapper.kt @@ -26,15 +26,11 @@ import com.rtbishop.look4sat.framework.model.Transmitter as FrameworkTransmitter fun DomainEntry.toFramework() = FrameworkEntry(this.tle, this.isSelected) -fun DomainItem.toFramework() = FrameworkItem(this.catnum, this.name, this.isSelected, this.modes) - fun DomainTransmitter.toFramework() = FrameworkTransmitter( this.uuid, this.info, this.isAlive, this.downlink, this.uplink, this.mode, this.isInverted, this.catnum ) -fun FrameworkEntry.toDomain() = DomainEntry(this.tle, this.isSelected) - fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.isSelected, this.modes) fun FrameworkTransmitter.toDomain() = DomainTransmitter( @@ -44,12 +40,8 @@ fun FrameworkTransmitter.toDomain() = DomainTransmitter( fun List.toFrameworkEntries() = this.map { it.toFramework() } -fun List.toFrameworkItems() = this.map { it.toFramework() } - fun List.toFramework() = this.map { it.toFramework() } -fun List.toDomainEntries() = this.map { it.toDomain() } - fun List.toDomainItems() = this.map { it.toDomain() } fun List.toDomain() = this.map { it.toDomain() } diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/model/OMM.kt b/app/src/main/java/com/rtbishop/look4sat/framework/model/OMM.kt deleted file mode 100644 index 87291b96..00000000 --- a/app/src/main/java/com/rtbishop/look4sat/framework/model/OMM.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Look4Sat. Amateur radio satellite tracker and pass predictor. - * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.rtbishop.look4sat.framework.model - -import com.rtbishop.look4sat.domain.predict.TLE -import com.squareup.moshi.Json -import java.text.SimpleDateFormat -import java.util.* - -data class OMM( - @field:Json(name = "OBJECT_NAME") val name: String, - @field:Json(name = "EPOCH") val epochString: String, - @field:Json(name = "MEAN_MOTION") val meanmo: Double, - @field:Json(name = "ECCENTRICITY") val eccn: Double, - @field:Json(name = "INCLINATION") val incl: Double, - @field:Json(name = "RA_OF_ASC_NODE") val raan: Double, - @field:Json(name = "ARG_OF_PERICENTER") val argper: Double, - @field:Json(name = "MEAN_ANOMALY") val meanan: Double, - @field:Json(name = "NORAD_CAT_ID") val catnum: Int, - @field:Json(name = "BSTAR") val bstar: Double, -) { - - fun toTLE(): TLE { - calendar.time = sdf.parse(epochString) ?: Date() - val year = calendar.get(Calendar.YEAR).toString().substring(2, 4) - val day = calendar.get(Calendar.DAY_OF_YEAR) - val hours = calendar.get(Calendar.HOUR_OF_DAY) - val minutes = calendar.get(Calendar.MINUTE) - val seconds = calendar.get(Calendar.SECOND) - val fraction = ((hours * 60 * 60 + minutes * 60 + seconds) / secondsInDay).toString() - val epoch = "$year$day${fraction.substring(1, fraction.length)}".toDouble() - return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar) - } - - companion object { - private const val secondsInDay = 86400.0 - private const val pattern = "yyyy-MM-dd'T'HH:mm:ss" - private val calendar = Calendar.getInstance() - private val sdf = SimpleDateFormat(pattern, Locale.US) - } -} diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/model/Transmitter.kt b/app/src/main/java/com/rtbishop/look4sat/framework/model/Transmitter.kt index a7377c45..22c4e449 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/model/Transmitter.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/model/Transmitter.kt @@ -19,16 +19,15 @@ package com.rtbishop.look4sat.framework.model import androidx.room.Entity import androidx.room.PrimaryKey -import com.squareup.moshi.Json @Entity(tableName = "transmitters") data class Transmitter( - @PrimaryKey @field:Json(name = "uuid") val uuid: String, - @field:Json(name = "description") val info: String, - @field:Json(name = "alive") val isAlive: Boolean, - @field:Json(name = "downlink_low") var downlink: Long?, - @field:Json(name = "uplink_low") var uplink: Long?, - @field:Json(name = "mode") val mode: String?, - @field:Json(name = "invert") val isInverted: Boolean, - @field:Json(name = "norad_cat_id") val catnum: Int? + @PrimaryKey val uuid: String, + val info: String, + val isAlive: Boolean, + var downlink: Long?, + var uplink: Long?, + val mode: String?, + val isInverted: Boolean, + val catnum: Int? ) diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/remote/DefaultRemoteSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/remote/DefaultRemoteSource.kt deleted file mode 100644 index 652e643a..00000000 --- a/app/src/main/java/com/rtbishop/look4sat/framework/remote/DefaultRemoteSource.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.rtbishop.look4sat.framework.remote - -import com.rtbishop.look4sat.data.RemoteDataSource -import com.rtbishop.look4sat.domain.model.Transmitter -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.withContext -import java.io.InputStream -import java.net.URL - -class DefaultRemoteSource(private val dispatcher: CoroutineDispatcher) : RemoteDataSource { - - @Suppress("BlockingMethodInNonBlockingContext") - override suspend fun fetchFileStream(url: String): InputStream? = withContext(dispatcher) { - return@withContext URL(url).openStream() - } - - override suspend fun fetchTransmitters(url: String): List { - val stream = URL(url).openStream() - return emptyList() - } - - private suspend fun download(url: String): InputStream? = withContext(dispatcher) { - return@withContext try { - URL(url).openStream().bufferedReader().useLines { lines -> - lines.forEachIndexed { index, line -> - val items = line.split(",") - } - } - null - } catch (e: Exception) { - return@withContext null - } - } -} diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/remote/RemoteSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/remote/RemoteSource.kt new file mode 100644 index 00000000..2d768731 --- /dev/null +++ b/app/src/main/java/com/rtbishop/look4sat/framework/remote/RemoteSource.kt @@ -0,0 +1,15 @@ +package com.rtbishop.look4sat.framework.remote + +import com.rtbishop.look4sat.data.RemoteDataSource +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import java.io.InputStream +import java.net.URL + +class RemoteSource(private val ioDispatcher: CoroutineDispatcher) : RemoteDataSource { + + @Suppress("BlockingMethodInNonBlockingContext") + override suspend fun fetchFileStream(fileUrl: String): InputStream { + return withContext(ioDispatcher) { URL(fileUrl).openStream() } + } +} diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/remote/RetrofitRemoteSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/remote/RetrofitRemoteSource.kt deleted file mode 100644 index ba80441e..00000000 --- a/app/src/main/java/com/rtbishop/look4sat/framework/remote/RetrofitRemoteSource.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Look4Sat. Amateur radio satellite tracker and pass predictor. - * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.rtbishop.look4sat.framework.remote - -import com.rtbishop.look4sat.data.RemoteDataSource -import com.rtbishop.look4sat.domain.model.Transmitter -import com.rtbishop.look4sat.framework.toDomain -import java.io.InputStream - -class RetrofitRemoteSource(private val satelliteApi: SatelliteApi) : RemoteDataSource { - - override suspend fun fetchFileStream(url: String): InputStream? { - return satelliteApi.fetchFileStream(url).body()?.byteStream() - } - - override suspend fun fetchTransmitters(url: String): List { - return satelliteApi.fetchTransmitters(url).toDomain() - } -} diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/remote/SatelliteApi.kt b/app/src/main/java/com/rtbishop/look4sat/framework/remote/SatelliteApi.kt deleted file mode 100644 index 5ee056ea..00000000 --- a/app/src/main/java/com/rtbishop/look4sat/framework/remote/SatelliteApi.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Look4Sat. Amateur radio satellite tracker and pass predictor. - * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.rtbishop.look4sat.framework.remote - -import com.rtbishop.look4sat.framework.model.Transmitter -import okhttp3.ResponseBody -import retrofit2.Response -import retrofit2.http.GET -import retrofit2.http.Streaming -import retrofit2.http.Url - -interface SatelliteApi { - - @Streaming - @GET - suspend fun fetchFileStream(@Url url: String): Response - - @GET - suspend fun fetchTransmitters(@Url url: String): List -} diff --git a/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt b/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt index 49b5150d..9d65dd0a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt +++ b/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt @@ -23,17 +23,11 @@ import android.content.SharedPreferences import android.hardware.SensorManager import android.location.LocationManager import androidx.preference.PreferenceManager -import androidx.room.Room -import com.rtbishop.look4sat.framework.local.* -import com.rtbishop.look4sat.framework.remote.SatelliteApi import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory -import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) diff --git a/app/src/main/java/com/rtbishop/look4sat/injection/CoreModule.kt b/app/src/main/java/com/rtbishop/look4sat/injection/CoreModule.kt index de9238ae..b8c31d7f 100644 --- a/app/src/main/java/com/rtbishop/look4sat/injection/CoreModule.kt +++ b/app/src/main/java/com/rtbishop/look4sat/injection/CoreModule.kt @@ -20,11 +20,12 @@ package com.rtbishop.look4sat.injection import android.content.Context import androidx.room.Room import com.rtbishop.look4sat.data.DefaultRepository +import com.rtbishop.look4sat.domain.DataParser import com.rtbishop.look4sat.domain.DataRepository import com.rtbishop.look4sat.domain.DataReporter import com.rtbishop.look4sat.domain.predict.Predictor import com.rtbishop.look4sat.framework.local.* -import com.rtbishop.look4sat.framework.remote.DefaultRemoteSource +import com.rtbishop.look4sat.framework.remote.RemoteSource import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -41,17 +42,15 @@ object CoreModule { @Singleton fun provideSatelliteRepo( @ApplicationContext context: Context, - @IoDispatcher dispatcher: CoroutineDispatcher + @IoDispatcher ioDispatcher: CoroutineDispatcher, + @DefaultDispatcher defaultDispatcher: CoroutineDispatcher ): DataRepository { + val dataParser = DataParser(defaultDispatcher) val db = Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb") .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4).build() - val roomLocalSource = LocalSource(db.entriesDao(), db.sourcesDao(), db.transmittersDao()) -// val satelliteApi = Retrofit.Builder().baseUrl("https://localhost") -// .addConverterFactory(MoshiConverterFactory.create()).build() -// .create(SatelliteApi::class.java) -// val retrofitRemoteSource = RetrofitRemoteSource(satelliteApi) - val defaultRemoteSource = DefaultRemoteSource(dispatcher) - return DefaultRepository(roomLocalSource, defaultRemoteSource, dispatcher) + val localSource = LocalSource(db.entriesDao(), db.sourcesDao(), db.transmittersDao()) + val remoteSource = RemoteSource(ioDispatcher) + return DefaultRepository(dataParser, localSource, remoteSource, ioDispatcher) } @Provides diff --git a/build.gradle b/build.gradle index e4328748..2768a340 100644 --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,7 @@ buildscript { preference_version = '1.1.1' room_version = '2.3.0' hilt_version = '2.39.1' - okhttp_version = '4.9.0' - retrofit_version = '2.9.0' + json_version = '20210307' osmdroid_version = '6.1.11' timber_version = '5.0.1' junit_version = '4.13.2' diff --git a/core/build.gradle b/core/build.gradle index 76af33fe..87408990 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -13,6 +13,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.json:json:$json_version" testImplementation "junit:junit:$junit_version" testImplementation "org.mockito:mockito-core:$mockito_version" diff --git a/core/src/main/java/com/rtbishop/look4sat/data/DefaultRepository.kt b/core/src/main/java/com/rtbishop/look4sat/data/DefaultRepository.kt index 0d6f563b..8b7ffa76 100644 --- a/core/src/main/java/com/rtbishop/look4sat/data/DefaultRepository.kt +++ b/core/src/main/java/com/rtbishop/look4sat/data/DefaultRepository.kt @@ -17,9 +17,9 @@ */ package com.rtbishop.look4sat.data +import com.rtbishop.look4sat.domain.DataParser import com.rtbishop.look4sat.domain.DataRepository import com.rtbishop.look4sat.domain.model.SatEntry -import com.rtbishop.look4sat.domain.predict.TLE import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -28,6 +28,7 @@ import java.io.InputStream import java.util.zip.ZipInputStream class DefaultRepository( + private val dataParser: DataParser, private val localSource: LocalDataSource, private val remoteSource: RemoteDataSource, private val repoDispatcher: CoroutineDispatcher @@ -66,20 +67,20 @@ class DefaultRepository( val streams = mutableListOf() val entries = mutableListOf() sources.forEach { source -> - remoteSource.fetchFileStream(source)?.let { stream -> - if (source.contains(".zip", true)) { - val zipStream = ZipInputStream(stream).apply { nextEntry } - streams.add(zipStream) - } else { - streams.add(stream) - } + val fileStream = remoteSource.fetchFileStream(source) + if (source.contains(".zip", true)) { + val zipStream = ZipInputStream(fileStream).apply { nextEntry } + streams.add(zipStream) + } else { + streams.add(fileStream) } } streams.forEach { stream -> entries.addAll(importSatellites(stream)) } localSource.updateEntries(entries) } launch(repoDispatcher) { - val transmitters = remoteSource.fetchTransmitters(transmittersSource) + val jsonStream = remoteSource.fetchFileStream(transmittersSource) + val transmitters = dataParser.parseJSONStream(jsonStream) localSource.updateTransmitters(transmitters) } } @@ -89,7 +90,7 @@ class DefaultRepository( localSource.updateSelection(catnums, isSelected) } - private fun importSatellites(stream: InputStream): List { - return TLE.parseTleStream(stream).map { tle -> SatEntry(tle) } + private suspend fun importSatellites(stream: InputStream): List { + return dataParser.parseTLEStream(stream).map { tle -> SatEntry(tle) } } } diff --git a/core/src/main/java/com/rtbishop/look4sat/data/RemoteDataSource.kt b/core/src/main/java/com/rtbishop/look4sat/data/RemoteDataSource.kt index 717ed5e1..dff99ea2 100644 --- a/core/src/main/java/com/rtbishop/look4sat/data/RemoteDataSource.kt +++ b/core/src/main/java/com/rtbishop/look4sat/data/RemoteDataSource.kt @@ -17,12 +17,9 @@ */ package com.rtbishop.look4sat.data -import com.rtbishop.look4sat.domain.model.Transmitter import java.io.InputStream interface RemoteDataSource { - suspend fun fetchFileStream(url: String): InputStream? - - suspend fun fetchTransmitters(url: String): List + suspend fun fetchFileStream(fileUrl: String): InputStream } diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/DataParser.kt b/core/src/main/java/com/rtbishop/look4sat/domain/DataParser.kt new file mode 100644 index 00000000..f191800e --- /dev/null +++ b/core/src/main/java/com/rtbishop/look4sat/domain/DataParser.kt @@ -0,0 +1,150 @@ +/* + * Look4Sat. Amateur radio satellite tracker and pass predictor. + * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.rtbishop.look4sat.domain + +import com.rtbishop.look4sat.domain.model.Transmitter +import com.rtbishop.look4sat.domain.predict.TLE +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import org.json.JSONArray +import org.json.JSONObject +import java.io.InputStream +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.pow + +class DataParser(private val parserDispatcher: CoroutineDispatcher) { + + private val calendar = Calendar.getInstance() + private val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) + + suspend fun parseCSVStream(csvStream: InputStream): List = withContext(parserDispatcher) { + val parsedItems = mutableListOf() + csvStream.bufferedReader().useLines { lines -> + lines.forEachIndexed { index, line -> + if (index != 0) { + val values = line.split(",") + parseCSV(values)?.let { tle -> parsedItems.add(tle) } + } + } + } + return@withContext parsedItems + } + + suspend fun parseTLEStream(tleStream: InputStream): List = withContext(parserDispatcher) { + val tleStrings = mutableListOf(String(), String(), String()) + val parsedItems = mutableListOf() + var lineIndex = 0 + tleStream.bufferedReader().forEachLine { line -> + tleStrings[lineIndex] = line + if (lineIndex < 2) { + lineIndex++ + } else { + val isLineOneValid = tleStrings[1].substring(0, 1) == "1" + val isLineTwoValid = tleStrings[2].substring(0, 1) == "2" + if (!isLineOneValid && !isLineTwoValid) return@forEachLine + parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) } + lineIndex = 0 + } + } + return@withContext parsedItems + } + + suspend fun parseJSONStream(jsonStream: InputStream): List { + return withContext(parserDispatcher) { + val parsedItems = mutableListOf() + try { + val jsonArray = JSONArray(jsonStream.bufferedReader().readText()) + for (index in 0 until jsonArray.length()) { + val jsonObject = jsonArray.getJSONObject(index) + parseJSON(jsonObject)?.let { parsedItems.add(it) } + } + return@withContext parsedItems + } catch (exception: Exception) { + return@withContext parsedItems + } + } + } + + private fun parseCSV(values: List): TLE? { + try { + val name = values[0] + calendar.time = simpleDateFormat.parse(values[2]) ?: Date() + val year = calendar.get(Calendar.YEAR).toString().substring(2, 4) + val day = calendar.get(Calendar.DAY_OF_YEAR) + val hours = calendar.get(Calendar.HOUR_OF_DAY) + val minutes = calendar.get(Calendar.MINUTE) + val seconds = calendar.get(Calendar.SECOND) + val fraction = ((hours * 60 * 60 + minutes * 60 + seconds) / 86400.0).toString() + val epoch = "$year$day${fraction.substring(1, fraction.length)}".toDouble() + val meanmo = values[3].toDouble() + val eccn = values[4].toDouble() + val incl = values[5].toDouble() + val raan = values[6].toDouble() + val argper = values[7].toDouble() + val meanan = values[8].toDouble() + val catnum = values[11].toInt() + val bstar = values[14].toDouble() + return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar) + } catch (exception: Exception) { + return null + } + } + + private fun parseTLE(tle: List): TLE? { + if (tle[1].substring(0, 1) != "1" && tle[2].substring(0, 1) != "2") { + return null + } + try { + val name: String = tle[0].trim() + val epoch: Double = tle[1].substring(18, 32).toDouble() + val meanmo: Double = tle[2].substring(52, 63).toDouble() + val eccn: Double = 1.0e-07 * tle[2].substring(26, 33).toDouble() + val incl: Double = tle[2].substring(8, 16).toDouble() + val raan: Double = tle[2].substring(17, 25).toDouble() + val argper: Double = tle[2].substring(34, 42).toDouble() + val meanan: Double = tle[2].substring(43, 51).toDouble() + val catnum: Int = tle[1].substring(2, 7).trim().toInt() + val bstar: Double = 1.0e-5 * tle[1].substring(53, 59).toDouble() / + 10.0.pow(tle[1].substring(60, 61).toDouble()) + return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar) + } catch (exception: Exception) { + return null + } + } + + private fun parseJSON(json: JSONObject): Transmitter? { + try { + val uuid = json.getString("uuid") + val info = json.getString("description") + val isAlive = json.getBoolean("alive") + val downlink = if (json.isNull("downlink_low")) null + else json.getLong("downlink_low") + val uplink = if (json.isNull("uplink_low")) null + else json.getLong("uplink_low") + val mode = if (json.isNull("mode")) null + else json.getString("mode") + val isInverted = json.getBoolean("invert") + val catnum = if (json.isNull("norad_cat_id")) null + else json.getInt("norad_cat_id") + return Transmitter(uuid, info, isAlive, downlink, uplink, mode, isInverted, catnum) + } catch (exception: Exception) { + return null + } + } +} diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/model/OMM.kt b/core/src/main/java/com/rtbishop/look4sat/domain/model/OMM.kt deleted file mode 100644 index 7a1ce573..00000000 --- a/core/src/main/java/com/rtbishop/look4sat/domain/model/OMM.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Look4Sat. Amateur radio satellite tracker and pass predictor. - * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.rtbishop.look4sat.domain.model - -data class OMM( - val name: String, - val epochString: String, - val meanmo: Double, - val eccn: Double, - val incl: Double, - val raan: Double, - val argper: Double, - val meanan: Double, - val catnum: Int, - val bstar: Double, -) diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict/TLE.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict/TLE.kt index 7953c90d..12926cb0 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict/TLE.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict/TLE.kt @@ -17,9 +17,6 @@ */ package com.rtbishop.look4sat.domain.predict -import java.io.InputStream -import kotlin.math.pow - data class TLE( val name: String, val epoch: Double, @@ -45,48 +42,4 @@ data class TLE( else -> NearEarthSat(this) } } - - companion object { - - fun parseTleStream(stream: InputStream): List { - val tleStrings = mutableListOf(String(), String(), String()) - val parsedItems = mutableListOf() - var lineIndex = 0 - stream.bufferedReader().forEachLine { line -> - tleStrings[lineIndex] = line - if (lineIndex < 2) { - lineIndex++ - } else { - val isLineOneValid = tleStrings[1].substring(0, 1) == "1" - val isLineTwoValid = tleStrings[2].substring(0, 1) == "2" - if (!isLineOneValid && !isLineTwoValid) return@forEachLine - parseTleStrings(tleStrings)?.let { tle -> parsedItems.add(tle) } - lineIndex = 0 - } - } - return parsedItems - } - - private fun parseTleStrings(tleStrings: List): TLE? { - if (tleStrings[1].substring(0, 1) != "1" && tleStrings[2].substring(0, 1) != "2") { - return null - } - try { - val name: String = tleStrings[0].trim() - val epoch: Double = tleStrings[1].substring(18, 32).toDouble() - val meanmo: Double = tleStrings[2].substring(52, 63).toDouble() - val eccn: Double = 1.0e-07 * tleStrings[2].substring(26, 33).toDouble() - val incl: Double = tleStrings[2].substring(8, 16).toDouble() - val raan: Double = tleStrings[2].substring(17, 25).toDouble() - val argper: Double = tleStrings[2].substring(34, 42).toDouble() - val meanan: Double = tleStrings[2].substring(43, 51).toDouble() - val catnum: Int = tleStrings[1].substring(2, 7).trim().toInt() - val bstar: Double = 1.0e-5 * tleStrings[1].substring(53, 59).toDouble() / - 10.0.pow(tleStrings[1].substring(60, 61).toDouble()) - return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar) - } catch (exception: Exception) { - return null - } - } - } } diff --git a/core/src/test/java/com/rtbishop/look4sat/DataParserTest.kt b/core/src/test/java/com/rtbishop/look4sat/DataParserTest.kt new file mode 100644 index 00000000..15c2508e --- /dev/null +++ b/core/src/test/java/com/rtbishop/look4sat/DataParserTest.kt @@ -0,0 +1,89 @@ +/* + * Look4Sat. Amateur radio satellite tracker and pass predictor. + * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.rtbishop.look4sat + +import com.rtbishop.look4sat.domain.DataParser +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test + +@ExperimentalCoroutinesApi +class DataParserTest { + + private val dataParser = DataParser(TestCoroutineDispatcher()) + private val validCSVStream = """ + OBJECT_NAME,OBJECT_ID,EPOCH,MEAN_MOTION,ECCENTRICITY,INCLINATION,RA_OF_ASC_NODE,ARG_OF_PERICENTER,MEAN_ANOMALY,EPHEMERIS_TYPE,CLASSIFICATION_TYPE,NORAD_CAT_ID,ELEMENT_SET_NO,REV_AT_EPOCH,BSTAR,MEAN_MOTION_DOT,MEAN_MOTION_DDOT + CALSPHERE 1,1964-063C,2021-10-23T03:34:39.713664,13.73605115,.0026891,90.1713,36.6548,20.0176,64.9795,0,U,900,999,83808,.30972E-3,.299E-5,0 + """.trimIndent().byteInputStream() + private val invalidCSVStream = """ + CALSPHERE 1,1964-063C,2021-10-23T03:34:39.713664,13.73605115,.0026891,90.1713,36.6548,20.0176,64.9795,0,U,900,999,83808,.30972E-3,.299E-5,0 + OBJECT_NAME,OBJECT_ID,EPOCH,MEAN_MOTION,ECCENTRICITY,INCLINATION,RA_OF_ASC_NODE,ARG_OF_PERICENTER,MEAN_ANOMALY,EPHEMERIS_TYPE,CLASSIFICATION_TYPE,NORAD_CAT_ID,ELEMENT_SET_NO,REV_AT_EPOCH,BSTAR,MEAN_MOTION_DOT,MEAN_MOTION_DDOT + """.trimIndent().byteInputStream() + private val validTLEStream = """ + ISS (ZARYA) + 1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998 + 2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085 + """.trimIndent().byteInputStream() + private val invalidTLEStream = """ + 1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998 + 2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085 + """.trimIndent().byteInputStream() + private val validJSONStream = """ + [{"uuid":"UzPz4gcsNBPKPKAFPmer7g","description":"Upper side band (drifting)","alive":true,"type":"Transmitter","uplink_low":null,"uplink_high":null,"uplink_drift":null,"downlink_low":136658500,"downlink_high":null,"downlink_drift":null,"mode":"USB","mode_id":9,"uplink_mode":null,"invert":false,"baud":null,"sat_id":"SCHX-0895-2361-9925-0309","norad_cat_id":965,"status":"active","updated":"2019-04-18T05:39:53.343316Z","citation":"CITATION NEEDED - https://xkcd.com/285/","service":"Unknown","coordination":"","coordination_url":""}] + """.trimIndent().byteInputStream() + private val invalidJSONStream = """ + [{"description":"Upper side band (drifting)","alive":true,"type":"Transmitter","uplink_low":null,"uplink_high":null,"uplink_drift":null,"downlink_low":136658500,"downlink_high":null,"downlink_drift":null,"mode":"USB","mode_id":9,"uplink_mode":null,"invert":false,"baud":null,"sat_id":"SCHX-0895-2361-9925-0309","norad_cat_id":965,"status":"active","updated":"2019-04-18T05:39:53.343316Z","citation":"CITATION NEEDED - https://xkcd.com/285/","service":"Unknown","coordination":"","coordination_url":""}] + """.trimIndent().byteInputStream() + + @Test + fun `Given valid CSV stream returns valid data`() = runBlockingTest { + val parsedList = dataParser.parseCSVStream(validCSVStream) + assert(parsedList[0].catnum == 900) + } + + @Test + fun `Given invalid CSV stream returns empty list`() = runBlockingTest { + val parsedList = dataParser.parseCSVStream(invalidCSVStream) + assert(parsedList.isEmpty()) + } + + @Test + fun `Given valid TLE stream returns valid data`() = runBlockingTest { + val parsedList = dataParser.parseTLEStream(validTLEStream) + assert(parsedList[0].catnum == 25544) + } + + @Test + fun `Given invalid TLE stream returns empty list`() = runBlockingTest { + val parsedList = dataParser.parseTLEStream(invalidTLEStream) + assert(parsedList.isEmpty()) + } + + @Test + fun `Given valid JSON stream returns valid data`() = runBlockingTest { + val parsedList = dataParser.parseJSONStream(validJSONStream) + assert(parsedList[0].catnum == 965) + } + + @Test + fun `Given invalid JSON stream returns empty list`() = runBlockingTest { + val parsedList = dataParser.parseJSONStream(invalidJSONStream) + assert(parsedList.isEmpty()) + } +} diff --git a/core/src/test/java/com/rtbishop/look4sat/TleParserTest.kt b/core/src/test/java/com/rtbishop/look4sat/TleParserTest.kt deleted file mode 100644 index e267cf71..00000000 --- a/core/src/test/java/com/rtbishop/look4sat/TleParserTest.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Look4Sat. Amateur radio satellite tracker and pass predictor. - * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.rtbishop.look4sat - -import com.rtbishop.look4sat.domain.predict.TLE -import org.junit.Test - -class TleParserTest { - - private val validTleStream = """ - ISS (ZARYA) - 1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998 - 2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085 - SHENZHOU-12 - 1 48852U 21053A 21254.65783594 .00009779 00000-0 11349-3 0 9992 - 2 48852 41.4714 115.4555 0001945 272.0324 187.3218 15.61782249 21227 - PROGRESS-MS 17 - 1 48869U 21057A 21254.57379002 .00002620 00000-0 56399-4 0 9993 - 2 48869 51.6436 275.5624 0003404 23.4371 118.9508 15.48629868301984 - """.trimIndent().byteInputStream() - private val invalidTleStream = """ - 1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998 - 2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085 - 1 48852U 21053A 21254.65783594 .00009779 00000-0 11349-3 0 9992 - 2 48852 41.4714 115.4555 0001945 272.0324 187.3218 15.61782249 21227 - 1 48869U 21057A 21254.57379002 .00002620 00000-0 56399-4 0 9993 - 2 48869 51.6436 275.5624 0003404 23.4371 118.9508 15.48629868301984 - """.trimIndent().byteInputStream() - - @Test - fun `Given valid TLE stream returns valid data`() { - val parsedList = TLE.parseTleStream(validTleStream) - assert(parsedList[2].catnum == 48869) - } - - @Test - fun `Given invalid TLE stream returns empty list`() { - val parsedList = TLE.parseTleStream(invalidTleStream) - assert(parsedList.isEmpty()) - } -}