Skip to content

Commit a45b8fd

Browse files
committed
implement telemetry report parsing
1 parent ef88e34 commit a45b8fd

File tree

5 files changed

+108
-32
lines changed

5 files changed

+108
-32
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
*.pyc
44
dist
55
*.egg-info
6+
.gitignore
7+
.idea

aprslib/parsing/__init__.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,25 @@ def detect(x):
4545
from aprslib.parsing.weather import *
4646

4747
unsupported_formats = {
48-
'#':'raw weather report',
49-
'$':'raw gps',
50-
'%':'agrelo',
51-
'&':'reserved',
52-
'(':'unused',
53-
')':'item report',
54-
'*':'complete weather report',
55-
'+':'reserved',
56-
'-':'unused',
57-
'.':'reserved',
58-
'<':'station capabilities',
59-
'?':'general query format',
60-
'T':'telemetry report',
61-
'[':'maidenhead locator beacon',
62-
'\\':'unused',
63-
']':'unused',
64-
'^':'unused',
48+
'#': 'raw weather report',
49+
'$': 'raw gps',
50+
'%': 'agrelo',
51+
'&': 'reserved',
52+
'(': 'unused',
53+
')': 'item report',
54+
'*': 'complete weather report',
55+
'+': 'reserved',
56+
'-': 'unused',
57+
'.': 'reserved',
58+
'<': 'station capabilities',
59+
'?': 'general query format',
60+
'[': 'maidenhead locator beacon',
61+
'\\': 'unused',
62+
']': 'unused',
63+
'^': 'unused',
6564
}
6665

66+
6767
def _unicode_packet(packet):
6868
# attempt utf-8
6969
try:
@@ -114,7 +114,7 @@ def parse(packet):
114114

115115
parsed = {
116116
'raw': packet,
117-
}
117+
}
118118

119119
# parse head
120120
try:
@@ -149,7 +149,7 @@ def parse(packet):
149149
parsed.update({
150150
'format': 'beacon',
151151
'text': packet_type + body,
152-
})
152+
})
153153

154154
logger.debug("Parsed ok.")
155155
return parsed
@@ -208,6 +208,10 @@ def _try_toparse_body(packet_type, body, parsed):
208208

209209
body, result = parse_position(packet_type, body)
210210

211+
elif packet_type == "T":
212+
logger.debug("Attempting to parse as telemetry report")
213+
214+
body, result = parse_telemetry_report(body)
215+
211216
# we are done
212217
parsed.update(result)
213-

aprslib/parsing/telemetry.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from aprslib.parsing import logger
55

66
__all__ = [
7-
'parse_comment_telemetry',
8-
'parse_telemetry_config',
9-
]
7+
'parse_comment_telemetry',
8+
'parse_telemetry_config',
9+
'parse_telemetry_report'
10+
]
1011

1112

1213
def parse_comment_telemetry(text):
@@ -23,19 +24,19 @@ def parse_comment_telemetry(text):
2324

2425
temp = [0] * 7
2526
for i in range(7):
26-
temp[i] = base91.to_decimal(telemetry[i*2:i*2+2])
27+
temp[i] = base91.to_decimal(telemetry[i * 2:i * 2 + 2])
2728

2829
parsed.update({
2930
'telemetry': {
3031
'seq': temp[0],
3132
'vals': temp[1:6]
32-
}
33-
})
33+
}
34+
})
3435

3536
if temp[6] != '':
3637
parsed['telemetry'].update({
3738
'bits': "{0:08b}".format(temp[6] & 0xFF)[::-1]
38-
})
39+
})
3940

4041
return (text, parsed)
4142

@@ -62,14 +63,14 @@ def parse_telemetry_config(body):
6263

6364
parsed.update({
6465
't%s' % form: defvals
65-
})
66+
})
6667
elif form == "EQNS":
6768
eqns = body.rstrip().split(',')[:15]
6869
teqns = [0, 1, 0] * 5
6970

7071
for idx, val in enumerate(eqns):
7172
if not re.match(r"^([-]?\d*\.?\d+|)$", val):
72-
raise ParseError("value at %d is not a number in %s" % (idx+1, form))
73+
raise ParseError("value at %d is not a number in %s" % (idx + 1, form))
7374
else:
7475
try:
7576
val = int(val)
@@ -79,11 +80,11 @@ def parse_telemetry_config(body):
7980
teqns[idx] = val
8081

8182
# group values in 5 list of 3
82-
teqns = [teqns[i*3:(i+1)*3] for i in range(5)]
83+
teqns = [teqns[i * 3:(i + 1) * 3] for i in range(5)]
8384

8485
parsed.update({
8586
't%s' % form: teqns
86-
})
87+
})
8788
elif form == "BITS":
8889
match = re.findall(r"^([01]{8}),(.{0,23})$", body.rstrip())
8990
if not match:
@@ -94,7 +95,26 @@ def parse_telemetry_config(body):
9495
parsed.update({
9596
't%s' % form: bits,
9697
'title': title.strip(' ')
97-
})
98+
})
9899

99100
return (body, parsed)
100101

102+
103+
def parse_telemetry_report(text):
104+
temp = text.split(",")
105+
parsed = {}
106+
107+
if len(temp) == 7:
108+
109+
seq = int(temp[0].replace('#', ''))
110+
values = list(map(float, temp[1:6]))
111+
112+
parsed.update({
113+
'telemetry': {
114+
'seq': seq,
115+
'vals': values,
116+
'bits': temp[6]
117+
}
118+
})
119+
120+
return '', parsed

docs/parse_formats.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,32 @@ Regular
257257
'to': u'TOCALL',
258258
'via': ''}
259259
260+
261+
Telemetry report
262+
-----------------------
263+
264+
265+
.. code:: python
266+
267+
>>> aprslib.parse("FROMCALL>APDW16,WIDE1-1,qAR,TOCALL:T#165,13.21,0.39,5.10,14.94,36.12,11111100")
268+
269+
{
270+
'raw': 'FROMCALL>APDW16,WIDE1-1,qAR,TOCALL:T#165,13.21,0.39,5.10,14.94,36.12,11111100',
271+
'from': 'FROMCALL',
272+
'to': 'APDW16',
273+
'path': ['WIDE1-1', 'qAR', 'TOCALL'],
274+
'via': 'TOCALL',
275+
'telemetry':
276+
{
277+
'seq': 165,
278+
'vals': [13.21, 0.39, 5.1, 14.94, 36.12],
279+
'bits': '11111100'
280+
},
281+
'format': 'beacon',
282+
'text': 'T#165,13.21,0.39,5.10,14.94,36.12,11111100'
283+
}
284+
285+
260286
Telemetry configuration
261287
-----------------------
262288

@@ -296,3 +322,5 @@ Telemetry configuration
296322
'to': 'TOCALL',
297323
'via': ''}
298324
325+
326+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import unittest
2+
3+
from aprslib.parsing import parse_telemetry_report
4+
5+
6+
class ParseTelemetryReport(unittest.TestCase):
7+
def setUp(self):
8+
self.maxDiff = None
9+
10+
def test_valid_telemetry_report(self):
11+
packet = "#111,13.64,0.37,5.10,16.96,33.38,11110000"
12+
expected = {'telemetry':
13+
{'bits': '11110000',
14+
'seq': 111,
15+
'vals': [13.64, 0.37, 5.1, 16.96, 33.38]}}
16+
17+
_, result = parse_telemetry_report(packet)
18+
self.assertEqual(expected, result)
19+
20+
21+
if __name__ == '__main__':
22+
unittest.main()

0 commit comments

Comments
 (0)