def filter_range(self, start: int, end: int, fir_filter: Filter): self.iq_array[start:end] = fir_filter.work(self.iq_array[start:end]) self._qad[start:end] = signal_functions.afp_demod( self.iq_array[start:end], self.noise_threshold, self.modulation_type, self.modulation_order, self.costas_loop_bandwidth) self.__invalidate_after_edit()
def test_plot(self): modulator = Modulator("gfsk") modulator.modulation_type = "GFSK" modulator.samples_per_symbol = 100 modulator.sample_rate = 1e6 modulator.parameters[1] = 20e3 modulator.parameters[0] = 10e3 modulator.carrier_freq_hz = 15e3 modulator.carrier_phase_deg = 90 modulated_samples = modulator.modulate([True, False, True, False, False], 77) data = copy.deepcopy(modulated_samples) modulated_samples = modulator.modulate([False, True, True, True, True, False, True], 100, start=len(data)) data = np.concatenate((data, modulated_samples)) plt.subplot(2, 1, 1) axes = plt.gca() axes.set_ylim([-2,2]) plt.plot(data.real) plt.title("Modulated Wave") plt.subplot(2, 1, 2) qad = signal_functions.afp_demod(np.ascontiguousarray(data), 0, "FSK") plt.plot(qad) plt.title("Quad Demod") plt.show()
def test_noisy_rect(self): data = Signal(get_path_for_data_file("fsk.complex")).iq_array.data rect = afp_demod(data, 0.008, "FSK", 2)[5:15000] center = detect_center(rect) self.assertGreaterEqual(center, -0.0587) self.assertLessEqual(center, 0.02)
def test_ask_center_detection(self): data = Signal(get_path_for_data_file("ask.complex")).iq_array.data rect = afp_demod(data, 0.01111, "ASK", 2) center = detect_center(rect) self.assertGreaterEqual(center, 0) self.assertLessEqual(center, 0.06)
def test_fsk_15db_center_detection(self): data = Signal(get_path_for_data_file("FSK15.complex"), "").iq_array.data rect = afp_demod(data, 0, "FSK", 2) center = detect_center(rect) self.assertGreaterEqual(center, -0.1979) self.assertLessEqual(center, 0.1131)
def test_plot(self): modulator = Modulator("gfsk") modulator.modulation_type_str = "GFSK" modulator.samples_per_bit = 100 modulator.sample_rate = 1e6 modulator.param_for_one = 20e3 modulator.param_for_zero = 10e3 modulator.carrier_freq_hz = 15e3 modulator.carrier_phase_deg = 90 modulated_samples = modulator.modulate([True, False, True, False, False], 77) data = copy.deepcopy(modulated_samples) modulated_samples = modulator.modulate([False, True, True, True, True, False, True], 100, start=len(data)) data = np.concatenate((data, modulated_samples)) plt.subplot(2, 1, 1) axes = plt.gca() axes.set_ylim([-2,2]) plt.plot(data.real) plt.title("Modulated Wave") plt.subplot(2, 1, 2) qad = signal_functions.afp_demod(np.ascontiguousarray(data), 0, 1) plt.plot(qad) plt.title("Quad Demod") plt.show()
def test_ask_center_detection(self): data = np.fromfile(get_path_for_data_file("ask.complex"), dtype=np.complex64) rect = afp_demod(data, 0.01111, 0) center = detect_center(rect) self.assertGreaterEqual(center, 0) self.assertLessEqual(center, 0.06)
def test_noisy_rect(self): data = np.fromfile(get_path_for_data_file("fsk.complex"), dtype=np.complex64) rect = afp_demod(data, 0.008, 1)[5:15000] center = detect_center(rect) self.assertGreaterEqual(center, -0.0587) self.assertLessEqual(center, 0.02)
def test_fsk_live_capture(self): data = Signal(get_path_for_data_file("fsk_live.coco"), "").data n = 10 moving_average_filter = Filter([1 / n for _ in range(n)], filter_type=FilterType.moving_average) filtered_data = moving_average_filter.apply_fir_filter(data) rect = afp_demod(filtered_data, 0.0175, 1) center = detect_center(rect) self.assertGreaterEqual(center, -0.0148, msg="Filtered") self.assertLessEqual(center, 0.01, msg="Filtered") rect = afp_demod(data, 0.0175, 1) center = detect_center(rect) self.assertGreaterEqual(center, -0.02, msg="Original") self.assertLessEqual(center, 0.01, msg="Original")
def test_enocean_center_detection(self): data = Signal(get_path_for_data_file("enocean.complex")).iq_array.data rect = afp_demod(data, 0.05, "ASK", 2) messages = [rect[2107:5432], rect[20428:23758], rect[44216:47546]] for i, msg in enumerate(messages): center = detect_center(msg) self.assertGreaterEqual(center, 0.04, msg=str(i)) self.assertLessEqual(center, 0.072, msg=str(i))
def quad_demod(self): if self.noise_threshold < self.max_magnitude: return signal_functions.afp_demod(self.iq_array.data, self.noise_threshold, self.modulation_type, self.modulation_order, self.costas_loop_bandwidth) else: return np.zeros(2, dtype=np.float32)
def test_noised_homematic_center_detection(self): data = Signal(get_path_for_data_file("noised_homematic.complex"), "").iq_array.data rect = afp_demod(data, 0.0, "FSK", 2) center = detect_center(rect) self.assertGreater(center, -0.0148) self.assertLess(center, 0.0024)
def test_enocean_center_detection(self): data = np.fromfile(get_path_for_data_file("enocean.complex"), dtype=np.complex64) rect = afp_demod(data, 0.05, 0) messages = [rect[2107:5432], rect[20428:23758], rect[44216:47546]] for i, msg in enumerate(messages): center = detect_center(msg) self.assertGreaterEqual(center, 0.04, msg=str(i)) self.assertLessEqual(center, 0.072, msg=str(i))
def test_fsk_live_capture(self): data = Signal(get_path_for_data_file("fsk_live.coco"), "").iq_array.data n = 10 moving_average_filter = Filter([1 / n for _ in range(n)], filter_type=FilterType.moving_average) filtered_data = moving_average_filter.apply_fir_filter( data.flatten()).view(np.float32) filtered_data = filtered_data.reshape((len(filtered_data) // 2, 2)) rect = afp_demod(filtered_data, 0.0175, "FSK", 2) center = detect_center(rect) self.assertGreaterEqual(center, -0.0148, msg="Filtered") self.assertLessEqual(center, 0.01, msg="Filtered") rect = afp_demod(data, 0.0175, "FSK", 2) center = detect_center(rect) self.assertGreaterEqual(center, -0.02, msg="Original") self.assertLessEqual(center, 0.01, msg="Original")
def test_ask_50_center_detection(self): message_indices = [(0, 8000), (18000, 26000), (36000, 44000), (54000, 62000), (72000, 80000)] data = Signal(get_path_for_data_file("ask50.complex")).iq_array.data rect = afp_demod(data, 0.0509, "ASK", 2) for start, end in message_indices: center = detect_center(rect[start:end]) self.assertGreaterEqual(center, 0.4, msg="{}/{}".format(start, end)) self.assertLessEqual(center, 0.65, msg="{}/{}".format(start, end))
def test_homematic_center_detection(self): data = Signal(get_path_for_data_file("homematic.coco"), "").data rect = afp_demod(data, 0.0012, 1) msg1 = rect[17719:37861] msg2 = rect[70412:99385] center1 = detect_center(msg1) self.assertGreaterEqual(center1, -0.1285) self.assertLessEqual(center1, -0.0413) center2 = detect_center(msg2) self.assertGreaterEqual(center2, -0.1377) self.assertLessEqual(center2, -0.0367)
def test_ask_50_center_detection(self): message_indices = [(0, 8000), (18000, 26000), (36000, 44000), (54000, 62000), (72000, 80000)] data = np.fromfile(get_path_for_data_file("ask50.complex"), dtype=np.complex64) rect = afp_demod(data, 0.0509, 0) for start, end in message_indices: center = detect_center(rect[start:end]) self.assertGreaterEqual(center, 0.5326, msg="{}/{}".format(start, end)) self.assertLessEqual(center, 0.9482, msg="{}/{}".format(start, end))
def filter_range(self, start: int, end: int, fir_filter: Filter): self._fulldata[start:end] = fir_filter.work(self._fulldata[start:end]) self._qad[start:end] = signal_functions.afp_demod(self.data[start:end], self.noise_threshold, self.modulation_type) self.__invalidate_after_edit()
def test_fsk_10db_center_detection(self): data = Signal(get_path_for_data_file("FSK10.complex"), "").data rect = afp_demod(data, 0, 1) center = detect_center(rect) self.assertGreaterEqual(center, -0.1413) self.assertLessEqual(center, 0.05)
def quad_demod(self): return signal_functions.afp_demod(self.iq_array.data, self.noise_threshold, self.modulation_type)
def quad_demod(self): return signal_functions.afp_demod(self.data, self.noise_threshold, self.modulation_type)
def quad_demod(self): return signal_functions.afp_demod(self.iq_array.data, self.noise_threshold, self.modulation_type, self.modulation_order, self.costas_loop_bandwidth)
def filter_range(self, start: int, end: int, fir_filter: Filter): self._fulldata[start:end] = fir_filter.work(self._fulldata[start:end]) self._qad[start:end] = signal_functions.afp_demod( self.data[start:end], self.noise_threshold, self.modulation_type) self.__invalidate_after_edit()
def estimate(iq_array: IQArray, noise: float = None, modulation: str = None) -> dict: if isinstance(iq_array, np.ndarray): iq_array = IQArray(iq_array) magnitudes = iq_array.magnitudes # find noise threshold noise = detect_noise_level(magnitudes) if noise is None else noise # segment messages message_indices = segment_messages_from_magnitudes(magnitudes, noise_threshold=noise) # detect modulation modulation = detect_modulation_for_messages( iq_array, message_indices) if modulation is None else modulation if modulation is None: return None if modulation == "OOK": message_indices = merge_message_segments_for_ook(message_indices) if modulation == "OOK" or modulation == "ASK": data = signal_functions.afp_demod(iq_array.data, noise, 0) elif modulation == "FSK": data = signal_functions.afp_demod(iq_array.data, noise, 1) elif modulation == "PSK": data = signal_functions.afp_demod(iq_array.data, noise, 2) else: raise ValueError("Unsupported Modulation") centers = [] bit_lengths = [] tolerances = [] for start, end in message_indices: msg_rect_data = data[start:end] center = detect_center(msg_rect_data) if center is None: continue plateau_lengths = c_auto_interpretation.get_plateau_lengths( msg_rect_data, center, percentage=25) tolerance = estimate_tolerance_from_plateau_lengths(plateau_lengths) if tolerance is None: tolerance = 0 else: tolerances.append(tolerance) merged_lengths = merge_plateau_lengths(plateau_lengths, tolerance=tolerance) if len(merged_lengths) < 2: continue bit_length = get_bit_length_from_plateau_lengths(merged_lengths) min_bit_length = tolerance + 1 if bit_length > min_bit_length: # only add to score if found bit length surpasses minimum bit length centers.append(center) bit_lengths.append(bit_length) # Since we cannot have different centers per message (yet) we need to combine them to return a common center if modulation == "OOK" or modulation == "ASK": # for ask modulations the center tends to be the minimum of all found centers center = min_without_outliers(np.array(centers), z=2) if center is None: # did not find any centers at all so we cannot return a valid estimation return None elif len(centers) > 0: # for other modulations it is a better strategy to take the mean of found centers center = np.mean(centers) else: # did not find any centers at all so we cannot return a valid estimation return None bit_length = get_most_frequent_value(bit_lengths) if bit_length is None: return None try: tolerance = np.percentile(tolerances, 50) except IndexError: # no tolerances found, default to 5% of bit length tolerance = max(1, int(0.05 * bit_length)) result = { "modulation_type": "ASK" if modulation == "OOK" else modulation, "bit_length": bit_length, "center": center, "tolerance": int(tolerance), "noise": noise } return result
def estimate(signal: np.ndarray, noise: float = None, modulation: str = None) -> dict: magnitudes = np.abs(signal) # find noise threshold noise = detect_noise_level(magnitudes) if noise is None else noise # segment messages message_indices = segment_messages_from_magnitudes(magnitudes, noise_threshold=noise) # detect modulation modulation = detect_modulation_for_messages(signal, message_indices) if modulation is None else modulation if modulation is None: return None if modulation == "OOK": message_indices = merge_message_segments_for_ook(message_indices) if modulation == "OOK" or modulation == "ASK": data = signal_functions.afp_demod(signal, noise, 0) elif modulation == "FSK": data = signal_functions.afp_demod(signal, noise, 1) elif modulation == "PSK": data = signal_functions.afp_demod(signal, noise, 2) else: raise ValueError("Unsupported Modulation") centers = [] bit_lengths = [] tolerances = [] for start, end in message_indices: msg_rect_data = data[start:end] center = detect_center(msg_rect_data) if center is None: continue plateau_lengths = c_auto_interpretation.get_plateau_lengths(msg_rect_data, center, percentage=25) tolerance = estimate_tolerance_from_plateau_lengths(plateau_lengths) if tolerance is None: tolerance = 0 else: tolerances.append(tolerance) merged_lengths = merge_plateau_lengths(plateau_lengths, tolerance=tolerance) if len(merged_lengths) < 2: continue bit_length = get_bit_length_from_plateau_lengths(merged_lengths) min_bit_length = tolerance + 1 if bit_length > min_bit_length: # only add to score if found bit length surpasses minimum bit length centers.append(center) bit_lengths.append(bit_length) # Since we cannot have different centers per message (yet) we need to combine them to return a common center if modulation == "OOK" or modulation == "ASK": # for ask modulations the center tends to be the minimum of all found centers center = min_without_outliers(np.array(centers), z=2) if center is None: # did not find any centers at all so we cannot return a valid estimation return None elif len(centers) > 0: # for other modulations it is a better strategy to take the mean of found centers center = np.mean(centers) else: # did not find any centers at all so we cannot return a valid estimation return None bit_length = get_most_frequent_value(bit_lengths) if bit_length is None: return None try: tolerance = np.percentile(tolerances, 50) except IndexError: # no tolerances found, default to 5% of bit length tolerance = max(1, int(0.05 * bit_length)) result = { "modulation_type": "ASK" if modulation == "OOK" else modulation, "bit_length": bit_length, "center": center, "tolerance": int(tolerance), "noise": noise } return result