dronecot 2.2.1__tar.gz → 2.2.3__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.
Files changed (34) hide show
  1. {dronecot-2.2.1 → dronecot-2.2.3}/CHANGELOG.md +11 -0
  2. {dronecot-2.2.1/dronecot.egg-info → dronecot-2.2.3}/PKG-INFO +1 -1
  3. {dronecot-2.2.1 → dronecot-2.2.3/dronecot.egg-info}/PKG-INFO +1 -1
  4. {dronecot-2.2.1 → dronecot-2.2.3}/dronecot.egg-info/SOURCES.txt +1 -0
  5. dronecot-2.2.3/src/dronecot/VERSION +1 -0
  6. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/classes.py +33 -0
  7. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/open_drone_id.py +2 -2
  8. dronecot-2.2.3/tests/test_adsb_vehicle.py +43 -0
  9. dronecot-2.2.1/src/dronecot/VERSION +0 -1
  10. {dronecot-2.2.1 → dronecot-2.2.3}/LICENSE +0 -0
  11. {dronecot-2.2.1 → dronecot-2.2.3}/MANIFEST.in +0 -0
  12. {dronecot-2.2.1 → dronecot-2.2.3}/README.md +0 -0
  13. {dronecot-2.2.1 → dronecot-2.2.3}/dronecot.egg-info/dependency_links.txt +0 -0
  14. {dronecot-2.2.1 → dronecot-2.2.3}/dronecot.egg-info/entry_points.txt +0 -0
  15. {dronecot-2.2.1 → dronecot-2.2.3}/dronecot.egg-info/requires.txt +0 -0
  16. {dronecot-2.2.1 → dronecot-2.2.3}/dronecot.egg-info/top_level.txt +0 -0
  17. {dronecot-2.2.1 → dronecot-2.2.3}/setup.cfg +0 -0
  18. {dronecot-2.2.1 → dronecot-2.2.3}/setup.py +0 -0
  19. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/__init__.py +0 -0
  20. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/ble_capture.py +0 -0
  21. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/ble_parse.py +0 -0
  22. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/commands.py +0 -0
  23. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/constants.py +0 -0
  24. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/dji_exceptions.py +0 -0
  25. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/dji_functions.py +0 -0
  26. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/dji_text_parser.py +0 -0
  27. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/functions.py +0 -0
  28. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/odid.py +0 -0
  29. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/rid_normalize.py +0 -0
  30. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/udp_rid.py +0 -0
  31. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/wifi_capture.py +0 -0
  32. {dronecot-2.2.1 → dronecot-2.2.3}/src/dronecot/wifi_parse.py +0 -0
  33. {dronecot-2.2.1 → dronecot-2.2.3}/tests/test_functions.py +0 -0
  34. {dronecot-2.2.1 → dronecot-2.2.3}/tests/test_rid_normalize.py +0 -0
@@ -1,3 +1,14 @@
1
+ ## DroneCOT 2.2.3
2
+
3
+ - SerialWorker: handle MAVLink **ADSB_VEHICLE** messages (in addition to
4
+ OPEN_DRONE_ID_MESSAGE_PACK), so Remote ID receivers that emit ADS-B-style
5
+ frames — e.g. the **BlueMark DroneScout Bridge (DS101)** in ADS-B output mode
6
+ — are decoded to CoT. Adds a test for the ADSB_VEHICLE -> RID conversion.
7
+
8
+ ## DroneCOT 2.2.2
9
+
10
+ - Fix UTC timestamp handling on Python 3.9 and 3.10.
11
+
1
12
  ## DroneCOT 2.2.1
2
13
 
3
14
  - Use PyTAK shared CoT event, point, detail, remarks, and serialization helpers.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.2.1
3
+ Version: 2.2.3
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>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dronecot
3
- Version: 2.2.1
3
+ Version: 2.2.3
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>
@@ -27,5 +27,6 @@ src/dronecot/rid_normalize.py
27
27
  src/dronecot/udp_rid.py
28
28
  src/dronecot/wifi_capture.py
29
29
  src/dronecot/wifi_parse.py
30
+ tests/test_adsb_vehicle.py
30
31
  tests/test_functions.py
31
32
  tests/test_rid_normalize.py
@@ -0,0 +1 @@
1
+ 2.2.3
@@ -87,6 +87,35 @@ class SerialWorker(pytak.QueueWorker):
87
87
  parsed = dronecot.odid.message_pack_to_dict(messages, pack_size)
88
88
  return dronecot.rid_normalize.odid_parsed_to_rid_dict(parsed)
89
89
 
