From 4ee74a67896bd62b5e00f6ceefb00733f884ed79 Mon Sep 17 00:00:00 2001
From: Arty Bishop <bishop.arty@gmail.com>
Date: Sun, 6 Mar 2022 11:37:47 +0000
Subject: [PATCH] Renamed and moved several classes

---
 app/build.gradle                              |  2 +-
 .../1.json                                    |  0
 ...{LocationHandler.kt => LocationManager.kt} | 16 ++--
 ...tationHandler.kt => OrientationManager.kt} | 10 +-
 ...{SettingsHandler.kt => SettingsManager.kt} |  4 +-
 .../data/{Provider.kt => FileDataSource.kt}   | 14 +--
 .../data/{StorageDb.kt => LocalDatabase.kt}   |  4 +-
 .../data/{Storage.kt => LocalEntrySource.kt}  | 36 +-------
 .../framework/data/LocalRadioSource.kt        | 52 +++++++++++
 .../framework/data/RemoteDataSource.kt        | 32 +++++++
 .../framework/data/{ => dao}/EntriesDao.kt    |  2 +-
 .../framework/data/{ => dao}/RadiosDao.kt     |  2 +-
 .../rtbishop/look4sat/injection/AppModule.kt  | 26 ------
 .../rtbishop/look4sat/injection/BaseModule.kt | 76 ++++++++++++++++
 .../rtbishop/look4sat/injection/DataModule.kt | 91 -------------------
 .../entriesScreen/EntriesViewModel.kt         |  8 +-
 .../presentation/entriesScreen/ModesDialog.kt |  4 +-
 .../presentation/mapScreen/MapViewModel.kt    | 22 ++---
 .../presentation/passesScreen/FilterDialog.kt |  4 +-
 .../passesScreen/PassesFragment.kt            |  2 +-
 .../passesScreen/PassesViewModel.kt           | 18 ++--
 .../presentation/radarScreen/RadarFragment.kt |  2 +-
 .../radarScreen/RadarViewModel.kt             | 34 +++----
 .../settingsScreen/LocatorDialog.kt           |  4 +-
 .../settingsScreen/PositionDialog.kt          |  4 +-
 .../settingsScreen/SettingsFragment.kt        |  4 +-
 .../settingsScreen/SettingsViewModel.kt       | 26 +++---
 .../settingsScreen/SourcesDialog.kt           |  4 +-
 {data => base}/.gitignore                     |  0
 {data => base}/build.gradle                   |  0
 .../look4sat/domain/IDataRepository.kt        |  2 +-
 .../look4sat/domain/ILocationManager.kt       |  2 +-
 .../look4sat/domain/ISatelliteManager.kt      | 34 +++++++
 .../look4sat/domain/ISettingsManager.kt       |  2 +-
 .../look4sat/domain/data/DataRepository.kt    | 50 +++++-----
 .../look4sat/domain/data/IFileDataSource.kt   | 25 +++++
 .../look4sat/domain/data/ILocalEntrySource.kt | 13 +--
 .../look4sat/domain/data/ILocalRadioSource.kt | 32 +++++++
 .../look4sat/domain/data/IRemoteDataSource.kt |  8 +-
 .../look4sat/domain/model/DataState.kt        |  0
 .../look4sat/domain/model/SatEntry.kt         |  0
 .../rtbishop/look4sat/domain/model/SatItem.kt |  0
 .../look4sat/domain/model/SatRadio.kt         |  0
 .../look4sat/domain/predict/DeepSpaceSat.kt   | 62 ++++++-------
 .../look4sat/domain/predict/GeoPos.kt         |  0
 .../look4sat/domain/predict/NearEarthSat.kt   | 48 +++++-----
 .../look4sat/domain/predict/OrbitalData.kt    | 10 +-
 .../look4sat/domain/predict/SatPass.kt        |  0
 .../look4sat/domain/predict/SatPos.kt         | 10 +-
 .../look4sat/domain/predict/Satellite.kt      | 90 +++++++++---------
 .../domain/predict/SatelliteManager.kt        | 39 +++++---
 .../rtbishop/look4sat/utility}/DataParser.kt  | 58 ++++++------
 .../look4sat/utility}/DataReporter.kt         | 10 +-
 .../rtbishop/look4sat/utility}/Extensions.kt  |  2 +-
 .../look4sat/utility}/QthConverter.kt         |  2 +-
 .../com/rtbishop/look4sat/DataParserTest.kt   |  2 +-
 .../com/rtbishop/look4sat/QthConverterTest.kt |  2 +-
 build.gradle                                  |  4 +-
 settings.gradle                               |  2 +-
 59 files changed, 563 insertions(+), 449 deletions(-)
 rename app/schemas/{com.rtbishop.look4sat.framework.data.StorageDb => com.rtbishop.look4sat.framework.data.LocalDatabase}/1.json (100%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/{LocationHandler.kt => LocationManager.kt} (93%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/{OrientationHandler.kt => OrientationManager.kt} (90%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/{SettingsHandler.kt => SettingsManager.kt} (97%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/data/{Provider.kt => FileDataSource.kt} (77%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/data/{StorageDb.kt => LocalDatabase.kt} (87%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/data/{Storage.kt => LocalEntrySource.kt} (63%)
 create mode 100644 app/src/main/java/com/rtbishop/look4sat/framework/data/LocalRadioSource.kt
 create mode 100644 app/src/main/java/com/rtbishop/look4sat/framework/data/RemoteDataSource.kt
 rename app/src/main/java/com/rtbishop/look4sat/framework/data/{ => dao}/EntriesDao.kt (96%)
 rename app/src/main/java/com/rtbishop/look4sat/framework/data/{ => dao}/RadiosDao.kt (96%)
 create mode 100644 app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt
 delete mode 100644 app/src/main/java/com/rtbishop/look4sat/injection/DataModule.kt
 rename {data => base}/.gitignore (100%)
 rename {data => base}/build.gradle (100%)
 rename data/src/main/java/com/rtbishop/look4sat/domain/IRepository.kt => base/src/main/java/com/rtbishop/look4sat/domain/IDataRepository.kt (98%)
 rename data/src/main/java/com/rtbishop/look4sat/domain/ILocationHandler.kt => base/src/main/java/com/rtbishop/look4sat/domain/ILocationManager.kt (97%)
 create mode 100644 base/src/main/java/com/rtbishop/look4sat/domain/ISatelliteManager.kt
 rename data/src/main/java/com/rtbishop/look4sat/domain/ISettings.kt => base/src/main/java/com/rtbishop/look4sat/domain/ISettingsManager.kt (98%)
 rename data/src/main/java/com/rtbishop/look4sat/data/Repository.kt => base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt (69%)
 create mode 100644 base/src/main/java/com/rtbishop/look4sat/domain/data/IFileDataSource.kt
 rename data/src/main/java/com/rtbishop/look4sat/data/IStorage.kt => base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalEntrySource.kt (80%)
 create mode 100644 base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalRadioSource.kt
 rename data/src/main/java/com/rtbishop/look4sat/data/IProvider.kt => base/src/main/java/com/rtbishop/look4sat/domain/data/IRemoteDataSource.kt (83%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/model/DataState.kt (100%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/model/SatEntry.kt (100%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/model/SatItem.kt (100%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/model/SatRadio.kt (100%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt (95%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/GeoPos.kt (100%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt (86%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt (84%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt (100%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt (86%)
 rename {data => base}/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt (81%)
 rename data/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt => base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt (87%)
 rename {data/src/main/java/com/rtbishop/look4sat/data => base/src/main/java/com/rtbishop/look4sat/utility}/DataParser.kt (80%)
 rename {data/src/main/java/com/rtbishop/look4sat/domain => base/src/main/java/com/rtbishop/look4sat/utility}/DataReporter.kt (91%)
 rename {data/src/main/java/com/rtbishop/look4sat/domain => base/src/main/java/com/rtbishop/look4sat/utility}/Extensions.kt (98%)
 rename {data/src/main/java/com/rtbishop/look4sat/domain => base/src/main/java/com/rtbishop/look4sat/utility}/QthConverter.kt (98%)
 rename {data => base}/src/test/java/com/rtbishop/look4sat/DataParserTest.kt (99%)
 rename {data => base}/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt (97%)

diff --git a/app/build.gradle b/app/build.gradle
index a44a13de..48c38e9d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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"
diff --git a/app/schemas/com.rtbishop.look4sat.framework.data.StorageDb/1.json b/app/schemas/com.rtbishop.look4sat.framework.data.LocalDatabase/1.json
similarity index 100%
rename from app/schemas/com.rtbishop.look4sat.framework.data.StorageDb/1.json
rename to app/schemas/com.rtbishop.look4sat.framework.data.LocalDatabase/1.json
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/LocationHandler.kt b/app/src/main/java/com/rtbishop/look4sat/framework/LocationManager.kt
similarity index 93%
rename from app/src/main/java/com/rtbishop/look4sat/framework/LocationHandler.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/LocationManager.kt
index 7d508471..3658d3e6 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/LocationHandler.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/LocationManager.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/OrientationHandler.kt b/app/src/main/java/com/rtbishop/look4sat/framework/OrientationManager.kt
similarity index 90%
rename from app/src/main/java/com/rtbishop/look4sat/framework/OrientationHandler.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/OrientationManager.kt
index c3a7368c..ab2b2143 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/OrientationHandler.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/OrientationManager.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/SettingsHandler.kt b/app/src/main/java/com/rtbishop/look4sat/framework/SettingsManager.kt
similarity index 97%
rename from app/src/main/java/com/rtbishop/look4sat/framework/SettingsHandler.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/SettingsManager.kt
index d4af3437..ed04678e 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/SettingsHandler.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/SettingsManager.kt
@@ -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"
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/Provider.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/FileDataSource.kt
similarity index 77%
rename from app/src/main/java/com/rtbishop/look4sat/framework/data/Provider.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/data/FileDataSource.kt
index aaba4089..afa5d73d 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/data/Provider.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/FileDataSource.kt
@@ -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() }
-    }
 }
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/StorageDb.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt
similarity index 87%
rename from app/src/main/java/com/rtbishop/look4sat/framework/data/StorageDb.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt
index 4bf5c2ee..9bf761e3 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/data/StorageDb.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt
@@ -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
 
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/Storage.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt
similarity index 63%
rename from app/src/main/java/com/rtbishop/look4sat/framework/data/Storage.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt
index c6d17104..61c870b9 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/data/Storage.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt
@@ -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() }
 }
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalRadioSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalRadioSource.kt
new file mode 100644
index 00000000..904fe9da
--- /dev/null
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalRadioSource.kt
@@ -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() }
+}
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/RemoteDataSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/RemoteDataSource.kt
new file mode 100644
index 00000000..100293e3
--- /dev/null
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/RemoteDataSource.kt
@@ -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()
+    }
+}
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/EntriesDao.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/dao/EntriesDao.kt
similarity index 96%
rename from app/src/main/java/com/rtbishop/look4sat/framework/data/EntriesDao.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/data/dao/EntriesDao.kt
index ecff8711..ef4d972e 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/data/EntriesDao.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/dao/EntriesDao.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/RadiosDao.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/dao/RadiosDao.kt
similarity index 96%
rename from app/src/main/java/com/rtbishop/look4sat/framework/data/RadiosDao.kt
rename to app/src/main/java/com/rtbishop/look4sat/framework/data/dao/RadiosDao.kt
index 3e0d42ae..b152ca42 100644
--- a/app/src/main/java/com/rtbishop/look4sat/framework/data/RadiosDao.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/dao/RadiosDao.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt b/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt
index f93d43f4..1f4eeb47 100644
--- a/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/injection/AppModule.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt b/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt
new file mode 100644
index 00000000..d1caffc8
--- /dev/null
+++ b/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt
@@ -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
+}
diff --git a/app/src/main/java/com/rtbishop/look4sat/injection/DataModule.kt b/app/src/main/java/com/rtbishop/look4sat/injection/DataModule.kt
deleted file mode 100644
index 0fa812e9..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/injection/DataModule.kt
+++ /dev/null
@@ -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)
-    }
-}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt
index e3090ac7..35c438df 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt
@@ -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())
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/ModesDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/ModesDialog.kt
index 6c1c9547..432f66ff 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/ModesDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/ModesDialog.kt
@@ -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",
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
index 96d66fdb..2fd4feff 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
@@ -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)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt
index 4e5136f5..3a389d54 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt
@@ -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)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt
index a1b60aed..4dc3d25f 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt
index 249a1a36..2c3570d3 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt
@@ -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)
         }
     }
 }
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt
index 8278ba06..6884bb64 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewModel.kt
index 9d85cb51..33b2106f 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarViewModel.kt
@@ -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)
             }
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/LocatorDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/LocatorDialog.kt
index 6c6f6b3e..7c98c377 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/LocatorDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/LocatorDialog.kt
@@ -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)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/PositionDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/PositionDialog.kt
index 7b695a08..b10aeb81 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/PositionDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/PositionDialog.kt
@@ -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)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt
index 57e835d1..4158e399 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt
@@ -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
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsViewModel.kt
index a4fd83aa..ac78f754 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsViewModel.kt
@@ -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()
 }
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SourcesDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SourcesDialog.kt
index 4cc64d05..e153c673 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SourcesDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SourcesDialog.kt
@@ -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
 
diff --git a/data/.gitignore b/base/.gitignore
similarity index 100%
rename from data/.gitignore
rename to base/.gitignore
diff --git a/data/build.gradle b/base/build.gradle
similarity index 100%
rename from data/build.gradle
rename to base/build.gradle
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/IRepository.kt b/base/src/main/java/com/rtbishop/look4sat/domain/IDataRepository.kt
similarity index 98%
rename from data/src/main/java/com/rtbishop/look4sat/domain/IRepository.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/IDataRepository.kt
index d315ec90..5388d73d 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/IRepository.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/IDataRepository.kt
@@ -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>>
 
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/ILocationHandler.kt b/base/src/main/java/com/rtbishop/look4sat/domain/ILocationManager.kt
similarity index 97%
rename from data/src/main/java/com/rtbishop/look4sat/domain/ILocationHandler.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/ILocationManager.kt
index d51202cc..55493384 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/ILocationHandler.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/ILocationManager.kt
@@ -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>>
 
diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/ISatelliteManager.kt b/base/src/main/java/com/rtbishop/look4sat/domain/ISatelliteManager.kt
new file mode 100644
index 00000000..acc8f6e1
--- /dev/null
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/ISatelliteManager.kt
@@ -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
+    )
+}
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/ISettings.kt b/base/src/main/java/com/rtbishop/look4sat/domain/ISettingsManager.kt
similarity index 98%
rename from data/src/main/java/com/rtbishop/look4sat/domain/ISettings.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/ISettingsManager.kt
index 17b97a9e..17ec4b63 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/ISettings.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/ISettingsManager.kt
@@ -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(
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/Repository.kt b/base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt
similarity index 69%
rename from data/src/main/java/com/rtbishop/look4sat/data/Repository.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt
index 1d851254..42eba294 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/Repository.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt
@@ -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)
         }
     }
diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/data/IFileDataSource.kt b/base/src/main/java/com/rtbishop/look4sat/domain/data/IFileDataSource.kt
new file mode 100644
index 00000000..51b4d912
--- /dev/null
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/data/IFileDataSource.kt
@@ -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?
+}
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/IStorage.kt b/base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalEntrySource.kt
similarity index 80%
rename from data/src/main/java/com/rtbishop/look4sat/data/IStorage.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalEntrySource.kt
index 60ce731c..a9692d8c 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/IStorage.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalEntrySource.kt
@@ -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()
 }
diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalRadioSource.kt b/base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalRadioSource.kt
new file mode 100644
index 00000000..8418ef01
--- /dev/null
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/data/ILocalRadioSource.kt
@@ -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()
+}
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/IProvider.kt b/base/src/main/java/com/rtbishop/look4sat/domain/data/IRemoteDataSource.kt
similarity index 83%
rename from data/src/main/java/com/rtbishop/look4sat/data/IProvider.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/data/IRemoteDataSource.kt
index 23de2d10..3468702d 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/IProvider.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/data/IRemoteDataSource.kt
@@ -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?
 }
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/model/DataState.kt b/base/src/main/java/com/rtbishop/look4sat/domain/model/DataState.kt
similarity index 100%
rename from data/src/main/java/com/rtbishop/look4sat/domain/model/DataState.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/model/DataState.kt
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/model/SatEntry.kt b/base/src/main/java/com/rtbishop/look4sat/domain/model/SatEntry.kt
similarity index 100%
rename from data/src/main/java/com/rtbishop/look4sat/domain/model/SatEntry.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/model/SatEntry.kt
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/model/SatItem.kt b/base/src/main/java/com/rtbishop/look4sat/domain/model/SatItem.kt
similarity index 100%
rename from data/src/main/java/com/rtbishop/look4sat/domain/model/SatItem.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/model/SatItem.kt
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/model/SatRadio.kt b/base/src/main/java/com/rtbishop/look4sat/domain/model/SatRadio.kt
similarity index 100%
rename from data/src/main/java/com/rtbishop/look4sat/domain/model/SatRadio.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/model/SatRadio.kt
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt
similarity index 95%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt
index 8ede45f3..98fb8dab 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/DeepSpaceSat.kt
@@ -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)
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/GeoPos.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/GeoPos.kt
similarity index 100%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/GeoPos.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/GeoPos.kt
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt
similarity index 86%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt
index ba4ffc45..6c64dfdf 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/NearEarthSat.kt
@@ -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
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt
similarity index 84%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt
index 283db460..37065018 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt
@@ -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
 ) {
 
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt
similarity index 100%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt
similarity index 86%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt
index 5ae5b682..e54c1230 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPos.kt
@@ -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))
     }
 }
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt
similarity index 81%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt
index 08d5525f..2e7e6035 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt
@@ -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
     }
 }
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt
similarity index 87%
rename from data/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt
rename to base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt
index 46f316f7..623fe251 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt
@@ -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))
diff --git a/data/src/main/java/com/rtbishop/look4sat/data/DataParser.kt b/base/src/main/java/com/rtbishop/look4sat/utility/DataParser.kt
similarity index 80%
rename from data/src/main/java/com/rtbishop/look4sat/data/DataParser.kt
rename to base/src/main/java/com/rtbishop/look4sat/utility/DataParser.kt
index 7a1ecea6..23a95de2 100644
--- a/data/src/main/java/com/rtbishop/look4sat/data/DataParser.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/utility/DataParser.kt
@@ -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())
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/DataReporter.kt b/base/src/main/java/com/rtbishop/look4sat/utility/DataReporter.kt
similarity index 91%
rename from data/src/main/java/com/rtbishop/look4sat/domain/DataReporter.kt
rename to base/src/main/java/com/rtbishop/look4sat/utility/DataReporter.kt
index 9212cf04..b670a921 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/DataReporter.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/utility/DataReporter.kt
@@ -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
 
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/Extensions.kt b/base/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt
similarity index 98%
rename from data/src/main/java/com/rtbishop/look4sat/domain/Extensions.kt
rename to base/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt
index 00f3c003..3c26b063 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/Extensions.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/utility/Extensions.kt
@@ -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
diff --git a/data/src/main/java/com/rtbishop/look4sat/domain/QthConverter.kt b/base/src/main/java/com/rtbishop/look4sat/utility/QthConverter.kt
similarity index 98%
rename from data/src/main/java/com/rtbishop/look4sat/domain/QthConverter.kt
rename to base/src/main/java/com/rtbishop/look4sat/utility/QthConverter.kt
index 8413fc6b..12bcb946 100644
--- a/data/src/main/java/com/rtbishop/look4sat/domain/QthConverter.kt
+++ b/base/src/main/java/com/rtbishop/look4sat/utility/QthConverter.kt
@@ -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
 
diff --git a/data/src/test/java/com/rtbishop/look4sat/DataParserTest.kt b/base/src/test/java/com/rtbishop/look4sat/DataParserTest.kt
similarity index 99%
rename from data/src/test/java/com/rtbishop/look4sat/DataParserTest.kt
rename to base/src/test/java/com/rtbishop/look4sat/DataParserTest.kt
index 8a1b938e..0b4da953 100644
--- a/data/src/test/java/com/rtbishop/look4sat/DataParserTest.kt
+++ b/base/src/test/java/com/rtbishop/look4sat/DataParserTest.kt
@@ -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
diff --git a/data/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt b/base/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt
similarity index 97%
rename from data/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt
rename to base/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt
index ecdb32e6..e5f0317d 100644
--- a/data/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt
+++ b/base/src/test/java/com/rtbishop/look4sat/QthConverterTest.kt
@@ -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 {
diff --git a/build.gradle b/build.gradle
index 0e9b3697..5e9a22d8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -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
 }
 
diff --git a/settings.gradle b/settings.gradle
index 701ee240..2f09b498 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -14,4 +14,4 @@ dependencyResolutionManagement {
 }
 rootProject.name = "Look4Sat"
 include ":app"
-include ":data"
+include ":base"