diff options
-rw-r--r-- | src/gr-fadingui/grc/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/gr-fadingui/grc/fadingui_netsink.block.yml (renamed from src/gr-fadingui/grc/fadingui_dearpygui_sink.block.yml) | 34 | ||||
-rw-r--r-- | src/gr-fadingui/python/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/gr-fadingui/python/__init__.py | 3 | ||||
-rw-r--r-- | src/gr-fadingui/python/dearpygui_sink.py | 36 | ||||
-rw-r--r-- | src/gr-fadingui/python/netsink.py | 59 | ||||
-rw-r--r-- | tests/correlator/correlator.grc | 345 | ||||
-rwxr-xr-x | tests/correlator/correlator.py | 41 | ||||
-rw-r--r-- | tests/correlator/epy_block_0.py | 52 | ||||
-rw-r--r-- | tests/correlator/epy_block_1.py | 27 | ||||
-rw-r--r-- | tests/sockets/send.py | 21 |
11 files changed, 338 insertions, 288 deletions
diff --git a/src/gr-fadingui/grc/CMakeLists.txt b/src/gr-fadingui/grc/CMakeLists.txt index 297ae8d..79c1a31 100644 --- a/src/gr-fadingui/grc/CMakeLists.txt +++ b/src/gr-fadingui/grc/CMakeLists.txt @@ -19,9 +19,9 @@ # Boston, MA 02110-1301, USA. install(FILES fadingui_datasource.block.yml - fadingui_dearpygui_sink.block.yml fadingui_deframer.block.yml fadingui_frame_obj.block.yml fadingui_multipath_fading.block.yml - fadingui_ber.block.yml DESTINATION share/gnuradio/grc/blocks + fadingui_ber.block.yml + fadingui_netsink.block.yml DESTINATION share/gnuradio/grc/blocks ) diff --git a/src/gr-fadingui/grc/fadingui_dearpygui_sink.block.yml b/src/gr-fadingui/grc/fadingui_netsink.block.yml index dbe6198..4e5b01b 100644 --- a/src/gr-fadingui/grc/fadingui_dearpygui_sink.block.yml +++ b/src/gr-fadingui/grc/fadingui_netsink.block.yml @@ -1,10 +1,11 @@ -id: fadingui_dearpygui_sink -label: UI Sink +id: fadingui_netsink +label: Network Sink category: '[fadingui]' +flags: [ python ] templates: imports: import fadingui - make: fadingui.dearpygui_sink(sock_addr=${sock_addr}, ui_element_id=${ui_element_id}) + make: fadingui.netsink(${address}, ${dtype}, ${vlen}) # Make one 'parameters' list entry for every parameter you want settable from the GUI. # Keys include: @@ -12,15 +13,22 @@ templates: # * label (label shown in the GUI) # * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...) parameters: -- id: sock_addr - label: Socket address +- id: dtype + label: Type + dtype: enum + options: [complex, float, int, short, byte] + option_attributes: + size: [gr.sizeof_gr_complex, gr.sizeof_floar, gr.sizeof_int, gr.sizeof_short, gr.sizeof_char ] + hide: part +- id: vlen + label: Vec Length + dtype: int + default: 1 + hide: ${ 'part' if vlen == 1 else 'none' } +- id: address + label: Address dtype: string - default: udp:// - -- id: ui_element_id - label: UI element ID - dtype: raw - + default: "udp://localhost:31415" # Make one 'inputs' list entry per input and one 'outputs' list entry per output. # Keys include: @@ -31,7 +39,9 @@ parameters: # * optional (optional - set to 1 for optional inputs. Default is 0) inputs: - label: in - dtype: complex + domain: stream + dtype: ${dtype} + vlen: ${vlen} # 'file_format' specifies the version of the GRC yml format used in the file # and should usually not be changed. diff --git a/src/gr-fadingui/python/CMakeLists.txt b/src/gr-fadingui/python/CMakeLists.txt index e04eb5b..bf484cc 100644 --- a/src/gr-fadingui/python/CMakeLists.txt +++ b/src/gr-fadingui/python/CMakeLists.txt @@ -34,11 +34,11 @@ GR_PYTHON_INSTALL( __init__.py logger.py datasource.py - dearpygui_sink.py deframer.py frame_obj.py multipath_fading.py - ber.py DESTINATION ${GR_PYTHON_DIR}/fadingui + ber.py + netsink.py DESTINATION ${GR_PYTHON_DIR}/fadingui ) ######################################################################## diff --git a/src/gr-fadingui/python/__init__.py b/src/gr-fadingui/python/__init__.py index 9fe45d5..56dbdd3 100644 --- a/src/gr-fadingui/python/__init__.py +++ b/src/gr-fadingui/python/__init__.py @@ -33,11 +33,12 @@ except ImportError: # import any pure python here from .datasource import datasource -from .dearpygui_sink import dearpygui_sink + from .deframer import deframer from .frame_obj import frame_obj from .multipath_fading import multipath_fading from .ber import ber +from .netsink import netsink # diff --git a/src/gr-fadingui/python/dearpygui_sink.py b/src/gr-fadingui/python/dearpygui_sink.py deleted file mode 100644 index 6153611..0000000 --- a/src/gr-fadingui/python/dearpygui_sink.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright 2021 Naoki Pross. - -import socket -from urllib.parse import urlparse - -import numpy as np -from gnuradio import gr - -class dearpygui_sink(gr.sync_block): - """ - DearPyGUI Sink - """ - def __init__(self, sock_addr, ui_element_id): - gr.sync_block.__init__(self, - name="dearpygui_sink", - in_sig=[np.complex64], - out_sig=None) - - # sockets - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.srv = urlparse(sock_addr) - - def send(self, value): - data = value.tobytes() - sent = self.socket.sendto(data, (self.srv.hostname, self.srv.port)) - - return len(data) == sent - - def work(self, input_items, output_items): - in0 = input_items[0] - self.send(in0) - return len(input_items[0]) - diff --git a/src/gr-fadingui/python/netsink.py b/src/gr-fadingui/python/netsink.py new file mode 100644 index 0000000..9df81f5 --- /dev/null +++ b/src/gr-fadingui/python/netsink.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2021 Sara Cinzia Halter, Naoki Pross. + +import socket +from urllib.parse import urlparse + +import numpy as np +from gnuradio import gr + +class netsink(gr.sync_block): + """ + Sink that sends the data over the network using UDP. + Keep in mind that is quite slow. + """ + def __init__(self, address, dtype, vlen): + gr.sync_block.__init__(self, + name="Network Sink", + in_sig=[], + out_sig=None) + + # Create a socket and parse remote machine url + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.url = urlparse(sock_addr) + self.srv = (self.srv.hostname, self.srv.port) + + def send(self, data): + """ + Send the data to self.srv + + @param data Data as python bytes + @return Number of bytes that were actually sent + """ + assert type(data) == bytes + return self.socket.sendto(data, self.srv) + + def encode(self, data): + """ + Encode the data into a dead simple format + + @param data Array like type + @return Bytes of ASCII encoded comma separated string of numbers + """ + # no data (what are you doing?) + if not data: + return bytes() + + values = "[" + ",".join(map(str, data)) + "]" + return bytes(values, "ascii") + + def work(self, input_items, output_items): + inp = input_items[0] + + # TODO: Check that inp has a reasonable size + self.send(self.encode(inp)) + + return len(input_items[0]) + diff --git a/tests/correlator/correlator.grc b/tests/correlator/correlator.grc index 220eaed..beea007 100644 --- a/tests/correlator/correlator.grc +++ b/tests/correlator/correlator.grc @@ -44,26 +44,7 @@ blocks: 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: - 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] + coordinate: [768, 1016.0] rotation: 0 state: enabled - name: const @@ -81,7 +62,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [592, 484.0] + coordinate: [592, 360.0] rotation: 0 state: enabled - name: excess_bw @@ -93,7 +74,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [496, 484.0] + coordinate: [496, 360.0] rotation: 0 state: enabled - name: nfilts @@ -105,7 +86,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [224, 988.0] + coordinate: [224, 856.0] rotation: 0 state: enabled - name: revconj_access_code_symbols @@ -120,7 +101,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [48, 564.0] + coordinate: [48, 440.0] rotation: 0 state: enabled - name: rrc_taps @@ -132,14 +113,14 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [304, 988.0] + coordinate: [304, 856.0] rotation: 0 state: enabled - name: samp_rate id: variable parameters: comment: '' - value: '32000' + value: int(1.5e6) states: bus_sink: false bus_source: false @@ -163,24 +144,24 @@ blocks: id: variable parameters: comment: '' - value: '[31, 53] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23, 0x41, 0xf3] ' + value: '[0x1f, 0x35] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23, 0x41, 0xf3] ' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [48, 492.0] + coordinate: [48, 360.0] rotation: 0 state: enabled - name: timing_loop_bw id: variable parameters: comment: '' - value: 2 * 3.141592653589793 / 100 + value: 2 * np.pi / 100 states: bus_sink: false bus_source: false bus_structure: null - coordinate: [224, 1068.0] + coordinate: [224, 936.0] rotation: 0 state: enabled - name: blocks_complex_to_magphase_0 @@ -196,7 +177,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1048, 696.0] + coordinate: [1040, 568.0] rotation: 0 state: disabled - name: blocks_complex_to_magphase_0_0 @@ -212,7 +193,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1088, 1304.0] + coordinate: [1080, 1176.0] rotation: 0 state: enabled - name: blocks_multiply_const_vxx_0 @@ -230,7 +211,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1288, 724.0] + coordinate: [1288, 596.0] rotation: 0 state: disabled - name: blocks_multiply_const_vxx_0_0 @@ -248,7 +229,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1376, 1380.0] + coordinate: [1376, 1248.0] rotation: 0 state: disabled - name: blocks_null_sink_3 @@ -265,7 +246,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1376, 1432.0] + coordinate: [1376, 1304.0] rotation: 0 state: true - name: blocks_null_source_0 @@ -284,47 +265,48 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [96, 344.0] + coordinate: [96, 200.0] rotation: 0 state: enabled -- name: blocks_stream_mux_0 - id: blocks_stream_mux +- name: blocks_repack_bits_bb_0 + id: blocks_repack_bits_bb parameters: affinity: '' alias: '' + align_output: 'False' comment: '' - lengths: '[10, len(testvec)]' + endianness: gr.GR_MSB_FIRST + k: '2' + l: '8' + len_tag_key: '""' 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] + coordinate: [672, 1252.0] rotation: 0 - state: enabled -- name: blocks_stream_mux_1 + state: true +- name: blocks_stream_mux_0 id: blocks_stream_mux parameters: affinity: '' alias: '' comment: '' - lengths: '[len(access_code_symbols_sps), sps * (len(testvec) + 15)]' + lengths: '[5, len(testvec)]' maxoutbuf: '0' minoutbuf: '0' num_inputs: '2' - type: complex + type: byte vlen: '1' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [776, 280.0] + coordinate: [288, 256.0] rotation: 0 - state: disabled + state: enabled - name: blocks_throttle_0 id: blocks_throttle parameters: @@ -341,25 +323,9 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1272, 404.0] + coordinate: [1272, 268.0] rotation: 0 state: enabled -- name: blocks_vector_sink_x_0 - id: blocks_vector_sink_x - parameters: - affinity: '' - alias: '' - comment: '' - reserve_items: '1024' - type: byte - vlen: '1' - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [536, 1392.0] - rotation: 0 - state: true - name: blocks_vector_source_x_0 id: blocks_vector_source_x parameters: @@ -371,35 +337,15 @@ blocks: repeat: 'True' tags: '[]' type: byte - vector: testvec * 1600 + vector: testvec * 500 vlen: '1' states: bus_sink: false bus_source: false bus_structure: null - coordinate: [48, 404.0] + coordinate: [48, 268.0] rotation: 0 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: @@ -411,14 +357,14 @@ blocks: freq_offset: '0.0001' maxoutbuf: '0' minoutbuf: '0' - noise_voltage: '0.2' + noise_voltage: '0.01' seed: '243' - taps: -1.4 + .4j + taps: np.exp(1j * 30 / 180 * np.pi) states: bus_sink: false bus_source: false bus_structure: null - coordinate: [992, 364.0] + coordinate: [1000, 228.0] rotation: 0 state: enabled - name: digital_cma_equalizer_cc_0 @@ -437,7 +383,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [528, 812.0] + coordinate: [520, 676.0] rotation: 0 state: enabled - name: digital_constellation_decoder_cb_0 @@ -453,7 +399,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [248, 1388.0] + coordinate: [224, 1260.0] rotation: 0 state: enabled - name: digital_constellation_modulator_0 @@ -474,7 +420,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [496, 380.0] + coordinate: [504, 244.0] rotation: 0 state: enabled - name: digital_corr_est_cc_0 @@ -494,7 +440,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [776, 1020.0] + coordinate: [768, 892.0] rotation: 0 state: enabled - name: digital_costas_loop_cc_0 @@ -512,9 +458,25 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1088, 1128.0] + coordinate: [1080, 1000.0] rotation: 0 state: true +- name: digital_map_bb_0 + id: digital_map_bb + parameters: + affinity: '' + alias: '' + comment: '' + map: '[0, 1, 3, 2]' + maxoutbuf: '0' + minoutbuf: '0' + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [496, 1260.0] + rotation: 0 + state: bypassed - name: digital_pfb_clock_sync_xxx_0 id: digital_pfb_clock_sync_xxx parameters: @@ -535,7 +497,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [224, 836.0] + coordinate: [224, 700.0] rotation: 0 state: enabled - name: epy_block_0 @@ -543,32 +505,33 @@ blocks: parameters: _source_code: "import pmt\n\nimport numpy as np\nfrom gnuradio import gr\n\n\n\ class blk(gr.sync_block):\n def __init__(self):\n gr.sync_block.__init__(\n\ - \ self,\n name='Phase Lock',\n in_sig=[np.complex64],\n\ - \ out_sig=[np.complex64]\n )\n\n # we need to keep\ - \ track of the aboslute number of samples that have\n # been processed,\ - \ because tags have an absolute offset\n self.counter: np.uint64 = 0\n\ - \n # because we do block processing, we need to keep track of the last\ - \ tag\n # of the previous block to correct the first values of the next\ - \ block\n self.last = None\n\n # both the phase and frequency\ - \ corrections should go through a low pass\n # filter to avoid werid\ - \ jumps in the correction. to do that, there are\n # two buffers with\ - \ an index\n self.index = 0\n self.length = 7\n self.freq\ - \ = np.zeros(self.length)\n\n def lpf_freq(self, new_sample):\n #\ - \ save new sample\n self.freq[self.index] = new_sample\n # increment\ - \ index\n self.index = (self.index + 1) % self.length\n\n return\ - \ np.sum(self.freq) / self.length\n\n def block_phase(self, start, end):\n\ - \ # compute number of samples in block\n nsamples = end.offset\ - \ - start.offset\n\n # unpack pmt values into start and end phase\n \ - \ sphase = pmt.to_python(start.value)\n ephase = pmt.to_python(end.value)\n\ - \n # compute frequency offset between start and end\n # and run\ - \ it through a low pass filter (mean)\n freq = (sphase - ephase) / nsamples\n\ - \ freq = self.lpf_freq(freq)\n\n # debugging\n print(f\"\ - Correction for block of {nsamples:2d} samples is \" \\\n f\"phase={sphase:\ - \ .4f} rad and freq={freq*1e3: .4f} milli rad / sample\")\n\n # compute\ - \ block values\n return sphase * np.ones(nsamples) + freq * np.arange(0,\ - \ nsamples)\n\n def work(self, input_items, output_items):\n # FIXME:\ - \ replace class counter with local variable\n self.counter = self.nitems_written(0)\n\ - \n # nicer aliases\n inp = input_items[0]\n out = output_items[0]\n\ + \ self,\n name='Phase and Frequency Correction',\n \ + \ in_sig=[np.complex64],\n out_sig=[np.complex64]\n \ + \ )\n\n # tags should not be propagated, we then output our own tags\n\ + \ self.set_tag_propagation_policy(gr.TPP_DONT)\n\n # because we\ + \ do block processing, we need to keep track of the last tag\n # of the\ + \ previous block to correct the first values of the next block\n self.last\ + \ = None\n self.lastfreq = 0\n\n def block_phase(self, start, end):\n\ + \ \"\"\"\n Compute a vector for the phase and frequency correction\ + \ for the samples\n between two tags (start and end).\n\n @param\ + \ start Tag where the samples should start to be corrected\n @param end\ + \ Tag where to stop correcting\n\n @return A vector of phase values\ + \ for each sample. If we call the ouput\n `phase' to correct\ + \ the samples between the start and end tags,\n TODO: finish\n\ + \ \"\"\"\n # compute number of samples in block\n nsamples\ + \ = end.offset - start.offset\n\n # unpack pmt values into start and\ + \ end phase\n sphase = pmt.to_python(start.value)\n ephase = pmt.to_python(end.value)\n\ + \n # compute frequency offset between start and end\n phasediff\ + \ = ephase - sphase\n\n if phasediff > np.pi:\n phasediff\ + \ -= 2*np.pi\n\n elif phasediff < -np.pi:\n phasediff += 2*np.pi\n\ + \n freq = phasediff / nsamples\n\n # save this one for the last\ + \ block (see variable `end' in self.work)\n self.lastfreq = freq\n\n\ + \ # debugging\n print(f\"Correction for block of {nsamples:2d}\ + \ samples is \" \\\n f\"sphase={sphase: .4f} rad and freq={freq*1e3:\ + \ .4f} milli rad / sample\")\n\n # compute block values\n return\ + \ sphase * np.ones(nsamples) + freq * np.arange(0, nsamples)\n\n def work(self,\ + \ input_items, output_items):\n counter = self.nitems_written(0)\n\n\ + \ # nicer aliases\n inp = input_items[0]\n out = output_items[0]\n\ \n # read phase tags\n is_phase = lambda tag: pmt.to_python(tag.key)\ \ == \"phase_est\"\n tags = list(filter(is_phase, self.get_tags_in_window(0,\ \ 0, len(inp))))\n\n if not tags:\n print(f\"There were no\ @@ -581,34 +544,60 @@ blocks: \ (start, end) in pairs ]\n middle = np.concatenate(blocks) if blocks\ \ else []\n\n # compute values at the end, we do not have informations\ \ about the future\n # but we can use the frequency of the last block\ - \ to approximate\n nback = len(inp) - (tags[-1].offset - self.counter)\n\ - \ print(f\"Processing {nback} samples at the back of the buffer\")\n\ - \ endfreq = self.lpf_freq(self.freq[-1])\n end = np.ones(nback)\ - \ * pmt.to_python(tags[-1].value) + endfreq * np.arange(0, nback)\n\n \ - \ # compute the \"start\", using the last tag from the previous call\n \ - \ nfront = tags[0].offset - self.counter\n print(f\"Processing {nfront}\ - \ samples at the front of the buffer\")\n start = self.block_phase(self.last,\ - \ tags[0])[-nfront:] \\\n if self.last and nfront else np.zeros(nfront)\n\ - \n # compute correction\n correction = np.exp(-1j * np.concatenate([start,\ + \ to approximate\n nback = len(inp) - (tags[-1].offset - counter)\n \ + \ print(f\"Processing {nback} samples at the back of the buffer\")\n \ + \ end = np.ones(nback) * pmt.to_python(tags[-1].value) \\\n \ + \ + self.lastfreq * np.arange(0, nback)\n\n # compute the \"start\"\ + , using the last tag from the previous call\n nfront = tags[0].offset\ + \ - counter\n print(f\"Processing {nfront} samples at the front of the\ + \ buffer\")\n start = self.block_phase(self.last, tags[0])[-nfront:]\ + \ \\\n if self.last and nfront else np.zeros(nfront)\n\n \ + \ # compute correction\n correction = np.exp(-1j * np.concatenate([start,\ \ middle, end]))\n length = len(correction)\n\n # write outputs\n\ - \ out[:length] = inp[:length] * correction\n self.counter += len(inp)\n\ - \n # save last tag for next call\n self.last = tags[-1]\n\n \ - \ # FIXME: should return `length' but then the last sample is not\n \ - \ # included and self.last does something weird\n return len(out)\n" + \ out[:length] = inp[:length] * correction\n\n # save last tag\ + \ for next call\n self.last = tags[-1]\n\n # FIXME: should return\ + \ `length' but then the last sample is not\n # included and self.last\ + \ does something weird\n return len(out)\n" affinity: '' alias: '' comment: '' maxoutbuf: '0' minoutbuf: '0' states: - _io_cache: ('Phase Lock', 'blk', [], [('0', 'complex', 1)], [('0', 'complex', - 1)], '', []) + _io_cache: ('Phase and Frequency Correction', 'blk', [], [('0', 'complex', 1)], + [('0', 'complex', 1)], '', []) bus_sink: false bus_source: false bus_structure: null - coordinate: [1088, 1040.0] + coordinate: [1072, 912.0] rotation: 0 state: enabled +- name: epy_block_1 + id: epy_block + parameters: + _source_code: "\"\"\"\nEmbedded Python Blocks:\n\nEach time this file is saved,\ + \ GRC will instantiate the first class it finds\nto get ports and parameters\ + \ of your block. The arguments to __init__ will\nbe the parameters. All of\ + \ them are required to have default values!\n\"\"\"\n\nimport numpy as np\n\ + from gnuradio import gr\n\nnp.set_printoptions(formatter={'int':hex})\n\nclass\ + \ blk(gr.sync_block):\n def __init__(self):\n gr.sync_block.__init__(\n\ + \ self,\n name='Printer',\n in_sig=[np.byte],\n\ + \ out_sig=[]\n )\n\n def work(self, input_items, output_items):\n\ + \ inp = np.array(input_items[0], dtype=np.uint8)\n print(f\"Decoded\ + \ {len(inp)} samples:\\n{inp}\")\n\n return len(inp)\n" + affinity: '' + alias: '' + comment: '' + maxoutbuf: '0' + minoutbuf: '0' + states: + _io_cache: ('Printer', 'blk', [], [('0', 'byte', 1)], [], '', []) + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [920, 1264.0] + rotation: 0 + state: true - name: fir_filter_xxx_1 id: fir_filter_xxx parameters: @@ -625,9 +614,22 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [776, 828.0] + coordinate: [776, 692.0] rotation: 0 state: disabled +- name: import_0 + id: import + parameters: + alias: '' + comment: '' + imports: import numpy as np + states: + bus_sink: false + bus_source: false + bus_structure: null + coordinate: [184, 12.0] + rotation: 0 + state: true - name: qtgui_const_sink_x_0 id: qtgui_const_sink_x parameters: @@ -717,7 +719,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [776, 716.0] + coordinate: [768, 592.0] rotation: 0 state: enabled - name: qtgui_const_sink_x_0_0 @@ -809,7 +811,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1368, 1092.0] + coordinate: [1408, 964.0] rotation: 0 state: enabled - name: qtgui_const_sink_x_1 @@ -901,7 +903,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1048, 612.0] + coordinate: [1040, 480.0] rotation: 0 state: disabled - name: qtgui_time_sink_x_0 @@ -998,7 +1000,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1320, 604.0] + coordinate: [1312, 480.0] rotation: 0 state: disabled - name: qtgui_time_sink_x_0_0 @@ -1057,7 +1059,7 @@ blocks: name: '""' nconnections: '1' size: '1024' - srate: samp_rate + srate: samp_rate / sps stemplot: 'False' style1: '1' style10: '1' @@ -1095,7 +1097,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1376, 1284.0] + coordinate: [1376, 1156.0] rotation: 0 state: enabled - name: qtgui_time_sink_x_0_0_0 @@ -1154,7 +1156,7 @@ blocks: name: '""' nconnections: '1' size: '1024' - srate: samp_rate + srate: samp_rate / sps stemplot: 'False' style1: '1' style10: '1' @@ -1192,7 +1194,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1048, 940.0] + coordinate: [1072, 788.0] rotation: 0 state: enabled - name: qtgui_time_sink_x_1_0 @@ -1289,7 +1291,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [808, 452.0] + coordinate: [800, 320.0] rotation: 0 state: enabled - name: qtgui_time_sink_x_1_1 @@ -1386,7 +1388,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [776, 612.0] + coordinate: [768, 480.0] rotation: 0 state: disabled - name: qtgui_time_sink_x_2 @@ -1483,7 +1485,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1480, 708.0] + coordinate: [1488, 580.0] rotation: 0 state: disabled - name: qtgui_time_sink_x_2_0 @@ -1580,30 +1582,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1552, 1364.0] - rotation: 0 - state: disabled -- 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: sps * samp_rate - type: fir_filter_ccf - states: - bus_sink: false - bus_source: false - bus_structure: null - coordinate: [496, 180.0] + coordinate: [1552, 1232.0] rotation: 0 state: disabled - name: virtual_sink_0 @@ -1616,7 +1595,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1480, 404.0] + coordinate: [1488, 268.0] rotation: 0 state: enabled - name: virtual_sink_3 @@ -1629,7 +1608,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [1368, 1036.0] + coordinate: [1400, 828.0] rotation: 0 state: true - name: virtual_source_0 @@ -1642,7 +1621,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [32, 884.0] + coordinate: [16, 748.0] rotation: 0 state: enabled - name: virtual_source_1 @@ -1655,7 +1634,7 @@ blocks: bus_sink: false bus_source: false bus_structure: null - coordinate: [24, 1388.0] + coordinate: [16, 1260.0] rotation: 0 state: true @@ -1668,18 +1647,16 @@ connections: - [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_repack_bits_bb_0, '0', epy_block_1, '0'] - [blocks_stream_mux_0, '0', digital_constellation_modulator_0, '0'] -- [blocks_stream_mux_1, '0', channels_channel_model_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_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_vector_sink_x_0, '0'] -- [digital_constellation_modulator_0, '0', blocks_stream_mux_1, '1'] +- [digital_constellation_decoder_cb_0, '0', digital_map_bb_0, '0'] - [digital_constellation_modulator_0, '0', channels_channel_model_0, '0'] - [digital_constellation_modulator_0, '0', qtgui_time_sink_x_1_0, '0'] - [digital_corr_est_cc_0, '0', digital_costas_loop_cc_0, '0'] @@ -1687,12 +1664,12 @@ connections: - [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_costas_loop_cc_0, '0', qtgui_const_sink_x_0_0, '1'] +- [digital_map_bb_0, '0', blocks_repack_bits_bb_0, '0'] - [digital_pfb_clock_sync_xxx_0, '0', digital_cma_equalizer_cc_0, '0'] - [epy_block_0, '0', qtgui_const_sink_x_0_0, '0'] - [epy_block_0, '0', virtual_sink_3, '0'] - [fir_filter_xxx_1, '0', blocks_complex_to_magphase_0, '0'] - [fir_filter_xxx_1, '0', qtgui_const_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_1, '0', digital_constellation_decoder_cb_0, '0'] diff --git a/tests/correlator/correlator.py b/tests/correlator/correlator.py index 50283c0..255010a 100755 --- a/tests/correlator/correlator.py +++ b/tests/correlator/correlator.py @@ -35,6 +35,8 @@ from argparse import ArgumentParser from gnuradio.eng_arg import eng_float, intx from gnuradio import eng_notation import epy_block_0 +import epy_block_1 +import numpy as np from gnuradio import qtgui @@ -77,13 +79,12 @@ class correlator(gr.top_block, Qt.QWidget): self.sps = sps = 4 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, 0x41, 0xf3] - self.samp_rate = samp_rate = 32000 + self.timing_loop_bw = timing_loop_bw = 2 * np.pi / 100 + self.testvec = testvec = [0x1f, 0x35] + [0x12, 0xe3, 0x9b, 0xee, 0x84, 0x23, 0x41, 0xf3] + self.samp_rate = samp_rate = int(1.5e6) 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.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)] ################################################## @@ -145,7 +146,7 @@ class correlator(gr.top_block, Qt.QWidget): 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 + samp_rate / sps, #samp_rate "", #name 1 #number of inputs ) @@ -195,7 +196,7 @@ class correlator(gr.top_block, Qt.QWidget): self.top_grid_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 + samp_rate / sps, #samp_rate "", #name 1 #number of inputs ) @@ -328,6 +329,7 @@ class correlator(gr.top_block, Qt.QWidget): self.top_grid_layout.setRowStretch(r, 1) for c in range(1, 2): self.top_grid_layout.setColumnStretch(c, 1) + self.epy_block_1 = epy_block_1.blk() self.epy_block_0 = epy_block_0.blk() 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_costas_loop_cc_0 = digital.costas_loop_cc(2 * 3.141592653589793 / 100, 4, False) @@ -343,16 +345,16 @@ class correlator(gr.top_block, Qt.QWidget): 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.channels_channel_model_0 = channels.channel_model( - noise_voltage=0.2, + noise_voltage=0.01, frequency_offset=0.0001, epsilon=1.0, - taps=[-1.4 + .4j], + taps=[np.exp(1j * 30 / 180 * np.pi)], noise_seed=243, block_tags=False) - self.blocks_vector_source_x_0 = blocks.vector_source_b(testvec * 1600, True, 1, []) - self.blocks_vector_sink_x_0 = blocks.vector_sink_b(1, 1024) + self.blocks_vector_source_x_0 = blocks.vector_source_b(testvec * 500, 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, [10, len(testvec)]) + self.blocks_stream_mux_0 = blocks.stream_mux(gr.sizeof_char*1, [5, len(testvec)]) + self.blocks_repack_bits_bb_0 = blocks.repack_bits_bb(2, 8, "", False, gr.GR_MSB_FIRST) self.blocks_null_source_0 = blocks.null_source(gr.sizeof_char*1) self.blocks_null_sink_3 = blocks.null_sink(gr.sizeof_float*1) self.blocks_complex_to_magphase_0_0 = blocks.complex_to_magphase(1) @@ -365,13 +367,14 @@ class correlator(gr.top_block, Qt.QWidget): 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_null_source_0, 0), (self.blocks_stream_mux_0, 0)) + self.connect((self.blocks_repack_bits_bb_0, 0), (self.epy_block_1, 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_corr_est_cc_0, 0)) self.connect((self.digital_cma_equalizer_cc_0, 0), (self.qtgui_const_sink_x_0, 0)) - self.connect((self.digital_constellation_decoder_cb_0, 0), (self.blocks_vector_sink_x_0, 0)) + self.connect((self.digital_constellation_decoder_cb_0, 0), (self.blocks_repack_bits_bb_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)) @@ -395,6 +398,8 @@ class correlator(gr.top_block, Qt.QWidget): def set_sps(self, sps): self.sps = sps self.set_rrc_taps(firdes.root_raised_cosine(self.nfilts, self.nfilts, 1.0/float(self.sps), self.excess_bw, 45*self.nfilts)) + self.qtgui_time_sink_x_0_0.set_samp_rate(self.samp_rate / self.sps) + self.qtgui_time_sink_x_0_0_0.set_samp_rate(self.samp_rate / self.sps) def get_nfilts(self): return self.nfilts @@ -422,7 +427,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 * 1600, []) + self.blocks_vector_source_x_0.set_data(self.testvec * 500, []) def get_samp_rate(self): return self.samp_rate @@ -430,8 +435,8 @@ class correlator(gr.top_block, Qt.QWidget): def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate self.blocks_throttle_0.set_sample_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_0_0.set_samp_rate(self.samp_rate / self.sps) + self.qtgui_time_sink_x_0_0_0.set_samp_rate(self.samp_rate / self.sps) self.qtgui_time_sink_x_1_0.set_samp_rate(self.samp_rate) def get_rrc_taps(self): @@ -453,12 +458,6 @@ class correlator(gr.top_block, Qt.QWidget): 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 - def get_access_code_symbols(self): return self.access_code_symbols diff --git a/tests/correlator/epy_block_0.py b/tests/correlator/epy_block_0.py index e7599c9..ddae02a 100644 --- a/tests/correlator/epy_block_0.py +++ b/tests/correlator/epy_block_0.py @@ -8,33 +8,18 @@ class blk(gr.sync_block): def __init__(self): gr.sync_block.__init__( self, - name='Phase Lock', + name='Phase and Frequency Correction', in_sig=[np.complex64], out_sig=[np.complex64] ) - # we need to keep track of the aboslute number of samples that have - # been processed, because tags have an absolute offset - self.counter: np.uint64 = 0 + # tags should not be propagated, we then output our own tags + self.set_tag_propagation_policy(gr.TPP_DONT) # because we do block processing, we need to keep track of the last tag # of the previous block to correct the first values of the next block self.last = None - - # both the phase and frequency corrections should go through a low pass - # filter to avoid werid jumps in the correction. to do that, there are - # two buffers with an index - self.index = 0 - self.length = 7 - self.freq = np.zeros(self.length) - - def lpf_freq(self, new_sample): - # save new sample - self.freq[self.index] = new_sample - # increment index - self.index = (self.index + 1) % self.length - - return np.sum(self.freq) / self.length + self.lastfreq = 0 def block_phase(self, start, end): # compute number of samples in block @@ -45,20 +30,28 @@ class blk(gr.sync_block): ephase = pmt.to_python(end.value) # compute frequency offset between start and end - # and run it through a low pass filter (mean) - freq = (sphase - ephase) / nsamples - freq = self.lpf_freq(freq) + phasediff = ephase - sphase + + if phasediff > np.pi: + phasediff -= 2*np.pi + + elif phasediff < -np.pi: + phasediff += 2*np.pi + + freq = phasediff / nsamples + + # save this one for the last block (see variable `end' in self.work) + self.lastfreq = freq # debugging print(f"Correction for block of {nsamples:2d} samples is " \ - f"phase={sphase: .4f} rad and freq={freq*1e3: .4f} milli rad / sample") + f"sphase={sphase: .4f} rad and freq={freq*1e3: .4f} milli rad / sample") # compute block values return sphase * np.ones(nsamples) + freq * np.arange(0, nsamples) def work(self, input_items, output_items): - # FIXME: replace class counter with local variable - self.counter = self.nitems_written(0) + counter = self.nitems_written(0) # nicer aliases inp = input_items[0] @@ -85,13 +78,13 @@ class blk(gr.sync_block): # compute values at the end, we do not have informations about the future # but we can use the frequency of the last block to approximate - nback = len(inp) - (tags[-1].offset - self.counter) + nback = len(inp) - (tags[-1].offset - counter) print(f"Processing {nback} samples at the back of the buffer") - endfreq = self.lpf_freq(self.freq[-1]) - end = np.ones(nback) * pmt.to_python(tags[-1].value) + endfreq * np.arange(0, nback) + end = np.ones(nback) * pmt.to_python(tags[-1].value) \ + + self.lastfreq * np.arange(0, nback) # compute the "start", using the last tag from the previous call - nfront = tags[0].offset - self.counter + nfront = tags[0].offset - counter print(f"Processing {nfront} samples at the front of the buffer") start = self.block_phase(self.last, tags[0])[-nfront:] \ if self.last and nfront else np.zeros(nfront) @@ -102,7 +95,6 @@ class blk(gr.sync_block): # write outputs out[:length] = inp[:length] * correction - self.counter += len(inp) # save last tag for next call self.last = tags[-1] diff --git a/tests/correlator/epy_block_1.py b/tests/correlator/epy_block_1.py new file mode 100644 index 0000000..d30c2eb --- /dev/null +++ b/tests/correlator/epy_block_1.py @@ -0,0 +1,27 @@ +""" +Embedded Python Blocks: + +Each time this file is saved, GRC will instantiate the first class it finds +to get ports and parameters of your block. The arguments to __init__ will +be the parameters. All of them are required to have default values! +""" + +import numpy as np +from gnuradio import gr + +np.set_printoptions(formatter={'int':hex}) + +class blk(gr.sync_block): + def __init__(self): + gr.sync_block.__init__( + self, + name='Printer', + in_sig=[np.byte], + out_sig=[] + ) + + def work(self, input_items, output_items): + inp = np.array(input_items[0], dtype=np.uint8) + print(f"Decoded {len(inp)} samples:\n{inp}") + + return len(inp) diff --git a/tests/sockets/send.py b/tests/sockets/send.py new file mode 100644 index 0000000..87faf5d --- /dev/null +++ b/tests/sockets/send.py @@ -0,0 +1,21 @@ +import socket +from urllib.parse import urlparse + +import numpy as np + +remote = "upd://localhost:31415" +url = urlparse(remote) + +print(url.hostname) +print(url.port) + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock.connect((url.hostname, url.port)) + +# sent some text +sock.send(bytes("hello", "ascii")) + +arr = np.arange(0, 10) +print(arr) + +sock.send(arr.tobytes()) |