90
+ @staticmethod
91
+ def _adsb_vehicle_to_rid_dict(msg) -> dict:
92
+ """Convert a MAVLink ADSB_VEHICLE to a RIDWorker dict.
93
+
94
+ The DroneScout Bridge (and similar Remote ID receivers) can emit detected
95
+ Remote ID as MAVLink ADSB_VEHICLE instead of OPEN_DRONE_ID_MESSAGE_PACK.
96
+ Units: lat/lon 1e-7 deg, altitude mm, hor/ver velocity cm/s, heading cdeg.
97
+ Returns {} when there is no position.
98
+ """
99
+ lat = getattr(msg, "lat", 0) / 1e7
100
+ lon = getattr(msg, "lon", 0) / 1e7
101
+ if not lat and not lon:
102
+ return {}
103
+ callsign = getattr(msg, "callsign", "") or ""
104
+ if isinstance(callsign, bytes):
105
+ callsign = callsign.decode("ascii", "ignore")
106
+ callsign = callsign.replace("\x00", "").strip()
107
+ icao = int(getattr(msg, "ICAO_address", 0) or 0)
108
+ return {
109
+ "BasicID": callsign or f"ICAO-{icao:06X}",
110
+ "Latitude": lat,
111
+ "Longitude": lon,
112
+ "AltitudeGeo": getattr(msg, "altitude", 0) / 1000.0, # mm -> m
113
+ "SpeedHorizontal": getattr(msg, "hor_velocity", 0) / 100.0, # cm/s -> m/s
114
+ "SpeedVertical": getattr(msg, "ver_velocity", 0) / 100.0,
115
+ "Direction": getattr(msg, "heading", 0) / 100.0, # cdeg -> deg
116
+ "data": {"type": "MAVLink ADSB_VEHICLE"},
117
+ }
118
+
90
119
  async def run(self, _=-1) -> None:
91
120
  """Read MAVLink messages from serial and enqueue decoded ODID payloads."""
92
121
  self._logger.info("Running SerialWorker")
@@ -154,6 +183,10 @@ class SerialWorker(pytak.QueueWorker):
154
183
  msg.messages, msg.msg_pack_size
155
184
  )
156
185
  await self.put_queue(parsed_payload)
186
+ elif msg_type == "ADSB_VEHICLE":
187
+ rid = self._adsb_vehicle_to_rid_dict(msg)
188
+ if rid:
189
+ await self.put_queue(rid)
157
190
  elif msg_type == "HEARTBEAT":
158
191
  self._logger.debug(
159
192
  "MAVLink heartbeat received at %s",
@@ -354,7 +354,7 @@ def parse_System(payload):
354
354
  if Timestamp != float("NaN") and Timestamp != 0:
355
355
  pl["Timestamp"] = (
356
356
  datetime.datetime.fromtimestamp(
357
- (int(Timestamp) + 1546300800), datetime.UTC
357
+ (int(Timestamp) + 1546300800), datetime.timezone.utc
358
358
  ).strftime("%Y-%m-%d %H:%M %Z"),
359
359
  )
360
360
  pl["TimestampRaw"] = Timestamp
@@ -409,7 +409,7 @@ def parse_AuthPage(payload, page):
409
409
 
410
410
  if Timestamp != float("NaN") and Timestamp != 0:
411
411
  pl["Timestamp"] = datetime.datetime.fromtimestamp(
412
- (int(Timestamp) + 1546300800), datetime.UTC
412
+ (int(Timestamp) + 1546300800), datetime.timezone.utc
413
413
  ).strftime("%Y-%m-%d %H:%M %Z")
414
414
 
415
415
  AuthData = payload[AuthPage_start_byte + 16 : AuthPage_start_byte + 16 + 17]
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright Sensors & Signals LLC https://www.snstac.com/
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ """Tests for SerialWorker MAVLink ADSB_VEHICLE -> RID dict (DroneScout Bridge)."""
5
+
6
+ import types
7
+ import unittest
8
+
9
+ from dronecot.classes import SerialWorker
10
+
11
+
12
+ def _adsb(**kw):
13
+ return types.SimpleNamespace(**kw)
14
+
15
+
16
+ class AdsbVehicleTestCase(unittest.TestCase):
17
+ def test_full_conversion(self):
18
+ rid = SerialWorker._adsb_vehicle_to_rid_dict(
19
+ _adsb(
20
+ lat=407128000, lon=-740060000, altitude=120500,
21
+ hor_velocity=825, ver_velocity=-50, heading=27000,
22
+ callsign="DRONE1\x00\x00", ICAO_address=0x123456,
23
+ )
24
+ )
25
+ self.assertAlmostEqual(rid["Latitude"], 40.7128, places=6)
26
+ self.assertAlmostEqual(rid["Longitude"], -74.0060, places=6)
27
+ self.assertEqual(rid["AltitudeGeo"], 120.5) # mm -> m
28
+ self.assertEqual(rid["SpeedHorizontal"], 8.25) # cm/s -> m/s
29
+ self.assertEqual(rid["Direction"], 270.0) # cdeg -> deg
30
+ self.assertEqual(rid["BasicID"], "DRONE1")
31
+
32
+ def test_no_callsign_falls_back_to_icao(self):
33
+ rid = SerialWorker._adsb_vehicle_to_rid_dict(
34
+ _adsb(lat=407128000, lon=-740060000, callsign="", ICAO_address=0x123456)
35
+ )
36
+ self.assertEqual(rid["BasicID"], "ICAO-123456")
37
+
38
+ def test_no_position_dropped(self):
39
+ self.assertEqual(SerialWorker._adsb_vehicle_to_rid_dict(_adsb(lat=0, lon=0)), {})
40
+
41
+
42
+ if __name__ == "__main__":
43
+ unittest.main()
@@ -1 +0,0 @@
1
- 2.2.1
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes