Renamed and refactored several predict4kotlin classes

This commit is contained in:
Arty Bishop committed 2021-09-12 12:21:23 +01:00
1 parent c38fe8b10f
commit 9a4de03cac
27 files changed
+315 -232

No files matched your search

@@ -23,7 +23,7 @@ import android.location.LocationManager
import androidx.core.content.edit
import com.rtbishop.look4sat.data.PreferencesSource
import com.rtbishop.look4sat.domain.predict4kotlin.QthConverter
import com.rtbishop.look4sat.domain.predict4kotlin.StationPosition
import com.rtbishop.look4sat.domain.predict4kotlin.StationPos
import com.rtbishop.look4sat.utility.round
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
@@ -89,19 +89,19 @@ class PreferencesProvider @Inject constructor(
return qthConverter.positionToQTH(lat, lon)
}
override fun loadStationPosition(): StationPosition {
override fun loadStationPosition(): StationPos {
val defaultSP = "0.0"
val latitude = preferences.getString(keyLatitude, null) ?: defaultSP
val longitude = preferences.getString(keyLongitude, null) ?: defaultSP
val altitude = preferences.getString(keyAltitude, null) ?: defaultSP
return StationPosition(latitude.toDouble(), longitude.toDouble(), altitude.toDouble())
return StationPos(latitude.toDouble(), longitude.toDouble(), altitude.toDouble())
}
override fun saveStationPosition(position: StationPosition) {
override fun saveStationPosition(pos: StationPos) {
preferences.edit {
putString(keyLatitude, position.latitude.toString())
putString(keyLongitude, position.longitude.toString())
putString(keyAltitude, position.altitude.toString())
putString(keyLatitude, pos.latitude.toString())
putString(keyLongitude, pos.longitude.toString())
putString(keyAltitude, pos.altitude.toString())
}
}
@@ -113,7 +113,7 @@ class PreferencesProvider @Inject constructor(
val latitude = location.latitude.round(4)
val longitude = location.longitude.round(4)
val altitude = location.altitude.round(1)
val stationPosition = StationPosition(latitude, longitude, altitude)
val stationPosition = StationPos(latitude, longitude, altitude)
saveStationPosition(stationPosition)
return true
}
@@ -124,7 +124,7 @@ class PreferencesProvider @Inject constructor(
override fun updatePositionFromQTH(qthString: String): Boolean {
val position = qthConverter.qthToPosition(qthString) ?: return false
val stationPosition = StationPosition(position.latitude, position.longitude, 0.0)
val stationPosition = StationPos(position.latitude, position.longitude, 0.0)
saveStationPosition(stationPosition)
return true
}
@@ -20,7 +20,6 @@ package com.rtbishop.look4sat.framework.db
import androidx.room.TypeConverter
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.TLE
import com.rtbishop.look4sat.domain.predict4kotlin.createSat
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
@@ -17,7 +17,7 @@
*/
package com.rtbishop.look4sat.framework.model
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.GeoPos
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
data class SatData(
@@ -28,5 +28,5 @@ data class SatData(
val altitude: Double,
val velocity: Double,
val qthLoc: String,
val osmPos: Position
)
val osmPos: GeoPos
)
@@ -29,7 +29,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentMapBinding
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.GeoPos
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.framework.model.SatData
import dagger.hilt.android.AndroidEntryPoint
@@ -96,7 +96,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
viewModel.satData.observe(viewLifecycleOwner, { renderSatData(it, binding) })
}
private fun renderStationPos(stationPos: Position, binding: FragmentMapBinding) {
private fun renderStationPos(stationPos: GeoPos, binding: FragmentMapBinding) {
binding.apply {
Marker(mapView).apply {
setInfoWindow(null)
@@ -109,7 +109,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
}
}
private fun renderSatPositions(posMap: Map<Satellite, Position>, binding: FragmentMapBinding) {
private fun renderSatPositions(posMap: Map<Satellite, GeoPos>, binding: FragmentMapBinding) {
binding.apply {
val markers = FolderOverlay()
posMap.entries.forEach {
@@ -120,7 +120,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
textLabelBackgroundColor = Color.TRANSPARENT
textLabelForegroundColor =
ContextCompat.getColor(requireContext(), R.color.themeLight)
setTextIcon(it.key.tle.name)
setTextIcon(it.key.params.name)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
try {
position = GeoPoint(it.value.latitude, it.value.longitude)
@@ -156,7 +156,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
}
}
private fun renderSatTrack(satTrack: List<List<Position>>, binding: FragmentMapBinding) {
private fun renderSatTrack(satTrack: List<List<GeoPos>>, binding: FragmentMapBinding) {
val trackOverlay = FolderOverlay()
satTrack.forEach { track ->
val trackPoints = track.map { GeoPoint(it.latitude, it.longitude) }
@@ -169,7 +169,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
binding.mapView.overlays[1] = trackOverlay
}
private fun renderSatFootprint(satFootprint: List<Position>, binding: FragmentMapBinding) {
private fun renderSatFootprint(satFootprint: List<GeoPos>, binding: FragmentMapBinding) {
val footprintPoints = satFootprint.map { GeoPoint(it.latitude, it.longitude) }
val footprintOverlay = Polygon().apply {
fillPaint.set(footprintPaint)
@@ -20,10 +20,9 @@ package com.rtbishop.look4sat.presentation.satMapScreen
import androidx.lifecycle.*
import com.rtbishop.look4sat.data.PreferencesSource
import com.rtbishop.look4sat.data.SatDataRepository
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.GeoPos
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.StationPosition
import com.rtbishop.look4sat.domain.predict4kotlin.getRangeCircle
import com.rtbishop.look4sat.domain.predict4kotlin.StationPos
import com.rtbishop.look4sat.framework.model.SatData
import com.rtbishop.look4sat.injection.DefaultDispatcher
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -49,20 +48,20 @@ class SatMapViewModel @Inject constructor(
val stationPos = liveData {
val osmLat = clipLat(gsp.latitude)
val osmLon = clipLon(gsp.longitude)
emit(Position(osmLat, osmLon))
emit(GeoPos(osmLat, osmLon))
}
private val _satTrack = MutableLiveData<List<List<Position>>>()
val satTrack: LiveData<List<List<Position>>> = _satTrack
private val _satTrack = MutableLiveData<List<List<GeoPos>>>()
val satTrack: LiveData<List<List<GeoPos>>> = _satTrack
private val _satFootprint = MutableLiveData<List<Position>>()
val satFootprint: LiveData<List<Position>> = _satFootprint
private val _satFootprint = MutableLiveData<List<GeoPos>>()
val satFootprint: LiveData<List<GeoPos>> = _satFootprint
private val _satData = MutableLiveData<SatData>()
val satData: LiveData<SatData> = this._satData
private val _satPositions = MutableLiveData<Map<Satellite, Position>>()
val satPositions: LiveData<Map<Satellite, Position>> = _satPositions
private val _satPositions = MutableLiveData<Map<Satellite, GeoPos>>()
val satPositions: LiveData<Map<Satellite, GeoPos>> = _satPositions
init {
viewModelScope.launch {
@@ -110,38 +109,38 @@ class SatMapViewModel @Inject constructor(
}
}
private suspend fun setSatPositions(list: List<Satellite>, gsp: StationPosition, date: Date) {
private suspend fun setSatPositions(list: List<Satellite>, gsp: StationPos, date: Date) {
withContext(defaultDispatcher) {
val satPositions = mutableMapOf<Satellite, Position>()
val satPositions = mutableMapOf<Satellite, GeoPos>()
list.forEach { satellite ->
val satPos = satellite.getPredictor(gsp).getSatPos(date)
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
satPositions[satellite] = Position(osmLat, osmLon)
satPositions[satellite] = GeoPos(osmLat, osmLon)
}
_satPositions.postValue(satPositions)
}
}
private suspend fun setSelectedSatTrack(sat: Satellite, gsp: StationPosition, date: Date) {
private suspend fun setSelectedSatTrack(sat: Satellite, gsp: StationPos, date: Date) {
withContext(defaultDispatcher) {
val satTracks = mutableListOf<List<Position>>()
val currentTrack = mutableListOf<Position>()
val satTracks = mutableListOf<List<GeoPos>>()
val currentTrack = mutableListOf<GeoPos>()
var oldLongitude = 0.0
sat.getPredictor(gsp).getPositions(date, 15, 0, 2.4).forEach { satPos ->
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
val currentPosition = Position(osmLat, osmLon)
val currentPosition = GeoPos(osmLat, osmLon)
if (oldLongitude < -170.0 && currentPosition.longitude > 170.0) {
// adding left terminal position
currentTrack.add(Position(osmLat, -180.0))
val finishedTrack = mutableListOf<Position>().apply { addAll(currentTrack) }
currentTrack.add(GeoPos(osmLat, -180.0))
val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) }
satTracks.add(finishedTrack)
currentTrack.clear()
} else if (oldLongitude > 170.0 && currentPosition.longitude < -170.0) {
// adding right terminal position
currentTrack.add(Position(osmLat, 180.0))
val finishedTrack = mutableListOf<Position>().apply { addAll(currentTrack) }
currentTrack.add(GeoPos(osmLat, 180.0))
val finishedTrack = mutableListOf<GeoPos>().apply { addAll(currentTrack) }
satTracks.add(finishedTrack)
currentTrack.clear()
}
@@ -153,28 +152,28 @@ class SatMapViewModel @Inject constructor(
}
}
private suspend fun setSelectedSatFootprint(sat: Satellite, gsp: StationPosition, date: Date) {
private suspend fun setSelectedSatFootprint(sat: Satellite, gsp: StationPos, date: Date) {
withContext(defaultDispatcher) {
val satFootprint = sat.getPosition(gsp, date).getRangeCircle().map { rangePos ->
val osmLat = clipLat(rangePos.latitude)
val osmLon = clipLon(rangePos.longitude)
Position(osmLat, osmLon)
GeoPos(osmLat, osmLon)
}
_satFootprint.postValue(satFootprint)
}
}
private suspend fun setSelectedSatData(sat: Satellite, gsp: StationPosition, date: Date) {
private suspend fun setSelectedSatData(sat: Satellite, gsp: StationPos, date: Date) {
withContext(defaultDispatcher) {
val satPos = sat.getPredictor(gsp).getSatPos(date)
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
val osmPos = Position(osmLat, osmLon)
val osmPos = GeoPos(osmLat, osmLon)
val qthLoc =
preferencesSource.positionToQTH(osmPos.latitude, osmPos.longitude) ?: "-- --"
val velocity = getOrbitalVelocity(satPos.altitude)
val satData = SatData(
sat, sat.tle.catnum, sat.tle.name, satPos.range,
sat, sat.params.catnum, sat.params.name, satPos.range,
satPos.altitude, velocity, qthLoc, osmPos
)
this@SatMapViewModel._satData.postValue(satData)
@@ -119,13 +119,13 @@ class PassInfoFragment : Fragment(R.layout.fragment_polar) {
binding.satName.text = satPass.name
if (!satPass.isDeepSpace) {
if (dateNow.before(satPass.aosDate)) {
val millisBeforeStart = satPass.aosDate.time.minus(timeNow)
if (dateNow.time < satPass.aosTime) {
val millisBeforeStart = satPass.aosTime.minus(timeNow)
binding.polarTimer.text = millisBeforeStart.toTimerString()
} else {
val millisBeforeEnd = satPass.losDate.time.minus(timeNow)
val millisBeforeEnd = satPass.losTime.minus(timeNow)
binding.polarTimer.text = millisBeforeEnd.toTimerString()
if (dateNow.after(satPass.losDate)) {
if (dateNow.time > satPass.losTime) {
binding.polarTimer.text = 0L.toTimerString()
findNavController().navigateSafe(R.id.action_polar_to_passes)
}
@@ -200,17 +200,17 @@ class PassInfoView(context: Context) : View(context) {
}
private fun createPassTrajectory(satPass: SatPass) {
val currentTime = satPass.aosDate
while (currentTime.before(satPass.losDate)) {
val satPos = satPass.predictor.getSatPos(currentTime)
var currentTime = satPass.aosTime
while (currentTime < satPass.losTime) {
val satPos = satPass.predictor.getSatPos(Date(currentTime))
val passX = sph2CartX(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
val passY = sph2CartY(satPos.azimuth, satPos.elevation, radarRadius.toDouble())
if (currentTime.compareTo(satPass.aosDate) == 0) {
if (currentTime == satPass.aosTime) {
trackPath.moveTo(passX, -passY)
} else {
trackPath.lineTo(passX, -passY)
}
currentTime.time += 15000
currentTime += 15000
}
}
@@ -50,7 +50,7 @@ class PassInfoViewModel @Inject constructor(
fun getPass(catNum: Int, aosTime: Long) = liveData {
satPassRepository.passes.collect { passes ->
val pass = passes.find { it.catNum == catNum && it.aosDate.time == aosTime }
val pass = passes.find { it.catNum == catNum && it.aosTime == aosTime }
pass?.let { satPass ->
processTransmitters(satPass)
initRotatorControl(satPass)
@@ -34,7 +34,7 @@ class SatPassAdapter(private val isUTC: Boolean, private val clickListener: Pass
private val diffCallback = object : DiffUtil.ItemCallback<SatPass>() {
override fun areItemsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
return oldItem.catNum == newItem.catNum && oldItem.aosDate == newItem.aosDate
return oldItem.catNum == newItem.catNum && oldItem.aosTime == newItem.aosTime
}
override fun areContentsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
@@ -99,9 +99,9 @@ class SatPassAdapter(private val isUTC: Boolean, private val clickListener: Pass
passLeoAosAz.text = String.format(aosAzFormat, satPass.aosAzimuth)
passLeoMaxEl.text = String.format(maxElFormat, satPass.maxElevation)
passLeoLosAz.text = String.format(losAzFormat, satPass.losAzimuth)
passLeoStart.text = startFormat.format(satPass.aosDate)
passLeoStart.text = startFormat.format(Date(satPass.aosTime))
passLeoTcaAz.text = String.format(tcaAzFormat, satPass.tcaAzimuth)
passLeoEnd.text = endFormat.format(satPass.losDate)
passLeoEnd.text = endFormat.format(Date(satPass.losTime))
passLeoProgress.progress = satPass.progress
}
@@ -110,12 +110,12 @@ class SatPassFragment : Fragment(R.layout.fragment_passes), SatPassAdapter.Passe
if (passes.isNotEmpty()) {
val timeNow = System.currentTimeMillis()
try {
val nextPass = passes.first { it.aosDate.time.minus(timeNow) > 0 }
val millisBeforeStart = nextPass.aosDate.time.minus(timeNow)
val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 }
val millisBeforeStart = nextPass.aosTime.minus(timeNow)
binding.passesTimer.text = millisBeforeStart.toTimerString()
} catch (e: NoSuchElementException) {
val lastPass = passes.last()
val millisBeforeEnd = lastPass.losDate.time.minus(timeNow)
val millisBeforeEnd = lastPass.losTime.minus(timeNow)
binding.passesTimer.text = millisBeforeEnd.toTimerString()
}
} else {
@@ -125,7 +125,7 @@ class SatPassFragment : Fragment(R.layout.fragment_passes), SatPassAdapter.Passe
override fun navigateToPass(satPass: SatPass) {
if (satPass.progress < 100) {
val bundle = bundleOf("catNum" to satPass.catNum, "aosTime" to satPass.aosDate.time)
val bundle = bundleOf("catNum" to satPass.catNum, "aosTime" to satPass.aosTime)
findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
}
}
@@ -92,10 +92,10 @@ class SatPassViewModel @Inject constructor(
val timeNow = System.currentTimeMillis()
currentPasses.forEach { pass ->
if (!pass.isDeepSpace) {
val timeStart = pass.aosDate.time
val timeStart = pass.aosTime
if (timeNow > timeStart) {
val deltaNow = timeNow.minus(timeStart).toFloat()
val deltaTotal = pass.losDate.time.minus(timeStart).toFloat()
val deltaTotal = pass.losTime.minus(timeStart).toFloat()
pass.progress = ((deltaNow / deltaTotal) * 100).toInt()
}
}
@@ -17,15 +17,15 @@
*/
package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.predict4kotlin.StationPosition
import com.rtbishop.look4sat.domain.predict4kotlin.StationPos
interface PreferencesSource {
fun positionToQTH(lat: Double, lon: Double): String?
fun loadStationPosition(): StationPosition
fun loadStationPosition(): StationPos
fun saveStationPosition(position: StationPosition)
fun saveStationPosition(pos: StationPos)
fun updatePositionFromGPS(): Boolean
@@ -91,6 +91,6 @@ class SatDataRepository(
}
private fun importSatEntries(stream: InputStream): List<SatEntry> {
return TLE.importElements(stream).map { tle -> SatEntry(tle) }
return TLE.parseStream(stream).map { tle -> SatEntry(tle) }
}
}
@@ -37,8 +37,8 @@ class SatPassRepository(
if (satellites.isEmpty()) {
_passes.emit(emptyList())
} else {
val oldCatNums = selectedSatellites.map { it.tle.catnum }
val newCatNums = satellites.map { it.tle.catnum }
val oldCatNums = selectedSatellites.map { it.params.catnum }
val newCatNums = satellites.map { it.params.catnum }
if (oldCatNums != newCatNums) forceCalculation(satellites, refDate)
}
}
@@ -62,10 +62,10 @@ class SatPassRepository(
}
private fun filterPasses(passes: List<SatPass>, refDate: Date): List<SatPass> {
val timeFuture = Date(refDate.time + (preferencesSource.getHoursAhead() * 3600 * 1000))
return passes.filter { it.losDate.after(refDate) }
.filter { it.aosDate.before(timeFuture) }
val timeFuture = refDate.time + (preferencesSource.getHoursAhead() * 3600 * 1000)
return passes.filter { it.losTime > refDate.time }
.filter { it.aosTime < timeFuture }
.filter { it.maxElevation > preferencesSource.getMinElevation() }
.sortedBy { it.aosDate }
.sortedBy { it.aosTime }
}
}
@@ -0,0 +1,86 @@
package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.predict4kotlin.*
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.withContext
import java.util.*
class SatPredictor(
private val preferencesSource: PreferencesSource,
private val defaultDispatcher: CoroutineDispatcher
) {
// Multi Sat passes
private val _passes = MutableSharedFlow<List<SatPass>>(replay = 1)
private var selectedSatellites = emptyList<Satellite>()
val passes: SharedFlow<List<SatPass>> = _passes
suspend fun triggerCalculation(satellites: List<Satellite>, refDate: Date = Date()) {
if (satellites.isEmpty()) {
_passes.emit(emptyList())
} else {
val oldCatNums = selectedSatellites.map { it.params.catnum }
val newCatNums = satellites.map { it.params.catnum }
if (oldCatNums != newCatNums) forceCalculation(satellites, refDate)
}
}
suspend fun forceCalculation(satellites: List<Satellite>, refDate: Date = Date()) {
if (satellites.isEmpty()) {
_passes.emit(emptyList())
} else {
withContext(defaultDispatcher) {
val allPasses = mutableListOf<SatPass>()
selectedSatellites = satellites
satellites.forEach { satellite -> allPasses.addAll(getPasses(satellite, refDate)) }
_passes.emit(filterPasses(allPasses, refDate))
}
}
}
private fun getPasses(satellite: Satellite, refDate: Date): List<SatPass> {
val predictor = satellite.getPredictor(preferencesSource.loadStationPosition())
return predictor.getPasses(refDate, preferencesSource.getHoursAhead(), true)
}
private fun filterPasses(passes: List<SatPass>, refDate: Date): List<SatPass> {
val timeFuture = refDate.time + (preferencesSource.getHoursAhead() * 3600 * 1000)
return passes.filter { it.losTime > refDate.time }
.filter { it.aosTime < timeFuture }
.filter { it.maxElevation > preferencesSource.getMinElevation() }
.sortedBy { it.aosTime }
}
// Single Sat radar
// Constant calculation of beacon Az/El point
private val _beaconPoint = MutableSharedFlow<SkyPos>(replay = 1)
val beaconPoint: SharedFlow<SkyPos> = _beaconPoint
suspend fun calculateSatBeacon(satPass: SatPass, stationPos: StationPos, date: Date) {
withContext(defaultDispatcher) {
val satPos = satPass.satellite.getPosition(stationPos, date)
_beaconPoint.emit(SkyPos(satPos.azimuth, satPos.elevation))
}
}
// Single calculation of beacon Az/El points list
private val _trajectoryPoints = MutableSharedFlow<List<SkyPos>>(replay = 1)
val trajectoryPoints: SharedFlow<List<SkyPos>> = _trajectoryPoints
suspend fun calculateSatTrajectory(satPass: SatPass, stationPos: StationPos) {
withContext(defaultDispatcher) {
val trajectoryPoints = mutableListOf<SkyPos>()
var currentTime = satPass.aosTime
while (currentTime < satPass.losTime) {
val satPos = satPass.satellite.getPosition(stationPos, Date(currentTime))
trajectoryPoints.add(SkyPos(satPos.azimuth, satPos.elevation))
currentTime += 15000
}
_trajectoryPoints.emit(trajectoryPoints)
}
}
// Multi Sat map
}
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict4kotlin
import kotlin.math.*
class DeepSpaceSat(tle: TLE) : Satellite(tle) {
class DeepSpaceSat(params: TLE) : Satellite(params) {
private val c1: Double
private val c4: Double
@@ -35,41 +35,41 @@ class DeepSpaceSat(tle: TLE) : Satellite(tle) {
init {
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
val a1 = (xke / super.tle.xno).pow(twoThirds)
dsv.cosio = cos(super.tle.xincl)
val a1 = (xke / super.params.xno).pow(twoThirds)
dsv.cosio = cos(super.params.xincl)
dsv.theta2 = dsv.cosio * dsv.cosio
x3thm1 = 3.0 * dsv.theta2 - 1
dsv.eosq = super.tle.eccn * super.tle.eccn
dsv.eosq = super.params.eccn * super.params.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.tle.xno / (1.0 + delo)
dsv.xnodp = super.params.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.tle.eccn) - 1.0) * earthRadius)
setPerigee((dsv.aodp * (1.0 - super.params.eccn) - 1.0) * earthRadius)
val pinvsq = invert(dsv.aodp * dsv.aodp * dsv.betao2 * dsv.betao2)
dsv.sing = sin(super.tle.omegao)
dsv.cosg = cos(super.tle.omegao)
dsv.sing = sin(super.params.omegao)
dsv.cosg = cos(super.params.omegao)
val tsi = invert(dsv.aodp - s4)
val eta = dsv.aodp * super.tle.eccn * tsi
val eta = dsv.aodp * super.params.eccn * tsi
val etasq = eta * eta
val eeta = super.tle.eccn * eta
val eeta = super.params.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.tle.bstar * c2
dsv.sinio = sin(super.tle.xincl)
c1 = super.params.bstar * c2
dsv.sinio = sin(super.params.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.tle.eccn
2 * dsv.xnodp * coef1 * dsv.aodp * dsv.betao2 * (eta * (2.0 + 0.5 * etasq) + super.params.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.tle.omegao))))
* (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * super.params.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(tle: TLE) : Satellite(tle) {
fun calculateSDP4(tSince: Double) {
synchronized(this) {
val temp = DoubleArray(12)
val xmdf = tle.xmo + dsv.xmdot * tSince
val xmdf = params.xmo + dsv.xmdot * tSince
val tsq = tSince * tSince
val templ = t2cof * tsq
dsv.xll = xmdf + dsv.xnodp * templ
dsv.omgadf = tle.omegao + dsv.omgdot * tSince
val xnoddf = tle.xnodeo + dsv.xnodot * tSince
dsv.omgadf = params.omegao + dsv.omgdot * tSince
val xnoddf = params.xnodeo + dsv.xnodot * tSince
dsv.xnode = xnoddf + xnodcf * tsq
val tempa = 1.0 - c1 * tSince
val tempe = tle.bstar * c4 * tSince
val tempe = params.bstar * c4 * tSince
dsv.xn = dsv.xnodp
dsv.t = tSince
deep.dpsec(tle)
deep.dpsec(params)
val a = (xke / dsv.xn).pow(twoThirds) * tempa * tempa
dsv.em = dsv.em - tempe
deep.dpper()
@@ -425,16 +425,16 @@ class DeepSpaceSat(tle: TLE) : Satellite(tle) {
private var epochRestart = false
init {
thgr = thetaG(tle.epoch)
eq = tle.eccn
thgr = thetaG(params.epoch)
eq = params.eccn
xnq = dsv.xnodp
aqnv = invert(dsv.aodp)
xqncl = tle.xincl
xmao = tle.xmo
xqncl = params.xincl
xmao = params.xmo
xpidot = dsv.omgdot + dsv.xnodot
sinq = sin(tle.xnodeo)
cosq = cos(tle.xnodeo)
omegaq = tle.omegao
sinq = sin(params.xnodeo)
cosq = cos(params.xnodeo)
omegaq = params.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(tle: TLE) : Satellite(tle) {
temp = 2.0 * temp1 * root54
d5421 = temp * f542 * g521
d5433 = temp * f543 * g533
xlamo = xmao + tle.xnodeo + tle.xnodeo - thgr - thgr
xlamo = xmao + params.xnodeo + params.xnodeo - thgr - thgr
bfact = dsv.xmdot + dsv.xnodot + dsv.xnodot - tHdt - tHdt
bfact += ssl + ssh + ssh
} else {
@@ -556,7 +556,7 @@ class DeepSpaceSat(tle: TLE) : Satellite(tle) {
fasx2 = 0.13130908
fasx4 = 2.8843198
fasx6 = 0.37448087
xlamo = xmao + tle.xnodeo + tle.omegao - thgr
xlamo = xmao + params.xnodeo + params.omegao - thgr
bfact = dsv.xmdot + xpidot - tHdt
bfact += ssl + ssg + ssh
}
@@ -623,12 +623,12 @@ class DeepSpaceSat(tle: TLE) : Satellite(tle) {
}
// Entrance for deep space secular effects
fun dpsec(tle: TLE) {
fun dpsec(params: TLE) {
dsv.xll = dsv.xll + ssl * dsv.t
dsv.omgadf = dsv.omgadf + ssg * dsv.t
dsv.xnode = dsv.xnode + ssh * dsv.t
dsv.em = tle.eccn + sse * dsv.t
dsv.xinc = tle.xincl + ssi * dsv.t
dsv.em = params.eccn + sse * dsv.t
dsv.xinc = params.xincl + ssi * dsv.t
if (dsv.xinc < 0) {
dsv.xinc = -dsv.xinc
dsv.xnode = dsv.xnode + Math.PI
@@ -17,4 +17,4 @@
*/
package com.rtbishop.look4sat.domain.predict4kotlin
data class Position(val latitude: Double, val longitude: Double)
data class GeoPos(val latitude: Double, val longitude: Double)
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict4kotlin
import kotlin.math.*
class NearEarthSat(tle: TLE) : Satellite(tle) {
class NearEarthSat(params: TLE) : Satellite(params) {
private val aodp: Double
private val aycof: Double
@@ -53,18 +53,18 @@ class NearEarthSat(tle: TLE) : Satellite(tle) {
init {
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
val a1 = (xke / super.tle.xno).pow(twoThirds)
cosio = cos(super.tle.xincl)
val a1 = (xke / super.params.xno).pow(twoThirds)
cosio = cos(super.params.xincl)
val theta2 = sqr(cosio)
x3thm1 = 3.0 * theta2 - 1.0
val eo = super.tle.eccn
val eo = super.params.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.tle.xno / (1.0 + delo)
xnodp = super.params.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(tle: TLE) : Satellite(tle) {
val psisq = abs(1.0 - etasq)
val coef = qoms24 * tsi.pow(4.0)
val coef1 = coef / psisq.pow(3.5)
val bstar = super.tle.bstar
val bstar = super.params.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.tle.xincl)
sinio = sin(super.params.xincl)
val a3ovk2 = -j3Harmonic / ck2
val c3 = coef * tsi * a3ovk2 * xnodp * sinio / eo
x1mth2 = 1.0 - theta2
val omegao = super.tle.omegao
val omegao = super.params.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(tle: TLE) : Satellite(tle) {
t2cof = 1.5 * c1
xlcof = 0.125 * a3ovk2 * sinio * (3.0 + 5 * cosio) / (1.0 + cosio)
aycof = 0.25 * a3ovk2 * sinio
val xmo = super.tle.xmo
val xmo = super.params.xmo
delmo = (1.0 + eta * cos(xmo)).pow(3.0)
sinmo = sin(xmo)
x7thm1 = 7.0 * theta2 - 1
@@ -138,14 +138,14 @@ class NearEarthSat(tle: TLE) : Satellite(tle) {
fun calculateSGP4(tSince: Double) {
synchronized(this) {
val temp = DoubleArray(9)
val xmdf = tle.xmo + xmdot * tSince
val omgadf = tle.omegao + omgdot * tSince
val xnoddf = tle.xnodeo + xnodot * tSince
val xmdf = params.xmo + xmdot * tSince
val omgadf = params.omegao + omgdot * tSince
val xnoddf = params.xnodeo + xnodot * tSince
var omega = omgadf
var xmp = xmdf
val tsq = sqr(tSince)
val xnode = xnoddf + xnodcf * tsq
val bstar = tle.bstar
val bstar = params.bstar
var tempa = 1.0 - c1 * tSince
var tempe = bstar * c4 * tSince
var templ = t2cof * tsq
@@ -162,7 +162,7 @@ class NearEarthSat(tle: TLE) : Satellite(tle) {
templ += t3cof * tcube + tfour * (t4cof + tSince * t5cof)
}
val a = aodp * tempa.pow(2.0)
val eo = tle.eccn
val eo = params.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(tle: TLE) : Satellite(tle) {
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 = tle.xincl + 1.5 * temp[2] * cosio * sinio * cos2u
val xinck = params.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)
@@ -18,11 +18,10 @@
package com.rtbishop.look4sat.domain.predict4kotlin
import java.util.*
import kotlin.math.*
class PassPredictor(private val satellite: Satellite, private val stationPos: StationPosition) {
class PassPredictor(private val satellite: Satellite, private val stationPos: StationPos) {
private val oneQuarterOrbitMin = (24.0 * 60.0 / satellite.tle.meanmo / 4.0).toInt()
private val oneQuarterOrbitMin = (24.0 * 60.0 / satellite.params.meanmo / 4.0).toInt()
private val speedOfLight = 2.99792458E8
fun getDownlinkFreq(freq: Long, date: Date): Long {
@@ -41,7 +40,7 @@ class PassPredictor(private val satellite: Satellite, private val stationPos: St
fun getPositions(refDate: Date, stepSec: Int, minBefore: Int, orbits: Double): List<SatPos> {
val positions = mutableListOf<SatPos>()
val orbitalPeriod = 24 * 60 / satellite.tle.meanmo
val orbitalPeriod = 24 * 60 / satellite.params.meanmo
val endDate = Date(refDate.time + (orbitalPeriod * orbits * 60L * 1000L).toLong())
val startDate = Date(refDate.time - minBefore * 60L * 1000L)
var currentDate = startDate
@@ -60,16 +59,15 @@ class PassPredictor(private val satellite: Satellite, private val stationPos: St
var lastAosDate: Date
var count = 0
if (satellite.willBeSeen(stationPos)) {
if (satellite.tle.isDeepspace) {
if (satellite.params.isDeepspace) {
passes.add(nextDeepSpacePass(refDate))
} else {
do {
if (count > 0) shouldWindBack = false
val pass = nextNearEarthPass(startDate, shouldWindBack)
lastAosDate = pass.aosDate
lastAosDate = Date(pass.aosTime)
passes.add(pass)
startDate =
Date(pass.losDate.time + (oneQuarterOrbitMin * 3) * 60L * 1000L)
startDate = Date(pass.losTime + (oneQuarterOrbitMin * 3) * 60L * 1000L)
count++
} while (lastAosDate < endDate)
}
@@ -79,16 +77,13 @@ class PassPredictor(private val satellite: Satellite, private val stationPos: St
private fun nextDeepSpacePass(refDate: Date): SatPass {
val satPos = getSatPos(refDate)
val id = satellite.tle.catnum
val name = satellite.tle.name
val isDeep = satellite.tle.isDeepspace
val aos = Date(refDate.time - 24 * 60L * 60L * 1000L).time
val los = Date(refDate.time + 24 * 60L * 60L * 1000L).time
val tca = Date((aos + los) / 2).time
val az = Math.toDegrees(satPos.azimuth)
val elev = Math.toDegrees(satPos.elevation)
val alt = satPos.altitude
return SatPass(id, name, isDeep, aos, az, los, az, tca, az, alt, elev, this)
return SatPass(aos, az, los, az, tca, az, alt, elev, satellite, this)
}
private fun nextNearEarthPass(refDate: Date, windBack: Boolean = false): SatPass {
@@ -96,9 +91,6 @@ class PassPredictor(private val satellite: Satellite, private val stationPos: St
clear()
timeInMillis = refDate.time
}
val id = satellite.tle.catnum
val name = satellite.tle.name
val isDeep = satellite.tle.isDeepspace
var elevation: Double
var maxElevation = 0.0
@@ -176,6 +168,6 @@ class PassPredictor(private val satellite: Satellite, private val stationPos: St
val losAz = Math.toDegrees(satPos.azimuth)
val tca = Date((aos + los) / 2).time
val elev = Math.toDegrees(maxElevation)
return SatPass(id, name, isDeep, aos, aosAz, los, losAz, tca, tcaAz, alt, elev, this)
return SatPass(aos, aosAz, los, losAz, tca, tcaAz, alt, elev, satellite, this)
}
}
@@ -21,7 +21,7 @@ import kotlin.math.round
class QthConverter {
fun qthToPosition(qthString: String): Position? {
fun qthToPosition(qthString: String): GeoPos? {
val trimmedQth = qthString.take(6)
if (!isValidQTH(trimmedQth)) return null
val lonFirst = (trimmedQth[0].uppercaseChar().code - 65) * 20
@@ -32,7 +32,7 @@ class QthConverter {
val latThird = (((trimmedQth[5].lowercaseChar().code - 97) / 24.0) + (1.0 / 48.0)) - 90
val longitude = (lonFirst + lonSecond + lonThird).roundToDecimals(4)
val latitude = (latFirst + latSecond + latThird).roundToDecimals(4)
return Position(latitude, longitude)
return GeoPos(latitude, longitude)
}
fun positionToQTH(lat: Double, lon: Double): String? {
@@ -17,29 +17,20 @@
*/
package com.rtbishop.look4sat.domain.predict4kotlin
import java.util.*
data class SatPass(
val catNum: Int,
val name: String,
val isDeepSpace: Boolean,
private val aosTime: Long,
val aosTime: Long,
val aosAzimuth: Double,
private val losTime: Long,
val losTime: Long,
val losAzimuth: Double,
private val tcaTime: Long,
val tcaTime: Long,
val tcaAzimuth: Double,
val altitude: Double,
val maxElevation: Double,
val satellite: Satellite,
val predictor: PassPredictor,
var progress: Int = 0
) {
val aosDate: Date
get() = Date(aosTime)
val losDate: Date
get() = Date(losTime)
val tcaDate: Date
get() = Date(tcaTime)
val catNum: Int = satellite.params.catnum
val name: String = satellite.params.name
val isDeepSpace: Boolean = satellite.params.isDeepspace
var progress: Int = 0
}
@@ -20,9 +20,6 @@ package com.rtbishop.look4sat.domain.predict4kotlin
import java.util.*
import kotlin.math.*
const val earthRadiusKm = 6378.16
const val speedOfLight = 2.99792458E8
data class SatPos(
var azimuth: Double = 0.0,
var elevation: Double = 0.0,
@@ -33,48 +30,51 @@ data class SatPos(
var rangeRate: Double = 0.0,
var theta: Double = 0.0,
var time: Date = Date()
)
) {
private val earthRadiusKm = 6378.16
private val speedOfLight = 2.99792458E8
fun SatPos.getDownlinkFreq(freq: Long): Long {
return (freq.toDouble() * (speedOfLight - this.rangeRate * 1000.0) / speedOfLight).toLong()
}
fun SatPos.getUplinkFreq(freq: Long): Long {
return (freq.toDouble() * (speedOfLight + this.rangeRate * 1000.0) / speedOfLight).toLong()
}
fun SatPos.getRangeCircle(): List<Position> {
val positions = mutableListOf<Position>()
val lat = this.latitude
val lon = this.longitude
// rangeCircleRadiusKm
// earthRadiusKm * acos(earthRadiusKm / (earthRadiusKm + satPos.altitude))
val beta = acos(earthRadiusKm / (earthRadiusKm + this.altitude))
var tempAzimuth = 0
while (tempAzimuth < 360) {
val azimuth = tempAzimuth / 360.0 * 2.0 * Math.PI
var rangelat = asin(sin(lat) * cos(beta) + cos(azimuth) * sin(beta) * cos(lat))
val num = (cos(beta) - (sin(lat) * sin(rangelat)))
val den = cos(lat) * cos(rangelat)
var rangelon = if (tempAzimuth == 0 && (beta > ((Math.PI / 2.0) - lat))) {
lon + Math.PI
} else if (tempAzimuth == 180 && (beta > ((Math.PI / 2.0) - lat))) {
lon + Math.PI
} else if (abs(num / den) > 1.0) {
lon
} else {
if ((180 - tempAzimuth) >= 0) {
lon - acos(num / den)
} else {
lon + acos(num / den)
}
}
while (rangelon < 0.0) rangelon += Math.PI * 2.0
while (rangelon > Math.PI * 2.0) rangelon -= Math.PI * 2.0
rangelat = Math.toDegrees(rangelat)
rangelon = Math.toDegrees(rangelon)
positions.add(Position(rangelat, rangelon))
tempAzimuth += 1
fun getDownlinkFreq(freq: Long): Long {
return (freq.toDouble() * (speedOfLight - this.rangeRate * 1000.0) / speedOfLight).toLong()
}
fun getUplinkFreq(freq: Long): Long {
return (freq.toDouble() * (speedOfLight + this.rangeRate * 1000.0) / speedOfLight).toLong()
}
fun getRangeCircle(): List<GeoPos> {
val positions = mutableListOf<GeoPos>()
val lat = this.latitude
val lon = this.longitude
// rangeCircleRadiusKm
// earthRadiusKm * acos(earthRadiusKm / (earthRadiusKm + satPos.altitude))
val beta = acos(earthRadiusKm / (earthRadiusKm + this.altitude))
var tempAzimuth = 0
while (tempAzimuth < 360) {
val azimuth = tempAzimuth / 360.0 * 2.0 * Math.PI
var rangelat = asin(sin(lat) * cos(beta) + cos(azimuth) * sin(beta) * cos(lat))
val num = (cos(beta) - (sin(lat) * sin(rangelat)))
val den = cos(lat) * cos(rangelat)
var rangelon = if (tempAzimuth == 0 && (beta > ((Math.PI / 2.0) - lat))) {
lon + Math.PI
} else if (tempAzimuth == 180 && (beta > ((Math.PI / 2.0) - lat))) {
lon + Math.PI
} else if (abs(num / den) > 1.0) {
lon
} else {
if ((180 - tempAzimuth) >= 0) {
lon - acos(num / den)
} else {
lon + acos(num / den)
}
}
while (rangelon < 0.0) rangelon += Math.PI * 2.0
while (rangelon > Math.PI * 2.0) rangelon -= Math.PI * 2.0
rangelat = Math.toDegrees(rangelat)
rangelon = Math.toDegrees(rangelon)
positions.add(GeoPos(rangelat, rangelon))
tempAzimuth += 1
}
return positions
}
return positions
}
@@ -21,7 +21,7 @@ import java.util.*
import java.util.concurrent.atomic.AtomicReference
import kotlin.math.*
abstract class Satellite(val tle: TLE) {
abstract class Satellite(val params: TLE) {
private val flatFactor = 3.35281066474748E-3
private val deg2Rad = 1.745329251994330E-2
@@ -41,26 +41,26 @@ abstract class Satellite(val tle: TLE) {
var s4 = 0.0
val xke = 7.43669161E-2
fun willBeSeen(pos: StationPosition): Boolean {
return if (tle.meanmo < 1e-8) false else {
var lin = tle.incl
fun willBeSeen(pos: StationPos): Boolean {
return if (params.meanmo < 1e-8) false else {
var lin = params.incl
if (lin >= 90.0) lin = 180.0 - lin
val sma = 331.25 * exp(ln(1440.0 / tle.meanmo) * (2.0 / 3.0))
val apogee = sma * (1.0 + tle.eccn) - earthRadius
val sma = 331.25 * exp(ln(1440.0 / params.meanmo) * (2.0 / 3.0))
val apogee = sma * (1.0 + params.eccn) - earthRadius
acos(earthRadius / (apogee + earthRadius)) + lin * deg2Rad > abs(pos.latitude * deg2Rad)
}
}
fun getPredictor(pos: StationPosition): PassPredictor {
fun getPredictor(pos: StationPos): PassPredictor {
return PassPredictor(this, pos)
}
fun getPosition(pos: StationPosition, time: Date): SatPos {
fun getPosition(pos: StationPos, time: Date): SatPos {
val satPos = SatPos()
// 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(tle.epoch)
val julEpoch = juliandDateOfEpoch(params.epoch)
val tsince = (julUTC - julEpoch) * minPerDay
calculateSDP4orSGP4(tsince)
// Scale position and velocity vectors to km and km/sec
@@ -105,7 +105,7 @@ abstract class Satellite(val tle: TLE) {
}
private fun calculateSDP4orSGP4(tsince: Double) {
if (tle.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
if (params.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
else (this as NearEarthSat).calculateSGP4(tsince)
}
@@ -120,7 +120,7 @@ abstract class Satellite(val tle: TLE) {
julianUTC: Double,
positionVector: Vector4,
velocityVector: Vector4,
gsPos: StationPosition,
gsPos: StationPos,
squintVector: Vector4,
satPos: SatPos
) {
@@ -162,7 +162,7 @@ abstract class Satellite(val tle: TLE) {
// Returns the ECI position and velocity of the observer
private fun calculateUserPosVel(
time: Double,
gsPos: StationPosition,
gsPos: StationPos,
gsPosTheta: AtomicReference<Double>,
obsPos: Vector4,
obsVel: Vector4
@@ -0,0 +1,20 @@
/*
* 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.predict4kotlin
data class SkyPos(val azimuth: Double, val elevation: Double)
@@ -17,7 +17,7 @@
*/
package com.rtbishop.look4sat.domain.predict4kotlin
data class StationPosition(
data class StationPos(
val latitude: Double,
val longitude: Double,
val altitude: Double,
@@ -39,13 +39,16 @@ data class TLE(
val xno: Double = meanmo * Math.PI * 2.0 / 1440
val isDeepspace: Boolean = meanmo < 6.4
fun createSat(): Satellite {
return when {
this.isDeepspace -> DeepSpaceSat(this)
else -> NearEarthSat(this)
}
}
companion object {
fun createSat(array: Array<String>): Satellite? {
return importElement(array)?.createSat()
}
fun importElement(array: Array<String>): TLE? {
fun parseArray(array: Array<String>): TLE? {
if (array.size != 3) return null
try {
val name: String = array[0].trim()
@@ -65,7 +68,7 @@ data class TLE(
}
}
fun importElements(stream: InputStream): List<TLE> {
fun parseStream(stream: InputStream): List<TLE> {
val elementArray = arrayOf(String(), String(), String())
val importedElements = mutableListOf<TLE>()
var line = 0
@@ -75,7 +78,7 @@ data class TLE(
line++
} else {
elementArray[line] = it
importElement(elementArray)?.let { tle -> importedElements.add(tle) }
parseArray(elementArray)?.let { tle -> importedElements.add(tle) }
line = 0
}
}
@@ -83,10 +86,3 @@ data class TLE(
}
}
}
fun TLE.createSat(): Satellite {
return when {
this.isDeepspace -> DeepSpaceSat(this)
else -> NearEarthSat(this)
}
}
@@ -7,20 +7,20 @@ class Look4SatTest {
@Test
fun `Given correct TLE array returns Satellite`() {
val elementArray = arrayOf(
val tleArray = arrayOf(
"ISS (ZARYA)",
"1 25544U 98067A 21242.56000419 .00070558 00000-0 12956-2 0 9996",
"2 25544 51.6433 334.9559 0003020 334.9496 106.9882 15.48593918300128"
)
assert(TLE.createSat(elementArray) != null)
assert(TLE.parseArray(tleArray)?.createSat() != null)
}
@Test
fun `Given incorrect TLE array returns null`() {
val elementArray = arrayOf(
val tleArray = arrayOf(
"1 25544U 98067A 21242.56000419 .00070558 00000-0 12956-2 0 9996",
"2 25544 51.6433 334.9559 0003020 334.9496 106.9882 15.48593918300128"
)
assert(TLE.createSat(elementArray) == null)
assert(TLE.parseArray(tleArray)?.createSat() == null)
}
}