Renamed and moved several classes

pull/87/head
Arty Bishop 2022-03-06 11:37:47 +00:00
rodzic 5804f42211
commit 4ee74a6789
59 zmienionych plików z 563 dodań i 449 usunięć

Wyświetl plik

@ -55,7 +55,7 @@ android {
}
dependencies {
implementation project(":data")
implementation project(":base")
implementation "androidx.core:core-splashscreen:1.0.0-beta01"
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"

Wyświetl plik

@ -26,12 +26,12 @@ import android.location.LocationManager
import android.os.Bundle
import androidx.core.content.ContextCompat
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.ILocationHandler
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.QthConverter
import com.rtbishop.look4sat.domain.ILocationManager
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.round
import com.rtbishop.look4sat.utility.QthConverter
import com.rtbishop.look4sat.utility.round
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -39,12 +39,12 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LocationHandler @Inject constructor(
class LocationManager @Inject constructor(
@ApplicationContext private val context: Context,
private val settings: ISettings,
) : LocationListener, ILocationHandler {
private val manager: LocationManager,
private val settings: ISettingsManager,
) : LocationListener, ILocationManager {
private val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private val providerDef = LocationManager.PASSIVE_PROVIDER
private val providerNet = LocationManager.NETWORK_PROVIDER
private val providerGps = LocationManager.GPS_PROVIDER

Wyświetl plik

@ -21,12 +21,13 @@ import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import com.rtbishop.look4sat.domain.predict.RAD2DEG
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.round
@Singleton
class OrientationHandler @Inject constructor(private val sensorManager: SensorManager) :
class OrientationManager @Inject constructor(private val sensorManager: SensorManager) :
SensorEventListener {
interface OrientationListener {
@ -36,7 +37,6 @@ class OrientationHandler @Inject constructor(private val sensorManager: SensorMa
private val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
private val rotationMatrix = FloatArray(9)
private val orientationValues = FloatArray(3)
private val oneRadDegrees = 57.295779513f
private var sensorAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE
private var orientationListener: OrientationListener? = null
@ -67,9 +67,9 @@ class OrientationHandler @Inject constructor(private val sensorManager: SensorMa
private fun updateOrientation(rotationVector: FloatArray) {
SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVector)
SensorManager.getOrientation(rotationMatrix, orientationValues)
val azimuth = orientationValues[0] * oneRadDegrees
val pitch = orientationValues[1] * oneRadDegrees
val roll = orientationValues[2] * oneRadDegrees
val azimuth = (orientationValues[0] * RAD2DEG).toFloat()
val pitch = (orientationValues[1] * RAD2DEG).toFloat()
val roll = (orientationValues[2] * RAD2DEG).toFloat()
val magneticAzimuth = (azimuth + 360f) % 360f
val roundedAzimuth = round(magneticAzimuth * 10) / 10
val roundedPitch = round(pitch * 10) / 10

Wyświetl plik

@ -19,13 +19,13 @@ package com.rtbishop.look4sat.framework
import android.content.SharedPreferences
import androidx.core.content.edit
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.predict.GeoPos
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SettingsHandler @Inject constructor(private val prefs: SharedPreferences) : ISettings {
class SettingsManager @Inject constructor(private val prefs: SharedPreferences) : ISettingsManager {
companion object {
const val keyDataSources = "dataSources"

Wyświetl plik

@ -19,24 +19,18 @@ package com.rtbishop.look4sat.framework.data
import android.content.ContentResolver
import android.net.Uri
import com.rtbishop.look4sat.data.IProvider
import com.rtbishop.look4sat.domain.data.IFileDataSource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.net.URL
class Provider(
class FileDataSource(
private val contentResolver: ContentResolver,
private val ioDispatcher: CoroutineDispatcher
) : IProvider {
) : IFileDataSource {
@Suppress("BlockingMethodInNonBlockingContext")
override suspend fun getLocalFileStream(uri: String): InputStream? {
override suspend fun getDataStream(uri: String): InputStream? {
return withContext(ioDispatcher) { contentResolver.openInputStream(Uri.parse(uri)) }
}
@Suppress("BlockingMethodInNonBlockingContext")
override suspend fun getRemoteFileStream(url: String): InputStream? {
return withContext(ioDispatcher) { URL(url).openStream() }
}
}

Wyświetl plik

@ -19,11 +19,13 @@ package com.rtbishop.look4sat.framework.data
import androidx.room.Database
import androidx.room.RoomDatabase
import com.rtbishop.look4sat.framework.data.dao.EntriesDao
import com.rtbishop.look4sat.framework.data.dao.RadiosDao
import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatRadio
@Database(entities = [SatEntry::class, SatRadio::class], version = 1, exportSchema = true)
abstract class StorageDb : RoomDatabase() {
abstract class LocalDatabase : RoomDatabase() {
abstract fun entriesDao(): EntriesDao

Wyświetl plik

@ -17,21 +17,18 @@
*/
package com.rtbishop.look4sat.framework.data
import com.rtbishop.look4sat.data.IStorage
import com.rtbishop.look4sat.domain.data.ILocalEntrySource
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.framework.data.dao.EntriesDao
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.framework.model.SatEntry as FrameworkEntry
import com.rtbishop.look4sat.framework.model.SatItem as FrameworkItem
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
class Storage(private val entriesDao: EntriesDao, private val radiosDao: RadiosDao) : IStorage {
class LocalEntrySource(private val entriesDao: EntriesDao) : ILocalEntrySource {
override fun getEntriesTotal() = entriesDao.getEntriesTotal()
override fun getRadiosTotal() = radiosDao.getRadiosTotal()
override suspend fun getEntriesWithModes(): List<DomainItem> {
return entriesDao.getEntriesWithModes().toDomainItems()
}
@ -45,42 +42,17 @@ class Storage(private val entriesDao: EntriesDao, private val radiosDao: RadiosD
return selectedSatellites
}
override suspend fun getRadiosWithId(id: Int): List<DomainRadio> {
return radiosDao.getRadiosWithId(id).toDomainRadios()
}
override suspend fun insertEntries(entries: List<DomainEntry>) {
entriesDao.insertEntries(entries.toFrameworkEntries())
}
override suspend fun insertRadios(radios: List<DomainRadio>) {
radiosDao.insertRadios(radios.toFrameworkRadios())
}
override suspend fun clearAllData() {
entriesDao.deleteEntries()
radiosDao.deleteRadios()
}
override suspend fun deleteEntries() = entriesDao.deleteEntries()
private fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
private fun DomainRadio.toFramework() = FrameworkRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.modes, false)
private fun FrameworkRadio.toDomain() = DomainRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun List<DomainEntry>.toFrameworkEntries() = this.map { entry -> entry.toFramework() }
private fun List<DomainRadio>.toFrameworkRadios() = this.map { radio -> radio.toFramework() }
private fun List<FrameworkItem>.toDomainItems() = this.map { item -> item.toDomain() }
private fun List<FrameworkRadio>.toDomainRadios() = this.map { radio -> radio.toDomain() }
}

Wyświetl plik

@ -0,0 +1,52 @@
/*
* 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.framework.data
import com.rtbishop.look4sat.domain.data.ILocalRadioSource
import com.rtbishop.look4sat.framework.data.dao.RadiosDao
import com.rtbishop.look4sat.domain.model.SatRadio as DomainRadio
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
class LocalRadioSource(private val radiosDao: RadiosDao) : ILocalRadioSource {
override fun getRadiosTotal() = radiosDao.getRadiosTotal()
override suspend fun getRadiosWithId(id: Int): List<DomainRadio> {
return radiosDao.getRadiosWithId(id).toDomainRadios()
}
override suspend fun insertRadios(radios: List<DomainRadio>) {
radiosDao.insertRadios(radios.toFrameworkRadios())
}
override suspend fun deleteRadios() = radiosDao.deleteRadios()
private fun DomainRadio.toFramework() = FrameworkRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun FrameworkRadio.toDomain() = DomainRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun List<DomainRadio>.toFrameworkRadios() = this.map { radio -> radio.toFramework() }
private fun List<FrameworkRadio>.toDomainRadios() = this.map { radio -> radio.toDomain() }
}

Wyświetl plik

@ -0,0 +1,32 @@
/*
* 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.framework.data
import com.rtbishop.look4sat.domain.data.IRemoteDataSource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.net.URL
class RemoteDataSource(private val ioDispatcher: CoroutineDispatcher) : IRemoteDataSource {
@Suppress("BlockingMethodInNonBlockingContext")
override suspend fun getDataStream(url: String): InputStream? = withContext(ioDispatcher) {
URL(url).openStream()
}
}

Wyświetl plik

@ -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.framework.data
package com.rtbishop.look4sat.framework.data.dao
import androidx.room.*
import com.rtbishop.look4sat.framework.model.SatEntry

Wyświetl plik

@ -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.framework.data
package com.rtbishop.look4sat.framework.data.dao
import androidx.room.*
import com.rtbishop.look4sat.framework.model.SatRadio

Wyświetl plik

@ -17,7 +17,6 @@
*/
package com.rtbishop.look4sat.injection
import android.content.ContentResolver
import android.content.Context
import android.content.SharedPreferences
import android.hardware.SensorManager
@ -27,21 +26,12 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import javax.inject.Qualifier
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideContentResolver(@ApplicationContext context: Context): ContentResolver {
return context.contentResolver
}
@Provides
@Singleton
fun provideLocationManager(@ApplicationContext context: Context): LocationManager {
@ -59,20 +49,4 @@ object AppModule {
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
return context.getSharedPreferences("default", Context.MODE_PRIVATE)
}
@Provides
@DefaultDispatcher
fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
@Provides
@IoDispatcher
fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DefaultDispatcher
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher

Wyświetl plik

@ -0,0 +1,76 @@
/*
* 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.injection
import android.content.Context
import androidx.room.Room
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ILocationManager
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.data.DataRepository
import com.rtbishop.look4sat.domain.predict.SatelliteManager
import com.rtbishop.look4sat.framework.LocationManager
import com.rtbishop.look4sat.framework.SettingsManager
import com.rtbishop.look4sat.framework.data.*
import com.rtbishop.look4sat.utility.DataParser
import com.rtbishop.look4sat.utility.DataReporter
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object BaseModule {
@Provides
@Singleton
fun provideDataRepository(@ApplicationContext context: Context): IDataRepository {
val db = Room.databaseBuilder(context, LocalDatabase::class.java, "Look4SatDb")
.fallbackToDestructiveMigration().build()
val parser = DataParser(Dispatchers.Default)
val fileSource = FileDataSource(context.contentResolver, Dispatchers.IO)
val entries = LocalEntrySource(db.entriesDao())
val radios = LocalRadioSource(db.radiosDao())
val remoteSource = RemoteDataSource(Dispatchers.IO)
val repositoryScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
return DataRepository(parser, fileSource, entries, radios, remoteSource, repositoryScope)
}
@Provides
@Singleton
fun provideDataReporter(): DataReporter = DataReporter(CoroutineScope(Dispatchers.IO))
@Provides
@Singleton
fun provideLocationManager(manager: LocationManager): ILocationManager = manager
@Provides
@Singleton
fun provideSatelliteManager(): ISatelliteManager = SatelliteManager(Dispatchers.Default)
@Provides
@Singleton
fun provideSettingsManager(manager: SettingsManager): ISettingsManager = manager
}

Wyświetl plik

@ -1,91 +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.injection
import android.content.Context
import android.content.SharedPreferences
import androidx.room.Room
import com.rtbishop.look4sat.data.DataParser
import com.rtbishop.look4sat.data.Repository
import com.rtbishop.look4sat.domain.DataReporter
import com.rtbishop.look4sat.domain.ILocationHandler
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.framework.LocationHandler
import com.rtbishop.look4sat.framework.SettingsHandler
import com.rtbishop.look4sat.framework.data.Provider
import com.rtbishop.look4sat.framework.data.Storage
import com.rtbishop.look4sat.framework.data.StorageDb
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DataModule {
@Provides
@Singleton
fun provideSatelliteRepo(
@ApplicationContext context: Context,
@IoDispatcher ioDispatcher: CoroutineDispatcher,
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
): IRepository {
val db = Room.databaseBuilder(context, StorageDb::class.java, "Look4SatDb")
.fallbackToDestructiveMigration().build()
val dataParser = DataParser(defaultDispatcher)
val localSource = Storage(db.entriesDao(), db.radiosDao())
val remoteSource = Provider(context.contentResolver, ioDispatcher)
val repositoryScope = CoroutineScope(SupervisorJob())
return Repository(dataParser, localSource, remoteSource, repositoryScope)
}
@Provides
@Singleton
fun provideSettingsHandler(sharedPreferences: SharedPreferences): ISettings {
return SettingsHandler(sharedPreferences)
}
@Provides
@Singleton
fun provideLocationHandler(
@ApplicationContext context: Context,
settings: ISettings
): ILocationHandler {
return LocationHandler(context, settings)
}
@Provides
@Singleton
fun providePredictor(@DefaultDispatcher dispatcher: CoroutineDispatcher): Predictor {
return Predictor(dispatcher)
}
@Provides
@Singleton
fun provideDataReporter(@IoDispatcher dispatcher: CoroutineDispatcher): DataReporter {
return DataReporter(dispatcher)
}
}

Wyświetl plik

@ -18,8 +18,8 @@
package com.rtbishop.look4sat.presentation.entriesScreen
import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatItem
import dagger.hilt.android.lifecycle.HiltViewModel
@ -29,8 +29,8 @@ import javax.inject.Inject
@HiltViewModel
class EntriesViewModel @Inject constructor(
private val repository: IRepository,
private val settings: ISettings
private val repository: IDataRepository,
private val settings: ISettingsManager
) : ViewModel(), EntriesAdapter.EntriesClickListener {
private val transModes = MutableLiveData(settings.loadModesSelection())

Wyświetl plik

@ -27,7 +27,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogModesBinding
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -36,7 +36,7 @@ import javax.inject.Inject
class ModesDialog : AppCompatDialogFragment(), ModesAdapter.ModesClickListener {
@Inject
lateinit var preferences: ISettings
lateinit var preferences: ISettingsManager
private lateinit var binding: DialogModesBinding
private val allModes = listOf(
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",

Wyświetl plik

@ -18,13 +18,13 @@
package com.rtbishop.look4sat.presentation.mapScreen
import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.QthConverter
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.utility.QthConverter
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import java.util.*
@ -34,9 +34,9 @@ import kotlin.math.min
@HiltViewModel
class MapViewModel @Inject constructor(
private val predictor: Predictor,
private val repository: IRepository,
private val settings: ISettings,
private val satelliteManager: ISatelliteManager,
private val repository: IDataRepository,
private val settings: ISettingsManager,
) : ViewModel() {
private val stationPosition = settings.loadStationPosition()
@ -115,7 +115,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.time, endDate.time).forEach { satPos ->
satelliteManager.getTrack(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)
@ -142,7 +142,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.time)
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
positions[satellite] = GeoPos(osmLat, osmLon)
@ -151,12 +151,12 @@ class MapViewModel @Inject constructor(
}
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
val satPos = predictor.getSatPos(satellite, pos, date.time)
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
_footprint.postValue(satPos)
}
private suspend fun getSatData(satellite: Satellite, pos: GeoPos, date: Date) {
val satPos = predictor.getSatPos(satellite, pos, date.time)
val satPos = satelliteManager.getPosition(satellite, pos, date.time)
val osmLat = clipLat(Math.toDegrees(satPos.latitude))
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
val osmPos = GeoPos(osmLat, osmLon)

Wyświetl plik

@ -25,7 +25,7 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialogFragment
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogFilterBinding
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -34,7 +34,7 @@ import javax.inject.Inject
class FilterDialog : AppCompatDialogFragment() {
@Inject
lateinit var preferences: ISettings
lateinit var preferences: ISettingsManager
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
return inflater.inflate(R.layout.dialog_filter, group, false)

Wyświetl plik

@ -31,8 +31,8 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentPassesBinding
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.toTimerString
import com.rtbishop.look4sat.presentation.getNavResult
import com.rtbishop.look4sat.utility.toTimerString
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint

Wyświetl plik

@ -18,10 +18,10 @@
package com.rtbishop.look4sat.presentation.passesScreen
import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.domain.predict.SatPass
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
@ -29,9 +29,9 @@ import javax.inject.Inject
@HiltViewModel
class PassesViewModel @Inject constructor(
private val predictor: Predictor,
private val repository: IRepository,
private val settings: ISettings
private val satelliteManager: ISatelliteManager,
private val repository: IDataRepository,
private val settings: ISettingsManager
) : ViewModel() {
private var passesProcessing: Job? = null
@ -41,12 +41,12 @@ class PassesViewModel @Inject constructor(
init {
viewModelScope.launch {
predictor.calculatedPasses.collect { passes ->
satelliteManager.calculatedPasses.collect { passes ->
passesProcessing?.cancelAndJoin()
passesProcessing = viewModelScope.launch {
while (isActive) {
val time = System.currentTimeMillis()
val newPasses = predictor.processPasses(passes, time)
val newPasses = satelliteManager.processPasses(passes, time)
_passes.postValue(DataState.Success(newPasses))
delay(1000)
}
@ -73,7 +73,7 @@ class PassesViewModel @Inject constructor(
val stationPos = settings.loadStationPosition()
val selectedIds = settings.loadEntriesSelection()
val satellites = repository.getEntriesWithIds(selectedIds)
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
satelliteManager.calculatePasses(satellites, stationPos, timeRef, hoursAhead, minElevation)
}
}
}

Wyświetl plik

@ -30,7 +30,7 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentRadarBinding
import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.toTimerString
import com.rtbishop.look4sat.utility.toTimerString
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint

Wyświetl plik

@ -19,16 +19,16 @@ package com.rtbishop.look4sat.presentation.radarScreen
import android.hardware.GeomagneticField
import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.DataReporter
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.round
import com.rtbishop.look4sat.framework.OrientationHandler
import com.rtbishop.look4sat.framework.OrientationManager
import com.rtbishop.look4sat.utility.DataReporter
import com.rtbishop.look4sat.utility.round
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@ -38,12 +38,12 @@ import javax.inject.Inject
@HiltViewModel
class RadarViewModel @Inject constructor(
private val orientationHandler: OrientationHandler,
private val orientationManager: OrientationManager,
private val reporter: DataReporter,
private val predictor: Predictor,
private val repository: IRepository,
private val settings: ISettings
) : ViewModel(), OrientationHandler.OrientationListener {
private val satelliteManager: ISatelliteManager,
private val repository: IDataRepository,
private val settings: ISettingsManager
) : ViewModel(), OrientationManager.OrientationListener {
private val stationPos = settings.loadStationPosition()
private val _passData = MutableLiveData<RadarData>()
@ -54,7 +54,7 @@ class RadarViewModel @Inject constructor(
val orientation: LiveData<Triple<Float, Float, Float>> = _orientation
fun getPass(catNum: Int, aosTime: Long) = liveData {
predictor.calculatedPasses.collect { passes ->
satelliteManager.calculatedPasses.collect { passes ->
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
pass?.let { satPass ->
emit(satPass)
@ -65,11 +65,11 @@ class RadarViewModel @Inject constructor(
}
fun enableSensor() {
if (settings.getUseCompass()) orientationHandler.startListening(this)
if (settings.getUseCompass()) orientationManager.startListening(this)
}
fun disableSensor() {
if (settings.getUseCompass()) orientationHandler.stopListening()
if (settings.getUseCompass()) orientationManager.stopListening()
}
fun getUseCompass(): Boolean = settings.getUseCompass()
@ -92,10 +92,10 @@ class RadarViewModel @Inject constructor(
if (!satPass.isDeepSpace) {
val startDate = satPass.aosTime
val endDate = satPass.losTime
satTrack = predictor.getSatTrack(satPass.satellite, stationPos, startDate, endDate)
satTrack = satelliteManager.getTrack(satPass.satellite, stationPos, startDate, endDate)
}
while (isActive) {
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date().time)
val satPos = satelliteManager.getPosition(satPass.satellite, stationPos, Date().time)
if (settings.getRotatorEnabled()) {
val server = settings.getRotatorServer()
val port = settings.getRotatorPort().toInt()
@ -115,7 +115,7 @@ class RadarViewModel @Inject constructor(
val transmitters = repository.getRadiosWithId(pass.catNum)
while (isActive) {
val time = System.currentTimeMillis()
val list = predictor.processRadios(pass.satellite, stationPos, transmitters, time)
val list = satelliteManager.processRadios(pass.satellite, stationPos, transmitters, time)
_transmitters.postValue(list)
delay(1000)
}

Wyświetl plik

@ -25,7 +25,7 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialogFragment
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogLocatorBinding
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -34,7 +34,7 @@ import javax.inject.Inject
class LocatorDialog : AppCompatDialogFragment() {
@Inject
lateinit var preferences: ISettings
lateinit var preferences: ISettingsManager
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
return inflater.inflate(R.layout.dialog_locator, group, false)

Wyświetl plik

@ -25,7 +25,7 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialogFragment
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogPositionBinding
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -34,7 +34,7 @@ import javax.inject.Inject
class PositionDialog : AppCompatDialogFragment() {
@Inject
lateinit var preferences: ISettings
lateinit var preferences: ISettingsManager
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
return inflater.inflate(R.layout.dialog_position, group, false)

Wyświetl plik

@ -34,11 +34,11 @@ import androidx.navigation.fragment.findNavController
import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
import com.rtbishop.look4sat.domain.isValidIPv4
import com.rtbishop.look4sat.domain.isValidPort
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.presentation.getNavResult
import com.rtbishop.look4sat.utility.isValidIPv4
import com.rtbishop.look4sat.utility.isValidPort
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint

Wyświetl plik

@ -19,9 +19,9 @@ package com.rtbishop.look4sat.presentation.settingsScreen
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.rtbishop.look4sat.domain.ILocationHandler
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ILocationManager
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.GeoPos
import dagger.hilt.android.lifecycle.HiltViewModel
@ -30,9 +30,9 @@ import javax.inject.Inject
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val locationHandler: ILocationHandler,
private val repository: IRepository,
private val settings: ISettings
private val locationManager: ILocationManager,
private val repository: IDataRepository,
private val settings: ISettingsManager
) : ViewModel() {
val entriesTotal = repository.getEntriesTotal().asLiveData()
@ -79,17 +79,17 @@ class SettingsViewModel @Inject constructor(
fun setUpdateHandled() = repository.setUpdateStateHandled()
val stationPosition: SharedFlow<DataState<GeoPos>> = locationHandler.stationPosition
val stationPosition: SharedFlow<DataState<GeoPos>> = locationManager.stationPosition
fun getStationPosition(): GeoPos = locationHandler.getStationPosition()
fun getStationPosition(): GeoPos = locationManager.getStationPosition()
fun setStationPosition(lat: Double, lon: Double) = locationHandler.setStationPosition(lat, lon)
fun setStationPosition(lat: Double, lon: Double) = locationManager.setStationPosition(lat, lon)
fun setPositionFromGps() = locationHandler.setPositionFromGps()
fun setPositionFromGps() = locationManager.setPositionFromGps()
fun setPositionFromNet() = locationHandler.setPositionFromNet()
fun setPositionFromNet() = locationManager.setPositionFromNet()
fun setPositionFromQth(qthString: String) = locationHandler.setPositionFromQth(qthString)
fun setPositionFromQth(qthString: String) = locationManager.setPositionFromQth(qthString)
fun setPositionHandled() = locationHandler.setPositionHandled()
fun setPositionHandled() = locationManager.setPositionHandled()
}

Wyświetl plik

@ -27,7 +27,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogSourcesBinding
import com.rtbishop.look4sat.domain.ISettings
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.framework.model.DataSource
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
@ -37,7 +37,7 @@ import javax.inject.Inject
class SourcesDialog : AppCompatDialogFragment(), SourcesAdapter.SourcesClickListener {
@Inject
lateinit var settings: ISettings
lateinit var settings: ISettingsManager
private lateinit var binding: DialogSourcesBinding
private lateinit var sourcesAdapter: SourcesAdapter

Wyświetl plik

@ -24,7 +24,7 @@ import com.rtbishop.look4sat.domain.predict.Satellite
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface IRepository {
interface IDataRepository {
val updateState: StateFlow<DataState<Long>>

Wyświetl plik

@ -21,7 +21,7 @@ import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.GeoPos
import kotlinx.coroutines.flow.SharedFlow
interface ILocationHandler {
interface ILocationManager {
val stationPosition: SharedFlow<DataState<GeoPos>>

Wyświetl plik

@ -0,0 +1,34 @@
package com.rtbishop.look4sat.domain
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 kotlinx.coroutines.flow.SharedFlow
interface ISatelliteManager {
val calculatedPasses: SharedFlow<List<SatPass>>
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 processRadios(
sat: Satellite,
pos: GeoPos,
radios: List<SatRadio>,
time: Long
): List<SatRadio>
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass>
suspend fun calculatePasses(
satList: List<Satellite>,
pos: GeoPos,
time: Long,
hoursAhead: Int = 8,
minElevation: Double = 16.0
)
}

Wyświetl plik

@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain
import com.rtbishop.look4sat.domain.predict.GeoPos
interface ISettings {
interface ISettingsManager {
val defaultSources: List<String>
get() = listOf(

Wyświetl plik

@ -15,23 +15,26 @@
* 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
package com.rtbishop.look4sat.domain.data
import com.rtbishop.look4sat.domain.IRepository
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.utility.DataParser
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.io.InputStream
import java.util.zip.ZipInputStream
class Repository(
class DataRepository(
private val dataParser: DataParser,
private val storage: IStorage,
private val provider: IProvider,
private val repoScope: CoroutineScope
) : IRepository {
private val fileSource: IFileDataSource,
private val entrySource: ILocalEntrySource,
private val radioSource: ILocalRadioSource,
private val remoteSource: IRemoteDataSource,
private val repositoryScope: CoroutineScope
) : IDataRepository {
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
_updateState.value = DataState.Error(exception.message)
@ -40,22 +43,22 @@ class Repository(
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
override val updateState: StateFlow<DataState<Long>> = _updateState
override fun getEntriesTotal() = storage.getEntriesTotal()
override fun getEntriesTotal() = entrySource.getEntriesTotal()
override fun getRadiosTotal() = storage.getRadiosTotal()
override fun getRadiosTotal() = radioSource.getRadiosTotal()
override suspend fun getEntriesWithModes() = storage.getEntriesWithModes()
override suspend fun getEntriesWithModes() = entrySource.getEntriesWithModes()
override suspend fun getEntriesWithIds(ids: List<Int>) = storage.getEntriesWithIds(ids)
override suspend fun getEntriesWithIds(ids: List<Int>) = entrySource.getEntriesWithIds(ids)
override suspend fun getRadiosWithId(id: Int) = storage.getRadiosWithId(id)
override suspend fun getRadiosWithId(id: Int) = radioSource.getRadiosWithId(id)
override fun updateFromFile(uri: String) {
repoScope.launch(exceptionHandler) {
repositoryScope.launch(exceptionHandler) {
_updateState.value = DataState.Loading
provider.getLocalFileStream(uri)?.let { fileStream ->
fileSource.getDataStream(uri)?.let { fileStream ->
delay(updateStateDelay)
storage.insertEntries(importSatellites(fileStream))
entrySource.insertEntries(importSatellites(fileStream))
}
_updateState.value = DataState.Success(0L)
}
@ -63,12 +66,12 @@ class Repository(
override fun updateFromWeb(urls: List<String>) {
_updateState.value = DataState.Loading
repoScope.launch(exceptionHandler) {
repositoryScope.launch(exceptionHandler) {
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
val streamsMap = mutableMapOf<String, InputStream?>()
val streams = mutableListOf<InputStream>()
val entries = mutableListOf<SatEntry>()
urls.forEach { jobsMap[it] = async { provider.getRemoteFileStream(it) } }
urls.forEach { jobsMap[it] = async { remoteSource.getDataStream(it) } }
jobsMap.forEach { job -> streamsMap[job.key] = job.value.await() }
streamsMap.forEach { stream ->
stream.value?.let { inputStream ->
@ -85,11 +88,11 @@ class Repository(
}
}
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
storage.insertEntries(entries)
entrySource.insertEntries(entries)
}
repoScope.launch(exceptionHandler) {
provider.getRemoteFileStream(provider.radioApi)?.let { stream ->
storage.insertRadios(dataParser.parseJSONStream(stream))
repositoryScope.launch(exceptionHandler) {
remoteSource.getDataStream(remoteSource.radioApi)?.let { stream ->
radioSource.insertRadios(dataParser.parseJSONStream(stream))
_updateState.value = DataState.Success(0L)
}
}
@ -100,10 +103,11 @@ class Repository(
}
override fun clearAllData() {
repoScope.launch {
repositoryScope.launch {
_updateState.value = DataState.Loading
delay(updateStateDelay)
storage.clearAllData()
entrySource.deleteEntries()
radioSource.deleteRadios()
_updateState.value = DataState.Success(0L)
}
}

Wyświetl plik

@ -0,0 +1,25 @@
/*
* 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.data
import java.io.InputStream
interface IFileDataSource {
suspend fun getDataStream(uri: String): InputStream?
}

Wyświetl plik

@ -15,29 +15,22 @@
* 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
package com.rtbishop.look4sat.domain.data
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 kotlinx.coroutines.flow.Flow
interface IStorage {
interface ILocalEntrySource {
fun getEntriesTotal(): Flow<Int>
fun getRadiosTotal(): Flow<Int>
suspend fun getEntriesWithModes(): List<SatItem>
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
suspend fun getRadiosWithId(id: Int): List<SatRadio>
suspend fun insertEntries(entries: List<SatEntry>)
suspend fun insertRadios(radios: List<SatRadio>)
suspend fun clearAllData()
suspend fun deleteEntries()
}

Wyświetl plik

@ -0,0 +1,32 @@
/*
* 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.data
import com.rtbishop.look4sat.domain.model.SatRadio
import kotlinx.coroutines.flow.Flow
interface ILocalRadioSource {
fun getRadiosTotal(): Flow<Int>
suspend fun getRadiosWithId(id: Int): List<SatRadio>
suspend fun insertRadios(radios: List<SatRadio>)
suspend fun deleteRadios()
}

Wyświetl plik

@ -15,15 +15,13 @@
* 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
package com.rtbishop.look4sat.domain.data
import java.io.InputStream
interface IProvider {
interface IRemoteDataSource {
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
suspend fun getLocalFileStream(uri: String): InputStream?
suspend fun getRemoteFileStream(url: String): InputStream?
suspend fun getDataStream(url: String): InputStream?
}

Wyświetl plik

@ -35,45 +35,45 @@ class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
init {
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
val a1 = (xke / super.data.xno).pow(twoThirds)
dsv.cosio = cos(super.data.xincl)
val a1 = (XKE / data.xno).pow(TWO_THIRDS)
dsv.cosio = cos(data.xincl)
dsv.theta2 = dsv.cosio * dsv.cosio
x3thm1 = 3.0 * dsv.theta2 - 1
dsv.eosq = super.data.eccn * super.data.eccn
dsv.eosq = data.eccn * data.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.data.xno / (1.0 + delo)
val del1 = 1.5 * CK2 * x3thm1 / (a1 * a1 * dsv.betao * dsv.betao2)
val ao = a1 * (1.0 - del1 * (0.5 * TWO_THIRDS + del1 * (1.0 + 134.0 / 81.0 * del1)))
val delo = 1.5 * CK2 * x3thm1 / (ao * ao * dsv.betao * dsv.betao2)
dsv.xnodp = data.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.data.eccn) - 1.0) * earthRadius)
setPerigee((dsv.aodp * (1.0 - data.eccn) - 1.0) * EARTH_RADIUS)
val pinvsq = invert(dsv.aodp * dsv.aodp * dsv.betao2 * dsv.betao2)
dsv.sing = sin(super.data.omegao)
dsv.cosg = cos(super.data.omegao)
dsv.sing = sin(data.omegao)
dsv.cosg = cos(data.omegao)
val tsi = invert(dsv.aodp - s4)
val eta = dsv.aodp * super.data.eccn * tsi
val eta = dsv.aodp * data.eccn * tsi
val etasq = eta * eta
val eeta = super.data.eccn * eta
val eeta = data.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.data.bstar * c2
dsv.sinio = sin(super.data.xincl)
val a3ovk2 = -j3Harmonic / ck2
+ 0.75 * CK2 * tsi / psisq * x3thm1 * (8.0 + 3.0 * etasq * (8.0 + etasq)))
c1 = data.bstar * c2
dsv.sinio = sin(data.xincl)
val a3ovk2 = -J3_HARMONIC / CK2
x1mth2 = 1.0 - dsv.theta2
c4 =
2 * dsv.xnodp * coef1 * dsv.aodp * dsv.betao2 * (eta * (2.0 + 0.5 * etasq) + super.data.eccn
* (0.5 + 2 * etasq) - 2 * ck2 * tsi / (dsv.aodp * psisq)
2 * dsv.xnodp * coef1 * dsv.aodp * dsv.betao2 * (eta * (2.0 + 0.5 * etasq) + data.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.data.omegao))))
* (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * data.omegao))))
val theta4 = dsv.theta2 * dsv.theta2
val temp1 = 3.0 * ck2 * pinvsq * dsv.xnodp
val temp2 = temp1 * ck2 * pinvsq
val temp3 = 1.25 * ck4 * pinvsq * pinvsq * dsv.xnodp
val temp1 = 3.0 * CK2 * pinvsq * dsv.xnodp
val temp2 = temp1 * CK2 * pinvsq
val temp3 = 1.25 * CK4 * pinvsq * pinvsq * dsv.xnodp
dsv.xmdot =
dsv.xnodp + 0.5 * temp1 * dsv.betao * x3thm1 + 0.0625 * temp2 * dsv.betao * (13 - 78 * dsv.theta2 + 137 * theta4)
val x1m5th = 1.0 - 5 * dsv.theta2
@ -105,12 +105,12 @@ class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
dsv.xn = dsv.xnodp
dsv.t = tSince
deep.dpsec(data)
val a = (xke / dsv.xn).pow(twoThirds) * tempa * tempa
val a = (XKE / dsv.xn).pow(TWO_THIRDS) * tempa * tempa
dsv.em = dsv.em - tempe
deep.dpper()
val xl = dsv.xll + dsv.omgadf + dsv.xnode
val beta = sqrt(1.0 - dsv.em * dsv.em)
dsv.xn = xke / a.pow(1.5)
dsv.xn = XKE / a.pow(1.5)
// Long period periodics
val axn = dsv.em * cos(dsv.omgadf)
@ -136,8 +136,8 @@ class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
val pl = a * temp[0]
temp[9] = a * (1.0 - ecose)
temp[1] = invert(temp[9])
temp[10] = xke * sqrt(a) * esine * temp[1]
temp[11] = xke * sqrt(pl) * temp[1]
temp[10] = XKE * sqrt(a) * esine * temp[1]
temp[11] = XKE * sqrt(pl) * temp[1]
temp[2] = a * temp[1]
val betal = sqrt(temp[0])
temp[3] = invert(1.0 + betal)
@ -147,7 +147,7 @@ class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
val sin2u = 2.0 * sinu * cosu
val cos2u = 2.0 * cosu * cosu - 1
temp[0] = invert(pl)
temp[1] = ck2 * temp[0]
temp[1] = CK2 * temp[0]
temp[2] = temp[1] * temp[0]
// Update for short periodics
@ -614,8 +614,8 @@ class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
} else {
applyPeriodics()
// This is a patch to Lyddane modification suggested by Rob Matson
if (abs(xnoh - dsv.xnode) > Math.PI) {
if (dsv.xnode < xnoh) dsv.xnode += twoPi else dsv.xnode -= twoPi
if (abs(xnoh - dsv.xnode) > PI) {
if (dsv.xnode < xnoh) dsv.xnode += TWO_PI else dsv.xnode -= TWO_PI
}
dsv.xll = dsv.xll + pl
dsv.omgadf = xls - dsv.xll - cos(dsv.xinc) * dsv.xnode
@ -631,8 +631,8 @@ class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
dsv.xinc = params.xincl + ssi * dsv.t
if (dsv.xinc < 0) {
dsv.xinc = -dsv.xinc
dsv.xnode = dsv.xnode + Math.PI
dsv.omgadf = dsv.omgadf - Math.PI
dsv.xnode = dsv.xnode + PI
dsv.omgadf = dsv.omgadf - PI
}
if (!resonance) return
do processEpochRestartLoop() while (doLoop && epochRestart)

Wyświetl plik

@ -53,25 +53,25 @@ class NearEarthSat(data: OrbitalData) : Satellite(data) {
init {
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
val a1 = (xke / super.data.xno).pow(twoThirds)
cosio = cos(super.data.xincl)
val a1 = (XKE / data.xno).pow(TWO_THIRDS)
cosio = cos(data.xincl)
val theta2 = sqr(cosio)
x3thm1 = 3.0 * theta2 - 1.0
val eo = super.data.eccn
val eo = data.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.data.xno / (1.0 + delo)
val del1 = 1.5 * CK2 * x3thm1 / (sqr(a1) * betao * betao2)
val ao = a1 * (1.0 - del1 * (0.5 * TWO_THIRDS + del1 * (1.0 + 134.0 / 81.0 * del1)))
val delo = 1.5 * CK2 * x3thm1 / (sqr(ao) * betao * betao2)
xnodp = data.xno / (1.0 + delo)
aodp = ao / (1.0 - delo)
// For perigee less than 220 kilometers, the "simple" flag is set
sgp4Simple = aodp * (1.0 - eo) < 220 / earthRadius + 1.0
sgp4Simple = aodp * (1.0 - eo) < 220 / EARTH_RADIUS + 1.0
// For perigees below 156 km, the values of S and QOMS2T are altered
setPerigee((aodp * (1.0 - eo) - 1.0) * earthRadius)
setPerigee((aodp * (1.0 - eo) - 1.0) * EARTH_RADIUS)
val pinvsq = invert(sqr(aodp) * sqr(betao2))
val tsi = invert(aodp - s4)
eta = aodp * eo * tsi
@ -80,24 +80,24 @@ class NearEarthSat(data: OrbitalData) : Satellite(data) {
val psisq = abs(1.0 - etasq)
val coef = qoms24 * tsi.pow(4.0)
val coef1 = coef / psisq.pow(3.5)
val bstar = super.data.bstar
val bstar = data.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)))
* CK2 * tsi / psisq * x3thm1 * (8.0 + 3.0 * etasq * (8.0 + etasq)))
c1 = bstar * c2
sinio = sin(super.data.xincl)
val a3ovk2 = -j3Harmonic / ck2
sinio = sin(data.xincl)
val a3ovk2 = -J3_HARMONIC / CK2
val c3 = coef * tsi * a3ovk2 * xnodp * sinio / eo
x1mth2 = 1.0 - theta2
val omegao = super.data.omegao
val omegao = data.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
- 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))
* cos(2.0 * omegao)))
c5 = 2.0 * coef1 * aodp * betao2 * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq)
val theta4 = sqr(theta2)
val temp1 = 3.0 * ck2 * pinvsq * xnodp
val temp2 = temp1 * ck2 * pinvsq
val temp3 = 1.25 * ck4 * pinvsq * pinvsq * xnodp
val temp1 = 3.0 * CK2 * pinvsq * xnodp
val temp2 = temp1 * CK2 * pinvsq
val temp3 = 1.25 * CK4 * pinvsq * pinvsq * xnodp
xmdot =
xnodp + 0.5 * temp1 * betao * x3thm1 + (0.0625 * temp2 * betao * (13.0 - 78.0 * theta2 + 137.0 * theta4))
val x1m5th = 1.0 - 5.0 * theta2
@ -107,12 +107,12 @@ class NearEarthSat(data: OrbitalData) : Satellite(data) {
xnodot =
xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * theta2) + 2.0 * temp3 * (3.0 - 7.0 * theta2)) * cosio
omgcof = bstar * c3 * cos(omegao)
xmcof = -twoThirds * coef * bstar / eeta
xmcof = -TWO_THIRDS * coef * bstar / eeta
xnodcf = 3.5 * betao2 * xhdot1 * c1
t2cof = 1.5 * c1
xlcof = 0.125 * a3ovk2 * sinio * (3.0 + 5 * cosio) / (1.0 + cosio)
aycof = 0.25 * a3ovk2 * sinio
val xmo = super.data.xmo
val xmo = data.xmo
delmo = (1.0 + eta * cos(xmo)).pow(3.0)
sinmo = sin(xmo)
x7thm1 = 7.0 * theta2 - 1
@ -166,7 +166,7 @@ class NearEarthSat(data: OrbitalData) : Satellite(data) {
val e = eo - tempe
val xl = xmp + omega + xnode + xnodp * templ
val beta = sqrt(1.0 - e * e)
val xn = xke / a.pow(1.5)
val xn = XKE / a.pow(1.5)
// Long period periodics
val axn = e * cos(omega)
@ -195,8 +195,8 @@ class NearEarthSat(data: OrbitalData) : Satellite(data) {
val pl = a * temp[0]
val r = a * (1.0 - ecose)
temp[1] = invert(r)
val rdot = xke * sqrt(a) * esine * temp[1]
val rfdot = xke * sqrt(pl) * temp[1]
val rdot = XKE * sqrt(a) * esine * temp[1]
val rfdot = XKE * sqrt(pl) * temp[1]
temp[2] = a * temp[1]
val betal = sqrt(temp[0])
temp[3] = invert(1.0 + betal)
@ -206,7 +206,7 @@ class NearEarthSat(data: OrbitalData) : Satellite(data) {
val sin2u = 2.0 * sinu * cosu
val cos2u = 2.0 * cosu * cosu - 1
temp[0] = invert(pl)
temp[1] = ck2 * temp[0]
temp[1] = CK2 * temp[0]
temp[2] = temp[1] * temp[0]
// Update for short periodics

Wyświetl plik

@ -28,11 +28,11 @@ data class OrbitalData(
val meanan: Double,
val catnum: Int,
val bstar: Double,
val xincl: Double = Math.toRadians(incl),
val xnodeo: Double = Math.toRadians(raan),
val omegao: Double = Math.toRadians(argper),
val xmo: Double = Math.toRadians(meanan),
val xno: Double = meanmo * Math.PI * 2.0 / 1440,
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 isDeepspace: Boolean = meanmo < 6.4
) {

Wyświetl plik

@ -30,15 +30,13 @@ data class SatPos(
var theta: Double = 0.0,
var time: Long = 0L
) {
private val earthRadiusKm = 6378.16
private val speedOfLight = 2.99792458E8
fun getDownlinkFreq(freq: Long): Long {
return (freq.toDouble() * (speedOfLight - distanceRate * 1000.0) / speedOfLight).toLong()
return (freq.toDouble() * (SPEED_OF_LIGHT - distanceRate * 1000.0) / SPEED_OF_LIGHT).toLong()
}
fun getUplinkFreq(freq: Long): Long {
return (freq.toDouble() * (speedOfLight + distanceRate * 1000.0) / speedOfLight).toLong()
return (freq.toDouble() * (SPEED_OF_LIGHT + distanceRate * 1000.0) / SPEED_OF_LIGHT).toLong()
}
fun getOrbitalVelocity(): Double {
@ -50,7 +48,7 @@ data class SatPos(
fun getRangeCircle(): List<GeoPos> {
val positions = mutableListOf<GeoPos>()
val beta = acos(earthRadiusKm / (earthRadiusKm + altitude))
val beta = acos(EARTH_RADIUS / (EARTH_RADIUS + altitude))
var tempAzimuth = 0
while (tempAzimuth < 360) {
val azimuth = tempAzimuth / 360.0 * 2.0 * Math.PI
@ -81,6 +79,6 @@ data class SatPos(
}
fun getRangeCircleRadiusKm(): Double {
return earthRadiusKm * acos(earthRadiusKm / (earthRadiusKm + altitude))
return EARTH_RADIUS * acos(EARTH_RADIUS / (EARTH_RADIUS + altitude))
}
}

Wyświetl plik

@ -19,36 +19,40 @@ package com.rtbishop.look4sat.domain.predict
import kotlin.math.*
const val PI = 3.141592653589793
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_FACTOR = 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 SPEED_OF_LIGHT = 2.99792458E8
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) {
private val flatFactor = 3.35281066474748E-3
private val deg2Rad = 1.745329251994330E-2
private val secPerDay = 8.6400E4
private val minPerDay = 1.44E3
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 / data.meanmo
val earthRadius = 6378.137
val j3Harmonic = -2.53881E-6
val twoPi = Math.PI * 2.0
val twoThirds = 2.0 / 3.0
val xke = 7.43669161E-2
val ck2 = 5.413079E-4
val ck4 = 6.209887E-7
var qoms24 = 0.0
var s4 = 0.0
internal fun willBeSeen(pos: GeoPos): Boolean {
return if (data.meanmo < 1e-8) false
else {
val sma = 331.25 * exp(ln(1440.0 / data.meanmo) * (2.0 / 3.0))
val apogee = sma * (1.0 + data.eccn) - earthRadius
val sma = 331.25 * exp(ln(MIN_PER_DAY / data.meanmo) * (2.0 / 3.0))
val apogee = sma * (1.0 + data.eccn) - EARTH_RADIUS
var lin = data.incl
if (lin >= 90.0) lin = 180.0 - lin
acos(earthRadius / (apogee + earthRadius)) + lin * deg2Rad > abs(pos.latitude * deg2Rad)
acos(EARTH_RADIUS / (apogee + EARTH_RADIUS)) + lin * DEG2RAD > abs(pos.latitude * DEG2RAD)
}
}
@ -58,7 +62,7 @@ abstract class Satellite(val data: OrbitalData) {
val julUTC = calcCurrentDaynum(time) + 2444238.5
// Convert satellite's epoch time to Julian and calculate time since epoch in minutes
val julEpoch = juliandDateOfEpoch(data.epoch)
val tsince = (julUTC - julEpoch) * minPerDay
val tsince = (julUTC - julEpoch) * MIN_PER_DAY
calculateSDP4orSGP4(tsince)
// Scale position and velocity vectors to km and km/sec
convertSatState(position, velocity)
@ -103,8 +107,8 @@ abstract class Satellite(val data: OrbitalData) {
// Converts the sat position and velocity vectors to km and km/sec
private fun convertSatState(pos: Vector4, vel: Vector4) {
scaleVector(earthRadius, pos)
scaleVector(earthRadius * minPerDay / secPerDay, vel)
scaleVector(EARTH_RADIUS, pos)
scaleVector(EARTH_RADIUS * MIN_PER_DAY / SEC_PER_DAY, vel)
}
// Calculates the topocentric coordinates of the object with ECI pos and vel at time
@ -134,16 +138,16 @@ abstract class Satellite(val data: OrbitalData) {
velocityVector.z - obsVel.z
)
magnitude(range)
val sinLat = sin(deg2Rad * gsPos.latitude)
val cosLat = cos(deg2Rad * gsPos.latitude)
val sinLat = sin(DEG2RAD * gsPos.latitude)
val cosLat = cos(DEG2RAD * gsPos.latitude)
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
var azim = atan(-topE / topS)
if (topS > 0.0) azim += Math.PI
if (azim < 0.0) azim += twoPi
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
@ -158,14 +162,14 @@ abstract class Satellite(val data: OrbitalData) {
obsVel: Vector4
) {
val mFactor = 7.292115E-5
gsPosTheta = 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)
invert(sqrt(1.0 + FLAT_FACTOR * (FLAT_FACTOR - 2) * sqr(sin(DEG2RAD * gsPos.latitude))))
val sq = sqr(1.0 - FLAT_FACTOR) * c
val achcp = (EARTH_RADIUS * c) * cos(DEG2RAD * gsPos.latitude)
obsPos.setXYZ(
achcp * cos(gsPosTheta), achcp * sin(gsPosTheta),
(earthRadius * sq) * sin(deg2Rad * gsPos.latitude)
(EARTH_RADIUS * sq) * sin(DEG2RAD * gsPos.latitude)
)
obsVel.setXYZ(-mFactor * obsPos.y, mFactor * obsPos.x, 0.0)
magnitude(obsPos)
@ -181,7 +185,7 @@ abstract class Satellite(val data: OrbitalData) {
satPos.theta = atan2(position.y, position.x)
satPos.longitude = mod2PI(satPos.theta - thetaGJD(time))
val r = sqrt(sqr(position.x) + sqr(position.y))
val e2 = flatFactor * (2.0 - flatFactor)
val e2 = FLAT_FACTOR * (2.0 - FLAT_FACTOR)
satPos.latitude = atan2(position.z, r)
var phi: Double
var c: Double
@ -190,13 +194,13 @@ abstract class Satellite(val data: OrbitalData) {
do {
phi = satPos.latitude
c = invert(sqrt(1.0 - e2 * sqr(sin(phi))))
satPos.latitude = atan2(position.z + earthRadius * c * e2 * sin(phi), r)
converged = abs(satPos.latitude - phi) < epsilon
satPos.latitude = atan2(position.z + EARTH_RADIUS * c * e2 * sin(phi), r)
converged = abs(satPos.latitude - phi) < EPSILON
} while (i++ < 10 && !converged)
satPos.altitude = r / cos(satPos.latitude) - earthRadius * c
satPos.altitude = r / cos(satPos.latitude) - EARTH_RADIUS * c
var temp = satPos.latitude
if (temp > Math.PI / 2.0) {
temp -= twoPi
temp -= TWO_PI
satPos.latitude = temp
}
}
@ -258,9 +262,9 @@ abstract class Satellite(val data: OrbitalData) {
// Calculates the modulus of 2 * PI
internal fun mod2PI(value: Double): Double {
var retVal = value
val i = (retVal / twoPi).toInt()
retVal -= i * twoPi
if (retVal < 0.0) retVal += twoPi
val i = (retVal / TWO_PI).toInt()
retVal -= i * TWO_PI
if (retVal < 0.0) retVal += TWO_PI
return retVal
}
@ -276,7 +280,7 @@ abstract class Satellite(val data: OrbitalData) {
temp[5] = axn * temp[8]
temp[6] = ayn * temp[7]
val epw = (capu - temp[4] + temp[3] - temp[2]) / (1.0 - temp[5] - temp[6]) + temp[2]
if (abs(epw - temp[2]) <= epsilon) converged = true else temp[2] = epw
if (abs(epw - temp[2]) <= EPSILON) converged = true else temp[2] = epw
} while (i++ < 10 && !converged)
}
@ -292,8 +296,8 @@ abstract class Satellite(val data: OrbitalData) {
qoms24 = 1.880279E-09
if (perigee < 156.0) {
s4 = if (perigee <= 98.0) 20.0 else perigee - 78.0
qoms24 = ((120 - s4) / earthRadius).pow(4.0)
s4 = s4 / earthRadius + 1.0
qoms24 = ((120 - s4) / EARTH_RADIUS).pow(4.0)
s4 = s4 / EARTH_RADIUS + 1.0
}
}
@ -314,9 +318,9 @@ abstract class Satellite(val data: OrbitalData) {
private fun modulus(arg1: Double): Double {
var returnValue = arg1
val i = floor(returnValue / secPerDay).toInt()
returnValue -= i * secPerDay
if (returnValue < 0.0) returnValue += secPerDay
val i = floor(returnValue / SEC_PER_DAY).toInt()
returnValue -= i * SEC_PER_DAY
if (returnValue < 0.0) returnValue += SEC_PER_DAY
return returnValue
}
@ -332,7 +336,7 @@ abstract class Satellite(val data: OrbitalData) {
val aJD = theJD - ut
val tu = (aJD - 2451545.0) / 36525.0
var gmst = 24110.54841 + tu * (8640184.812866 + tu * (0.093104 - tu * 6.2E-6))
gmst = modulus(gmst + secPerDay * earthRotPerSidDay * ut)
return twoPi * gmst / secPerDay
gmst = modulus(gmst + SEC_PER_DAY * earthRotPerSidDay * ut)
return TWO_PI * gmst / SEC_PER_DAY
}
}

Wyświetl plik

@ -17,23 +17,29 @@
*/
package com.rtbishop.look4sat.domain.predict
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.model.SatRadio
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.withContext
class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISatelliteManager {
private val _calculatedPasses = MutableSharedFlow<List<SatPass>>(replay = 1)
val calculatedPasses: SharedFlow<List<SatPass>> = _calculatedPasses
override val calculatedPasses: SharedFlow<List<SatPass>> = _calculatedPasses
suspend fun getSatPos(sat: Satellite, pos: GeoPos, time: Long): SatPos {
return withContext(predictorDispatcher) { sat.getPosition(pos, time) }
override suspend fun getPosition(sat: Satellite, pos: GeoPos, time: Long): SatPos {
return withContext(defaultDispatcher) { sat.getPosition(pos, time) }
}
suspend fun getSatTrack(sat: Satellite, pos: GeoPos, start: Long, end: Long): List<SatPos> {
return withContext(predictorDispatcher) {
override suspend fun getTrack(
sat: Satellite,
pos: GeoPos,
start: Long,
end: Long
): List<SatPos> {
return withContext(defaultDispatcher) {
val positions = mutableListOf<SatPos>()
var currentTime = start
while (currentTime < end) {
@ -44,8 +50,13 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
}
}
suspend fun processRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio> {
return withContext(predictorDispatcher) {
override suspend fun processRadios(
sat: Satellite,
pos: GeoPos,
radios: List<SatRadio>,
time: Long
): List<SatRadio> {
return withContext(defaultDispatcher) {
val satPos = sat.getPosition(pos, time)
val copiedList = radios.map { it.copy() }
copiedList.forEach { transmitter ->
@ -60,8 +71,8 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
}
}
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> {
return withContext(predictorDispatcher) {
override suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> {
return withContext(defaultDispatcher) {
passList.forEach { pass ->
if (!pass.isDeepSpace) {
val timeStart = pass.aosTime
@ -76,17 +87,17 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
}
}
suspend fun forceCalculation(
override suspend fun calculatePasses(
satList: List<Satellite>,
pos: GeoPos,
time: Long,
hoursAhead: Int = 8,
minElevation: Double = 16.0
hoursAhead: Int,
minElevation: Double
) {
if (satList.isEmpty()) {
_calculatedPasses.emit(emptyList())
} else {
withContext(predictorDispatcher) {
withContext(defaultDispatcher) {
val allPasses = mutableListOf<SatPass>()
satList.forEach { satellite ->
allPasses.addAll(satellite.getPasses(pos, time, hoursAhead))

Wyświetl plik

@ -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
package com.rtbishop.look4sat.utility
import com.rtbishop.look4sat.domain.model.SatRadio
import com.rtbishop.look4sat.domain.predict.OrbitalData
@ -26,42 +26,44 @@ import org.json.JSONObject
import java.io.InputStream
import kotlin.math.pow
class DataParser(private val parserDispatcher: CoroutineDispatcher) {
class DataParser(private val defaultDispatcher: CoroutineDispatcher) {
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> = withContext(parserDispatcher) {
val parsedItems = mutableListOf<OrbitalData>()
csvStream.bufferedReader().useLines { lines ->
lines.forEachIndexed { index, line ->
if (index != 0) {
val values = line.split(",")
parseCSV(values)?.let { tle -> parsedItems.add(tle) }
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> =
withContext(defaultDispatcher) {
val parsedItems = mutableListOf<OrbitalData>()
csvStream.bufferedReader().useLines { lines ->
lines.forEachIndexed { index, line ->
if (index != 0) {
val values = line.split(",")
parseCSV(values)?.let { tle -> parsedItems.add(tle) }
}
}
}
return@withContext parsedItems
}
return@withContext parsedItems
}
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> = withContext(parserDispatcher) {
val tleStrings = mutableListOf(String(), String(), String())
val parsedItems = mutableListOf<OrbitalData>()
var lineIndex = 0
tleStream.bufferedReader().forEachLine { line ->
tleStrings[lineIndex] = line
if (lineIndex < 2) {
lineIndex++
} else {
val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
if (!isLineOneValid && !isLineTwoValid) return@forEachLine
parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) }
lineIndex = 0
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> =
withContext(defaultDispatcher) {
val tleStrings = mutableListOf(String(), String(), String())
val parsedItems = mutableListOf<OrbitalData>()
var lineIndex = 0
tleStream.bufferedReader().forEachLine { line ->
tleStrings[lineIndex] = line
if (lineIndex < 2) {
lineIndex++
} else {
val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
if (!isLineOneValid && !isLineTwoValid) return@forEachLine
parseTLE(tleStrings)?.let { tle -> parsedItems.add(tle) }
lineIndex = 0
}
}
return@withContext parsedItems
}
return@withContext parsedItems
}
suspend fun parseJSONStream(jsonStream: InputStream): List<SatRadio> {
return withContext(parserDispatcher) {
return withContext(defaultDispatcher) {
val parsedItems = mutableListOf<SatRadio>()
try {
val jsonArray = JSONArray(jsonStream.bufferedReader().readText())

Wyświetl plik

@ -15,16 +15,18 @@
* 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
package com.rtbishop.look4sat.utility
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.SocketChannel
class DataReporter(reporterDispatcher: CoroutineDispatcher) {
class DataReporter(private val reporterScope: CoroutineScope) {
private val reporterScope = CoroutineScope(reporterDispatcher)
private var rotationSocketChannel: SocketChannel? = null
private var rotationReporting: Job? = null

Wyświetl plik

@ -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.domain
package com.rtbishop.look4sat.utility
import java.net.InetSocketAddress
import java.net.Socket

Wyświetl plik

@ -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.domain
package com.rtbishop.look4sat.utility
import com.rtbishop.look4sat.domain.predict.GeoPos

Wyświetl plik

@ -17,7 +17,7 @@
*/
package com.rtbishop.look4sat
import com.rtbishop.look4sat.data.DataParser
import com.rtbishop.look4sat.utility.DataParser
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest

Wyświetl plik

@ -17,7 +17,7 @@
*/
package com.rtbishop.look4sat
import com.rtbishop.look4sat.domain.QthConverter
import com.rtbishop.look4sat.utility.QthConverter
import org.junit.Test
class QthConverterTest {

Wyświetl plik

@ -6,8 +6,8 @@ buildscript {
}
plugins {
id "com.android.application" version '7.1.2' apply false
id "com.android.library" version '7.1.2' apply false
id "com.android.application" version "7.1.2" apply false
id "com.android.library" version "7.1.2" apply false
id "org.jetbrains.kotlin.android" version "1.6.10" apply false
}

Wyświetl plik

@ -14,4 +14,4 @@ dependencyResolutionManagement {
}
rootProject.name = "Look4Sat"
include ":app"
include ":data"
include ":base"