From 9a4de03cac4a94f64e061e512a6b8d59b0c389fe Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sun, 12 Sep 2021 12:08:58 +0100 Subject: [PATCH] Renamed and refactored several predict4kotlin classes --- .../look4sat/framework/PreferencesProvider.kt | 18 ++-- .../look4sat/framework/db/RoomConverters.kt | 1 - .../look4sat/framework/model/SatData.kt | 6 +- .../satMapScreen/SatMapFragment.kt | 12 +-- .../satMapScreen/SatMapViewModel.kt | 51 ++++++----- .../satPassInfoScreen/PassInfoFragment.kt | 8 +- .../satPassInfoScreen/PassInfoView.kt | 10 +-- .../satPassInfoScreen/PassInfoViewModel.kt | 2 +- .../satPassScreen/SatPassAdapter.kt | 6 +- .../satPassScreen/SatPassFragment.kt | 8 +- .../satPassScreen/SatPassViewModel.kt | 4 +- .../look4sat/data/PreferencesSource.kt | 6 +- .../look4sat/data/SatDataRepository.kt | 2 +- .../look4sat/data/SatPassRepository.kt | 12 +-- .../rtbishop/look4sat/data/SatPredictor.kt | 86 ++++++++++++++++++ .../domain/predict4kotlin/DeepSpaceSat.kt | 62 ++++++------- .../predict4kotlin/{Position.kt => GeoPos.kt} | 2 +- .../domain/predict4kotlin/NearEarthSat.kt | 30 +++---- .../domain/predict4kotlin/PassPredictor.kt | 24 ++--- .../domain/predict4kotlin/QthConverter.kt | 4 +- .../look4sat/domain/predict4kotlin/SatPass.kt | 25 ++---- .../look4sat/domain/predict4kotlin/SatPos.kt | 90 +++++++++---------- .../domain/predict4kotlin/Satellite.kt | 24 ++--- .../look4sat/domain/predict4kotlin/SkyPos.kt | 20 +++++ .../{StationPosition.kt => StationPos.kt} | 2 +- .../look4sat/domain/predict4kotlin/TLE.kt | 24 +++-- .../com/rtbishop/look4sat/Look4SatTest.kt | 8 +- 27 files changed, 315 insertions(+), 232 deletions(-) create mode 100644 core/src/main/java/com/rtbishop/look4sat/data/SatPredictor.kt rename core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/{Position.kt => GeoPos.kt} (92%) create mode 100644 core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SkyPos.kt rename core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/{StationPosition.kt => StationPos.kt} (97%) diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/PreferencesProvider.kt b/app/src/main/java/com/rtbishop/look4sat/framework/PreferencesProvider.kt index 96e8ac59..32dbf55a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/PreferencesProvider.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/PreferencesProvider.kt @@ -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 } diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/db/RoomConverters.kt b/app/src/main/java/com/rtbishop/look4sat/framework/db/RoomConverters.kt index e7a190ca..2411c223 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/db/RoomConverters.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/db/RoomConverters.kt @@ -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 diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt b/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt index 474b4f41..bb950034 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt @@ -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 -) \ No newline at end of file + val osmPos: GeoPos +) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapFragment.kt index 40fd60a9..77a4d7a6 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapFragment.kt @@ -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, binding: FragmentMapBinding) { + private fun renderSatPositions(posMap: Map, 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>, binding: FragmentMapBinding) { + private fun renderSatTrack(satTrack: List>, 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, binding: FragmentMapBinding) { + private fun renderSatFootprint(satFootprint: List, binding: FragmentMapBinding) { val footprintPoints = satFootprint.map { GeoPoint(it.latitude, it.longitude) } val footprintOverlay = Polygon().apply { fillPaint.set(footprintPaint) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapViewModel.kt index 797ce1ea..3b072925 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satMapScreen/SatMapViewModel.kt @@ -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>>() - val satTrack: LiveData>> = _satTrack + private val _satTrack = MutableLiveData>>() + val satTrack: LiveData>> = _satTrack - private val _satFootprint = MutableLiveData>() - val satFootprint: LiveData> = _satFootprint + private val _satFootprint = MutableLiveData>() + val satFootprint: LiveData> = _satFootprint private val _satData = MutableLiveData() val satData: LiveData = this._satData - private val _satPositions = MutableLiveData>() - val satPositions: LiveData> = _satPositions + private val _satPositions = MutableLiveData>() + val satPositions: LiveData> = _satPositions init { viewModelScope.launch { @@ -110,38 +109,38 @@ class SatMapViewModel @Inject constructor( } } - private suspend fun setSatPositions(list: List, gsp: StationPosition, date: Date) { + private suspend fun setSatPositions(list: List, gsp: StationPos, date: Date) { withContext(defaultDispatcher) { - val satPositions = mutableMapOf() + val satPositions = mutableMapOf() 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>() - val currentTrack = mutableListOf() + val satTracks = mutableListOf>() + val currentTrack = mutableListOf() 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().apply { addAll(currentTrack) } + currentTrack.add(GeoPos(osmLat, -180.0)) + val finishedTrack = mutableListOf().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().apply { addAll(currentTrack) } + currentTrack.add(GeoPos(osmLat, 180.0)) + val finishedTrack = mutableListOf().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) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoFragment.kt index 6fa77dbd..6b6e5c33 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoFragment.kt @@ -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) } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoView.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoView.kt index ff939c2d..a8d38880 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoView.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoView.kt @@ -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 } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoViewModel.kt index 9e95c9b3..2fe3729e 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassInfoScreen/PassInfoViewModel.kt @@ -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) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassAdapter.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassAdapter.kt index e14d5d91..fa0ccee1 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassAdapter.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassAdapter.kt @@ -34,7 +34,7 @@ class SatPassAdapter(private val isUTC: Boolean, private val clickListener: Pass private val diffCallback = object : DiffUtil.ItemCallback() { 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 } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassFragment.kt index f47e68f7..7a8b58ec 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassFragment.kt @@ -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) } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassViewModel.kt index 836cfd74..1f345e44 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/satPassScreen/SatPassViewModel.kt @@ -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() } } diff --git a/core/src/main/java/com/rtbishop/look4sat/data/PreferencesSource.kt b/core/src/main/java/com/rtbishop/look4sat/data/PreferencesSource.kt index 8e3bcbe6..3215bed8 100644 --- a/core/src/main/java/com/rtbishop/look4sat/data/PreferencesSource.kt +++ b/core/src/main/java/com/rtbishop/look4sat/data/PreferencesSource.kt @@ -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 diff --git a/core/src/main/java/com/rtbishop/look4sat/data/SatDataRepository.kt b/core/src/main/java/com/rtbishop/look4sat/data/SatDataRepository.kt index fd3b9892..6bebe70e 100644 --- a/core/src/main/java/com/rtbishop/look4sat/data/SatDataRepository.kt +++ b/core/src/main/java/com/rtbishop/look4sat/data/SatDataRepository.kt @@ -91,6 +91,6 @@ class SatDataRepository( } private fun importSatEntries(stream: InputStream): List { - return TLE.importElements(stream).map { tle -> SatEntry(tle) } + return TLE.parseStream(stream).map { tle -> SatEntry(tle) } } } diff --git a/core/src/main/java/com/rtbishop/look4sat/data/SatPassRepository.kt b/core/src/main/java/com/rtbishop/look4sat/data/SatPassRepository.kt index 8aef96d0..8f25f6f2 100644 --- a/core/src/main/java/com/rtbishop/look4sat/data/SatPassRepository.kt +++ b/core/src/main/java/com/rtbishop/look4sat/data/SatPassRepository.kt @@ -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, refDate: Date): List { - 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 } } } diff --git a/core/src/main/java/com/rtbishop/look4sat/data/SatPredictor.kt b/core/src/main/java/com/rtbishop/look4sat/data/SatPredictor.kt new file mode 100644 index 00000000..7e945894 --- /dev/null +++ b/core/src/main/java/com/rtbishop/look4sat/data/SatPredictor.kt @@ -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>(replay = 1) + private var selectedSatellites = emptyList() + val passes: SharedFlow> = _passes + + suspend fun triggerCalculation(satellites: List, 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, refDate: Date = Date()) { + if (satellites.isEmpty()) { + _passes.emit(emptyList()) + } else { + withContext(defaultDispatcher) { + val allPasses = mutableListOf() + selectedSatellites = satellites + satellites.forEach { satellite -> allPasses.addAll(getPasses(satellite, refDate)) } + _passes.emit(filterPasses(allPasses, refDate)) + } + } + } + + private fun getPasses(satellite: Satellite, refDate: Date): List { + val predictor = satellite.getPredictor(preferencesSource.loadStationPosition()) + return predictor.getPasses(refDate, preferencesSource.getHoursAhead(), true) + } + + private fun filterPasses(passes: List, refDate: Date): List { + 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(replay = 1) + val beaconPoint: SharedFlow = _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>(replay = 1) + val trajectoryPoints: SharedFlow> = _trajectoryPoints + + suspend fun calculateSatTrajectory(satPass: SatPass, stationPos: StationPos) { + withContext(defaultDispatcher) { + val trajectoryPoints = mutableListOf() + 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 +} \ No newline at end of file diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/DeepSpaceSat.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/DeepSpaceSat.kt index a5067911..820d67c0 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/DeepSpaceSat.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/DeepSpaceSat.kt @@ -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 diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Position.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/GeoPos.kt similarity index 92% rename from core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Position.kt rename to core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/GeoPos.kt index 405bf083..24d4bf13 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Position.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/GeoPos.kt @@ -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) diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/NearEarthSat.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/NearEarthSat.kt index 9438f159..6d59231b 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/NearEarthSat.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/NearEarthSat.kt @@ -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) diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/PassPredictor.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/PassPredictor.kt index a23ed3f1..487876b1 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/PassPredictor.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/PassPredictor.kt @@ -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 { val positions = mutableListOf() - 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) } } diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/QthConverter.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/QthConverter.kt index 4cfbae05..b279a810 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/QthConverter.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/QthConverter.kt @@ -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? { diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPass.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPass.kt index 36809f9b..b0b037bd 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPass.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPass.kt @@ -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 } diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPos.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPos.kt index 7b6c24f8..5195c061 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPos.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SatPos.kt @@ -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 { - val positions = mutableListOf() - 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 { + val positions = mutableListOf() + 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 } diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Satellite.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Satellite.kt index afbecb80..a2792409 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Satellite.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/Satellite.kt @@ -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, obsPos: Vector4, obsVel: Vector4 diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SkyPos.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SkyPos.kt new file mode 100644 index 00000000..835d16d0 --- /dev/null +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/SkyPos.kt @@ -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 . + */ +package com.rtbishop.look4sat.domain.predict4kotlin + +data class SkyPos(val azimuth: Double, val elevation: Double) diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/StationPosition.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/StationPos.kt similarity index 97% rename from core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/StationPosition.kt rename to core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/StationPos.kt index 98bc20c4..184deeba 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/StationPosition.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/StationPos.kt @@ -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, diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/TLE.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/TLE.kt index cf480d7a..53a9b7d7 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/TLE.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict4kotlin/TLE.kt @@ -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): Satellite? { - return importElement(array)?.createSat() - } - - fun importElement(array: Array): TLE? { + fun parseArray(array: Array): 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 { + fun parseStream(stream: InputStream): List { val elementArray = arrayOf(String(), String(), String()) val importedElements = mutableListOf() 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) - } -} diff --git a/core/src/test/java/com/rtbishop/look4sat/Look4SatTest.kt b/core/src/test/java/com/rtbishop/look4sat/Look4SatTest.kt index 1b73a052..1ad4bec8 100644 --- a/core/src/test/java/com/rtbishop/look4sat/Look4SatTest.kt +++ b/core/src/test/java/com/rtbishop/look4sat/Look4SatTest.kt @@ -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) } }