-
-
Notifications
You must be signed in to change notification settings - Fork 122
/
sonde_specific.py
223 lines (196 loc) · 6.91 KB
/
sonde_specific.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/usr/bin/env python
#
# radiosonde_auto_rx - Radiosonde-Type Specific Functions
#
# Copyright (C) 2019 Mark Jessop <vk5qi@rfhead.net>
# Released under GNU GPL v3 or later
#
import datetime
import hashlib
from dateutil.parser import parse
def fix_datetime(datetime_str, local_dt_str=None):
"""
Given a HH:MM:SS string from a telemetry sentence, produce a complete timestamp, using the current system time as a guide for the date.
"""
if local_dt_str is None:
_now = datetime.datetime.utcnow()
else:
_now = parse(local_dt_str)
# Are we in the rollover window?
if _now.hour == 23 or _now.hour == 0:
_outside_window = False
else:
_outside_window = True
# Parsing just a HH:MM:SS will return a datetime object with the year, month and day replaced by values in the 'default'
# argument.
_imet_dt = parse(datetime_str, default=_now)
if _outside_window:
# We are outside the day-rollover window, and can safely use the current zulu date.
return _imet_dt
else:
# We are within the window, and need to adjust the day backwards or forwards based on the sonde time.
if _imet_dt.hour == 23 and _now.hour == 0:
# Assume system clock running slightly fast, and subtract a day from the telemetry date.
_imet_dt = _imet_dt - datetime.timedelta(days=1)
elif _imet_dt.hour == 00 and _now.hour == 23:
# System clock running slow. Add a day.
_imet_dt = _imet_dt + datetime.timedelta(days=1)
return _imet_dt
#
# iMet Radiosonde Functions
#
def imet_unique_id(telemetry, custom="SONDE", imet1=False):
"""
Generate a 'unique' imet radiosonde ID based on the power-on time, frequency, and an optional location code.
This requires the following fields be present in the telemetry dict:
datetime_dt (datetime) (will need to be generated above)
frame (int) - Frame number
freq_float (float) - Frequency in MHz, as a floating point number.
"""
_imet_dt = telemetry["datetime_dt"]
if imet1:
# iMet-1 sondes increment their frame counter TWICE every second, so we need to
# compensate for this to be able to determine a power-on time.
_frame = telemetry["frame"]//2
else:
# iMet-4 sondes increment the frame counter once per second.
_frame = telemetry["frame"]
# Determine power on time: Current time - number of frames (one frame per second)
_power_on_time = _imet_dt - datetime.timedelta(seconds=_frame)
# Round frequency to the nearest 100 kHz (iMet sondes only have 100 kHz frequency steps)
_freq = round(telemetry["freq_float"] * 10.0) / 10.0
_freq = "%.3f MHz" % _freq
# Now we generate a string to hash based on the power-on time, the rounded frequency, and the custom field.
_temp_str = _power_on_time.strftime("%Y-%m-%dT%H:%M:%SZ") + _freq + custom
# Calculate a SHA256 hash of the
_hash = hashlib.sha256(_temp_str.encode("ascii")).hexdigest().upper()
return "IMET-" + _hash[-8:]
#
# DFM Sonde Subtypes
#
def decode_dfm_subtype(subtype):
"""
Decode a DFM subtype (0xB through 0xD) into a possible model number.
NOTE: These are best guesses as to the relationship between subtype ID nibble
and actual model number. Graw have said that sonde decoders should not rely on
this nibble for identification.
"""
if "0x" not in subtype:
return "DFM-Unknown"
if subtype == "0x6":
return "DFM06"
elif subtype == "0x7":
return "PS-15"
elif subtype == "0xA":
return "DFM09"
elif subtype == "0xB":
return "DFM17"
elif subtype == "0xC":
return "DFM09P"
elif subtype == "0xD":
return "DFM17"
else:
# Unknown subtype
return "DFMx" + subtype[-1]
if __name__ == "__main__":
# Testing scripts for the above.
test_data = [
{
"datetime": "23:59:58",
"frame": 50,
"freq": "402.001 MHz",
"local_dt": "2019-03-01T23:59:58Z",
},
{
"datetime": "23:59:58",
"frame": 50,
"freq": "401.999 MHz",
"local_dt": "2019-03-01T23:59:57Z",
},
{
"datetime": "23:59:58",
"frame": 50,
"freq": "402.000 MHz",
"local_dt": "2019-03-02T00:00:03Z",
},
{
"datetime": "00:00:00",
"frame": 52,
"freq": "402.000 MHz",
"local_dt": "2019-03-01T23:59:57Z",
},
{
"datetime": "00:00:00",
"frame": 52,
"freq": "402.000 MHz",
"local_dt": "2019-03-02T00:00:03Z",
},
{
"datetime": "00:00:01",
"frame": 53,
"freq": "402.000 MHz",
"local_dt": "2019-03-01T23:59:57Z",
},
{
"datetime": "00:00:01",
"frame": 53,
"freq": "402.000 MHz",
"local_dt": "2019-03-02T00:00:03Z",
},
{
"datetime": "11:59:58",
"frame": 42,
"freq": "402.000 MHz",
"local_dt": "2019-03-01T12:00:03Z",
},
{
"datetime": "12:00:02",
"frame": 46,
"freq": "402.000 MHz",
"local_dt": "2019-03-01T12:00:03Z",
},
# 20:12:05Z,IMET-E2596815,3935,33.29227,35.68153,9253.0,-42.8,62.7,iMet,401.998,SATS 10,BATT 5.0
{
"datetime": "20:12:05",
"frame": 3935,
"freq": "401.998 MHz",
"local_dt": "2019-03-01T12:00:03Z",
},
# 20:12:06Z,IMET-E2596815,3937,33.29233,35.68149,9259.0,-42.9,62.1,iMet,401.998,SATS 11,BATT 5.0
{
"datetime": "20:12:06",
"frame": 3937,
"freq": "401.998 MHz",
"local_dt": "2019-03-01T12:00:03Z",
},
# 20:13:03Z,IMET-F46CA05C,4049,33.29655,35.67995,9640.0,-45.9,51.2,iMet,401.999,SATS 10,BATT 5.0
{
"datetime": "20:13:03",
"frame": 4049,
"freq": "401.999 MHz",
"local_dt": "2019-03-01T12:00:03Z",
},
# 20:13:04Z,IMET-F46CA05C,4051,33.29666,35.67995,9646.0,-46.0,51.1,iMet,401.999,SATS 10,BATT 5.0
{
"datetime": "20:13:04",
"frame": 4051,
"freq": "401.999 MHz",
"local_dt": "2019-03-01T12:00:03Z",
},
]
for _test in test_data:
_test["freq_float"] = float(_test["freq"].split(" ")[0])
_test["datetime_dt"] = fix_datetime(
_test["datetime"], local_dt_str=_test["local_dt"]
)
print(
"Input Time: %s, Local Time: %s, Output Time: %s"
% (
_test["datetime"],
_test["local_dt"],
_test["datetime_dt"].strftime("%Y-%m-%dT%H:%M:%SZ"),
)
)
_test["id"] = imet_unique_id(_test)
print("Generated ID: %s" % _test["id"])
print(" ")