mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Destructively migrated to the new database
This commit is contained in:
1 parent
92eec290b1
commit
64b5010a44
53 files changed
+381
-901
No files matched your search
+1
-1
@@ -18,4 +18,4 @@ dependencies {
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "org.mockito:mockito-core:4.3.1"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0"
|
||||
}
|
||||
}
|
||||
@@ -21,115 +21,99 @@ import com.rtbishop.look4sat.domain.DataParser
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class DataRepository(
|
||||
private val dataParser: DataParser,
|
||||
private val settings: ISettingsHandler,
|
||||
private val localSource: ILocalSource,
|
||||
private val remoteSource: IRemoteSource,
|
||||
private val repositoryScope: CoroutineScope
|
||||
) : IDataRepository {
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||
println("DataRepository: handled $exception")
|
||||
_updateState.value = DataState.Error(exception.message)
|
||||
}
|
||||
private val updateStateDelay = 875L
|
||||
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
|
||||
override val updateState: StateFlow<DataState<Long>> = _updateState
|
||||
|
||||
override suspend fun getAllSatellites(): List<SatItem> {
|
||||
val selection = settings.loadSatelliteSelection()
|
||||
val satellites = localSource.getAllSatellites()
|
||||
satellites.forEach { satItem -> satItem.isSelected = satItem.catnum in selection }
|
||||
return satellites
|
||||
override fun setUpdateStateHandled() {
|
||||
_updateState.value = DataState.Handled
|
||||
}
|
||||
|
||||
override suspend fun getSelectedSatellites(): List<Satellite> {
|
||||
val selection = settings.loadSatelliteSelection()
|
||||
return localSource.getSelectedSatellites(selection)
|
||||
}
|
||||
override suspend fun getEntriesWithModes() = localSource.getEntriesWithModes()
|
||||
|
||||
override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum)
|
||||
override suspend fun getSelectedEntries() = localSource.getSelectedEntries()
|
||||
|
||||
override suspend fun getRadios(catnum: Int) = localSource.getRadios(catnum)
|
||||
|
||||
override fun updateFromFile(uri: String) {
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
_updateState.value = DataState.Loading
|
||||
val updateTimeMillis = measureTimeMillis {
|
||||
localSource.getFileStream(uri)?.let { fileStream ->
|
||||
localSource.updateEntries(importSatellites(fileStream))
|
||||
}
|
||||
localSource.getFileStream(uri)?.let { fileStream ->
|
||||
localSource.insertEntries(importSatellites(fileStream))
|
||||
delay(updateStateDelay)
|
||||
}
|
||||
_updateState.value = DataState.Success(updateTimeMillis)
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateFromWeb(sources: List<String>) {
|
||||
_updateState.value = DataState.Loading
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
settings.saveDataSources(sources)
|
||||
localSource.setDataSources(sources)
|
||||
}
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
val updateTimeMillis = measureTimeMillis {
|
||||
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
||||
val streamsMap = mutableMapOf<String, InputStream?>()
|
||||
val streams = mutableListOf<InputStream>()
|
||||
val entries = mutableListOf<SatEntry>()
|
||||
sources.forEach { jobsMap[it] = async { remoteSource.fetchFileStream(it) } }
|
||||
jobsMap.forEach { streamsMap[it.key] = it.value.await() }
|
||||
streamsMap.forEach { stream ->
|
||||
stream.value?.let { inputStream ->
|
||||
when {
|
||||
stream.key.contains("=csv", true) -> {
|
||||
val tles = dataParser.parseCSVStream(inputStream)
|
||||
entries.addAll(tles.map { tle -> SatEntry(tle) })
|
||||
}
|
||||
stream.key.contains(".zip", true) -> {
|
||||
streams.add(ZipInputStream(inputStream).apply { nextEntry })
|
||||
}
|
||||
else -> streams.add(inputStream)
|
||||
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
||||
val streamsMap = mutableMapOf<String, InputStream?>()
|
||||
val streams = mutableListOf<InputStream>()
|
||||
val entries = mutableListOf<SatEntry>()
|
||||
sources.forEach { jobsMap[it] = async { remoteSource.getFileStream(it) } }
|
||||
jobsMap.forEach { job -> streamsMap[job.key] = job.value.await() }
|
||||
streamsMap.forEach { stream ->
|
||||
stream.value?.let { inputStream ->
|
||||
when {
|
||||
stream.key.contains("=csv", true) -> {
|
||||
val tles = dataParser.parseCSVStream(inputStream)
|
||||
entries.addAll(tles.map { tle -> SatEntry(tle) })
|
||||
}
|
||||
stream.key.contains(".zip", true) -> {
|
||||
streams.add(ZipInputStream(inputStream).apply { nextEntry })
|
||||
}
|
||||
else -> streams.add(inputStream)
|
||||
}
|
||||
}
|
||||
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
||||
localSource.updateEntries(entries)
|
||||
}
|
||||
println("Update from web took $updateTimeMillis ms")
|
||||
_updateState.value = DataState.Success(updateTimeMillis)
|
||||
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
||||
localSource.insertEntries(entries)
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
remoteSource.fetchFileStream(settings.transmittersSource)?.let { stream ->
|
||||
val transmitters = dataParser.parseJSONStream(stream)
|
||||
localSource.updateTransmitters(transmitters)
|
||||
remoteSource.getFileStream(remoteSource.radioApi)?.let { stream ->
|
||||
localSource.insertRadios(dataParser.parseJSONStream(stream))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updatesSelection(catnums: List<Int>) {
|
||||
repositoryScope.launch {
|
||||
settings.saveSatelliteSelection(catnums)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpdateStateHandled() {
|
||||
_updateState.value = DataState.Handled
|
||||
}
|
||||
|
||||
override fun clearAllData() {
|
||||
repositoryScope.launch {
|
||||
_updateState.value = DataState.Loading
|
||||
localSource.clearAllData()
|
||||
delay(updateStateDelay)
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDataSources() = localSource.getDataSources()
|
||||
|
||||
override fun setEntriesSelection(catnums: List<Int>) {
|
||||
repositoryScope.launch { localSource.setEntriesSelection(catnums) }
|
||||
}
|
||||
|
||||
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
||||
return dataParser.parseTLEStream(stream).map { tle -> SatEntry(tle) }
|
||||
}
|
||||
|
||||
@@ -19,23 +19,31 @@ package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import java.io.InputStream
|
||||
|
||||
interface ILocalSource {
|
||||
|
||||
suspend fun getAllSatellites(): List<SatItem>
|
||||
suspend fun getEntriesWithModes(): List<SatItem>
|
||||
|
||||
suspend fun getSelectedSatellites(catnums: List<Int>): List<Satellite>
|
||||
suspend fun getSelectedEntries(): List<Satellite>
|
||||
|
||||
suspend fun getTransmitters(catnum: Int): List<Transmitter>
|
||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
||||
|
||||
suspend fun getFileStream(uri: String): InputStream?
|
||||
|
||||
suspend fun updateEntries(entries: List<SatEntry>)
|
||||
suspend fun insertEntries(entries: List<SatEntry>)
|
||||
|
||||
suspend fun updateTransmitters(transmitters: List<Transmitter>)
|
||||
suspend fun insertRadios(radios: List<SatRadio>)
|
||||
|
||||
suspend fun clearAllData()
|
||||
|
||||
suspend fun getDataSources(): List<String>
|
||||
|
||||
suspend fun setDataSources(sources: List<String>)
|
||||
|
||||
suspend fun getEntriesSelection(): List<Int>
|
||||
|
||||
suspend fun setEntriesSelection(catnums: List<Int>)
|
||||
}
|
||||
@@ -21,5 +21,7 @@ import java.io.InputStream
|
||||
|
||||
interface IRemoteSource {
|
||||
|
||||
suspend fun fetchFileStream(url: String): InputStream?
|
||||
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||
|
||||
suspend fun getFileStream(url: String): InputStream?
|
||||
}
|
||||
@@ -28,8 +28,6 @@ interface ISettingsHandler {
|
||||
"https://celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||
"https://amsat.org/tle/current/nasabare.txt"
|
||||
)
|
||||
val transmittersSource: String
|
||||
get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||
|
||||
fun loadStationPosition(): GeoPos
|
||||
|
||||
@@ -59,9 +57,9 @@ interface ISettingsHandler {
|
||||
|
||||
fun loadModesSelection(): List<String>
|
||||
|
||||
fun saveSatelliteSelection(catnums: List<Int>)
|
||||
fun saveEntriesSelection(catnums: List<Int>)
|
||||
|
||||
fun loadSatelliteSelection(): List<Int>
|
||||
fun loadEntriesSelection(): List<Int>
|
||||
|
||||
fun getRotatorEnabled(): Boolean
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.predict.TLE
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
@@ -28,8 +28,8 @@ import kotlin.math.pow
|
||||
|
||||
class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
|
||||
suspend fun parseCSVStream(csvStream: InputStream): List<TLE> = withContext(parserDispatcher) {
|
||||
val parsedItems = mutableListOf<TLE>()
|
||||
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> = withContext(parserDispatcher) {
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
csvStream.bufferedReader().useLines { lines ->
|
||||
lines.forEachIndexed { index, line ->
|
||||
if (index != 0) {
|
||||
@@ -41,9 +41,9 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
return@withContext parsedItems
|
||||
}
|
||||
|
||||
suspend fun parseTLEStream(tleStream: InputStream): List<TLE> = withContext(parserDispatcher) {
|
||||
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> = withContext(parserDispatcher) {
|
||||
val tleStrings = mutableListOf(String(), String(), String())
|
||||
val parsedItems = mutableListOf<TLE>()
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
var lineIndex = 0
|
||||
tleStream.bufferedReader().forEachLine { line ->
|
||||
tleStrings[lineIndex] = line
|
||||
@@ -60,9 +60,9 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
return@withContext parsedItems
|
||||
}
|
||||
|
||||
suspend fun parseJSONStream(jsonStream: InputStream): List<Transmitter> {
|
||||
suspend fun parseJSONStream(jsonStream: InputStream): List<SatRadio> {
|
||||
return withContext(parserDispatcher) {
|
||||
val parsedItems = mutableListOf<Transmitter>()
|
||||
val parsedItems = mutableListOf<SatRadio>()
|
||||
try {
|
||||
val jsonArray = JSONArray(jsonStream.bufferedReader().readText())
|
||||
for (index in 0 until jsonArray.length()) {
|
||||
@@ -76,7 +76,7 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseCSV(values: List<String>): TLE? {
|
||||
private fun parseCSV(values: List<String>): OrbitalData? {
|
||||
try {
|
||||
val name = values[0]
|
||||
val year = values[2].substring(0, 4)
|
||||
@@ -98,13 +98,13 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
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)
|
||||
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseTLE(tle: List<String>): TLE? {
|
||||
private fun parseTLE(tle: List<String>): OrbitalData? {
|
||||
if (tle[1].substring(0, 1) != "1" && tle[2].substring(0, 1) != "2") {
|
||||
return null
|
||||
}
|
||||
@@ -120,13 +120,13 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
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)
|
||||
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseJSON(json: JSONObject): Transmitter? {
|
||||
private fun parseJSON(json: JSONObject): SatRadio? {
|
||||
try {
|
||||
val uuid = json.getString("uuid")
|
||||
val info = json.getString("description")
|
||||
@@ -140,7 +140,7 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
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)
|
||||
return SatRadio(uuid, info, isAlive, downlink, uplink, mode, isInverted, catnum)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
@@ -27,19 +27,21 @@ interface IDataRepository {
|
||||
|
||||
val updateState: StateFlow<DataState<Long>>
|
||||
|
||||
suspend fun getAllSatellites(): List<SatItem>
|
||||
fun setUpdateStateHandled()
|
||||
|
||||
suspend fun getSelectedSatellites(): List<Satellite>
|
||||
suspend fun getEntriesWithModes(): List<SatItem>
|
||||
|
||||
suspend fun getTransmitters(catnum: Int): List<Transmitter>
|
||||
suspend fun getSelectedEntries(): List<Satellite>
|
||||
|
||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
||||
|
||||
fun updateFromFile(uri: String)
|
||||
|
||||
fun updateFromWeb(sources: List<String>)
|
||||
|
||||
fun updatesSelection(catnums: List<Int>)
|
||||
|
||||
fun setUpdateStateHandled()
|
||||
|
||||
fun clearAllData()
|
||||
|
||||
suspend fun getDataSources(): List<String>
|
||||
|
||||
fun setEntriesSelection(catnums: List<Int>)
|
||||
}
|
||||
@@ -17,9 +17,6 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.TLE
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
|
||||
data class SatEntry(
|
||||
val tle: TLE,
|
||||
var isSelected: Boolean = false
|
||||
)
|
||||
data class SatEntry(val data: OrbitalData, var comment: String? = null)
|
||||
@@ -20,6 +20,6 @@ package com.rtbishop.look4sat.domain.model
|
||||
data class SatItem(
|
||||
val catnum: Int,
|
||||
val name: String,
|
||||
var isSelected: Boolean,
|
||||
val modes: List<String>
|
||||
val modes: List<String>,
|
||||
var isSelected: Boolean
|
||||
)
|
||||
+3
-2
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
data class Transmitter(
|
||||
data class SatRadio(
|
||||
val uuid: String,
|
||||
val info: String,
|
||||
val isAlive: Boolean,
|
||||
@@ -25,5 +25,6 @@ data class Transmitter(
|
||||
var uplink: Long?,
|
||||
val mode: String?,
|
||||
val isInverted: Boolean,
|
||||
val catnum: Int?
|
||||
val catnum: Int?,
|
||||
var comment: String? = null
|
||||
)
|
||||
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
import kotlin.math.*
|
||||
|
||||
class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
|
||||
|
||||
private val c1: Double
|
||||
private val c4: Double
|
||||
@@ -35,41 +35,41 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
|
||||
init {
|
||||
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
|
||||
val a1 = (xke / super.params.xno).pow(twoThirds)
|
||||
dsv.cosio = cos(super.params.xincl)
|
||||
val a1 = (xke / super.data.xno).pow(twoThirds)
|
||||
dsv.cosio = cos(super.data.xincl)
|
||||
dsv.theta2 = dsv.cosio * dsv.cosio
|
||||
x3thm1 = 3.0 * dsv.theta2 - 1
|
||||
dsv.eosq = super.params.eccn * super.params.eccn
|
||||
dsv.eosq = super.data.eccn * super.data.eccn
|
||||
dsv.betao2 = 1.0 - dsv.eosq
|
||||
dsv.betao = sqrt(dsv.betao2)
|
||||
val del1 = 1.5 * ck2 * x3thm1 / (a1 * a1 * dsv.betao * dsv.betao2)
|
||||
val ao = a1 * (1.0 - del1 * (0.5 * twoThirds + del1 * (1.0 + 134.0 / 81.0 * del1)))
|
||||
val delo = 1.5 * ck2 * x3thm1 / (ao * ao * dsv.betao * dsv.betao2)
|
||||
dsv.xnodp = super.params.xno / (1.0 + delo)
|
||||
dsv.xnodp = super.data.xno / (1.0 + delo)
|
||||
dsv.aodp = ao / (1.0 - delo)
|
||||
// For perigee below 156 km, the values of S and QOMS2T are altered
|
||||
setPerigee((dsv.aodp * (1.0 - super.params.eccn) - 1.0) * earthRadius)
|
||||
setPerigee((dsv.aodp * (1.0 - super.data.eccn) - 1.0) * earthRadius)
|
||||
val pinvsq = invert(dsv.aodp * dsv.aodp * dsv.betao2 * dsv.betao2)
|
||||
dsv.sing = sin(super.params.omegao)
|
||||
dsv.cosg = cos(super.params.omegao)
|
||||
dsv.sing = sin(super.data.omegao)
|
||||
dsv.cosg = cos(super.data.omegao)
|
||||
val tsi = invert(dsv.aodp - s4)
|
||||
val eta = dsv.aodp * super.params.eccn * tsi
|
||||
val eta = dsv.aodp * super.data.eccn * tsi
|
||||
val etasq = eta * eta
|
||||
val eeta = super.params.eccn * eta
|
||||
val eeta = super.data.eccn * eta
|
||||
val psisq = abs(1.0 - etasq)
|
||||
val coef = qoms24 * tsi.pow(4.0)
|
||||
val coef1 = coef / psisq.pow(3.5)
|
||||
val c2 = coef1 * dsv.xnodp * (dsv.aodp * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq))
|
||||
+ 0.75 * ck2 * tsi / psisq * x3thm1 * (8.0 + 3.0 * etasq * (8.0 + etasq)))
|
||||
c1 = super.params.bstar * c2
|
||||
dsv.sinio = sin(super.params.xincl)
|
||||
c1 = super.data.bstar * c2
|
||||
dsv.sinio = sin(super.data.xincl)
|
||||
val a3ovk2 = -j3Harmonic / ck2
|
||||
x1mth2 = 1.0 - dsv.theta2
|
||||
c4 =
|
||||
2 * dsv.xnodp * coef1 * dsv.aodp * dsv.betao2 * (eta * (2.0 + 0.5 * etasq) + super.params.eccn
|
||||
2 * dsv.xnodp * coef1 * dsv.aodp * dsv.betao2 * (eta * (2.0 + 0.5 * etasq) + super.data.eccn
|
||||
* (0.5 + 2 * etasq) - 2 * ck2 * tsi / (dsv.aodp * psisq)
|
||||
* (-3 * x3thm1 * (1.0 - 2 * eeta + etasq * (1.5 - 0.5 * eeta)) + (0.75 * x1mth2
|
||||
* (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * super.params.omegao))))
|
||||
* (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * super.data.omegao))))
|
||||
val theta4 = dsv.theta2 * dsv.theta2
|
||||
val temp1 = 3.0 * ck2 * pinvsq * dsv.xnodp
|
||||
val temp2 = temp1 * ck2 * pinvsq
|
||||
@@ -93,18 +93,18 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
internal fun calculateSDP4(tSince: Double) {
|
||||
synchronized(this) {
|
||||
val temp = DoubleArray(12)
|
||||
val xmdf = params.xmo + dsv.xmdot * tSince
|
||||
val xmdf = data.xmo + dsv.xmdot * tSince
|
||||
val tsq = tSince * tSince
|
||||
val templ = t2cof * tsq
|
||||
dsv.xll = xmdf + dsv.xnodp * templ
|
||||
dsv.omgadf = params.omegao + dsv.omgdot * tSince
|
||||
val xnoddf = params.xnodeo + dsv.xnodot * tSince
|
||||
dsv.omgadf = data.omegao + dsv.omgdot * tSince
|
||||
val xnoddf = data.xnodeo + dsv.xnodot * tSince
|
||||
dsv.xnode = xnoddf + xnodcf * tsq
|
||||
val tempa = 1.0 - c1 * tSince
|
||||
val tempe = params.bstar * c4 * tSince
|
||||
val tempe = data.bstar * c4 * tSince
|
||||
dsv.xn = dsv.xnodp
|
||||
dsv.t = tSince
|
||||
deep.dpsec(params)
|
||||
deep.dpsec(data)
|
||||
val a = (xke / dsv.xn).pow(twoThirds) * tempa * tempa
|
||||
dsv.em = dsv.em - tempe
|
||||
deep.dpper()
|
||||
@@ -425,16 +425,16 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
private var epochRestart = false
|
||||
|
||||
init {
|
||||
thgr = thetaG(params.epoch)
|
||||
eq = params.eccn
|
||||
thgr = thetaG(data.epoch)
|
||||
eq = data.eccn
|
||||
xnq = dsv.xnodp
|
||||
aqnv = invert(dsv.aodp)
|
||||
xqncl = params.xincl
|
||||
xmao = params.xmo
|
||||
xqncl = data.xincl
|
||||
xmao = data.xmo
|
||||
xpidot = dsv.omgdot + dsv.xnodot
|
||||
sinq = sin(params.xnodeo)
|
||||
cosq = cos(params.xnodeo)
|
||||
omegaq = params.omegao
|
||||
sinq = sin(data.xnodeo)
|
||||
cosq = cos(data.xnodeo)
|
||||
omegaq = data.omegao
|
||||
// Initialize lunar solar terms, days since 1900 Jan 0.5
|
||||
day = dsv.ds50 + 18261.5
|
||||
if (abs(day - preep) > 1.0E-6) {
|
||||
@@ -534,7 +534,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
temp = 2.0 * temp1 * root54
|
||||
d5421 = temp * f542 * g521
|
||||
d5433 = temp * f543 * g533
|
||||
xlamo = xmao + params.xnodeo + params.xnodeo - thgr - thgr
|
||||
xlamo = xmao + data.xnodeo + data.xnodeo - thgr - thgr
|
||||
bfact = dsv.xmdot + dsv.xnodot + dsv.xnodot - tHdt - tHdt
|
||||
bfact += ssl + ssh + ssh
|
||||
} else {
|
||||
@@ -556,7 +556,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
fasx2 = 0.13130908
|
||||
fasx4 = 2.8843198
|
||||
fasx6 = 0.37448087
|
||||
xlamo = xmao + params.xnodeo + params.omegao - thgr
|
||||
xlamo = xmao + data.xnodeo + data.omegao - thgr
|
||||
bfact = dsv.xmdot + xpidot - tHdt
|
||||
bfact += ssl + ssg + ssh
|
||||
}
|
||||
@@ -623,7 +623,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
}
|
||||
|
||||
// Entrance for deep space secular effects
|
||||
fun dpsec(params: TLE) {
|
||||
fun dpsec(params: OrbitalData) {
|
||||
dsv.xll = dsv.xll + ssl * dsv.t
|
||||
dsv.omgadf = dsv.omgadf + ssg * dsv.t
|
||||
dsv.xnode = dsv.xnode + ssh * dsv.t
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
import kotlin.math.*
|
||||
|
||||
class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
class NearEarthSat(data: OrbitalData) : Satellite(data) {
|
||||
|
||||
private val aodp: Double
|
||||
private val aycof: Double
|
||||
@@ -53,18 +53,18 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
|
||||
init {
|
||||
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
|
||||
val a1 = (xke / super.params.xno).pow(twoThirds)
|
||||
cosio = cos(super.params.xincl)
|
||||
val a1 = (xke / super.data.xno).pow(twoThirds)
|
||||
cosio = cos(super.data.xincl)
|
||||
val theta2 = sqr(cosio)
|
||||
x3thm1 = 3.0 * theta2 - 1.0
|
||||
val eo = super.params.eccn
|
||||
val eo = super.data.eccn
|
||||
val eosq = sqr(eo)
|
||||
val betao2 = 1.0 - eosq
|
||||
val betao = sqrt(betao2)
|
||||
val del1 = 1.5 * ck2 * x3thm1 / (sqr(a1) * betao * betao2)
|
||||
val ao = a1 * (1.0 - del1 * (0.5 * twoThirds + del1 * (1.0 + 134.0 / 81.0 * del1)))
|
||||
val delo = 1.5 * ck2 * x3thm1 / (sqr(ao) * betao * betao2)
|
||||
xnodp = super.params.xno / (1.0 + delo)
|
||||
xnodp = super.data.xno / (1.0 + delo)
|
||||
aodp = ao / (1.0 - delo)
|
||||
|
||||
// For perigee less than 220 kilometers, the "simple" flag is set
|
||||
@@ -80,15 +80,15 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
val psisq = abs(1.0 - etasq)
|
||||
val coef = qoms24 * tsi.pow(4.0)
|
||||
val coef1 = coef / psisq.pow(3.5)
|
||||
val bstar = super.params.bstar
|
||||
val bstar = super.data.bstar
|
||||
val c2 = coef1 * xnodp * (aodp * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + 0.75
|
||||
* ck2 * tsi / psisq * x3thm1 * (8.0 + 3.0 * etasq * (8.0 + etasq)))
|
||||
c1 = bstar * c2
|
||||
sinio = sin(super.params.xincl)
|
||||
sinio = sin(super.data.xincl)
|
||||
val a3ovk2 = -j3Harmonic / ck2
|
||||
val c3 = coef * tsi * a3ovk2 * xnodp * sinio / eo
|
||||
x1mth2 = 1.0 - theta2
|
||||
val omegao = super.params.omegao
|
||||
val omegao = super.data.omegao
|
||||
c4 = 2 * xnodp * coef1 * aodp * betao2 * (eta * (2.0 + 0.5 * etasq) + eo * (0.5 + 2 * etasq)
|
||||
- 2 * ck2 * tsi / (aodp * psisq) * (-3 * x3thm1 * (1.0 - 2 * eeta + etasq
|
||||
* (1.5 - 0.5 * eeta)) + 0.75 * x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq))
|
||||
@@ -112,7 +112,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
t2cof = 1.5 * c1
|
||||
xlcof = 0.125 * a3ovk2 * sinio * (3.0 + 5 * cosio) / (1.0 + cosio)
|
||||
aycof = 0.25 * a3ovk2 * sinio
|
||||
val xmo = super.params.xmo
|
||||
val xmo = super.data.xmo
|
||||
delmo = (1.0 + eta * cos(xmo)).pow(3.0)
|
||||
sinmo = sin(xmo)
|
||||
x7thm1 = 7.0 * theta2 - 1
|
||||
@@ -138,14 +138,14 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
internal fun calculateSGP4(tSince: Double) {
|
||||
synchronized(this) {
|
||||
val temp = DoubleArray(9)
|
||||
val xmdf = params.xmo + xmdot * tSince
|
||||
val omgadf = params.omegao + omgdot * tSince
|
||||
val xnoddf = params.xnodeo + xnodot * tSince
|
||||
val xmdf = data.xmo + xmdot * tSince
|
||||
val omgadf = data.omegao + omgdot * tSince
|
||||
val xnoddf = data.xnodeo + xnodot * tSince
|
||||
var omega = omgadf
|
||||
var xmp = xmdf
|
||||
val tsq = sqr(tSince)
|
||||
val xnode = xnoddf + xnodcf * tsq
|
||||
val bstar = params.bstar
|
||||
val bstar = data.bstar
|
||||
var tempa = 1.0 - c1 * tSince
|
||||
var tempe = bstar * c4 * tSince
|
||||
var templ = t2cof * tsq
|
||||
@@ -162,7 +162,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
templ += t3cof * tcube + tfour * (t4cof + tSince * t5cof)
|
||||
}
|
||||
val a = aodp * tempa.pow(2.0)
|
||||
val eo = params.eccn
|
||||
val eo = data.eccn
|
||||
val e = eo - tempe
|
||||
val xl = xmp + omega + xnode + xnodp * templ
|
||||
val beta = sqrt(1.0 - e * e)
|
||||
@@ -213,7 +213,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
val rk = r * (1.0 - 1.5 * temp[2] * betal * x3thm1) + 0.5 * temp[1] * x1mth2 * cos2u
|
||||
val uk = u - 0.25 * temp[2] * x7thm1 * sin2u
|
||||
val xnodek = xnode + 1.5 * temp[2] * cosio * sin2u
|
||||
val xinck = params.xincl + 1.5 * temp[2] * cosio * sinio * cos2u
|
||||
val xinck = data.xincl + 1.5 * temp[2] * cosio * sinio * cos2u
|
||||
val rdotk = rdot - xn * temp[1] * x1mth2 * sin2u
|
||||
val rfdotk = rfdot + xn * temp[1] * (x1mth2 * cos2u + 1.5 * x3thm1)
|
||||
super.calculatePosAndVel(rk, uk, xnodek, xinck, rdotk, rfdotk)
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
data class TLE(
|
||||
data class OrbitalData(
|
||||
val name: String,
|
||||
val epoch: Double,
|
||||
val meanmo: Double,
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
@@ -44,7 +44,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun processRadios(sat: Satellite, pos: GeoPos, radios: List<Transmitter>, time: Long): List<Transmitter> {
|
||||
suspend fun processRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio> {
|
||||
return withContext(predictorDispatcher) {
|
||||
val satPos = sat.getPosition(pos, time)
|
||||
val copiedList = radios.map { it.copy() }
|
||||
@@ -106,7 +106,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
||||
var lastAosDate: Long
|
||||
var count = 0
|
||||
if (this.willBeSeen(pos)) {
|
||||
if (this.params.isDeepspace) {
|
||||
if (this.data.isDeepspace) {
|
||||
passes.add(getGeoPass(this, pos, time))
|
||||
} else {
|
||||
do {
|
||||
|
||||
@@ -29,7 +29,7 @@ data class SatPass(
|
||||
val satellite: Satellite,
|
||||
var progress: Int = 0
|
||||
) {
|
||||
val catNum: Int = satellite.params.catnum
|
||||
val name: String = satellite.params.name
|
||||
val isDeepSpace: Boolean = satellite.params.isDeepspace
|
||||
val catNum: Int = satellite.data.catnum
|
||||
val name: String = satellite.data.name
|
||||
val isDeepSpace: Boolean = satellite.data.isDeepspace
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
import kotlin.math.*
|
||||
|
||||
abstract class Satellite(val params: TLE) {
|
||||
abstract class Satellite(val data: OrbitalData) {
|
||||
|
||||
private val flatFactor = 3.35281066474748E-3
|
||||
private val deg2Rad = 1.745329251994330E-2
|
||||
@@ -30,7 +30,7 @@ abstract class Satellite(val params: TLE) {
|
||||
private val velocity = Vector4()
|
||||
private var gsPosTheta = 0.0
|
||||
private var perigee = 0.0
|
||||
val orbitalPeriod = 24 * 60 / params.meanmo
|
||||
val orbitalPeriod = 24 * 60 / data.meanmo
|
||||
val earthRadius = 6378.137
|
||||
val j3Harmonic = -2.53881E-6
|
||||
val twoPi = Math.PI * 2.0
|
||||
@@ -42,11 +42,11 @@ abstract class Satellite(val params: TLE) {
|
||||
var s4 = 0.0
|
||||
|
||||
internal fun willBeSeen(pos: GeoPos): Boolean {
|
||||
return if (params.meanmo < 1e-8) false
|
||||
return if (data.meanmo < 1e-8) false
|
||||
else {
|
||||
val sma = 331.25 * exp(ln(1440.0 / params.meanmo) * (2.0 / 3.0))
|
||||
val apogee = sma * (1.0 + params.eccn) - earthRadius
|
||||
var lin = params.incl
|
||||
val sma = 331.25 * exp(ln(1440.0 / data.meanmo) * (2.0 / 3.0))
|
||||
val apogee = sma * (1.0 + data.eccn) - earthRadius
|
||||
var lin = data.incl
|
||||
if (lin >= 90.0) lin = 180.0 - lin
|
||||
acos(earthRadius / (apogee + earthRadius)) + lin * deg2Rad > abs(pos.latitude * deg2Rad)
|
||||
}
|
||||
@@ -57,7 +57,7 @@ abstract class Satellite(val params: TLE) {
|
||||
// Date/time at which the position and velocity were calculated
|
||||
val julUTC = calcCurrentDaynum(time) + 2444238.5
|
||||
// Convert satellite's epoch time to Julian and calculate time since epoch in minutes
|
||||
val julEpoch = juliandDateOfEpoch(params.epoch)
|
||||
val julEpoch = juliandDateOfEpoch(data.epoch)
|
||||
val tsince = (julUTC - julEpoch) * minPerDay
|
||||
calculateSDP4orSGP4(tsince)
|
||||
// Scale position and velocity vectors to km and km/sec
|
||||
@@ -97,7 +97,7 @@ abstract class Satellite(val params: TLE) {
|
||||
}
|
||||
|
||||
private fun calculateSDP4orSGP4(tsince: Double) {
|
||||
if (params.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
||||
if (data.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
||||
else (this as NearEarthSat).calculateSGP4(tsince)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user