def __call__(self, schedule_entry_name, task_id): """This is the entrypoint function called by the scheduler.""" from tasks.models import TaskResult # Raises TaskResult.DoesNotExist if no matching task result task_result = TaskResult.objects.get( schedule_entry__name=schedule_entry_name, task_id=task_id ) self.test_required_components() for recording_id, measurement_params in enumerate( self.measurement_params_list, start=1 ): start_time = utils.get_datetime_str_now() data = self.acquire_data(measurement_params, task_id) end_time = utils.get_datetime_str_now() sigmf_md = self.build_sigmf_md( task_id, measurement_params, data, task_result.schedule_entry, recording_id, start_time, end_time, ) self.archive(task_result, recording_id, data, sigmf_md)
def acquire_data(self, fc, parent_entry, task_id): tuning_parameters = self.tuning_parameters[fc] self.configure_usrp(fc, **tuning_parameters) # Use the radio's actual reported sample rate instead of requested rate sample_rate = self.usrp.radio.sample_rate # Build global metadata sigmf_md = SigMFFile() sigmf_md.set_global_info(GLOBAL_INFO) sigmf_md.set_global_field("core:sample_rate", sample_rate) sigmf_md.set_global_field("core:description", self.description) sensor_def = capabilities['sensor_definition'] sigmf_md.set_global_field("ntia:sensor_definition", sensor_def) sigmf_md.set_global_field("ntia:sensor_id", settings.FQDN) sigmf_md.set_global_field("scos:version", SCOS_TRANSFER_SPEC_VER) # Acquire data and build per-capture metadata data = np.array([], dtype=np.complex64) nsamps = int(sample_rate * tuning_parameters['duration_ms'] * 1e-3) dt = utils.get_datetime_str_now() acq = self.usrp.radio.acquire_samples(nsamps).astype(np.complex64) data = np.append(data, acq) capture_md = {"core:frequency": fc, "core:datetime": dt} sigmf_md.add_capture(start_index=0, metadata=capture_md) annotation_md = {"applied_scale_factor": self.usrp.radio.scale_factor} sigmf_md.add_annotation(start_index=0, length=nsamps, metadata=annotation_md) return data, sigmf_md
def status(request, version, format=None): """The status overview of the sensor.""" return Response({ "scheduler": scheduler.thread.status, "location": serialize_location(), "system_time": utils.get_datetime_str_now(), "last_calibration_time": get_last_calibration_time(), })
def __call__(self, schedule_entry_name, task_id): """This is the entrypoint function called by the scheduler.""" from tasks.models import TaskResult # Raises TaskResult.DoesNotExist if no matching task result task_result = TaskResult.objects.get( schedule_entry__name=schedule_entry_name, task_id=task_id) self.test_required_components() self.configure_sdr() start_time = utils.get_datetime_str_now() data = self.acquire_data() end_time = utils.get_datetime_str_now() m4s_data = self.apply_detector(data) sigmf_md = self.build_sigmf_md(task_id, data, task_result.schedule_entry, start_time, end_time) self.archive(task_result, m4s_data, sigmf_md)
def status(request, version, format=None): """The status overview of the sensor.""" context = {'request': request} taskq = scheduler.thread.task_queue.to_list()[:settings.MAX_TASK_QUEUE] task_serializer = TaskSerializer(taskq, many=True, context=context) return Response({ 'scheduler': scheduler.thread.status, 'location': get_location(), 'system_time': utils.get_datetime_str_now(), 'task_queue': task_serializer.data })
def build_sigmf_md(self): logger.debug("Building SigMF metadata file") sigmf_md = SigMFFile() sigmf_md.set_global_field("core:datatype", "rf32_le") sigmf_md.set_global_field("core:sample_rate", self.sample_rate) sigmf_md.set_global_field("core:description", self.description) # get() expects a single object - currently there are none and this fails. # how do I put one in? #sensor_def_obj = SensorDefinition.objects.get() #sensor_def_json = SensorDefinitionSerializer(sensor_def_obj).data #sigmf_md.set_global_field("scos:sensor_definition", sensor_def_json) try: fqdn = settings.ALLOWED_HOSTS[1] except IndexError: fqdn = 'not.set' sigmf_md.set_global_field("scos:sensor_id", fqdn) sigmf_md.set_global_field("scos:version", SCOS_TRANSFER_SPEC_VER) capture_md = { "core:frequency": self.frequency, "core:time": utils.get_datetime_str_now() } sigmf_md.add_capture(start_index=0, metadata=capture_md) # for i, detector in enumerate(M4sDetector): # single_frequency_fft_md = { # "number_of_samples_in_fft": self.fft_size, # "window": "blackman", # "equivalent_noise_bandwidth": self.enbw, # "detector": detector.name + "_power", # "number_of_ffts": self.nffts, # "units": "dBm", # "reference": "not referenced" # } # annotation_md = { # "scos:measurement_type": { # "single_frequency_fft_detection": single_frequency_fft_md, # } # } # sigmf_md.add_annotation( # start_index=(i * self.fft_size), # length=self.fft_size, # metadata=annotation_md # ) return sigmf_md
def build_sigmf_md(self): logger.debug("Building SigMF metadata file") sigmf_md = SigMFFile() sigmf_md.set_global_info(GLOBAL_INFO) sigmf_md.set_global_field("core:sample_rate", self.sample_rate) sigmf_md.set_global_field("core:description", self.description) try: sensor_def_obj = SensorDefinition.objects.get() sensor_def = SensorDefinitionSerializer(sensor_def_obj).data sigmf_md.set_global_field("scos:sensor_definition", sensor_def) except SensorDefinition.DoesNotExist: pass try: fqdn = settings.ALLOWED_HOSTS[1] except IndexError: fqdn = 'not.set' sigmf_md.set_global_field("scos:sensor_id", fqdn) sigmf_md.set_global_field("scos:version", SCOS_TRANSFER_SPEC_VER) capture_md = { "core:frequency": self.frequency, "core:time": utils.get_datetime_str_now() } sigmf_md.add_capture(start_index=0, metadata=capture_md) for i, detector in enumerate(M4sDetector): single_frequency_fft_md = { "number_of_samples_in_fft": self.fft_size, "window": "blackman", "equivalent_noise_bandwidth": self.enbw, "detector": detector.name + "_power", "number_of_ffts": self.nffts, "units": "dBm", "reference": "not referenced" } annotation_md = { "scos:measurement_type": { "single_frequency_fft_detection": single_frequency_fft_md, } } sigmf_md.add_annotation(start_index=(i * self.fft_size), length=self.fft_size, metadata=annotation_md) return sigmf_md
def build_sigmf_md(self): logger.debug("Building SigMF metadata file") sigmf_md = SigMFFile() sigmf_md.set_global_info(GLOBAL_INFO) sigmf_md.set_global_field("core:sample_rate", self.sample_rate) sigmf_md.set_global_field("core:description", self.description) sensor_def = capabilities['sensor_definition'] sigmf_md.set_global_field("ntia:sensor_definition", sensor_def) sigmf_md.set_global_field("ntia:sensor_id", settings.FQDN) sigmf_md.set_global_field("scos:version", SCOS_TRANSFER_SPEC_VER) capture_md = { "core:frequency": self.frequency, "core:time": utils.get_datetime_str_now() } sigmf_md.add_capture(start_index=0, metadata=capture_md) for i, detector in enumerate(M4sDetector): single_frequency_fft_md = { "number_of_samples_in_fft": self.fft_size, "window": "blackman", "equivalent_noise_bandwidth": self.enbw, "detector": detector.name + "_power", "number_of_ffts": self.nffts, "units": "dBm", "reference": "not referenced" } annotation_md = { "scos:measurement_type": { "single_frequency_fft_detection": single_frequency_fft_md, } } sigmf_md.add_annotation(start_index=(i * self.fft_size), length=self.fft_size, metadata=annotation_md) return sigmf_md
def acquire_data(self, parent_entry, task_id): # Build global metadata sigmf_md = SigMFFile() sigmf_md.set_global_info(GLOBAL_INFO) sigmf_md.set_global_field("core:sample_rate", self.sample_rate) sigmf_md.set_global_field("core:description", self.description) try: sensor_def_obj = SensorDefinition.objects.get() sensor_def = SensorDefinitionSerializer(sensor_def_obj).data sigmf_md.set_global_field("scos:sensor_definition", sensor_def) except SensorDefinition.DoesNotExist: pass try: fqdn = settings.ALLOWED_HOSTS[1] except IndexError: fqdn = 'not.set' sigmf_md.set_global_field("scos:sensor_id", fqdn) sigmf_md.set_global_field("scos:version", SCOS_TRANSFER_SPEC_VER) # Acquire data and build per-capture metadata data = np.array([], dtype=np.complex64) nsamps = self.nsamples for idx, fc in enumerate(self.fcs): self.usrp.radio.tune_frequency(fc) dt = utils.get_datetime_str_now() acq = self.usrp.radio.acquire_samples(nsamps).astype(np.complex64) data = np.append(data, acq) start_idx = idx * nsamps capture_md = {"core:frequency": fc, "core:datetime": dt} sigmf_md.add_capture(start_index=start_idx, metadata=capture_md) annotation_md = { "applied_scale_factor": self.usrp.radio.scale_factor } sigmf_md.add_annotation(start_index=start_idx, length=nsamps, metadata=annotation_md) return data, sigmf_md
def build_sigmf_md(self, task_id, measurement_params, data): # Build global metadata sigmf_md = SigMFFile() sigmf_md.set_global_info(GLOBAL_INFO) sample_rate = self.sdr.radio.sample_rate sigmf_md.set_global_field("core:sample_rate", sample_rate) sensor_def = capabilities["sensor_definition"] sensor_def["id"] = settings.FQDN sigmf_md.set_global_field("ntia-sensor:sensor", sensor_def) action_def = { "name": self.name, "description": self.description, "type": ["TimeDomain"], } sigmf_md.set_global_field("ntia-scos:action", action_def) sigmf_md.set_global_field("ntia-scos:task_id", task_id) dt = utils.get_datetime_str_now() num_samples = measurement_params.get_num_samples() capture_md = { "core:frequency": self.sdr.radio.frequency, "core:datetime": dt } sigmf_md.add_capture(start_index=0, metadata=capture_md) calibration_annotation_md = self.sdr.radio.create_calibration_annotation( ) sigmf_md.add_annotation(start_index=0, length=num_samples, metadata=calibration_annotation_md) time_domain_detection_md = { "ntia-core:annotation_type": "TimeDomainDetection", "ntia-algorithm:detector": "sample_iq", "ntia-algorithm:detection_domain": "time", "ntia-algorithm:number_of_samples": num_samples, "ntia-algorithm:units": "volts", "ntia-algorithm:reference": "not referenced", } sigmf_md.add_annotation(start_index=0, length=num_samples, metadata=time_domain_detection_md) # Recover the sigan overload flag sigan_overload = self.sdr.radio.sigan_overload # Check time domain average power versus calibrated compression time_domain_avg_power = 10 * np.log10(np.mean(np.abs(data)**2)) time_domain_avg_power += (10 * np.log10(1 / (2 * 50)) + 30 ) # Convert log(V^2) to dBm sensor_overload = ( time_domain_avg_power > self.sdr.radio.sensor_calibration_data["1db_compression_sensor"]) # Create SensorAnnotation and add gain setting and overload indicators sensor_annotation_md = { "ntia-core:annotation_type": "SensorAnnotation", "ntia-sensor:overload_sensor": sensor_overload, "ntia-sensor:overload_sigan": sigan_overload, "ntia-sensor:gain_setting_sigan": measurement_params.gain, } location = get_location() if location: sensor_annotation_md["core:latitude"] = (location.latitude, ) sensor_annotation_md["core:longitude"] = location.longitude sigmf_md.add_annotation(start_index=0, length=num_samples, metadata=sensor_annotation_md) return sigmf_md
def acquire_samples(self, n, nskip=0, retries=5): # -> np.ndarray: """Aquire nskip+n samples and return the last n""" self.sigan_overload = False self.capture_time = None # Get the calibration data for the acquisition self.recompute_calibration_data() # Compute the linear gain db_gain = self.sensor_calibration_data["gain_sensor"] linear_gain = 10 ** (db_gain / 20.0) # Try to acquire the samples max_retries = retries while True: # No need to skip initial samples when simulating the radio if settings.MOCK_RADIO: nsamps = n else: nsamps = n + nskip self.capture_time = utils.get_datetime_str_now() samples = self.usrp.recv_num_samps( nsamps, # number of samples self.frequency, # center frequency in Hz self.sample_rate, # sample rate in samples per second [0], # channel list self.gain, # gain in dB ) # usrp.recv_num_samps returns a numpy array of shape # (n_channels, n_samples) and dtype complex64 assert samples.dtype == np.complex64 assert len(samples.shape) == 2 and samples.shape[0] == 1 data = samples[0] # isolate data for channel 0 data_len = len(data) if not settings.MOCK_RADIO: data = data[nskip:] if not len(data) == n: if retries > 0: msg = "USRP error: requested {} samples, but got {}." logger.warning(msg.format(n + nskip, data_len)) logger.warning("Retrying {} more times.".format(retries)) retries = retries - 1 else: err = "Failed to acquire correct number of samples " err += "{} times in a row.".format(max_retries) raise RuntimeError(err) else: logger.debug("Successfully acquired {} samples.".format(n)) # Check IQ values versus ADC max for sigan compression self.sigan_overload = False i_samples = np.abs(np.real(data)) q_samples = np.abs(np.imag(data)) i_over_threshold = np.sum(i_samples > self.ADC_FULL_RANGE_THRESHOLD) q_over_threshold = np.sum(q_samples > self.ADC_FULL_RANGE_THRESHOLD) total_over_threshold = i_over_threshold + q_over_threshold ratio_over_threshold = float(total_over_threshold) / n if ratio_over_threshold > self.ADC_OVERLOAD_THRESHOLD: self.sigan_overload = True # Scale the data back to RF power and return it data /= linear_gain return data
def build_sigmf_md(self, task_id, data): logger.debug("Building SigMF metadata file") # Use the radio's actual reported sample rate instead of requested rate sample_rate = self.sdr.radio.sample_rate sigmf_md = SigMFFile() sigmf_md.set_global_info(GLOBAL_INFO) sigmf_md.set_global_field("core:sample_rate", sample_rate) sensor_def = capabilities["sensor_definition"] sensor_def["id"] = settings.FQDN sigmf_md.set_global_field("ntia-sensor:sensor", sensor_def) action_def = { "name": self.name, "description": self.description, "type": ["FrequencyDomain"], } sigmf_md.set_global_field("ntia-scos:action", action_def) sigmf_md.set_global_field("ntia-scos:task_id", task_id) capture_md = { "core:frequency": self.sdr.radio.frequency, "core:datetime": utils.get_datetime_str_now(), } sigmf_md.add_capture(start_index=0, metadata=capture_md) for i, detector in enumerate(M4sDetector): frequency_domain_detection_md = { "ntia-core:annotation_type": "FrequencyDomainDetection", "ntia-algorithm:number_of_samples_in_fft": self.measurement_params.fft_size, "ntia-algorithm:window": "flattop", "ntia-algorithm:equivalent_noise_bandwidth": self.enbw, "ntia-algorithm:detector": detector.name + "_power", "ntia-algorithm:number_of_ffts": self.measurement_params.num_ffts, "ntia-algorithm:units": "dBm", "ntia-algorithm:reference": "not referenced", "nita-algorithm:detection_domain": "frequency", } sigmf_md.add_annotation( start_index=(i * self.measurement_params.fft_size), length=self.measurement_params.fft_size, metadata=frequency_domain_detection_md, ) calibration_annotation_md = self.sdr.radio.create_calibration_annotation( ) sigmf_md.add_annotation( start_index=0, length=self.measurement_params.fft_size * len(M4sDetector), metadata=calibration_annotation_md, ) # Recover the sigan overload flag sigan_overload = self.sdr.radio.sigan_overload # Check time domain average power versus calibrated compression flattened_data = data.flatten() time_domain_avg_power = 10 * np.log10( np.mean(np.abs(flattened_data)**2)) time_domain_avg_power += (10 * np.log10(1 / (2 * 50)) + 30 ) # Convert log(V^2) to dBm sensor_overload = ( time_domain_avg_power > self.sdr.radio.sensor_calibration_data["1db_compression_sensor"]) # Create SensorAnnotation and add gain setting and overload indicators sensor_annotation_md = { "ntia-core:annotation_type": "SensorAnnotation", "ntia-sensor:overload_sensor": sensor_overload, "ntia-sensor:overload_sigan": sigan_overload, "ntia-sensor:gain_setting_sigan": self.measurement_params.gain, } location = get_location() if location: sensor_annotation_md["core:latitude"] = (location.latitude, ) sensor_annotation_md["core:longitude"] = location.longitude sigmf_md.add_annotation( start_index=0, length=self.measurement_params.fft_size * len(M4sDetector), metadata=sensor_annotation_md, ) return sigmf_md