TCP Streaming

Stream live radio samples over a plain TCP connection and receive them back in the graph.

Blocks 5
Category Networking
Revision 1
License MIT

Blocks used

SDR
TCP Transmitter
TCP Receiver
Spectrum Analyzer
Note

Overview

Getting radio samples out of CyberEther and into another program, or onto another computer, is the first step toward a remote receiver, a recording server, or a decoder written in another language. A plain TCP connection is the most widely supported way to do it, since nearly every language and tool can open one. It delivers every byte in order and resends anything lost, so the reader sees exactly what the sender wrote.[1] At 2 MS/s, the complex float32 samples the graph works with add up to 16 MB/s, or about 30 buffers of 512 KiB every second.

This flowgraph sends a live radio stream through a TCP Transmitter, which listens on the loopback address 127.0.0.1, to a TCP Receiver that connects to it on the same machine, and shows the received samples on a spectrum analyzer. The same two blocks work across a network, or with any program that reads or writes raw samples over TCP. Running it needs a USB radio supported by the SDR block, such as an RTL-SDR, and a free port 5001 on this machine.

Warning

This flowgraph requires native CyberEther, because the TCP blocks are not part of CyberEther Web.

How it works

The SDR block delivers buffers of 8 batches of 8,192 complex samples, 512 KiB of data every 32.8 ms. The TCP Transmitter writes each buffer to the connection as raw bytes, real part then imaginary part, with no header. The type, shape, sample rate, and frequency stay with the sender, so the receiver is set to the same CF32 type and buffer shape, and the analyzer labels its axis in bins.

The connection carries a byte stream with no boundaries between writes, and its flow control lets a slow reader hold the sender back.[1] On an Ethernet link, the 1,500 bytes a frame carries[2] leave room for up to 1,460 bytes of samples once the 20-byte IPv4 header[3] and a TCP header of at least 20 bytes are added.[1] Each buffer then travels as about 360 segments, which the receiver joins back into one stream.

Quantity Value
Bytes per buffer 524,288
Buffers per second about 30.5
Sample bytes per Ethernet segment up to 1,460
Stalled send before a client is dropped 1 s

The transmitter writes during the compute step, so a slow client slows the whole graph, and a send that stalls for a second closes that connection. Every client gets every buffer, and one that joins late starts at the next. The TCP Receiver reads on a background thread into a ring buffer that holds four of its output buffers, or 131 ms of signal. It empties the ring on every new connection, so each received buffer lines up with one sent buffer, and Buffer Loss counts samples overwritten when the graph falls behind.

What to look for

The analyzer shows the received samples as a trace above a waterfall, just as it would when fed directly by the radio. Each station is a hump in the trace and a stripe in the waterfall, and the tuned frequency sits in the center bin. At 2 MS/s each of the 8,192 bins is about 244 Hz wide, so a 200 kHz FM channel spans about 820 bins. The trace averages about eight buffers, a quarter of a second, and the 512 waterfall rows cover about 2.1 seconds.

Throughput should read about 16 MB/s on both network blocks, Clients on the transmitter should read 1, and Buffer Loss on the receiver should stay at 0.00%. Throughput on the transmitter is summed over every client, so a second client doubles it.

The connection shows when the link breaks. Changing Port on the TCP Transmitter closes the server, Clients drops to 0, the display freezes, and the receiver logs a warning and retries twice a second. Restoring the port brings the stream back by itself, starting clean at the next buffer.

Either end can listen. With the transmitter in Client mode and the receiver in Server mode, the stream looks the same, and the transmitter shows Connection instead of Clients. That suits a sender behind a firewall, which can still connect out. A receiver in Server mode takes one sender at a time.

For a deeper treatment of TCP and its flow control, see the sources below.

Going further

A few lines of Python outside CyberEther can join the same server as a second client, which is the starting point for a program of your own. Run the code below with Python and NumPy while the flowgraph runs. Clients on the transmitter goes to 2, and once per second the script prints the sample rate and the average power. At the saved settings it should read about 2.00 MS/s. Because the stream has no boundaries, the script keeps any partial sample for the next read. Pausing the script for a few seconds stalls the graph for about a second, and then the transmitter drops it and Clients returns to 1.

PYTHON
import socket
import time

import numpy as np

HOST = "127.0.0.1"
PORT = 5001

sock = socket.create_connection((HOST, PORT))
pending = b""
samples = 0
energy = 0.0
start = time.monotonic()

while True:
    chunk = sock.recv(1 << 20)
    if not chunk:
        print("Connection closed by the transmitter")
        break
    pending += chunk
    usable = len(pending) - len(pending) % 8
    x = np.frombuffer(pending[:usable], dtype=np.complex64)
    pending = pending[usable:]
    samples += x.size
    energy += float(np.sum(np.abs(x) ** 2))
    elapsed = time.monotonic() - start
    if elapsed >= 1.0:
        level = 10 * np.log10(energy / samples) if energy > 0 else float("-inf")
        print(f"{samples / elapsed / 1e6:.2f} MS/s, {level:.1f} dBFS")
        samples = 0
        energy = 0.0
        start = time.monotonic()

Batches and Samples on the receiver are independent of the radio, since the stream carries no shape. Setting Samples to 2,048 gives the analyzer 2,048 bins, and raising Buffer Multiplier to 16 keeps the ring at four sender buffers.

To reach another computer, set Address on the TCP Transmitter to 0.0.0.0 and point a TCP Receiver there at the address of this machine. The connection is plain TCP with no encryption or login, so anyone who can reach the port can read the stream.

The UDP Streaming entry sends the same samples without a connection, the WebSocket Streaming entry frames them as messages for browsers, and the block catalog covers every setting of both TCP blocks.

References

References

  1. W. Eddy, Ed., "Transmission control protocol (TCP)," IETF, RFC 9293 (STD 7), Aug. 2022. rfc-editor.org/rfc/rfc9293 ↩ ↩2 ↩3

  2. C. Hornig, "A standard for the transmission of IP datagrams over Ethernet networks," IETF, RFC 894, Apr. 1984. rfc-editor.org/rfc/rfc894 ↩

  3. J. Postel, "Internet protocol," IETF, RFC 791 (STD 5), Sep. 1981. rfc-editor.org/rfc/rfc791 ↩

CyberEther

High-performance GPU-accelerated signal processing and visualization framework that runs anywhere.

Star on GitHub

© 2026 CyberEther. All rights reserved.

The final frontier!