source: ammosreader/src/ammosreader/PDW.py@ 4180d6a

AmmosSource guix
Last change on this file since 4180d6a was 4180d6a, checked in by Enrico Schwass <ennoausberlin@…>, 3 years ago

Add pymongo import script

  • Property mode set to 100644
File size: 10.9 KB
Line 
1"""I store the information of a single PDW block."""
2
3import struct
4import math
5import numpy as np
6
7
8class PDW():
9 """
10 I store information from a single ppdw data block.
11
12 .. automethod:: __init__
13 """
14
15 @classmethod
16 def from_bytes(cls, byte_string):
17 """
18 I create an instance of class PDW from data body (8 * 32 bits).
19
20 :param byte_string: a byte string containing a single data body read from a ppdw file
21 :type byte_string: byte string
22
23 :return: an instance of class PDW with attributes set according to the data of a data body
24 :rtype: PDW
25 """
26 assert(len(byte_string) == 32)
27
28 parts = struct.unpack('Q4s4s4s4s4s4s', byte_string)
29 nanoseconds = (parts[0])
30 time_of_arrival = np.datetime64(nanoseconds, 'ns')
31
32 third_entry = bin(int.from_bytes(parts[1], byteorder='little'))
33 padding = 32-len(str(third_entry)[2:])
34 third_entry_bit_string = "0" * padding + str(third_entry)[2:]
35 pdw_format_identifier = int(third_entry_bit_string[0:6], 2)
36 center_frequency = int(third_entry_bit_string[5:32], 2)
37
38 fourth_entry = bin(int.from_bytes(parts[2], byteorder='little'))
39 padding = 32-len(str(fourth_entry)[2:])
40 fourth_entry_bit_string = "0" * padding + str(fourth_entry)[2:]
41 is_valid = bool(int(fourth_entry_bit_string[0]))
42 is_pulse = bool(int(fourth_entry_bit_string[1]))
43 level_unit = int(fourth_entry_bit_string[2])
44 signal_start_missing = bool(int(fourth_entry_bit_string[3]))
45 signal_end_missing = bool(int(fourth_entry_bit_string[4]))
46 pulse_width = int(fourth_entry_bit_string[7:33], 2)
47
48 fifth_entry = bin(int.from_bytes(parts[3], byteorder='little'))
49 padding = 32-len(str(fifth_entry)[2:])
50 fifth_entry_bit_string = "0" * padding + str(fifth_entry)[2:]
51 frequency_shift_or_bandwidth = int(fifth_entry_bit_string[0:20], 2)
52 # FIXME: You have to scale me to the range from -200.0 to 200.0 in 0.1 steps
53 pulse_level_or_pulse_field_strength = math.ceil(int(fifth_entry_bit_string[20:32], 2)) / 10
54
55 sixth_entry = bin(int.from_bytes(parts[4], byteorder='little'))
56 padding = 32-len(str(sixth_entry)[2:])
57 sixth_entry_bit_string = "0" * padding + str(sixth_entry)[2:]
58 region_of_interest = bool(int(sixth_entry_bit_string[0]))
59 # FIXME: You have to scale me to a range from 0.0 to 6.2 in steps of 0.1 - 6.3 means unknown
60 azimuth_confidence = math.ceil(int(sixth_entry_bit_string[1:7], 2)) / 10
61 modulations = {0: 'Unknown', 1: 'Unmodulated', 2: 'FM', 3: 'LFM', 4: 'PSK-2', 5: 'PSK-3', 6: 'PSK-4',
62 7: 'PSK-m', 8: 'NLFM', 9: 'SFM', 10: 'TFM', 11: 'Pulse too short'}
63 modulation = modulations[int(sixth_entry_bit_string[7:12], 2)]
64 sector = int(sixth_entry_bit_string[28:32], 2)
65
66 seventh_entry = bin(int.from_bytes(parts[5], byteorder='little'))
67 padding = 32-len(str(seventh_entry)[2:])
68 seventh_entry_bit_string = "0" * padding + str(seventh_entry)[2:]
69 polarities = {0: 'Horizontal/Unknown', 1: 'Vertical', 2: 'Counter clockwise', 3: 'Clockwise'}
70 polarity = polarities[int(seventh_entry_bit_string[0:2], 2)]
71 df_quality = int(seventh_entry_bit_string[2:9], 2)
72 # FIXME: You have to scale me from -90 to 90 in 0.1 degree steps
73 elevation = int(seventh_entry_bit_string[9:20], 2)
74 # FIXME: You have to check me for a range from 0.0 to 359.9 in steps of 0.1
75 azimuth = 0.1 * (int(seventh_entry_bit_string[20:32], 2))
76
77 eighth_entry = bin(int.from_bytes(parts[5], byteorder='little'))
78 padding = 32-len(str(eighth_entry)[2:])
79 eighth_entry_bit_string = "0" * padding + str(eighth_entry)[2:]
80 channel = int(eighth_entry_bit_string[0:4], 2)
81
82 return PDW(time_of_arrival, pdw_format_identifier, center_frequency, is_valid, is_pulse, level_unit,
83 signal_start_missing, signal_end_missing, pulse_width, frequency_shift_or_bandwidth,
84 pulse_level_or_pulse_field_strength, region_of_interest, azimuth_confidence, modulation,
85 sector, polarity, df_quality, elevation, azimuth, channel)
86
87 def __init__(self, time_of_arrival, pdw_format_identifier, center_frequency, is_valid, is_pulse,
88 level_unit, signal_start_missing, signal_end_missing, pulse_width, frequency_shift_or_bandwidth,
89 pulse_level_or_pulse_field_strength, region_of_interest, azimuth_confidence, modulation,
90 sector, polarity, df_quality, elevation, azimuth, channel):
91 r"""
92 I return an instance of an Pulse Data word.
93
94 :param time_of_arrival: nanoseconds since 1970-01-01 00:00:00
95 :type time_of_arrival: Integer
96 :param pdw_format: format code
97 :type pdw_format: Integer
98 :param center_frequency: center frequency in KHz
99 :type center_frequency: Integer
100 :param is_valid: flag to mark if pdw data body is valid
101 :type is_valid: Boolean
102 :param is_pulse: flag to mark if pdw data body contains a pulse or a continuous wave signal
103 :type is_pulse: Boolean
104 :param level_unit: 0 means dBµV - 1 means dBµV/m
105 :type level_unit: Integer
106 :param signal_start_missing: signal started before time of arrival
107 :type signal_start_missing: Boolean
108 :param signal_end_missing: signal stops after time of arrival
109 :type signal_end_missing: Boolean
110 :param pulse_width: pulse width in nanoseconds - Zero if no valid pulse detected
111 :type pulse_width: Integer
112 :param frequency_shift_or_bandwidth: Value in KHz - Value set to 1048575 means Unknown
113 :type frequency_shift_or_bandwidth: Integer
114 :param pulse_level_or_pulse_field_strength: Pulse level or Pulse Field Strength depending on level_unit \
115 (-200.0...200.0) in 0.1 steps / minus 204.8 means no valid level detected
116 :type pulse_level_or_pulse_field_strength: Float
117 :param region_of_interest: Marks if signal is from region of interest
118 :type region_of_interest: Boolean
119 :param azimuth_confidence: degree in steps of 0.1 (0.0-6.2) / 6.3 means confidence unknown
120 :type azimuth_confidence: Float
121 :param modulation: type of modulation (e.g. PSK-2, PSK-4, FM etc.)
122 :type modulation: String
123 :param sector: reference antenna sector (0-15)
124 :type sector: Integer
125 :param polarity: Horizontal, Vertical, Clockwise, Counter clockwise
126 :type polarity: String
127 :param df_quality: Direction finding quality in percent (0-100) - Zero means unknown
128 :type df_quality: Integer
129 :param elevation: elevation of incoming signal (from -90 to 90 degree) in steps of 0.1 degree \
130 minus 102.4 means unknown
131 :type elevation: Float
132 :param azimuth: azimuth of incoming signal (from 0 to 359.9 degree) in steps of 0.1 degree \
133 plus 409.5 means unknown
134 :type azimuth: Float
135 :param channel: detecting channel (0-16) - Zero means unknown
136 :type channel: Integer
137 :return: An instance of class PDW with attributes set according to the data of a data body
138 :rtype: PDW
139 """
140 self.time_of_arrival = time_of_arrival #
141 self.pdw_format_identifier = pdw_format_identifier
142 self.center_frequency = center_frequency #
143 self.is_valid = is_valid #
144 self.is_pulse = is_pulse #
145 self.level_unit = level_unit #
146 self.signal_start_missing = signal_start_missing
147 self.signal_end_missing = signal_end_missing
148 self.pulse_width = pulse_width #
149 self.frequency_shift_or_bandwidth = frequency_shift_or_bandwidth #
150 self.pulse_level_or_pulse_field_strength = pulse_level_or_pulse_field_strength #
151 self.region_of_interest = region_of_interest
152 self.azimuth_confidence = azimuth_confidence
153 self.modulation = modulation #
154 self.sector = sector
155 self.polarity = polarity
156 self.df_quality = df_quality #
157 self.elevation = elevation #
158 self.azimuth = azimuth
159 self.channel = channel #
160
161 def __str__(self):
162 """
163 I return the string representation of myself.
164
165 :rtype: str
166 """
167 output = ("Time of arrival: " + str(self.time_of_arrival) + "\n" +
168 "PDW Format identifier: " + str(self.pdw_format_identifier) + "\n" +
169 "Center frequency: " + str(self.center_frequency) + " KHz\n")
170
171 if self.is_valid:
172 output += "Signal: Valid\n"
173 else:
174 output += "Signal: Invalid\n"
175
176 if self.is_pulse:
177 output += "Signal type: Pulse\n"
178 else:
179 output += "Signal type: Continuous wave\n"
180
181 if self.level_unit == 1:
182 output += "Pulse level: " + str(self.pulse_level_or_pulse_field_strength) + " dbµV\n"
183 else:
184 output += "Pulse field strength: " + str(self.pulse_level_or_pulse_field_strength) + " dbµV/meter\n"
185
186 output += ("Pulse width: " + str(self.pulse_width) + " nanoseconds\n" +
187 "Frequency shift or bandwidth: " + str(self.frequency_shift_or_bandwidth) + " KHz\n")
188
189 if self.region_of_interest:
190 output += "Region of interest: Yes\n"
191 else:
192 output += "Region of interest: No\n"
193
194 if self.azimuth_confidence == 6.3:
195 output += "Azimuth confidence: Invalid\n"
196 else:
197 output += "Azimuth confidence: " + str(self.azimuth_confidence) + " degree\n"
198
199 output += "Modulation: " + str(self.modulation) + "\n"
200
201 if self.sector == 0:
202 output += "Sector: Unknown\n"
203 else:
204 output += "Sector:" + str(self.sector) + "\n"
205
206 output += "Polarity: " + str(self.polarity) + "\n"
207
208 output += "DF quality: " + str(self.df_quality) + " %\n"
209
210 if self.elevation == 1024:
211 output += "Elevation: Unknown\n"
212 else:
213 output += "Elevation: " + str(self.elevation) + " degree\n"
214
215 if self.azimuth == 409.5:
216 output += "Azimuth: Unknown\n"
217 else:
218 output += "Azimuth: " + str(self.azimuth) + " degree\n"
219
220 output += "Channel: " + str(self.channel) + "\n"
221
222 return output
223
224 def to_json(self):
225 return {'TIMEOFARRIVAL': self.time_of_arrival.item(),
226 'CENTERFREQUENCY': self.center_frequency,
227 'VALID': self.is_valid,
228 'PULSE': self.is_pulse,
229 'PULSEWIDTH': self.pulse_width,
230 'LEVELUNIT': self.level_unit,
231 'FREQUENCYSHIFTORBANDWIDTH': self.frequency_shift_or_bandwidth,
232 'PULSELEVELORFIELDSTRENGTH': self.pulse_level_or_pulse_field_strength,
233 'MODULATION': self.modulation,
234 'ELEVATION': self.elevation,
235 'AZIMUTH': self.azimuth,
236 'CHANNEL': self.channel
237 }
238
239if __name__ == '__main__':
240 pass
Note: See TracBrowser for help on using the repository browser.