Slightly changed classes structure for readability

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:45 +00:00
1 parent 19d0c1b108
commit 549a915b24
40 files changed
+309 -381

No files matched your search

@@ -1,22 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 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
import com.rtbishop.look4sat.domain.predict.OrbitalData
data class SatEntry(val data: OrbitalData, var comment: String? = null)
@@ -17,4 +17,4 @@
*/
package com.rtbishop.look4sat.domain.model
data class SatItem(val catnum: Int, val name: String, val isSelected: Boolean)
data class SatItem(val catnum: Int, val name: String, val isSelected: Boolean = false)
@@ -28,6 +28,5 @@ data class SatRadio(
var uplinkHigh: Long?,
val uplinkMode: String?,
val isInverted: Boolean,
val catnum: Int?,
var comment: String? = null
val catnum: Int?
)
@@ -0,0 +1,20 @@
package com.rtbishop.look4sat.domain.predict
const val ASTRONOMICAL_UNIT = 1.49597870691E8
const val DEG2RAD = 0.017453292519943295
const val RAD2DEG = 57.29577951308232
const val EARTH_RADIUS = 6378.137
const val EPSILON = 1.0E-12
const val FLAT_FACT = 3.35281066474748E-3
const val J3_HARMONIC = -2.53881E-6
const val MIN_PER_DAY = 1.44E3
const val SEC_PER_DAY = 8.6400E4
const val SOLAR_RADIUS = 6.96000E5
const val SPEED_OF_LIGHT = 2.99792458E8
const val PI = 3.141592653589793
const val PI_2 = PI / 2.0
const val TWO_PI = PI * 2.0
const val TWO_THIRDS = 2.0 / 3.0
const val CK2 = 5.413079E-4
const val CK4 = 6.209887E-7
const val XKE = 7.43669161E-2
@@ -25,7 +25,7 @@ import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
class DeepSpaceSatellite(data: OrbitalData) : Satellite(data) {
class DeepSpaceObject(data: OrbitalData) : OrbitalObject(data) {
private val c1: Double
private val c4: Double
@@ -24,7 +24,7 @@ import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
class NearEarthSatellite(data: OrbitalData) : Satellite(data) {
class NearEarthObject(data: OrbitalData) : OrbitalObject(data) {
private val aodp: Double
private val aycof: Double
@@ -27,15 +27,14 @@ data class OrbitalData(
val argper: Double,
val meanan: Double,
val catnum: Int,
val bstar: Double,
val xincl: Double = incl * DEG2RAD,
val xnodeo: Double = raan * DEG2RAD,
val omegao: Double = argper * DEG2RAD,
val xmo: Double = meanan * DEG2RAD,
val xno: Double = meanmo * TWO_PI / MIN_PER_DAY,
val orbitalPeriod: Double = MIN_PER_DAY / meanmo,
// Space objects are classified as NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
val isDeepSpace: Boolean = orbitalPeriod >= 225.0
val bstar: Double
) {
fun getSatellite(): Satellite = if (isDeepSpace) DeepSpaceSatellite(this) else NearEarthSatellite(this)
val xincl: Double = incl * DEG2RAD
val xnodeo: Double = raan * DEG2RAD
val omegao: Double = argper * DEG2RAD
val xmo: Double = meanan * DEG2RAD
val xno: Double = meanmo * TWO_PI / MIN_PER_DAY
val orbitalPeriod: Double = MIN_PER_DAY / meanmo
val isDeepSpace: Boolean = orbitalPeriod >= 225.0 // NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
fun getObject(): OrbitalObject = if (isDeepSpace) DeepSpaceObject(this) else NearEarthObject(this)
}
@@ -30,30 +30,11 @@ import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
const val ASTRONOMICAL_UNIT = 1.49597870691E8
const val DEG2RAD = 0.017453292519943295
const val RAD2DEG = 57.29577951308232
const val EARTH_RADIUS = 6378.137
const val EPSILON = 1.0E-12
const val FLAT_FACT = 3.35281066474748E-3
const val J3_HARMONIC = -2.53881E-6
const val MIN_PER_DAY = 1.44E3
const val SEC_PER_DAY = 8.6400E4
const val SOLAR_RADIUS = 6.96000E5
const val SPEED_OF_LIGHT = 2.99792458E8
const val PI = 3.141592653589793
const val PI_2 = PI / 2.0
const val TWO_PI = PI * 2.0
const val TWO_THIRDS = 2.0 / 3.0
const val CK2 = 5.413079E-4
const val CK4 = 6.209887E-7
const val XKE = 7.43669161E-2
abstract class Satellite(val data: OrbitalData) {
abstract class OrbitalObject(val data: OrbitalData) {
private val position = Vector4()
private val velocity = Vector4()
private var satPos = SatPos()
private var orbitalPos = OrbitalPos()
private var eclipseDepth = 0.0
private var gsPosTheta = 0.0
private var julUTC = 0.0
@@ -72,8 +53,8 @@ abstract class Satellite(val data: OrbitalData) {
}
}
fun getPosition(pos: GeoPos, time: Long): SatPos {
satPos = SatPos()
fun getPosition(pos: GeoPos, time: Long): OrbitalPos {
orbitalPos = OrbitalPos()
// Date/time at which the position and velocity were calculated
julUTC = calcCurrentDaynum(time) + 2444238.5
// Convert satellite's epoch time to Julian and calculate time since epoch in minutes
@@ -88,10 +69,10 @@ abstract class Satellite(val data: OrbitalData) {
// Angles in rads, dist in km, vel in km/S. Calculate sat Az, El, Range and Range-rate.
calculateObs(julUTC, position, velocity, pos, squintVector)
calculateLatLonAlt(julUTC)
satPos.time = time
satPos.eclipsed = isEclipsed()
satPos.eclipseDepth = eclipseDepth
return satPos
orbitalPos.time = time
orbitalPos.eclipsed = isEclipsed()
orbitalPos.eclipseDepth = eclipseDepth
return orbitalPos
}
private fun calcCurrentDaynum(now: Long): Double {
@@ -118,8 +99,8 @@ abstract class Satellite(val data: OrbitalData) {
}
private fun calculateSDP4orSGP4(tsince: Double) {
if (data.isDeepSpace) (this as DeepSpaceSatellite).calculateSDP4(tsince)
else (this as NearEarthSatellite).calculateSGP4(tsince)
if (data.isDeepSpace) (this as DeepSpaceObject).calculateSDP4(tsince)
else (this as NearEarthObject).calculateSGP4(tsince)
}
// Converts the sat position and velocity vectors to km and km/sec
@@ -164,13 +145,13 @@ abstract class Satellite(val data: OrbitalData) {
var azim = atan(-topE / topS)
if (topS > 0.0) azim += PI
if (azim < 0.0) azim += TWO_PI
satPos.azimuth = azim
satPos.elevation = asin(topZ / range.w)
satPos.distance = range.w
satPos.distanceRate = dot(range, rgvel) / range.w
var elevation = satPos.elevation / TWO_PI * 360.0
orbitalPos.azimuth = azim
orbitalPos.elevation = asin(topZ / range.w)
orbitalPos.distance = range.w
orbitalPos.distanceRate = dot(range, rgvel) / range.w
var elevation = orbitalPos.elevation / TWO_PI * 360.0
if (elevation > 90) elevation = 180 - elevation
satPos.aboveHorizon = elevation - 0 > EPSILON
orbitalPos.aboveHorizon = elevation - 0 > EPSILON
}
// Returns the ECI position and velocity of the observer
@@ -196,26 +177,26 @@ abstract class Satellite(val data: OrbitalData) {
// Calculate the geodetic position of an object given its ECI pos and time
private fun calculateLatLonAlt(time: Double) {
satPos.theta = atan2(position.y, position.x)
satPos.longitude = mod2PI(satPos.theta - thetaGJD(time))
orbitalPos.theta = atan2(position.y, position.x)
orbitalPos.longitude = mod2PI(orbitalPos.theta - thetaGJD(time))
val r = sqrt(sqr(position.x) + sqr(position.y))
val e2 = FLAT_FACT * (2.0 - FLAT_FACT)
satPos.latitude = atan2(position.z, r)
orbitalPos.latitude = atan2(position.z, r)
var phi: Double
var c: Double
var i = 0
var converged: Boolean
do {
phi = satPos.latitude
phi = orbitalPos.latitude
c = invert(sqrt(1.0 - e2 * sqr(sin(phi))))
satPos.latitude = atan2(position.z + EARTH_RADIUS * c * e2 * sin(phi), r)
converged = abs(satPos.latitude - phi) < EPSILON
orbitalPos.latitude = atan2(position.z + EARTH_RADIUS * c * e2 * sin(phi), r)
converged = abs(orbitalPos.latitude - phi) < EPSILON
} while (i++ < 10 && !converged)
satPos.altitude = r / cos(satPos.latitude) - EARTH_RADIUS * c
var temp = satPos.latitude
orbitalPos.altitude = r / cos(orbitalPos.latitude) - EARTH_RADIUS * c
var temp = orbitalPos.latitude
if (temp > PI_2) {
temp -= TWO_PI
satPos.latitude = temp
orbitalPos.latitude = temp
}
}
@@ -302,7 +283,7 @@ abstract class Satellite(val data: OrbitalData) {
internal fun calculatePhase(xlt: Double, xnode: Double, omgadf: Double) {
var phaseValue = xlt - xnode - omgadf + TWO_PI
if (phaseValue < 0.0) phaseValue += TWO_PI
satPos.phase = mod2PI(phaseValue)
orbitalPos.phase = mod2PI(phaseValue)
}
// Sets perigee and checks and adjusts the calculation if the perigee is less tan 156KM
@@ -17,17 +17,17 @@
*/
package com.rtbishop.look4sat.domain.predict
data class SatPass(
data class OrbitalPass(
val aosTime: Long = 0L,
val aosAzimuth: Double = 90.0,
val losTime: Long = 0L,
val losAzimuth: Double = 270.0,
val altitude: Int = 1000,
val maxElevation: Double = 75.0,
val satellite: Satellite,
val orbitalObject: OrbitalObject,
var progress: Float = 0.0f
) {
val catNum: Int = satellite.data.catnum
val name: String = satellite.data.name
val isDeepSpace: Boolean = satellite.data.isDeepSpace
val catNum: Int = orbitalObject.data.catnum
val name: String = orbitalObject.data.name
val isDeepSpace: Boolean = orbitalObject.data.isDeepSpace
}
@@ -25,7 +25,7 @@ import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
data class SatPos(
data class OrbitalPos(
var azimuth: Double = 0.0,
var elevation: Double = 0.0,
var latitude: Double = 0.0,
@@ -2,19 +2,19 @@ package com.rtbishop.look4sat.domain.repository
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.domain.predict.OrbitalObject
import com.rtbishop.look4sat.domain.predict.OrbitalPass
import com.rtbishop.look4sat.domain.predict.OrbitalPos
import kotlinx.coroutines.flow.StateFlow
interface ISatelliteRepo {
val passes: StateFlow<List<SatPass>>
val satellites: StateFlow<List<Satellite>>
val passes: StateFlow<List<OrbitalPass>>
val satellites: StateFlow<List<OrbitalObject>>
suspend fun getRadiosWithId(id: Int): List<SatRadio>
suspend fun initRepository()
suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos
suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos>
suspend fun getRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio>
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass>
suspend fun getPosition(sat: OrbitalObject, pos: GeoPos, time: Long): OrbitalPos
suspend fun getTrack(sat: OrbitalObject, pos: GeoPos, start: Long, end: Long): List<OrbitalPos>
suspend fun getRadios(sat: OrbitalObject, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio>
suspend fun processPasses(passList: List<OrbitalPass>, time: Long): List<OrbitalPass>
suspend fun calculatePasses(time: Long, hoursAhead: Int, minElevation: Double, modes: List<String>)
}
@@ -1,8 +1,8 @@
package com.rtbishop.look4sat.domain.source
package com.rtbishop.look4sat.domain.repository
import kotlinx.coroutines.flow.StateFlow
interface ISensorSource {
interface ISensorsRepo {
val orientation: StateFlow<Pair<Float, Float>>
fun enableSensor()
fun disableSensor()
@@ -25,37 +25,6 @@ import kotlinx.coroutines.flow.StateFlow
interface ISettingsRepo {
val radioSourceUrl: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
val satelliteSourcesMap: Map<String, String>
get() = mapOf(
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
"Amsat" to "https://amsat.org/tle/current/nasabare.txt",
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
"Classified" to "https://www.mmccants.org/tles/classfd.zip",
"Cubesat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=csv",
"Education" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=education&FORMAT=csv",
"Engineer" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=engineering&FORMAT=csv",
"Geostationary" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=geo&FORMAT=csv",
"Globalstar" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=csv",
"GNSS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=gnss&FORMAT=csv",
"Intelsat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=csv",
"Iridium" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=csv",
"McCants" to "https://www.mmccants.org/tles/inttles.zip",
"Military" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=military&FORMAT=csv",
"New" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=csv",
"OneWeb" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=oneweb&FORMAT=csv",
"Orbcomm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=csv",
"R4UAB" to "https://r4uab.ru/satonline.txt",
"Resource" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=resource&FORMAT=csv",
"SatNOGS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=satnogs&FORMAT=csv",
"Science" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=science&FORMAT=csv",
"Spire" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=spire&FORMAT=csv",
"Starlink" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=csv",
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
)
//region # Satellites selection settings
val selectedIds: StateFlow<List<Int>>
fun setSelectedIds(ids: List<Int>)
@@ -1,24 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 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.source
import java.io.InputStream
interface IFileSource {
suspend fun getFileStream(uri: String): InputStream?
}
@@ -17,16 +17,16 @@
*/
package com.rtbishop.look4sat.domain.source
import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.domain.predict.OrbitalData
import com.rtbishop.look4sat.domain.predict.OrbitalObject
interface ILocalSource {
suspend fun getEntriesTotal(): Int
suspend fun getEntriesList(): List<SatItem>
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
suspend fun insertEntries(entries: List<SatEntry>)
suspend fun getEntriesWithIds(ids: List<Int>): List<OrbitalObject>
suspend fun insertEntries(entries: List<OrbitalData>)
suspend fun deleteEntries()
suspend fun getIdsWithModes(modes: List<String>): List<Int>
suspend fun getRadiosTotal(): Int
@@ -20,5 +20,6 @@ package com.rtbishop.look4sat.domain.source
import java.io.InputStream
interface IRemoteSource {
suspend fun getRemoteStream(url: String): InputStream?
suspend fun getFileStream(uri: String): InputStream?
suspend fun getNetworkStream(url: String): InputStream?
}
@@ -0,0 +1,33 @@
package com.rtbishop.look4sat.domain.source
object Sources {
const val radioDataUrl = "https://db.satnogs.org/api/transmitters/?format=json"
val satelliteDataUrls = mapOf(
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
"Amsat" to "https://amsat.org/tle/current/nasabare.txt",
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
"Classified" to "https://www.mmccants.org/tles/classfd.zip",
"Cubesat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=csv",
"Education" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=education&FORMAT=csv",
"Engineer" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=engineering&FORMAT=csv",
"Geostationary" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=geo&FORMAT=csv",
"Globalstar" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=csv",
"GNSS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=gnss&FORMAT=csv",
"Intelsat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=csv",
"Iridium" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=csv",
"McCants" to "https://www.mmccants.org/tles/inttles.zip",
"Military" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=military&FORMAT=csv",
"New" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=csv",
"OneWeb" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=oneweb&FORMAT=csv",
"Orbcomm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=csv",
"R4UAB" to "https://r4uab.ru/satonline.txt",
"Resource" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=resource&FORMAT=csv",
"SatNOGS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=satnogs&FORMAT=csv",
"Science" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=science&FORMAT=csv",
"Spire" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=spire&FORMAT=csv",
"Starlink" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=csv",
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
)
}