Changeset a8f6b97 in ammosreader
- Timestamp:
- 09/03/23 17:01:14 (21 months ago)
- Branches:
- guix
- Children:
- be36a02
- Parents:
- 2b95c18
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
ammosreader/PDW.py
r2b95c18 ra8f6b97 4 4 import math 5 5 import numpy as np 6 from bitstring import BitArray 6 7 from ammosreader import logger 7 8 … … 13 14 """ 14 15 16 @classmethod 17 def polarities(cls): 18 return {0: 'Horizontal/Unknown', 1: 'Vertical', 2: 'Counter clockwise', 3: 'Clockwise'} 19 20 @classmethod 21 def modulations(cls): 22 return {0: 'Unknown', 1: 'Unmodulated', 2: 'FM', 3: 'LFM', 4: 'PSK-2', 5: 'PSK-3', 6: 'PSK-4', 23 7: 'PSK-m', 8: 'NLFM', 9: 'SFM', 10: 'TFM', 11: 'Pulse too short'} 24 15 25 @classmethod 16 26 def from_bytes(cls, byte_string): … … 36 46 if nanoseconds >= unix_time: 37 47 raise OverflowError("Timestamp invalid") 38 time_of_arrival = np.datetime64(nanoseconds, 'ns') 39 40 third_entry = bin(int.from_bytes(parts[1], byteorder='little')) 41 padding = 32-len(str(third_entry)[2:]) 42 third_entry_bit_string = "0" * padding + str(third_entry)[2:] 43 pdw_format_identifier = int(third_entry_bit_string[0:6], 2) 44 center_frequency = int(third_entry_bit_string[5:32], 2) 45 46 fourth_entry = bin(int.from_bytes(parts[2], byteorder='little')) 47 padding = 32-len(str(fourth_entry)[2:]) 48 fourth_entry_bit_string = "0" * padding + str(fourth_entry)[2:] 49 is_valid = bool(int(fourth_entry_bit_string[0])) 50 is_pulse = bool(int(fourth_entry_bit_string[1])) 51 level_unit = bool(int(fourth_entry_bit_string[2])) 52 signal_start_missing = bool(int(fourth_entry_bit_string[3])) 53 signal_end_missing = bool(int(fourth_entry_bit_string[4])) 54 pulse_width = int(fourth_entry_bit_string[7:33], 2) 55 56 fifth_entry = bin(int.from_bytes(parts[3], byteorder='little')) 57 padding = 32-len(str(fifth_entry)[2:]) 58 fifth_entry_bit_string = "0" * padding + str(fifth_entry)[2:] 59 frequency_shift_or_bandwidth = int(fifth_entry_bit_string[0:20], 2) 60 # FIXME: You have to scale me to the range from -200.0 to 200.0 in 0.1 steps 61 pulse_level_or_pulse_field_strength = math.ceil(int(fifth_entry_bit_string[20:32], 2)) / 10 62 63 sixth_entry = bin(int.from_bytes(parts[4], byteorder='little')) 64 padding = 32-len(str(sixth_entry)[2:]) 65 sixth_entry_bit_string = "0" * padding + str(sixth_entry)[2:] 66 region_of_interest = bool(int(sixth_entry_bit_string[0])) 67 # FIXME: You have to scale me to a range from 0.0 to 6.2 in steps of 0.1 - 6.3 means unknown 68 azimuth_confidence = math.ceil(int(sixth_entry_bit_string[1:7], 2)) / 10 69 modulations = {0: 'Unknown', 1: 'Unmodulated', 2: 'FM', 3: 'LFM', 4: 'PSK-2', 5: 'PSK-3', 6: 'PSK-4', 70 7: 'PSK-m', 8: 'NLFM', 9: 'SFM', 10: 'TFM', 11: 'Pulse too short'} 71 modulation = modulations.get(int(sixth_entry_bit_string[7:12], 2), 0) 72 sector = int(sixth_entry_bit_string[28:32], 2) 73 74 seventh_entry = bin(int.from_bytes(parts[5], byteorder='little')) 75 padding = 32-len(str(seventh_entry)[2:]) 76 seventh_entry_bit_string = "0" * padding + str(seventh_entry)[2:] 77 polarities = {0: 'Horizontal/Unknown', 1: 'Vertical', 2: 'Counter clockwise', 3: 'Clockwise'} 78 polarity = polarities[int(seventh_entry_bit_string[0:2], 2)] 79 df_quality = int(seventh_entry_bit_string[2:9], 2) 80 # FIXME: You have to scale me from -90 to 90 in 0.1 degree steps 81 elevation = int(seventh_entry_bit_string[9:20], 2) / 10 82 # FIXME: You have to check me for a range from 0.0 to 359.9 in steps of 0.1 83 azimuth = 0.1 * (int(seventh_entry_bit_string[20:32], 2)) 84 85 eighth_entry = bin(int.from_bytes(parts[5], byteorder='little')) 86 padding = 32-len(str(eighth_entry)[2:]) 87 eighth_entry_bit_string = "0" * padding + str(eighth_entry)[2:] 88 channel = int(eighth_entry_bit_string[0:4], 2) 48 time_of_arrival = nanoseconds 49 50 third_word = bin(int.from_bytes(parts[1], byteorder='little')) 51 padding = 32-len(str(third_word)[2:]) 52 third_word_bit_string = "0" * padding + str(third_word)[2:] 53 pdw_format_identifier = int(third_word_bit_string[0:6], 2) 54 center_frequency = int(third_word_bit_string[5:32], 2) # Value of zero means unknown 55 56 fourth_word = bin(int.from_bytes(parts[2], byteorder='little')) 57 padding = 32-len(str(fourth_word)[2:]) 58 fourth_word_bit_string = "0" * padding + str(fourth_word)[2:] 59 is_valid = bool(int(fourth_word_bit_string[0])) 60 is_pulse = bool(int(fourth_word_bit_string[1])) 61 level_unit = int(fourth_word_bit_string[2]) 62 signal_start_missing = bool(int(fourth_word_bit_string[3])) 63 signal_end_missing = bool(int(fourth_word_bit_string[4])) 64 pulse_width = int(fourth_word_bit_string[7:33], 2) 65 66 fifth_word = bin(int.from_bytes(parts[3], byteorder='little')) 67 padding = 32-len(str(fifth_word)[2:]) 68 fifth_word_bit_string = "0" * padding + str(fifth_word)[2:] 69 frequency_shift_or_bandwidth = int(fifth_word_bit_string[0:20], 2) 70 pulse_level_or_pulse_field_strength = BitArray(fifth_word_bit_string[20:32]).int 71 72 sixth_word = bin(int.from_bytes(parts[4], byteorder='little')) 73 padding = 32-len(str(sixth_word)[2:]) 74 sixth_word_bit_string = "0" * padding + str(sixth_word)[2:] 75 region_of_interest = bool(int(sixth_word_bit_string[0])) 76 azimuth_confidence = int(sixth_word_bit_string[1:7], 2) 77 modulation = int(sixth_word_bit_string[7:12], 2) 78 sector = int(sixth_word_bit_string[28:32], 2) 79 80 seventh_word = bin(int.from_bytes(parts[5], byteorder='little')) 81 padding = 32-len(str(seventh_word)[2:]) 82 seventh_word_bit_string = "0" * padding + str(seventh_word)[2:] 83 polarity = int(seventh_word_bit_string[0:2], 2) 84 df_quality = int(seventh_word_bit_string[2:9], 2) 85 elevation = BitArray(bin=seventh_word_bit_string[9:20]).int 86 azimuth = int(seventh_word_bit_string[20:32], 2) 87 88 eighth_word = bin(int.from_bytes(parts[5], byteorder='little')) 89 padding = 32-len(str(eighth_word)[2:]) 90 eighth_word_bit_string = "0" * padding + str(eighth_word)[2:] 91 channel = int(eighth_word_bit_string[0:4], 2) 89 92 90 93 return PDW(time_of_arrival, pdw_format_identifier, center_frequency, is_valid, is_pulse, level_unit, … … 110 113 :param is_pulse: flag to mark if pdw data body contains a pulse or a continuous wave signal 111 114 :type is_pulse: Boolean 112 :param level_unit: 0 means dBµV - 1means dBµV/m115 :param level_unit: 1 means dBµV - 0 means dBµV/m 113 116 :type level_unit: Integer 114 117 :param signal_start_missing: signal started before time of arrival … … 121 124 :type frequency_shift_or_bandwidth: Integer 122 125 :param pulse_level_or_pulse_field_strength: Pulse level or Pulse Field Strength depending on level_unit \ 123 (-200.0...200.0) in 0.1 steps / minus 204.8 means no valid level detected124 :type pulse_level_or_pulse_field_strength: Float126 (-200.0...200.0) in tenth degrees / minus 2048 means no valid level detected 127 :type pulse_level_or_pulse_field_strength: Integer 125 128 :param region_of_interest: Marks if signal is from region of interest 126 129 :type region_of_interest: Boolean 127 :param azimuth_confidence: degree in steps of 0.1 (0.0-6.2) / 6.3 means confidence unknown128 :type azimuth_confidence: Float130 :param azimuth_confidence: degree in tenth steps of (0-62) / 63 means confidence unknown 131 :type azimuth_confidence: Integer 129 132 :param modulation: type of modulation (e.g. PSK-2, PSK-4, FM etc.) 130 133 :type modulation: String … … 135 138 :param df_quality: Direction finding quality in percent (0-100) - Zero means unknown 136 139 :type df_quality: Integer 137 :param elevation: elevation of incoming signal (from -90 to 90 degree) in steps of 0.1 degree \ 138 minus 102.4 means unknown 139 :type elevation: Float 140 :param azimuth: azimuth of incoming signal (from 0 to 359.9 degree) in steps of 0.1 degree \ 141 plus 409.5 means unknown 142 :type azimuth: Float 140 :param elevation: elevation of incoming signal (from -90 to 90 degree) in steps tenths degree \ 141 minus 1024 means unknown 142 :type elevation: Integer 143 :param azimuth: azimuth of incoming signal (from 0 to 3599 tenth degree) plus 4095 means unknown 144 :type azimuth: Integer 143 145 :param channel: detecting channel (0-16) - Zero means unknown 144 146 :type channel: Integer … … 146 148 :rtype: PDW 147 149 """ 148 self. time_of_arrival = time_of_arrival #149 self. pdw_format_identifier = pdw_format_identifier150 self. center_frequency = center_frequency #150 self.__time_of_arrival = time_of_arrival # 151 self.__pdw_format_identifier = pdw_format_identifier 152 self.__center_frequency = center_frequency # 151 153 self.__is_valid = is_valid # 152 self. is_pulse = is_pulse #153 self. level_unit = level_unit #154 self. signal_start_missing = signal_start_missing155 self. signal_end_missing = signal_end_missing156 self. pulse_width = pulse_width #157 self. frequency_shift_or_bandwidth = frequency_shift_or_bandwidth #158 self. pulse_level_or_pulse_field_strength = pulse_level_or_pulse_field_strength #159 self. region_of_interest = region_of_interest160 self. azimuth_confidence = azimuth_confidence161 self. modulation = modulation #162 self. sector = sector163 self. polarity = polarity164 self. df_quality = df_quality #165 self. elevation = elevation #166 self. azimuth = azimuth167 self. channel = channel #154 self.__is_pulse = is_pulse # 155 self.__level_unit = level_unit # 156 self.__signal_start_missing = signal_start_missing 157 self.__signal_end_missing = signal_end_missing 158 self.__pulse_width = pulse_width # 159 self.__frequency_shift_or_bandwidth = frequency_shift_or_bandwidth # 160 self.__pulse_level_or_pulse_field_strength = pulse_level_or_pulse_field_strength # 161 self.__region_of_interest = region_of_interest 162 self.__azimuth_confidence = azimuth_confidence 163 self.__modulation = modulation # 164 self.__sector = sector 165 self.__polarity = polarity 166 self.__df_quality = df_quality # 167 self.__elevation = elevation # 168 self.__azimuth = azimuth 169 self.__channel = channel # 168 170 169 171 def __str__(self): … … 177 179 "Center frequency: " + str(self.center_frequency) + " KHz\n") 178 180 179 if self. __is_valid:181 if self.is_valid: 180 182 output += "Signal: Valid\n" 181 183 else: … … 187 189 output += "Signal type: Continuous wave\n" 188 190 189 if self.level_unit == 1: 191 # FIXME: use fstrings or another better performance string concat 192 if self.level_unit == "dbµV": 190 193 output += "Pulse level: " + str(self.pulse_level_or_pulse_field_strength) + " dbµV\n" 191 194 else: 192 output += "Pulse field strength: " + str(self.pulse_level_or_pulse_field_strength) + " dbµV/m eter\n"195 output += "Pulse field strength: " + str(self.pulse_level_or_pulse_field_strength) + " dbµV/m\n" 193 196 194 197 output += ("Pulse width: " + str(self.pulse_width) + " nanoseconds\n" + … … 200 203 output += "Region of interest: No\n" 201 204 202 if self.azimuth_confidence == 6.3:205 if self.azimuth_confidence == None: 203 206 output += "Azimuth confidence: Invalid\n" 204 207 else: … … 207 210 output += "Modulation: " + str(self.modulation) + "\n" 208 211 209 if self.sector == 0:212 if self.sector == None: 210 213 output += "Sector: Unknown\n" 211 214 else: … … 216 219 output += "DF quality: " + str(self.df_quality) + " %\n" 217 220 218 if self.elevation == 102.4:221 if self.elevation == None: 219 222 output += "Elevation: Unknown\n" 220 223 else: 221 224 output += "Elevation: " + str(self.elevation) + " degree\n" 222 225 223 if self.azimuth == 409.5:226 if self.azimuth == None: 224 227 output += "Azimuth: Unknown\n" 225 228 else: … … 230 233 return output 231 234 235 @property 236 def time_of_arrival(self): 237 return np.datetime64(self.__time_of_arrival, 'ns') 238 239 @property 240 def center_frequency(self): 241 if self.__center_frequency == 0: 242 return None 243 return self.__center_frequency 244 245 @property 232 246 def is_valid(self): 233 247 return self.__is_valid 234 248 249 @property 250 def is_invalid(self): 251 return not self.__is_valid 252 253 @property 254 def is_pulse(self): 255 return self.__is_pulse 256 257 @property 258 def is_cw(self): 259 return not self.__is_pulse 260 261 @property 262 def level_unit(self): 263 if self.__level_unit == 1: 264 return "dbµV" 265 else: 266 return "dbµV/m" 267 268 @property 269 def signal_start_missing(self): 270 return self.__signal_start_missing 271 272 @property 273 def signal_end_missing(self): 274 return self.__signal_end_missing 275 276 @property 277 def pulse_width(self): 278 if self.__pulse_width == 0: 279 return None 280 return self.__pulse_width 281 282 @property 283 def frequency_shift_or_bandwidth(self): 284 if self.__frequency_shift_or_bandwidth == 1048575: 285 return None 286 return self.__frequency_shift_or_bandwidth 287 288 @property 289 def pulse_level_or_pulse_field_strength(self): 290 if self.__pulse_level_or_pulse_field_strength == -2048: 291 return None 292 return (self.__pulse_level_or_field_strength / 10) 293 294 @property 295 def azimuth_confidence(self): 296 if azimuth_confidence == 63: 297 return None 298 return (azimuth_confidence / 10) 299 300 @property 301 def modulation(self): 302 return PDW.modulations().get(self.__modulation, PDW.modulations()[0]) 303 304 @property 305 def region_of_interest(self): 306 return self.__region_of_interest 307 308 @property 309 def sector(self): 310 if self.__sector == 0: 311 return None 312 return self.__sector 313 314 @property 315 def polarity(self): 316 return PDW.polarities().get(self.__polarity, PDW.polarities()[0]) 317 318 @property 319 def elevation(self): 320 if self.__elevation == -1024: 321 return None 322 return self.__elevation / 10 323 324 @property 325 def azimuth(self): 326 if self.__azimuth == 4095: 327 return None 328 return self.__azimuth / 10 329 330 @property 331 def channel(self): 332 return self.__channel 333 235 334 def to_datacrunch_json(self): 236 335 import uuid … … 238 337 'FORMAT': self.pdw_format_identifier, 239 338 'CENTER_FREQUENCY': self.center_frequency, 240 'VALID_FLAG': self. __is_valid,339 'VALID_FLAG': self.is_valid, 241 340 'PULSE_FLAG': self.is_pulse, 242 341 'LU_FLAG': self.level_unit, … … 259 358 260 359 def to_json(self): 261 return {' time of arrival': self.time_of_arrival,360 return {'TIME OF ARRIVAL': self.time_of_arrival, 262 361 'FORMATIDENTIFIER': self.pdw_format_identifier, 263 ' center frequency': self.center_frequency,264 'VALID': self. __is_valid,362 'CENTER FREQUENCY': self.center_frequency, 363 'VALID': self.is_valid, 265 364 'PULSE': self.is_pulse, 266 365 'PULSELEVEL': self.pulse_level_or_pulse_field_strength, -
pyproject.toml
r2b95c18 ra8f6b97 28 28 [tool.poetry.dependencies] 29 29 numpy = "~1.23.2" 30 30 bitstring = "~3.1.7"
Note:
See TracChangeset
for help on using the changeset viewer.