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