kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
				
				
				
			Added ability to temporary block the frequency of non-decodable (encrypted) sondes.
							rodzic
							
								
									e6aa3a043c
								
							
						
					
					
						commit
						f24fab881a
					
				|  | @ -61,6 +61,10 @@ exporter_objects = []   # This list will hold references to each exporter instan | |||
| exporter_functions = [] # This list will hold references to the exporter add functions, which will be passed onto the decoders. | ||||
| 
 | ||||
| 
 | ||||
| # Temporary frequency block list | ||||
| # This contains frequncies that should be blocked for a short amount of time. | ||||
| temporary_block_list = {} | ||||
| 
 | ||||
| 
 | ||||
| # Scan Result Queue | ||||
| # Scan results are processed asynchronously from the main scanner object. | ||||
|  | @ -96,7 +100,7 @@ def allocate_sdr(check_only = False, task_description = ""): | |||
| 
 | ||||
| def start_scanner(): | ||||
|     """ Start a scanner thread on the first available SDR """ | ||||
|     global config, scan_results, RS_PATH | ||||
|     global config, scan_results, RS_PATH, temporary_block_list | ||||
| 
 | ||||
|     if 'SCAN' in autorx.task_list: | ||||
|         # Already a scanner running! Return. | ||||
|  | @ -136,7 +140,9 @@ def start_scanner(): | |||
|             gain = autorx.sdr_list[_device_idx]['gain'], | ||||
|             ppm = autorx.sdr_list[_device_idx]['ppm'], | ||||
|             bias = autorx.sdr_list[_device_idx]['bias'], | ||||
|             save_detection_audio = config['save_detection_audio'] | ||||
|             save_detection_audio = config['save_detection_audio'], | ||||
|             temporary_block_list = temporary_block_list, | ||||
|             temporary_block_time = config['temporary_block_time'] | ||||
|             ) | ||||
| 
 | ||||
