Completed
Push — master ( b8f070...484cc4 )
by Jeffrey
03:38
created

decode_frame()   B

Complexity

Conditions 6

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
dl 0
loc 28
rs 7.5384
c 0
b 0
f 0
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""Utilities for the APRS Python Module."""
5
6
# These imports are for python3 compatability inside python2
7
from __future__ import absolute_import
8
from __future__ import division
9
from __future__ import print_function
10
11
import logging
12
13
import apex.aprs.constants
14
import apex.aprs.decimal_degrees
15
import apex.kiss.constants
16
17
__author__ = 'Jeffrey Phillips Freeman (WI2ARD)'
18
__maintainer__ = 'Jeffrey Phillips Freeman (WI2ARD)'
19
__email__ = '[email protected]'
20
__license__ = 'Apache License, Version 2.0'
21
__copyright__ = 'Copyright 2016, Syncleus, Inc. and contributors'
22
__credits__ = []
23
24
25
def dec2dm_lat(dec):
26
    """Converts DecDeg to APRS Coord format.
27
    See: http://ember2ash.com/lat.htm
28
29
    Source: http://stackoverflow.com/questions/2056750
30
31
    Example:
32
        >>> test_lat = 37.7418096
33
        >>> aprs_lat = dec2dm_lat(test_lat)
34
        >>> aprs_lat
35
        '3744.51N'
36
    """
37
    dec_min = apex.aprs.decimal_degrees.decimal2dm(dec)
38
39
    deg = dec_min[0]
40
    abs_deg = abs(deg)
41
42
    if not deg == abs_deg:
43
        suffix = 'S'
44
    else:
45
        suffix = 'N'
46
47
    return''.join([str(abs_deg), '%.2f' % dec_min[1], suffix])
48
49
50
def dec2dm_lng(dec):
51
    """Converts DecDeg to APRS Coord format.
52
    See: http://ember2ash.com/lat.htm
53
54
    Example:
55
        >>> test_lng = -122.38833
56
        >>> aprs_lng = dec2dm_lng(test_lng)
57
        >>> aprs_lng
58
        '12223.30W'
59
    """
60
    dec_min = apex.aprs.decimal_degrees.decimal2dm(dec)
61
62
    deg = dec_min[0]
63
    abs_deg = abs(deg)
64
65
    if not deg == abs_deg:
66
        suffix = 'W'
67
    else:
68
        suffix = 'E'
69
70
    return ''.join([str(abs_deg), '%.2f' % dec_min[1], suffix])
71
72
73
def decode_frame(frame):
74
    """
75
    Breaks an ASCII APRS Frame down to it's constituent parts.
76
77
    :param frame: ASCII APRS Frame.
78
    :type frame: str
79
80
    :returns: Dictionary of APRS Frame parts: source, destination, path, text.
81
    :rtype: dict
82
    """
83
    logging.debug('frame=%s', frame)
84
    decoded_frame = {}
85
    frame_so_far = ''
86
87
    for char in frame:
88
        if '>' in char and 'source' not in decoded_frame:
89
            decoded_frame['source'] = frame_so_far
90
            frame_so_far = ''
91
        elif ':' in char and 'path' not in decoded_frame:
92
            decoded_frame['path'] = frame_so_far
93
            frame_so_far = ''
94
        else:
95
            frame_so_far = ''.join([frame_so_far, char])
96
97
    decoded_frame['text'] = frame_so_far
98
    decoded_frame['destination'] = decoded_frame['path'].split(',')[0]
99
100
    return decoded_frame
101
102
103
def encode_frame(frame):
104
    """
105
    Formats APRS frame-as-dict into APRS frame-as-string.
106
107
    :param frame: APRS frame-as-dict
108
    :type frame: dict
109
110
    :return: APRS frame-as-string.
111
    :rtype: str
112
    """
113
    formatted_frame = '>'.join([frame['source'], frame['destination']])
114
    if frame['path']:
115
        formatted_frame = ','.join([formatted_frame, format_path(frame['path'])])
116
    formatted_frame += ':'
117
    formatted_frame += frame['text']
118
    return formatted_frame
119
120
121
def format_path(path_list):
122
    """
123
    Formats path from raw APRS KISS frame.
124
125
    :param path_list: List of path elements.
126
    :type path_list: list
127
128
    :return: Formatted APRS path.
129
    :rtype: str
130
    """
131
    return ','.join(path_list)
132
133
134
def valid_callsign(callsign):
135
    """
136
    Validates callsign.
137
138
    :param callsign: Callsign to validate.
139
    :type callsign: str
140
141
    :returns: True if valid, False otherwise.
142
    :rtype: bool
143
    """
144
    logging.debug('callsign=%s', callsign)
145
146
    if '-' in callsign:
147
        if not callsign.count('-') == 1:
148
            return False
149
        else:
150
            callsign, ssid = callsign.split('-')
151
    else:
152
        ssid = 0
153
154
    logging.debug('callsign=%s ssid=%s', callsign, ssid)
155
156
    if (len(callsign) < 2 or len(callsign) > 6 or len(str(ssid)) < 1 or
157
            len(str(ssid)) > 2):
158
        return False
159
160
    for char in callsign:
161
        if not (char.isalpha() or char.isdigit()):
162
            return False
163
164
    if not str(ssid).isdigit():
165
        return False
166
167
    if int(ssid) < 0 or int(ssid) > 15:
168
        return False
169
170
    return True
171
172
173
def run_doctest():  # pragma: no cover
174
    """Runs doctests for this module."""
175
    import doctest
176
    import apex.aprs.util  # pylint: disable=W0406,W0621
177
    return doctest.testmod(apex.aprs.util)
178
179
180
def hash_frame(frame):
181
    """
182
    Produces an integer value that acts as a hash for the frame
183
    :param frame: A frame packet
184
    :type frame: dict
185
    :return: an integer representing the hash
186
    """
187
    hashing = 0
188
    index = 0
189
    frame_string_prefix = frame['source'] + '>' + frame['destination'] + ':'
190
    for frame_chr in frame_string_prefix:
191
        hashing = ord(frame_chr) << (8*(index % 4)) ^ hashing
192
        index += 1
193
    for char in frame['text']:
194
        hashing = ord(char) << (8*(index % 4)) ^ hashing
195
        index += 1
196
    return hashing
197
198
199
if __name__ == '__main__':
200
    run_doctest()  # pragma: no cover
201