Destructively migrated to the new database

This commit is contained in:
Arty Bishop committed 2022-02-19 12:10:30 +00:00
1 parent 92eec290b1
commit 64b5010a44
53 files changed
+381 -901

No files matched your search

+1 -1
View File
@@ -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
)
@@ -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)
@@ -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)
}