|         # Add a reference into the sdr_list entry | ||||
|  | @ -174,7 +180,19 @@ def start_decoder(freq, sonde_type): | |||
|         sonde_type (str): The radiosonde type ('RS41', 'RS92', 'DFM', 'M10, 'iMet') | ||||
| 
 | ||||
|     """ | ||||
|     global config, RS_PATH, exporter_functions, rs92_ephemeris | ||||
|     global config, RS_PATH, exporter_functions, rs92_ephemeris, temporary_block_list | ||||
| 
 | ||||
|     # Check the frequency is not in our temporary block list  | ||||
|     # (This may happen from time-to-time depending on the timing of the scan thread) | ||||
|     if freq in temporary_block_list.keys(): | ||||
|         if temporary_block_list[freq] > (time.time()-config['temporary_block_time']*60): | ||||
|             logging.error("Task Manager - Attempted to start a decoder on a temporarily blocked frequency (%.3f MHz)" % (freq/1e6)) | ||||
|             return | ||||
|         else: | ||||
|             # This frequency should not be blocked any more, remove it from the block list. | ||||
|             logging.info("Task Manager - Removed %.3f MHz from temporary block list." % (freq/1e6)) | ||||
|             temporary_block_list.pop(freq) | ||||
| 
 | ||||
| 
 | ||||
|     # Allocate a SDR. | ||||
|     _device_idx = allocate_sdr(task_description="Decoder (%s, %.3f MHz)" % (sonde_type, freq/1e6)) | ||||
|  | @ -250,6 +268,7 @@ def handle_scan_results(): | |||
|                 # Break if we don't support this sonde type. | ||||
|                 if (_check_type not in VALID_SONDE_TYPES): | ||||
|                     logging.error("Unsupported sonde type: %s" % _check_type) | ||||
|                     # TODO - Potentially add the frequency of the unsupported sonde to the temporary block list? | ||||
|                     continue | ||||
| 
 | ||||
|                 if allocate_sdr(check_only=True) is not None : | ||||
|  | @ -277,19 +296,40 @@ def clean_task_list(): | |||
|         try: | ||||
|             _running = autorx.task_list[_key]['task'].running() | ||||
|             _task_sdr = autorx.task_list[_key]['device_idx'] | ||||
|             _exit_state = autorx.task_list[_key]['task'].exit_state | ||||
|         except Exception as e: | ||||
|             logging.error("Task Manager - Error getting task %s state - %s" % (str(_key),str(e))) | ||||
|             continue | ||||
| 
 | ||||
|         if _running == False: | ||||
|             # This task has stopped. Release its associated SDR. | ||||
|             # This task has stopped. | ||||
|             # Check the exit state of the task for any abnormalities: | ||||
|             if _exit_state == "Encrypted": | ||||
|                 # This task was a decoder, and it has encountered an encrypted sonde. | ||||
|                 logging.info("Task Manager - Adding temporary block for frequency %.3f MHz" % (_key/1e6)) | ||||
|                 # Add the sonde's frequency to the global temporary block-list | ||||
|                 temporary_block_list[_key] = time.time() | ||||
|                 # If there is a scanner currently running, add it to the scanners internal block list. | ||||
|                 if 'SCAN' in autorx.task_list: | ||||
|                     auto_rx.task_list['SCAN']['task'].add_temporary_block(_key) | ||||
| 
 | ||||
| 
 | ||||
|             # Release its associated SDR. | ||||
|             autorx.sdr_list[_task_sdr]['in_use'] = False | ||||
|             autorx.sdr_list[_task_sdr]['task'] = None | ||||
| 
 | ||||
|             # Pop the task from the task list. | ||||
|             autorx.task_list.pop(_key) | ||||
|             # Indicate to the web client that the task list has been updated. | ||||
|             flask_emit_event('task_event') | ||||
| 
 | ||||
|     # Clean out the temporary block list of old entries. | ||||
|     for _freq in temporary_block_list.keys(): | ||||
|         if temporary_block_list[_freq] < (time.time() - config['temporary_block_time']*60): | ||||
|             temporary_block_list.pop(_freq) | ||||
|             logging.info("Task Manager - Removed %.3f MHz from temporary block list." % (_freq/1e6)) | ||||
| 
 | ||||
| 
 | ||||
|     # Check if there is a scanner thread still running. If not, and if there is a SDR free, start one up again. | ||||
|     if ('SCAN' not in autorx.task_list) and (allocate_sdr(check_only=True) is not None): | ||||
|         # We have a SDR free, and we are not running a scan thread. Start one. | ||||
|  |  | |||
|  | @ -100,7 +100,8 @@ def read_auto_rx_config(filename): | |||
| 		'scan_dwell_time' : 20, | ||||
| 		'detect_dwell_time' : 5, | ||||
| 		'scan_delay' : 10, | ||||
| 		'payload_id_valid' : 5,  | ||||
| 		'payload_id_valid' : 5, | ||||
| 		'temporary_block_time' : 60, | ||||
| 		# Rotator Settings | ||||
| 		'enable_rotator': False, | ||||
| 		'rotator_update_rate': 30, | ||||
|  | @ -245,6 +246,12 @@ def read_auto_rx_config(filename): | |||
| 		except: | ||||
| 			logging.error("Config - Could not find station_code field, using default.") | ||||
| 
 | ||||
| 		# New temporary block time - added 2019-04-14 | ||||
| 		try: | ||||
| 			auto_rx_config['temporary_block_time'] = config.getint('advanced', 'temporary_block_time') | ||||
| 		except: | ||||
| 			logging.error("Config - New advanced settings missing, using defaults.") | ||||
| 
 | ||||
| 
 | ||||
| 		# Now we attempt to read in the individual SDR parameters. | ||||
| 		auto_rx_config['sdr_settings'] = {} | ||||
|  |  | |||
|  | @ -142,6 +142,8 @@ class SondeDecoder(object): | |||
|         # This will become our decoder thread. | ||||
|         self.decoder = None | ||||
| 
 | ||||
|         self.exit_state = "OK" | ||||
| 
 | ||||
|         # Detect if we have an 'inverted' sonde. | ||||
|         if self.sonde_type.startswith('-'): | ||||
|             self.inverted = True | ||||
|  | @ -363,6 +365,7 @@ class SondeDecoder(object): | |||
|             if time.time() > (_last_packet + self.timeout): | ||||
|                 # If we have not seen data for a while, break. | ||||
|                 self.log_error("RX Timed out.") | ||||
|                 self.exit_state = "Timeout" | ||||
|                 break | ||||
|             else: | ||||
|                 # Otherwise, sleep for a short time. | ||||
|  | @ -442,9 +445,12 @@ class SondeDecoder(object): | |||
|                 if _field not in _telemetry: | ||||
|                     _telemetry[_field] = self.DECODER_OPTIONAL_FIELDS[_field] | ||||
| 
 | ||||
|             _telemetry['encrypted'] = True | ||||
| 
 | ||||
|             # Check for an encrypted flag (this indicates a sonde that we cannot decode telemetry from.) | ||||
|             if 'encrypted' in _telemetry: | ||||
|                 self.log_error("Radiosonde %s has encrypted telemetry (possible RS41-SGM)! We cannot decode this, closing decoder." % _telemetry['id']) | ||||
|                 self.exit_state = "Encrypted" | ||||
|                 self.decoder_running = False | ||||
|                 return False | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import platform | |||
| import subprocess | ||||
| import time | ||||
| import traceback | ||||
| from threading import Thread | ||||
| from threading import Thread, Lock | ||||
| from types import FunctionType, MethodType | ||||
| from .utils import detect_peaks, rtlsdr_test, reset_rtlsdr_by_serial, reset_all_rtlsdrs, peak_decimation | ||||
| try: | ||||
|  | @ -358,7 +358,9 @@ class SondeScanner(object): | |||
|         gain = -1, | ||||
|         ppm = 0, | ||||
|         bias = False, | ||||
|         save_detection_audio = False): | ||||
|         save_detection_audio = False, | ||||
|         temporary_block_list = {}, | ||||
|         temporary_block_time = 60): | ||||
|         """ Initialise a Sonde Scanner Object. | ||||
| 
 | ||||
|         Apologies for the huge number of args... | ||||
|  | @ -390,6 +392,8 @@ class SondeScanner(object): | |||
|             gain (int): SDR Gain setting, in dB. A gain setting of -1 enables the RTLSDR AGC. | ||||
|             bias (bool): If True, enable the bias tee on the SDR. | ||||
|             save_detection_audio (bool): Save the audio used in each detecton to detect_<device_idx>.wav | ||||
|             temporary_block_list (dict): A dictionary where each attribute represents a frequency that should be blacklisted for a set time. | ||||
|             temporary_block_time (int): How long (minutes) frequencies in the temporary block list should remain blocked for. | ||||
|         """ | ||||
| 
 | ||||
|         # Thread flag. This is set to True when a scan is running. | ||||
|  | @ -420,6 +424,14 @@ class SondeScanner(object): | |||
|         self.callback = callback | ||||
|         self.save_detection_audio = save_detection_audio | ||||
| 
 | ||||
|         # Temporary block list. | ||||
|         self.temporary_block_list = temporary_block_list.copy() | ||||
|         self.temporary_block_list_lock = Lock() | ||||
|         self.temporary_block_time = temporary_block_time | ||||
| 
 | ||||
|         # Alert the user if there are temporary blocks in place. | ||||
|         if len(self.temporary_block_list.keys())>0: | ||||
|             self.log_info("Temporary blocks in place for frequencies: %s" % str(self.temporary_block_list.keys())) | ||||
| 
 | ||||
|         # Error counter.  | ||||
|         self.error_retries = 0 | ||||
|  | @ -439,8 +451,11 @@ class SondeScanner(object): | |||
|         if not _rtlsdr_ok: | ||||
|             self.log_error("RTLSDR #%s non-functional - exiting." % device_idx) | ||||
|             self.sonde_scanner_running = False | ||||
|             self.exit_state = "Failed SDR" | ||||
|             return | ||||
| 
 | ||||
|         self.exit_state = "OK" | ||||
| 
 | ||||
|         if auto_start: | ||||
|             self.start() | ||||
| 
 | ||||
|  | @ -611,6 +626,7 @@ class SondeScanner(object): | |||
|             _, peak_idx = np.unique(peak_frequencies, return_index=True) | ||||
|             peak_frequencies = peak_frequencies[np.sort(peak_idx)] | ||||
| 
 | ||||
| 
 | ||||
|             # Remove any frequencies in the blacklist. | ||||
|             for _frequency in np.array(self.blacklist)*1e6: | ||||
|                 _index = np.argwhere(peak_frequencies==_frequency) | ||||
|  | @ -623,6 +639,25 @@ class SondeScanner(object): | |||
|             # Append on any frequencies in the supplied greylist | ||||
|             peak_frequencies = np.append(np.array(self.greylist)*1e6, peak_frequencies) | ||||
| 
 | ||||
|              | ||||
|             # Remove any frequencies in the temporary block list | ||||
|             self.temporary_block_list_lock.acquire() | ||||
|             for _frequency in self.temporary_block_list.keys(): | ||||
|                 # Check the time the block was added. | ||||
|                 if self.temporary_block_list[_frequency] > (time.time()-self.temporary_block_time*60): | ||||
|                     # We should still be blocking this frequency, so remove any peaks with this frequency. | ||||
|                     _index = np.argwhere(peak_frequencies==_frequency) | ||||
|                     peak_frequencies = np.delete(peak_frequencies, _index) | ||||
|                     if len(_index) > 0: | ||||
|                         self.log_debug("Peak on %.3f MHz was removed due to temporary block." % (_frequency/1e6)) | ||||
| 
 | ||||
|                 else: | ||||
|                     # This frequency doesn't need to be blocked any more, remove it from the block list. | ||||
|                     self.temporary_block_list.pop(_frequency) | ||||
|                     self.log_info("Removed %.3f MHz from temporary block list." % (_frequency/1e6)) | ||||
| 
 | ||||
|             self.temporary_block_list_lock.release() | ||||
| 
 | ||||
|             # Get the level of our peak search results, to send to the web client. | ||||
|             # This is actually a bit of a pain to do... | ||||
|             _peak_freq = [] | ||||
|  | @ -734,6 +769,20 @@ class SondeScanner(object): | |||
|         return self.sonde_scanner_running | ||||
| 
 | ||||
| 
 | ||||
|     def add_temporary_block(self, frequency): | ||||
|         """ Add a frequency to the temporary block list. | ||||
|              | ||||
|         Args: | ||||
|             frequency (float): Frequency to be blocked, in Hz | ||||
|         """ | ||||
|         # Acquire a lock on the block list, so we don't accidentally modify it | ||||
|         # while it is being used in a scan. | ||||
|         self.temporary_block_list_lock.acquire() | ||||
|         self.temporary_block_list[frequency] = time.time() | ||||
|         self.temporary_block_list_lock.release() | ||||
|         self.log_info("Adding temporary block for frequency %.3f MHz." % (frequency/1e6)) | ||||
| 
 | ||||
| 
 | ||||
|     def log_debug(self, line): | ||||
|         """ Helper function to log a debug message with a descriptive heading.  | ||||
|         Args: | ||||
|  |  | |||
|  | @ -335,6 +335,8 @@ detect_dwell_time = 5 | |||
| scan_delay = 10 | ||||
| # Quantize search results to x Hz steps. Useful as most sondes are on 10 kHz frequency steps.  | ||||
| quantization = 10000 | ||||
| # Temporary Block Time (minutes) - How long to block encrypted or otherwise non-decodable sondes for. | ||||
| temporary_block_time = 60 | ||||
| # Upload when (seconds_since_utc_epoch%upload_rate) == 0. Otherwise just delay upload_rate seconds between uploads. | ||||
| # Setting this to True with multple uploaders should give a higher chance of all uploaders uploading the same frame, | ||||
| # however the upload_rate should not be set too low, else there may be a chance of missing upload slots. | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Mark Jessop
						Mark Jessop