mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Slightly changed classes structure for readability
This commit is contained in:
1 parent
19d0c1b108
commit
549a915b24
40 files changed
+309
-381
No files matched your search
@@ -35,7 +35,7 @@ class MainApplication : Application() {
|
||||
enableStrictMode()
|
||||
super.onCreate()
|
||||
container = MainContainer(this)
|
||||
container.mainScope.launch { checkAutoUpdate() }
|
||||
container.appScope.launch { checkAutoUpdate() }
|
||||
}
|
||||
|
||||
private suspend fun checkAutoUpdate() {
|
||||
|
||||
@@ -6,25 +6,23 @@ import android.hardware.Sensor
|
||||
import android.hardware.SensorManager
|
||||
import android.location.LocationManager
|
||||
import androidx.room.Room
|
||||
import com.rtbishop.look4sat.data.database.MainDatabase
|
||||
import com.rtbishop.look4sat.data.database.Look4SatDb
|
||||
import com.rtbishop.look4sat.data.framework.BluetoothReporter
|
||||
import com.rtbishop.look4sat.data.framework.NetworkReporter
|
||||
import com.rtbishop.look4sat.data.repository.DatabaseRepo
|
||||
import com.rtbishop.look4sat.data.repository.SatelliteRepo
|
||||
import com.rtbishop.look4sat.data.repository.SelectionRepo
|
||||
import com.rtbishop.look4sat.data.repository.SensorsRepo
|
||||
import com.rtbishop.look4sat.data.repository.SettingsRepo
|
||||
import com.rtbishop.look4sat.data.source.FileSource
|
||||
import com.rtbishop.look4sat.data.source.LocalSource
|
||||
import com.rtbishop.look4sat.data.source.RemoteSource
|
||||
import com.rtbishop.look4sat.data.source.SensorSource
|
||||
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISelectionRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.source.IFileSource
|
||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||
import com.rtbishop.look4sat.domain.source.IRemoteSource
|
||||
import com.rtbishop.look4sat.domain.source.ISensorSource
|
||||
import com.rtbishop.look4sat.domain.utility.DataParser
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -37,7 +35,8 @@ import org.osmdroid.config.Configuration
|
||||
class MainContainer(private val context: Context) {
|
||||
|
||||
private val localSource = provideLocalSource()
|
||||
val mainScope = provideMainScope()
|
||||
private val mainHandler = CoroutineExceptionHandler { _, error -> println("MainHandler: $error") }
|
||||
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + mainHandler)
|
||||
val settingsRepo = provideSettingsRepo()
|
||||
val selectionRepo = provideSelectionRepo()
|
||||
val satelliteRepo = provideSatelliteRepo()
|
||||
@@ -52,40 +51,30 @@ class MainContainer(private val context: Context) {
|
||||
return NetworkReporter(CoroutineScope(Dispatchers.IO))
|
||||
}
|
||||
|
||||
fun provideSensorSource(): ISensorSource {
|
||||
fun provideSensorsRepo(): ISensorsRepo {
|
||||
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
|
||||
return SensorSource(manager, sensor)
|
||||
return SensorsRepo(manager, sensor)
|
||||
}
|
||||
|
||||
private fun provideDatabaseRepo(): IDatabaseRepo {
|
||||
val dataParser = DataParser(Dispatchers.Default)
|
||||
val fileSource = provideFileSource()
|
||||
val remoteSource = provideRemoteSource()
|
||||
return DatabaseRepo(Dispatchers.Default, dataParser, fileSource, localSource, remoteSource, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideFileSource(): IFileSource {
|
||||
return FileSource(context.contentResolver, Dispatchers.IO)
|
||||
return DatabaseRepo(Dispatchers.Default, dataParser, localSource, remoteSource, settingsRepo)
|
||||
}
|
||||
|
||||
private fun provideLocalSource(): ILocalSource {
|
||||
val database = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDBv313").apply {
|
||||
val database = Room.databaseBuilder(context, Look4SatDb::class.java, "Look4SatDBv313").apply {
|
||||
// addMigrations(MIGRATION_1_2)
|
||||
fallbackToDestructiveMigration()
|
||||
}.build()
|
||||
return LocalSource(database.storageDao())
|
||||
}
|
||||
|
||||
private fun provideMainScope(): CoroutineScope {
|
||||
val mainHandler = CoroutineExceptionHandler { _, error -> println("Look4Sat: $error") }
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.Default + mainHandler)
|
||||
return LocalSource(database.look4SatDao())
|
||||
}
|
||||
|
||||
private fun provideRemoteSource(): IRemoteSource {
|
||||
val cache = Cache(context.cacheDir, MainApplication.MAX_OKHTTP_CACHE_SIZE)
|
||||
val httpClient = OkHttpClient.Builder().cache(cache).build()
|
||||
return RemoteSource(httpClient, Dispatchers.IO)
|
||||
return RemoteSource(context.contentResolver, httpClient, Dispatchers.IO)
|
||||
}
|
||||
|
||||
private fun provideSatelliteRepo(): ISatelliteRepo {
|
||||
|
||||
@@ -29,8 +29,8 @@ import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import org.osmdroid.tileprovider.tilesource.XYTileSource
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.CustomZoomButtonsController
|
||||
@@ -69,7 +69,7 @@ fun MapScreen() {
|
||||
val positions = viewModel.positions.collectAsState(initial = null)
|
||||
val satTrack = viewModel.track.collectAsState(initial = null)
|
||||
val footprint = viewModel.footprint.collectAsState(initial = null)
|
||||
val positionClick = { satellite: Satellite -> viewModel.selectSatellite(satellite) }
|
||||
val positionClick = { orbitalObject: OrbitalObject -> viewModel.selectSatellite(orbitalObject) }
|
||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||
MapView(modifier = Modifier.fillMaxSize()) { mapView ->
|
||||
@@ -97,7 +97,7 @@ private fun setStationPosition(stationPos: GeoPos, mapView: MapView) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPositions(posMap: Map<Satellite, GeoPos>, mapView: MapView, action: (Satellite) -> Unit) {
|
||||
private fun setPositions(posMap: Map<OrbitalObject, GeoPos>, mapView: MapView, action: (OrbitalObject) -> Unit) {
|
||||
val markers = FolderOverlay()
|
||||
try {
|
||||
posMap.entries.forEach {
|
||||
@@ -156,8 +156,8 @@ private fun setSatelliteTrack(satTrack: List<List<GeoPos>>, mapView: MapView) {
|
||||
// mapView.controller.animateTo(center)
|
||||
}
|
||||
|
||||
private fun setFootprint(satPos: SatPos, mapView: MapView) {
|
||||
val footprintPoints = satPos.getRangeCircle().map { GeoPoint(it.latitude, it.longitude) }
|
||||
private fun setFootprint(orbitalPos: OrbitalPos, mapView: MapView) {
|
||||
val footprintPoints = orbitalPos.getRangeCircle().map { GeoPoint(it.latitude, it.longitude) }
|
||||
try {
|
||||
val footprintOverlay = Polyline().apply {
|
||||
outlinePaint.set(footprintPaint)
|
||||
|
||||
@@ -24,8 +24,8 @@ import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.rtbishop.look4sat.MainApplication
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.utility.clipLat
|
||||
@@ -51,7 +51,7 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
private var allSatellites = satelliteRepo.satellites.value
|
||||
private var dataUpdateJob: Job? = null
|
||||
private var dataUpdateRate = 1000L
|
||||
private var selectedSatellite: Satellite? = null
|
||||
private var selectedOrbitalObject: OrbitalObject? = null
|
||||
|
||||
val stationPosition = flow {
|
||||
val osmLat = clipLat(stationPos.latitude)
|
||||
@@ -62,14 +62,14 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
private val _track = MutableSharedFlow<List<List<GeoPos>>>()
|
||||
val track: SharedFlow<List<List<GeoPos>>> = _track
|
||||
|
||||
private val _footprint = MutableSharedFlow<SatPos>()
|
||||
val footprint: SharedFlow<SatPos> = _footprint
|
||||
private val _footprint = MutableSharedFlow<OrbitalPos>()
|
||||
val footprint: SharedFlow<OrbitalPos> = _footprint
|
||||
|
||||
private val _mapData = MutableSharedFlow<MapData>()
|
||||
// val mapData: SharedFlow<MapData> = _mapData
|
||||
|
||||
private val _positions = MutableSharedFlow<Map<Satellite, GeoPos>>()
|
||||
val positions: SharedFlow<Map<Satellite, GeoPos>> = _positions
|
||||
private val _positions = MutableSharedFlow<Map<OrbitalObject, GeoPos>>()
|
||||
val positions: SharedFlow<Map<OrbitalObject, GeoPos>> = _positions
|
||||
|
||||
// fun scrollSelection(decrement: Boolean) {
|
||||
// if (allSatellites.isNotEmpty()) {
|
||||
@@ -88,37 +88,37 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
if (allSatellites.isNotEmpty()) {
|
||||
if (catnum == -1) {
|
||||
allPasses.find { pass -> pass.progress < 100 && !pass.isDeepSpace }
|
||||
?.let { pass -> selectSatellite(pass.satellite) }
|
||||
?.let { pass -> selectSatellite(pass.orbitalObject) }
|
||||
} else {
|
||||
allSatellites.find { it.data.catnum == catnum }?.let { selectSatellite(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectSatellite(satellite: Satellite, updateFreq: Long = dataUpdateRate) {
|
||||
selectedSatellite = satellite
|
||||
fun selectSatellite(orbitalObject: OrbitalObject, updateFreq: Long = dataUpdateRate) {
|
||||
selectedOrbitalObject = orbitalObject
|
||||
viewModelScope.launch {
|
||||
dataUpdateJob?.cancelAndJoin()
|
||||
dataUpdateJob = launch {
|
||||
val dateNow = Date()
|
||||
getSatTrack(satellite, stationPos, dateNow)
|
||||
getSatTrack(orbitalObject, stationPos, dateNow)
|
||||
while (isActive) {
|
||||
dateNow.time = System.currentTimeMillis()
|
||||
getPositions(allSatellites, stationPos, dateNow)
|
||||
getSatFootprint(satellite, stationPos, dateNow)
|
||||
getSatData(satellite, stationPos, dateNow)
|
||||
getSatFootprint(orbitalObject, stationPos, dateNow)
|
||||
getSatData(orbitalObject, stationPos, dateNow)
|
||||
delay(updateFreq)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSatTrack(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||
private suspend fun getSatTrack(orbitalObject: OrbitalObject, pos: GeoPos, date: Date) {
|
||||
val satTracks = mutableListOf<List<GeoPos>>()
|
||||
val currentTrack = mutableListOf<GeoPos>()
|
||||
val endDate = Date(date.time + (satellite.data.orbitalPeriod * 2.4 * 60000L).toLong())
|
||||
val endDate = Date(date.time + (orbitalObject.data.orbitalPeriod * 2.4 * 60000L).toLong())
|
||||
var oldLongitude = 0.0
|
||||
satelliteRepo.getTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
|
||||
satelliteRepo.getTrack(orbitalObject, pos, date.time, endDate.time).forEach { satPos ->
|
||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||
val currentPosition = GeoPos(osmLat, osmLon)
|
||||
@@ -142,9 +142,9 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
_track.emit(satTracks)
|
||||
}
|
||||
|
||||
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
||||
val positions = mutableMapOf<Satellite, GeoPos>()
|
||||
satellites.forEach { satellite ->
|
||||
private suspend fun getPositions(orbitalObjects: List<OrbitalObject>, pos: GeoPos, date: Date) {
|
||||
val positions = mutableMapOf<OrbitalObject, GeoPos>()
|
||||
orbitalObjects.forEach { satellite ->
|
||||
val satPos = satelliteRepo.getPosition(satellite, pos, date.time)
|
||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||
@@ -153,12 +153,12 @@ class MapViewModel(private val satelliteRepo: ISatelliteRepo, settingsRepo: ISet
|
||||
_positions.emit(positions)
|
||||
}
|
||||
|
||||
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||
val satPos = satelliteRepo.getPosition(satellite, pos, date.time)
|
||||
private suspend fun getSatFootprint(orbitalObject: OrbitalObject, pos: GeoPos, date: Date) {
|
||||
val satPos = satelliteRepo.getPosition(orbitalObject, pos, date.time)
|
||||
_footprint.emit(satPos)
|
||||
}
|
||||
|
||||
private suspend fun getSatData(sat: Satellite, pos: GeoPos, date: Date) {
|
||||
private suspend fun getSatData(sat: OrbitalObject, pos: GeoPos, date: Date) {
|
||||
var aosTime = 0L.toTimerString()
|
||||
allPasses.find { pass -> pass.catNum == sat.data.catnum && pass.progress < 100 }
|
||||
?.let { satPass ->
|
||||
|
||||
@@ -36,9 +36,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.predict.NearEarthSatellite
|
||||
import com.rtbishop.look4sat.domain.predict.NearEarthObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.presentation.MainTheme
|
||||
import com.rtbishop.look4sat.presentation.components.PullRefreshIndicator
|
||||
import com.rtbishop.look4sat.presentation.components.PullRefreshState
|
||||
@@ -114,13 +114,13 @@ private fun PassPreview() {
|
||||
val data = OrbitalData(
|
||||
"Satellite", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 45000, 0.0
|
||||
)
|
||||
val satellite = NearEarthSatellite(data)
|
||||
val pass = SatPass(1L, 0.0, 10L, 180.0, 850, 45.0, satellite, 0.5f)
|
||||
val satellite = NearEarthObject(data)
|
||||
val pass = OrbitalPass(1L, 0.0, 10L, 180.0, 850, 45.0, satellite, 0.5f)
|
||||
MainTheme { Pass(pass = pass, { _, _ -> }) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Pass(pass: SatPass, navToRadar: (Int, Long) -> Unit, modifier: Modifier = Modifier) {
|
||||
private fun Pass(pass: OrbitalPass, navToRadar: (Int, Long) -> Unit, modifier: Modifier = Modifier) {
|
||||
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
|
||||
Surface(modifier = Modifier
|
||||
.padding(bottom = 2.dp)
|
||||
@@ -207,7 +207,7 @@ private fun Pass(pass: SatPass, navToRadar: (Int, Long) -> Unit, modifier: Modif
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun PassesCard(
|
||||
refreshState: PullRefreshState, isRefreshing: Boolean, passes: List<SatPass>, navToRadar: (Int, Long) -> Unit
|
||||
refreshState: PullRefreshState, isRefreshing: Boolean, passes: List<OrbitalPass>, navToRadar: (Int, Long) -> Unit
|
||||
) {
|
||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||
Box(Modifier.pullRefresh(refreshState)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.rtbishop.look4sat.presentation.passes
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
|
||||
data class PassesState(
|
||||
val isDialogShown: Boolean,
|
||||
@@ -12,7 +12,7 @@ data class PassesState(
|
||||
val hours: Int,
|
||||
val elevation: Double,
|
||||
val modes: List<String>,
|
||||
val itemsList: List<SatPass>,
|
||||
val itemsList: List<OrbitalPass>,
|
||||
val takeAction: (PassesAction) -> Unit
|
||||
)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.rtbishop.look4sat.MainApplication
|
||||
import com.rtbishop.look4sat.domain.model.PassesSettings
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.utility.toTimerString
|
||||
@@ -99,7 +99,7 @@ class PassesViewModel(
|
||||
satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes)
|
||||
}
|
||||
|
||||
private fun setPassInfo(passes: List<SatPass>, timeNow: Long) {
|
||||
private fun setPassInfo(passes: List<OrbitalPass>, timeNow: Long) {
|
||||
if (passes.isEmpty()) return
|
||||
try {
|
||||
val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 }
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.radar
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
|
||||
data class RadarData(val satPos: SatPos, val satTrack: List<SatPos> = emptyList())
|
||||
data class RadarData(val orbitalPos: OrbitalPos, val satTrack: List<OrbitalPos> = emptyList())
|
||||
@@ -69,7 +69,7 @@ fun RadarScreen() {
|
||||
) {
|
||||
viewModel.radarData.value?.let { data ->
|
||||
RadarViewCompose(
|
||||
item = data.satPos,
|
||||
item = data.orbitalPos,
|
||||
items = data.satTrack,
|
||||
azimElev = viewModel.orientation.value
|
||||
)
|
||||
|
||||
@@ -29,14 +29,14 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.drawText
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.domain.predict.PI_2
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.utility.toRadians
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
@Composable
|
||||
fun RadarViewCompose(item: SatPos, items: List<SatPos>, azimElev: Pair<Float, Float>) {
|
||||
fun RadarViewCompose(item: OrbitalPos, items: List<OrbitalPos>, azimElev: Pair<Float, Float>) {
|
||||
val radarColor = Color(0xFFDCDCDC)
|
||||
val primaryColor = Color(0xFFFFE082)
|
||||
val secondaryColor = Color(0xFFDC0000)
|
||||
@@ -92,7 +92,7 @@ private fun DrawScope.drawTrack(path: Path, effect: PathEffect, color: Color, ef
|
||||
drawPath(path, effectColor, style = Stroke(pathEffect = effect))
|
||||
}
|
||||
|
||||
private fun DrawScope.drawPosition(item: SatPos, radius: Float, posRadius: Float, color: Color) {
|
||||
private fun DrawScope.drawPosition(item: OrbitalPos, radius: Float, posRadius: Float, color: Color) {
|
||||
val satX = sph2CartX(item.azimuth, item.elevation, radius.toDouble())
|
||||
val satY = sph2CartY(item.azimuth, item.elevation, radius.toDouble())
|
||||
drawCircle(color, 16f, center.copy(satX, -satY))
|
||||
@@ -122,7 +122,7 @@ private fun DrawScope.drawSweep(center: Offset, degrees: Float, radius: Float, c
|
||||
rotate(-90 + degrees, center) { drawCircle(brush, radius, style = Fill) }
|
||||
}
|
||||
|
||||
private fun createTrackPath(positions: List<SatPos>, radius: Float): Path {
|
||||
private fun createTrackPath(positions: List<OrbitalPos>, radius: Float): Path {
|
||||
val trackPath = Path()
|
||||
positions.forEachIndexed { index, satPos ->
|
||||
val passX = sph2CartX(satPos.azimuth, satPos.elevation, radius.toDouble())
|
||||
|
||||
@@ -32,12 +32,12 @@ import com.rtbishop.look4sat.data.framework.BluetoothReporter
|
||||
import com.rtbishop.look4sat.data.framework.NetworkReporter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.source.ISensorSource
|
||||
import com.rtbishop.look4sat.domain.utility.round
|
||||
import com.rtbishop.look4sat.domain.utility.toDegrees
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -51,20 +51,20 @@ class RadarViewModel(
|
||||
private val networkReporter: NetworkReporter,
|
||||
private val satelliteRepo: ISatelliteRepo,
|
||||
private val settingsRepo: ISettingsRepo,
|
||||
private val sensorSource: ISensorSource
|
||||
private val sensorsRepo: ISensorsRepo
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPos = settingsRepo.stationPosition.value
|
||||
private val magDeclination = getMagDeclination(stationPos)
|
||||
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
|
||||
val radarData = mutableStateOf<RadarData?>(null)
|
||||
val orientation = mutableStateOf(sensorSource.orientation.value)
|
||||
val orientation = mutableStateOf(sensorsRepo.orientation.value)
|
||||
|
||||
init {
|
||||
if (settingsRepo.otherSettings.value.stateOfSensors) {
|
||||
viewModelScope.launch {
|
||||
sensorSource.enableSensor()
|
||||
sensorSource.orientation.collect { data ->
|
||||
sensorsRepo.enableSensor()
|
||||
sensorsRepo.orientation.collect { data ->
|
||||
orientation.value = Pair(data.first + magDeclination, data.second)
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class RadarViewModel(
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
sensorSource.disableSensor()
|
||||
sensorsRepo.disableSensor()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
@@ -89,10 +89,10 @@ class RadarViewModel(
|
||||
while (isActive) {
|
||||
val timeNow = System.currentTimeMillis()
|
||||
val satPos =
|
||||
satelliteRepo.getPosition(satPass.satellite, stationPos, timeNow)
|
||||
sendPassData(satPass, satPos, satPass.satellite)
|
||||
satelliteRepo.getPosition(satPass.orbitalObject, stationPos, timeNow)
|
||||
sendPassData(satPass, satPos, satPass.orbitalObject)
|
||||
sendPassDataBT(satPos)
|
||||
processRadios(transmitters, satPass.satellite, timeNow)
|
||||
processRadios(transmitters, satPass.orbitalObject, timeNow)
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
@@ -109,33 +109,33 @@ class RadarViewModel(
|
||||
return GeomagneticField(latitude, longitude, geoPos.altitude.toFloat(), time).declination
|
||||
}
|
||||
|
||||
private fun sendPassData(satPass: SatPass, satPos: SatPos, satellite: Satellite) {
|
||||
private fun sendPassData(orbitalPass: OrbitalPass, orbitalPos: OrbitalPos, orbitalObject: OrbitalObject) {
|
||||
viewModelScope.launch {
|
||||
var track: List<SatPos> = emptyList()
|
||||
if (!satPass.isDeepSpace) {
|
||||
var track: List<OrbitalPos> = emptyList()
|
||||
if (!orbitalPass.isDeepSpace) {
|
||||
track = satelliteRepo.getTrack(
|
||||
satellite, stationPos, satPass.aosTime, satPass.losTime
|
||||
orbitalObject, stationPos, orbitalPass.aosTime, orbitalPass.losTime
|
||||
)
|
||||
}
|
||||
if (settingsRepo.getRotatorState()) {
|
||||
val server = settingsRepo.getRotatorAddress()
|
||||
val port = settingsRepo.getRotatorPort().toInt()
|
||||
val azimuth = satPos.azimuth.toDegrees().round(1)
|
||||
val elevation = satPos.elevation.toDegrees().round(1)
|
||||
val azimuth = orbitalPos.azimuth.toDegrees().round(1)
|
||||
val elevation = orbitalPos.elevation.toDegrees().round(1)
|
||||
networkReporter.reportRotation(server, port, azimuth, elevation)
|
||||
}
|
||||
radarData.value = RadarData(satPos, track)
|
||||
radarData.value = RadarData(orbitalPos, track)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendPassDataBT(satPos: SatPos) {
|
||||
private fun sendPassDataBT(orbitalPos: OrbitalPos) {
|
||||
viewModelScope.launch {
|
||||
if (settingsRepo.getBluetoothState()) {
|
||||
val btDevice = settingsRepo.getBluetoothAddress()
|
||||
if (bluetoothReporter.isConnected()) {
|
||||
val format = settingsRepo.getBluetoothFormat()
|
||||
val azimuth = satPos.azimuth.toDegrees().round(0).toInt()
|
||||
val elevation = satPos.elevation.toDegrees().round(0).toInt()
|
||||
val azimuth = orbitalPos.azimuth.toDegrees().round(0).toInt()
|
||||
val elevation = orbitalPos.elevation.toDegrees().round(0).toInt()
|
||||
bluetoothReporter.reportRotation(format, azimuth, elevation)
|
||||
} else if (!bluetoothReporter.isConnecting()) {
|
||||
Log.i("BTReporter", "BTReporter: Attempting to connect...")
|
||||
@@ -145,10 +145,10 @@ class RadarViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
|
||||
private fun processRadios(radios: List<SatRadio>, orbitalObject: OrbitalObject, time: Long) {
|
||||
viewModelScope.launch {
|
||||
delay(125)
|
||||
val list = satelliteRepo.getRadios(satellite, stationPos, radios, time)
|
||||
val list = satelliteRepo.getRadios(orbitalObject, stationPos, radios, time)
|
||||
transmitters.value = list
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ class RadarViewModel(
|
||||
container.provideNetworkReporter(),
|
||||
container.satelliteRepo,
|
||||
container.settingsRepo,
|
||||
container.provideSensorSource()
|
||||
container.provideSensorsRepo()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -15,7 +15,7 @@
|
||||
* 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.data.database.dao
|
||||
package com.rtbishop.look4sat.data.database
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
@@ -23,17 +23,16 @@ import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import com.rtbishop.look4sat.data.database.entity.SatEntry
|
||||
import com.rtbishop.look4sat.data.database.entity.SatItem
|
||||
import com.rtbishop.look4sat.data.database.entity.SatRadio
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
|
||||
@Dao
|
||||
interface StorageDao {
|
||||
interface Look4SatDao {
|
||||
|
||||
@Query("SELECT COUNT(*) FROM entries")
|
||||
suspend fun getEntriesTotal(): Int
|
||||
|
||||
//@Transaction
|
||||
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
|
||||
@Query("SELECT catnum, name, 0 as isSelected FROM entries ORDER BY name ASC")
|
||||
suspend fun getEntriesList(): List<SatItem>
|
||||
|
||||
@Transaction
|
||||
+2
-3
@@ -19,13 +19,12 @@ package com.rtbishop.look4sat.data.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.rtbishop.look4sat.data.database.dao.StorageDao
|
||||
import com.rtbishop.look4sat.data.database.entity.SatEntry
|
||||
import com.rtbishop.look4sat.data.database.entity.SatRadio
|
||||
|
||||
@Database(entities = [SatEntry::class, SatRadio::class], version = 1, exportSchema = false)
|
||||
abstract class MainDatabase : RoomDatabase() {
|
||||
abstract fun storageDao(): StorageDao
|
||||
abstract class Look4SatDb : RoomDatabase() {
|
||||
abstract fun look4SatDao(): Look4SatDao
|
||||
}
|
||||
|
||||
//val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
@@ -17,9 +17,18 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.data.database.entity
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
|
||||
@Entity(tableName = "entries", primaryKeys = ["catnum"])
|
||||
data class SatEntry(@Embedded val data: OrbitalData, var comment: String? = null)
|
||||
data class SatEntry(
|
||||
val name: String,
|
||||
val epoch: Double,
|
||||
val meanmo: Double,
|
||||
val eccn: Double,
|
||||
val incl: Double,
|
||||
val raan: Double,
|
||||
val argper: Double,
|
||||
val meanan: Double,
|
||||
val catnum: Int,
|
||||
val bstar: Double
|
||||
)
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.data.database.entity
|
||||
|
||||
data class SatItem(val catnum: Int, val name: String)
|
||||
@@ -32,6 +32,5 @@ data class SatRadio(
|
||||
var uplinkHigh: Long?,
|
||||
val uplinkMode: String?,
|
||||
val isInverted: Boolean,
|
||||
val catnum: Int?,
|
||||
var comment: String? = null
|
||||
val catnum: Int?
|
||||
)
|
||||
@@ -18,15 +18,14 @@
|
||||
package com.rtbishop.look4sat.data.repository
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.DatabaseState
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.domain.repository.IDatabaseRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.source.IFileSource
|
||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||
import com.rtbishop.look4sat.domain.source.IRemoteSource
|
||||
import com.rtbishop.look4sat.domain.source.Sources
|
||||
import com.rtbishop.look4sat.domain.utility.DataParser
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.async
|
||||
@@ -35,32 +34,31 @@ import kotlinx.coroutines.withContext
|
||||
class DatabaseRepo(
|
||||
private val dispatcher: CoroutineDispatcher,
|
||||
private val dataParser: DataParser,
|
||||
private val fileSource: IFileSource,
|
||||
private val localSource: ILocalSource,
|
||||
private val remoteSource: IRemoteSource,
|
||||
private val settingsRepo: ISettingsRepo
|
||||
) : IDatabaseRepo {
|
||||
|
||||
override suspend fun updateFromFile(uri: String) = withContext(dispatcher) {
|
||||
val importedSatellites = fileSource.getFileStream(uri)?.let { importSatellites(it) }
|
||||
val importedSatellites = remoteSource.getFileStream(uri)?.let { dataParser.parseTLEStream(it) }
|
||||
importedSatellites?.let { localSource.insertEntries(it) }
|
||||
setUpdateSuccessful(System.currentTimeMillis())
|
||||
}
|
||||
|
||||
override suspend fun updateFromRemote() = withContext(dispatcher) {
|
||||
val importedEntries = mutableListOf<SatEntry>()
|
||||
val importedEntries = mutableListOf<OrbitalData>()
|
||||
val importedRadios = mutableListOf<SatRadio>()
|
||||
// fetch
|
||||
val jobsMap = settingsRepo.satelliteSourcesMap.mapValues { async { remoteSource.getRemoteStream(it.value) } }
|
||||
val jobRadios = async { remoteSource.getRemoteStream(settingsRepo.radioSourceUrl) }
|
||||
val jobsMap = Sources.satelliteDataUrls.mapValues { async { remoteSource.getNetworkStream(it.value) } }
|
||||
val jobRadios = async { remoteSource.getNetworkStream(Sources.radioDataUrl) }
|
||||
// parse
|
||||
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
|
||||
entry.value?.let { stream ->
|
||||
when (val type = entry.key) {
|
||||
"Amsat", "R4UAB" -> {
|
||||
// parse tle stream
|
||||
val satellites = importSatellites(stream)
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
val satellites = dataParser.parseTLEStream(stream)
|
||||
val catnums = satellites.map { it.catnum }
|
||||
settingsRepo.setSatelliteTypeIds(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
@@ -68,17 +66,16 @@ class DatabaseRepo(
|
||||
"McCants", "Classified" -> {
|
||||
// unzip and parse tle stream
|
||||
val unzipped = ZipInputStream(stream).apply { nextEntry }
|
||||
val satellites = importSatellites(unzipped)
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
val satellites = dataParser.parseTLEStream(unzipped)
|
||||
val catnums = satellites.map { it.catnum }
|
||||
settingsRepo.setSatelliteTypeIds(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// parse csv stream
|
||||
val parsed = dataParser.parseCSVStream(stream)
|
||||
val satellites = parsed.map { data -> SatEntry(data) }
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
val satellites = dataParser.parseCSVStream(stream)
|
||||
val catnums = satellites.map { it.catnum }
|
||||
settingsRepo.setSatelliteTypeIds(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
@@ -103,8 +100,4 @@ class DatabaseRepo(
|
||||
val numberOfSatellites = localSource.getEntriesTotal()
|
||||
settingsRepo.updateDatabaseState(DatabaseState(numberOfRadios, numberOfSatellites, timestamp))
|
||||
}
|
||||
|
||||
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
||||
return dataParser.parseTLEStream(stream).map { data -> SatEntry(data) }
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,9 @@ package com.rtbishop.look4sat.data.repository
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import com.rtbishop.look4sat.domain.repository.ISatelliteRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||
@@ -39,11 +39,11 @@ class SatelliteRepo(
|
||||
private val settingsRepo: ISettingsRepo
|
||||
) : ISatelliteRepo {
|
||||
|
||||
private val _passes = MutableStateFlow<List<SatPass>>(emptyList())
|
||||
override val passes: StateFlow<List<SatPass>> = _passes
|
||||
private val _passes = MutableStateFlow<List<OrbitalPass>>(emptyList())
|
||||
override val passes: StateFlow<List<OrbitalPass>> = _passes
|
||||
|
||||
private val _satellites = MutableStateFlow<List<Satellite>>(emptyList())
|
||||
override val satellites: StateFlow<List<Satellite>> = _satellites
|
||||
private val _satellites = MutableStateFlow<List<OrbitalObject>>(emptyList())
|
||||
override val satellites: StateFlow<List<OrbitalObject>> = _satellites
|
||||
|
||||
override suspend fun getRadiosWithId(id: Int) = localStorage.getRadiosWithId(id)
|
||||
|
||||
@@ -55,13 +55,13 @@ class SatelliteRepo(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos {
|
||||
override suspend fun getPosition(sat: OrbitalObject, pos: GeoPos, time: Long): OrbitalPos {
|
||||
return withContext(dispatcher) { sat.getPosition(pos, time) }
|
||||
}
|
||||
|
||||
override suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos> {
|
||||
override suspend fun getTrack(sat: OrbitalObject, pos: GeoPos, start: Long, end: Long): List<OrbitalPos> {
|
||||
return withContext(dispatcher) {
|
||||
val positions = mutableListOf<SatPos>()
|
||||
val positions = mutableListOf<OrbitalPos>()
|
||||
var currentTime = start
|
||||
while (currentTime < end) {
|
||||
positions.add(sat.getPosition(pos, currentTime))
|
||||
@@ -71,7 +71,12 @@ class SatelliteRepo(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio> {
|
||||
override suspend fun getRadios(
|
||||
sat: OrbitalObject,
|
||||
pos: GeoPos,
|
||||
radios: List<SatRadio>,
|
||||
time: Long
|
||||
): List<SatRadio> {
|
||||
return withContext(dispatcher) {
|
||||
val satPos = sat.getPosition(pos, time)
|
||||
val copiedList = radios.map { it.copy() }
|
||||
@@ -83,7 +88,7 @@ class SatelliteRepo(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> {
|
||||
override suspend fun processPasses(passList: List<OrbitalPass>, time: Long): List<OrbitalPass> {
|
||||
return withContext(dispatcher) {
|
||||
passList.forEach { pass ->
|
||||
if (!pass.isDeepSpace) {
|
||||
@@ -102,7 +107,7 @@ class SatelliteRepo(
|
||||
override suspend fun calculatePasses(time: Long, hoursAhead: Int, minElevation: Double, modes: List<String>) {
|
||||
if (_satellites.value.isNotEmpty()) {
|
||||
withContext(dispatcher) {
|
||||
val newPasses = mutableListOf<SatPass>()
|
||||
val newPasses = mutableListOf<OrbitalPass>()
|
||||
val idsWithModes = localStorage.getIdsWithModes(modes)
|
||||
_satellites.value.forEach { satellite ->
|
||||
if (idsWithModes.isEmpty() || satellite.data.catnum in idsWithModes) {
|
||||
@@ -116,8 +121,8 @@ class SatelliteRepo(
|
||||
}
|
||||
}
|
||||
|
||||
private fun Satellite.getPasses(pos: GeoPos, time: Long, hours: Int): List<SatPass> {
|
||||
val passes = mutableListOf<SatPass>()
|
||||
private fun OrbitalObject.getPasses(pos: GeoPos, time: Long, hours: Int): List<OrbitalPass> {
|
||||
val passes = mutableListOf<OrbitalPass>()
|
||||
val endDate = time + hours * 60L * 60L * 1000L
|
||||
val quarterOrbitMin = (this.data.orbitalPeriod / 4.0).toInt()
|
||||
var startDate = time
|
||||
@@ -141,23 +146,23 @@ class SatelliteRepo(
|
||||
return passes
|
||||
}
|
||||
|
||||
private fun List<SatPass>.filter(time: Long, hoursAhead: Int, minElev: Double): List<SatPass> {
|
||||
private fun List<OrbitalPass>.filter(time: Long, hoursAhead: Int, minElev: Double): List<OrbitalPass> {
|
||||
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, time: Long): SatPass {
|
||||
private fun getGeoPass(sat: OrbitalObject, pos: GeoPos, time: Long): OrbitalPass {
|
||||
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 = satPos.azimuth.toDegrees().round(1)
|
||||
val elev = satPos.elevation.toDegrees().round(1)
|
||||
val alt = satPos.altitude
|
||||
return SatPass(aos, az, los, az, alt.toInt(), elev, sat)
|
||||
return OrbitalPass(aos, az, los, az, alt.toInt(), elev, sat)
|
||||
}
|
||||
|
||||
private fun getLeoPass(sat: Satellite, pos: GeoPos, time: Long, rewind: Boolean): SatPass {
|
||||
private fun getLeoPass(sat: OrbitalObject, pos: GeoPos, time: Long, rewind: Boolean): OrbitalPass {
|
||||
val quarterOrbitMin = (sat.data.orbitalPeriod / 4.0).toInt()
|
||||
var calendarTimeMillis = time
|
||||
var elevation: Double
|
||||
@@ -229,6 +234,6 @@ class SatelliteRepo(
|
||||
val los = satPos.time // val tca = (aos + los) / 2
|
||||
val losAz = satPos.azimuth.toDegrees().round(1)
|
||||
val elev = maxElevation.toDegrees().round(1)
|
||||
return SatPass(aos, aosAz, los, losAz, alt.toInt(), elev, sat)
|
||||
return OrbitalPass(aos, aosAz, los, losAz, alt.toInt(), elev, sat)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.repository.ISelectionRepo
|
||||
import com.rtbishop.look4sat.domain.repository.ISettingsRepo
|
||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||
import com.rtbishop.look4sat.domain.source.Sources
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -30,7 +31,7 @@ class SelectionRepo(
|
||||
|
||||
override fun getCurrentType() = currentType.value
|
||||
|
||||
override fun getTypesList() = settingsRepo.satelliteSourcesMap.keys.sorted()
|
||||
override fun getTypesList() = Sources.satelliteDataUrls.keys.sorted()
|
||||
|
||||
override suspend fun getEntriesFlow() = withContext(dispatcher) {
|
||||
val selectedIds = settingsRepo.selectedIds.value
|
||||
|
||||
+5
-5
@@ -15,20 +15,20 @@
|
||||
* 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.data.source
|
||||
package com.rtbishop.look4sat.data.repository
|
||||
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import com.rtbishop.look4sat.domain.predict.RAD2DEG
|
||||
import com.rtbishop.look4sat.domain.source.ISensorSource
|
||||
import com.rtbishop.look4sat.domain.repository.ISensorsRepo
|
||||
import kotlin.math.round
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlin.math.round
|
||||
|
||||
class SensorSource(private val sensorManager: SensorManager, private val sensor: Sensor?) :
|
||||
SensorEventListener, ISensorSource {
|
||||
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
|
||||
SensorEventListener, ISensorsRepo {
|
||||
|
||||
private val _orientation = MutableStateFlow(Pair(0f, 0f))
|
||||
private val rotationMatrix = FloatArray(9)
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.rtbishop.look4sat.data.source
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import com.rtbishop.look4sat.domain.source.IFileSource
|
||||
import java.io.InputStream
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FileSource(
|
||||
private val contentResolver: ContentResolver, private val dispatcher: CoroutineDispatcher
|
||||
) : IFileSource {
|
||||
override suspend fun getFileStream(uri: String): InputStream? {
|
||||
return withContext(dispatcher) { contentResolver.openInputStream(Uri.parse(uri)) }
|
||||
}
|
||||
}
|
||||
@@ -18,75 +18,75 @@
|
||||
package com.rtbishop.look4sat.data.source
|
||||
|
||||
import com.rtbishop.look4sat.data.database.entity.SatEntry as FrameworkEntry
|
||||
import com.rtbishop.look4sat.data.database.entity.SatItem as FrameworkItem
|
||||
import com.rtbishop.look4sat.data.database.entity.SatRadio as FrameworkRadio
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem as DomainItem
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio as DomainRadio
|
||||
import com.rtbishop.look4sat.data.database.dao.StorageDao
|
||||
import com.rtbishop.look4sat.data.database.Look4SatDao
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.source.ILocalSource
|
||||
|
||||
class LocalSource(private val storageDao: StorageDao) : ILocalSource {
|
||||
class LocalSource(private val look4SatDao: Look4SatDao) : ILocalSource {
|
||||
|
||||
//region # Entries region
|
||||
|
||||
override suspend fun getEntriesTotal() = storageDao.getEntriesTotal()
|
||||
override suspend fun getEntriesTotal() = look4SatDao.getEntriesTotal()
|
||||
|
||||
override suspend fun getEntriesList(): List<DomainItem> {
|
||||
return storageDao.getEntriesList().toDomainItems()
|
||||
}
|
||||
override suspend fun getEntriesList() = look4SatDao.getEntriesList()
|
||||
|
||||
override suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite> {
|
||||
val selectedSatellites = mutableListOf<Satellite>()
|
||||
override suspend fun getEntriesWithIds(ids: List<Int>): List<OrbitalObject> {
|
||||
val selectedOrbitalObjects = mutableListOf<OrbitalObject>()
|
||||
ids.chunked(999).forEach { idsPart ->
|
||||
val entries = storageDao.getEntriesWithIds(idsPart)
|
||||
selectedSatellites.addAll(entries.map { entry -> entry.data.getSatellite() })
|
||||
val entries = look4SatDao.getEntriesWithIds(idsPart)
|
||||
selectedOrbitalObjects.addAll(entries.toDomain().map { entry -> entry.getObject() })
|
||||
}
|
||||
return selectedSatellites
|
||||
return selectedOrbitalObjects
|
||||
}
|
||||
|
||||
override suspend fun insertEntries(entries: List<DomainEntry>) {
|
||||
storageDao.insertEntries(entries.toFrameworkEntries())
|
||||
}
|
||||
override suspend fun insertEntries(entries: List<OrbitalData>) = look4SatDao.insertEntries(entries.toEntity())
|
||||
|
||||
override suspend fun deleteEntries() = storageDao.deleteEntries()
|
||||
override suspend fun deleteEntries() = look4SatDao.deleteEntries()
|
||||
|
||||
override suspend fun getIdsWithModes(modes: List<String>) = storageDao.getIdsWithModes(modes)
|
||||
override suspend fun getIdsWithModes(modes: List<String>) = look4SatDao.getIdsWithModes(modes)
|
||||
|
||||
private fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
|
||||
private fun FrameworkEntry.toDomain() = OrbitalData(
|
||||
this.name, this.epoch, this.meanmo, this.eccn, this.incl,
|
||||
this.raan, this.argper, this.meanan, this.catnum, this.bstar
|
||||
)
|
||||
|
||||
private fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, false)
|
||||
private fun OrbitalData.toEntity() = FrameworkEntry(
|
||||
this.name, this.epoch, this.meanmo, this.eccn, this.incl,
|
||||
this.raan, this.argper, this.meanan, this.catnum, this.bstar
|
||||
)
|
||||
|
||||
private fun List<DomainEntry>.toFrameworkEntries() = this.map { entry -> entry.toFramework() }
|
||||
private fun List<OrbitalData>.toEntity() = this.map { item -> item.toEntity() }
|
||||
|
||||
private fun List<FrameworkItem>.toDomainItems() = this.map { item -> item.toDomain() }
|
||||
private fun List<FrameworkEntry>.toDomain() = this.map { item -> item.toDomain() }
|
||||
|
||||
//endregion
|
||||
|
||||
//region # Radios region
|
||||
|
||||
override suspend fun getRadiosTotal() = storageDao.getRadiosTotal()
|
||||
override suspend fun getRadiosTotal() = look4SatDao.getRadiosTotal()
|
||||
|
||||
override suspend fun getRadiosWithId(id: Int): List<SatRadio> {
|
||||
return storageDao.getRadiosWithId(id).toDomainRadios()
|
||||
return look4SatDao.getRadiosWithId(id).toDomainRadios()
|
||||
}
|
||||
|
||||
override suspend fun insertRadios(radios: List<SatRadio>) {
|
||||
storageDao.insertRadios(radios.toFrameworkRadios())
|
||||
look4SatDao.insertRadios(radios.toFrameworkRadios())
|
||||
}
|
||||
|
||||
override suspend fun deleteRadios() = storageDao.deleteRadios()
|
||||
override suspend fun deleteRadios() = look4SatDao.deleteRadios()
|
||||
|
||||
private fun DomainRadio.toFramework() = FrameworkRadio(
|
||||
this.uuid, this.info, this.isAlive, this.downlinkLow, this.downlinkHigh, this.downlinkMode,
|
||||
this.uplinkLow, this.uplinkHigh, this.uplinkMode, this.isInverted, this.catnum, this.comment
|
||||
this.uplinkLow, this.uplinkHigh, this.uplinkMode, this.isInverted, this.catnum
|
||||
)
|
||||
|
||||
private fun FrameworkRadio.toDomain() = DomainRadio(
|
||||
this.uuid, this.info, this.isAlive, this.downlinkLow, this.downlinkHigh, this.downlinkMode,
|
||||
this.uplinkLow, this.uplinkHigh, this.uplinkMode, this.isInverted, this.catnum, this.comment
|
||||
this.uplinkLow, this.uplinkHigh, this.uplinkMode, this.isInverted, this.catnum
|
||||
)
|
||||
|
||||
private fun List<DomainRadio>.toFrameworkRadios() = this.map { radio -> radio.toFramework() }
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.data.source
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import com.rtbishop.look4sat.domain.source.IRemoteSource
|
||||
import java.io.InputStream
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -25,15 +27,27 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
class RemoteSource(
|
||||
private val httpClient: OkHttpClient, private val dispatcher: CoroutineDispatcher
|
||||
private val contentResolver: ContentResolver,
|
||||
private val httpClient: OkHttpClient,
|
||||
private val dispatcher: CoroutineDispatcher
|
||||
) : IRemoteSource {
|
||||
|
||||
override suspend fun getRemoteStream(url: String): InputStream? = withContext(dispatcher) {
|
||||
override suspend fun getFileStream(uri: String): InputStream? = withContext(dispatcher) {
|
||||
try {
|
||||
val request = Request.Builder().url(url).build()
|
||||
httpClient.newCall(request).execute().body?.byteStream()
|
||||
val fileUri = Uri.parse(uri)
|
||||
contentResolver.openInputStream(fileUri)
|
||||
} catch (exception: Exception) {
|
||||
println("RemoteSource fetching stream exception: $exception")
|
||||
println("RemoteSource file stream exception: $exception")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getNetworkStream(url: String): InputStream? = withContext(dispatcher) {
|
||||
try {
|
||||
val networkRequest = Request.Builder().url(url).build()
|
||||
httpClient.newCall(networkRequest).execute().body?.byteStream()
|
||||
} catch (exception: Exception) {
|
||||
println("RemoteSource network stream exception: $exception")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
|
||||
data class SatEntry(val data: OrbitalData, var comment: String? = null)
|
||||
@@ -17,4 +17,4 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
data class SatItem(val catnum: Int, val name: String, val isSelected: Boolean)
|
||||
data class SatItem(val catnum: Int, val name: String, val isSelected: Boolean = false)
|
||||
@@ -28,6 +28,5 @@ data class SatRadio(
|
||||
var uplinkHigh: Long?,
|
||||
val uplinkMode: String?,
|
||||
val isInverted: Boolean,
|
||||
val catnum: Int?,
|
||||
var comment: String? = null
|
||||
val catnum: Int?
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
const val ASTRONOMICAL_UNIT = 1.49597870691E8
|
||||
const val DEG2RAD = 0.017453292519943295
|
||||
const val RAD2DEG = 57.29577951308232
|
||||
const val EARTH_RADIUS = 6378.137
|
||||
const val EPSILON = 1.0E-12
|
||||
const val FLAT_FACT = 3.35281066474748E-3
|
||||
const val J3_HARMONIC = -2.53881E-6
|
||||
const val MIN_PER_DAY = 1.44E3
|
||||
const val SEC_PER_DAY = 8.6400E4
|
||||
const val SOLAR_RADIUS = 6.96000E5
|
||||
const val SPEED_OF_LIGHT = 2.99792458E8
|
||||
const val PI = 3.141592653589793
|
||||
const val PI_2 = PI / 2.0
|
||||
const val TWO_PI = PI * 2.0
|
||||
const val TWO_THIRDS = 2.0 / 3.0
|
||||
const val CK2 = 5.413079E-4
|
||||
const val CK4 = 6.209887E-7
|
||||
const val XKE = 7.43669161E-2
|
||||
+1
-1
@@ -25,7 +25,7 @@ import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class DeepSpaceSatellite(data: OrbitalData) : Satellite(data) {
|
||||
class DeepSpaceObject(data: OrbitalData) : OrbitalObject(data) {
|
||||
|
||||
private val c1: Double
|
||||
private val c4: Double
|
||||
+1
-1
@@ -24,7 +24,7 @@ import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class NearEarthSatellite(data: OrbitalData) : Satellite(data) {
|
||||
class NearEarthObject(data: OrbitalData) : OrbitalObject(data) {
|
||||
|
||||
private val aodp: Double
|
||||
private val aycof: Double
|
||||
@@ -27,15 +27,14 @@ data class OrbitalData(
|
||||
val argper: Double,
|
||||
val meanan: Double,
|
||||
val catnum: Int,
|
||||
val bstar: Double,
|
||||
val xincl: Double = incl * DEG2RAD,
|
||||
val xnodeo: Double = raan * DEG2RAD,
|
||||
val omegao: Double = argper * DEG2RAD,
|
||||
val xmo: Double = meanan * DEG2RAD,
|
||||
val xno: Double = meanmo * TWO_PI / MIN_PER_DAY,
|
||||
val orbitalPeriod: Double = MIN_PER_DAY / meanmo,
|
||||
// Space objects are classified as NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
|
||||
val isDeepSpace: Boolean = orbitalPeriod >= 225.0
|
||||
val bstar: Double
|
||||
) {
|
||||
fun getSatellite(): Satellite = if (isDeepSpace) DeepSpaceSatellite(this) else NearEarthSatellite(this)
|
||||
val xincl: Double = incl * DEG2RAD
|
||||
val xnodeo: Double = raan * DEG2RAD
|
||||
val omegao: Double = argper * DEG2RAD
|
||||
val xmo: Double = meanan * DEG2RAD
|
||||
val xno: Double = meanmo * TWO_PI / MIN_PER_DAY
|
||||
val orbitalPeriod: Double = MIN_PER_DAY / meanmo
|
||||
val isDeepSpace: Boolean = orbitalPeriod >= 225.0 // NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
|
||||
fun getObject(): OrbitalObject = if (isDeepSpace) DeepSpaceObject(this) else NearEarthObject(this)
|
||||
}
|
||||
+26
-45
@@ -30,30 +30,11 @@ import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
const val ASTRONOMICAL_UNIT = 1.49597870691E8
|
||||
const val DEG2RAD = 0.017453292519943295
|
||||
const val RAD2DEG = 57.29577951308232
|
||||
const val EARTH_RADIUS = 6378.137
|
||||
const val EPSILON = 1.0E-12
|
||||
const val FLAT_FACT = 3.35281066474748E-3
|
||||
const val J3_HARMONIC = -2.53881E-6
|
||||
const val MIN_PER_DAY = 1.44E3
|
||||
const val SEC_PER_DAY = 8.6400E4
|
||||
const val SOLAR_RADIUS = 6.96000E5
|
||||
const val SPEED_OF_LIGHT = 2.99792458E8
|
||||
const val PI = 3.141592653589793
|
||||
const val PI_2 = PI / 2.0
|
||||
const val TWO_PI = PI * 2.0
|
||||
const val TWO_THIRDS = 2.0 / 3.0
|
||||
const val CK2 = 5.413079E-4
|
||||
const val CK4 = 6.209887E-7
|
||||
const val XKE = 7.43669161E-2
|
||||
|
||||
abstract class Satellite(val data: OrbitalData) {
|
||||
abstract class OrbitalObject(val data: OrbitalData) {
|
||||
|
||||
private val position = Vector4()
|
||||
private val velocity = Vector4()
|
||||
private var satPos = SatPos()
|
||||
private var orbitalPos = OrbitalPos()
|
||||
private var eclipseDepth = 0.0
|
||||
private var gsPosTheta = 0.0
|
||||
private var julUTC = 0.0
|
||||
@@ -72,8 +53,8 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getPosition(pos: GeoPos, time: Long): SatPos {
|
||||
satPos = SatPos()
|
||||
fun getPosition(pos: GeoPos, time: Long): OrbitalPos {
|
||||
orbitalPos = OrbitalPos()
|
||||
// Date/time at which the position and velocity were calculated
|
||||
julUTC = calcCurrentDaynum(time) + 2444238.5
|
||||
// Convert satellite's epoch time to Julian and calculate time since epoch in minutes
|
||||
@@ -88,10 +69,10 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
// Angles in rads, dist in km, vel in km/S. Calculate sat Az, El, Range and Range-rate.
|
||||
calculateObs(julUTC, position, velocity, pos, squintVector)
|
||||
calculateLatLonAlt(julUTC)
|
||||
satPos.time = time
|
||||
satPos.eclipsed = isEclipsed()
|
||||
satPos.eclipseDepth = eclipseDepth
|
||||
return satPos
|
||||
orbitalPos.time = time
|
||||
orbitalPos.eclipsed = isEclipsed()
|
||||
orbitalPos.eclipseDepth = eclipseDepth
|
||||
return orbitalPos
|
||||
}
|
||||
|
||||
private fun calcCurrentDaynum(now: Long): Double {
|
||||
@@ -118,8 +99,8 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
}
|
||||
|
||||
private fun calculateSDP4orSGP4(tsince: Double) {
|
||||
if (data.isDeepSpace) (this as DeepSpaceSatellite).calculateSDP4(tsince)
|
||||
else (this as NearEarthSatellite).calculateSGP4(tsince)
|
||||
if (data.isDeepSpace) (this as DeepSpaceObject).calculateSDP4(tsince)
|
||||
else (this as NearEarthObject).calculateSGP4(tsince)
|
||||
}
|
||||
|
||||
// Converts the sat position and velocity vectors to km and km/sec
|
||||
@@ -164,13 +145,13 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
var azim = atan(-topE / topS)
|
||||
if (topS > 0.0) azim += PI
|
||||
if (azim < 0.0) azim += TWO_PI
|
||||
satPos.azimuth = azim
|
||||
satPos.elevation = asin(topZ / range.w)
|
||||
satPos.distance = range.w
|
||||
satPos.distanceRate = dot(range, rgvel) / range.w
|
||||
var elevation = satPos.elevation / TWO_PI * 360.0
|
||||
orbitalPos.azimuth = azim
|
||||
orbitalPos.elevation = asin(topZ / range.w)
|
||||
orbitalPos.distance = range.w
|
||||
orbitalPos.distanceRate = dot(range, rgvel) / range.w
|
||||
var elevation = orbitalPos.elevation / TWO_PI * 360.0
|
||||
if (elevation > 90) elevation = 180 - elevation
|
||||
satPos.aboveHorizon = elevation - 0 > EPSILON
|
||||
orbitalPos.aboveHorizon = elevation - 0 > EPSILON
|
||||
}
|
||||
|
||||
// Returns the ECI position and velocity of the observer
|
||||
@@ -196,26 +177,26 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
|
||||
// Calculate the geodetic position of an object given its ECI pos and time
|
||||
private fun calculateLatLonAlt(time: Double) {
|
||||
satPos.theta = atan2(position.y, position.x)
|
||||
satPos.longitude = mod2PI(satPos.theta - thetaGJD(time))
|
||||
orbitalPos.theta = atan2(position.y, position.x)
|
||||
orbitalPos.longitude = mod2PI(orbitalPos.theta - thetaGJD(time))
|
||||
val r = sqrt(sqr(position.x) + sqr(position.y))
|
||||
val e2 = FLAT_FACT * (2.0 - FLAT_FACT)
|
||||
satPos.latitude = atan2(position.z, r)
|
||||
orbitalPos.latitude = atan2(position.z, r)
|
||||
var phi: Double
|
||||
var c: Double
|
||||
var i = 0
|
||||
var converged: Boolean
|
||||
do {
|
||||
phi = satPos.latitude
|
||||
phi = orbitalPos.latitude
|
||||
c = invert(sqrt(1.0 - e2 * sqr(sin(phi))))
|
||||
satPos.latitude = atan2(position.z + EARTH_RADIUS * c * e2 * sin(phi), r)
|
||||
converged = abs(satPos.latitude - phi) < EPSILON
|
||||
orbitalPos.latitude = atan2(position.z + EARTH_RADIUS * c * e2 * sin(phi), r)
|
||||
converged = abs(orbitalPos.latitude - phi) < EPSILON
|
||||
} while (i++ < 10 && !converged)
|
||||
satPos.altitude = r / cos(satPos.latitude) - EARTH_RADIUS * c
|
||||
var temp = satPos.latitude
|
||||
orbitalPos.altitude = r / cos(orbitalPos.latitude) - EARTH_RADIUS * c
|
||||
var temp = orbitalPos.latitude
|
||||
if (temp > PI_2) {
|
||||
temp -= TWO_PI
|
||||
satPos.latitude = temp
|
||||
orbitalPos.latitude = temp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +283,7 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
internal fun calculatePhase(xlt: Double, xnode: Double, omgadf: Double) {
|
||||
var phaseValue = xlt - xnode - omgadf + TWO_PI
|
||||
if (phaseValue < 0.0) phaseValue += TWO_PI
|
||||
satPos.phase = mod2PI(phaseValue)
|
||||
orbitalPos.phase = mod2PI(phaseValue)
|
||||
}
|
||||
|
||||
// Sets perigee and checks and adjusts the calculation if the perigee is less tan 156KM
|
||||
+5
-5
@@ -17,17 +17,17 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
data class SatPass(
|
||||
data class OrbitalPass(
|
||||
val aosTime: Long = 0L,
|
||||
val aosAzimuth: Double = 90.0,
|
||||
val losTime: Long = 0L,
|
||||
val losAzimuth: Double = 270.0,
|
||||
val altitude: Int = 1000,
|
||||
val maxElevation: Double = 75.0,
|
||||
val satellite: Satellite,
|
||||
val orbitalObject: OrbitalObject,
|
||||
var progress: Float = 0.0f
|
||||
) {
|
||||
val catNum: Int = satellite.data.catnum
|
||||
val name: String = satellite.data.name
|
||||
val isDeepSpace: Boolean = satellite.data.isDeepSpace
|
||||
val catNum: Int = orbitalObject.data.catnum
|
||||
val name: String = orbitalObject.data.name
|
||||
val isDeepSpace: Boolean = orbitalObject.data.isDeepSpace
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,7 @@ import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
data class SatPos(
|
||||
data class OrbitalPos(
|
||||
var azimuth: Double = 0.0,
|
||||
var elevation: Double = 0.0,
|
||||
var latitude: Double = 0.0,
|
||||
@@ -2,19 +2,19 @@ package com.rtbishop.look4sat.domain.repository
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPass
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalPos
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISatelliteRepo {
|
||||
val passes: StateFlow<List<SatPass>>
|
||||
val satellites: StateFlow<List<Satellite>>
|
||||
val passes: StateFlow<List<OrbitalPass>>
|
||||
val satellites: StateFlow<List<OrbitalObject>>
|
||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||
suspend fun initRepository()
|
||||
suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos
|
||||
suspend fun getTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos>
|
||||
suspend fun getRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio>
|
||||
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass>
|
||||
suspend fun getPosition(sat: OrbitalObject, pos: GeoPos, time: Long): OrbitalPos
|
||||
suspend fun getTrack(sat: OrbitalObject, pos: GeoPos, start: Long, end: Long): List<OrbitalPos>
|
||||
suspend fun getRadios(sat: OrbitalObject, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio>
|
||||
suspend fun processPasses(passList: List<OrbitalPass>, time: Long): List<OrbitalPass>
|
||||
suspend fun calculatePasses(time: Long, hoursAhead: Int, minElevation: Double, modes: List<String>)
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package com.rtbishop.look4sat.domain.source
|
||||
package com.rtbishop.look4sat.domain.repository
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISensorSource {
|
||||
interface ISensorsRepo {
|
||||
val orientation: StateFlow<Pair<Float, Float>>
|
||||
fun enableSensor()
|
||||
fun disableSensor()
|
||||
@@ -25,37 +25,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISettingsRepo {
|
||||
|
||||
val radioSourceUrl: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||
val satelliteSourcesMap: Map<String, String>
|
||||
get() = mapOf(
|
||||
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||
"Amsat" to "https://amsat.org/tle/current/nasabare.txt",
|
||||
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
||||
"Classified" to "https://www.mmccants.org/tles/classfd.zip",
|
||||
"Cubesat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=csv",
|
||||
"Education" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=education&FORMAT=csv",
|
||||
"Engineer" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=engineering&FORMAT=csv",
|
||||
"Geostationary" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=geo&FORMAT=csv",
|
||||
"Globalstar" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=csv",
|
||||
"GNSS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=gnss&FORMAT=csv",
|
||||
"Intelsat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=csv",
|
||||
"Iridium" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=csv",
|
||||
"McCants" to "https://www.mmccants.org/tles/inttles.zip",
|
||||
"Military" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=military&FORMAT=csv",
|
||||
"New" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=csv",
|
||||
"OneWeb" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=oneweb&FORMAT=csv",
|
||||
"Orbcomm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=csv",
|
||||
"R4UAB" to "https://r4uab.ru/satonline.txt",
|
||||
"Resource" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=resource&FORMAT=csv",
|
||||
"SatNOGS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=satnogs&FORMAT=csv",
|
||||
"Science" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=science&FORMAT=csv",
|
||||
"Spire" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=spire&FORMAT=csv",
|
||||
"Starlink" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=csv",
|
||||
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
|
||||
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
|
||||
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
|
||||
)
|
||||
|
||||
//region # Satellites selection settings
|
||||
val selectedIds: StateFlow<List<Int>>
|
||||
fun setSelectedIds(ids: List<Int>)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.source
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
interface IFileSource {
|
||||
suspend fun getFileStream(uri: String): InputStream?
|
||||
}
|
||||
@@ -17,16 +17,16 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain.source
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalObject
|
||||
|
||||
interface ILocalSource {
|
||||
suspend fun getEntriesTotal(): Int
|
||||
suspend fun getEntriesList(): List<SatItem>
|
||||
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
||||
suspend fun insertEntries(entries: List<SatEntry>)
|
||||
suspend fun getEntriesWithIds(ids: List<Int>): List<OrbitalObject>
|
||||
suspend fun insertEntries(entries: List<OrbitalData>)
|
||||
suspend fun deleteEntries()
|
||||
suspend fun getIdsWithModes(modes: List<String>): List<Int>
|
||||
suspend fun getRadiosTotal(): Int
|
||||
|
||||
@@ -20,5 +20,6 @@ package com.rtbishop.look4sat.domain.source
|
||||
import java.io.InputStream
|
||||
|
||||
interface IRemoteSource {
|
||||
suspend fun getRemoteStream(url: String): InputStream?
|
||||
suspend fun getFileStream(uri: String): InputStream?
|
||||
suspend fun getNetworkStream(url: String): InputStream?
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.rtbishop.look4sat.domain.source
|
||||
|
||||
object Sources {
|
||||
const val radioDataUrl = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||
val satelliteDataUrls = mapOf(
|
||||
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||
"Amsat" to "https://amsat.org/tle/current/nasabare.txt",
|
||||
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
||||
"Classified" to "https://www.mmccants.org/tles/classfd.zip",
|
||||
"Cubesat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=cubesat&FORMAT=csv",
|
||||
"Education" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=education&FORMAT=csv",
|
||||
"Engineer" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=engineering&FORMAT=csv",
|
||||
"Geostationary" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=geo&FORMAT=csv",
|
||||
"Globalstar" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=globalstar&FORMAT=csv",
|
||||
"GNSS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=gnss&FORMAT=csv",
|
||||
"Intelsat" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=intelsat&FORMAT=csv",
|
||||
"Iridium" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=iridium-NEXT&FORMAT=csv",
|
||||
"McCants" to "https://www.mmccants.org/tles/inttles.zip",
|
||||
"Military" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=military&FORMAT=csv",
|
||||
"New" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=last-30-days&FORMAT=csv",
|
||||
"OneWeb" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=oneweb&FORMAT=csv",
|
||||
"Orbcomm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=orbcomm&FORMAT=csv",
|
||||
"R4UAB" to "https://r4uab.ru/satonline.txt",
|
||||
"Resource" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=resource&FORMAT=csv",
|
||||
"SatNOGS" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=satnogs&FORMAT=csv",
|
||||
"Science" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=science&FORMAT=csv",
|
||||
"Spire" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=spire&FORMAT=csv",
|
||||
"Starlink" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=csv",
|
||||
"Swarm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=swarm&FORMAT=csv",
|
||||
"Weather" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=csv",
|
||||
"X-Comm" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=x-comm&FORMAT=csv"
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user