kopia lustrzana https://github.com/rt-bishop/Look4Sat
Removed all java dependencies from domain.predict package
rodzic
8ef0d103e4
commit
88c7bb6bca
|
@ -116,7 +116,7 @@ class MapViewModel @Inject constructor(
|
|||
val currentTrack = mutableListOf<GeoPos>()
|
||||
val endDate = Date(date.time + (satellite.orbitalPeriod * 2.4 * 60000L).toLong())
|
||||
var oldLongitude = 0.0
|
||||
predictor.getSatTrack(satellite, pos, date, endDate).forEach { satPos ->
|
||||
predictor.getSatTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
|
||||
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
|
||||
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
|
||||
val currentPosition = GeoPos(osmLat, osmLon)
|
||||
|
@ -143,7 +143,7 @@ class MapViewModel @Inject constructor(
|
|||
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
||||
val positions = mutableMapOf<Satellite, GeoPos>()
|
||||
satellites.forEach { satellite ->
|
||||
val satPos = predictor.getSatPos(satellite, pos, date)
|
||||
val satPos = predictor.getSatPos(satellite, pos, date.time)
|
||||
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
|
||||
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
|
||||
positions[satellite] = GeoPos(osmLat, osmLon)
|
||||
|
@ -152,7 +152,7 @@ class MapViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||
val satPos = predictor.getSatPos(satellite, pos, date)
|
||||
val satPos = predictor.getSatPos(satellite, pos, date.time)
|
||||
val satFootprint = satPos.getRangeCircle().map { rangePos ->
|
||||
val osmLat = clipLat(rangePos.latitude)
|
||||
val osmLon = clipLon(rangePos.longitude)
|
||||
|
@ -162,7 +162,7 @@ class MapViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun getSatData(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||
val satPos = predictor.getSatPos(satellite, pos, date)
|
||||
val satPos = predictor.getSatPos(satellite, pos, date.time)
|
||||
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
|
||||
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
|
||||
val osmPos = GeoPos(osmLat, osmLon)
|
||||
|
|
|
@ -49,12 +49,12 @@ class PassesViewModel @Inject constructor(
|
|||
if (preferences.isSetupDone()) {
|
||||
viewModelScope.launch {
|
||||
_passes.postValue(DataState.Loading)
|
||||
val dateNow = Date()
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val satellites = dataRepository.getSelectedSatellites()
|
||||
val stationPos = preferences.loadStationPosition()
|
||||
val hoursAhead = preferences.getHoursAhead()
|
||||
val minElev = preferences.getMinElevation()
|
||||
predictor.triggerCalculation(satellites, stationPos, dateNow, hoursAhead, minElev)
|
||||
predictor.triggerCalculation(satellites, stationPos, timeNow, hoursAhead, minElev)
|
||||
}
|
||||
} else {
|
||||
_isFirstLaunchDone.value = false
|
||||
|
@ -77,7 +77,7 @@ class PassesViewModel @Inject constructor(
|
|||
val minElev = preferences.getMinElevation()
|
||||
dataRepository.updateDataFromWeb()
|
||||
dataRepository.updateSelection(isSelected = true)
|
||||
predictor.forceCalculation(satellites, stationPos, Date(), hoursAhead, minElev)
|
||||
predictor.forceCalculation(satellites, stationPos, Date().time, hoursAhead, minElev)
|
||||
preferences.setSetupDone()
|
||||
_isFirstLaunchDone.value = true
|
||||
}
|
||||
|
@ -87,12 +87,12 @@ class PassesViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
_passes.postValue(DataState.Loading)
|
||||
passesProcessing?.cancelAndJoin()
|
||||
val dateNow = Date()
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val satellites = dataRepository.getSelectedSatellites()
|
||||
val stationPos = preferences.loadStationPosition()
|
||||
val hoursAhead = preferences.getHoursAhead()
|
||||
val minElev = preferences.getMinElevation()
|
||||
predictor.forceCalculation(satellites, stationPos, dateNow, hoursAhead, minElev)
|
||||
predictor.forceCalculation(satellites, stationPos, timeNow, hoursAhead, minElev)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,12 +79,12 @@ class RadarViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
var satTrack: List<SatPos> = emptyList()
|
||||
if (!satPass.isDeepSpace) {
|
||||
val startDate = Date(satPass.aosTime)
|
||||
val endDate = Date(satPass.losTime)
|
||||
val startDate = satPass.aosTime
|
||||
val endDate = satPass.losTime
|
||||
satTrack = predictor.getSatTrack(satPass.satellite, stationPos, startDate, endDate)
|
||||
}
|
||||
while (isActive) {
|
||||
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date())
|
||||
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date().time)
|
||||
if (preferences.isRotatorEnabled()) {
|
||||
val server = preferences.getRotatorServer().first
|
||||
val port = preferences.getRotatorServer().second
|
||||
|
@ -102,7 +102,7 @@ class RadarViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
val transmitters = dataRepository.getTransmitters(satPass.catNum)
|
||||
while (isActive) {
|
||||
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date())
|
||||
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date().time)
|
||||
val copiedList = transmitters.map { it.copy() }
|
||||
copiedList.forEach { transmitter ->
|
||||
transmitter.downlink?.let {
|
||||
|
|
|
@ -21,7 +21,6 @@ import kotlinx.coroutines.CoroutineDispatcher
|
|||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
||||
|
||||
|
@ -29,26 +28,26 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
private var selectedSatIds = emptyList<Int>()
|
||||
val passes: SharedFlow<List<SatPass>> = _passes
|
||||
|
||||
suspend fun getSatPos(sat: Satellite, pos: GeoPos, date: Date): SatPos =
|
||||
withContext(predictorDispatcher) {
|
||||
return@withContext sat.getPosition(pos, date.time)
|
||||
}
|
||||
suspend fun getSatPos(sat: Satellite, pos: GeoPos, time: Long): SatPos {
|
||||
return withContext(predictorDispatcher) { sat.getPosition(pos, time) }
|
||||
}
|
||||
|
||||
suspend fun getSatTrack(sat: Satellite, pos: GeoPos, start: Date, end: Date): List<SatPos> =
|
||||
withContext(predictorDispatcher) {
|
||||
suspend fun getSatTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos> {
|
||||
return withContext(predictorDispatcher) {
|
||||
val positions = mutableListOf<SatPos>()
|
||||
var currentTime = start.time
|
||||
while (currentTime < end.time) {
|
||||
var currentTime = start
|
||||
while (currentTime < end) {
|
||||
positions.add(sat.getPosition(pos, currentTime))
|
||||
currentTime += 15000
|
||||
}
|
||||
return@withContext positions
|
||||
positions
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun triggerCalculation(
|
||||
satellites: List<Satellite>,
|
||||
pos: GeoPos,
|
||||
date: Date = Date(),
|
||||
time: Long,
|
||||
hoursAhead: Int = 8,
|
||||
minElevation: Double = 16.0
|
||||
) {
|
||||
|
@ -57,7 +56,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
} else {
|
||||
val newCatNums = satellites.map { it.params.catnum }
|
||||
if (selectedSatIds != newCatNums) {
|
||||
forceCalculation(satellites, pos, date, hoursAhead, minElevation)
|
||||
forceCalculation(satellites, pos, time, hoursAhead, minElevation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +64,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
suspend fun forceCalculation(
|
||||
satellites: List<Satellite>,
|
||||
pos: GeoPos,
|
||||
date: Date = Date(),
|
||||
time: Long,
|
||||
hoursAhead: Int = 8,
|
||||
minElevation: Double = 16.0
|
||||
) {
|
||||
|
@ -76,31 +75,31 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
val allPasses = mutableListOf<SatPass>()
|
||||
selectedSatIds = satellites.map { it.params.catnum }
|
||||
satellites.forEach { satellite ->
|
||||
allPasses.addAll(satellite.getPasses(pos, date, hoursAhead))
|
||||
allPasses.addAll(satellite.getPasses(pos, time, hoursAhead))
|
||||
}
|
||||
_passes.emit(allPasses.filter(date, hoursAhead, minElevation))
|
||||
_passes.emit(allPasses.filter(time, hoursAhead, minElevation))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Satellite.getPasses(pos: GeoPos, date: Date, hours: Int): List<SatPass> {
|
||||
private fun Satellite.getPasses(pos: GeoPos, time: Long, hours: Int): List<SatPass> {
|
||||
val passes = mutableListOf<SatPass>()
|
||||
val endDate = Date(date.time + hours * 60L * 60L * 1000L)
|
||||
val endDate = time + hours * 60L * 60L * 1000L
|
||||
val quarterOrbitMin = (this.orbitalPeriod / 4.0).toInt()
|
||||
var startDate = date
|
||||
var startDate = time
|
||||
var shouldRewind = true
|
||||
var lastAosDate: Date
|
||||
var lastAosDate: Long
|
||||
var count = 0
|
||||
if (this.willBeSeen(pos)) {
|
||||
if (this.params.isDeepspace) {
|
||||
passes.add(getGeoPass(this, pos, date))
|
||||
passes.add(getGeoPass(this, pos, time))
|
||||
} else {
|
||||
do {
|
||||
if (count > 0) shouldRewind = false
|
||||
val pass = getLeoPass(this, pos, startDate, shouldRewind)
|
||||
lastAosDate = Date(pass.aosTime)
|
||||
lastAosDate = pass.aosTime
|
||||
passes.add(pass)
|
||||
startDate = Date(pass.losTime + (quarterOrbitMin * 3) * 60L * 1000L)
|
||||
startDate = pass.losTime + (quarterOrbitMin * 3) * 60L * 1000L
|
||||
count++
|
||||
} while (lastAosDate < endDate)
|
||||
}
|
||||
|
@ -108,53 +107,50 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
return passes
|
||||
}
|
||||
|
||||
private fun List<SatPass>.filter(date: Date, hoursAhead: Int, minElev: Double): List<SatPass> {
|
||||
val timeFuture = date.time + (hoursAhead * 60L * 60L * 1000L)
|
||||
return this.filter { it.losTime > date.time }
|
||||
private fun List<SatPass>.filter(time: Long, hoursAhead: Int, minElev: Double): List<SatPass> {
|
||||
val timeFuture = time + (hoursAhead * 60L * 60L * 1000L)
|
||||
return this.filter { it.losTime > time }
|
||||
.filter { it.aosTime < timeFuture }
|
||||
.filter { it.maxElevation > minElev }
|
||||
.sortedBy { it.aosTime }
|
||||
}
|
||||
|
||||
private fun getGeoPass(sat: Satellite, pos: GeoPos, date: Date): SatPass {
|
||||
val satPos = sat.getPosition(pos, date.time)
|
||||
val aos = Date(date.time - 24 * 60L * 60L * 1000L).time
|
||||
val los = Date(date.time + 24 * 60L * 60L * 1000L).time
|
||||
val tca = Date((aos + los) / 2).time
|
||||
private fun getGeoPass(sat: Satellite, pos: GeoPos, time: Long): SatPass {
|
||||
val satPos = sat.getPosition(pos, time)
|
||||
val aos = time - 24 * 60L * 60L * 1000L
|
||||
val los = time + 24 * 60L * 60L * 1000L
|
||||
val tca = (aos + los) / 2
|
||||
val az = Math.toDegrees(satPos.azimuth)
|
||||
val elev = Math.toDegrees(satPos.elevation)
|
||||
val alt = satPos.altitude
|
||||
return SatPass(aos, az, los, az, tca, az, alt, elev, sat)
|
||||
}
|
||||
|
||||
private fun getLeoPass(sat: Satellite, pos: GeoPos, date: Date, rewind: Boolean): SatPass {
|
||||
private fun getLeoPass(sat: Satellite, pos: GeoPos, time: Long, rewind: Boolean): SatPass {
|
||||
val quarterOrbitMin = (sat.orbitalPeriod / 4.0).toInt()
|
||||
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply {
|
||||
clear()
|
||||
timeInMillis = date.time
|
||||
}
|
||||
var calendarTimeMillis = time
|
||||
var elevation: Double
|
||||
var maxElevation = 0.0
|
||||
var alt = 0.0
|
||||
var tcaAz = 0.0
|
||||
// rewind 1/4 of an orbit
|
||||
if (rewind) calendar.add(Calendar.MINUTE, -quarterOrbitMin)
|
||||
if (rewind) calendarTimeMillis += -quarterOrbitMin * 60L * 1000L
|
||||
|
||||
var satPos = sat.getPosition(pos, calendar.time.time)
|
||||
var satPos = sat.getPosition(pos, calendarTimeMillis)
|
||||
if (satPos.elevation > 0.0) {
|
||||
// move forward in 30 second intervals until the sat goes below the horizon
|
||||
do {
|
||||
calendar.add(Calendar.SECOND, 30)
|
||||
satPos = sat.getPosition(pos, calendar.time.time)
|
||||
calendarTimeMillis += 30 * 1000L
|
||||
satPos = sat.getPosition(pos, calendarTimeMillis)
|
||||
} while (satPos.elevation > 0.0)
|
||||
// move forward 3/4 of an orbit
|
||||
calendar.add(Calendar.MINUTE, quarterOrbitMin * 3)
|
||||
calendarTimeMillis += quarterOrbitMin * 3 * 60L * 1000L
|
||||
}
|
||||
|
||||
// find the next time sat comes above the horizon
|
||||
do {
|
||||
calendar.add(Calendar.SECOND, 60)
|
||||
satPos = sat.getPosition(pos, calendar.time.time)
|
||||
calendarTimeMillis += 60L * 1000L
|
||||
satPos = sat.getPosition(pos, calendarTimeMillis)
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
|
@ -164,10 +160,10 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
} while (satPos.elevation < 0.0)
|
||||
|
||||
// refine to 3 seconds
|
||||
calendar.add(Calendar.SECOND, -60)
|
||||
calendarTimeMillis += -60L * 1000L
|
||||
do {
|
||||
calendar.add(Calendar.SECOND, 3)
|
||||
satPos = sat.getPosition(pos, calendar.time.time)
|
||||
calendarTimeMillis += 3L * 1000L
|
||||
satPos = sat.getPosition(pos, calendarTimeMillis)
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
|
@ -181,8 +177,8 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
|
||||
// find when sat goes below
|
||||
do {
|
||||
calendar.add(Calendar.SECOND, 30)
|
||||
satPos = sat.getPosition(pos, calendar.time.time)
|
||||
calendarTimeMillis += 30L * 1000L
|
||||
satPos = sat.getPosition(pos, calendarTimeMillis)
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
|
@ -192,10 +188,10 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
} while (satPos.elevation > 0.0)
|
||||
|
||||
// refine to 3 seconds
|
||||
calendar.add(Calendar.SECOND, -30)
|
||||
calendarTimeMillis += -30L * 1000L
|
||||
do {
|
||||
calendar.add(Calendar.SECOND, 3)
|
||||
satPos = sat.getPosition(pos, calendar.time.time)
|
||||
calendarTimeMillis += 3L * 1000L
|
||||
satPos = sat.getPosition(pos, calendarTimeMillis)
|
||||
elevation = satPos.elevation
|
||||
if (elevation > maxElevation) {
|
||||
maxElevation = elevation
|
||||
|
@ -206,7 +202,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
|
||||
val los = satPos.time
|
||||
val losAz = Math.toDegrees(satPos.azimuth)
|
||||
val tca = Date((aos + los) / 2).time
|
||||
val tca = (aos + los) / 2
|
||||
val elev = Math.toDegrees(maxElevation)
|
||||
return SatPass(aos, aosAz, los, losAz, tca, tcaAz, alt, elev, sat)
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.math.*
|
||||
|
||||
abstract class Satellite(val params: TLE) {
|
||||
|
@ -30,6 +28,7 @@ abstract class Satellite(val params: TLE) {
|
|||
private val epsilon = 1.0E-12
|
||||
private val position = Vector4()
|
||||
private val velocity = Vector4()
|
||||
private var gsPosTheta = 0.0
|
||||
private var perigee = 0.0
|
||||
val orbitalPeriod = 24 * 60 / params.meanmo
|
||||
val earthRadius = 6378.137
|
||||
|
@ -74,10 +73,7 @@ abstract class Satellite(val params: TLE) {
|
|||
|
||||
// Read the system clock and return the number of days since 31Dec79 00:00:00 UTC (daynum 0)
|
||||
private fun calcCurrentDaynum(now: Long): Double {
|
||||
val sgp4Epoch = Calendar.getInstance(TimeZone.getTimeZone("UTC:UTC"))
|
||||
sgp4Epoch.clear()
|
||||
sgp4Epoch[1979, 11, 31, 0, 0] = 0
|
||||
val then = sgp4Epoch.timeInMillis
|
||||
val then = 315446400000 // time in millis on 31Dec79 00:00:00 UTC (daynum 0)
|
||||
val millis = now - then
|
||||
return millis / 1000.0 / 60.0 / 60.0 / 24.0
|
||||
}
|
||||
|
@ -124,8 +120,7 @@ abstract class Satellite(val params: TLE) {
|
|||
val obsVel = Vector4()
|
||||
val range = Vector4()
|
||||
val rgvel = Vector4()
|
||||
val gsPosTheta = AtomicReference<Double>()
|
||||
calculateUserPosVel(julianUTC, gsPos, gsPosTheta, obsPos, obsVel)
|
||||
calculateUserPosVel(julianUTC, gsPos, obsPos, obsVel)
|
||||
range.setXYZ(
|
||||
positionVector.x - obsPos.x,
|
||||
positionVector.y - obsPos.y,
|
||||
|
@ -141,8 +136,8 @@ abstract class Satellite(val params: TLE) {
|
|||
magnitude(range)
|
||||
val sinLat = sin(deg2Rad * gsPos.latitude)
|
||||
val cosLat = cos(deg2Rad * gsPos.latitude)
|
||||
val sinTheta = sin(gsPosTheta.get())
|
||||
val cosTheta = cos(gsPosTheta.get())
|
||||
val sinTheta = sin(gsPosTheta)
|
||||
val cosTheta = cos(gsPosTheta)
|
||||
val topS = sinLat * cosTheta * range.x + sinLat * sinTheta * range.y - cosLat * range.z
|
||||
val topE = -sinTheta * range.x + cosTheta * range.y
|
||||
val topZ = cosLat * cosTheta * range.x + cosLat * sinTheta * range.y + sinLat * range.z
|
||||
|
@ -159,18 +154,17 @@ abstract class Satellite(val params: TLE) {
|
|||
private fun calculateUserPosVel(
|
||||
time: Double,
|
||||
gsPos: GeoPos,
|
||||
gsPosTheta: AtomicReference<Double>,
|
||||
obsPos: Vector4,
|
||||
obsVel: Vector4
|
||||
) {
|
||||
val mFactor = 7.292115E-5
|
||||
gsPosTheta.set(mod2PI(thetaGJD(time) + deg2Rad * gsPos.longitude))
|
||||
gsPosTheta = mod2PI(thetaGJD(time) + deg2Rad * gsPos.longitude)
|
||||
val c =
|
||||
invert(sqrt(1.0 + flatFactor * (flatFactor - 2) * sqr(sin(deg2Rad * gsPos.latitude))))
|
||||
val sq = sqr(1.0 - flatFactor) * c
|
||||
val achcp = (earthRadius * c) * cos(deg2Rad * gsPos.latitude)
|
||||
obsPos.setXYZ(
|
||||
achcp * cos(gsPosTheta.get()), achcp * sin(gsPosTheta.get()),
|
||||
achcp * cos(gsPosTheta), achcp * sin(gsPosTheta),
|
||||
(earthRadius * sq) * sin(deg2Rad * gsPos.latitude)
|
||||
)
|
||||
obsVel.setXYZ(-mFactor * obsPos.y, mFactor * obsPos.x, 0.0)
|
||||
|
|
Ładowanie…
Reference in New Issue