mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Added DataParser.kt with tests, removed unused classes
This commit is contained in:
1 parent
c8f7a29812
commit
5998a7e66d
19 files changed
+282
-343
No files matched your search
@@ -71,10 +71,6 @@ dependencies {
|
|||||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||||
kapt "com.google.dagger:hilt-android-compiler:$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 "org.osmdroid:osmdroid-android:$osmdroid_version"
|
||||||
implementation "com.jakewharton.timber:timber:$timber_version"
|
implementation "com.jakewharton.timber:timber:$timber_version"
|
||||||
|
|
||||||
|
|||||||
@@ -26,15 +26,11 @@ import com.rtbishop.look4sat.framework.model.Transmitter as FrameworkTransmitter
|
|||||||
|
|
||||||
fun DomainEntry.toFramework() = FrameworkEntry(this.tle, this.isSelected)
|
fun DomainEntry.toFramework() = FrameworkEntry(this.tle, this.isSelected)
|
||||||
|
|
||||||
fun DomainItem.toFramework() = FrameworkItem(this.catnum, this.name, this.isSelected, this.modes)
|
|
||||||
|
|
||||||
fun DomainTransmitter.toFramework() = FrameworkTransmitter(
|
fun DomainTransmitter.toFramework() = FrameworkTransmitter(
|
||||||
this.uuid, this.info, this.isAlive, this.downlink,
|
this.uuid, this.info, this.isAlive, this.downlink,
|
||||||
this.uplink, this.mode, this.isInverted, this.catnum
|
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 FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.isSelected, this.modes)
|
||||||
|
|
||||||
fun FrameworkTransmitter.toDomain() = DomainTransmitter(
|
fun FrameworkTransmitter.toDomain() = DomainTransmitter(
|
||||||
@@ -44,12 +40,8 @@ fun FrameworkTransmitter.toDomain() = DomainTransmitter(
|
|||||||
|
|
||||||
fun List<DomainEntry>.toFrameworkEntries() = this.map { it.toFramework() }
|
fun List<DomainEntry>.toFrameworkEntries() = this.map { it.toFramework() }
|
||||||
|
|
||||||
fun List<DomainItem>.toFrameworkItems() = this.map { it.toFramework() }
|
|
||||||
|
|
||||||
fun List<DomainTransmitter>.toFramework() = this.map { it.toFramework() }
|
fun List<DomainTransmitter>.toFramework() = this.map { it.toFramework() }
|
||||||
|
|
||||||
fun List<FrameworkEntry>.toDomainEntries() = this.map { it.toDomain() }
|
|
||||||
|
|
||||||
fun List<FrameworkItem>.toDomainItems() = this.map { it.toDomain() }
|
fun List<FrameworkItem>.toDomainItems() = this.map { it.toDomain() }
|
||||||
|
|
||||||
fun List<FrameworkTransmitter>.toDomain() = this.map { it.toDomain() }
|
fun List<FrameworkTransmitter>.toDomain() = this.map { it.toDomain() }
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,16 +19,15 @@ package com.rtbishop.look4sat.framework.model
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.squareup.moshi.Json
|
|
||||||
|
|
||||||
@Entity(tableName = "transmitters")
|
@Entity(tableName = "transmitters")
|
||||||
data class Transmitter(
|
data class Transmitter(
|
||||||
@PrimaryKey @field:Json(name = "uuid") val uuid: String,
|
@PrimaryKey val uuid: String,
|
||||||
@field:Json(name = "description") val info: String,
|
val info: String,
|
||||||
@field:Json(name = "alive") val isAlive: Boolean,
|
val isAlive: Boolean,
|
||||||
@field:Json(name = "downlink_low") var downlink: Long?,
|
var downlink: Long?,
|
||||||
@field:Json(name = "uplink_low") var uplink: Long?,
|
var uplink: Long?,
|
||||||
@field:Json(name = "mode") val mode: String?,
|
val mode: String?,
|
||||||
@field:Json(name = "invert") val isInverted: Boolean,
|
val isInverted: Boolean,
|
||||||
@field:Json(name = "norad_cat_id") val catnum: Int?
|
val catnum: Int?
|
||||||
)
|
)
|
||||||
@@ -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<Transmitter> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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<Transmitter> {
|
|
||||||
return satelliteApi.fetchTransmitters(url).toDomain()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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<ResponseBody>
|
|
||||||
|
|
||||||
@GET
|
|
||||||
suspend fun fetchTransmitters(@Url url: String): List<Transmitter>
|
|
||||||
}
|
|
||||||
@@ -23,17 +23,11 @@ import android.content.SharedPreferences
|
|||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import androidx.preference.PreferenceManager
|
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.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import retrofit2.Retrofit
|
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ package com.rtbishop.look4sat.injection
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.rtbishop.look4sat.data.DefaultRepository
|
import com.rtbishop.look4sat.data.DefaultRepository
|
||||||
|
import com.rtbishop.look4sat.domain.DataParser
|
||||||
import com.rtbishop.look4sat.domain.DataRepository
|
import com.rtbishop.look4sat.domain.DataRepository
|
||||||
import com.rtbishop.look4sat.domain.DataReporter
|
import com.rtbishop.look4sat.domain.DataReporter
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||||
import com.rtbishop.look4sat.framework.local.*
|
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.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
@@ -41,17 +42,15 @@ object CoreModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
fun provideSatelliteRepo(
|
fun provideSatelliteRepo(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
@IoDispatcher dispatcher: CoroutineDispatcher
|
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||||
|
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
||||||
): DataRepository {
|
): DataRepository {
|
||||||
|
val dataParser = DataParser(defaultDispatcher)
|
||||||
val db = Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb")
|
val db = Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb")
|
||||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4).build()
|
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4).build()
|
||||||
val roomLocalSource = LocalSource(db.entriesDao(), db.sourcesDao(), db.transmittersDao())
|
val localSource = LocalSource(db.entriesDao(), db.sourcesDao(), db.transmittersDao())
|
||||||
// val satelliteApi = Retrofit.Builder().baseUrl("https://localhost")
|
val remoteSource = RemoteSource(ioDispatcher)
|
||||||
// .addConverterFactory(MoshiConverterFactory.create()).build()
|
return DefaultRepository(dataParser, localSource, remoteSource, ioDispatcher)
|
||||||
// .create(SatelliteApi::class.java)
|
|
||||||
// val retrofitRemoteSource = RetrofitRemoteSource(satelliteApi)
|
|
||||||
val defaultRemoteSource = DefaultRemoteSource(dispatcher)
|
|
||||||
return DefaultRepository(roomLocalSource, defaultRemoteSource, dispatcher)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
+1
-2
@@ -11,8 +11,7 @@ buildscript {
|
|||||||
preference_version = '1.1.1'
|
preference_version = '1.1.1'
|
||||||
room_version = '2.3.0'
|
room_version = '2.3.0'
|
||||||
hilt_version = '2.39.1'
|
hilt_version = '2.39.1'
|
||||||
okhttp_version = '4.9.0'
|
json_version = '20210307'
|
||||||
retrofit_version = '2.9.0'
|
|
||||||
osmdroid_version = '6.1.11'
|
osmdroid_version = '6.1.11'
|
||||||
timber_version = '5.0.1'
|
timber_version = '5.0.1'
|
||||||
junit_version = '4.13.2'
|
junit_version = '4.13.2'
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||||
|
implementation "org.json:json:$json_version"
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.mockito:mockito-core:$mockito_version"
|
testImplementation "org.mockito:mockito-core:$mockito_version"
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.data
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.domain.DataParser
|
||||||
import com.rtbishop.look4sat.domain.DataRepository
|
import com.rtbishop.look4sat.domain.DataRepository
|
||||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||||
import com.rtbishop.look4sat.domain.predict.TLE
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -28,6 +28,7 @@ import java.io.InputStream
|
|||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
class DefaultRepository(
|
class DefaultRepository(
|
||||||
|
private val dataParser: DataParser,
|
||||||
private val localSource: LocalDataSource,
|
private val localSource: LocalDataSource,
|
||||||
private val remoteSource: RemoteDataSource,
|
private val remoteSource: RemoteDataSource,
|
||||||
private val repoDispatcher: CoroutineDispatcher
|
private val repoDispatcher: CoroutineDispatcher
|
||||||
@@ -66,20 +67,20 @@ class DefaultRepository(
|
|||||||
val streams = mutableListOf<InputStream>()
|
val streams = mutableListOf<InputStream>()
|
||||||
val entries = mutableListOf<SatEntry>()
|
val entries = mutableListOf<SatEntry>()
|
||||||
sources.forEach { source ->
|
sources.forEach { source ->
|
||||||
remoteSource.fetchFileStream(source)?.let { stream ->
|
val fileStream = remoteSource.fetchFileStream(source)
|
||||||
if (source.contains(".zip", true)) {
|
if (source.contains(".zip", true)) {
|
||||||
val zipStream = ZipInputStream(stream).apply { nextEntry }
|
val zipStream = ZipInputStream(fileStream).apply { nextEntry }
|
||||||
streams.add(zipStream)
|
streams.add(zipStream)
|
||||||
} else {
|
} else {
|
||||||
streams.add(stream)
|
streams.add(fileStream)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
||||||
localSource.updateEntries(entries)
|
localSource.updateEntries(entries)
|
||||||
}
|
}
|
||||||
launch(repoDispatcher) {
|
launch(repoDispatcher) {
|
||||||
val transmitters = remoteSource.fetchTransmitters(transmittersSource)
|
val jsonStream = remoteSource.fetchFileStream(transmittersSource)
|
||||||
|
val transmitters = dataParser.parseJSONStream(jsonStream)
|
||||||
localSource.updateTransmitters(transmitters)
|
localSource.updateTransmitters(transmitters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +90,7 @@ class DefaultRepository(
|
|||||||
localSource.updateSelection(catnums, isSelected)
|
localSource.updateSelection(catnums, isSelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importSatellites(stream: InputStream): List<SatEntry> {
|
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
||||||
return TLE.parseTleStream(stream).map { tle -> SatEntry(tle) }
|
return dataParser.parseTLEStream(stream).map { tle -> SatEntry(tle) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,12 +17,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.data
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
interface RemoteDataSource {
|
interface RemoteDataSource {
|
||||||
|
|
||||||
suspend fun fetchFileStream(url: String): InputStream?
|
suspend fun fetchFileStream(fileUrl: String): InputStream
|
||||||
|
|
||||||
suspend fun fetchTransmitters(url: String): List<Transmitter>
|
|
||||||
}
|
}
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<TLE> = withContext(parserDispatcher) {
|
||||||
|
val parsedItems = mutableListOf<TLE>()
|
||||||
|
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<TLE> = withContext(parserDispatcher) {
|
||||||
|
val tleStrings = mutableListOf(String(), String(), String())
|
||||||
|
val parsedItems = mutableListOf<TLE>()
|
||||||
|
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<Transmitter> {
|
||||||
|
return withContext(parserDispatcher) {
|
||||||
|
val parsedItems = mutableListOf<Transmitter>()
|
||||||
|
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<String>): 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<String>): 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
@@ -17,9 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.domain.predict
|
package com.rtbishop.look4sat.domain.predict
|
||||||
|
|
||||||
import java.io.InputStream
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
data class TLE(
|
data class TLE(
|
||||||
val name: String,
|
val name: String,
|
||||||
val epoch: Double,
|
val epoch: Double,
|
||||||
@@ -45,48 +42,4 @@ data class TLE(
|
|||||||
else -> NearEarthSat(this)
|
else -> NearEarthSat(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun parseTleStream(stream: InputStream): List<TLE> {
|
|
||||||
val tleStrings = mutableListOf(String(), String(), String())
|
|
||||||
val parsedItems = mutableListOf<TLE>()
|
|
||||||
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<String>): 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user