From 958c9920a5f05c97952f72c93b2e9dca231cfb8b Mon Sep 17 00:00:00 2001 From: Nao Pross Date: Sat, 27 Nov 2021 19:57:03 +0100 Subject: Start access code cross correlation --- tests/correlator/acgen.dat | Bin 5120 -> 3840 bytes tests/correlator/acgen.grc | 24 +- tests/correlator/acgen.py | 25 +- tests/correlator/acproc.py | 48 ++- tests/correlator/correlator.grc | 816 +++++++++++++++++++++++++++++++++++++--- tests/correlator/correlator.py | 244 +++++++++--- 6 files changed, 1007 insertions(+), 150 deletions(-) diff --git a/tests/correlator/acgen.dat b/tests/correlator/acgen.dat index 537dcc5..a13db8e 100644 Binary files a/tests/correlator/acgen.dat and b/tests/correlator/acgen.dat differ diff --git a/tests/correlator/acgen.grc b/tests/correlator/acgen.grc index ec7c1e8..abdc3bc 100644 --- a/tests/correlator/acgen.grc +++ b/tests/correlator/acgen.grc @@ -1,7 +1,6 @@ options: parameters: author: Naoki Pross - catch_exceptions: 'True' category: '[GRC Hier Blocks]' cmake_opt: '' comment: '' @@ -23,6 +22,7 @@ options: sizing_mode: fixed thread_safe_setters: '' title: Access Code Symbols Generator + window_size: '' states: bus_sink: false bus_source: false @@ -36,7 +36,7 @@ blocks: id: variable parameters: comment: '' - value: '[ 0xaa, 0xff, 0x0a ]' + value: '[ 0xaa, 0xff, 0xff ]' states: bus_sink: false bus_source: false @@ -50,7 +50,6 @@ blocks: comment: '' const_points: '[-1-1j, -1+1j, 1+1j, 1-1j]' dims: '1' - normalization: digital.constellation.AMPLITUDE_NORMALIZATION precision: '8' rot_sym: '4' soft_dec_lut: None @@ -75,6 +74,18 @@ blocks: coordinate: [464, 308.0] rotation: 0 state: true +- name: padding_zeros + id: variable + parameters: + comment: '' + value: '10' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [40, 364.0] + rotation: 0 + state: true - name: samp_rate id: variable parameters: @@ -84,7 +95,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [200, 44.0] + coordinate: [208, 12.0] rotation: 0 state: enabled - name: sps @@ -96,7 +107,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [312, 44.0] + coordinate: [304, 12.0] rotation: 0 state: true - name: blocks_file_sink_0 @@ -147,7 +158,7 @@ blocks: repeat: 'False' tags: '[]' type: byte - vector: '[0x00] * 10 + access_code * 10' + vector: '[0x00] * padding_zeros + access_code * 10' vlen: '1' states: bus_sink: false @@ -169,7 +180,6 @@ blocks: maxoutbuf: '0' minoutbuf: '0' samples_per_symbol: sps - truncate: 'False' verbose: 'False' states: bus_sink: false diff --git a/tests/correlator/acgen.py b/tests/correlator/acgen.py index 5fbdbb4..4d18b92 100755 --- a/tests/correlator/acgen.py +++ b/tests/correlator/acgen.py @@ -7,13 +7,12 @@ # GNU Radio Python Flow Graph # Title: Access Code Symbols Generator # Author: Naoki Pross -# GNU Radio version: 3.9.2.0 +# GNU Radio version: 3.8.2.0 from gnuradio import blocks from gnuradio import digital from gnuradio import gr from gnuradio.filter import firdes -from gnuradio.fft import window import sys import signal from argparse import ArgumentParser @@ -21,21 +20,20 @@ from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation - - class acgen(gr.top_block): def __init__(self): - gr.top_block.__init__(self, "Access Code Symbols Generator", catch_exceptions=True) + gr.top_block.__init__(self, "Access Code Symbols Generator") ################################################## # Variables ################################################## self.sps = sps = 4 self.samp_rate = samp_rate = 32000 + self.padding_zeros = padding_zeros = 10 self.excess_bw = excess_bw = 1 self.const = const = digital.constellation_qpsk().base() - self.access_code = access_code = [ 0xaa, 0xff, 0x0a ] + self.access_code = access_code = [ 0xaa, 0xff, 0xff ] ################################################## # Blocks @@ -47,9 +45,8 @@ class acgen(gr.top_block): pre_diff_code=True, excess_bw=excess_bw, verbose=False, - log=False, - truncate=False) - self.blocks_vector_source_x_0 = blocks.vector_source_b([0x00] * 10 + access_code * 10, False, 1, []) + log=False) + self.blocks_vector_source_x_0 = blocks.vector_source_b([0x00] * padding_zeros + access_code * 10, False, 1, []) self.blocks_throttle_0 = blocks.throttle(gr.sizeof_char*1, samp_rate,True) self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_gr_complex*1, 'acgen.dat', False) self.blocks_file_sink_0.set_unbuffered(False) @@ -77,6 +74,13 @@ class acgen(gr.top_block): self.samp_rate = samp_rate self.blocks_throttle_0.set_sample_rate(self.samp_rate) + def get_padding_zeros(self): + return self.padding_zeros + + def set_padding_zeros(self, padding_zeros): + self.padding_zeros = padding_zeros + self.blocks_vector_source_x_0.set_data([0x00] * self.padding_zeros + self.access_code * 10, []) + def get_excess_bw(self): return self.excess_bw @@ -94,7 +98,8 @@ class acgen(gr.top_block): def set_access_code(self, access_code): self.access_code = access_code - self.blocks_vector_source_x_0.set_data([0x00] * 10 + self.access_code * 10, []) + self.blocks_vector_source_x_0.set_data([0x00] * self.padding_zeros + self.access_code * 10, []) + diff --git a/tests/correlator/acproc.py b/tests/correlator/acproc.py index e119520..50c9a38 100755 --- a/tests/correlator/acproc.py +++ b/tests/correlator/acproc.py @@ -2,19 +2,21 @@ import numpy as np import matplotlib.pyplot as plt -import acgen +from acgen import acgen -# Parameters -# samples per symbol -sps = 4 -# number of initial bytes (to ignore) -nzeros = 10 -# length of the access code in bytes -aclen = 2 +# parameters +access_code_bits = [ 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, ] +access_code = list(np.packbits([0] * 3 + access_code_bits)) +padding_zeros = 10 # Create samples -print("Modulating symbols") -acgen.main() +print(f"Modulating symbols for access code {access_code_bits} = {access_code} with after {padding_zeros} empty bytes") +gen = acgen() +gen.set_access_code(access_code) +gen.set_padding_zeros(padding_zeros) + +gen.start() +gen.wait() # Extract one sequence print("Extracting symbol sequence") @@ -27,17 +29,24 @@ plt.title("Raw Data (time domain)") plt.show() # take only symbols -symbols = data[1::sps] +symbols = data[1::gen.sps] + plt.plot(symbols.real, symbols.imag) plt.title("Symbols only (constellation)") plt.show() # where ac symbols start, in symbols -ac_start = nzeros * 8 -ac_end = ac_start + aclen * 8 +ac_start = (padding_zeros) * 8 + 3 # plus three because code is 13 bits +ac_end = ac_start + int(np.ceil(len(access_code_bits) / 2.)) # divided by two because QPSK ac = symbols[ac_start:ac_end] +print(f"Generated {len(ac)} (left padded) symbols from a {len(access_code_bits)} bit sequence") +print(list(ac)) + +print(f"Upsampled to {gen.sps} samples per symbos") +print(sum([[i, i, i, i] for i in ac], [])) + fig, (ax1, ax2) = plt.subplots(2, 1) fig.tight_layout() @@ -51,7 +60,14 @@ plt.show() fir = list(np.conj(ac[::-1])) -# print the symbols -print(f"Generated {len(ac)} symbols from a {aclen} byte sequence") -print("Reversed symbols (for FIR filter):") +# Print the symbols +print("Reversed complex conjugate symbols (for FIR filter):") print(fir) + +# Compute cross correlation + +xc = np.convolve(fir, ac) + +plt.plot(np.abs(xc)) +plt.title("Cross correlation (FIR)") +plt.show() diff --git a/tests/correlator/correlator.grc b/tests/correlator/correlator.grc index cc57e18..723a99d 100644 --- a/tests/correlator/correlator.grc +++ b/tests/correlator/correlator.grc @@ -1,7 +1,6 @@ options: parameters: author: Naoki Pross - catch_exceptions: 'True' category: '[GRC Hier Blocks]' cmake_opt: '' comment: '' @@ -23,6 +22,7 @@ options: sizing_mode: fixed thread_safe_setters: '' title: Correlator Test + window_size: '' states: bus_sink: false bus_source: false @@ -32,13 +32,31 @@ options: state: enabled blocks: +- name: access_code_symbols_sps + id: variable + parameters: + comment: '' + value: '[(1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), + (1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), + (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), + (1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), + (-1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), + (1.4142197-1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), + (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), + (1.4142197+1.4142197j)]' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [224, 132.0] + rotation: 0 + state: true - name: const id: variable_constellation parameters: comment: '' const_points: '[-1-1j, -1+1j, 1+1j, 1-1j]' dims: '1' - normalization: digital.constellation.AMPLITUDE_NORMALIZATION precision: '8' rot_sym: '4' soft_dec_lut: None @@ -48,9 +66,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [432, 164.0] + coordinate: [592, 484.0] rotation: 0 - state: true + state: enabled - name: excess_bw id: variable parameters: @@ -60,9 +78,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [432, 340.0] + coordinate: [496, 484.0] rotation: 0 - state: true + state: enabled - name: nfilts id: variable parameters: @@ -72,9 +90,22 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [696, 356.0] + coordinate: [224, 988.0] rotation: 0 - state: true + state: enabled +- name: revconj_access_code_symbols + id: variable + parameters: + comment: '' + value: '[(1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), + (-1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197-1.4142197j)]' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [992, 620.0] + rotation: 0 + state: enabled - name: rrc_taps id: variable parameters: @@ -84,9 +115,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [768, 356.0] + coordinate: [304, 988.0] rotation: 0 - state: true + state: enabled - name: samp_rate id: variable parameters: @@ -96,7 +127,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [256, 52.0] + coordinate: [8, 116.0] rotation: 0 state: enabled - name: sps @@ -108,9 +139,21 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [352, 52.0] + coordinate: [104, 116.0] rotation: 0 - state: true + state: enabled +- name: testvec + id: variable + parameters: + comment: '' + value: '[31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23]' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [48, 492.0] + rotation: 0 + state: enabled - name: timing_loop_bw id: variable parameters: @@ -120,11 +163,11 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [696, 420.0] + coordinate: [224, 1068.0] rotation: 0 - state: true -- name: blocks_complex_to_mag_0 - id: blocks_complex_to_mag + state: enabled +- name: blocks_complex_to_magphase_0 + id: blocks_complex_to_magphase parameters: affinity: '' alias: '' @@ -136,9 +179,43 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1584, 80.0] + coordinate: [1008, 696.0] rotation: 0 - state: true + state: enabled +- name: blocks_complex_to_magphase_0_0 + id: blocks_complex_to_magphase + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1072, 1112.0] + rotation: 0 + state: disabled +- name: blocks_multiply_const_vxx_0 + id: blocks_multiply_const_vxx + parameters: + affinity: '' + alias: '' + comment: '' + const: 180 / 3.141592653589793 + maxoutbuf: '0' + minoutbuf: '0' + type: float + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1280, 788.0] + rotation: 0 + state: enabled - name: blocks_null_sink_0 id: blocks_null_sink parameters: @@ -153,9 +230,117 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1528, 216.0] + coordinate: [1056, 840.0] rotation: 0 - state: true + state: enabled +- name: blocks_null_sink_0_0 + id: blocks_null_sink + parameters: + affinity: '' + alias: '' + bus_structure_sink: '[[0,],]' + comment: '' + num_inputs: '1' + type: complex + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1080, 1048.0] + rotation: 0 + state: disabled +- name: blocks_null_sink_1_0 + id: blocks_null_sink + parameters: + affinity: '' + alias: '' + bus_structure_sink: '[[0,],]' + comment: '' + num_inputs: '1' + type: float + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1336, 1176.0] + rotation: 0 + state: disabled +- name: blocks_null_sink_1_0_0 + id: blocks_null_sink + parameters: + affinity: '' + alias: '' + bus_structure_sink: '[[0,],]' + comment: '' + num_inputs: '1' + type: float + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1288, 856.0] + rotation: 0 + state: disabled +- name: blocks_null_source_0 + id: blocks_null_source + parameters: + affinity: '' + alias: '' + bus_structure_source: '[[0,],]' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + num_outputs: '1' + type: byte + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [96, 344.0] + rotation: 0 + state: enabled +- name: blocks_stream_mux_0 + id: blocks_stream_mux + parameters: + affinity: '' + alias: '' + comment: '' + lengths: '[15, len(testvec)]' + maxoutbuf: '0' + minoutbuf: '0' + num_inputs: '2' + type: byte + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [288, 392.0] + rotation: 0 + state: enabled +- name: blocks_stream_mux_1 + id: blocks_stream_mux + parameters: + affinity: '' + alias: '' + comment: '' + lengths: '[len(access_code_symbols_sps), sps * (len(testvec) + 15)]' + maxoutbuf: '0' + minoutbuf: '0' + num_inputs: '2' + type: complex + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [776, 280.0] + rotation: 0 + state: disabled - name: blocks_throttle_0 id: blocks_throttle parameters: @@ -166,15 +351,15 @@ blocks: maxoutbuf: '0' minoutbuf: '0' samples_per_second: samp_rate - type: byte + type: complex vlen: '1' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [248, 260.0] + coordinate: [1272, 404.0] rotation: 0 - state: true + state: enabled - name: blocks_vector_source_x_0 id: blocks_vector_source_x parameters: @@ -183,18 +368,59 @@ blocks: comment: '' maxoutbuf: '0' minoutbuf: '0' - repeat: 'False' + repeat: 'True' tags: '[]' type: byte - vector: ([0x00] * 10 + [0xaa, 0xff, 0x0a] + [0x00] * 10) * 20 + vector: testvec vlen: '1' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [16, 244.0] + coordinate: [48, 404.0] rotation: 0 - state: true + state: enabled +- name: blocks_vector_source_x_1 + id: blocks_vector_source_x + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + repeat: 'True' + tags: '[]' + type: complex + vector: access_code_symbols_sps + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [224, 204.0] + rotation: 0 + state: disabled +- name: channels_channel_model_0 + id: channels_channel_model + parameters: + affinity: '' + alias: '' + block_tags: 'False' + comment: '' + epsilon: '1.0' + freq_offset: '0.0' + maxoutbuf: '0' + minoutbuf: '0' + noise_voltage: '0.1' + seed: '243' + taps: '1. ' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [992, 364.0] + rotation: 0 + state: enabled - name: digital_cma_equalizer_cc_0 id: digital_cma_equalizer_cc parameters: @@ -211,7 +437,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [992, 188.0] + coordinate: [528, 812.0] rotation: 0 state: enabled - name: digital_constellation_decoder_cb_0 @@ -227,9 +453,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1264, 212.0] + coordinate: [800, 836.0] rotation: 0 - state: true + state: enabled - name: digital_constellation_modulator_0 id: digital_constellation_modulator parameters: @@ -243,15 +469,35 @@ blocks: maxoutbuf: '0' minoutbuf: '0' samples_per_symbol: sps - truncate: 'False' verbose: 'False' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [432, 236.0] + coordinate: [496, 380.0] rotation: 0 - state: true + state: enabled +- name: digital_corr_est_cc_0 + id: digital_corr_est_cc + parameters: + affinity: '' + alias: '' + comment: '' + mark_delay: '0' + maxoutbuf: '0' + minoutbuf: '0' + sps: '1' + symbols: '[(1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), + (-1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j)]' + threshold: '.9' + threshold_method: digital.THRESHOLD_DYNAMIC + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [768, 1076.0] + rotation: 0 + state: disabled - name: digital_pfb_clock_sync_xxx_0 id: digital_pfb_clock_sync_xxx parameters: @@ -272,9 +518,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [696, 212.0] + coordinate: [224, 836.0] rotation: 0 - state: true + state: enabled - name: fir_filter_xxx_1 id: fir_filter_xxx parameters: @@ -285,17 +531,105 @@ blocks: maxoutbuf: '0' minoutbuf: '0' samp_delay: '0' - taps: '[(-1.4142197+1.4142197j), (-1.4142197+1.4142197j), (1.4142197-1.4142197j), - (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (-1.4142197-1.4142197j), - (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), - (-1.4142197-1.4142197j), (-1.4142197+1.4142197j), (-1.4142197+1.4142197j), (1.4142197-1.4142197j), - (1.4142197-1.4142197j)]' + taps: revconj_access_code_symbols type: ccc states: bus_sink: false bus_source: false bus_structure: null - coordinate: [1328, 68.0] + coordinate: [768, 700.0] + rotation: 0 + state: enabled +- name: qtgui_const_sink_x_0 + id: qtgui_const_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'False' + axislabels: 'True' + color1: '"blue"' + color10: '"red"' + color2: '"red"' + color3: '"red"' + color4: '"red"' + color5: '"red"' + color6: '"red"' + color7: '"red"' + color8: '"red"' + color9: '"red"' + comment: '' + grid: 'False' + gui_hint: '' + label1: '' + label10: '' + label2: '' + label3: '' + label4: '' + label5: '' + label6: '' + label7: '' + label8: '' + label9: '' + legend: 'True' + marker1: '0' + marker10: '0' + marker2: '0' + marker3: '0' + marker4: '0' + marker5: '0' + marker6: '0' + marker7: '0' + marker8: '0' + marker9: '0' + name: '""' + nconnections: '1' + size: '1024' + style1: '0' + style10: '0' + style2: '0' + style3: '0' + style4: '0' + style5: '0' + style6: '0' + style7: '0' + style8: '0' + style9: '0' + tr_chan: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: complex + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + xmax: '2' + xmin: '-2' + ymax: '2' + ymin: '-2' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [768, 620.0] rotation: 0 state: enabled - name: qtgui_time_sink_x_0 @@ -313,7 +647,7 @@ blocks: alpha7: '1.0' alpha8: '1.0' alpha9: '1.0' - autoscale: 'False' + autoscale: 'True' axislabels: 'True' color1: blue color10: dark blue @@ -326,10 +660,10 @@ blocks: color8: dark red color9: dark green comment: '' - ctrlpanel: 'True' + ctrlpanel: 'False' entags: 'True' grid: 'False' - gui_hint: '' + gui_hint: 2,0,1,1 label1: Signal 1 label10: Signal 10 label2: Signal 2 @@ -384,18 +718,18 @@ blocks: width7: '1' width8: '1' width9: '1' - ylabel: Amplitude - ymax: '50' + ylabel: XC Magnitude + ymax: '100' ymin: '0' yunit: '""' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [1792, 60.0] + coordinate: [1280, 676.0] rotation: 0 - state: true -- name: qtgui_time_sink_x_1 + state: enabled +- name: qtgui_time_sink_x_0_0 id: qtgui_time_sink_x parameters: affinity: '' @@ -410,7 +744,7 @@ blocks: alpha7: '1.0' alpha8: '1.0' alpha9: '1.0' - autoscale: 'False' + autoscale: 'True' axislabels: 'True' color1: blue color10: dark blue @@ -423,10 +757,107 @@ blocks: color8: dark red color9: dark green comment: '' - ctrlpanel: 'True' + ctrlpanel: 'False' entags: 'True' grid: 'False' - gui_hint: '' + gui_hint: 2,0,1,1 + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: float + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: Cross Correlation Magnitude + ymax: '100' + ymin: '0' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1336, 1092.0] + rotation: 0 + state: disabled +- name: qtgui_time_sink_x_1_0 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: 0,0,1,1 label1: Signal 1 label10: Signal 10 label2: Signal 2 @@ -481,7 +912,7 @@ blocks: width7: '1' width8: '1' width9: '1' - ylabel: Amplitude + ylabel: Modulated ymax: '2' ymin: '-2' yunit: '""' @@ -489,21 +920,282 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [696, 108.0] + coordinate: [992, 196.0] rotation: 0 - state: true + state: disabled +- name: qtgui_time_sink_x_1_1 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: 1,0,1,1 + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: complex + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: Equalized + ymax: '2' + ymin: '-2' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [800, 932.0] + rotation: 0 + state: enabled +- name: qtgui_time_sink_x_2 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: 3,0,1,1 + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: float + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: XC Phase + ymax: '1' + ymin: '-1' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1456, 772.0] + rotation: 0 + state: enabled +- name: root_raised_cosine_filter_0 + id: root_raised_cosine_filter + parameters: + affinity: '' + alias: '' + alpha: excess_bw + comment: '' + decim: '1' + gain: '2' + interp: '1' + maxoutbuf: '0' + minoutbuf: '0' + ntaps: 11*samp_rate + samp_rate: samp_rate + sym_rate: '1' + type: fir_filter_ccf + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [496, 180.0] + rotation: 0 + state: disabled +- name: virtual_sink_0 + id: virtual_sink + parameters: + alias: '' + comment: '' + stream_id: envelope + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1480, 404.0] + rotation: 0 + state: enabled +- name: virtual_source_0 + id: virtual_source + parameters: + alias: '' + comment: '' + stream_id: envelope + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [8, 884.0] + rotation: 0 + state: enabled connections: -- [blocks_complex_to_mag_0, '0', qtgui_time_sink_x_0, '0'] -- [blocks_throttle_0, '0', digital_constellation_modulator_0, '0'] -- [blocks_vector_source_x_0, '0', blocks_throttle_0, '0'] +- [blocks_complex_to_magphase_0, '0', qtgui_time_sink_x_0, '0'] +- [blocks_complex_to_magphase_0, '1', blocks_multiply_const_vxx_0, '0'] +- [blocks_complex_to_magphase_0, '1', blocks_null_sink_1_0_0, '0'] +- [blocks_complex_to_magphase_0_0, '0', qtgui_time_sink_x_0_0, '0'] +- [blocks_complex_to_magphase_0_0, '1', blocks_null_sink_1_0, '0'] +- [blocks_multiply_const_vxx_0, '0', qtgui_time_sink_x_2, '0'] +- [blocks_null_source_0, '0', blocks_stream_mux_0, '0'] +- [blocks_stream_mux_0, '0', digital_constellation_modulator_0, '0'] +- [blocks_stream_mux_1, '0', channels_channel_model_0, '0'] +- [blocks_stream_mux_1, '0', qtgui_time_sink_x_1_0, '0'] +- [blocks_throttle_0, '0', virtual_sink_0, '0'] +- [blocks_vector_source_x_0, '0', blocks_stream_mux_0, '1'] +- [blocks_vector_source_x_1, '0', root_raised_cosine_filter_0, '0'] +- [channels_channel_model_0, '0', blocks_throttle_0, '0'] - [digital_cma_equalizer_cc_0, '0', digital_constellation_decoder_cb_0, '0'] +- [digital_cma_equalizer_cc_0, '0', digital_corr_est_cc_0, '0'] - [digital_cma_equalizer_cc_0, '0', fir_filter_xxx_1, '0'] +- [digital_cma_equalizer_cc_0, '0', qtgui_const_sink_x_0, '0'] +- [digital_cma_equalizer_cc_0, '0', qtgui_time_sink_x_1_1, '0'] - [digital_constellation_decoder_cb_0, '0', blocks_null_sink_0, '0'] -- [digital_constellation_modulator_0, '0', digital_pfb_clock_sync_xxx_0, '0'] -- [digital_constellation_modulator_0, '0', qtgui_time_sink_x_1, '0'] +- [digital_constellation_modulator_0, '0', blocks_stream_mux_1, '1'] +- [digital_constellation_modulator_0, '0', channels_channel_model_0, '0'] +- [digital_corr_est_cc_0, '0', blocks_null_sink_0_0, '0'] +- [digital_corr_est_cc_0, '1', blocks_complex_to_magphase_0_0, '0'] - [digital_pfb_clock_sync_xxx_0, '0', digital_cma_equalizer_cc_0, '0'] -- [fir_filter_xxx_1, '0', blocks_complex_to_mag_0, '0'] +- [fir_filter_xxx_1, '0', blocks_complex_to_magphase_0, '0'] +- [root_raised_cosine_filter_0, '0', blocks_stream_mux_1, '0'] +- [virtual_source_0, '0', digital_pfb_clock_sync_xxx_0, '0'] metadata: file_format: 1 diff --git a/tests/correlator/correlator.py b/tests/correlator/correlator.py index 79fa3f8..15d9c9c 100755 --- a/tests/correlator/correlator.py +++ b/tests/correlator/correlator.py @@ -7,7 +7,7 @@ # GNU Radio Python Flow Graph # Title: Correlator Test # Author: Naoki Pross -# GNU Radio version: 3.9.2.0 +# GNU Radio version: 3.8.2.0 from distutils.version import StrictVersion @@ -26,24 +26,22 @@ from gnuradio import qtgui from gnuradio.filter import firdes import sip from gnuradio import blocks +from gnuradio import channels from gnuradio import digital from gnuradio import filter from gnuradio import gr -from gnuradio.fft import window import sys import signal from argparse import ArgumentParser from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation - - from gnuradio import qtgui class correlator(gr.top_block, Qt.QWidget): def __init__(self): - gr.top_block.__init__(self, "Correlator Test", catch_exceptions=True) + gr.top_block.__init__(self, "Correlator Test") Qt.QWidget.__init__(self) self.setWindowTitle("Correlator Test") qtgui.util.check_set_qss() @@ -80,32 +78,85 @@ class correlator(gr.top_block, Qt.QWidget): self.nfilts = nfilts = 32 self.excess_bw = excess_bw = .35 self.timing_loop_bw = timing_loop_bw = 2 * 3.141592653589793 / 100 + self.testvec = testvec = [31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23] self.samp_rate = samp_rate = 32000 self.rrc_taps = rrc_taps = firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), excess_bw, 45*nfilts) + self.revconj_access_code_symbols = revconj_access_code_symbols = [(1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), (-1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197-1.4142197j)] self.const = const = digital.constellation_qpsk().base() + self.access_code_symbols_sps = access_code_symbols_sps = [(1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j)] ################################################## # Blocks ################################################## - self.qtgui_time_sink_x_1 = qtgui.time_sink_c( + self.qtgui_time_sink_x_2 = qtgui.time_sink_f( + 1024, #size + samp_rate, #samp_rate + "", #name + 1 #number of inputs + ) + self.qtgui_time_sink_x_2.set_update_time(0.10) + self.qtgui_time_sink_x_2.set_y_axis(-1, 1) + + self.qtgui_time_sink_x_2.set_y_label('XC Phase', "") + + self.qtgui_time_sink_x_2.enable_tags(True) + self.qtgui_time_sink_x_2.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") + self.qtgui_time_sink_x_2.enable_autoscale(True) + self.qtgui_time_sink_x_2.enable_grid(False) + self.qtgui_time_sink_x_2.enable_axis_labels(True) + self.qtgui_time_sink_x_2.enable_control_panel(False) + self.qtgui_time_sink_x_2.enable_stem_plot(False) + + + labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5', + 'Signal 6', 'Signal 7', 'Signal 8', 'Signal 9', 'Signal 10'] + widths = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + colors = ['blue', 'red', 'green', 'black', 'cyan', + 'magenta', 'yellow', 'dark red', 'dark green', 'dark blue'] + alphas = [1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0] + styles = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + markers = [-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1] + + + for i in range(1): + if len(labels[i]) == 0: + self.qtgui_time_sink_x_2.set_line_label(i, "Data {0}".format(i)) + else: + self.qtgui_time_sink_x_2.set_line_label(i, labels[i]) + self.qtgui_time_sink_x_2.set_line_width(i, widths[i]) + self.qtgui_time_sink_x_2.set_line_color(i, colors[i]) + self.qtgui_time_sink_x_2.set_line_style(i, styles[i]) + self.qtgui_time_sink_x_2.set_line_marker(i, markers[i]) + self.qtgui_time_sink_x_2.set_line_alpha(i, alphas[i]) + + self._qtgui_time_sink_x_2_win = sip.wrapinstance(self.qtgui_time_sink_x_2.pyqwidget(), Qt.QWidget) + self.top_grid_layout.addWidget(self._qtgui_time_sink_x_2_win, 3, 0, 1, 1) + for r in range(3, 4): + self.top_grid_layout.setRowStretch(r, 1) + for c in range(0, 1): + self.top_grid_layout.setColumnStretch(c, 1) + self.qtgui_time_sink_x_1_1 = qtgui.time_sink_c( 1024, #size samp_rate, #samp_rate "", #name - 1, #number of inputs - None # parent + 1 #number of inputs ) - self.qtgui_time_sink_x_1.set_update_time(0.10) - self.qtgui_time_sink_x_1.set_y_axis(-2, 2) + self.qtgui_time_sink_x_1_1.set_update_time(0.10) + self.qtgui_time_sink_x_1_1.set_y_axis(-2, 2) - self.qtgui_time_sink_x_1.set_y_label('Amplitude', "") + self.qtgui_time_sink_x_1_1.set_y_label('Equalized', "") - self.qtgui_time_sink_x_1.enable_tags(True) - self.qtgui_time_sink_x_1.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") - self.qtgui_time_sink_x_1.enable_autoscale(False) - self.qtgui_time_sink_x_1.enable_grid(False) - self.qtgui_time_sink_x_1.enable_axis_labels(True) - self.qtgui_time_sink_x_1.enable_control_panel(True) - self.qtgui_time_sink_x_1.enable_stem_plot(False) + self.qtgui_time_sink_x_1_1.enable_tags(True) + self.qtgui_time_sink_x_1_1.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") + self.qtgui_time_sink_x_1_1.enable_autoscale(True) + self.qtgui_time_sink_x_1_1.enable_grid(False) + self.qtgui_time_sink_x_1_1.enable_axis_labels(True) + self.qtgui_time_sink_x_1_1.enable_control_panel(False) + self.qtgui_time_sink_x_1_1.enable_stem_plot(False) labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5', @@ -125,37 +176,40 @@ class correlator(gr.top_block, Qt.QWidget): for i in range(2): if len(labels[i]) == 0: if (i % 2 == 0): - self.qtgui_time_sink_x_1.set_line_label(i, "Re{{Data {0}}}".format(i/2)) + self.qtgui_time_sink_x_1_1.set_line_label(i, "Re{{Data {0}}}".format(i/2)) else: - self.qtgui_time_sink_x_1.set_line_label(i, "Im{{Data {0}}}".format(i/2)) + self.qtgui_time_sink_x_1_1.set_line_label(i, "Im{{Data {0}}}".format(i/2)) else: - self.qtgui_time_sink_x_1.set_line_label(i, labels[i]) - self.qtgui_time_sink_x_1.set_line_width(i, widths[i]) - self.qtgui_time_sink_x_1.set_line_color(i, colors[i]) - self.qtgui_time_sink_x_1.set_line_style(i, styles[i]) - self.qtgui_time_sink_x_1.set_line_marker(i, markers[i]) - self.qtgui_time_sink_x_1.set_line_alpha(i, alphas[i]) - - self._qtgui_time_sink_x_1_win = sip.wrapinstance(self.qtgui_time_sink_x_1.pyqwidget(), Qt.QWidget) - self.top_layout.addWidget(self._qtgui_time_sink_x_1_win) + self.qtgui_time_sink_x_1_1.set_line_label(i, labels[i]) + self.qtgui_time_sink_x_1_1.set_line_width(i, widths[i]) + self.qtgui_time_sink_x_1_1.set_line_color(i, colors[i]) + self.qtgui_time_sink_x_1_1.set_line_style(i, styles[i]) + self.qtgui_time_sink_x_1_1.set_line_marker(i, markers[i]) + self.qtgui_time_sink_x_1_1.set_line_alpha(i, alphas[i]) + + self._qtgui_time_sink_x_1_1_win = sip.wrapinstance(self.qtgui_time_sink_x_1_1.pyqwidget(), Qt.QWidget) + self.top_grid_layout.addWidget(self._qtgui_time_sink_x_1_1_win, 1, 0, 1, 1) + for r in range(1, 2): + self.top_grid_layout.setRowStretch(r, 1) + for c in range(0, 1): + self.top_grid_layout.setColumnStretch(c, 1) self.qtgui_time_sink_x_0 = qtgui.time_sink_f( 1024, #size samp_rate, #samp_rate "", #name - 1, #number of inputs - None # parent + 1 #number of inputs ) self.qtgui_time_sink_x_0.set_update_time(0.10) - self.qtgui_time_sink_x_0.set_y_axis(0, 50) + self.qtgui_time_sink_x_0.set_y_axis(0, 100) - self.qtgui_time_sink_x_0.set_y_label('Amplitude', "") + self.qtgui_time_sink_x_0.set_y_label('XC Magnitude', "") self.qtgui_time_sink_x_0.enable_tags(True) self.qtgui_time_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") - self.qtgui_time_sink_x_0.enable_autoscale(False) + self.qtgui_time_sink_x_0.enable_autoscale(True) self.qtgui_time_sink_x_0.enable_grid(False) self.qtgui_time_sink_x_0.enable_axis_labels(True) - self.qtgui_time_sink_x_0.enable_control_panel(True) + self.qtgui_time_sink_x_0.enable_control_panel(False) self.qtgui_time_sink_x_0.enable_stem_plot(False) @@ -185,8 +239,52 @@ class correlator(gr.top_block, Qt.QWidget): self.qtgui_time_sink_x_0.set_line_alpha(i, alphas[i]) self._qtgui_time_sink_x_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0.pyqwidget(), Qt.QWidget) - self.top_layout.addWidget(self._qtgui_time_sink_x_0_win) - self.fir_filter_xxx_1 = filter.fir_filter_ccc(1, [(-1.4142197+1.4142197j), (-1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197+1.4142197j), (-1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j)]) + self.top_grid_layout.addWidget(self._qtgui_time_sink_x_0_win, 2, 0, 1, 1) + for r in range(2, 3): + self.top_grid_layout.setRowStretch(r, 1) + for c in range(0, 1): + self.top_grid_layout.setColumnStretch(c, 1) + self.qtgui_const_sink_x_0 = qtgui.const_sink_c( + 1024, #size + "", #name + 1 #number of inputs + ) + self.qtgui_const_sink_x_0.set_update_time(0.10) + self.qtgui_const_sink_x_0.set_y_axis(-2, 2) + self.qtgui_const_sink_x_0.set_x_axis(-2, 2) + self.qtgui_const_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, "") + self.qtgui_const_sink_x_0.enable_autoscale(False) + self.qtgui_const_sink_x_0.enable_grid(False) + self.qtgui_const_sink_x_0.enable_axis_labels(True) + + + labels = ['', '', '', '', '', + '', '', '', '', ''] + widths = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + colors = ["blue", "red", "red", "red", "red", + "red", "red", "red", "red", "red"] + styles = [0, 0, 0, 0, 0, + 0, 0, 0, 0, 0] + markers = [0, 0, 0, 0, 0, + 0, 0, 0, 0, 0] + alphas = [1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0] + + for i in range(1): + if len(labels[i]) == 0: + self.qtgui_const_sink_x_0.set_line_label(i, "Data {0}".format(i)) + else: + self.qtgui_const_sink_x_0.set_line_label(i, labels[i]) + self.qtgui_const_sink_x_0.set_line_width(i, widths[i]) + self.qtgui_const_sink_x_0.set_line_color(i, colors[i]) + self.qtgui_const_sink_x_0.set_line_style(i, styles[i]) + self.qtgui_const_sink_x_0.set_line_marker(i, markers[i]) + self.qtgui_const_sink_x_0.set_line_alpha(i, alphas[i]) + + self._qtgui_const_sink_x_0_win = sip.wrapinstance(self.qtgui_const_sink_x_0.pyqwidget(), Qt.QWidget) + self.top_grid_layout.addWidget(self._qtgui_const_sink_x_0_win) + self.fir_filter_xxx_1 = filter.fir_filter_ccc(1, revconj_access_code_symbols) self.fir_filter_xxx_1.declare_sample_delay(0) self.digital_pfb_clock_sync_xxx_0 = digital.pfb_clock_sync_ccf(sps, timing_loop_bw, rrc_taps, nfilts, 16, 1.5, 1) self.digital_constellation_modulator_0 = digital.generic_mod( @@ -196,38 +294,50 @@ class correlator(gr.top_block, Qt.QWidget): pre_diff_code=True, excess_bw=excess_bw, verbose=False, - log=False, - truncate=False) + log=False) self.digital_constellation_decoder_cb_0 = digital.constellation_decoder_cb(const) self.digital_cma_equalizer_cc_0 = digital.cma_equalizer_cc(15, 1, .002, 1) - self.blocks_vector_source_x_0 = blocks.vector_source_b(([0x00] * 10 + [0xaa, 0xff, 0x0a] + [0x00] * 10) * 20, False, 1, []) - self.blocks_throttle_0 = blocks.throttle(gr.sizeof_char*1, samp_rate,True) + self.channels_channel_model_0 = channels.channel_model( + noise_voltage=0.1, + frequency_offset=0.0, + epsilon=1.0, + taps=[1. ], + noise_seed=243, + block_tags=False) + self.blocks_vector_source_x_0 = blocks.vector_source_b(testvec, True, 1, []) + self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True) + self.blocks_stream_mux_0 = blocks.stream_mux(gr.sizeof_char*1, [15, len(testvec)]) + self.blocks_null_source_0 = blocks.null_source(gr.sizeof_char*1) self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_char*1) - self.blocks_complex_to_mag_0 = blocks.complex_to_mag(1) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_ff(180 / 3.141592653589793) + self.blocks_complex_to_magphase_0 = blocks.complex_to_magphase(1) ################################################## # Connections ################################################## - self.connect((self.blocks_complex_to_mag_0, 0), (self.qtgui_time_sink_x_0, 0)) - self.connect((self.blocks_throttle_0, 0), (self.digital_constellation_modulator_0, 0)) - self.connect((self.blocks_vector_source_x_0, 0), (self.blocks_throttle_0, 0)) + self.connect((self.blocks_complex_to_magphase_0, 1), (self.blocks_multiply_const_vxx_0, 0)) + self.connect((self.blocks_complex_to_magphase_0, 0), (self.qtgui_time_sink_x_0, 0)) + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.qtgui_time_sink_x_2, 0)) + self.connect((self.blocks_null_source_0, 0), (self.blocks_stream_mux_0, 0)) + self.connect((self.blocks_stream_mux_0, 0), (self.digital_constellation_modulator_0, 0)) + self.connect((self.blocks_throttle_0, 0), (self.digital_pfb_clock_sync_xxx_0, 0)) + self.connect((self.blocks_vector_source_x_0, 0), (self.blocks_stream_mux_0, 1)) + self.connect((self.channels_channel_model_0, 0), (self.blocks_throttle_0, 0)) self.connect((self.digital_cma_equalizer_cc_0, 0), (self.digital_constellation_decoder_cb_0, 0)) self.connect((self.digital_cma_equalizer_cc_0, 0), (self.fir_filter_xxx_1, 0)) + self.connect((self.digital_cma_equalizer_cc_0, 0), (self.qtgui_const_sink_x_0, 0)) + self.connect((self.digital_cma_equalizer_cc_0, 0), (self.qtgui_time_sink_x_1_1, 0)) self.connect((self.digital_constellation_decoder_cb_0, 0), (self.blocks_null_sink_0, 0)) - self.connect((self.digital_constellation_modulator_0, 0), (self.digital_pfb_clock_sync_xxx_0, 0)) - self.connect((self.digital_constellation_modulator_0, 0), (self.qtgui_time_sink_x_1, 0)) + self.connect((self.digital_constellation_modulator_0, 0), (self.channels_channel_model_0, 0)) self.connect((self.digital_pfb_clock_sync_xxx_0, 0), (self.digital_cma_equalizer_cc_0, 0)) - self.connect((self.fir_filter_xxx_1, 0), (self.blocks_complex_to_mag_0, 0)) + self.connect((self.fir_filter_xxx_1, 0), (self.blocks_complex_to_magphase_0, 0)) def closeEvent(self, event): self.settings = Qt.QSettings("GNU Radio", "correlator") self.settings.setValue("geometry", self.saveGeometry()) - self.stop() - self.wait() - event.accept() def get_sps(self): @@ -258,6 +368,13 @@ class correlator(gr.top_block, Qt.QWidget): self.timing_loop_bw = timing_loop_bw self.digital_pfb_clock_sync_xxx_0.set_loop_bandwidth(self.timing_loop_bw) + def get_testvec(self): + return self.testvec + + def set_testvec(self, testvec): + self.testvec = testvec + self.blocks_vector_source_x_0.set_data(self.testvec, []) + def get_samp_rate(self): return self.samp_rate @@ -265,7 +382,8 @@ class correlator(gr.top_block, Qt.QWidget): self.samp_rate = samp_rate self.blocks_throttle_0.set_sample_rate(self.samp_rate) self.qtgui_time_sink_x_0.set_samp_rate(self.samp_rate) - self.qtgui_time_sink_x_1.set_samp_rate(self.samp_rate) + self.qtgui_time_sink_x_1_1.set_samp_rate(self.samp_rate) + self.qtgui_time_sink_x_2.set_samp_rate(self.samp_rate) def get_rrc_taps(self): return self.rrc_taps @@ -274,12 +392,26 @@ class correlator(gr.top_block, Qt.QWidget): self.rrc_taps = rrc_taps self.digital_pfb_clock_sync_xxx_0.update_taps(self.rrc_taps) + def get_revconj_access_code_symbols(self): + return self.revconj_access_code_symbols + + def set_revconj_access_code_symbols(self, revconj_access_code_symbols): + self.revconj_access_code_symbols = revconj_access_code_symbols + self.fir_filter_xxx_1.set_taps(self.revconj_access_code_symbols) + def get_const(self): return self.const def set_const(self, const): self.const = const + def get_access_code_symbols_sps(self): + return self.access_code_symbols_sps + + def set_access_code_symbols_sps(self, access_code_symbols_sps): + self.access_code_symbols_sps = access_code_symbols_sps + + @@ -297,9 +429,6 @@ def main(top_block_cls=correlator, options=None): tb.show() def sig_handler(sig=None, frame=None): - tb.stop() - tb.wait() - Qt.QApplication.quit() signal.signal(signal.SIGINT, sig_handler) @@ -309,6 +438,11 @@ def main(top_block_cls=correlator, options=None): timer.start(500) timer.timeout.connect(lambda: None) + def quitting(): + tb.stop() + tb.wait() + + qapp.aboutToQuit.connect(quitting) qapp.exec_() if __name__ == '__main__': -- cgit v1.2.1 From 7588ef396ffeb2422d5d2deacbb5c5443475bee1 Mon Sep 17 00:00:00 2001 From: Nao Pross Date: Sun, 28 Nov 2021 00:14:41 +0100 Subject: Move GUI files into their dir --- src/gui.py | 131 --------------------------------------------------------- src/gui/gui.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/gui/net.py | 68 ++++++++++++++++++++++++++++++ src/net.py | 68 ------------------------------ 4 files changed, 199 insertions(+), 199 deletions(-) delete mode 100755 src/gui.py create mode 100755 src/gui/gui.py create mode 100644 src/gui/net.py delete mode 100644 src/net.py diff --git a/src/gui.py b/src/gui.py deleted file mode 100755 index b2cbebb..0000000 --- a/src/gui.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python3 - -# Python stdlib -import sys - -# Grahical libraries -from dearpygui.dearpygui import * -import dearpygui._dearpygui as internal_dpg -from dearpygui.demo import show_demo - -# Detect (unix) signals -import signal - -# Mathematics -import numpy as np - -# For debugging -import logging - -# Remote resources -import net - -#================================================ -# Debugging tools - -logging.basicConfig(format="[%(levelname)s] %(asctime)s %(message)s", level=logging.DEBUG) -logger = logging.getLogger(__name__) - -#================================================ -# Initialize DearPyGUI - -create_context() -create_viewport(title="Fading Demonstrator") -setup_dearpygui() - -# Show demo for dev -show_demo() - - -#================================================ -# GUI Callback functions - -# Flow graph window -def on_rx_node_link(sender, app_data): - link_id_1, link_id_2 = app_data - add_node_link(link_id_1, link_id_2, parent=sender) - -def on_rx_node_delink(sender, app_data): - link_id = app_data - delete_item(link_id) - -#================================================ -# Settings Window - -with window(label="Settings", width=200, height=400, pos=(25, 450), tag="sim_win"): - with child_window(autosize_x=True, height=100): - add_button(label="Toggle Fullscreen", callback= toggle_viewport_fullscreen) - -#================================================ -# Flow Graph Window - -with window(label="RX DSP Flow Graph", width=800, height=400, pos=(25,25), tag="rx_win"): - with node_editor(callback=on_rx_node_link, delink_callback=on_rx_node_delink): - with node(label="USRP Source", pos=(20,100)): - with node_attribute(tag="src_out", attribute_type=mvNode_Attr_Output): - add_text("Signal from antenna") - - with node(label="Clock Sync", pos=(200,200)): - with node_attribute(tag="clksync_in", attribute_type=mvNode_Attr_Input): - add_text("Input") - - with node_attribute(tag="clksync_out", attribute_type=mvNode_Attr_Output): - add_text("Synchronized") - - with node(label="Equalizer", pos=(350,100)): - with node_attribute(tag="eq_in", attribute_type=mvNode_Attr_Input): - add_text("Input") - - with node_attribute(attribute_type=mvNode_Attr_Static): - add_knob_float(label="Gain") - - with node_attribute(tag="eq_out", attribute_type=mvNode_Attr_Output): - add_text("Equalized") - - with node(label="Phase Locked Loop", pos=(600, 200)): - with node_attribute(tag="pll_in", attribute_type=mvNode_Attr_Input): - add_text("Input") - - with node_attribute(tag="pll_out", attribute_type=mvNode_Attr_Output): - add_text("Locked") - add_knob_float(label="Loop BW") - - - add_node_link(get_alias_id("src_out"), get_alias_id("clksync_in")) - add_node_link(get_alias_id("clksync_out"), get_alias_id("eq_in")) - add_node_link(get_alias_id("eq_out"), get_alias_id("pll_in")) - -#================================================ -# Network plots Window - -recv_plot = net.network_plot(url="udp://localhost:31415", nsamples=100, label="Test", height=300, width=800) - -plots = { - recv_plot: "plt_ampl" -} - -with window(label="Time domain plots", width=800, height=400, pos=(850,25)): - with recv_plot: - add_plot_axis(mvXAxis, label="Time") - add_plot_axis(mvYAxis, label="Amplitude", tag="plt_ampl") - - add_line_series(recv_plot.x_data, recv_plot.y_data, parent="plt_ampl") - -#================================================ -# Start GUI and main loop - -# Start window and main loop -show_viewport() - -# Main loop -while is_dearpygui_running(): - for plt, tag in plots.items(): - plt.refresh_series(tag) - - render_dearpygui_frame() - -#================================================ -# Close everything - -# clean up gui -destroy_context() diff --git a/src/gui/gui.py b/src/gui/gui.py new file mode 100755 index 0000000..b2cbebb --- /dev/null +++ b/src/gui/gui.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +# Python stdlib +import sys + +# Grahical libraries +from dearpygui.dearpygui import * +import dearpygui._dearpygui as internal_dpg +from dearpygui.demo import show_demo + +# Detect (unix) signals +import signal + +# Mathematics +import numpy as np + +# For debugging +import logging + +# Remote resources +import net + +#================================================ +# Debugging tools + +logging.basicConfig(format="[%(levelname)s] %(asctime)s %(message)s", level=logging.DEBUG) +logger = logging.getLogger(__name__) + +#================================================ +# Initialize DearPyGUI + +create_context() +create_viewport(title="Fading Demonstrator") +setup_dearpygui() + +# Show demo for dev +show_demo() + + +#================================================ +# GUI Callback functions + +# Flow graph window +def on_rx_node_link(sender, app_data): + link_id_1, link_id_2 = app_data + add_node_link(link_id_1, link_id_2, parent=sender) + +def on_rx_node_delink(sender, app_data): + link_id = app_data + delete_item(link_id) + +#================================================ +# Settings Window + +with window(label="Settings", width=200, height=400, pos=(25, 450), tag="sim_win"): + with child_window(autosize_x=True, height=100): + add_button(label="Toggle Fullscreen", callback= toggle_viewport_fullscreen) + +#================================================ +# Flow Graph Window + +with window(label="RX DSP Flow Graph", width=800, height=400, pos=(25,25), tag="rx_win"): + with node_editor(callback=on_rx_node_link, delink_callback=on_rx_node_delink): + with node(label="USRP Source", pos=(20,100)): + with node_attribute(tag="src_out", attribute_type=mvNode_Attr_Output): + add_text("Signal from antenna") + + with node(label="Clock Sync", pos=(200,200)): + with node_attribute(tag="clksync_in", attribute_type=mvNode_Attr_Input): + add_text("Input") + + with node_attribute(tag="clksync_out", attribute_type=mvNode_Attr_Output): + add_text("Synchronized") + + with node(label="Equalizer", pos=(350,100)): + with node_attribute(tag="eq_in", attribute_type=mvNode_Attr_Input): + add_text("Input") + + with node_attribute(attribute_type=mvNode_Attr_Static): + add_knob_float(label="Gain") + + with node_attribute(tag="eq_out", attribute_type=mvNode_Attr_Output): + add_text("Equalized") + + with node(label="Phase Locked Loop", pos=(600, 200)): + with node_attribute(tag="pll_in", attribute_type=mvNode_Attr_Input): + add_text("Input") + + with node_attribute(tag="pll_out", attribute_type=mvNode_Attr_Output): + add_text("Locked") + add_knob_float(label="Loop BW") + + + add_node_link(get_alias_id("src_out"), get_alias_id("clksync_in")) + add_node_link(get_alias_id("clksync_out"), get_alias_id("eq_in")) + add_node_link(get_alias_id("eq_out"), get_alias_id("pll_in")) + +#================================================ +# Network plots Window + +recv_plot = net.network_plot(url="udp://localhost:31415", nsamples=100, label="Test", height=300, width=800) + +plots = { + recv_plot: "plt_ampl" +} + +with window(label="Time domain plots", width=800, height=400, pos=(850,25)): + with recv_plot: + add_plot_axis(mvXAxis, label="Time") + add_plot_axis(mvYAxis, label="Amplitude", tag="plt_ampl") + + add_line_series(recv_plot.x_data, recv_plot.y_data, parent="plt_ampl") + +#================================================ +# Start GUI and main loop + +# Start window and main loop +show_viewport() + +# Main loop +while is_dearpygui_running(): + for plt, tag in plots.items(): + plt.refresh_series(tag) + + render_dearpygui_frame() + +#================================================ +# Close everything + +# clean up gui +destroy_context() diff --git a/src/gui/net.py b/src/gui/net.py new file mode 100644 index 0000000..2c91bb8 --- /dev/null +++ b/src/gui/net.py @@ -0,0 +1,68 @@ +import select +import socket +from urllib.parse import urlparse + +import numpy as np +from numpy_ringbuffer import RingBuffer +import dearpygui.dearpygui as dpg + + +class udpsource: + """ + Creates an UDP listening socket + """ + def __init__(self, url): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.url = urlparse(url) + + def __del__(self): + self.sock.close() + + def bind(self): + self.sock.setblocking(False) + self.sock.bind((self.url.hostname, self.url.port)) + # self.sock.listen() + + def read(self, nbytes): + ready_to_read, ready_to_write, in_err = \ + select.select([self.sock], [], [], 1) + + if ready_to_read: + data = sock.recv(nbytes) + print(data) + else: + return None + + +class network_plot(udpsource): + def __init__(self, url, nsamples, **kwargs): + udpsource.__init__(self, url) + + self.nsamples = nsamples + self.plot = dpg.plot(**kwargs) + + # create buffer and fill with zeroes + self.buffer = RingBuffer(capacity=nsamples, dtype=(float, 2)) + for i in range(nsamples): + # TODO: remove random data used for testing + self.buffer.append(np.array([i, 1 + np.random.rand() / 5])) + + self.bind() + + def __enter__(self): + return self.plot.__enter__() + + def __exit__(self, t, val, tb): + self.plot.__exit__(t, val, tb) + + @property + def x_data(self): + return np.array(self.buffer[:,0]) + + @property + def y_data(self): + return np.array(self.buffer[:,1]) + + def refresh_series(self, tag): + dpg.set_value(tag, [self.x_data, self.y_data]) + pass diff --git a/src/net.py b/src/net.py deleted file mode 100644 index 2c91bb8..0000000 --- a/src/net.py +++ /dev/null @@ -1,68 +0,0 @@ -import select -import socket -from urllib.parse import urlparse - -import numpy as np -from numpy_ringbuffer import RingBuffer -import dearpygui.dearpygui as dpg - - -class udpsource: - """ - Creates an UDP listening socket - """ - def __init__(self, url): - self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.url = urlparse(url) - - def __del__(self): - self.sock.close() - - def bind(self): - self.sock.setblocking(False) - self.sock.bind((self.url.hostname, self.url.port)) - # self.sock.listen() - - def read(self, nbytes): - ready_to_read, ready_to_write, in_err = \ - select.select([self.sock], [], [], 1) - - if ready_to_read: - data = sock.recv(nbytes) - print(data) - else: - return None - - -class network_plot(udpsource): - def __init__(self, url, nsamples, **kwargs): - udpsource.__init__(self, url) - - self.nsamples = nsamples - self.plot = dpg.plot(**kwargs) - - # create buffer and fill with zeroes - self.buffer = RingBuffer(capacity=nsamples, dtype=(float, 2)) - for i in range(nsamples): - # TODO: remove random data used for testing - self.buffer.append(np.array([i, 1 + np.random.rand() / 5])) - - self.bind() - - def __enter__(self): - return self.plot.__enter__() - - def __exit__(self, t, val, tb): - self.plot.__exit__(t, val, tb) - - @property - def x_data(self): - return np.array(self.buffer[:,0]) - - @property - def y_data(self): - return np.array(self.buffer[:,1]) - - def refresh_series(self, tag): - dpg.set_value(tag, [self.x_data, self.y_data]) - pass -- cgit v1.2.1 From 9eb911230923e11773be422f76726dfbfad7e5bc Mon Sep 17 00:00:00 2001 From: Nao Pross Date: Sun, 28 Nov 2021 01:07:08 +0100 Subject: Delete acgen stuff, it was all wrong --- tests/correlator/acgen.dat | 15 ---- tests/correlator/acgen.grc | 198 --------------------------------------------- tests/correlator/acgen.py | 125 ---------------------------- tests/correlator/acproc.py | 73 ----------------- 4 files changed, 411 deletions(-) delete mode 100644 tests/correlator/acgen.dat delete mode 100644 tests/correlator/acgen.grc delete mode 100755 tests/correlator/acgen.py delete mode 100755 tests/correlator/acproc.py diff --git a/tests/correlator/acgen.dat b/tests/correlator/acgen.dat deleted file mode 100644 index a13db8e..0000000 --- a/tests/correlator/acgen.dat +++ /dev/null @@ -1,15 +0,0 @@ -ßØs9ßØs9ty¢ty¢‚R¹‚R¹P8|!P8|!±¼ÿ9±¼ÿ9)Eס)Eס— -º— -ºŸ¸‡"Ÿ¸‡"b¢I:b¢I:¥T¡¥T¡£Sº£SºÙ¦¨"Ù¦¨"o¯:o¯:ÌŸw¢ÌŸw¢S픺S픺Nj#Nj#­8»:­8»:‚¿]¢‚¿]¢XźXź €"# €"#ÿEî:ÿEî:åÅ¢åÅ¢îKûºîKûºÐ´k#дk#9ô;9ô;ŒB­¢ŒB­¢ñZ»ñZ»U2}#U2}#&¾4;&¾4;N -£N -£ëq?»ëq?»­±«#­±«#lbZ;lbZ;“ 颓 é¢víg»víg»Øa´#Øa´#fƒ;fƒ;3£3£“‹»“‹»Ÿî#Ÿî#.¤œ;.¤œ;m=£m=£àx§»àx§»Tö#Tö#P"»;P"»;Y~]£Y~]£•ðÈ»•ðÈ»(Ç!$(Ç!$Âà;Âà;›?œ£›?œ£DÔñ»DÔñ»j¿$$j¿$$`Ì<`Ì<3nÑ£3nÑ£ u¼ u¼Äœ&$Äœ&$£#<£#<€¤€¤÷E3¼÷E3¼3›&$3›&$„PI<„PI<מ/¤×ž/¤Áú^¼Áú^¼­V#$­V#$/Ÿ|ê¹@>òN¥òN¥óµ3¿óµ3¿'µ¿'µ¿&dο&dο'µ¿'µ¿Å®¦¿Å®¦¿'µ¿'µ¿—¿¿—¿¿'µ¿'µ¿”D­¿”D­¿'µ¿'µ¿^»¿^»¿'µ¿'µ¿“˜¯¿“˜¯¿'µ¿'µ¿jÁ¹¿jÁ¹¿'µ¿'µ¿(Ç°¿(Ç°¿'µ¿'µ¿Ú¸¿Ú¸¿'µ¿'µ¿Õ}±¿Õ}±¿'µ¿'µ¿&F¸¿&F¸¿'µ¿'µ¿ý÷±¿ý÷±¿'µ¿'µ¿‰ß·¿‰ß·¿'µ¿'µ¿gO²¿gO²¿'µ¿'µ¿.”·¿.”·¿'µ¿'µ¿‘²¿‘²¿'µ¿'µ¿‚Z·¿‚Z·¿'µ¿'µ¿IJ¿IJ¿'µ¿'µ¿ñ,·¿ñ,·¿'µ¿'µ¿í²¿í²¿'µ¿'µ¿ -·¿ -·¿'µ¿'µ¿z³¿z³¿'µ¿'µ¿‹é¶¿‹é¶¿'µ¿'µ¿`*³¿`*³¿'µ¿'µ¿ì϶¿ì϶¿'µ¿'µ¿úA³¿úA³¿'µ¿'µ¿º¶¿º¶¿'µ¿'µ¿:V³¿:V³¿'µ¿'µ¿E§¶¿E§¶¿'µ¿'µ¿Äg³¿Äg³¿'µ¿'µ¿á–¶¿á–¶¿'µ¿'µ¿w³¿w³¿'µ¿'µ¿xˆ¶¿xˆ¶¿'µ¿'µ¿¬„³¿¬„³¿'µ¿'µ¿µ{¶¿µ{¶¿'µ¿'µ¿·³¿·³¿'µ¿'µ¿Rp¶¿Rp¶¿'µ¿'µ¿~›³¿~›³¿'µ¿'µ¿Yu¶¿f¶¿'µ¿'µ¿<•³¿2¥³¿'µ¿'µ¿Û|¶¿!l¶¿'µ¿'µ¿tŒ³¿ž³¿'µ¿'µ¿ï†¶¿}t¶¿'µ¿'µ¿ -³¿q”³¿'µ¿'µ¿ƒ„¶¿p¶¿'µ¿'µ¿p{³¿þ³¿'µ¿'µ¿Ë™¶¿ƒ¶¿'µ¿'µ¿òd³¿}³¿'µ¿'µ¿™±¶¿Õˆ¶¿'µ¿'µ¿³K³¿Áv³¿'µ¿'µ¿m̶¿£¶¿'µ¿'µ¿%/³¿^o³¿'µ¿'µ¿¥Û¶¿¯—¶¿'µ¿'µ¿Ž³¿˜f³¿'µ¿'µ¿ ý¶¿G¡¶¿'µ¿'µ¿¬ú²¿\³¿'µ¿'µ¿¯#·¿¼¶¿'µ¿'µ¿éв¿S?³¿'µ¿'µ¿÷P·¿ýÚ¶¿'µ¿'µ¿¦Ÿ²¿³¿'µ¿'µ¿‡w·¿Ãﶿ'µ¿'µ¿št²¿ä³¿'µ¿'µ¿··¿å·¿'µ¿'µ¿(.²¿ÐÙ²¿'µ¿'µ¿—¸¿E;·¿'µ¿'µ¿Ö±¿Ñ²²¿'µ¿'µ¿>i¸¿Îg·¿'µ¿'µ¿nd±¿ž²¿'µ¿'µ¿¸Ü¸¿&£·¿'µ¿'µ¿XÜ°¿9:²¿'µ¿'µ¿]Ž¹¿$õ·¿'µ¿'µ¿À°¿4ر¿'µ¿'µ¿I‘º¿*{¸¿'µ¿'µ¿ˆÅ®¿6±¿'µ¿'µ¿ÿ,¼¿UC¹¿'µ¿'µ¿™¥¬¿9°¿'µ¿'µ¿y ¿¿}º¿'µ¿'µ¿Øi¨¿Ž®¿'µ¿'µ¿{çÅ¿'µ¿'µ¿úÖ›¿Xિ'µ¿'µ¿ÀÖã¿_í¿'µ¿'µ¿•Ò¼ÜqŸ¿'µ?'µ¿‰½ê?ÐBß¿'µ?'µ¿4Õ“?îó”½'µ?'µ?tÐ?I_ó?'µ?'µ?ô˜?ò¼…?'µ?'µ? á?dõî?'µ?'µ?ŠCú=ñ¨‰<'µ¿'µ¿ŽÀ…Bý¿'µ¿'µ¿3>ÇY7½'µ?'µ?jdÞ?”@'µ?'µ?³Gœ?¯Åø½'µ?'µ¿QDÊ?@Þ¿'µ?'µ¿ÈÏœ?n—¿'µ?'µ¿L?Ý?Å5Ç¿'µ?'µ¿´¥>°Ö£¿'µ¿'µ¿öÓÀ”È¿'µ¿'µ¿Óf>q›¿'µ?'µ¿Ü?‡uâ¿'µ?'µ¿õß?çùU½'µ?'µ? ßÈ?v-ñ?'µ?'µ?ÿ -ž?³š‡?'µ?'µ?'Ü?®Yí?'µ?'µ?Æ|>qZã<'µ¿'µ¿êDÀ(~þ¿'µ¿'µ¿åÊ>mZ½'µ?'µ?ÓÛ?{@'µ?'µ?鉞?@·ê½'µ?'µ¿>CÈ?x ß¿'µ?'µ¿~šž?¢Þœ¿'µ?'µ¿y¢Û?xÞÇ¿'µ?'µ¿S!> <£¿'µ¿'µ¿ö}ÀÞ©È¿'µ¿'µ¿!">.îš¿'µ?'µ¿×oÛ?3ïâ¿'µ?'µ¿Îæž?HÙG½'µ?'µ?ìÇ?2Äð?'µ?'µ?cìž?ýü‡?'µ?'µ?\UÛ?±ýì?'µ?'µ? -™#>ìø<'µ¿'µ¿R À>Ïþ¿'µ¿'µ¿w%$>CÏ -½'µ?'µ?22Û?xó@'µ?'µ?O!Ÿ?tvæ½'µ?'µ¿´Ç?ÞKß¿'µ?'µ¿L!Ÿ?˜¡œ¿'µ?'µ¿ó"Û?hÈ¿'µ?'µ¿ ¥$>ü£¿'µ¿'µ¿N°ÀDÞÈ¿'µ¿'µ¿!¥$>ÓÊš¿'µ?'µ¿ô"Û?âã¿'µ?'µ¿O!Ÿ?˜§E½'µ?'µ?´Ç?x³ð?'µ?'µ?L!Ÿ?ò ˆ?'µ?'µ?ó"Û?tîì?'µ?'µ? ¥$>ìø<'µ¿'µ¿N°À>Ïþ¿'µ¿'µ¿!¥$>CÏ -½'µ?'µ?ô"Û?xó@'µ?'µ?O!Ÿ?tvæ½'µ?'µ¿´Ç?ÞKß¿'µ?'µ¿L!Ÿ?˜¡œ¿'µ?'µ¿ó"Û?hÈ¿'µ?'µ¿ ¥$>ü£¿'µ¿'µ¿N°ÀDÞÈ¿'µ¿'µ¿!¥$>ÓÊš¿'µ?'µ¿ô"Û?âã¿'µ?'µ¿O!Ÿ?˜§E½'µ?'µ?´Ç?x³ð?'µ?'µ?L!Ÿ?ò ˆ?'µ?'µ?ó"Û?tîì?'µ?'µ? ¥$>ìø<'µ¿'µ¿N°À>Ïþ¿'µ¿'µ¿!¥$>CÏ -½'µ?'µ?ô"Û?xó@'µ?'µ?O!Ÿ?tvæ½'µ?'µ¿´Ç?ÞKß¿'µ?'µ¿L!Ÿ?˜¡œ¿'µ?'µ¿ó"Û?hÈ¿'µ?'µ¿ ¥$>ü£¿'µ¿'µ¿N°ÀDÞÈ¿'µ¿'µ¿!¥$>ÓÊš¿'µ?'µ¿ô"Û?âã¿'µ?'µ¿O!Ÿ?˜§E½'µ?'µ?´Ç?x³ð?'µ?'µ?L!Ÿ?ò ˆ?'µ?'µ?ó"Û?tîì?'µ?'µ? ¥$>ìø<'µ¿'µ¿N°À>Ïþ¿'µ¿'µ¿!¥$>CÏ -½'µ?'µ?ô"Û?xó@'µ?'µ?O!Ÿ?tvæ½'µ?'µ¿´Ç?ÞKß¿'µ?'µ¿L!Ÿ?˜¡œ¿'µ?'µ¿ó"Û?hÈ¿'µ?'µ¿ ¥$>ü£¿'µ¿'µ¿N°ÀDÞÈ¿'µ¿'µ¿!¥$>ÓÊš¿'µ?'µ¿ô"Û?âã¿'µ?'µ¿O!Ÿ?˜§E½'µ?'µ?´Ç?x³ð?'µ?'µ?L!Ÿ?ò ˆ?'µ?'µ?ó"Û?tîì?'µ?'µ? ¥$>ìø<'µ¿'µ¿N°À>Ïþ¿'µ¿'µ¿!¥$>CÏ -½'µ?'µ?ô"Û?xó@'µ?'µ?O!Ÿ?tvæ½'µ?'µ¿´Ç?ÞKß¿'µ?'µ¿L!Ÿ?˜¡œ¿'µ?'µ¿ó"Û?hÈ¿'µ?'µ¿ ¥$>ü£¿'µ¿'µ¿N°ÀDÞÈ¿'µ¿'µ¿!¥$>ÓÊš¿'µ?'µ¿ô"Û?âã¿'µ?'µ¿O!Ÿ?˜§E½'µ?'µ? \ No newline at end of file diff --git a/tests/correlator/acgen.grc b/tests/correlator/acgen.grc deleted file mode 100644 index abdc3bc..0000000 --- a/tests/correlator/acgen.grc +++ /dev/null @@ -1,198 +0,0 @@ -options: - parameters: - author: Naoki Pross - category: '[GRC Hier Blocks]' - cmake_opt: '' - comment: '' - copyright: '' - description: '' - gen_cmake: 'On' - gen_linking: dynamic - generate_options: no_gui - hier_block_src_path: '.:' - id: acgen - max_nouts: '0' - output_language: python - placement: (0,0) - qt_qss_theme: '' - realtime_scheduling: '' - run: 'True' - run_command: '{python} -u {filename}' - run_options: run - sizing_mode: fixed - thread_safe_setters: '' - title: Access Code Symbols Generator - window_size: '' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [8, 8] - rotation: 0 - state: enabled - -blocks: -- name: access_code - id: variable - parameters: - comment: '' - value: '[ 0xaa, 0xff, 0xff ]' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [40, 292.0] - rotation: 0 - state: true -- name: const - id: variable_constellation - parameters: - comment: '' - const_points: '[-1-1j, -1+1j, 1+1j, 1-1j]' - dims: '1' - precision: '8' - rot_sym: '4' - soft_dec_lut: None - sym_map: '[0, 1, 3, 2]' - type: qpsk - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [464, 124.0] - rotation: 0 - state: true -- name: excess_bw - id: variable - parameters: - comment: '' - value: '1' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [464, 308.0] - rotation: 0 - state: true -- name: padding_zeros - id: variable - parameters: - comment: '' - value: '10' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [40, 364.0] - rotation: 0 - state: true -- name: samp_rate - id: variable - parameters: - comment: '' - value: '32000' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [208, 12.0] - rotation: 0 - state: enabled -- name: sps - id: variable - parameters: - comment: '' - value: '4' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [304, 12.0] - rotation: 0 - state: true -- name: blocks_file_sink_0 - id: blocks_file_sink - parameters: - affinity: '' - alias: '' - append: 'False' - comment: '' - file: acgen.dat - type: complex - unbuffered: 'False' - vlen: '1' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [752, 204.0] - rotation: 0 - state: true -- name: blocks_throttle_0 - id: blocks_throttle - parameters: - affinity: '' - alias: '' - comment: '' - ignoretag: 'True' - maxoutbuf: '0' - minoutbuf: '0' - samples_per_second: samp_rate - type: byte - vlen: '1' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [264, 220.0] - rotation: 0 - state: true -- name: blocks_vector_source_x_0 - id: blocks_vector_source_x - parameters: - affinity: '' - alias: '' - comment: '' - maxoutbuf: '0' - minoutbuf: '0' - repeat: 'False' - tags: '[]' - type: byte - vector: '[0x00] * padding_zeros + access_code * 10' - vlen: '1' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [40, 204.0] - rotation: 0 - state: true -- name: digital_constellation_modulator_0 - id: digital_constellation_modulator - parameters: - affinity: '' - alias: '' - comment: '' - constellation: const - differential: 'False' - excess_bw: excess_bw - log: 'False' - maxoutbuf: '0' - minoutbuf: '0' - samples_per_symbol: sps - verbose: 'False' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [464, 196.0] - rotation: 0 - state: true - -connections: -- [blocks_throttle_0, '0', digital_constellation_modulator_0, '0'] -- [blocks_vector_source_x_0, '0', blocks_throttle_0, '0'] -- [digital_constellation_modulator_0, '0', blocks_file_sink_0, '0'] - -metadata: - file_format: 1 diff --git a/tests/correlator/acgen.py b/tests/correlator/acgen.py deleted file mode 100755 index 4d18b92..0000000 --- a/tests/correlator/acgen.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# -# SPDX-License-Identifier: GPL-3.0 -# -# GNU Radio Python Flow Graph -# Title: Access Code Symbols Generator -# Author: Naoki Pross -# GNU Radio version: 3.8.2.0 - -from gnuradio import blocks -from gnuradio import digital -from gnuradio import gr -from gnuradio.filter import firdes -import sys -import signal -from argparse import ArgumentParser -from gnuradio.eng_arg import eng_float, intx -from gnuradio import eng_notation - - -class acgen(gr.top_block): - - def __init__(self): - gr.top_block.__init__(self, "Access Code Symbols Generator") - - ################################################## - # Variables - ################################################## - self.sps = sps = 4 - self.samp_rate = samp_rate = 32000 - self.padding_zeros = padding_zeros = 10 - self.excess_bw = excess_bw = 1 - self.const = const = digital.constellation_qpsk().base() - self.access_code = access_code = [ 0xaa, 0xff, 0xff ] - - ################################################## - # Blocks - ################################################## - self.digital_constellation_modulator_0 = digital.generic_mod( - constellation=const, - differential=False, - samples_per_symbol=sps, - pre_diff_code=True, - excess_bw=excess_bw, - verbose=False, - log=False) - self.blocks_vector_source_x_0 = blocks.vector_source_b([0x00] * padding_zeros + access_code * 10, False, 1, []) - self.blocks_throttle_0 = blocks.throttle(gr.sizeof_char*1, samp_rate,True) - self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_gr_complex*1, 'acgen.dat', False) - self.blocks_file_sink_0.set_unbuffered(False) - - - - ################################################## - # Connections - ################################################## - self.connect((self.blocks_throttle_0, 0), (self.digital_constellation_modulator_0, 0)) - self.connect((self.blocks_vector_source_x_0, 0), (self.blocks_throttle_0, 0)) - self.connect((self.digital_constellation_modulator_0, 0), (self.blocks_file_sink_0, 0)) - - - def get_sps(self): - return self.sps - - def set_sps(self, sps): - self.sps = sps - - def get_samp_rate(self): - return self.samp_rate - - def set_samp_rate(self, samp_rate): - self.samp_rate = samp_rate - self.blocks_throttle_0.set_sample_rate(self.samp_rate) - - def get_padding_zeros(self): - return self.padding_zeros - - def set_padding_zeros(self, padding_zeros): - self.padding_zeros = padding_zeros - self.blocks_vector_source_x_0.set_data([0x00] * self.padding_zeros + self.access_code * 10, []) - - def get_excess_bw(self): - return self.excess_bw - - def set_excess_bw(self, excess_bw): - self.excess_bw = excess_bw - - def get_const(self): - return self.const - - def set_const(self, const): - self.const = const - - def get_access_code(self): - return self.access_code - - def set_access_code(self, access_code): - self.access_code = access_code - self.blocks_vector_source_x_0.set_data([0x00] * self.padding_zeros + self.access_code * 10, []) - - - - - -def main(top_block_cls=acgen, options=None): - tb = top_block_cls() - - def sig_handler(sig=None, frame=None): - tb.stop() - tb.wait() - - sys.exit(0) - - signal.signal(signal.SIGINT, sig_handler) - signal.signal(signal.SIGTERM, sig_handler) - - tb.start() - - tb.wait() - - -if __name__ == '__main__': - main() diff --git a/tests/correlator/acproc.py b/tests/correlator/acproc.py deleted file mode 100755 index 50c9a38..0000000 --- a/tests/correlator/acproc.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 - -import numpy as np -import matplotlib.pyplot as plt -from acgen import acgen - -# parameters -access_code_bits = [ 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, ] -access_code = list(np.packbits([0] * 3 + access_code_bits)) -padding_zeros = 10 - -# Create samples -print(f"Modulating symbols for access code {access_code_bits} = {access_code} with after {padding_zeros} empty bytes") -gen = acgen() -gen.set_access_code(access_code) -gen.set_padding_zeros(padding_zeros) - -gen.start() -gen.wait() - -# Extract one sequence -print("Extracting symbol sequence") - -# raw data -data = np.fromfile("acgen.dat", dtype=np.complex64) -plt.plot(data.real) -plt.plot(data.imag) -plt.title("Raw Data (time domain)") -plt.show() - -# take only symbols -symbols = data[1::gen.sps] - -plt.plot(symbols.real, symbols.imag) -plt.title("Symbols only (constellation)") -plt.show() - -# where ac symbols start, in symbols -ac_start = (padding_zeros) * 8 + 3 # plus three because code is 13 bits -ac_end = ac_start + int(np.ceil(len(access_code_bits) / 2.)) # divided by two because QPSK - -ac = symbols[ac_start:ac_end] - -print(f"Generated {len(ac)} (left padded) symbols from a {len(access_code_bits)} bit sequence") -print(list(ac)) - -print(f"Upsampled to {gen.sps} samples per symbos") -print(sum([[i, i, i, i] for i in ac], [])) - -fig, (ax1, ax2) = plt.subplots(2, 1) -fig.tight_layout() - -ax1.plot(ac.real, ac.imag) -ax1.set_title("Symbols of Access Code (constellation)") - -ax2.plot(ac.real, ".-") -ax2.plot(ac.imag, ".-") -ax2.set_title("Symbols of Access Code (time)") -plt.show() - -fir = list(np.conj(ac[::-1])) - -# Print the symbols -print("Reversed complex conjugate symbols (for FIR filter):") -print(fir) - -# Compute cross correlation - -xc = np.convolve(fir, ac) - -plt.plot(np.abs(xc)) -plt.title("Cross correlation (FIR)") -plt.show() -- cgit v1.2.1 From db1d1deeddec5301c86876cdc284f33c3a215ff9 Mon Sep 17 00:00:00 2001 From: Nao Pross Date: Sun, 28 Nov 2021 02:05:49 +0100 Subject: Working access code correlation --- notebooks/FrameSynchronization.ipynb | 221 ++++++-- tests/correlator/correlator.grc | 961 +++++++++++++++++++++++++++++------ tests/correlator/correlator.py | 283 ++++++++++- 3 files changed, 1240 insertions(+), 225 deletions(-) diff --git a/notebooks/FrameSynchronization.ipynb b/notebooks/FrameSynchronization.ipynb index e0187b2..911ddd6 100644 --- a/notebooks/FrameSynchronization.ipynb +++ b/notebooks/FrameSynchronization.ipynb @@ -8,10 +8,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "from numpy_ringbuffer import RingBuffer\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib.image as mpimg" + "import matplotlib.pyplot as plt" ] }, { @@ -22,40 +19,144 @@ "# Digital Frame Synchronization\n" ] }, + { + "cell_type": "markdown", + "id": "fc118de2-937b-4157-a5a1-9b7f152bf59b", + "metadata": {}, + "source": [ + "First we need to create the access code, a barker sequence which has a very good autocorrelation." + ] + }, { "cell_type": "code", - "execution_count": 27, - "id": "025c6919", - "metadata": { - "scrolled": false - }, + "execution_count": 2, + "id": "20dcd173-2889-4e21-9746-3d0a0ab060e8", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Header (N=16): [1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 1]\n", - "Stream (N=80): [0 0 1 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 1 1 0 1 1 1\n", - " 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 1 0 1 0 1 1 0 0 1 1 1 0 0 1 1 1 1 1 0\n", - " 0 1 1 1 0 1]\n", - "Correlation peak value: 16 at i=47\n" + "Access code: 13 bit pattern [1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1] = left padded bytes [31, 53]\n" ] - }, + } + ], + "source": [ + "ac = [ 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, ]\n", + "\n", + "ac_pad = [0] * int(8 * np.ceil(len(ac) / 8) - len(ac)) + ac\n", + "ac_bytes = list(np.packbits(ac_pad))\n", + "\n", + "print(f\"Access code: {len(ac)} bit pattern {ac} = left padded bytes {ac_bytes}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5cfd3c9a-d697-4462-bc26-26e82d25cbfd", + "metadata": {}, + "source": [ + "To correlate with the access code we need its symbols, thus the functions to modulate the access code." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "515891a9-dbd2-4088-9a41-b792d878f0fc", + "metadata": {}, + "outputs": [], + "source": [ + "def modulate_qpsk(m):\n", + " ampl = np.sqrt(2)\n", + " sym = {\n", + " 0: ampl * (-1 -1j),\n", + " 1: ampl * ( 1 -1j),\n", + " 2: ampl * (-1 +1j),\n", + " 3: ampl * ( 1 +1j)\n", + " }\n", + "\n", + " return map(lambda k: sym[k], m)" + ] + }, + { + "cell_type": "markdown", + "id": "d328e765-e977-4de9-9694-35012193a0aa", + "metadata": {}, + "source": [ + "### Symbols for QPSK" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3a4c834f-7012-4e22-b9f3-865527d09c02", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Modulate chunks [0, 1, 3, 3, 0, 3, 1, 1] with QPSK into 8 symbols:\n", + "[(-1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (-1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j)]\n", + "\n", + "Reversed complex conjugate list for FIR filter:\n", + "[(1.4142135623730951+1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (-1.4142135623730951+1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (-1.4142135623730951+1.4142135623730951j)]\n" + ] + } + ], + "source": [ + "# convert into chunks of 2 bits for QPSK\n", + "chunks = list(np.matmul(np.array(ac_pad).reshape((-1,2)), np.array([2, 1])))\n", + "syms = list(modulate_qpsk(chunks))\n", + "print(f\"Modulate chunks {chunks} with QPSK into {len(syms)} symbols:\\n{syms}\\n\")\n", + "\n", + "fir_syms = list(np.conj(syms[::-1]))\n", + "print(f\"Reversed complex conjugate list for FIR filter:\\n{fir_syms}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d6155b2e-9405-4d20-8c05-72af45575a04", + "metadata": {}, + "outputs": [ { "data": { + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, (left, right) = plt.subplots(1, 2, figsize = (20, 4))\n", + "\n", + "left.plot(np.real(syms), \".-\")\n", + "left.plot(np.imag(syms), \".-\")\n", + "left.set_title(\"Access Code\")\n", + "\n", + "right.plot(np.real(fir_syms), \".-\")\n", + "right.plot(np.imag(fir_syms), \".-\")\n", + "right.set_title(\"FIR Filter\")\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8163c2e0-0652-43c5-ad32-eaf0e94a638e", + "metadata": {}, + "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABZUAAAEvCAYAAAA90y+qAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAirUlEQVR4nO3dcYxlWX4X9u8v1b1Qa8cqk2ljd+1uZolMwQoTinSCm02Q4yWpBSxvyzKKV1m0cUAzIDAGQQ3b5o9N/gi7SiECCghpZK/XCKsZq+k0FhDKKy+OQQo79LpgetfjCpbxtqd6zbRlFUbkyd1bc/ijumemZ7unb7336r5btz4fadT9Tr3T53fPvee++76qubdaawEAAAAAgC7+o0UXAAAAAADAySFUBgAAAACgM6EyAAAAAACdCZUBAAAAAOhMqAwAAAAAQGdCZQAAAAAAOjvT52BPPfVUe/rpp/scEgAAAACAI/r85z//K621c4/6Wa+h8tNPP50bN270OSQAAAAAAEdUVV963M/c/gIAAAAAgM6EygAAAAAAdCZUBgAAAACgM6EyAAAAAACdCZUBAAAAAOhMqAwAAAAAQGdCZQAAAAAAOntiqFxVn6qqV6vqC29p/76q2q2qL1bV/358JQIAwOJc39nL+z/52bz3Y/8g7//kZ3N9Z2/RJQEAwEKd6fCeTyf560n+1oOGqvpvk3woye9srf16VX3D8ZQHAACLc31nL5ev3czk3kGSZG9/ksvXbiZJLq2vLrI0AABYmCf+pnJr7aeT/Opbmv9Ekk+21n79/ntePYbaAABgoba2d18PlB+Y3DvI1vbugioCAIDFm/aeyr81yX9TVZ+rqv+nqv7Lx72xqp6pqhtVdePOnTtTDgcAAP27vT85UjsAAJwG04bKZ5J8fZJvTbKZ5Meqqh71xtba8621C621C+fOnZtyOAAA6N/5leUjtQMAwGkwbaj8SpJr7dCLSV5L8tT8ygIAgMXb3FjL8tmlh9qWzy5lc2NtQRUBAMDiTRsqX0/y7UlSVb81yTuS/MqcagIAgEG4tL6aT3zXt+QdS4eXzasry/nEd32Lh/QBAHCqnXnSG6rqSpJvS/JUVb2S5ONJPpXkU1X1hSR3k3y0tdaOs1AAAFiES+urufLirSTJC89eXHA1AACweE8MlVtrH37Mjz4y51oAAAAAABi4aW9/AQAAAADAKSRUBgAAAACgM6EyAAAAAACdCZUBAAAAAOhMqAwAAAAAQGdCZQAAAAAAOhMqAwAAAADQ2ZlFFwAAAHCSXN/Zy9b2bm7vT3J+ZTmbG2u5tL666LIAAHojVAYAAOjo+s5eLl+7mcm9gyTJ3v4kl6/dTBLBMgBwarj9BQAAQEdb27uvB8oPTO4dZGt7d0EVAQD0T6gMAADQ0e39yZHaAQDGSKgMAADQ0fmV5SO1AwCMkVAZAACgo82NtSyfXXqobfnsUjY31hZUEQBA/zyoDwAAoKMHD+N77upLuXvwWlZXlrO5seYhfQDAqSJUBgAAOIJL66u58uKtJMkLz15ccDUAAP1z+wsAAAAAADoTKgMAAAAA0JlQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ09MVSuqk9V1atV9YVH/OzPV1WrqqeOpzwAAAAAAIaky28qfzrJB9/aWFXvTvLfJbk155oAAAAAABioJ4bKrbWfTvKrj/jR/5HkuSRt3kUBAAAAADBMU91Tuaq+M8lea+1fzrkeAAAAAAAG7MxRO1TVO5P8xST/fcf3P5PkmSR5z3vec9ThAAAAAAAYkGl+U/k/S/LeJP+yqn4xybuS/ExVfeOj3txae761dqG1duHcuXPTVwoAAAAAwMId+TeVW2s3k3zDg9f3g+ULrbVfmWNdAAAAAAAM0BN/U7mqriT5f5OsVdUrVfVHj78sAAAAAACG6Im/qdxa+/ATfv703KoBAAAAAGDQprmnMgAAAAAAp5RQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZ0JlAAAAAAA6O7PoAgAAgPG6vrOXre3d3N6f5PzKcjY31nJpfXXRZcGpYy0CME9CZQAA4Fhc39nL5Ws3M7l3kCTZ25/k8rWbSSLMgh5ZiwDMm9tfAAAAx2Jre/f1EOuByb2DbG3vLqgiOJ2sRQDmTagMAAAci9v7kyO1A8fDWgRg3oTKAADAsTi/snykduB4WIsAzJtQGQAAOBabG2tZPrv0UNvy2aVsbqwtqCI4naxFAObNg/oAAIBj8eABYM9dfSl3D17L6spyNjfWPBgMemYtAjBvQmUAAODYXFpfzZUXbyVJXnj24oKrgdPLWgRgntz+AgAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZ08MlavqU1X1alV94U1tW1X1c1X1UlX9X1W1cqxVAgAAAAAwCF1+U/nTST74lrbPJPkdrbXfmeT/S3J5znUBADAn13f28v5Pfjbv/dg/yPs/+dlc39lbdEksmGMCAIBZnHnSG1prP11VT7+l7Sfe9PKfJfnuOdcFAMAcXN/Zy+VrNzO5d5Ak2duf5PK1m0mSS+uriyyNBXFMAAAwq3ncU/l/TvJ/z+HfAQBgzra2d18PDx+Y3DvI1vbugipi0RwTAADMaqZQuar+YpKvJPnRt3nPM1V1o6pu3LlzZ5bhAAA4otv7kyO1M36OCQAAZjV1qFxVH03yHUn+x9Zae9z7WmvPt9YutNYunDt3btrhAACYwvmV5SO1M36OCQAAZjVVqFxVH0zyF5J8Z2vt/59vSQAAzMvmxlqWzy491LZ8dimbG2sLqohFc0wAADCrJz6or6quJPm2JE9V1StJPp7kcpLfkOQzVZUk/6y19sePsU4AAKbw4MFrz119KXcPXsvqynI2N9Y8kO0Uc0wAADCrJ4bKrbUPP6L5h46hFgAAjsGl9dVcefFWkuSFZy8uuBqGwDEBAMAsZnpQHwAAAAAAp4tQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZ0JlAAAAAAA6O7PoAgAAGJ7rO3vZ2t7N7f1Jzq8sZ3NjLZfWV+feB5g/a3E25m/87GOA2QmVAQB4yPWdvVy+djOTewdJkr39SS5fu5kkj/3SPU0fYP6sxdmYv/GzjwHmw+0vAAB4yNb27utfth+Y3DvI1vbuXPsA82ctzsb8jZ99DDAfQmUAAB5ye39ypPZp+wDzZy3OxvyNn30MMB9CZQAAHnJ+ZflI7dP2AebPWpyN+Rs/+xhgPoTKAAA8ZHNjLctnlx5qWz67lM2Ntbn2AebPWpyN+Rs/+xhgPjyoDwCAhzx4UNFzV1/K3YPXsrqynM2Ntbd9gNE0fYD5sxZnY/7Gzz4GmA+hMgAAX+XS+mquvHgrSfLCsxePrQ8wf9bibMzf+NnHALNz+wsAAAAAADoTKgMAAAAA0JlQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ09MVSuqk9V1atV9YU3tf2mqvpMVf2r+39+/fGWCQAAAADAEJzp8J5PJ/nrSf7Wm9o+luQnW2ufrKqP3X/9F+ZfHgAAY3d9Zy9b27u5vT/J+ZXlbG6s5dL66iD6TKvP+oa+XX0aen1DN8b5G+M2nQTm/dBJOK9PY+ifwUA/nhgqt9Z+uqqefkvzh5J82/2//0iSn4pQGQCAI7q+s5fL125mcu8gSbK3P8nlazeT5LFfNvvqM60+6xv6dvVp6PUN3Rjnb4zbdBKY90Mn4bw+jaF/BgP9mfaeyr+5tfblJLn/5zfMryQAAE6Lre3d179kPjC5d5Ct7d2F95lWn/UNfbv6NPT6hm6M8zfGbToJzPuhk3Ben8bQP4OB/hz7g/qq6pmqulFVN+7cuXPcwwEAcILc3p8cqb3PPtPqs76hb1efhl7f0I1x/sa4TSeBeT90Es7r0xj6ZzDQn2lD5X9TVd+UJPf/fPVxb2ytPd9au9Bau3Du3LkphwMAYIzOrywfqb3PPtPqs76hb1efhl7f0I1x/sa4TSeBeT90Es7r0xj6ZzDQn2lD5R9P8tH7f/9okr83n3IAADhNNjfWsnx26aG25bNL2dxYW3ifafVZ39C3q09Dr2/oxjh/Y9ymk8C8HzoJ5/VpDP0zGOjPEx/UV1VXcvhQvqeq6pUkH0/yySQ/VlV/NMmtJH/4OIsEAGCcHjyg57mrL+XuwWtZ7fBE+L76TKvP+oa+XX0aen1DN8b5G+M2nQTm/dBJOK9PY+ifwUB/nhgqt9Y+/JgffWDOtQAAcApdWl/NlRdvJUleePbioPpMq8/6hr5dfRp6fUM3xvkb4zadBOb90Ek4r09j6J/BQD+O/UF9AAAAAACMh1AZAAAAAIDOhMoAAAAAAHQmVAYAAAAAoDOhMgAAAAAAnQmVAQAAAADoTKgMAAAAAEBnZxZdADA813f2srW9m9v7k5xfWc7mxloura8uuizgGI1x3fe5TX2NNcb9BI8yxvU7rTHWN+02DX0u+mJ9vMFczGaM2wT0R6gMPOT6zl4uX7uZyb2DJMne/iSXr91MEhcYMFJjXPd9blNfY41xP8GjjHH9TmuM9U27TUOfi75YH28wF7MZ4zYB/XL7C+AhW9u7r19YPDC5d5Ct7d0FVQQctzGu+z63qa+xxrif4FHGuH6nNcb6pt2moc9FX6yPN5iL2Yxxm4B+CZWBh9zenxypHTj5xrju+9ymvsYa436CRxnj+p3WGOubdpuGPhd9sT7eYC5mM8ZtAvolVAYecn5l+UjtwMk3xnXf5zb1NdYY9xM8yhjX77TGWN+02zT0ueiL9fEGczGbMW4T0C+hMvCQzY21LJ9deqht+exSNjfWFlQRcNzGuO773Ka+xhrjfoJHGeP6ndYY65t2m4Y+F32xPt5gLmYzxm0C+uVBfcBDHjyU4bmrL+XuwWtZ9RRgGL0xrvs+t6mvsca4n+BRxrh+pzXG+qbdpqHPRV+sjzeYi9mMcZuAfgmVga9yaX01V168lSR54dmLC64G6MMY132f29TXWGPcT/AoY1y/0xpjfdNu09Dnoi/WxxvMxWzGuE1Af9z+AgAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZ0JlAAAAAAA6mylUrqo/W1VfrKovVNWVqvqN8yoMAAAAAIDhOTNtx6paTfKnk7yvtTapqh9L8j1JPj2n2gB4gus7e9na3s3t/UnOryxnc2Mtl9ZXF10WPNa0x2xfx3qfa8r6hdPHuh83+5dFcvwBfZs6VH5T/+WqupfknUluz14SAF1c39nL5Ws3M7l3kCTZ25/k8rWbSeICkkGa9pjt61jvc01Zv3D6WPfjZv+ySI4/YBGmvv1Fa20vyV9OcivJl5P829baT8yrMADe3tb27usXjg9M7h1ka3t3QRXB25v2mO3rWO9zTVm/cPpY9+Nm/7JIjj9gEaYOlavq65N8KMl7k5xP8jVV9ZFHvO+ZqrpRVTfu3LkzfaUAPOT2/uRI7bBo0x6zfR3rfa4p6xdOH+t+3OxfFsnxByzCLA/q+/1J/nVr7U5r7V6Sa0l+71vf1Fp7vrV2obV24dy5czMMB8CbnV9ZPlI7LNq0x2xfx3qfa8r6hdPHuh83+5dFcvwBizBLqHwrybdW1TurqpJ8IMnL8ykLgCfZ3FjL8tmlh9qWzy5lc2NtQRXB25v2mO3rWO9zTVm/cPpY9+Nm/7JIjj9gEaZ+UF9r7XNVdTXJzyT5SpKdJM/PqzAA3t6Dh248d/Wl3D14Laue8szATXvM9nWs97mmrF84faz7cbN/WSTHH7AIU4fKSdJa+3iSj8+pFgCO6NL6aq68eCtJ8sKzFxdcDTzZtMdsX8d6n2vK+oXTx7ofN/uXRXL8AX2b5fYXAAAAAACcMkJlAAAAAAA6EyoDAAAAANCZUBkAAAAAgM6EygAAAAAAdCZUBgAAAACgM6EyAAAAAACdnVl0AcDxur6zl63t3dzen+T8ynI2N9ZyaX11MOMMvT4Omb/FMO8AwGnhuod5GeN3TOuDIRIqw4hd39nL5Ws3M7l3kCTZ25/k8rWbSTLXD6Bpxxl6fRwyf4th3gGA08J1D/Myxu+Y1gdD5fYXMGJb27uvf/A8MLl3kK3t3UGMM/T6OGT+FsO8AwCnhese5mWM3zGtD4ZKqAwjdnt/cqT2vscZen0cMn+LYd4BgNPCdQ/zMsbvmNYHQyVUhhE7v7J8pPa+xxl6fRwyf4th3gGA08J1D/Myxu+Y1gdDJVSGEdvcWMvy2aWH2pbPLmVzY20Q4wy9Pg6Zv8Uw7wDAaeG6h3kZ43dM64Oh8qA+GLEHN+1/7upLuXvwWlaP6Smx044z9Po4ZP4Ww7wDAKeF6x7mZYzfMa0PhkqoDCN3aX01V168lSR54dmLgxtn6PVxyPwthnkHAE4L1z3Myxi/Y1ofDJHbXwAAAAAA0JlQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZzOFylW1UlVXq+rnqurlqro4r8IAAAAAABieMzP2/2tJ/lFr7bur6h1J3jmHmmAhru/sZWt7N7f3Jzm/spzNjbVcWl89tn591cdsppn3vvpMa+hjjfFYH/qcAwCL4XMbHm+s19DWPWMxdahcVV+X5Pcl+Z+SpLV2N8nd+ZQF/bq+s5fL125mcu8gSbK3P8nlazeT5G1P7tP266s+ZjPNvPfVZ1pDH2uMx/rQ5xwAWAyf2/B4Y72Gtu4Zk1luf/FbktxJ8sNVtVNVP1hVXzOnuqBXW9u7r5/UH5jcO8jW9u6x9OurPmYzzbz31WdaQx9rjMf60OccAFgMn9vweGO9hrbuGZNZQuUzSX53kr/ZWltP8u+TfOytb6qqZ6rqRlXduHPnzgzDwfG5vT85Uvus/Y6qr3F42DTz3lefaQ19rDEe60OfcwBgMXxuw+ON9RraumdMZgmVX0nySmvtc/dfX81hyPyQ1trzrbULrbUL586dm2E4OD7nV5aP1D5rv6PqaxweNs2899VnWkMfa4zH+tDnHABYDJ/b8HhjvYa27hmTqUPl1tovJ/mlqlq73/SBJD87l6qgZ5sba1k+u/RQ2/LZpWxurD2mx2z9+qqP2Uwz7331mdbQxxrjsT70OQcAFsPnNjzeWK+hrXvGZOoH9d33fUl+tKrekeQXknzv7CVB/x7cEP+5qy/l7sFrWe34BNZp+/VVH7OZZt776jOtoY81xmN96HMOACyGz214vLFeQ1v3jMlMoXJr7V8kuTCfUmCxLq2v5sqLt5IkLzx78dj7HVVf4/Cwaea9rz7TGvpYYzzWhz7nAMBi+NyGxxvrNbR1z1jMck9lAAAAAABOGaEyAAAAAACdCZUBAAAAAOhMqAwAAAAAQGdCZQAAAAAAOhMqAwAAAADQmVAZAAAAAIDOziy6ABbr+s5etrZ3c3t/kvMry9ncWMul9dVj6TftWGNkLmC++lxT1u8bzAUAAAxTn3lPn/UxHELlU+z6zl4uX7uZyb2DJMne/iSXr91MkrddyNP0m3asMTIXMF99rinr9w3mAgAAhqnPvKfP+hgWt784xba2d19fwA9M7h1ka3t37v2mHWuMzAXMV59ryvp9g7kAAIBh6jPv6bM+hkWofIrd3p8cqX2WftOONUbmAuarzzVl/b7BXAAAwDD1mfdMw3eJcRAqn2LnV5aP1D5Lv2nHGiNzAfPV55qyft9gLgAAYJj6zHum4bvEOAiVT7HNjbUsn116qG357FI2N9bm3m/ascbIXMB89bmmrN83mAsAABimPvOePutjWDyo7xR7cPPz566+lLsHr2W149M2p+k37VhjZC5gvvpcU9bvG8wFAAAMU595T5/1MSxC5VPu0vpqrrx4K0nywrMXj7XftGONkbmA+epzTVm/bzAXAAAwTH3mPdPwXeLkc/sLAAAAAAA6EyoDAAAAANCZUBkAAAAAgM6EygAAAAAAdCZUBgAAAACgM6EyAAAAAACdCZUBAAAAAOhs5lC5qpaqaqeq/v48CgIAAAAAYLjOzOHf+P4kLyf5ujn8W8zg+s5etrZ3c3t/kvMry9ncWMul9dVFlwUAAADASA09jxp6fSfVTL+pXFXvSvKHkvzgfMphWtd39nL52s3s7U/SkuztT3L52s1c39lbdGkAAAAAjNDQ86ih13eSzXr7i7+a5Lkkr81eCrPY2t7N5N7BQ22TewfZ2t5dUEUAAAAAjNnQ86ih13eSTR0qV9V3JHm1tfb5J7zvmaq6UVU37ty5M+1wPMHt/cmR2gEAAABgFkPPo4Ze30k2y28qvz/Jd1bVLyb5O0m+var+9lvf1Fp7vrV2obV24dy5czMMx9s5v7J8pHYAAAAAmMXQ86ih13eSTR0qt9Yut9be1Vp7Osn3JPlsa+0jc6uMI9ncWMvy2aWH2pbPLmVzY21BFQEAAAAwZkPPo4Ze30l2ZtEFMB8Pnlr53NWXcvfgtax6miUAAAAAx2joedTQ6zvJ5hIqt9Z+KslPzePfYnqX1ldz5cVbSZIXnr244GoAAAAAGLuh51FDr++kmuWeygAAAAAAnDJCZQAAAAAAOhMqAwAAAADQmVAZAAAAAIDOhMoAAAAAAHQmVAYAAAAAoDOhMgAAAAAAnZ1ZdAFjd31nL1vbu7m9P8n5leVsbqzl0vrqsfUbG/PHo9i/AAAAwHGSPbw9ofIxur6zl8vXbmZy7yBJsrc/yeVrN5PkbQ/CafuNjfnjUexfAAAA4DjJHp7M7S+O0db27usH3wOTewfZ2t49ln5jY/54FPsXAAAAOE6yhycTKh+j2/uTI7XP2m9szB+PYv8CAAAAx0n28GRC5WN0fmX5SO2z9hsb88ej2L8AAADAcZI9PJlQ+Rhtbqxl+ezSQ23LZ5eyubF2LP3GxvzxKPYvAAAAcJxkD0/mQX3H6MGNu5+7+lLuHryW1Y5Pipy239iYPx7F/gUAAACOk+zhyYTKx+zS+mquvHgrSfLCsxePvd/YmD8exf4FAAAAjpPs4e25/QUAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZ0JlAAAAAAA6EyoDAAAAANCZUBkAAAAAgM6EygAAAAAAdDZ1qFxV766qf1xVL1fVF6vq++dZGAAAAAAAw3Nmhr5fSfLnWms/U1X/cZLPV9VnWms/O6faAOBYXd/Zy9b2bm7vT3J+ZTmbG2u5tL666LIAAAB4BN/hhmPqULm19uUkX77/939XVS8nWU0iVAZg8K7v7OXytZuZ3DtIkuztT3L52s0kcVECAAAwML7DDctc7qlcVU8nWU/yuXn8ewBw3La2d1+/GHlgcu8gW9u7C6oIAACAx/EdblhmDpWr6muT/N0kf6a19muP+PkzVXWjqm7cuXNn1uEAYC5u70+O1A4AAMDi+A43LDOFylV1NoeB8o+21q496j2ttedbaxdaaxfOnTs3y3AAMDfnV5aP1A4AAMDi+A43LFOHylVVSX4oycuttb8yv5IA4Phtbqxl+ezSQ23LZ5eyubG2oIoAAAB4HN/hhmWW31R+f5I/kuTbq+pf3P/vD86pLgA4VpfWV/OJ7/qWvGPp8KNwdWU5n/iub/GABwAAgAHyHW5YzkzbsbX2T5PUHGsBgF5dWl/NlRdvJUleePbigqsBAADg7fgONxwzP6gPAAAAAIDTQ6gMAAAAAEBnQmUAAAAAADoTKgMAAAAA0JlQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6EyoDAAAAABAZ0JlAAAAAAA6EyoDAAAAANCZUBkAAAAAgM6EygAAAAAAdCZUBgAAAACgM6EyAAAAAACdCZUBAAAAAOhMqAwAAAAAQGdCZQAAAAAAOhMqAwAAAADQmVAZAAAAAIDOhMoAAAAAAHQ2U6hcVR+sqt2q+vmq+ti8igIAAAAAYJimDpWrainJ30jyB5K8L8mHq+p98yoMAAAAAIDhqdbadB2rLib5X1prG/dfX06S1tonHtfnwoUL7caNG1ONd5L98Ie/L99455fyvm/6uiP1+9kv/1qSHKlfX336HGvo9fU5lvr679PnWOrrv0+fYw29vj7HUl//ffocS3399xnrWOrrv0+fY6mv/z5jHUt9/ffpcyz19d+nz7GGXt+Dfr987t353iv/55H6jUVVfb61duGRP5shVP7uJB9srf2x+6//SJLf01r7U2953zNJnkmS97znPf/Fl770panGO8l++S/9pfz6yz+36DIAAAAAgCP4Db/9t+Ubf+AHFl3GQrxdqHxmln/3EW1flVC31p5P8nxy+JvKM4x3Yp3WAw8AAAAAGJ9ZHtT3SpJ3v+n1u5Lcnq0cAAAAAACGbJZQ+Z8n+eaqem9VvSPJ9yT58fmUBQAAAADAEE19+4vW2leq6k8l2U6ylORTrbUvzq0yAAAAAAAGZ5Z7Kqe19g+T/MM51QIAAAAAwMDNcvsLAAAAAABOGaEyAAAAAACdCZUBAAAAAOhMqAwAAAAAQGdCZQAAAAAAOhMqAwAAAADQmVAZAAAAAIDOqrXW32BVd5J8qbcBh+WpJL+y6CKAwXOuALpwrgC6cK4AunCuAB7nP22tnXvUD3oNlU+zqrrRWruw6DqAYXOuALpwrgC6cK4AunCuAKbh9hcAAAAAAHQmVAYAAAAAoDOhcn+eX3QBwIngXAF04VwBdOFcAXThXAEcmXsqAwAAAADQmd9UBgAAAACgM6FyD6rqg1W1W1U/X1UfW3Q9wDBU1bur6h9X1ctV9cWq+v777b+pqj5TVf/q/p9fv+hagcWqqqWq2qmqv3//tfME8FWqaqWqrlbVz92/vrjofAG8VVX92fvfP75QVVeq6jc6VwBHJVQ+ZlW1lORvJPkDSd6X5MNV9b7FVgUMxFeS/LnW2m9P8q1J/uT988PHkvxka+2bk/zk/dfA6fb9SV5+02vnCeBR/lqSf9Ra+21J/vMcnjecL4DXVdVqkj+d5EJr7XckWUryPXGuAI5IqHz8/qskP99a+4XW2t0kfyfJhxZcEzAArbUvt9Z+5v7f/10Ov/it5vAc8SP33/YjSS4tpEBgEKrqXUn+UJIffFOz8wTwkKr6uiS/L8kPJUlr7W5rbT/OF8BXO5NkuarOJHlnkttxrgCOSKh8/FaT/NKbXr9yvw3gdVX1dJL1JJ9L8ptba19ODoPnJN+wwNKAxfurSZ5L8tqb2pwngLf6LUnuJPnh+7fL+cGq+po4XwBv0lrbS/KXk9xK8uUk/7a19hNxrgCOSKh8/OoRba33KoDBqqqvTfJ3k/yZ1tqvLboeYDiq6juSvNpa+/yiawEG70yS353kb7bW1pP8+/jf14G3uH+v5A8leW+S80m+pqo+stiqgJNIqHz8Xkny7je9flcO/9cSgFTV2RwGyj/aWrt2v/nfVNU33f/5NyV5dVH1AQv3/iTfWVW/mMNbaH17Vf3tOE8AX+2VJK+01j53//XVHIbMzhfAm/3+JP+6tXantXYvybUkvzfOFcARCZWP3z9P8s1V9d6qekcOb4D/4wuuCRiAqqoc3vfw5dbaX3nTj348yUfv//2jSf5e37UBw9Bau9xae1dr7ekcXkN8trX2kThPAG/RWvvlJL9UVWv3mz6Q5GfjfAE87FaSb62qd97/PvKBHD7bxbkCOJJqzZ0YjltV/cEc3g9xKcmnWmv/22IrAoagqv7rJP8kyc28ca/UH8jhfZV/LMl7cnjR94dba7+6kCKBwaiqb0vy51tr31FV/0mcJ4C3qKrflcOHer4jyS8k+d4c/iKR8wXwuqr6X5P8D0m+kmQnyR9L8rVxrgCOQKgMAAAAAEBnbn8BAAAAAEBnQmUAAAAAADoTKgMAAAAA0JlQGQAAAACAzoTKAAAAAAB0JlQGAAAAAKAzoTIAAAAAAJ0JlQEAAAAA6Ow/AF/VxqC7bv8qAAAAAElFTkSuQmCC\n", + "image/png": "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\n", "text/plain": [ - "
" + "
" ] }, "metadata": { @@ -65,47 +166,57 @@ } ], "source": [ - "# Create test data\n", - "seq = np.unpackbits(np.array([0xbe, 0xef], dtype=np.dtype(\"uint8\")))\n", - "stream = np.concatenate([\n", - " np.random.randint(low=0, high=2, size=32), seq, np.random.randint(low=0, high=2, size=32)\n", - "])\n", - "\n", - "print(f\"Header (N={len(seq)}): {seq}\")\n", - "print(f\"Stream (N={len(stream)}): {stream}\")\n", - "\n", - "# Create buffers for cross correlation\n", - "fifo = RingBuffer(len(seq), dtype=np.dtype(\"uint8\"))\n", - "xcorr = RingBuffer(len(stream) + len(seq), dtype=np.dtype(\"uint8\"))\n", + "fig, (left, right) = plt.subplots(1, 2, figsize = (20, 4))\n", "\n", - "## fill FIFO with zeros\n", - "fifo.extend(np.zeros(fifo.maxlen))\n", + "left.plot(np.real(syms), np.imag(syms))\n", + "left.set_title(\"AC Constellation Diagram\")\n", "\n", - "def correlation(v):\n", - " n = len(seq)\n", - " d = np.logical_xor(v, seq) # or bitwise_xor, no difference in this case\n", - " return n - sum(d)\n", - " \n", - "for i in range(len(stream) + len(seq) + 1):\n", - " xcorr.append(correlation(np.array(fifo)))\n", - " \n", - " # append stream data\n", - " # if the stream is finished use zeros\n", - " fifo.append(stream[i] if i < len(stream) else 0)\n", + "xc = np.convolve(fir_syms, syms)\n", + "right.plot(np.abs(xc))\n", + "right.set_title(\"AC Autocorrelation\")\n", "\n", - "# unwrap values\n", - "xc = np.array(xcorr)\n", - "# print(f\"Cross correlation: {xc}\")\n", - "print(f\"Correlation peak value: {np.amax(xc)} at i={np.argmax(xc)}\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "bc633da5-61b7-4569-adaf-3d019e0f1b17", + "metadata": {}, + "source": [ + "## Symbols for 16-QAM" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b56d782d-fcea-4fb9-b0c1-9c3933d5ce6c", + "metadata": {}, + "outputs": [], + "source": [ + "def modulate_16qam(m):\n", + " sym = {}\n", + " return map(lambda k: sym[k] if k in sym.keys() else None, m)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "2fb687aa-0061-4626-bf6b-3ec7d628d739", + "metadata": {}, + "outputs": [], + "source": [ + "# convert into chunks of 4 bits for QPSK\n", + "# chunks = list(np.matmul(np.array(ac_pad).reshape((-1,4)), np.array([4, 3, 2, 1])))\n", + "# syms = list(modulate_16qam(chunks))\n", "\n", - "plt.figure(figsize = (25, 5))\n", - "plt.stem(xc)" + "# print(chunks)\n", + "# print(syms)" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -119,7 +230,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.11" } }, "nbformat": 4, diff --git a/tests/correlator/correlator.grc b/tests/correlator/correlator.grc index 723a99d..9deec54 100644 --- a/tests/correlator/correlator.grc +++ b/tests/correlator/correlator.grc @@ -1,6 +1,7 @@ options: parameters: author: Naoki Pross + catch_exceptions: 'True' category: '[GRC Hier Blocks]' cmake_opt: '' comment: '' @@ -22,7 +23,6 @@ options: sizing_mode: fixed thread_safe_setters: '' title: Correlator Test - window_size: '' states: bus_sink: false bus_source: false @@ -32,6 +32,21 @@ options: state: enabled blocks: +- name: access_code_symbols + id: variable + parameters: + comment: '' + value: '[(-1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), + (1.4142135623730951+1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), + (-1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), + (1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j)]' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [776, 1140.0] + rotation: 0 + state: enabled - name: access_code_symbols_sps id: variable parameters: @@ -50,13 +65,14 @@ blocks: bus_structure: null coordinate: [224, 132.0] rotation: 0 - state: true + state: enabled - name: const id: variable_constellation parameters: comment: '' const_points: '[-1-1j, -1+1j, 1+1j, 1-1j]' dims: '1' + normalization: digital.constellation.AMPLITUDE_NORMALIZATION precision: '8' rot_sym: '4' soft_dec_lut: None @@ -97,13 +113,15 @@ blocks: id: variable parameters: comment: '' - value: '[(1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), - (-1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197-1.4142197j)]' + value: '[(1.4142135623730951+1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), + (1.4142135623730951-1.4142135623730951j), (-1.4142135623730951+1.4142135623730951j), + (1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), + (1.4142135623730951+1.4142135623730951j), (-1.4142135623730951+1.4142135623730951j)]' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [992, 620.0] + coordinate: [48, 564.0] rotation: 0 state: enabled - name: rrc_taps @@ -146,7 +164,7 @@ blocks: id: variable parameters: comment: '' - value: '[31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23]' + value: '[31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23, 0x41, 0xf3]' states: bus_sink: false bus_source: false @@ -166,6 +184,42 @@ blocks: coordinate: [224, 1068.0] rotation: 0 state: enabled +- name: blocks_burst_tagger_0 + id: blocks_burst_tagger + parameters: + affinity: '' + alias: '' + comment: '' + false_key: nothing + false_value: '0' + maxoutbuf: '0' + minoutbuf: '0' + true_key: peak + true_value: 'True' + type: float + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [944, 1580.0] + rotation: 0 + state: disabled +- name: blocks_char_to_short_0 + id: blocks_char_to_short + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [752, 1624.0] + rotation: 0 + state: disabled - name: blocks_complex_to_magphase_0 id: blocks_complex_to_magphase parameters: @@ -179,7 +233,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1008, 696.0] + coordinate: [1048, 696.0] rotation: 0 state: enabled - name: blocks_complex_to_magphase_0_0 @@ -195,7 +249,23 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1072, 1112.0] + coordinate: [1048, 1104.0] + rotation: 0 + state: enabled +- name: blocks_complex_to_magphase_0_1 + id: blocks_complex_to_magphase + parameters: + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + vlen: '1' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [256, 1592.0] rotation: 0 state: disabled - name: blocks_multiply_const_vxx_0 @@ -213,27 +283,28 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1280, 788.0] + coordinate: [1288, 724.0] rotation: 0 state: enabled -- name: blocks_null_sink_0 - id: blocks_null_sink +- name: blocks_multiply_const_vxx_0_0 + id: blocks_multiply_const_vxx parameters: affinity: '' alias: '' - bus_structure_sink: '[[0,],]' comment: '' - num_inputs: '1' - type: byte + const: 180 / 3.141592653589793 + maxoutbuf: '0' + minoutbuf: '0' + type: float vlen: '1' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [1056, 840.0] + coordinate: [1336, 1180.0] rotation: 0 - state: enabled -- name: blocks_null_sink_0_0 + state: disabled +- name: blocks_null_sink_0 id: blocks_null_sink parameters: affinity: '' @@ -241,16 +312,16 @@ blocks: bus_structure_sink: '[[0,],]' comment: '' num_inputs: '1' - type: complex + type: byte vlen: '1' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [1080, 1048.0] + coordinate: [1504, 1696.0] rotation: 0 state: disabled -- name: blocks_null_sink_1_0 +- name: blocks_null_sink_2 id: blocks_null_sink parameters: affinity: '' @@ -264,10 +335,10 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1336, 1176.0] + coordinate: [528, 1720.0] rotation: 0 state: disabled -- name: blocks_null_sink_1_0_0 +- name: blocks_null_sink_3 id: blocks_null_sink parameters: affinity: '' @@ -281,9 +352,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1288, 856.0] + coordinate: [1336, 1232.0] rotation: 0 - state: disabled + state: true - name: blocks_null_source_0 id: blocks_null_source parameters: @@ -303,13 +374,31 @@ blocks: coordinate: [96, 344.0] rotation: 0 state: enabled +- name: blocks_peak_detector2_fb_0 + id: blocks_peak_detector2_fb + parameters: + affinity: '' + alias: '' + alpha: '0.001' + comment: '' + look_ahead: '1000' + maxoutbuf: '0' + minoutbuf: '0' + threshold_factor_rise: '7' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [528, 1620.0] + rotation: 0 + state: disabled - name: blocks_stream_mux_0 id: blocks_stream_mux parameters: affinity: '' alias: '' comment: '' - lengths: '[15, len(testvec)]' + lengths: '[10, len(testvec)]' maxoutbuf: '0' minoutbuf: '0' num_inputs: '2' @@ -368,10 +457,10 @@ blocks: comment: '' maxoutbuf: '0' minoutbuf: '0' - repeat: 'True' + repeat: 'False' tags: '[]' type: byte - vector: testvec + vector: testvec * 1600 vlen: '1' states: bus_sink: false @@ -408,12 +497,12 @@ blocks: block_tags: 'False' comment: '' epsilon: '1.0' - freq_offset: '0.0' + freq_offset: '0.000001' maxoutbuf: '0' minoutbuf: '0' - noise_voltage: '0.1' + noise_voltage: '0.2' seed: '243' - taps: '1. ' + taps: -1.4 + .4j states: bus_sink: false bus_source: false @@ -453,9 +542,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [800, 836.0] + coordinate: [1232, 1692.0] rotation: 0 - state: enabled + state: disabled - name: digital_constellation_modulator_0 id: digital_constellation_modulator parameters: @@ -469,6 +558,7 @@ blocks: maxoutbuf: '0' minoutbuf: '0' samples_per_symbol: sps + truncate: 'False' verbose: 'False' states: bus_sink: false @@ -487,17 +577,16 @@ blocks: maxoutbuf: '0' minoutbuf: '0' sps: '1' - symbols: '[(1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), - (-1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j)]' - threshold: '.9' + symbols: access_code_symbols + threshold: '.8' threshold_method: digital.THRESHOLD_DYNAMIC states: bus_sink: false bus_source: false bus_structure: null - coordinate: [768, 1076.0] + coordinate: [776, 1020.0] rotation: 0 - state: disabled + state: enabled - name: digital_pfb_clock_sync_xxx_0 id: digital_pfb_clock_sync_xxx parameters: @@ -537,9 +626,57 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [768, 700.0] + coordinate: [776, 828.0] rotation: 0 state: enabled +- name: high_pass_filter_0 + id: high_pass_filter + parameters: + affinity: '' + alias: '' + beta: '6.76' + comment: '' + cutoff_freq: 5e3 + decim: '1' + gain: '1' + interp: '1' + maxoutbuf: '0' + minoutbuf: '0' + samp_rate: samp_rate + type: fir_filter_fff + width: '.7' + win: window.WIN_HAMMING + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [528, 1420.0] + rotation: 0 + state: disabled +- name: low_pass_filter_0 + id: low_pass_filter + parameters: + affinity: '' + alias: '' + beta: '6.76' + comment: '' + cutoff_freq: 7e3 + decim: '1' + gain: '1' + interp: '1' + maxoutbuf: '0' + minoutbuf: '0' + samp_rate: samp_rate + type: fir_filter_fff + width: '.8' + win: window.WIN_HAMMING + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [528, 1252.0] + rotation: 0 + state: disabled - name: qtgui_const_sink_x_0 id: qtgui_const_sink_x parameters: @@ -569,7 +706,7 @@ blocks: color9: '"red"' comment: '' grid: 'False' - gui_hint: '' + gui_hint: 0,1,2,1 label1: '' label10: '' label2: '' @@ -591,7 +728,7 @@ blocks: marker7: '0' marker8: '0' marker9: '0' - name: '""' + name: '"Equalized Signal"' nconnections: '1' size: '1024' style1: '0' @@ -629,15 +766,15 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [768, 620.0] + coordinate: [776, 716.0] rotation: 0 state: enabled -- name: qtgui_time_sink_x_0 - id: qtgui_time_sink_x +- name: qtgui_const_sink_x_1 + id: qtgui_const_sink_x parameters: affinity: '' alias: '' - alpha1: '1.0' + alpha1: '.5' alpha10: '1.0' alpha2: '1.0' alpha3: '1.0' @@ -649,64 +786,59 @@ blocks: alpha9: '1.0' autoscale: 'True' axislabels: 'True' - color1: blue - color10: dark blue - color2: red - color3: green - color4: black - color5: cyan - color6: magenta - color7: yellow - color8: dark red - color9: dark green + color1: '"blue"' + color10: '"red"' + color2: '"red"' + color3: '"red"' + color4: '"red"' + color5: '"red"' + color6: '"red"' + color7: '"red"' + color8: '"red"' + color9: '"red"' comment: '' - ctrlpanel: 'False' - entags: 'True' - grid: 'False' - gui_hint: 2,0,1,1 - label1: Signal 1 - label10: Signal 10 - label2: Signal 2 - label3: Signal 3 - label4: Signal 4 - label5: Signal 5 - label6: Signal 6 - label7: Signal 7 - label8: Signal 8 - label9: Signal 9 + grid: 'True' + gui_hint: 2,1,2,1 + label1: '' + label10: '' + label2: '' + label3: '' + label4: '' + label5: '' + label6: '' + label7: '' + label8: '' + label9: '' legend: 'True' - marker1: '-1' - marker10: '-1' - marker2: '-1' - marker3: '-1' - marker4: '-1' - marker5: '-1' - marker6: '-1' - marker7: '-1' - marker8: '-1' - marker9: '-1' - name: '""' + marker1: '9' + marker10: '0' + marker2: '0' + marker3: '0' + marker4: '0' + marker5: '0' + marker6: '0' + marker7: '0' + marker8: '0' + marker9: '0' + name: '"Cross Correlation"' nconnections: '1' size: '1024' - srate: samp_rate - stemplot: 'False' - style1: '1' - style10: '1' - style2: '1' - style3: '1' - style4: '1' - style5: '1' - style6: '1' - style7: '1' - style8: '1' - style9: '1' + style1: '2' + style10: '0' + style2: '0' + style3: '0' + style4: '0' + style5: '0' + style6: '0' + style7: '0' + style8: '0' + style9: '0' tr_chan: '0' - tr_delay: '0' tr_level: '0.0' tr_mode: qtgui.TRIG_MODE_FREE tr_slope: qtgui.TRIG_SLOPE_POS tr_tag: '""' - type: float + type: complex update_time: '0.10' width1: '1' width10: '1' @@ -718,19 +850,19 @@ blocks: width7: '1' width8: '1' width9: '1' - ylabel: XC Magnitude - ymax: '100' - ymin: '0' - yunit: '""' + xmax: '2' + xmin: '-2' + ymax: '2' + ymin: '-2' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [1280, 676.0] + coordinate: [1048, 612.0] rotation: 0 state: enabled -- name: qtgui_time_sink_x_0_0 - id: qtgui_time_sink_x +- name: qtgui_const_sink_x_2 + id: qtgui_const_sink_x parameters: affinity: '' alias: '' @@ -744,35 +876,418 @@ blocks: alpha7: '1.0' alpha8: '1.0' alpha9: '1.0' - autoscale: 'True' + autoscale: 'False' axislabels: 'True' - color1: blue - color10: dark blue - color2: red - color3: green - color4: black - color5: cyan - color6: magenta - color7: yellow - color8: dark red - color9: dark green + color1: '"blue"' + color10: '"red"' + color2: '"red"' + color3: '"red"' + color4: '"red"' + color5: '"red"' + color6: '"red"' + color7: '"red"' + color8: '"red"' + color9: '"red"' comment: '' - ctrlpanel: 'False' - entags: 'True' grid: 'False' - gui_hint: 2,0,1,1 - label1: Signal 1 - label10: Signal 10 - label2: Signal 2 - label3: Signal 3 - label4: Signal 4 - label5: Signal 5 - label6: Signal 6 - label7: Signal 7 - label8: Signal 8 - label9: Signal 9 - legend: 'True' - marker1: '-1' + gui_hint: '2,1,2,1 ' + label1: '' + label10: '' + label2: '' + label3: '' + label4: '' + label5: '' + label6: '' + label7: '' + label8: '' + label9: '' + legend: 'True' + marker1: '0' + marker10: '0' + marker2: '0' + marker3: '0' + marker4: '0' + marker5: '0' + marker6: '0' + marker7: '0' + marker8: '0' + marker9: '0' + name: '"Phase Correction"' + nconnections: '1' + size: '1024' + style1: '0' + style10: '0' + style2: '0' + style3: '0' + style4: '0' + style5: '0' + style6: '0' + style7: '0' + style8: '0' + style9: '0' + tr_chan: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: complex + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + xmax: '2' + xmin: '-2' + ymax: '2' + ymin: '-2' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1248, 1588.0] + rotation: 0 + state: disabled +- name: qtgui_time_sink_x_0 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: 2,0,1,1 + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: float + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: XC Magnitude + ymax: '20' + ymin: '0' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1320, 604.0] + rotation: 0 + state: enabled +- name: qtgui_time_sink_x_0_0 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: '' + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: float + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: XC Magnitude + ymax: '20' + ymin: '0' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1336, 1084.0] + rotation: 0 + state: enabled +- name: qtgui_time_sink_x_0_0_0 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: '' + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: complex + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: XC Magnitude + ymax: '2' + ymin: '-2' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1048, 1020.0] + rotation: 0 + state: enabled +- name: qtgui_time_sink_x_1 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'False' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: '' + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' marker10: '-1' marker2: '-1' marker3: '-1' @@ -815,15 +1330,15 @@ blocks: width7: '1' width8: '1' width9: '1' - ylabel: Cross Correlation Magnitude - ymax: '100' - ymin: '0' + ylabel: Amplitude + ymax: '20' + ymin: '-5' yunit: '""' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [1336, 1092.0] + coordinate: [784, 1452.0] rotation: 0 state: disabled - name: qtgui_time_sink_x_1_0 @@ -920,9 +1435,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [992, 196.0] + coordinate: [808, 452.0] rotation: 0 - state: disabled + state: enabled - name: qtgui_time_sink_x_1_1 id: qtgui_time_sink_x parameters: @@ -953,7 +1468,7 @@ blocks: comment: '' ctrlpanel: 'False' entags: 'True' - grid: 'False' + grid: 'True' gui_hint: 1,0,1,1 label1: Signal 1 label10: Signal 10 @@ -1017,7 +1532,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [800, 932.0] + coordinate: [776, 612.0] rotation: 0 state: enabled - name: qtgui_time_sink_x_2 @@ -1114,9 +1629,106 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1456, 772.0] + coordinate: [1480, 708.0] rotation: 0 state: enabled +- name: qtgui_time_sink_x_2_0 + id: qtgui_time_sink_x + parameters: + affinity: '' + alias: '' + alpha1: '1.0' + alpha10: '1.0' + alpha2: '1.0' + alpha3: '1.0' + alpha4: '1.0' + alpha5: '1.0' + alpha6: '1.0' + alpha7: '1.0' + alpha8: '1.0' + alpha9: '1.0' + autoscale: 'True' + axislabels: 'True' + color1: blue + color10: dark blue + color2: red + color3: green + color4: black + color5: cyan + color6: magenta + color7: yellow + color8: dark red + color9: dark green + comment: '' + ctrlpanel: 'False' + entags: 'True' + grid: 'False' + gui_hint: '' + label1: Signal 1 + label10: Signal 10 + label2: Signal 2 + label3: Signal 3 + label4: Signal 4 + label5: Signal 5 + label6: Signal 6 + label7: Signal 7 + label8: Signal 8 + label9: Signal 9 + legend: 'True' + marker1: '-1' + marker10: '-1' + marker2: '-1' + marker3: '-1' + marker4: '-1' + marker5: '-1' + marker6: '-1' + marker7: '-1' + marker8: '-1' + marker9: '-1' + name: '""' + nconnections: '1' + size: '1024' + srate: samp_rate + stemplot: 'False' + style1: '1' + style10: '1' + style2: '1' + style3: '1' + style4: '1' + style5: '1' + style6: '1' + style7: '1' + style8: '1' + style9: '1' + tr_chan: '0' + tr_delay: '0' + tr_level: '0.0' + tr_mode: qtgui.TRIG_MODE_FREE + tr_slope: qtgui.TRIG_SLOPE_POS + tr_tag: '""' + type: float + update_time: '0.10' + width1: '1' + width10: '1' + width2: '1' + width3: '1' + width4: '1' + width5: '1' + width6: '1' + width7: '1' + width8: '1' + width9: '1' + ylabel: XC Phase + ymax: '1' + ymin: '-1' + yunit: '""' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1512, 1164.0] + rotation: 0 + state: disabled - name: root_raised_cosine_filter_0 id: root_raised_cosine_filter parameters: @@ -1131,7 +1743,7 @@ blocks: minoutbuf: '0' ntaps: 11*samp_rate samp_rate: samp_rate - sym_rate: '1' + sym_rate: sps * samp_rate type: fir_filter_ccf states: bus_sink: false @@ -1153,6 +1765,32 @@ blocks: coordinate: [1480, 404.0] rotation: 0 state: enabled +- name: virtual_sink_1 + id: virtual_sink + parameters: + alias: '' + comment: '' + stream_id: symbols + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [776, 916.0] + rotation: 0 + state: true +- name: virtual_sink_2 + id: virtual_sink + parameters: + alias: '' + comment: '' + stream_id: xcorrelation + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [1080, 836.0] + rotation: 0 + state: true - name: virtual_source_0 id: virtual_source parameters: @@ -1163,39 +1801,78 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [8, 884.0] + coordinate: [32, 884.0] rotation: 0 state: enabled +- name: virtual_source_2 + id: virtual_source + parameters: + alias: '' + comment: '' + stream_id: symbols + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [32, 1716.0] + rotation: 0 + state: disabled +- name: virtual_source_3 + id: virtual_source + parameters: + alias: '' + comment: '' + stream_id: xcorrelation + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [32, 1604.0] + rotation: 0 + state: disabled connections: +- [blocks_char_to_short_0, '0', blocks_burst_tagger_0, '1'] - [blocks_complex_to_magphase_0, '0', qtgui_time_sink_x_0, '0'] - [blocks_complex_to_magphase_0, '1', blocks_multiply_const_vxx_0, '0'] -- [blocks_complex_to_magphase_0, '1', blocks_null_sink_1_0_0, '0'] - [blocks_complex_to_magphase_0_0, '0', qtgui_time_sink_x_0_0, '0'] -- [blocks_complex_to_magphase_0_0, '1', blocks_null_sink_1_0, '0'] +- [blocks_complex_to_magphase_0_0, '1', blocks_multiply_const_vxx_0_0, '0'] +- [blocks_complex_to_magphase_0_0, '1', blocks_null_sink_3, '0'] +- [blocks_complex_to_magphase_0_1, '0', blocks_burst_tagger_0, '0'] +- [blocks_complex_to_magphase_0_1, '0', blocks_peak_detector2_fb_0, '0'] +- [blocks_complex_to_magphase_0_1, '0', high_pass_filter_0, '0'] +- [blocks_complex_to_magphase_0_1, '0', low_pass_filter_0, '0'] +- [blocks_complex_to_magphase_0_1, '1', blocks_null_sink_2, '0'] - [blocks_multiply_const_vxx_0, '0', qtgui_time_sink_x_2, '0'] +- [blocks_multiply_const_vxx_0_0, '0', qtgui_time_sink_x_2_0, '0'] - [blocks_null_source_0, '0', blocks_stream_mux_0, '0'] +- [blocks_peak_detector2_fb_0, '0', blocks_char_to_short_0, '0'] - [blocks_stream_mux_0, '0', digital_constellation_modulator_0, '0'] - [blocks_stream_mux_1, '0', channels_channel_model_0, '0'] -- [blocks_stream_mux_1, '0', qtgui_time_sink_x_1_0, '0'] - [blocks_throttle_0, '0', virtual_sink_0, '0'] - [blocks_vector_source_x_0, '0', blocks_stream_mux_0, '1'] - [blocks_vector_source_x_1, '0', root_raised_cosine_filter_0, '0'] - [channels_channel_model_0, '0', blocks_throttle_0, '0'] -- [digital_cma_equalizer_cc_0, '0', digital_constellation_decoder_cb_0, '0'] - [digital_cma_equalizer_cc_0, '0', digital_corr_est_cc_0, '0'] - [digital_cma_equalizer_cc_0, '0', fir_filter_xxx_1, '0'] - [digital_cma_equalizer_cc_0, '0', qtgui_const_sink_x_0, '0'] - [digital_cma_equalizer_cc_0, '0', qtgui_time_sink_x_1_1, '0'] +- [digital_cma_equalizer_cc_0, '0', virtual_sink_1, '0'] - [digital_constellation_decoder_cb_0, '0', blocks_null_sink_0, '0'] - [digital_constellation_modulator_0, '0', blocks_stream_mux_1, '1'] - [digital_constellation_modulator_0, '0', channels_channel_model_0, '0'] -- [digital_corr_est_cc_0, '0', blocks_null_sink_0_0, '0'] +- [digital_constellation_modulator_0, '0', qtgui_time_sink_x_1_0, '0'] +- [digital_corr_est_cc_0, '0', qtgui_time_sink_x_0_0_0, '0'] - [digital_corr_est_cc_0, '1', blocks_complex_to_magphase_0_0, '0'] - [digital_pfb_clock_sync_xxx_0, '0', digital_cma_equalizer_cc_0, '0'] - [fir_filter_xxx_1, '0', blocks_complex_to_magphase_0, '0'] +- [fir_filter_xxx_1, '0', qtgui_const_sink_x_1, '0'] +- [fir_filter_xxx_1, '0', virtual_sink_2, '0'] +- [high_pass_filter_0, '0', qtgui_time_sink_x_1, '0'] +- [low_pass_filter_0, '0', qtgui_time_sink_x_1, '0'] - [root_raised_cosine_filter_0, '0', blocks_stream_mux_1, '0'] - [virtual_source_0, '0', digital_pfb_clock_sync_xxx_0, '0'] +- [virtual_source_3, '0', blocks_complex_to_magphase_0_1, '0'] metadata: file_format: 1 diff --git a/tests/correlator/correlator.py b/tests/correlator/correlator.py index 15d9c9c..376d061 100755 --- a/tests/correlator/correlator.py +++ b/tests/correlator/correlator.py @@ -7,7 +7,7 @@ # GNU Radio Python Flow Graph # Title: Correlator Test # Author: Naoki Pross -# GNU Radio version: 3.8.2.0 +# GNU Radio version: 3.9.2.0 from distutils.version import StrictVersion @@ -30,18 +30,21 @@ from gnuradio import channels from gnuradio import digital from gnuradio import filter from gnuradio import gr +from gnuradio.fft import window import sys import signal from argparse import ArgumentParser from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation + + from gnuradio import qtgui class correlator(gr.top_block, Qt.QWidget): def __init__(self): - gr.top_block.__init__(self, "Correlator Test") + gr.top_block.__init__(self, "Correlator Test", catch_exceptions=True) Qt.QWidget.__init__(self) self.setWindowTitle("Correlator Test") qtgui.util.check_set_qss() @@ -78,12 +81,13 @@ class correlator(gr.top_block, Qt.QWidget): self.nfilts = nfilts = 32 self.excess_bw = excess_bw = .35 self.timing_loop_bw = timing_loop_bw = 2 * 3.141592653589793 / 100 - self.testvec = testvec = [31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23] + self.testvec = testvec = [31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23, 0x41, 0xf3] self.samp_rate = samp_rate = 32000 self.rrc_taps = rrc_taps = firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), excess_bw, 45*nfilts) - self.revconj_access_code_symbols = revconj_access_code_symbols = [(1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), (-1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197-1.4142197j)] + self.revconj_access_code_symbols = revconj_access_code_symbols = [(1.4142135623730951+1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (-1.4142135623730951+1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (-1.4142135623730951+1.4142135623730951j)] self.const = const = digital.constellation_qpsk().base() self.access_code_symbols_sps = access_code_symbols_sps = [(1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (-1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197-1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j), (1.4142197+1.4142197j)] + self.access_code_symbols = access_code_symbols = [(-1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (-1.4142135623730951-1.4142135623730951j), (1.4142135623730951+1.4142135623730951j), (1.4142135623730951-1.4142135623730951j), (1.4142135623730951-1.4142135623730951j)] ################################################## # Blocks @@ -92,7 +96,8 @@ class correlator(gr.top_block, Qt.QWidget): 1024, #size samp_rate, #samp_rate "", #name - 1 #number of inputs + 1, #number of inputs + None # parent ) self.qtgui_time_sink_x_2.set_update_time(0.10) self.qtgui_time_sink_x_2.set_y_axis(-1, 1) @@ -143,7 +148,8 @@ class correlator(gr.top_block, Qt.QWidget): 1024, #size samp_rate, #samp_rate "", #name - 1 #number of inputs + 1, #number of inputs + None # parent ) self.qtgui_time_sink_x_1_1.set_update_time(0.10) self.qtgui_time_sink_x_1_1.set_y_axis(-2, 2) @@ -153,7 +159,7 @@ class correlator(gr.top_block, Qt.QWidget): self.qtgui_time_sink_x_1_1.enable_tags(True) self.qtgui_time_sink_x_1_1.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") self.qtgui_time_sink_x_1_1.enable_autoscale(True) - self.qtgui_time_sink_x_1_1.enable_grid(False) + self.qtgui_time_sink_x_1_1.enable_grid(True) self.qtgui_time_sink_x_1_1.enable_axis_labels(True) self.qtgui_time_sink_x_1_1.enable_control_panel(False) self.qtgui_time_sink_x_1_1.enable_stem_plot(False) @@ -193,14 +199,169 @@ class correlator(gr.top_block, Qt.QWidget): self.top_grid_layout.setRowStretch(r, 1) for c in range(0, 1): self.top_grid_layout.setColumnStretch(c, 1) + self.qtgui_time_sink_x_1_0 = qtgui.time_sink_c( + 1024, #size + samp_rate, #samp_rate + "", #name + 1, #number of inputs + None # parent + ) + self.qtgui_time_sink_x_1_0.set_update_time(0.10) + self.qtgui_time_sink_x_1_0.set_y_axis(-2, 2) + + self.qtgui_time_sink_x_1_0.set_y_label('Modulated', "") + + self.qtgui_time_sink_x_1_0.enable_tags(True) + self.qtgui_time_sink_x_1_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") + self.qtgui_time_sink_x_1_0.enable_autoscale(True) + self.qtgui_time_sink_x_1_0.enable_grid(False) + self.qtgui_time_sink_x_1_0.enable_axis_labels(True) + self.qtgui_time_sink_x_1_0.enable_control_panel(False) + self.qtgui_time_sink_x_1_0.enable_stem_plot(False) + + + labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5', + 'Signal 6', 'Signal 7', 'Signal 8', 'Signal 9', 'Signal 10'] + widths = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + colors = ['blue', 'red', 'green', 'black', 'cyan', + 'magenta', 'yellow', 'dark red', 'dark green', 'dark blue'] + alphas = [1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0] + styles = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + markers = [-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1] + + + for i in range(2): + if len(labels[i]) == 0: + if (i % 2 == 0): + self.qtgui_time_sink_x_1_0.set_line_label(i, "Re{{Data {0}}}".format(i/2)) + else: + self.qtgui_time_sink_x_1_0.set_line_label(i, "Im{{Data {0}}}".format(i/2)) + else: + self.qtgui_time_sink_x_1_0.set_line_label(i, labels[i]) + self.qtgui_time_sink_x_1_0.set_line_width(i, widths[i]) + self.qtgui_time_sink_x_1_0.set_line_color(i, colors[i]) + self.qtgui_time_sink_x_1_0.set_line_style(i, styles[i]) + self.qtgui_time_sink_x_1_0.set_line_marker(i, markers[i]) + self.qtgui_time_sink_x_1_0.set_line_alpha(i, alphas[i]) + + self._qtgui_time_sink_x_1_0_win = sip.wrapinstance(self.qtgui_time_sink_x_1_0.pyqwidget(), Qt.QWidget) + self.top_grid_layout.addWidget(self._qtgui_time_sink_x_1_0_win, 0, 0, 1, 1) + for r in range(0, 1): + self.top_grid_layout.setRowStretch(r, 1) + for c in range(0, 1): + self.top_grid_layout.setColumnStretch(c, 1) + self.qtgui_time_sink_x_0_0_0 = qtgui.time_sink_c( + 1024, #size + samp_rate, #samp_rate + "", #name + 1, #number of inputs + None # parent + ) + self.qtgui_time_sink_x_0_0_0.set_update_time(0.10) + self.qtgui_time_sink_x_0_0_0.set_y_axis(-2, 2) + + self.qtgui_time_sink_x_0_0_0.set_y_label('XC Magnitude', "") + + self.qtgui_time_sink_x_0_0_0.enable_tags(True) + self.qtgui_time_sink_x_0_0_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") + self.qtgui_time_sink_x_0_0_0.enable_autoscale(True) + self.qtgui_time_sink_x_0_0_0.enable_grid(False) + self.qtgui_time_sink_x_0_0_0.enable_axis_labels(True) + self.qtgui_time_sink_x_0_0_0.enable_control_panel(False) + self.qtgui_time_sink_x_0_0_0.enable_stem_plot(False) + + + labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5', + 'Signal 6', 'Signal 7', 'Signal 8', 'Signal 9', 'Signal 10'] + widths = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + colors = ['blue', 'red', 'green', 'black', 'cyan', + 'magenta', 'yellow', 'dark red', 'dark green', 'dark blue'] + alphas = [1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0] + styles = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + markers = [-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1] + + + for i in range(2): + if len(labels[i]) == 0: + if (i % 2 == 0): + self.qtgui_time_sink_x_0_0_0.set_line_label(i, "Re{{Data {0}}}".format(i/2)) + else: + self.qtgui_time_sink_x_0_0_0.set_line_label(i, "Im{{Data {0}}}".format(i/2)) + else: + self.qtgui_time_sink_x_0_0_0.set_line_label(i, labels[i]) + self.qtgui_time_sink_x_0_0_0.set_line_width(i, widths[i]) + self.qtgui_time_sink_x_0_0_0.set_line_color(i, colors[i]) + self.qtgui_time_sink_x_0_0_0.set_line_style(i, styles[i]) + self.qtgui_time_sink_x_0_0_0.set_line_marker(i, markers[i]) + self.qtgui_time_sink_x_0_0_0.set_line_alpha(i, alphas[i]) + + self._qtgui_time_sink_x_0_0_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0_0_0.pyqwidget(), Qt.QWidget) + self.top_layout.addWidget(self._qtgui_time_sink_x_0_0_0_win) + self.qtgui_time_sink_x_0_0 = qtgui.time_sink_f( + 1024, #size + samp_rate, #samp_rate + "", #name + 1, #number of inputs + None # parent + ) + self.qtgui_time_sink_x_0_0.set_update_time(0.10) + self.qtgui_time_sink_x_0_0.set_y_axis(0, 20) + + self.qtgui_time_sink_x_0_0.set_y_label('XC Magnitude', "") + + self.qtgui_time_sink_x_0_0.enable_tags(True) + self.qtgui_time_sink_x_0_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, 0, "") + self.qtgui_time_sink_x_0_0.enable_autoscale(True) + self.qtgui_time_sink_x_0_0.enable_grid(False) + self.qtgui_time_sink_x_0_0.enable_axis_labels(True) + self.qtgui_time_sink_x_0_0.enable_control_panel(False) + self.qtgui_time_sink_x_0_0.enable_stem_plot(False) + + + labels = ['Signal 1', 'Signal 2', 'Signal 3', 'Signal 4', 'Signal 5', + 'Signal 6', 'Signal 7', 'Signal 8', 'Signal 9', 'Signal 10'] + widths = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + colors = ['blue', 'red', 'green', 'black', 'cyan', + 'magenta', 'yellow', 'dark red', 'dark green', 'dark blue'] + alphas = [1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0] + styles = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + markers = [-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1] + + + for i in range(1): + if len(labels[i]) == 0: + self.qtgui_time_sink_x_0_0.set_line_label(i, "Data {0}".format(i)) + else: + self.qtgui_time_sink_x_0_0.set_line_label(i, labels[i]) + self.qtgui_time_sink_x_0_0.set_line_width(i, widths[i]) + self.qtgui_time_sink_x_0_0.set_line_color(i, colors[i]) + self.qtgui_time_sink_x_0_0.set_line_style(i, styles[i]) + self.qtgui_time_sink_x_0_0.set_line_marker(i, markers[i]) + self.qtgui_time_sink_x_0_0.set_line_alpha(i, alphas[i]) + + self._qtgui_time_sink_x_0_0_win = sip.wrapinstance(self.qtgui_time_sink_x_0_0.pyqwidget(), Qt.QWidget) + self.top_layout.addWidget(self._qtgui_time_sink_x_0_0_win) self.qtgui_time_sink_x_0 = qtgui.time_sink_f( 1024, #size samp_rate, #samp_rate "", #name - 1 #number of inputs + 1, #number of inputs + None # parent ) self.qtgui_time_sink_x_0.set_update_time(0.10) - self.qtgui_time_sink_x_0.set_y_axis(0, 100) + self.qtgui_time_sink_x_0.set_y_axis(0, 20) self.qtgui_time_sink_x_0.set_y_label('XC Magnitude', "") @@ -244,10 +405,56 @@ class correlator(gr.top_block, Qt.QWidget): self.top_grid_layout.setRowStretch(r, 1) for c in range(0, 1): self.top_grid_layout.setColumnStretch(c, 1) + self.qtgui_const_sink_x_1 = qtgui.const_sink_c( + 1024, #size + "Cross Correlation", #name + 1, #number of inputs + None # parent + ) + self.qtgui_const_sink_x_1.set_update_time(0.10) + self.qtgui_const_sink_x_1.set_y_axis(-2, 2) + self.qtgui_const_sink_x_1.set_x_axis(-2, 2) + self.qtgui_const_sink_x_1.set_trigger_mode(qtgui.TRIG_MODE_FREE, qtgui.TRIG_SLOPE_POS, 0.0, 0, "") + self.qtgui_const_sink_x_1.enable_autoscale(True) + self.qtgui_const_sink_x_1.enable_grid(True) + self.qtgui_const_sink_x_1.enable_axis_labels(True) + + + labels = ['', '', '', '', '', + '', '', '', '', ''] + widths = [1, 1, 1, 1, 1, + 1, 1, 1, 1, 1] + colors = ["blue", "red", "red", "red", "red", + "red", "red", "red", "red", "red"] + styles = [2, 0, 0, 0, 0, + 0, 0, 0, 0, 0] + markers = [9, 0, 0, 0, 0, + 0, 0, 0, 0, 0] + alphas = [.5, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0] + + for i in range(1): + if len(labels[i]) == 0: + self.qtgui_const_sink_x_1.set_line_label(i, "Data {0}".format(i)) + else: + self.qtgui_const_sink_x_1.set_line_label(i, labels[i]) + self.qtgui_const_sink_x_1.set_line_width(i, widths[i]) + self.qtgui_const_sink_x_1.set_line_color(i, colors[i]) + self.qtgui_const_sink_x_1.set_line_style(i, styles[i]) + self.qtgui_const_sink_x_1.set_line_marker(i, markers[i]) + self.qtgui_const_sink_x_1.set_line_alpha(i, alphas[i]) + + self._qtgui_const_sink_x_1_win = sip.wrapinstance(self.qtgui_const_sink_x_1.pyqwidget(), Qt.QWidget) + self.top_grid_layout.addWidget(self._qtgui_const_sink_x_1_win, 2, 1, 2, 1) + for r in range(2, 4): + self.top_grid_layout.setRowStretch(r, 1) + for c in range(1, 2): + self.top_grid_layout.setColumnStretch(c, 1) self.qtgui_const_sink_x_0 = qtgui.const_sink_c( 1024, #size - "", #name - 1 #number of inputs + "Equalized Signal", #name + 1, #number of inputs + None # parent ) self.qtgui_const_sink_x_0.set_update_time(0.10) self.qtgui_const_sink_x_0.set_y_axis(-2, 2) @@ -283,10 +490,15 @@ class correlator(gr.top_block, Qt.QWidget): self.qtgui_const_sink_x_0.set_line_alpha(i, alphas[i]) self._qtgui_const_sink_x_0_win = sip.wrapinstance(self.qtgui_const_sink_x_0.pyqwidget(), Qt.QWidget) - self.top_grid_layout.addWidget(self._qtgui_const_sink_x_0_win) + self.top_grid_layout.addWidget(self._qtgui_const_sink_x_0_win, 0, 1, 2, 1) + for r in range(0, 2): + self.top_grid_layout.setRowStretch(r, 1) + for c in range(1, 2): + self.top_grid_layout.setColumnStretch(c, 1) self.fir_filter_xxx_1 = filter.fir_filter_ccc(1, revconj_access_code_symbols) self.fir_filter_xxx_1.declare_sample_delay(0) self.digital_pfb_clock_sync_xxx_0 = digital.pfb_clock_sync_ccf(sps, timing_loop_bw, rrc_taps, nfilts, 16, 1.5, 1) + self.digital_corr_est_cc_0 = digital.corr_est_cc(access_code_symbols, 1, 0, .8, digital.THRESHOLD_DYNAMIC) self.digital_constellation_modulator_0 = digital.generic_mod( constellation=const, differential=False, @@ -294,22 +506,23 @@ class correlator(gr.top_block, Qt.QWidget): pre_diff_code=True, excess_bw=excess_bw, verbose=False, - log=False) - self.digital_constellation_decoder_cb_0 = digital.constellation_decoder_cb(const) + log=False, + truncate=False) self.digital_cma_equalizer_cc_0 = digital.cma_equalizer_cc(15, 1, .002, 1) self.channels_channel_model_0 = channels.channel_model( - noise_voltage=0.1, - frequency_offset=0.0, + noise_voltage=0.2, + frequency_offset=0.000001, epsilon=1.0, - taps=[1. ], + taps=[-1.4 + .4j], noise_seed=243, block_tags=False) - self.blocks_vector_source_x_0 = blocks.vector_source_b(testvec, True, 1, []) + self.blocks_vector_source_x_0 = blocks.vector_source_b(testvec * 1600, False, 1, []) self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True) - self.blocks_stream_mux_0 = blocks.stream_mux(gr.sizeof_char*1, [15, len(testvec)]) + self.blocks_stream_mux_0 = blocks.stream_mux(gr.sizeof_char*1, [10, len(testvec)]) self.blocks_null_source_0 = blocks.null_source(gr.sizeof_char*1) - self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_char*1) + self.blocks_null_sink_3 = blocks.null_sink(gr.sizeof_float*1) self.blocks_multiply_const_vxx_0 = blocks.multiply_const_ff(180 / 3.141592653589793) + self.blocks_complex_to_magphase_0_0 = blocks.complex_to_magphase(1) self.blocks_complex_to_magphase_0 = blocks.complex_to_magphase(1) @@ -319,25 +532,33 @@ class correlator(gr.top_block, Qt.QWidget): ################################################## self.connect((self.blocks_complex_to_magphase_0, 1), (self.blocks_multiply_const_vxx_0, 0)) self.connect((self.blocks_complex_to_magphase_0, 0), (self.qtgui_time_sink_x_0, 0)) + self.connect((self.blocks_complex_to_magphase_0_0, 1), (self.blocks_null_sink_3, 0)) + self.connect((self.blocks_complex_to_magphase_0_0, 0), (self.qtgui_time_sink_x_0_0, 0)) self.connect((self.blocks_multiply_const_vxx_0, 0), (self.qtgui_time_sink_x_2, 0)) self.connect((self.blocks_null_source_0, 0), (self.blocks_stream_mux_0, 0)) self.connect((self.blocks_stream_mux_0, 0), (self.digital_constellation_modulator_0, 0)) self.connect((self.blocks_throttle_0, 0), (self.digital_pfb_clock_sync_xxx_0, 0)) self.connect((self.blocks_vector_source_x_0, 0), (self.blocks_stream_mux_0, 1)) self.connect((self.channels_channel_model_0, 0), (self.blocks_throttle_0, 0)) - self.connect((self.digital_cma_equalizer_cc_0, 0), (self.digital_constellation_decoder_cb_0, 0)) + self.connect((self.digital_cma_equalizer_cc_0, 0), (self.digital_corr_est_cc_0, 0)) self.connect((self.digital_cma_equalizer_cc_0, 0), (self.fir_filter_xxx_1, 0)) self.connect((self.digital_cma_equalizer_cc_0, 0), (self.qtgui_const_sink_x_0, 0)) self.connect((self.digital_cma_equalizer_cc_0, 0), (self.qtgui_time_sink_x_1_1, 0)) - self.connect((self.digital_constellation_decoder_cb_0, 0), (self.blocks_null_sink_0, 0)) self.connect((self.digital_constellation_modulator_0, 0), (self.channels_channel_model_0, 0)) + self.connect((self.digital_constellation_modulator_0, 0), (self.qtgui_time_sink_x_1_0, 0)) + self.connect((self.digital_corr_est_cc_0, 1), (self.blocks_complex_to_magphase_0_0, 0)) + self.connect((self.digital_corr_est_cc_0, 0), (self.qtgui_time_sink_x_0_0_0, 0)) self.connect((self.digital_pfb_clock_sync_xxx_0, 0), (self.digital_cma_equalizer_cc_0, 0)) self.connect((self.fir_filter_xxx_1, 0), (self.blocks_complex_to_magphase_0, 0)) + self.connect((self.fir_filter_xxx_1, 0), (self.qtgui_const_sink_x_1, 0)) def closeEvent(self, event): self.settings = Qt.QSettings("GNU Radio", "correlator") self.settings.setValue("geometry", self.saveGeometry()) + self.stop() + self.wait() + event.accept() def get_sps(self): @@ -373,7 +594,7 @@ class correlator(gr.top_block, Qt.QWidget): def set_testvec(self, testvec): self.testvec = testvec - self.blocks_vector_source_x_0.set_data(self.testvec, []) + self.blocks_vector_source_x_0.set_data(self.testvec * 1600, []) def get_samp_rate(self): return self.samp_rate @@ -382,6 +603,9 @@ class correlator(gr.top_block, Qt.QWidget): self.samp_rate = samp_rate self.blocks_throttle_0.set_sample_rate(self.samp_rate) self.qtgui_time_sink_x_0.set_samp_rate(self.samp_rate) + self.qtgui_time_sink_x_0_0.set_samp_rate(self.samp_rate) + self.qtgui_time_sink_x_0_0_0.set_samp_rate(self.samp_rate) + self.qtgui_time_sink_x_1_0.set_samp_rate(self.samp_rate) self.qtgui_time_sink_x_1_1.set_samp_rate(self.samp_rate) self.qtgui_time_sink_x_2.set_samp_rate(self.samp_rate) @@ -411,6 +635,11 @@ class correlator(gr.top_block, Qt.QWidget): def set_access_code_symbols_sps(self, access_code_symbols_sps): self.access_code_symbols_sps = access_code_symbols_sps + def get_access_code_symbols(self): + return self.access_code_symbols + + def set_access_code_symbols(self, access_code_symbols): + self.access_code_symbols = access_code_symbols @@ -429,6 +658,9 @@ def main(top_block_cls=correlator, options=None): tb.show() def sig_handler(sig=None, frame=None): + tb.stop() + tb.wait() + Qt.QApplication.quit() signal.signal(signal.SIGINT, sig_handler) @@ -438,11 +670,6 @@ def main(top_block_cls=correlator, options=None): timer.start(500) timer.timeout.connect(lambda: None) - def quitting(): - tb.stop() - tb.wait() - - qapp.aboutToQuit.connect(quitting) qapp.exec_() if __name__ == '__main__': -- cgit v1.2.1