mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Fix APRS object and item position decoding
Objects (;) and items ()) were identified but position data was never extracted, causing them to appear without location on the map. Added parse_object() and parse_item() functions to properly extract name, status, and coordinates. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
120
routes/aprs.py
120
routes/aprs.py
@@ -154,12 +154,20 @@ def parse_aprs_packet(raw_packet: str) -> Optional[dict]:
|
||||
packet['weather'] = parse_weather(data)
|
||||
|
||||
elif data.startswith(';'):
|
||||
# Object
|
||||
# Object format: ;OBJECTNAME*DDHHMMzPOSITION or ;OBJECTNAME_DDHHMMzPOSITION
|
||||
# Name is exactly 9 chars, then * (live) or _ (killed), then 7-char timestamp, then position
|
||||
packet['packet_type'] = 'object'
|
||||
obj_data = parse_object(data)
|
||||
if obj_data:
|
||||
packet.update(obj_data)
|
||||
|
||||
elif data.startswith(')'):
|
||||
# Item
|
||||
# Item format: )ITEMNAME!POSITION or )ITEMNAME_POSITION
|
||||
# Name is 3-9 chars, terminated by ! (live) or _ (killed), then position
|
||||
packet['packet_type'] = 'item'
|
||||
item_data = parse_item(data)
|
||||
if item_data:
|
||||
packet.update(item_data)
|
||||
|
||||
elif data.startswith('T'):
|
||||
# Telemetry
|
||||
@@ -233,6 +241,114 @@ def parse_position(data: str) -> Optional[dict]:
|
||||
return None
|
||||
|
||||
|
||||
def parse_object(data: str) -> Optional[dict]:
|
||||
"""Parse APRS object data.
|
||||
|
||||
Object format: ;OBJECTNAME*DDHHMMzPOSITION or ;OBJECTNAME_DDHHMMzPOSITION
|
||||
- ; is the object marker
|
||||
- OBJECTNAME is exactly 9 characters (padded with spaces if needed)
|
||||
- * means object is live, _ means object is killed/deleted
|
||||
- DDHHMMz is the timestamp (day/hour/minute zulu) - 7 chars
|
||||
- Position follows in standard APRS format
|
||||
|
||||
Some implementations have whitespace variations, so we search for the status
|
||||
character rather than assuming exact position.
|
||||
"""
|
||||
try:
|
||||
if not data.startswith(';') or len(data) < 18:
|
||||
return None
|
||||
|
||||
# Find the status character (* or _) which marks end of object name
|
||||
# It should be around position 10, but allow some flexibility
|
||||
status_pos = -1
|
||||
for i in range(10, min(13, len(data))):
|
||||
if data[i] in '*_':
|
||||
status_pos = i
|
||||
break
|
||||
|
||||
if status_pos == -1:
|
||||
# Fallback: assume standard position
|
||||
status_pos = 10
|
||||
|
||||
# Extract object name (chars between ; and status)
|
||||
obj_name = data[1:status_pos].strip()
|
||||
|
||||
# Get status character
|
||||
status_char = data[status_pos] if status_pos < len(data) else '*'
|
||||
is_live = status_char == '*'
|
||||
|
||||
# Timestamp is 7 chars after status, position follows
|
||||
pos_start = status_pos + 8 # status + 7 char timestamp
|
||||
if len(data) > pos_start:
|
||||
pos = parse_position(data[pos_start:])
|
||||
else:
|
||||
pos = None
|
||||
|
||||
result = {
|
||||
'object_name': obj_name,
|
||||
'object_live': is_live,
|
||||
}
|
||||
|
||||
if pos:
|
||||
result.update(pos)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to parse object: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def parse_item(data: str) -> Optional[dict]:
|
||||
"""Parse APRS item data.
|
||||
|
||||
Item format: )ITEMNAME!POSITION or )ITEMNAME_POSITION
|
||||
- ) is the item marker
|
||||
- ITEMNAME is 3-9 characters
|
||||
- ! means item is live, _ means item is killed/deleted
|
||||
- Position follows immediately in standard APRS format
|
||||
"""
|
||||
try:
|
||||
if not data.startswith(')') or len(data) < 5:
|
||||
return None
|
||||
|
||||
# Find the status delimiter (! or _) which terminates the name
|
||||
# Item name is 3-9 chars, so check positions 4-10 (1-based: chars 4-10 after ')')
|
||||
status_pos = -1
|
||||
for i in range(4, min(11, len(data))):
|
||||
if data[i] in '!_':
|
||||
status_pos = i
|
||||
break
|
||||
|
||||
if status_pos == -1:
|
||||
return None
|
||||
|
||||
# Extract item name and status
|
||||
item_name = data[1:status_pos].strip()
|
||||
status_char = data[status_pos]
|
||||
is_live = status_char == '!'
|
||||
|
||||
# Parse position after status character
|
||||
if len(data) > status_pos + 1:
|
||||
pos = parse_position(data[status_pos + 1:])
|
||||
else:
|
||||
pos = None
|
||||
|
||||
result = {
|
||||
'item_name': item_name,
|
||||
'item_live': is_live,
|
||||
}
|
||||
|
||||
if pos:
|
||||
result.update(pos)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to parse item: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def parse_weather(data: str) -> dict:
|
||||
"""Parse APRS weather data."""
|
||||
weather = {}
|
||||
|
||||
Reference in New Issue
Block a user