kopia lustrzana https://github.com/rt-bishop/Look4Sat
Destructively migrated to the new database
rodzic
92eec290b1
commit
64b5010a44
|
@ -89,4 +89,4 @@ dependencies {
|
|||
androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"
|
||||
androidTestImplementation "org.mockito:mockito-android:4.3.1"
|
||||
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,111 +1,111 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 5,
|
||||
"identityHash": "0b523e732e116bbdcccd8188922898e2",
|
||||
"version": 1,
|
||||
"identityHash": "834981e960b712254e56b633f3772115",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isSelected` INTEGER NOT NULL, `name` TEXT NOT NULL, `epoch` REAL NOT NULL, `meanmo` REAL NOT NULL, `eccn` REAL NOT NULL, `incl` REAL NOT NULL, `raan` REAL NOT NULL, `argper` REAL NOT NULL, `meanan` REAL NOT NULL, `catnum` INTEGER NOT NULL, `bstar` REAL NOT NULL, `xincl` REAL NOT NULL, `xnodeo` REAL NOT NULL, `omegao` REAL NOT NULL, `xmo` REAL NOT NULL, `xno` REAL NOT NULL, `isDeepspace` INTEGER NOT NULL, PRIMARY KEY(`catnum`))",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`comment` TEXT, `name` TEXT NOT NULL, `epoch` REAL NOT NULL, `meanmo` REAL NOT NULL, `eccn` REAL NOT NULL, `incl` REAL NOT NULL, `raan` REAL NOT NULL, `argper` REAL NOT NULL, `meanan` REAL NOT NULL, `catnum` INTEGER NOT NULL, `bstar` REAL NOT NULL, `xincl` REAL NOT NULL, `xnodeo` REAL NOT NULL, `omegao` REAL NOT NULL, `xmo` REAL NOT NULL, `xno` REAL NOT NULL, `isDeepspace` INTEGER NOT NULL, PRIMARY KEY(`catnum`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "isSelected",
|
||||
"columnName": "isSelected",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
"fieldPath": "comment",
|
||||
"columnName": "comment",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.name",
|
||||
"fieldPath": "data.name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.epoch",
|
||||
"fieldPath": "data.epoch",
|
||||
"columnName": "epoch",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.meanmo",
|
||||
"fieldPath": "data.meanmo",
|
||||
"columnName": "meanmo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.eccn",
|
||||
"fieldPath": "data.eccn",
|
||||
"columnName": "eccn",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.incl",
|
||||
"fieldPath": "data.incl",
|
||||
"columnName": "incl",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.raan",
|
||||
"fieldPath": "data.raan",
|
||||
"columnName": "raan",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.argper",
|
||||
"fieldPath": "data.argper",
|
||||
"columnName": "argper",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.meanan",
|
||||
"fieldPath": "data.meanan",
|
||||
"columnName": "meanan",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.catnum",
|
||||
"fieldPath": "data.catnum",
|
||||
"columnName": "catnum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.bstar",
|
||||
"fieldPath": "data.bstar",
|
||||
"columnName": "bstar",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xincl",
|
||||
"fieldPath": "data.xincl",
|
||||
"columnName": "xincl",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xnodeo",
|
||||
"fieldPath": "data.xnodeo",
|
||||
"columnName": "xnodeo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.omegao",
|
||||
"fieldPath": "data.omegao",
|
||||
"columnName": "omegao",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xmo",
|
||||
"fieldPath": "data.xmo",
|
||||
"columnName": "xmo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xno",
|
||||
"fieldPath": "data.xno",
|
||||
"columnName": "xno",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.isDeepspace",
|
||||
"fieldPath": "data.isDeepspace",
|
||||
"columnName": "isDeepspace",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
|
@ -121,8 +121,8 @@
|
|||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "transmitters",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catnum` INTEGER, PRIMARY KEY(`uuid`))",
|
||||
"tableName": "radios",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catnum` INTEGER, `comment` TEXT, PRIMARY KEY(`uuid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uuid",
|
||||
|
@ -171,6 +171,12 @@
|
|||
"columnName": "catnum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "comment",
|
||||
"columnName": "comment",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
@ -186,7 +192,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0b523e732e116bbdcccd8188922898e2')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '834981e960b712254e56b633f3772115')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "555494c464b5a29285eb5493cc8aa5f6",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tle` TEXT NOT NULL, `catNum` INTEGER NOT NULL, `name` TEXT NOT NULL, `isSelected` INTEGER NOT NULL, PRIMARY KEY(`catNum`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tle",
|
||||
"columnName": "tle",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "catNum",
|
||||
"columnName": "catNum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSelected",
|
||||
"columnName": "isSelected",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"catNum"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "transmitters",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catNum` INTEGER, PRIMARY KEY(`uuid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uuid",
|
||||
"columnName": "uuid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "info",
|
||||
"columnName": "info",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlive",
|
||||
"columnName": "isAlive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "downlink",
|
||||
"columnName": "downlink",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uplink",
|
||||
"columnName": "uplink",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "mode",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isInverted",
|
||||
"columnName": "isInverted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "catNum",
|
||||
"columnName": "catNum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"uuid"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, PRIMARY KEY(`sourceUrl`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "sourceUrl",
|
||||
"columnName": "sourceUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"sourceUrl"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '555494c464b5a29285eb5493cc8aa5f6')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "555494c464b5a29285eb5493cc8aa5f6",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tle` TEXT NOT NULL, `catNum` INTEGER NOT NULL, `name` TEXT NOT NULL, `isSelected` INTEGER NOT NULL, PRIMARY KEY(`catNum`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tle",
|
||||
"columnName": "tle",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "catNum",
|
||||
"columnName": "catNum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSelected",
|
||||
"columnName": "isSelected",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"catNum"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "transmitters",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catNum` INTEGER, PRIMARY KEY(`uuid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uuid",
|
||||
"columnName": "uuid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "info",
|
||||
"columnName": "info",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlive",
|
||||
"columnName": "isAlive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "downlink",
|
||||
"columnName": "downlink",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uplink",
|
||||
"columnName": "uplink",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "mode",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isInverted",
|
||||
"columnName": "isInverted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "catNum",
|
||||
"columnName": "catNum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"uuid"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, PRIMARY KEY(`sourceUrl`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "sourceUrl",
|
||||
"columnName": "sourceUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"sourceUrl"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '555494c464b5a29285eb5493cc8aa5f6')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "0f08defa24a78647801005e9277faac7",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isSelected` INTEGER NOT NULL, `name` TEXT NOT NULL, `epoch` REAL NOT NULL, `meanmo` REAL NOT NULL, `eccn` REAL NOT NULL, `incl` REAL NOT NULL, `raan` REAL NOT NULL, `argper` REAL NOT NULL, `meanan` REAL NOT NULL, `catnum` INTEGER NOT NULL, `bstar` REAL NOT NULL, `xincl` REAL NOT NULL, `xnodeo` REAL NOT NULL, `omegao` REAL NOT NULL, `xmo` REAL NOT NULL, `xno` REAL NOT NULL, `isDeepspace` INTEGER NOT NULL, PRIMARY KEY(`catnum`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "isSelected",
|
||||
"columnName": "isSelected",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.epoch",
|
||||
"columnName": "epoch",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.meanmo",
|
||||
"columnName": "meanmo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.eccn",
|
||||
"columnName": "eccn",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.incl",
|
||||
"columnName": "incl",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.raan",
|
||||
"columnName": "raan",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.argper",
|
||||
"columnName": "argper",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.meanan",
|
||||
"columnName": "meanan",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.catnum",
|
||||
"columnName": "catnum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.bstar",
|
||||
"columnName": "bstar",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xincl",
|
||||
"columnName": "xincl",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xnodeo",
|
||||
"columnName": "xnodeo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.omegao",
|
||||
"columnName": "omegao",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xmo",
|
||||
"columnName": "xmo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.xno",
|
||||
"columnName": "xno",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tle.isDeepspace",
|
||||
"columnName": "isDeepspace",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"catnum"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "transmitters",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catnum` INTEGER, PRIMARY KEY(`uuid`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "uuid",
|
||||
"columnName": "uuid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "info",
|
||||
"columnName": "info",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isAlive",
|
||||
"columnName": "isAlive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "downlink",
|
||||
"columnName": "downlink",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uplink",
|
||||
"columnName": "uplink",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "mode",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isInverted",
|
||||
"columnName": "isInverted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "catnum",
|
||||
"columnName": "catnum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"uuid"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, PRIMARY KEY(`sourceUrl`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "sourceUrl",
|
||||
"columnName": "sourceUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"sourceUrl"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0f08defa24a78647801005e9277faac7')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.rtbishop.bbctestapp
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
|
@ -6,17 +6,12 @@ import org.junit.Assert.assertEquals
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.rtbishop.bbctestapp", appContext.packageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
@ -19,29 +19,29 @@ package com.rtbishop.look4sat.framework
|
|||
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem as DomainItem
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter as DomainTransmitter
|
||||
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.Transmitter as FrameworkTransmitter
|
||||
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
|
||||
|
||||
fun DomainEntry.toFramework() = FrameworkEntry(this.tle, this.isSelected)
|
||||
fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
|
||||
|
||||
fun DomainTransmitter.toFramework() = FrameworkTransmitter(
|
||||
this.uuid, this.info, this.isAlive, this.downlink,
|
||||
this.uplink, this.mode, this.isInverted, this.catnum
|
||||
fun DomainRadio.toFramework() = FrameworkRadio(
|
||||
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
|
||||
this.mode, this.isInverted, this.catnum, this.comment
|
||||
)
|
||||
|
||||
fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.isSelected, this.modes)
|
||||
fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.modes, false)
|
||||
|
||||
fun FrameworkTransmitter.toDomain() = DomainTransmitter(
|
||||
this.uuid, this.info, this.isAlive, this.downlink,
|
||||
this.uplink, this.mode, this.isInverted, this.catnum
|
||||
fun FrameworkRadio.toDomain() = DomainRadio(
|
||||
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
|
||||
this.mode, this.isInverted, this.catnum, this.comment
|
||||
)
|
||||
|
||||
fun List<DomainEntry>.toFrameworkEntries() = this.map { it.toFramework() }
|
||||
|
||||
fun List<DomainTransmitter>.toFramework() = this.map { it.toFramework() }
|
||||
fun List<DomainRadio>.toFrameworkRadios() = this.map { it.toFramework() }
|
||||
|
||||
fun List<FrameworkItem>.toDomainItems() = this.map { it.toDomain() }
|
||||
|
||||
fun List<FrameworkTransmitter>.toDomain() = this.map { it.toDomain() }
|
||||
fun List<FrameworkRadio>.toDomainRadios() = this.map { it.toDomain() }
|
||||
|
|
|
@ -61,12 +61,12 @@ class SettingsHandler @Inject constructor(private val prefs: SharedPreferences)
|
|||
}
|
||||
}
|
||||
|
||||
override fun saveSatelliteSelection(catnums: List<Int>) {
|
||||
override fun saveEntriesSelection(catnums: List<Int>) {
|
||||
val stringList = catnums.map { catnum -> catnum.toString() }
|
||||
prefs.edit { putStringSet(keySelection, stringList.toSet()) }
|
||||
}
|
||||
|
||||
override fun loadSatelliteSelection(): List<Int> {
|
||||
override fun loadEntriesSelection(): List<Int> {
|
||||
val catnums = prefs.getStringSet(keySelection, emptySet())?.map { catnum -> catnum.toInt() }
|
||||
return catnums?.sorted() ?: emptyList()
|
||||
}
|
||||
|
|
|
@ -20,52 +20,77 @@ package com.rtbishop.look4sat.framework.local
|
|||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import com.rtbishop.look4sat.data.ILocalSource
|
||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.framework.toDomain
|
||||
import com.rtbishop.look4sat.framework.toDomainItems
|
||||
import com.rtbishop.look4sat.framework.toFramework
|
||||
import com.rtbishop.look4sat.framework.toDomainRadios
|
||||
import com.rtbishop.look4sat.framework.toFrameworkEntries
|
||||
import com.rtbishop.look4sat.framework.toFrameworkRadios
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.InputStream
|
||||
|
||||
class LocalSource(
|
||||
private val entriesDao: SatEntriesDao,
|
||||
private val radiosDao: SatRadiosDao,
|
||||
private val settings: ISettingsHandler,
|
||||
private val resolver: ContentResolver,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
private val entriesDao: EntriesDao,
|
||||
private val transmittersDao: TransmittersDao,
|
||||
private val ioDispatcher: CoroutineDispatcher
|
||||
) : ILocalSource {
|
||||
|
||||
override suspend fun getAllSatellites(): List<SatItem> {
|
||||
return entriesDao.getAllSatellites().toDomainItems()
|
||||
override suspend fun getEntriesWithModes(): List<SatItem> {
|
||||
val selectedCatnums = getEntriesSelection()
|
||||
val entriesWithModes = entriesDao.getEntriesWithModes().toDomainItems()
|
||||
entriesWithModes.forEach { entry -> entry.isSelected = entry.catnum in selectedCatnums }
|
||||
return entriesWithModes
|
||||
}
|
||||
|
||||
override suspend fun getSelectedSatellites(catnums: List<Int>): List<Satellite> {
|
||||
return entriesDao.getSelectedSatellites(catnums).map { entry -> entry.tle.createSat() }
|
||||
override suspend fun getSelectedEntries(): List<Satellite> {
|
||||
val selectedSatellites = mutableListOf<Satellite>()
|
||||
getEntriesSelection().chunked(999).forEach { catnums ->
|
||||
val entries = entriesDao.getSelectedEntries(catnums)
|
||||
selectedSatellites.addAll(entries.map { entry -> entry.data.createSat() })
|
||||
}
|
||||
return selectedSatellites
|
||||
}
|
||||
|
||||
override suspend fun getTransmitters(catnum: Int): List<Transmitter> {
|
||||
return transmittersDao.getTransmitters(catnum).toDomain()
|
||||
override suspend fun getRadios(catnum: Int): List<SatRadio> {
|
||||
return radiosDao.getRadios(catnum).toDomainRadios()
|
||||
}
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun getFileStream(uri: String): InputStream? {
|
||||
return withContext(ioDispatcher) { resolver.openInputStream(Uri.parse(uri)) }
|
||||
}
|
||||
|
||||
override suspend fun updateEntries(entries: List<SatEntry>) {
|
||||
override suspend fun insertEntries(entries: List<SatEntry>) {
|
||||
entriesDao.insertEntries(entries.toFrameworkEntries())
|
||||
}
|
||||
|
||||
override suspend fun updateTransmitters(transmitters: List<Transmitter>) {
|
||||
transmittersDao.updateTransmitters(transmitters.toFramework())
|
||||
override suspend fun insertRadios(radios: List<SatRadio>) {
|
||||
radiosDao.insertRadios(radios.toFrameworkRadios())
|
||||
}
|
||||
|
||||
override suspend fun clearAllData() {
|
||||
entriesDao.deleteEntries()
|
||||
transmittersDao.deleteTransmitters()
|
||||
radiosDao.deleteRadios()
|
||||
}
|
||||
|
||||
override suspend fun getDataSources(): List<String> {
|
||||
return withContext(ioDispatcher) { settings.loadDataSources() }
|
||||
}
|
||||
|
||||
override suspend fun setDataSources(sources: List<String>) {
|
||||
withContext(ioDispatcher) { settings.saveDataSources(sources) }
|
||||
}
|
||||
|
||||
override suspend fun getEntriesSelection(): List<Int> {
|
||||
return withContext(ioDispatcher) { settings.loadEntriesSelection() }
|
||||
}
|
||||
|
||||
override suspend fun setEntriesSelection(catnums: List<Int>) {
|
||||
withContext(ioDispatcher) { settings.saveEntriesSelection(catnums) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework.local
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
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 Look4SatDb : RoomDatabase() {
|
||||
|
||||
abstract fun entriesDao(): SatEntriesDao
|
||||
|
||||
abstract fun radiosDao(): SatRadiosDao
|
||||
}
|
|
@ -22,23 +22,15 @@ import com.rtbishop.look4sat.framework.model.SatEntry
|
|||
import com.rtbishop.look4sat.framework.model.SatItem
|
||||
|
||||
@Dao
|
||||
interface EntriesDao {
|
||||
interface SatEntriesDao {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT catnum, name, isSelected FROM entries ORDER BY name ASC")
|
||||
suspend fun getAllSatellites(): List<SatItem>
|
||||
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
|
||||
suspend fun getEntriesWithModes(): List<SatItem>
|
||||
|
||||
@Transaction
|
||||
suspend fun getSelectedSatellites(catnums: List<Int>): List<SatEntry> {
|
||||
val selectedSatellites = mutableListOf<SatEntry>()
|
||||
catnums.chunked(999).forEach { chunkedList ->
|
||||
selectedSatellites.addAll(getSelectedSatellitesChunked(chunkedList))
|
||||
}
|
||||
return selectedSatellites
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM entries WHERE catnum IN (:catnums)")
|
||||
suspend fun getSelectedSatellitesChunked(catnums: List<Int>): List<SatEntry>
|
||||
suspend fun getSelectedEntries(catnums: List<Int>): List<SatEntry>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertEntries(entries: List<SatEntry>)
|
|
@ -17,24 +17,21 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.framework.local
|
||||
|
||||
import androidx.room.*
|
||||
import com.rtbishop.look4sat.framework.model.Transmitter
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.rtbishop.look4sat.framework.model.SatRadio
|
||||
|
||||
@Dao
|
||||
interface TransmittersDao {
|
||||
interface SatRadiosDao {
|
||||
|
||||
@Query("SELECT * FROM transmitters WHERE catnum = :catnum AND isAlive = 1")
|
||||
suspend fun getTransmitters(catnum: Int): List<Transmitter>
|
||||
@Query("SELECT * FROM radios WHERE catnum = :catnum AND isAlive = 1")
|
||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertTransmitters(transmitters: List<Transmitter>)
|
||||
suspend fun insertRadios(radios: List<SatRadio>)
|
||||
|
||||
@Transaction
|
||||
suspend fun updateTransmitters(transmitters: List<Transmitter>) {
|
||||
deleteTransmitters()
|
||||
insertTransmitters(transmitters)
|
||||
}
|
||||
|
||||
@Query("DELETE FROM transmitters")
|
||||
suspend fun deleteTransmitters()
|
||||
@Query("DELETE FROM radios")
|
||||
suspend fun deleteRadios()
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Look4Sat. Amateur radio satellite tracker and pass predictor.
|
||||
* Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.rtbishop.look4sat.framework.local
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.rtbishop.look4sat.framework.model.SatEntry
|
||||
import com.rtbishop.look4sat.framework.model.Transmitter
|
||||
|
||||
@Database(entities = [SatEntry::class, Transmitter::class], version = 5, exportSchema = true)
|
||||
abstract class SatelliteDb : RoomDatabase() {
|
||||
|
||||
abstract fun entriesDao(): EntriesDao
|
||||
|
||||
abstract fun transmittersDao(): TransmittersDao
|
||||
}
|
||||
|
||||
val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE trans_backup (uuid TEXT NOT NULL, info TEXT NOT NULL, isAlive INTEGER NOT NULL, downlink INTEGER, uplink INTEGER, mode TEXT, isInverted INTEGER NOT NULL, catNum INTEGER, PRIMARY KEY(uuid))")
|
||||
database.execSQL("INSERT INTO trans_backup (uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum) SELECT uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum FROM transmitters")
|
||||
database.execSQL("DROP TABLE transmitters")
|
||||
database.execSQL("ALTER TABLE trans_backup RENAME TO transmitters")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_2_3 = object : Migration(2, 3) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE sources (sourceUrl TEXT NOT NULL, PRIMARY KEY(sourceUrl))")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_3_4 = object : Migration(3, 4) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE entries_backup (isSelected INTEGER NOT NULL, name TEXT NOT NULL, epoch REAL NOT NULL, meanmo REAL NOT NULL, eccn REAL NOT NULL, incl REAL NOT NULL, raan REAL NOT NULL, argper REAL NOT NULL, meanan REAL NOT NULL, catnum INTEGER NOT NULL, bstar REAL NOT NULL, xincl REAL NOT NULL, xnodeo REAL NOT NULL, omegao REAL NOT NULL, xmo REAL NOT NULL, xno REAL NOT NULL, isDeepspace INTEGER NOT NULL, PRIMARY KEY(catnum))")
|
||||
database.execSQL("INSERT INTO entries_backup (isSelected, name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar, xincl, xnodeo, omegao, xmo, xno, isDeepspace) SELECT isSelected, 'name', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, catNum, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0 FROM entries")
|
||||
database.execSQL("DROP TABLE entries")
|
||||
database.execSQL("ALTER TABLE entries_backup RENAME TO entries")
|
||||
database.execSQL("CREATE TABLE trans_backup (uuid TEXT NOT NULL, info TEXT NOT NULL, isAlive INTEGER NOT NULL, downlink INTEGER, uplink INTEGER, mode TEXT, isInverted INTEGER NOT NULL, catnum INTEGER, PRIMARY KEY(uuid))")
|
||||
database.execSQL("INSERT INTO trans_backup (uuid, info, isAlive, downlink, uplink, mode, isInverted, catnum) SELECT uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum FROM transmitters")
|
||||
database.execSQL("DROP TABLE transmitters")
|
||||
database.execSQL("ALTER TABLE trans_backup RENAME TO transmitters")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_4_5 = object : Migration(4, 5) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE sources")
|
||||
}
|
||||
}
|
|
@ -17,8 +17,4 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.framework.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "sources")
|
||||
data class Source(@PrimaryKey var sourceUrl: String = String())
|
||||
data class DataSource(var url: String = String())
|
|
@ -19,11 +19,7 @@ package com.rtbishop.look4sat.framework.model
|
|||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import com.rtbishop.look4sat.domain.predict.TLE
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
|
||||
@Entity(tableName = "entries", primaryKeys = ["catnum"])
|
||||
data class SatEntry(
|
||||
@Embedded
|
||||
val tle: TLE,
|
||||
var isSelected: Boolean = false
|
||||
)
|
||||
data class SatEntry(@Embedded val data: OrbitalData, var comment: String? = null)
|
||||
|
|
|
@ -22,10 +22,9 @@ import androidx.room.Relation
|
|||
data class SatItem(
|
||||
val catnum: Int,
|
||||
val name: String,
|
||||
var isSelected: Boolean = false,
|
||||
@Relation(
|
||||
parentColumn = "catnum",
|
||||
entity = Transmitter::class,
|
||||
entity = SatRadio::class,
|
||||
entityColumn = "catnum",
|
||||
projection = ["mode"]
|
||||
)
|
||||
|
|
|
@ -20,8 +20,8 @@ package com.rtbishop.look4sat.framework.model
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "transmitters")
|
||||
data class Transmitter(
|
||||
@Entity(tableName = "radios")
|
||||
data class SatRadio(
|
||||
@PrimaryKey val uuid: String,
|
||||
val info: String,
|
||||
val isAlive: Boolean,
|
||||
|
@ -29,5 +29,6 @@ data class Transmitter(
|
|||
var uplink: Long?,
|
||||
val mode: String?,
|
||||
val isInverted: Boolean,
|
||||
val catnum: Int?
|
||||
val catnum: Int?,
|
||||
var comment: String? = null
|
||||
)
|
|
@ -8,8 +8,7 @@ import java.net.URL
|
|||
|
||||
class RemoteSource(private val ioDispatcher: CoroutineDispatcher) : IRemoteSource {
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun fetchFileStream(url: String): InputStream? {
|
||||
override suspend fun getFileStream(url: String): InputStream? {
|
||||
return withContext(ioDispatcher) { URL(url).openStream() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ import com.rtbishop.look4sat.domain.ILocationHandler
|
|||
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||
import com.rtbishop.look4sat.framework.LocationHandler
|
||||
import com.rtbishop.look4sat.framework.SettingsHandler
|
||||
import com.rtbishop.look4sat.framework.local.*
|
||||
import com.rtbishop.look4sat.framework.local.LocalSource
|
||||
import com.rtbishop.look4sat.framework.local.Look4SatDb
|
||||
import com.rtbishop.look4sat.framework.remote.RemoteSource
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
@ -53,15 +54,14 @@ object CoreModule {
|
|||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
||||
): IDataRepository {
|
||||
val db = Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb")
|
||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
|
||||
val db = Room.databaseBuilder(context, Look4SatDb::class.java, "Look4SatDb")
|
||||
.fallbackToDestructiveMigration().build()
|
||||
val dataParser = DataParser(defaultDispatcher)
|
||||
val resolver = context.contentResolver
|
||||
val localSource = LocalSource(resolver, ioDispatcher, db.entriesDao(), db.transmittersDao())
|
||||
val remoteSource = RemoteSource(ioDispatcher)
|
||||
val local = LocalSource(db.entriesDao(), db.radiosDao(), settings, resolver, ioDispatcher)
|
||||
val remote = RemoteSource(ioDispatcher)
|
||||
val repositoryScope = CoroutineScope(SupervisorJob())
|
||||
return DataRepository(dataParser, settings, localSource, remoteSource, repositoryScope)
|
||||
return DataRepository(dataParser, local, remote, repositoryScope)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -21,4 +21,4 @@ import android.app.Application
|
|||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class Look4SatApplication : Application()
|
||||
class Look4SatApplication : Application()
|
||||
|
|
|
@ -22,7 +22,7 @@ class MainViewModel @Inject constructor(
|
|||
timeRef: Long = System.currentTimeMillis()
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val satellites = dataRepository.getSelectedSatellites()
|
||||
val satellites = dataRepository.getSelectedEntries()
|
||||
val stationPos = preferences.loadStationPosition()
|
||||
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class EntriesViewModel @Inject constructor(
|
|||
|
||||
private val transModes = MutableLiveData(preferences.loadModesSelection())
|
||||
private val currentQuery = MutableLiveData(String())
|
||||
private val itemsFromRepo = liveData { emit(repository.getAllSatellites()) } as MutableLiveData
|
||||
private val itemsFromRepo = liveData { emit(repository.getEntriesWithModes()) } as MutableLiveData
|
||||
private val itemsWithModes = transModes.switchMap { modes ->
|
||||
itemsFromRepo.map { items -> filterByModes(items, modes) }
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class EntriesViewModel @Inject constructor(
|
|||
fun saveSelection() {
|
||||
itemsFromRepo.value?.let { itemsAll ->
|
||||
val filteredItems = itemsAll.filter { item -> item.isSelected }
|
||||
repository.updatesSelection(filteredItems.map { item -> item.catnum })
|
||||
repository.setEntriesSelection(filteredItems.map { item -> item.catnum })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||
textLabelBackgroundColor = Color.TRANSPARENT
|
||||
textLabelForegroundColor =
|
||||
ContextCompat.getColor(requireContext(), R.color.themeAccent)
|
||||
setTextIcon(it.key.params.name)
|
||||
setTextIcon(it.key.data.name)
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
|
||||
try {
|
||||
position = GeoPoint(it.value.latitude, it.value.longitude)
|
||||
|
|
|
@ -76,13 +76,13 @@ class MapViewModel @Inject constructor(
|
|||
|
||||
fun selectDefaultSatellite(catnum: Int) {
|
||||
viewModelScope.launch {
|
||||
dataRepository.getSelectedSatellites().also { satellites ->
|
||||
dataRepository.getSelectedEntries().also { satellites ->
|
||||
if (satellites.isNotEmpty()) {
|
||||
allSatList = satellites
|
||||
if (catnum == -1) {
|
||||
selectSatellite(satellites.first())
|
||||
} else {
|
||||
satellites.find { it.params.catnum == catnum }?.let { selectSatellite(it) }
|
||||
satellites.find { it.data.catnum == catnum }?.let { selectSatellite(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ class MapViewModel @Inject constructor(
|
|||
val osmPos = GeoPos(osmLat, osmLon)
|
||||
val qthLoc = QthConverter.positionToQth(osmPos.latitude, osmPos.longitude) ?: "-- --"
|
||||
val satData = MapData(
|
||||
satellite, satellite.params.catnum, satellite.params.name, satPos.range,
|
||||
satellite, satellite.data.catnum, satellite.data.name, satPos.range,
|
||||
satPos.altitude, satPos.getOrbitalVelocity(), qthLoc, osmPos
|
||||
)
|
||||
_satData.postValue(satData)
|
||||
|
|
|
@ -65,7 +65,7 @@ class PassesViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
_passes.postValue(DataState.Loading)
|
||||
passesProcessing?.cancelAndJoin()
|
||||
val satellites = repository.getSelectedSatellites()
|
||||
val satellites = repository.getSelectedEntries()
|
||||
val stationPos = settings.loadStationPosition()
|
||||
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||
|
||||
private fun setupViews() {
|
||||
val context = requireContext()
|
||||
val adapter = TransmittersAdapter()
|
||||
val adapter = RadiosAdapter()
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
val itemDecoration = DividerItemDecoration(context, layoutManager.orientation)
|
||||
binding.run {
|
||||
|
@ -68,7 +68,7 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupObservers(transmittersAdapter: TransmittersAdapter) {
|
||||
private fun setupObservers(radiosAdapter: RadiosAdapter) {
|
||||
viewModel.getPass(args.catNum, args.aosTime).observe(viewLifecycleOwner) { pass ->
|
||||
radarView = RadarView(requireContext()).apply {
|
||||
setShowAim(preferences.getUseCompass())
|
||||
|
@ -82,7 +82,7 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||
}
|
||||
viewModel.transmitters.observe(viewLifecycleOwner) { list ->
|
||||
if (list.isNotEmpty()) {
|
||||
transmittersAdapter.submitList(list)
|
||||
radiosAdapter.submitList(list)
|
||||
binding.radarRecyclerMsg.text = getString(R.string.trans_data)
|
||||
} else {
|
||||
binding.radarRecyclerMsg.text = getString(R.string.trans_no_data)
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.lifecycle.*
|
|||
import com.rtbishop.look4sat.data.ISettingsHandler
|
||||
import com.rtbishop.look4sat.domain.DataReporter
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
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
|
||||
|
@ -47,10 +47,10 @@ class RadarViewModel @Inject constructor(
|
|||
|
||||
private val stationPos = preferences.loadStationPosition()
|
||||
private val _passData = MutableLiveData<RadarData>()
|
||||
private val _transmitters = MutableLiveData<List<Transmitter>>()
|
||||
private val _transmitters = MutableLiveData<List<SatRadio>>()
|
||||
private val _orientation = MutableLiveData<Triple<Float, Float, Float>>()
|
||||
val radarData: LiveData<RadarData> = _passData
|
||||
val transmitters: LiveData<List<Transmitter>> = _transmitters
|
||||
val transmitters: LiveData<List<SatRadio>> = _transmitters
|
||||
val orientation: LiveData<Triple<Float, Float, Float>> = _orientation
|
||||
|
||||
fun getPass(catNum: Int, aosTime: Long) = liveData {
|
||||
|
@ -107,7 +107,7 @@ class RadarViewModel @Inject constructor(
|
|||
|
||||
private fun processTransmitters(pass: SatPass) {
|
||||
viewModelScope.launch {
|
||||
val transmitters = dataRepository.getTransmitters(pass.catNum)
|
||||
val transmitters = dataRepository.getRadios(pass.catNum)
|
||||
while (isActive) {
|
||||
val time = System.currentTimeMillis()
|
||||
val list = predictor.processRadios(pass.satellite, stationPos, transmitters, time)
|
||||
|
|
|
@ -24,24 +24,24 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.ItemTransBinding
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import java.util.*
|
||||
|
||||
class TransmittersAdapter : RecyclerView.Adapter<TransmittersAdapter.TransHolder>() {
|
||||
class RadiosAdapter : RecyclerView.Adapter<RadiosAdapter.TransHolder>() {
|
||||
|
||||
private val diffCallback = object : DiffUtil.ItemCallback<Transmitter>() {
|
||||
override fun areItemsTheSame(oldItem: Transmitter, newItem: Transmitter): Boolean {
|
||||
private val diffCallback = object : DiffUtil.ItemCallback<SatRadio>() {
|
||||
override fun areItemsTheSame(oldItem: SatRadio, newItem: SatRadio): Boolean {
|
||||
return oldItem.uuid == newItem.uuid
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Transmitter, newItem: Transmitter): Boolean {
|
||||
override fun areContentsTheSame(oldItem: SatRadio, newItem: SatRadio): Boolean {
|
||||
return oldItem.downlink == newItem.downlink
|
||||
}
|
||||
}
|
||||
private val differ = AsyncListDiffer(this, diffCallback)
|
||||
|
||||
fun submitList(transmitters: List<Transmitter>) {
|
||||
differ.submitList(transmitters)
|
||||
fun submitList(radios: List<SatRadio>) {
|
||||
differ.submitList(radios)
|
||||
}
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
@ -65,10 +65,10 @@ class TransmittersAdapter : RecyclerView.Adapter<TransmittersAdapter.TransHolder
|
|||
private val formatLinkNull = itemView.context.getString(R.string.trans_no_link)
|
||||
private val isInverted = itemView.context.getString(R.string.trans_inverted)
|
||||
|
||||
fun bind(transmitter: Transmitter) {
|
||||
binding.transDesc.text = transmitter.info
|
||||
fun bind(radio: SatRadio) {
|
||||
binding.transDesc.text = radio.info
|
||||
|
||||
transmitter.downlink.let { downlink ->
|
||||
radio.downlink.let { downlink ->
|
||||
if (downlink != null) {
|
||||
val downlinkFreq = downlink / divider
|
||||
binding.transDownlink.text =
|
||||
|
@ -78,7 +78,7 @@ class TransmittersAdapter : RecyclerView.Adapter<TransmittersAdapter.TransHolder
|
|||
}
|
||||
}
|
||||
|
||||
transmitter.uplink.let { uplink ->
|
||||
radio.uplink.let { uplink ->
|
||||
if (uplink != null) {
|
||||
val uplinkFreq = uplink / divider
|
||||
binding.transUplink.text = String.format(Locale.ENGLISH, formatLink, uplinkFreq)
|
||||
|
@ -87,13 +87,13 @@ class TransmittersAdapter : RecyclerView.Adapter<TransmittersAdapter.TransHolder
|
|||
}
|
||||
}
|
||||
|
||||
if (transmitter.mode != null) {
|
||||
binding.transMode.text = String.format(mode, transmitter.mode)
|
||||
if (radio.mode != null) {
|
||||
binding.transMode.text = String.format(mode, radio.mode)
|
||||
} else {
|
||||
binding.transMode.text = String.format(mode, strNo)
|
||||
}
|
||||
|
||||
if (transmitter.isInverted) {
|
||||
if (radio.isInverted) {
|
||||
binding.transInverted.text = String.format(isInverted, strYes)
|
||||
} else {
|
||||
binding.transInverted.text = String.format(isInverted, strNo)
|
|
@ -15,29 +15,29 @@
|
|||
* 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.presentation.sourcesScreen
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.rtbishop.look4sat.databinding.ItemSourceBinding
|
||||
import com.rtbishop.look4sat.framework.model.Source
|
||||
import com.rtbishop.look4sat.framework.model.DataSource
|
||||
|
||||
class SourcesAdapter(private val sources: MutableList<Source> = mutableListOf()) :
|
||||
class SourcesAdapter(private val sources: MutableList<DataSource> = mutableListOf()) :
|
||||
RecyclerView.Adapter<SourcesAdapter.TleSourceHolder>() {
|
||||
|
||||
fun getSources(): List<Source> {
|
||||
return sources.filter { it.sourceUrl.contains("https://") }
|
||||
fun getSources(): List<DataSource> {
|
||||
return sources.filter { source -> source.url.contains("https://") }
|
||||
}
|
||||
|
||||
fun setSources(list: List<Source>) {
|
||||
fun setSources(list: List<DataSource>) {
|
||||
sources.clear()
|
||||
sources.addAll(list)
|
||||
}
|
||||
|
||||
fun addSource() {
|
||||
sources.add(Source())
|
||||
sources.add(DataSource())
|
||||
notifyItemInserted(itemCount - 1)
|
||||
}
|
||||
|
||||
|
@ -58,9 +58,9 @@ class SourcesAdapter(private val sources: MutableList<Source> = mutableListOf())
|
|||
inner class TleSourceHolder(private val binding: ItemSourceBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(source: Source) {
|
||||
binding.sourceUrl.setText(source.sourceUrl)
|
||||
binding.sourceUrl.doOnTextChanged { txt, _, _, _ -> source.sourceUrl = txt.toString() }
|
||||
fun bind(source: DataSource) {
|
||||
binding.sourceUrl.setText(source.url)
|
||||
binding.sourceUrl.doOnTextChanged { txt, _, _, _ -> source.url = txt.toString() }
|
||||
binding.sourceBtn.setOnClickListener {
|
||||
sources.remove(source)
|
||||
notifyItemRemoved(adapterPosition)
|
|
@ -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.presentation.sourcesScreen
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -23,11 +23,11 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
||||
import com.rtbishop.look4sat.databinding.DialogSourcesBinding
|
||||
import com.rtbishop.look4sat.framework.model.Source
|
||||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.framework.model.DataSource
|
||||
import com.rtbishop.look4sat.presentation.setNavResult
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
@ -36,35 +36,37 @@ import javax.inject.Inject
|
|||
class SourcesDialog : AppCompatDialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var settings: ISettingsHandler
|
||||
lateinit var repository: IDataRepository
|
||||
private lateinit var binding: DialogSourcesBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_sources, group, false)
|
||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View {
|
||||
binding = DialogSourcesBinding.inflate(inflater, group, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, state: Bundle?) {
|
||||
super.onViewCreated(view, state)
|
||||
val sources = settings.loadDataSources()
|
||||
val adapter = SourcesAdapter().apply { setSources(sources.map { Source(it) }) }
|
||||
val layoutManager = LinearLayoutManager(requireContext())
|
||||
DialogSourcesBinding.bind(view).apply {
|
||||
dialog?.window?.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
sourcesRecycler.apply {
|
||||
setHasFixedSize(true)
|
||||
this.adapter = adapter
|
||||
this.layoutManager = layoutManager
|
||||
dialog?.window?.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
lifecycleScope.launchWhenResumed {
|
||||
val adapter = SourcesAdapter()
|
||||
val layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter.setSources(repository.getDataSources().map { url -> DataSource(url) })
|
||||
binding.run {
|
||||
sourcesRecycler.apply {
|
||||
setHasFixedSize(true)
|
||||
this.adapter = adapter
|
||||
this.layoutManager = layoutManager
|
||||
}
|
||||
sourcesBtnAdd.setOnClickListener { adapter.addSource() }
|
||||
sourcesBtnNeg.setOnClickListener { dismiss() }
|
||||
sourcesBtnPos.setOnClickListener {
|
||||
setNavResult("sources", adapter.getSources().map { source -> source.url })
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
sourcesBtnAdd.setOnClickListener {
|
||||
adapter.addSource()
|
||||
}
|
||||
sourcesBtnPos.setOnClickListener {
|
||||
setNavResult("sources", adapter.getSources().map { it.sourceUrl })
|
||||
dismiss()
|
||||
}
|
||||
sourcesBtnNeg.setOnClickListener { dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@
|
|||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/nav_sources"
|
||||
android:name="com.rtbishop.look4sat.presentation.sourcesScreen.SourcesDialog"
|
||||
android:name="com.rtbishop.look4sat.presentation.settingsScreen.SourcesDialog"
|
||||
tools:layout="@layout/dialog_sources" />
|
||||
<action
|
||||
android:id="@+id/action_global_entriesFragment"
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
package com.rtbishop.bbctestapp
|
||||
package com.rtbishop.look4sat
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ plugins {
|
|||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,4 +18,4 @@ dependencies {
|
|||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "org.mockito:mockito-core:4.3.1"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,115 +21,99 @@ import com.rtbishop.look4sat.domain.DataParser
|
|||
import com.rtbishop.look4sat.domain.IDataRepository
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class DataRepository(
|
||||
private val dataParser: DataParser,
|
||||
private val settings: ISettingsHandler,
|
||||
private val localSource: ILocalSource,
|
||||
private val remoteSource: IRemoteSource,
|
||||
private val repositoryScope: CoroutineScope
|
||||
) : IDataRepository {
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||
println("DataRepository: handled $exception")
|
||||
_updateState.value = DataState.Error(exception.message)
|
||||
}
|
||||
private val updateStateDelay = 875L
|
||||
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
|
||||
override val updateState: StateFlow<DataState<Long>> = _updateState
|
||||
|
||||
override suspend fun getAllSatellites(): List<SatItem> {
|
||||
val selection = settings.loadSatelliteSelection()
|
||||
val satellites = localSource.getAllSatellites()
|
||||
satellites.forEach { satItem -> satItem.isSelected = satItem.catnum in selection }
|
||||
return satellites
|
||||
override fun setUpdateStateHandled() {
|
||||
_updateState.value = DataState.Handled
|
||||
}
|
||||
|
||||
override suspend fun getSelectedSatellites(): List<Satellite> {
|
||||
val selection = settings.loadSatelliteSelection()
|
||||
return localSource.getSelectedSatellites(selection)
|
||||
}
|
||||
override suspend fun getEntriesWithModes() = localSource.getEntriesWithModes()
|
||||
|
||||
override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum)
|
||||
override suspend fun getSelectedEntries() = localSource.getSelectedEntries()
|
||||
|
||||
override suspend fun getRadios(catnum: Int) = localSource.getRadios(catnum)
|
||||
|
||||
override fun updateFromFile(uri: String) {
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
_updateState.value = DataState.Loading
|
||||
val updateTimeMillis = measureTimeMillis {
|
||||
localSource.getFileStream(uri)?.let { fileStream ->
|
||||
localSource.updateEntries(importSatellites(fileStream))
|
||||
}
|
||||
localSource.getFileStream(uri)?.let { fileStream ->
|
||||
localSource.insertEntries(importSatellites(fileStream))
|
||||
delay(updateStateDelay)
|
||||
}
|
||||
_updateState.value = DataState.Success(updateTimeMillis)
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateFromWeb(sources: List<String>) {
|
||||
_updateState.value = DataState.Loading
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
settings.saveDataSources(sources)
|
||||
localSource.setDataSources(sources)
|
||||
}
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
val updateTimeMillis = measureTimeMillis {
|
||||
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
||||
val streamsMap = mutableMapOf<String, InputStream?>()
|
||||
val streams = mutableListOf<InputStream>()
|
||||
val entries = mutableListOf<SatEntry>()
|
||||
sources.forEach { jobsMap[it] = async { remoteSource.fetchFileStream(it) } }
|
||||
jobsMap.forEach { streamsMap[it.key] = it.value.await() }
|
||||
streamsMap.forEach { stream ->
|
||||
stream.value?.let { inputStream ->
|
||||
when {
|
||||
stream.key.contains("=csv", true) -> {
|
||||
val tles = dataParser.parseCSVStream(inputStream)
|
||||
entries.addAll(tles.map { tle -> SatEntry(tle) })
|
||||
}
|
||||
stream.key.contains(".zip", true) -> {
|
||||
streams.add(ZipInputStream(inputStream).apply { nextEntry })
|
||||
}
|
||||
else -> streams.add(inputStream)
|
||||
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
||||
val streamsMap = mutableMapOf<String, InputStream?>()
|
||||
val streams = mutableListOf<InputStream>()
|
||||
val entries = mutableListOf<SatEntry>()
|
||||
sources.forEach { jobsMap[it] = async { remoteSource.getFileStream(it) } }
|
||||
jobsMap.forEach { job -> streamsMap[job.key] = job.value.await() }
|
||||
streamsMap.forEach { stream ->
|
||||
stream.value?.let { inputStream ->
|
||||
when {
|
||||
stream.key.contains("=csv", true) -> {
|
||||
val tles = dataParser.parseCSVStream(inputStream)
|
||||
entries.addAll(tles.map { tle -> SatEntry(tle) })
|
||||
}
|
||||
stream.key.contains(".zip", true) -> {
|
||||
streams.add(ZipInputStream(inputStream).apply { nextEntry })
|
||||
}
|
||||
else -> streams.add(inputStream)
|
||||
}
|
||||
}
|
||||
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
||||
localSource.updateEntries(entries)
|
||||
}
|
||||
println("Update from web took $updateTimeMillis ms")
|
||||
_updateState.value = DataState.Success(updateTimeMillis)
|
||||
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
||||
localSource.insertEntries(entries)
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
remoteSource.fetchFileStream(settings.transmittersSource)?.let { stream ->
|
||||
val transmitters = dataParser.parseJSONStream(stream)
|
||||
localSource.updateTransmitters(transmitters)
|
||||
remoteSource.getFileStream(remoteSource.radioApi)?.let { stream ->
|
||||
localSource.insertRadios(dataParser.parseJSONStream(stream))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updatesSelection(catnums: List<Int>) {
|
||||
repositoryScope.launch {
|
||||
settings.saveSatelliteSelection(catnums)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpdateStateHandled() {
|
||||
_updateState.value = DataState.Handled
|
||||
}
|
||||
|
||||
override fun clearAllData() {
|
||||
repositoryScope.launch {
|
||||
_updateState.value = DataState.Loading
|
||||
localSource.clearAllData()
|
||||
delay(updateStateDelay)
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDataSources() = localSource.getDataSources()
|
||||
|
||||
override fun setEntriesSelection(catnums: List<Int>) {
|
||||
repositoryScope.launch { localSource.setEntriesSelection(catnums) }
|
||||
}
|
||||
|
||||
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
||||
return dataParser.parseTLEStream(stream).map { tle -> SatEntry(tle) }
|
||||
}
|
||||
|
|
|
@ -19,23 +19,31 @@ package com.rtbishop.look4sat.data
|
|||
|
||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import java.io.InputStream
|
||||
|
||||
interface ILocalSource {
|
||||
|
||||
suspend fun getAllSatellites(): List<SatItem>
|
||||
suspend fun getEntriesWithModes(): List<SatItem>
|
||||
|
||||
suspend fun getSelectedSatellites(catnums: List<Int>): List<Satellite>
|
||||
suspend fun getSelectedEntries(): List<Satellite>
|
||||
|
||||
suspend fun getTransmitters(catnum: Int): List<Transmitter>
|
||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
||||
|
||||
suspend fun getFileStream(uri: String): InputStream?
|
||||
|
||||
suspend fun updateEntries(entries: List<SatEntry>)
|
||||
suspend fun insertEntries(entries: List<SatEntry>)
|
||||
|
||||
suspend fun updateTransmitters(transmitters: List<Transmitter>)
|
||||
suspend fun insertRadios(radios: List<SatRadio>)
|
||||
|
||||
suspend fun clearAllData()
|
||||
|
||||
suspend fun getDataSources(): List<String>
|
||||
|
||||
suspend fun setDataSources(sources: List<String>)
|
||||
|
||||
suspend fun getEntriesSelection(): List<Int>
|
||||
|
||||
suspend fun setEntriesSelection(catnums: List<Int>)
|
||||
}
|
||||
|
|
|
@ -21,5 +21,7 @@ import java.io.InputStream
|
|||
|
||||
interface IRemoteSource {
|
||||
|
||||
suspend fun fetchFileStream(url: String): InputStream?
|
||||
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||
|
||||
suspend fun getFileStream(url: String): InputStream?
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ interface ISettingsHandler {
|
|||
"https://celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||
"https://amsat.org/tle/current/nasabare.txt"
|
||||
)
|
||||
val transmittersSource: String
|
||||
get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||
|
||||
fun loadStationPosition(): GeoPos
|
||||
|
||||
|
@ -59,9 +57,9 @@ interface ISettingsHandler {
|
|||
|
||||
fun loadModesSelection(): List<String>
|
||||
|
||||
fun saveSatelliteSelection(catnums: List<Int>)
|
||||
fun saveEntriesSelection(catnums: List<Int>)
|
||||
|
||||
fun loadSatelliteSelection(): List<Int>
|
||||
fun loadEntriesSelection(): List<Int>
|
||||
|
||||
fun getRotatorEnabled(): Boolean
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.predict.TLE
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
|
@ -28,8 +28,8 @@ import kotlin.math.pow
|
|||
|
||||
class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
||||
|
||||
suspend fun parseCSVStream(csvStream: InputStream): List<TLE> = withContext(parserDispatcher) {
|
||||
val parsedItems = mutableListOf<TLE>()
|
||||
suspend fun parseCSVStream(csvStream: InputStream): List<OrbitalData> = withContext(parserDispatcher) {
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
csvStream.bufferedReader().useLines { lines ->
|
||||
lines.forEachIndexed { index, line ->
|
||||
if (index != 0) {
|
||||
|
@ -41,9 +41,9 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
|||
return@withContext parsedItems
|
||||
}
|
||||
|
||||
suspend fun parseTLEStream(tleStream: InputStream): List<TLE> = withContext(parserDispatcher) {
|
||||
suspend fun parseTLEStream(tleStream: InputStream): List<OrbitalData> = withContext(parserDispatcher) {
|
||||
val tleStrings = mutableListOf(String(), String(), String())
|
||||
val parsedItems = mutableListOf<TLE>()
|
||||
val parsedItems = mutableListOf<OrbitalData>()
|
||||
var lineIndex = 0
|
||||
tleStream.bufferedReader().forEachLine { line ->
|
||||
tleStrings[lineIndex] = line
|
||||
|
@ -60,9 +60,9 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
|||
return@withContext parsedItems
|
||||
}
|
||||
|
||||
suspend fun parseJSONStream(jsonStream: InputStream): List<Transmitter> {
|
||||
suspend fun parseJSONStream(jsonStream: InputStream): List<SatRadio> {
|
||||
return withContext(parserDispatcher) {
|
||||
val parsedItems = mutableListOf<Transmitter>()
|
||||
val parsedItems = mutableListOf<SatRadio>()
|
||||
try {
|
||||
val jsonArray = JSONArray(jsonStream.bufferedReader().readText())
|
||||
for (index in 0 until jsonArray.length()) {
|
||||
|
@ -76,7 +76,7 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun parseCSV(values: List<String>): TLE? {
|
||||
private fun parseCSV(values: List<String>): OrbitalData? {
|
||||
try {
|
||||
val name = values[0]
|
||||
val year = values[2].substring(0, 4)
|
||||
|
@ -98,13 +98,13 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
|||
val meanan = values[8].toDouble()
|
||||
val catnum = values[11].toInt()
|
||||
val bstar = values[14].toDouble()
|
||||
return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseTLE(tle: List<String>): TLE? {
|
||||
private fun parseTLE(tle: List<String>): OrbitalData? {
|
||||
if (tle[1].substring(0, 1) != "1" && tle[2].substring(0, 1) != "2") {
|
||||
return null
|
||||
}
|
||||
|
@ -120,13 +120,13 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
|||
val catnum: Int = tle[1].substring(2, 7).trim().toInt()
|
||||
val bstar: Double = 1.0e-5 * tle[1].substring(53, 59).toDouble() /
|
||||
10.0.pow(tle[1].substring(60, 61).toDouble())
|
||||
return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
return OrbitalData(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseJSON(json: JSONObject): Transmitter? {
|
||||
private fun parseJSON(json: JSONObject): SatRadio? {
|
||||
try {
|
||||
val uuid = json.getString("uuid")
|
||||
val info = json.getString("description")
|
||||
|
@ -140,7 +140,7 @@ class DataParser(private val parserDispatcher: CoroutineDispatcher) {
|
|||
val isInverted = json.getBoolean("invert")
|
||||
val catnum = if (json.isNull("norad_cat_id")) null
|
||||
else json.getInt("norad_cat_id")
|
||||
return Transmitter(uuid, info, isAlive, downlink, uplink, mode, isInverted, catnum)
|
||||
return SatRadio(uuid, info, isAlive, downlink, uplink, mode, isInverted, catnum)
|
||||
} catch (exception: Exception) {
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain
|
|||
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
|
@ -27,19 +27,21 @@ interface IDataRepository {
|
|||
|
||||
val updateState: StateFlow<DataState<Long>>
|
||||
|
||||
suspend fun getAllSatellites(): List<SatItem>
|
||||
fun setUpdateStateHandled()
|
||||
|
||||
suspend fun getSelectedSatellites(): List<Satellite>
|
||||
suspend fun getEntriesWithModes(): List<SatItem>
|
||||
|
||||
suspend fun getTransmitters(catnum: Int): List<Transmitter>
|
||||
suspend fun getSelectedEntries(): List<Satellite>
|
||||
|
||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
||||
|
||||
fun updateFromFile(uri: String)
|
||||
|
||||
fun updateFromWeb(sources: List<String>)
|
||||
|
||||
fun updatesSelection(catnums: List<Int>)
|
||||
|
||||
fun setUpdateStateHandled()
|
||||
|
||||
fun clearAllData()
|
||||
|
||||
suspend fun getDataSources(): List<String>
|
||||
|
||||
fun setEntriesSelection(catnums: List<Int>)
|
||||
}
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.TLE
|
||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||
|
||||
data class SatEntry(
|
||||
val tle: TLE,
|
||||
var isSelected: Boolean = false
|
||||
)
|
||||
data class SatEntry(val data: OrbitalData, var comment: String? = null)
|
||||
|
|
|
@ -20,6 +20,6 @@ package com.rtbishop.look4sat.domain.model
|
|||
data class SatItem(
|
||||
val catnum: Int,
|
||||
val name: String,
|
||||
var isSelected: Boolean,
|
||||
val modes: List<String>
|
||||
val modes: List<String>,
|
||||
var isSelected: Boolean
|
||||
)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain.model
|
||||
|
||||
data class Transmitter(
|
||||
data class SatRadio(
|
||||
val uuid: String,
|
||||
val info: String,
|
||||
val isAlive: Boolean,
|
||||
|
@ -25,5 +25,6 @@ data class Transmitter(
|
|||
var uplink: Long?,
|
||||
val mode: String?,
|
||||
val isInverted: Boolean,
|
||||
val catnum: Int?
|
||||
val catnum: Int?,
|
||||
var comment: String? = null
|
||||
)
|
|
@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict
|
|||
|
||||
import kotlin.math.*
|
||||
|
||||
class DeepSpaceSat(params: TLE) : Satellite(params) {
|
||||
class DeepSpaceSat(data: OrbitalData) : Satellite(data) {
|
||||
|
||||
private val c1: Double
|
||||
private val c4: Double
|
||||
|
@ -35,41 +35,41 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
|||
|
||||
init {
|
||||
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
|
||||
val a1 = (xke / super.params.xno).pow(twoThirds)
|
||||
dsv.cosio = cos(super.params.xincl)
|
||||
val a1 = (xke / super.data.xno).pow(twoThirds)
|
||||
dsv.cosio = cos(super.data.xincl)
|
||||
dsv.theta2 = dsv.cosio * dsv.cosio
|
||||
x3thm1 = 3.0 * dsv.theta2 - 1
|
||||
dsv.eosq = super.params.eccn * super.params.eccn
|
||||
dsv.eosq = super.data.eccn * super.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.params.xno / (1.0 + delo)
|
||||
dsv.xnodp = super.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.params.eccn) - 1.0) * earthRadius)
|
||||
setPerigee((dsv.aodp * (1.0 - super.data.eccn) - 1.0) * earthRadius)
|
||||
val pinvsq = invert(dsv.aodp * dsv.aodp * dsv.betao2 * dsv.betao2)
|
||||
dsv.sing = sin(super.params.omegao)
|
||||
dsv.cosg = cos(super.params.omegao)
|
||||
dsv.sing = sin(super.data.omegao)
|
||||
dsv.cosg = cos(super.data.omegao)
|
||||
val tsi = invert(dsv.aodp - s4)
|
||||
val eta = dsv.aodp * super.params.eccn * tsi
|
||||
val eta = dsv.aodp * super.data.eccn * tsi
|
||||
val etasq = eta * eta
|
||||
val eeta = super.params.eccn * eta
|
||||
val eeta = super.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.params.bstar * c2
|
||||
dsv.sinio = sin(super.params.xincl)
|
||||
c1 = super.data.bstar * c2
|
||||
dsv.sinio = sin(super.data.xincl)
|
||||
val a3ovk2 = -j3Harmonic / ck2
|
||||
x1mth2 = 1.0 - dsv.theta2
|
||||
c4 =
|
||||
2 * dsv.xnodp * coef1 * dsv.aodp * dsv.betao2 * (eta * (2.0 + 0.5 * etasq) + super.params.eccn
|
||||
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)
|
||||
* (-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.params.omegao))))
|
||||
* (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * super.data.omegao))))
|
||||
val theta4 = dsv.theta2 * dsv.theta2
|
||||
val temp1 = 3.0 * ck2 * pinvsq * dsv.xnodp
|
||||
val temp2 = temp1 * ck2 * pinvsq
|
||||
|
@ -93,18 +93,18 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
|||
internal fun calculateSDP4(tSince: Double) {
|
||||
synchronized(this) {
|
||||
val temp = DoubleArray(12)
|
||||
val xmdf = params.xmo + dsv.xmdot * tSince
|
||||
val xmdf = data.xmo + dsv.xmdot * tSince
|
||||
val tsq = tSince * tSince
|
||||
val templ = t2cof * tsq
|
||||
dsv.xll = xmdf + dsv.xnodp * templ
|
||||
dsv.omgadf = params.omegao + dsv.omgdot * tSince
|
||||
val xnoddf = params.xnodeo + dsv.xnodot * tSince
|
||||
dsv.omgadf = data.omegao + dsv.omgdot * tSince
|
||||
val xnoddf = data.xnodeo + dsv.xnodot * tSince
|
||||
dsv.xnode = xnoddf + xnodcf * tsq
|
||||
val tempa = 1.0 - c1 * tSince
|
||||
val tempe = params.bstar * c4 * tSince
|
||||
val tempe = data.bstar * c4 * tSince
|
||||
dsv.xn = dsv.xnodp
|
||||
dsv.t = tSince
|
||||
deep.dpsec(params)
|
||||
deep.dpsec(data)
|
||||
val a = (xke / dsv.xn).pow(twoThirds) * tempa * tempa
|
||||
dsv.em = dsv.em - tempe
|
||||
deep.dpper()
|
||||
|
@ -425,16 +425,16 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
|||
private var epochRestart = false
|
||||
|
||||
init {
|
||||
thgr = thetaG(params.epoch)
|
||||
eq = params.eccn
|
||||
thgr = thetaG(data.epoch)
|
||||
eq = data.eccn
|
||||
xnq = dsv.xnodp
|
||||
aqnv = invert(dsv.aodp)
|
||||
xqncl = params.xincl
|
||||
xmao = params.xmo
|
||||
xqncl = data.xincl
|
||||
xmao = data.xmo
|
||||
xpidot = dsv.omgdot + dsv.xnodot
|
||||
sinq = sin(params.xnodeo)
|
||||
cosq = cos(params.xnodeo)
|
||||
omegaq = params.omegao
|
||||
sinq = sin(data.xnodeo)
|
||||
cosq = cos(data.xnodeo)
|
||||
omegaq = data.omegao
|
||||
// Initialize lunar solar terms, days since 1900 Jan 0.5
|
||||
day = dsv.ds50 + 18261.5
|
||||
if (abs(day - preep) > 1.0E-6) {
|
||||
|
@ -534,7 +534,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
|||
temp = 2.0 * temp1 * root54
|
||||
d5421 = temp * f542 * g521
|
||||
d5433 = temp * f543 * g533
|
||||
xlamo = xmao + params.xnodeo + params.xnodeo - thgr - thgr
|
||||
xlamo = xmao + data.xnodeo + data.xnodeo - thgr - thgr
|
||||
bfact = dsv.xmdot + dsv.xnodot + dsv.xnodot - tHdt - tHdt
|
||||
bfact += ssl + ssh + ssh
|
||||
} else {
|
||||
|
@ -556,7 +556,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
|||
fasx2 = 0.13130908
|
||||
fasx4 = 2.8843198
|
||||
fasx6 = 0.37448087
|
||||
xlamo = xmao + params.xnodeo + params.omegao - thgr
|
||||
xlamo = xmao + data.xnodeo + data.omegao - thgr
|
||||
bfact = dsv.xmdot + xpidot - tHdt
|
||||
bfact += ssl + ssg + ssh
|
||||
}
|
||||
|
@ -623,7 +623,7 @@ class DeepSpaceSat(params: TLE) : Satellite(params) {
|
|||
}
|
||||
|
||||
// Entrance for deep space secular effects
|
||||
fun dpsec(params: TLE) {
|
||||
fun dpsec(params: OrbitalData) {
|
||||
dsv.xll = dsv.xll + ssl * dsv.t
|
||||
dsv.omgadf = dsv.omgadf + ssg * dsv.t
|
||||
dsv.xnode = dsv.xnode + ssh * dsv.t
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict
|
|||
|
||||
import kotlin.math.*
|
||||
|
||||
class NearEarthSat(params: TLE) : Satellite(params) {
|
||||
class NearEarthSat(data: OrbitalData) : Satellite(data) {
|
||||
|
||||
private val aodp: Double
|
||||
private val aycof: Double
|
||||
|
@ -53,18 +53,18 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
|||
|
||||
init {
|
||||
// Recover original mean motion (xnodp) and semimajor axis (aodp) from input elements
|
||||
val a1 = (xke / super.params.xno).pow(twoThirds)
|
||||
cosio = cos(super.params.xincl)
|
||||
val a1 = (xke / super.data.xno).pow(twoThirds)
|
||||
cosio = cos(super.data.xincl)
|
||||
val theta2 = sqr(cosio)
|
||||
x3thm1 = 3.0 * theta2 - 1.0
|
||||
val eo = super.params.eccn
|
||||
val eo = super.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.params.xno / (1.0 + delo)
|
||||
xnodp = super.data.xno / (1.0 + delo)
|
||||
aodp = ao / (1.0 - delo)
|
||||
|
||||
// For perigee less than 220 kilometers, the "simple" flag is set
|
||||
|
@ -80,15 +80,15 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
|||
val psisq = abs(1.0 - etasq)
|
||||
val coef = qoms24 * tsi.pow(4.0)
|
||||
val coef1 = coef / psisq.pow(3.5)
|
||||
val bstar = super.params.bstar
|
||||
val bstar = super.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)))
|
||||
c1 = bstar * c2
|
||||
sinio = sin(super.params.xincl)
|
||||
sinio = sin(super.data.xincl)
|
||||
val a3ovk2 = -j3Harmonic / ck2
|
||||
val c3 = coef * tsi * a3ovk2 * xnodp * sinio / eo
|
||||
x1mth2 = 1.0 - theta2
|
||||
val omegao = super.params.omegao
|
||||
val omegao = super.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
|
||||
* (1.5 - 0.5 * eeta)) + 0.75 * x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq))
|
||||
|
@ -112,7 +112,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
|||
t2cof = 1.5 * c1
|
||||
xlcof = 0.125 * a3ovk2 * sinio * (3.0 + 5 * cosio) / (1.0 + cosio)
|
||||
aycof = 0.25 * a3ovk2 * sinio
|
||||
val xmo = super.params.xmo
|
||||
val xmo = super.data.xmo
|
||||
delmo = (1.0 + eta * cos(xmo)).pow(3.0)
|
||||
sinmo = sin(xmo)
|
||||
x7thm1 = 7.0 * theta2 - 1
|
||||
|
@ -138,14 +138,14 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
|||
internal fun calculateSGP4(tSince: Double) {
|
||||
synchronized(this) {
|
||||
val temp = DoubleArray(9)
|
||||
val xmdf = params.xmo + xmdot * tSince
|
||||
val omgadf = params.omegao + omgdot * tSince
|
||||
val xnoddf = params.xnodeo + xnodot * tSince
|
||||
val xmdf = data.xmo + xmdot * tSince
|
||||
val omgadf = data.omegao + omgdot * tSince
|
||||
val xnoddf = data.xnodeo + xnodot * tSince
|
||||
var omega = omgadf
|
||||
var xmp = xmdf
|
||||
val tsq = sqr(tSince)
|
||||
val xnode = xnoddf + xnodcf * tsq
|
||||
val bstar = params.bstar
|
||||
val bstar = data.bstar
|
||||
var tempa = 1.0 - c1 * tSince
|
||||
var tempe = bstar * c4 * tSince
|
||||
var templ = t2cof * tsq
|
||||
|
@ -162,7 +162,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
|||
templ += t3cof * tcube + tfour * (t4cof + tSince * t5cof)
|
||||
}
|
||||
val a = aodp * tempa.pow(2.0)
|
||||
val eo = params.eccn
|
||||
val eo = data.eccn
|
||||
val e = eo - tempe
|
||||
val xl = xmp + omega + xnode + xnodp * templ
|
||||
val beta = sqrt(1.0 - e * e)
|
||||
|
@ -213,7 +213,7 @@ class NearEarthSat(params: TLE) : Satellite(params) {
|
|||
val rk = r * (1.0 - 1.5 * temp[2] * betal * x3thm1) + 0.5 * temp[1] * x1mth2 * cos2u
|
||||
val uk = u - 0.25 * temp[2] * x7thm1 * sin2u
|
||||
val xnodek = xnode + 1.5 * temp[2] * cosio * sin2u
|
||||
val xinck = params.xincl + 1.5 * temp[2] * cosio * sinio * cos2u
|
||||
val xinck = data.xincl + 1.5 * temp[2] * cosio * sinio * cos2u
|
||||
val rdotk = rdot - xn * temp[1] * x1mth2 * sin2u
|
||||
val rfdotk = rfdot + xn * temp[1] * (x1mth2 * cos2u + 1.5 * x3thm1)
|
||||
super.calculatePosAndVel(rk, uk, xnodek, xinck, rdotk, rfdotk)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
data class TLE(
|
||||
data class OrbitalData(
|
||||
val name: String,
|
||||
val epoch: Double,
|
||||
val meanmo: Double,
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
package com.rtbishop.look4sat.domain.predict
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
@ -44,7 +44,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun processRadios(sat: Satellite, pos: GeoPos, radios: List<Transmitter>, time: Long): List<Transmitter> {
|
||||
suspend fun processRadios(sat: Satellite, pos: GeoPos, radios: List<SatRadio>, time: Long): List<SatRadio> {
|
||||
return withContext(predictorDispatcher) {
|
||||
val satPos = sat.getPosition(pos, time)
|
||||
val copiedList = radios.map { it.copy() }
|
||||
|
@ -106,7 +106,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
|
|||
var lastAosDate: Long
|
||||
var count = 0
|
||||
if (this.willBeSeen(pos)) {
|
||||
if (this.params.isDeepspace) {
|
||||
if (this.data.isDeepspace) {
|
||||
passes.add(getGeoPass(this, pos, time))
|
||||
} else {
|
||||
do {
|
||||
|
|
|
@ -29,7 +29,7 @@ data class SatPass(
|
|||
val satellite: Satellite,
|
||||
var progress: Int = 0
|
||||
) {
|
||||
val catNum: Int = satellite.params.catnum
|
||||
val name: String = satellite.params.name
|
||||
val isDeepSpace: Boolean = satellite.params.isDeepspace
|
||||
val catNum: Int = satellite.data.catnum
|
||||
val name: String = satellite.data.name
|
||||
val isDeepSpace: Boolean = satellite.data.isDeepspace
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.rtbishop.look4sat.domain.predict
|
|||
|
||||
import kotlin.math.*
|
||||
|
||||
abstract class Satellite(val params: TLE) {
|
||||
abstract class Satellite(val data: OrbitalData) {
|
||||
|
||||
private val flatFactor = 3.35281066474748E-3
|
||||
private val deg2Rad = 1.745329251994330E-2
|
||||
|
@ -30,7 +30,7 @@ abstract class Satellite(val params: TLE) {
|
|||
private val velocity = Vector4()
|
||||
private var gsPosTheta = 0.0
|
||||
private var perigee = 0.0
|
||||
val orbitalPeriod = 24 * 60 / params.meanmo
|
||||
val orbitalPeriod = 24 * 60 / data.meanmo
|
||||
val earthRadius = 6378.137
|
||||
val j3Harmonic = -2.53881E-6
|
||||
val twoPi = Math.PI * 2.0
|
||||
|
@ -42,11 +42,11 @@ abstract class Satellite(val params: TLE) {
|
|||
var s4 = 0.0
|
||||
|
||||
internal fun willBeSeen(pos: GeoPos): Boolean {
|
||||
return if (params.meanmo < 1e-8) false
|
||||
return if (data.meanmo < 1e-8) false
|
||||
else {
|
||||
val sma = 331.25 * exp(ln(1440.0 / params.meanmo) * (2.0 / 3.0))
|
||||
val apogee = sma * (1.0 + params.eccn) - earthRadius
|
||||
var lin = params.incl
|
||||
val sma = 331.25 * exp(ln(1440.0 / data.meanmo) * (2.0 / 3.0))
|
||||
val apogee = sma * (1.0 + data.eccn) - earthRadius
|
||||
var lin = data.incl
|
||||
if (lin >= 90.0) lin = 180.0 - lin
|
||||
acos(earthRadius / (apogee + earthRadius)) + lin * deg2Rad > abs(pos.latitude * deg2Rad)
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ abstract class Satellite(val params: TLE) {
|
|||
// Date/time at which the position and velocity were calculated
|
||||
val julUTC = calcCurrentDaynum(time) + 2444238.5
|
||||
// Convert satellite's epoch time to Julian and calculate time since epoch in minutes
|
||||
val julEpoch = juliandDateOfEpoch(params.epoch)
|
||||
val julEpoch = juliandDateOfEpoch(data.epoch)
|
||||
val tsince = (julUTC - julEpoch) * minPerDay
|
||||
calculateSDP4orSGP4(tsince)
|
||||
// Scale position and velocity vectors to km and km/sec
|
||||
|
@ -97,7 +97,7 @@ abstract class Satellite(val params: TLE) {
|
|||
}
|
||||
|
||||
private fun calculateSDP4orSGP4(tsince: Double) {
|
||||
if (params.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
||||
if (data.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
||||
else (this as NearEarthSat).calculateSGP4(tsince)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,4 +21,4 @@ kotlin.code.style=official
|
|||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
kapt.use.worker.api=true
|
||||
kapt.use.worker.api=true
|
||||
|
|
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -14,4 +14,4 @@ dependencyResolutionManagement {
|
|||
}
|
||||
rootProject.name = "Look4Sat"
|
||||
include ":app"
|
||||
include ":core"
|
||||
include ":core"
|
||||
|
|
Ładowanie…
Reference in New Issue