aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNao Pross <np@0hm.ch>2021-12-04 17:05:29 +0100
committerNao Pross <np@0hm.ch>2021-12-04 17:05:29 +0100
commit9d0910c5140b28a84017b9bbb4def22d15425518 (patch)
tree26836d9005a93afabbb71edd6fddbc194e601a5f /src
parentUpdate documentation (diff)
parentUpdate net.py to decode UDP data stream (diff)
downloadFading-9d0910c5140b28a84017b9bbb4def22d15425518.tar.gz
Fading-9d0910c5140b28a84017b9bbb4def22d15425518.zip
Merge branch 'master' of github.com:NaoPross/Fading
Diffstat (limited to '')
-rw-r--r--src/gr-fadingui/grc/CMakeLists.txt6
-rw-r--r--src/gr-fadingui/grc/fadingui_ber.block.yml (renamed from src/gr-fadingui/grc/fadingui_dearpygui_sink.block.yml)23
-rw-r--r--src/gr-fadingui/grc/fadingui_netsink.block.yml (renamed from src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml)40
-rw-r--r--src/gr-fadingui/python/CMakeLists.txt8
-rw-r--r--src/gr-fadingui/python/__init__.py6
-rw-r--r--src/gr-fadingui/python/ber.py58
-rw-r--r--src/gr-fadingui/python/dearpygui_sink.py36
-rw-r--r--src/gr-fadingui/python/netsink.py84
-rwxr-xr-xsrc/gr-fadingui/python/qa_ber.py48
-rw-r--r--src/gr-fadingui/python/qa_xor_frame_sync.py57
-rw-r--r--src/gr-fadingui/python/xor_frame_sync.py152
-rwxr-xr-xsrc/gui/gui.py7
-rw-r--r--src/gui/net.py67
13 files changed, 283 insertions, 309 deletions
diff --git a/src/gr-fadingui/grc/CMakeLists.txt b/src/gr-fadingui/grc/CMakeLists.txt
index 2394de4..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_xor_frame_sync.block.yml
fadingui_deframer.block.yml
fadingui_frame_obj.block.yml
- fadingui_multipath_fading.block.yml DESTINATION share/gnuradio/grc/blocks
+ fadingui_multipath_fading.block.yml
+ 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_ber.block.yml
index dbe6198..3070311 100644
--- a/src/gr-fadingui/grc/fadingui_dearpygui_sink.block.yml
+++ b/src/gr-fadingui/grc/fadingui_ber.block.yml
@@ -1,10 +1,10 @@
-id: fadingui_dearpygui_sink
-label: UI Sink
+id: fadingui_ber
+label: BER
category: '[fadingui]'
templates:
imports: import fadingui
- make: fadingui.dearpygui_sink(sock_addr=${sock_addr}, ui_element_id=${ui_element_id})
+ make: fadingui.ber(vgl=${vgl}, vlen=${vlen})
# Make one 'parameters' list entry for every parameter you want settable from the GUI.
# Keys include:
@@ -12,15 +12,12 @@ templates:
# * label (label shown in the GUI)
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
parameters:
-- id: sock_addr
- label: Socket address
- dtype: string
- default: udp://
-
-- id: ui_element_id
- label: UI element ID
+- id: vgl
+ label: Vergleichsparameter
dtype: raw
-
+- id: vlen
+ label: Vec Length
+ dtype: int
# Make one 'inputs' list entry per input and one 'outputs' list entry per output.
# Keys include:
@@ -31,7 +28,9 @@ parameters:
# * optional (optional - set to 1 for optional inputs. Default is 0)
inputs:
- label: in
- dtype: complex
+ domain: stream
+ dtype: byte
+ 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/grc/fadingui_xor_frame_sync.block.yml b/src/gr-fadingui/grc/fadingui_netsink.block.yml
index 1a8640d..3cd8ae7 100644
--- a/src/gr-fadingui/grc/fadingui_xor_frame_sync.block.yml
+++ b/src/gr-fadingui/grc/fadingui_netsink.block.yml
@@ -1,11 +1,13 @@
-id: fadingui_xor_frame_sync
-label: XOR Correlation Synchronizer
+id: fadingui_netsink
+label: Network Sink
category: '[fadingui]'
flags: [ python ]
templates:
- imports: import fadingui
- make: fadingui.xor_frame_sync(sync_pattern=${pattern}, buffer_size=${buffer_size})
+ imports: |-
+ import fadingui
+ import numpy as np
+ make: fadingui.netsink(address=${address}, dtype="${type}", vlen=${veclen})
# Make one 'parameters' list entry for every parameter you want settable from the GUI.
# Keys include:
@@ -13,12 +15,22 @@ templates:
# * label (label shown in the GUI)
# * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
parameters:
-- id: pattern
- label: Bit pattern
- dtype: raw
-- id: buffer_size
- label: Delay buffer size
- dtype: raw
+- id: type
+ label: Type
+ dtype: enum
+ options: [complex, float, int, short, byte]
+ option_attributes:
+ size: [gr.sizeof_gr_complex, gr.sizeof_float, gr.sizeof_int, gr.sizeof_short, gr.sizeof_char]
+ hide: part
+- id: veclen
+ label: Vec Length
+ dtype: int
+ default: '1'
+ hide: ${ 'part' if veclen == 1 else 'none' }
+- id: address
+ label: Address
+ dtype: string
+ default: "udp://localhost:31415"
# Make one 'inputs' list entry per input and one 'outputs' list entry per output.
# Keys include:
@@ -30,12 +42,8 @@ parameters:
inputs:
- label: in
domain: stream
- dtype: byte
-
-outputs:
-- label: out
- domain: stream
- dtype: byte
+ dtype: ${type}
+ vlen: ${veclen}
# '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 95bb852..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
- xor_frame_sync.py
deframer.py
frame_obj.py
- multipath_fading.py DESTINATION ${GR_PYTHON_DIR}/fadingui
+ multipath_fading.py
+ ber.py
+ netsink.py DESTINATION ${GR_PYTHON_DIR}/fadingui
)
########################################################################
@@ -48,5 +48,5 @@ include(GrTest)
set(GR_TEST_TARGET_DEPS gnuradio-fadingui)
set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig)
-GR_ADD_TEST(qa_xor_frame_sync ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_xor_frame_sync.py)
GR_ADD_TEST(qa_multipath_fading ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_multipath_fading.py)
+GR_ADD_TEST(qa_ber ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_ber.py)
diff --git a/src/gr-fadingui/python/__init__.py b/src/gr-fadingui/python/__init__.py
index 5a7b546..56dbdd3 100644
--- a/src/gr-fadingui/python/__init__.py
+++ b/src/gr-fadingui/python/__init__.py
@@ -33,10 +33,12 @@ except ImportError:
# import any pure python here
from .datasource import datasource
-from .dearpygui_sink import dearpygui_sink
-from .xor_frame_sync import xor_frame_sync
+
+
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/ber.py b/src/gr-fadingui/python/ber.py
new file mode 100644
index 0000000..e966f17
--- /dev/null
+++ b/src/gr-fadingui/python/ber.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 Sara Cinzia Halter.
+#
+# This is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+
+import numpy as np
+from gnuradio import gr
+
+from fadingui.logger import get_logger
+log = get_logger("ber")
+
+class ber(gr.sync_block):
+ """
+ docstring for block ber
+ """
+ def __init__(self, vgl, vlen):
+ gr.sync_block.__init__(self,
+ name="ber",
+ in_sig=[np.dtype(str(vlen) + "b")],
+ out_sig=None)
+ self.vgl=vgl
+ self.vlen=vlen
+
+ def work(self, input_items, output_items):
+
+ inp = input_items[0]
+ ber_tot = 0
+ log.debug(f"Length: {len(inp)}")
+ log.debug(f"Inp_vector:{inp}")
+
+ for i in inp:
+ log.debug(f"In Schlaufe{i}")
+ v = np.array(self.vgl, dtype=np.uint8)^np.array(i, dtype=np.uint8)
+ ber = sum(np.unpackbits(v))
+ log.debug(f"BER {ber} in Paket {i}")
+ ber_tot+=ber
+ log.debug(f"BER Total{ber_tot}")
+
+
+ return len(input_items[0])
+
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..130e5dc
--- /dev/null
+++ b/src/gr-fadingui/python/netsink.py
@@ -0,0 +1,84 @@
+#!/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):
+ to_numpy = {
+ "complex": np.complex64,
+ "float": np.float32,
+ "int": np.int32,
+ "short": np.short,
+ "byte": np.byte,
+ }
+
+ dt = to_numpy[dtype]
+ if vlen > 1:
+ dt = np.dtype(dt, (vlen,))
+ print(dt)
+
+ gr.sync_block.__init__(self,
+ name="Network Sink",
+ in_sig=[dt],
+ out_sig=None)
+
+ # Create a socket and parse remote machine url
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.url = urlparse(address)
+ self.srv = (self.url.hostname, self.url.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
+ """
+ # FIXME: this could be (very) slow, is there a faster way with numpy?
+ values = "[" + ",".join(map(str, data)) + "]"
+ return bytes(values, "ascii")
+
+ def work(self, input_items, output_items):
+ # FIXME: it is probably better NOT to send *every* sample
+ inp = input_items[0]
+ inp_len = len(inp)
+ blocksize = 1024
+
+ # Check that the packet is not huge
+ if len(inp) < blocksize:
+ self.send(self.encode(inp))
+ else:
+ # compute how to split inp into blocks
+ nblocks = inp_len // blocksize
+ index = blocksize * nblocks
+
+ # send blocks
+ blocks = np.array(inp[:index]).reshape((blocksize, nblocks))
+ for block in blocks:
+ self.send(self.encode(block))
+
+ # sent the rest
+ self.send(self.encode(inp[index:]))
+
+ return len(inp)
diff --git a/src/gr-fadingui/python/qa_ber.py b/src/gr-fadingui/python/qa_ber.py
new file mode 100755
index 0000000..cb6c198
--- /dev/null
+++ b/src/gr-fadingui/python/qa_ber.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 Sara Cinzia Halter.
+#
+# This is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+from gnuradio import blocks
+from ber import ber
+import numpy as np
+
+class qa_ber(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001_t(self):
+ # pattern = np.array([0xaa], dtype=np.uint8)
+ # testdata = np.array([0xc0, 0xfa, 0xae] * 4, dtype=np.uint8)
+
+ # src = blocks.vector_source_b(testdata)
+ # op = ber(pattern)
+
+ # self.tb.connect(src, op)
+ # self.tb.run()
+ pass
+
+
+if __name__ == '__main__':
+ gr_unittest.run(qa_ber)
diff --git a/src/gr-fadingui/python/qa_xor_frame_sync.py b/src/gr-fadingui/python/qa_xor_frame_sync.py
deleted file mode 100644
index 9c480a0..0000000
--- a/src/gr-fadingui/python/qa_xor_frame_sync.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python
-
-from gnuradio import gr, gr_unittest, blocks
-
-from xor_frame_sync import xor_frame_sync
-import numpy as np
-
-
-class test_xor_frame_sync(gr_unittest.TestCase):
-
- def setUp(self):
- self.tb = gr.top_block()
-
- def tearDown(self):
- self.tb = None
-
- def test_001(self):
- """Test a byte aligned delay"""
- pattern = np.array([0xc0, 0xff, 0xee], dtype=np.uint8)
- testdata = np.packbits(np.concatenate([
- np.unpackbits(np.arange(0, 5, dtype=np.uint8)),
- np.random.randint(0, 2, size = 8 * 5),
- np.unpackbits(pattern),
- np.random.randint(0, 2, size = 64)
- ]))
-
- src = blocks.vector_source_b(testdata)
- op = xor_frame_sync(pattern, 2048)
- dst = blocks.vector_sink_b()
-
- self.tb.connect(src, op, dst)
- self.tb.run()
-
- self.assertEqual(op.synchronized, True)
-
- # FIXME: implement feature
- # def test_002(self):
- # """Test a byte unaligned delay"""
- # pattern = np.array([0xbe, 0xef], dtype=np.uint8)
- # testdata = np.packbits(np.concatenate([
- # np.unpackbits(np.arange(0, 10, dtype=np.uint8)),
- # np.random.randint(0, 2, size = (2 + 8 * 5)), np.unpackbits(pattern),
- # np.random.randint(0, 2, size = 64)
- # ]))
-
- # src = blocks.vector_source_b(testdata)
- # op = xor_frame_sync(pattern, 2048)
- # dst = blocks.vector_sink_b()
-
- # self.tb.connect(src, op, dst)
- # self.tb.run()
-
- # self.assertEqual(op.synchronized, True)
-
-
-if __name__ == "__main__":
- gr_unittest.run(test_xor_frame_sync)
diff --git a/src/gr-fadingui/python/xor_frame_sync.py b/src/gr-fadingui/python/xor_frame_sync.py
deleted file mode 100644
index bb5cfb1..0000000
--- a/src/gr-fadingui/python/xor_frame_sync.py
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright 2021 Naoki Pross.
-
-
-import numpy as np
-from numpy_ringbuffer import RingBuffer
-
-from gnuradio import gr
-
-from fadingui.logger import get_logger
-log = get_logger("xor_frame_sync")
-
-
-class xor_frame_sync(gr.sync_block):
- """
- Performs a frame synchronization by XOR matching a preamble bit sequence
- """
- def __init__(self, sync_pattern, buffer_size):
- # TODO: buffer size should be in packets
- gr.sync_block.__init__(self,
- name="xor_frame_sync",
- in_sig=[np.byte],
- out_sig=[np.byte])
-
- # binary pattern to match
- self.pattern = sync_pattern
- self.nbytes = len(self.pattern)
-
- self.pattern_bits = np.unpackbits(np.array(self.pattern, dtype=np.uint8))[::-1]
- self.nbits = len(self.pattern_bits)
-
- log.debug(f"Loaded pattern {self.pattern_bits} length={self.nbits}")
- assert(self.nbits % 8 == 0)
-
- # packed buffer to delay the data
- self.delaybuf = RingBuffer(buffer_size, dtype=np.uint8)
- self.delay = 0
-
- log.debug(f"Created delay ring buffer of size {self.delaybuf.maxlen}")
-
- # unpacked buffer to compute correlation values, initially filled with zeros
- self.corrbuf = RingBuffer(self.nbits, dtype=np.uint8)
- self.corrbuf.extend(np.zeros(self.corrbuf.maxlen))
-
- # synchronization state
- self.synchronized = False
-
- def xcorrelation(self, v):
- """
- Compute the binary correlations between the stored pattern and
- correlation buffer, while shifting v into the buffer.
-
- Binary correlation between two bit vectors is just size of the
- vector(s) minus the number of bits that differ.
-
- @return: Number of bits of v that were shifted into the buffer
- when the correlation matched. If no match is found
- the return value is None.
- """
- # this could be much faster with shifts, bitwise or and xor
- # but this should do alright for the moment
- v_bits = np.unpackbits(np.array(v, dtype=np.uint8))
- for bitnr, b in enumerate(v_bits):
- self.corrbuf.appendleft(b)
- if (np.bitwise_xor(self.corrbuf, self.pattern_bits) == 0).all():
- return bitnr
-
- # no cross correlation found
- return None
-
- def work(self, input_items, output_items):
- """
- Process the inputs, that means:
-
- - Check that the buffer is synchronized, i.e. there is the sync
- pattern appears every k bits, where k is the size of the packet.
-
- - If the buffer is not synchronized, compute a binary cross
- correlation to find how much the stream should be delayed.
- """
- # array of samples, growing index = forward in time
- inp = input_items[0]
- inp_len = len(inp)
-
- if not self.synchronized:
- if inp_len > self.delaybuf.maxlen:
- log.error("Input is bigger than delay buffer")
-
- # FIXME: Makes the QA hang for some reason
- raise NotImplemented
-
- # create space for new samples in the delay buffer
- self.delaybuf.extendleft(np.zeros(inp_len))
-
- # Add values and while processing
- for bytenr, value in enumerate(inp):
- # save value in the buffer
- # FIXME: this is wrong, it should be in reverse order
- self.delaybuf.appendleft(value)
-
- # compute the cross correlation
- bitnr = self.xcorrelation(value)
- if bitnr is not None:
- # correlation was found
- delay_bits = (bitnr - 7)
- delay_bytes = 8 * (bytenr -1)
-
- log.debug(f"Synchronized with delay_bytes={delay_bytes} delay_bits={delay_bits}")
-
- # FIXME: add bit delay
- self.delay = delay_bytes
- self.synchronized = True
-
- # Not aligned to bytes
- if delay_bits != 0:
- log.error("Not implemented: byte unaligned delay")
- self.synchronized = False
- self.delay = 0
-
- # FIXME: Makes the QA hang for some reason
- # raise NotImplemented
-
- # stop processing inputs
- break
-
- if not self.synchronized:
- log.warning(f"Processed {inp_len} samples but could not synchronize")
- else:
- self.delaybuf.extendleft(inp)
-
- # return data with delay
- out = output_items[0]
- # FIXME: this is also wrong
- # out[:] = self.delaybuf[:len(out)]
- out[:] = inp[:]
-
-
- inptmp = np.array(inp[:12], dtype=np.uint8)
- inphex = np.array(list(map(hex, inptmp)))
- inpbits = np.array(list(map("{:08b}".format, inptmp)))
-
- log.debug(f"inp={inptmp}")
- log.debug(f"inp={inphex}")
- log.debug(f"inp={inpbits}")
-
- # outtmp = np.array(out[:12], dtype=np.uint8)
- # log.debug(f"out={outtmp}")
-
- return inp_len
-
diff --git a/src/gui/gui.py b/src/gui/gui.py
index b2cbebb..d817f63 100755
--- a/src/gui/gui.py
+++ b/src/gui/gui.py
@@ -98,7 +98,8 @@ with window(label="RX DSP Flow Graph", width=800, height=400, pos=(25,25), tag="
#================================================
# Network plots Window
-recv_plot = net.network_plot(url="udp://localhost:31415", nsamples=100, label="Test", height=300, width=800)
+recv_plot = net.network_plot(url="udp://localhost:31415", dtype=float, nsamples=100, \
+ label="Test", height=300, width=800)
plots = {
recv_plot: "plt_ampl"
@@ -107,9 +108,9 @@ plots = {
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_plot_axis(mvYAxis, label="Amplitude", tag="axis")
- add_line_series(recv_plot.x_data, recv_plot.y_data, parent="plt_ampl")
+ add_line_series(recv_plot.xdata, recv_plot.ydata, parent="axis", tag="plt_ampl")
#================================================
# Start GUI and main loop
diff --git a/src/gui/net.py b/src/gui/net.py
index 2c91bb8..c7008cd 100644
--- a/src/gui/net.py
+++ b/src/gui/net.py
@@ -1,6 +1,7 @@
import select
import socket
from urllib.parse import urlparse
+import re
import numpy as np
from numpy_ringbuffer import RingBuffer
@@ -11,9 +12,10 @@ class udpsource:
"""
Creates an UDP listening socket
"""
- def __init__(self, url):
+ def __init__(self, url, dtype):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.url = urlparse(url)
+ self.dtype = dtype
def __del__(self):
self.sock.close()
@@ -23,32 +25,46 @@ class udpsource:
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:
+ def read(self, nblocks):
+ ready, _, _ = select.select([self.sock], [], [])
+ if not ready:
return None
+ # read from socket
+ blocksize = 1024 * 4
+ string = ready[0].recv(nblocks * blocksize).decode("ascii")
+
+ # decode string, remove empty values
+ chunks = filter(None, re.split(r"\[(.+?)\]", string))
+
+ def chunk_to_samples(chunk):
+ samples = chunk.split(",")
+ if samples:
+ return list(map(self.dtype, samples))
+
+ # convert each chunk into a list of samples
+ chunk_values = map(chunk_to_samples, chunks)
+
+ # flatten list of lists into a single list
+ values = sum(chunk_values, [])
+
+ return values
class network_plot(udpsource):
- def __init__(self, url, nsamples, **kwargs):
- udpsource.__init__(self, url)
+ def __init__(self, url, dtype, nsamples, **kwargs):
+ udpsource.__init__(self, url, dtype)
+ # create buffers for x and y values
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.xvalues = np.arange(0, self.nsamples)
+ self.yvalues = RingBuffer(capacity=self.nsamples, dtype=np.dtype(dtype))
+ self.yvalues.extend(np.zeros(self.nsamples))
+ # create a plot
+ self.plot = dpg.plot(**kwargs)
self.bind()
+ # Map `with' expressions to the underlying plot
def __enter__(self):
return self.plot.__enter__()
@@ -56,13 +72,16 @@ class network_plot(udpsource):
self.plot.__exit__(t, val, tb)
@property
- def x_data(self):
- return np.array(self.buffer[:,0])
+ def xdata(self):
+ return self.xvalues
@property
- def y_data(self):
- return np.array(self.buffer[:,1])
+ def ydata(self):
+ return np.array(self.yvalues)
def refresh_series(self, tag):
- dpg.set_value(tag, [self.x_data, self.y_data])
- pass
+ new_values = self.read(1)
+
+ if new_values:
+ self.yvalues.extendleft(new_values)
+ dpg.set_value(tag, [self.xdata, self.ydata])