dronecot 2.1.4__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dronecot-2.1.4 → dronecot-2.2.0}/CHANGELOG.md +12 -0
- {dronecot-2.1.4/dronecot.egg-info → dronecot-2.2.0}/PKG-INFO +2 -2
- {dronecot-2.1.4 → dronecot-2.2.0/dronecot.egg-info}/PKG-INFO +2 -2
- {dronecot-2.1.4 → dronecot-2.2.0}/dronecot.egg-info/requires.txt +1 -1
- {dronecot-2.1.4 → dronecot-2.2.0}/setup.cfg +1 -1
- dronecot-2.2.0/src/dronecot/VERSION +1 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/__init__.py +7 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/classes.py +54 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/constants.py +6 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/functions.py +54 -0
- dronecot-2.1.4/src/dronecot/VERSION +0 -1
- {dronecot-2.1.4 → dronecot-2.2.0}/LICENSE +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/MANIFEST.in +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/README.md +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/dronecot.egg-info/SOURCES.txt +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/dronecot.egg-info/dependency_links.txt +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/dronecot.egg-info/entry_points.txt +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/dronecot.egg-info/top_level.txt +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/setup.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/ble_capture.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/ble_parse.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/commands.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/dji_exceptions.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/dji_functions.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/dji_text_parser.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/odid.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/open_drone_id.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/rid_normalize.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/udp_rid.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/wifi_capture.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/src/dronecot/wifi_parse.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/tests/test_functions.py +0 -0
- {dronecot-2.1.4 → dronecot-2.2.0}/tests/test_rid_normalize.py +0 -0
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## DroneCOT 2.2.0
|
|
2
|
+
|
|
3
|
+
- Add `SensorWorker`: periodic `a-f-G-E-S-E` sensor CoT heartbeat, emitted every `SENSOR_KEEPALIVE_PERIOD` seconds (default 30).
|
|
4
|
+
- Position sourced from system gpsd if present (mode 2/3 fix); falls back to static `SENSOR_LAT`/`SENSOR_LON`/`SENSOR_HAE` config; falls back to null island (0, 0, 0).
|
|
5
|
+
- Add `gen_sensor_cot()`: reusable CoT generator for sensor beacon events, used by `SensorWorker`.
|
|
6
|
+
- New constants: `DEFAULT_SENSOR_KEEPALIVE_PERIOD = 30`, `DEFAULT_SENSOR_LAT/LON/HAE = 0.0`.
|
|
7
|
+
- Add `gpsd-py3` as optional runtime dependency (soft import, gracefully absent).
|
|
8
|
+
|
|
9
|
+
## DroneCOT 2.1.5
|
|
10
|
+
|
|
11
|
+
- Add `takproto` to postinst pip install; enables TAK Protocol v1 protobuf encoding over WebSocket when connected to TAK Server via `wss://`.
|
|
12
|
+
|
|
1
13
|
## DroneCOT 2.1.4
|
|
2
14
|
|
|
3
15
|
- Fix packaging: `postinst` now installs `aiomqtt` and `pymavlink` via pip for Debian systems where these packages are not yet available in the distro repos.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dronecot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Display Drones in Team Awareness Kit (TAK).
|
|
5
5
|
Home-page: https://github.com/snstac/dronecot
|
|
6
6
|
Maintainer: Greg Albrecht <oss@undef.net>
|
|
@@ -22,7 +22,7 @@ Classifier: Operating System :: OS Independent
|
|
|
22
22
|
Requires-Python: <4,>=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: pytak>=
|
|
25
|
+
Requires-Dist: pytak>=7.3.0
|
|
26
26
|
Requires-Dist: paho-mqtt<2.0.0
|
|
27
27
|
Requires-Dist: bitstruct
|
|
28
28
|
Requires-Dist: pymavlink
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dronecot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Display Drones in Team Awareness Kit (TAK).
|
|
5
5
|
Home-page: https://github.com/snstac/dronecot
|
|
6
6
|
Maintainer: Greg Albrecht <oss@undef.net>
|
|
@@ -22,7 +22,7 @@ Classifier: Operating System :: OS Independent
|
|
|
22
22
|
Requires-Python: <4,>=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: pytak>=
|
|
25
|
+
Requires-Dist: pytak>=7.3.0
|
|
26
26
|
Requires-Dist: paho-mqtt<2.0.0
|
|
27
27
|
Requires-Dist: bitstruct
|
|
28
28
|
Requires-Dist: pymavlink
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.2.0
|
|
@@ -54,6 +54,11 @@ from .constants import ( # NOQA
|
|
|
54
54
|
DEFAULT_DJI_SENSOR_NAME,
|
|
55
55
|
DEFAULT_DJI_SENSOR_TYPE,
|
|
56
56
|
DEFAULT_DJI_SENSOR_COT_TYPE,
|
|
57
|
+
# Sensor keep-alive constants
|
|
58
|
+
DEFAULT_SENSOR_KEEPALIVE_PERIOD,
|
|
59
|
+
DEFAULT_SENSOR_LAT,
|
|
60
|
+
DEFAULT_SENSOR_LON,
|
|
61
|
+
DEFAULT_SENSOR_HAE,
|
|
57
62
|
)
|
|
58
63
|
|
|
59
64
|
from .functions import ( # NOQA
|
|
@@ -69,6 +74,7 @@ from .functions import ( # NOQA
|
|
|
69
74
|
dji_handle_frame,
|
|
70
75
|
dji_handle_text_line,
|
|
71
76
|
dji_handle_parsed_data,
|
|
77
|
+
gen_sensor_cot,
|
|
72
78
|
)
|
|
73
79
|
|
|
74
80
|
from .udp_rid import ( # NOQA
|
|
@@ -97,6 +103,7 @@ from .classes import ( # NOQA
|
|
|
97
103
|
TextNetWorker,
|
|
98
104
|
FileReplayWorker,
|
|
99
105
|
TCPListenerWorker,
|
|
106
|
+
SensorWorker,
|
|
100
107
|
)
|
|
101
108
|
|
|
102
109
|
from .dji_exceptions import ( # NOQA
|
|
@@ -31,6 +31,7 @@ from typing import Optional, Union
|
|
|
31
31
|
from urllib.parse import urlparse
|
|
32
32
|
|
|
33
33
|
import lzma
|
|
34
|
+
import xml.etree.ElementTree as ET
|
|
34
35
|
|
|
35
36
|
try:
|
|
36
37
|
import aiomqtt
|
|
@@ -804,3 +805,56 @@ class UDPRIDWorker(pytak.QueueWorker):
|
|
|
804
805
|
raise
|
|
805
806
|
finally:
|
|
806
807
|
transport.close()
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
try:
|
|
811
|
+
import gpsd as _gpsd
|
|
812
|
+
except ImportError:
|
|
813
|
+
_gpsd = None # type: ignore[assignment]
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
class SensorWorker(pytak.QueueWorker):
|
|
817
|
+
"""Periodic sensor CoT heartbeat. Sources position from gpsd, config, or null island."""
|
|
818
|
+
|
|
819
|
+
async def run(self, _=-1) -> None:
|
|
820
|
+
period = int(self.config.get(
|
|
821
|
+
"SENSOR_KEEPALIVE_PERIOD", dronecot.DEFAULT_SENSOR_KEEPALIVE_PERIOD))
|
|
822
|
+
self._logger.info(
|
|
823
|
+
"Running SensorWorker (period=%ds, gpsd=%s)", period, _gpsd is not None)
|
|
824
|
+
while True:
|
|
825
|
+
lat, lon, hae, ce, le = await self._get_position()
|
|
826
|
+
cot = dronecot.gen_sensor_cot(self.config, lat, lon, hae, ce, le)
|
|
827
|
+
if cot is not None:
|
|
828
|
+
await self.put_queue(ET.tostring(cot))
|
|
829
|
+
await asyncio.sleep(period)
|
|
830
|
+
|
|
831
|
+
async def _get_position(self):
|
|
832
|
+
if _gpsd is not None:
|
|
833
|
+
try:
|
|
834
|
+
result = await asyncio.to_thread(self._poll_gpsd)
|
|
835
|
+
if result is not None:
|
|
836
|
+
return result
|
|
837
|
+
except Exception as exc:
|
|
838
|
+
self._logger.debug("gpsd unavailable: %s", exc)
|
|
839
|
+
lat = float(self.config.get("SENSOR_LAT") or dronecot.DEFAULT_SENSOR_LAT)
|
|
840
|
+
lon = float(self.config.get("SENSOR_LON") or dronecot.DEFAULT_SENSOR_LON)
|
|
841
|
+
hae = float(self.config.get("SENSOR_HAE") or dronecot.DEFAULT_SENSOR_HAE)
|
|
842
|
+
return lat, lon, hae, "9999999.0", "9999999.0"
|
|
843
|
+
|
|
844
|
+
@staticmethod
|
|
845
|
+
def _poll_gpsd():
|
|
846
|
+
_gpsd.connect()
|
|
847
|
+
packet = _gpsd.get_current()
|
|
848
|
+
if packet.mode < 2:
|
|
849
|
+
return None
|
|
850
|
+
try:
|
|
851
|
+
lat, lon = packet.position()
|
|
852
|
+
except Exception:
|
|
853
|
+
return None
|
|
854
|
+
try:
|
|
855
|
+
hae = packet.altitude()
|
|
856
|
+
except Exception:
|
|
857
|
+
hae = 0.0
|
|
858
|
+
ce = str(getattr(packet, "error", {}).get("x", "9999999.0") or "9999999.0")
|
|
859
|
+
le = str(getattr(packet, "error", {}).get("v", "9999999.0") or "9999999.0")
|
|
860
|
+
return lat, lon, hae, ce, le
|
|
@@ -46,6 +46,12 @@ DEFAULT_OP_COT_TYPE: str = "a-n-G"
|
|
|
46
46
|
DEFAULT_UAS_COT_TYPE: str = "a-n-A-M-H-Q"
|
|
47
47
|
DEFAULT_HOME_COT_TYPE: str = "a-n-G"
|
|
48
48
|
|
|
49
|
+
# Sensor keep-alive / heartbeat
|
|
50
|
+
DEFAULT_SENSOR_KEEPALIVE_PERIOD: int = 30 # seconds between a-f-G-E-S-E CoT beacons
|
|
51
|
+
DEFAULT_SENSOR_LAT: float = 0.0
|
|
52
|
+
DEFAULT_SENSOR_LON: float = 0.0
|
|
53
|
+
DEFAULT_SENSOR_HAE: float = 0.0
|
|
54
|
+
|
|
49
55
|
# Pre-decoded Remote ID JSON over UDP constants
|
|
50
56
|
DEFAULT_UDP_RID_PORT: int = 9999
|
|
51
57
|
DEFAULT_UDP_RID_HOST: str = "0.0.0.0"
|
|
@@ -133,6 +133,8 @@ def create_tasks(config: SectionProxy, clitool: pytak.CLITool) -> Set[pytak.Work
|
|
|
133
133
|
if config.get("ENABLE_RX_MOCK", "0") == "1":
|
|
134
134
|
tasks.add(dronecot.RXMockWorker(clitool.rx_queue, config))
|
|
135
135
|
|
|
136
|
+
tasks.add(dronecot.SensorWorker(clitool.tx_queue, config))
|
|
137
|
+
|
|
136
138
|
return tasks
|
|
137
139
|
|
|
138
140
|
|
|
@@ -843,3 +845,55 @@ def dji_handle_frame(
|
|
|
843
845
|
_DJI_Logger.warning("Error parsing DJI data: %s", exc)
|
|
844
846
|
return []
|
|
845
847
|
return dji_handle_parsed_data(parsed_data, config)
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
def gen_sensor_cot(
|
|
851
|
+
config: Union[SectionProxy, dict, None] = None,
|
|
852
|
+
lat: float = 0.0,
|
|
853
|
+
lon: float = 0.0,
|
|
854
|
+
hae: float = 0.0,
|
|
855
|
+
ce: str = "9999999.0",
|
|
856
|
+
le: str = "9999999.0",
|
|
857
|
+
) -> Optional[ET.Element]:
|
|
858
|
+
"""Generate a periodic sensor beacon CoT event (a-f-G-E-S-E).
|
|
859
|
+
|
|
860
|
+
Position sourced from caller: gpsd, static config, or null island.
|
|
861
|
+
ce/le are '9999999.0' when accuracy is unknown.
|
|
862
|
+
"""
|
|
863
|
+
config = config or {}
|
|
864
|
+
sensor_id = config.get("SENSOR_ID", dronecot.DEFAULT_SENSOR_ID)
|
|
865
|
+
cot_type = config.get("SENSOR_COT_TYPE", dronecot.DEFAULT_SENSOR_COT_TYPE)
|
|
866
|
+
cot_stale = int(config.get("COT_STALE", pytak.DEFAULT_COT_STALE))
|
|
867
|
+
callsign = config.get("SENSOR_CALLSIGN", sensor_id)
|
|
868
|
+
payload_type = config.get("SENSOR_PAYLOAD_TYPE", dronecot.DEFAULT_SENSOR_PAYLOAD_TYPE)
|
|
869
|
+
|
|
870
|
+
contact = ET.Element("contact")
|
|
871
|
+
contact.set("callsign", callsign)
|
|
872
|
+
|
|
873
|
+
cuas = ET.Element("__cuas")
|
|
874
|
+
cuas.set("sensor_id", sensor_id)
|
|
875
|
+
cuas.set("type", payload_type)
|
|
876
|
+
|
|
877
|
+
detail = ET.Element("detail")
|
|
878
|
+
detail.append(contact)
|
|
879
|
+
detail.append(cuas)
|
|
880
|
+
|
|
881
|
+
cot = pytak.gen_cot_xml(
|
|
882
|
+
lat=str(lat),
|
|
883
|
+
lon=str(lon),
|
|
884
|
+
hae=str(hae),
|
|
885
|
+
ce=ce,
|
|
886
|
+
le=le,
|
|
887
|
+
uid=f"SENSOR.{sensor_id}",
|
|
888
|
+
cot_type=cot_type,
|
|
889
|
+
stale=cot_stale,
|
|
890
|
+
)
|
|
891
|
+
cot.set("how", "m-g")
|
|
892
|
+
cot.set("access", config.get("COT_ACCESS", pytak.DEFAULT_COT_ACCESS))
|
|
893
|
+
|
|
894
|
+
_detail = cot.find("detail")
|
|
895
|
+
if _detail is not None:
|
|
896
|
+
cot.remove(_detail)
|
|
897
|
+
cot.append(detail)
|
|
898
|
+
|
|
899
|
+
return cot
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.1.4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|