Add rtl_tcp input: -i rtltcp:host:port:freq:gain:ppm:bw:sq:vol

This commit is contained in:
Ryan Drake
2026-10-12 00:26:31 -07:00
parent 5244c85774
commit f38b5976f3
5 changed files with 336 additions and 30 deletions

View File

@@ -134,6 +134,7 @@ FILE(GLOB HEADERS include/*.h include/*.hpp)
if(NOT RTLSDR_FOUND) if(NOT RTLSDR_FOUND)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp) list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_fm.cpp)
list(REMOVE_ITEM SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_sdr_tcp.c)
endif(NOT RTLSDR_FOUND) endif(NOT RTLSDR_FOUND)
configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY) configure_file("src/git_ver.c.in" "${CMAKE_CURRENT_BINARY_DIR}/git_ver.c" @ONLY)

View File

@@ -372,6 +372,9 @@ typedef struct
int rtl_bandwidth; int rtl_bandwidth;
int rtl_started; int rtl_started;
long int rtl_rms; long int rtl_rms;
int rtltcp_enabled;
char rtltcp_hostname[1024];
int rtltcp_portno;
int monitor_input_audio; int monitor_input_audio;
int analog_only; int analog_only;
int pulse_raw_rate_in; int pulse_raw_rate_in;
@@ -1707,6 +1710,21 @@ void rtlsdr_sighandler();
void rtl_dev_tune(dsd_opts * opts, long int frequency); void rtl_dev_tune(dsd_opts * opts, long int frequency);
long int rtl_return_rms(); long int rtl_return_rms();
void rtl_clean_queue(); void rtl_clean_queue();
//rtl_tcp client (rtl_sdr_tcp.c)
int rtltcp_open(const char *hostname, int portno);
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev);
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains);
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq);
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate);
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on);
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on);
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual);
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain);
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm);
#endif #endif

View File

@@ -695,6 +695,9 @@
opts->rtlsdr_center_freq = 850000000; //set to an initial value (if user is using a channel map, then they won't need to specify anything other than -i rtl if desired) opts->rtlsdr_center_freq = 850000000; //set to an initial value (if user is using a channel map, then they won't need to specify anything other than -i rtl if desired)
opts->rtl_started = 0; opts->rtl_started = 0;
opts->rtl_rms = 0; //root means square power level on rtl input signal opts->rtl_rms = 0; //root means square power level on rtl input signal
opts->rtltcp_enabled = 0; //1 when the rtl input is an rtl_tcp server instead of a local dongle
sprintf (opts->rtltcp_hostname, "%s", "localhost");
opts->rtltcp_portno = 1234; //rtl_tcp default port
//end RTL user options //end RTL user options
opts->pulse_raw_rate_in = 48000; opts->pulse_raw_rate_in = 48000;
opts->pulse_raw_rate_out = 48000;// opts->pulse_raw_rate_out = 48000;//
@@ -1406,6 +1409,7 @@
printf (" pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O) \n"); printf (" pulse:6 or pulse:virtual_sink2.monitor for pulse audio signal input on virtual_sink2 (see -O) \n");
printf (" rtl for rtl dongle (Default Values -- see below)\n"); printf (" rtl for rtl dongle (Default Values -- see below)\n");
printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n"); printf (" rtl:dev:freq:gain:ppm:bw:sq:vol for rtl dongle (see below)\n");
printf (" rtltcp:host:port:freq:gain:ppm:bw:sq:vol for rtl dongle on an rtl_tcp server (see below)\n");
printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n"); printf (" tcp for tcp client SDR++/GNURadio Companion/Other (Port 7355)\n");
printf (" tcp:192.168.7.5:7355 for custom address and port \n"); printf (" tcp:192.168.7.5:7355 for custom address and port \n");
printf (" filename.bin for OP25/FME capture bin files\n"); printf (" filename.bin for OP25/FME capture bin files\n");
@@ -1477,6 +1481,11 @@
printf (" vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)\n"); printf (" vol <num> RTL-SDR Sample 'Volume' Multiplier (default = 2)(1,2,3)\n");
printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n"); printf (" Example: dsd-fme -fs -i rtl -C cap_plus_channel.csv -T\n");
printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n"); printf (" Example: dsd-fme -fp -i rtl:0:851.375M:22:-2:24:0:2\n");
printf (" Usage: rtltcp:host:port:freq:gain:ppm:bw:sq:vol\n");
printf (" host <name> rtl_tcp Server Hostname or IP Address (default = localhost)\n");
printf (" port <num> rtl_tcp Server Port (default = 1234)\n");
printf (" freq, gain, ppm, bw, sq, vol as above\n");
printf (" Example: dsd-fme -fs -i rtltcp:192.168.1.10:1234:450M:26:-2:8 -C connect_plus_chan.csv -T\n");
printf ("\n"); printf ("\n");
printf ("Decoder options:\n"); printf ("Decoder options:\n");
printf (" -fa Auto Detection\n"); printf (" -fa Auto Detection\n");
@@ -3281,9 +3290,24 @@
if (curr != NULL) ; //continue if (curr != NULL) ; //continue
else goto RTLEND; //end early with preset values else goto RTLEND; //end early with preset values
curr = strtok(NULL, ":"); //rtl device number "-D" if (strcmp (curr, "rtltcp") == 0) //rtl_tcp server instead of a local dongle
if (curr != NULL) opts.rtl_dev_index = atoi (curr); {
else goto RTLEND; opts.rtltcp_enabled = 1;
curr = strtok(NULL, ":"); //rtl_tcp hostname
if (curr != NULL) snprintf (opts.rtltcp_hostname, sizeof(opts.rtltcp_hostname), "%s", curr);
else goto RTLEND;
curr = strtok(NULL, ":"); //rtl_tcp port
if (curr != NULL) opts.rtltcp_portno = atoi (curr);
else goto RTLEND;
}
else
{
curr = strtok(NULL, ":"); //rtl device number "-D"
if (curr != NULL) opts.rtl_dev_index = atoi (curr);
else goto RTLEND;
}
curr = strtok(NULL, ":"); //rtl freq "-c" curr = strtok(NULL, ":"); //rtl freq "-c"
if (curr != NULL) opts.rtlsdr_center_freq = (uint32_t)atofs(curr); if (curr != NULL) opts.rtlsdr_center_freq = (uint32_t)atofs(curr);
@@ -3326,6 +3350,8 @@
RTLEND: RTLEND:
if (opts.rtltcp_enabled == 1) goto RTLTCPEND; //no local dongle to look for
device_count = rtlsdr_get_device_count(); device_count = rtlsdr_get_device_count();
if (!device_count) if (!device_count)
{ {
@@ -3353,10 +3379,15 @@
} }
RTLTCPEND:
if (opts.rtl_volume_multiplier > 3 || opts.rtl_volume_multiplier < 0) if (opts.rtl_volume_multiplier > 3 || opts.rtl_volume_multiplier < 0)
opts.rtl_volume_multiplier = 1; //I wonder if you could flip polarity by using -1 opts.rtl_volume_multiplier = 1; //I wonder if you could flip polarity by using -1
fprintf (stderr, "Dev %d ", opts.rtl_dev_index); if (opts.rtltcp_enabled == 1)
fprintf (stderr, "rtl_tcp %s:%d ", opts.rtltcp_hostname, opts.rtltcp_portno);
else
fprintf (stderr, "Dev %d ", opts.rtl_dev_index);
fprintf (stderr, "Freq %d ", opts.rtlsdr_center_freq); fprintf (stderr, "Freq %d ", opts.rtlsdr_center_freq);
fprintf (stderr, "Gain %d ", opts.rtl_gain_value); fprintf (stderr, "Gain %d ", opts.rtl_gain_value);
fprintf (stderr, "PPM %d ", opts.rtlsdr_ppm_error); fprintf (stderr, "PPM %d ", opts.rtlsdr_ppm_error);

View File

@@ -567,7 +567,7 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
static void *dongle_thread_fn(void *arg) static void *dongle_thread_fn(void *arg)
{ {
struct dongle_state *s = static_cast<dongle_state*>(arg); struct dongle_state *s = static_cast<dongle_state*>(arg);
rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); rtlsdr_or_tcp_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len);
return 0; return 0;
} }
@@ -604,17 +604,17 @@ int nearest_gain(rtlsdr_dev_t *dev, int target_gain)
{ {
int i, r, err1, err2, count, nearest; int i, r, err1, err2, count, nearest;
int* gains; int* gains;
r = rtlsdr_set_tuner_gain_mode(dev, 1); r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 1);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to enable manual gain.\n"); fprintf (stderr, "WARNING: Failed to enable manual gain.\n");
return r; return r;
} }
count = rtlsdr_get_tuner_gains(dev, NULL); count = rtlsdr_or_tcp_get_tuner_gains(dev, NULL);
if (count <= 0) { if (count <= 0) {
return 0; return 0;
} }
gains = static_cast<int*>(malloc(sizeof(int) * count)); gains = static_cast<int*>(malloc(sizeof(int) * count));
count = rtlsdr_get_tuner_gains(dev, gains); count = rtlsdr_or_tcp_get_tuner_gains(dev, gains);
nearest = gains[0]; nearest = gains[0];
for (i=0; i<count; i++) { for (i=0; i<count; i++) {
err1 = abs(target_gain - nearest); err1 = abs(target_gain - nearest);
@@ -634,7 +634,7 @@ int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency)
//spam tuning //spam tuning
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
r = rtlsdr_set_center_freq(dev, frequency); r = rtlsdr_or_tcp_set_center_freq(dev, frequency);
if (r < 0) if (r < 0)
fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq); fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq);
else else
@@ -650,7 +650,7 @@ int verbose_set_frequency(rtlsdr_dev_t *dev, uint32_t frequency)
int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
{ {
int r; int r;
r = rtlsdr_set_sample_rate(dev, samp_rate); r = rtlsdr_or_tcp_set_sample_rate(dev, samp_rate);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to set sample rate.\n"); fprintf (stderr, "WARNING: Failed to set sample rate.\n");
} else { } else {
@@ -662,7 +662,7 @@ int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate)
int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) int verbose_direct_sampling(rtlsdr_dev_t *dev, int on)
{ {
int r; int r;
r = rtlsdr_set_direct_sampling(dev, on); r = rtlsdr_or_tcp_set_direct_sampling(dev, on);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set direct sampling mode.\n"); fprintf (stderr, "WARNING: Failed to set direct sampling mode.\n");
return r; return r;
@@ -679,7 +679,7 @@ int verbose_direct_sampling(rtlsdr_dev_t *dev, int on)
int verbose_offset_tuning(rtlsdr_dev_t *dev) int verbose_offset_tuning(rtlsdr_dev_t *dev)
{ {
int r; int r;
r = rtlsdr_set_offset_tuning(dev, 1); r = rtlsdr_or_tcp_set_offset_tuning(dev, 1);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set offset tuning.\n"); fprintf (stderr, "WARNING: Failed to set offset tuning.\n");
} else { } else {
@@ -691,7 +691,7 @@ int verbose_offset_tuning(rtlsdr_dev_t *dev)
int verbose_auto_gain(rtlsdr_dev_t *dev) int verbose_auto_gain(rtlsdr_dev_t *dev)
{ {
int r; int r;
r = rtlsdr_set_tuner_gain_mode(dev, 0); r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 0);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set tuner gain.\n"); fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
} else { } else {
@@ -703,12 +703,12 @@ int verbose_auto_gain(rtlsdr_dev_t *dev)
int verbose_gain_set(rtlsdr_dev_t *dev, int gain) int verbose_gain_set(rtlsdr_dev_t *dev, int gain)
{ {
int r; int r;
r = rtlsdr_set_tuner_gain_mode(dev, 1); r = rtlsdr_or_tcp_set_tuner_gain_mode(dev, 1);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to enable manual gain.\n"); fprintf (stderr, "WARNING: Failed to enable manual gain.\n");
return r; return r;
} }
r = rtlsdr_set_tuner_gain(dev, gain); r = rtlsdr_or_tcp_set_tuner_gain(dev, gain);
if (r != 0) { if (r != 0) {
fprintf (stderr, "WARNING: Failed to set tuner gain.\n"); fprintf (stderr, "WARNING: Failed to set tuner gain.\n");
} else { } else {
@@ -722,7 +722,7 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
int r; int r;
// if (ppm_error == 0) { // if (ppm_error == 0) {
// return 0;} // return 0;}
r = rtlsdr_set_freq_correction(dev, ppm_error); r = rtlsdr_or_tcp_set_freq_correction(dev, ppm_error);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to set ppm error.\n"); fprintf (stderr, "WARNING: Failed to set ppm error.\n");
} else { } else {
@@ -734,7 +734,7 @@ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error)
int verbose_reset_buffer(rtlsdr_dev_t *dev) int verbose_reset_buffer(rtlsdr_dev_t *dev)
{ {
int r; int r;
r = rtlsdr_reset_buffer(dev); r = rtlsdr_or_tcp_reset_buffer(dev);
if (r < 0) { if (r < 0) {
fprintf (stderr, "WARNING: Failed to reset buffers.\n");} fprintf (stderr, "WARNING: Failed to reset buffers.\n");}
return r; return r;
@@ -807,7 +807,7 @@ static void *controller_thread_fn(void *arg)
/* hacky hopping */ /* hacky hopping */
s->freq_now = (s->freq_now + 1) % s->freq_len; s->freq_now = (s->freq_now + 1) % s->freq_len;
optimal_settings(s->freqs[s->freq_now], demod.rate_in); optimal_settings(s->freqs[s->freq_now], demod.rate_in);
rtlsdr_set_center_freq(dongle.dev, dongle.freq); rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
dongle.mute = BUFFER_DUMP; dongle.mute = BUFFER_DUMP;
} }
return 0; return 0;
@@ -1012,7 +1012,7 @@ static void *socket_thread_fn(void *arg) {
new_freq = chars_to_int(buffer); new_freq = chars_to_int(buffer);
dongle.freq = new_freq; dongle.freq = new_freq;
optimal_settings(new_freq, demod.rate_in); optimal_settings(new_freq, demod.rate_in);
rtlsdr_set_center_freq(dongle.dev, dongle.freq); rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq); fprintf (stderr, "\nTuning to: %d [Hz] \n", new_freq);
} }
@@ -1027,7 +1027,7 @@ static void *socket_thread_fn(void *arg) {
void rtlsdr_sighandler() void rtlsdr_sighandler()
{ {
fprintf (stderr, "Signal caught, exiting!\n"); fprintf (stderr, "Signal caught, exiting!\n");
rtlsdr_cancel_async(dongle.dev); rtlsdr_or_tcp_cancel_async(dongle.dev);
} }
void open_rtlsdr_stream(dsd_opts *opts) void open_rtlsdr_stream(dsd_opts *opts)
@@ -1080,14 +1080,23 @@ void open_rtlsdr_stream(dsd_opts *opts)
ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH;
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); if (opts->rtltcp_enabled == 1)
if (r < 0)
{ {
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index); if (rtltcp_open(opts->rtltcp_hostname, opts->rtltcp_portno) < 0)
exit(1); //rtltcp_open prints its own error messages
} else { exit(1);
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index); }
} else
{
r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index);
if (r < 0)
{
fprintf (stderr, "Failed to open rtlsdr device %d.\n", dongle.dev_index);
exit(1);
} else {
fprintf (stderr, "Using RTLSDR Device Index: %d. \n", dongle.dev_index);
}
}
if (demod.deemph) { if (demod.deemph) {
demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6))))); demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * 75e-6)))));
@@ -1119,7 +1128,7 @@ void open_rtlsdr_stream(dsd_opts *opts)
void cleanup_rtlsdr_stream() void cleanup_rtlsdr_stream()
{ {
fprintf (stderr, "cleaning up...\n"); fprintf (stderr, "cleaning up...\n");
rtlsdr_cancel_async(dongle.dev); rtlsdr_or_tcp_cancel_async(dongle.dev);
pthread_join(dongle.thread, NULL); pthread_join(dongle.thread, NULL);
safe_cond_signal(&demod.ready, &demod.ready_m); safe_cond_signal(&demod.ready, &demod.ready_m);
pthread_join(demod.thread, NULL); pthread_join(demod.thread, NULL);
@@ -1132,7 +1141,7 @@ void cleanup_rtlsdr_stream()
output_cleanup(&output); output_cleanup(&output);
controller_cleanup(&controller); controller_cleanup(&controller);
rtlsdr_close(dongle.dev); rtlsdr_or_tcp_close(dongle.dev);
} }
//original for safe keeping //original for safe keeping
@@ -1241,7 +1250,7 @@ void rtl_dev_tune(dsd_opts * opts, long int frequency)
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
int r = -1; int r = -1;
r = rtlsdr_set_center_freq(dongle.dev, dongle.freq); r = rtlsdr_or_tcp_set_center_freq(dongle.dev, dongle.freq);
if (r < 0) if (r < 0)
fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq); fprintf (stderr, " (RETRY: Failed to set Center Frequency %u). \n", dongle.freq);
else break; else break;

247
src/rtl_sdr_tcp.c Normal file
View File

@@ -0,0 +1,247 @@
/*-------------------------------------------------------------------------------
* rtl_sdr_tcp.c
* rtl_tcp client for the RTL-SDR input: samples arrive on a TCP socket, and tuner commands are sent back on it
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
//MSG_NOSIGNAL is missing before macOS 11; SO_NOSIGPIPE in rtltcp_open keeps send from raising SIGPIPE there
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
//rtlsdr_read_async's default block size, in bytes
#define RTLTCP_BUF_LENGTH (16 * 32 * 512)
//rtl_tcp state: socket, tuner type, and a mutex that keeps commands from different threads from interleaving
//NOTE: Keeping these as static global is not great. They really should be in some kind of opaque handle
// like rtl_tcp_dev_t or something. For applications that only use one device at a time, this is fine.
// But if we ever move to supporting multiple simultaneous RTL tuners, this will need to change.
static int rtltcp_sock = -1;
static int rtltcp_tuner_type = RTLSDR_TUNER_UNKNOWN; //from the server's greeting
static pthread_mutex_t rtltcp_m = PTHREAD_MUTEX_INITIALIZER;
//rtl_tcp command bytes, each named after the librtlsdr function the server calls with the parameter
enum
{
RTLTCP_SET_CENTER_FREQ = 0x01,
RTLTCP_SET_SAMPLE_RATE = 0x02,
RTLTCP_SET_TUNER_GAIN_MODE = 0x03,
RTLTCP_SET_TUNER_GAIN = 0x04,
RTLTCP_SET_FREQ_CORRECTION = 0x05,
RTLTCP_SET_DIRECT_SAMPLING = 0x09,
RTLTCP_SET_OFFSET_TUNING = 0x0a
};
//send one rtl_tcp command: a command byte followed by a 32-bit big-endian parameter.
//A partly sent command would misalign every later one, so a failed send ends the connection.
static int rtltcp_command(uint8_t cmd, uint32_t param)
{
//encode command and parameter to send to rtl_tcp
unsigned char buf[5] = {cmd, (unsigned char)(param >> 24), (unsigned char)(param >> 16), (unsigned char)(param >> 8), (unsigned char)param};
size_t sent = 0;
pthread_mutex_lock(&rtltcp_m);
while (sent < 5) {
ssize_t n = send(rtltcp_sock, buf + sent, 5 - sent, MSG_NOSIGNAL);
if (n < 0 && errno == EINTR) {
continue;
}
if (n <= 0) {
break;
}
sent += n;
}
//a failed or partial send ends the connection
if (sent < 5) {
shutdown(rtltcp_sock, SHUT_RDWR);
}
pthread_mutex_unlock(&rtltcp_m);
return sent < 5 ? -1 : 0;
}
//connect to an rtl_tcp server over IPv4 or IPv6, read its greeting, and store the socket in rtltcp_sock; prints the outcome and returns 0, or -1 on failure
int rtltcp_open(const char *hostname, int portno)
{
struct addrinfo hints, *res, *ai;
char portstr[16];
unsigned char header[12];
int sock = -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
snprintf(portstr, sizeof(portstr), "%d", portno);
if (getaddrinfo(hostname, portstr, &hints, &res) != 0) {
fprintf (stderr, "Failed to resolve rtl_tcp server %s.\n", hostname);
return -1;
}
//try each resolved address in turn until one connects
for (ai = res; ai != NULL; ai = ai->ai_next) {
//socket creation
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
continue;
}
//connect
if (connect(sock, ai->ai_addr, ai->ai_addrlen) == 0) {
break;
}
//close socket and retry with different address
close(sock);
sock = -1;
}
freeaddrinfo(res);
if (sock < 0) {
fprintf (stderr, "Failed to connect to rtl_tcp server %s:%d.\n", hostname, portno);
return -1;
}
#ifdef SO_NOSIGPIPE
//macOS and the BSDs: a send to a closed connection returns an error instead of raising SIGPIPE
int one = 1;
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
#endif
//the greeting is "RTL0", the tuner type, and the number of tuner gain steps
if (recv(sock, header, 12, MSG_WAITALL) != 12 || memcmp(header, "RTL0", 4) != 0) {
fprintf (stderr, "%s:%d did not send an rtl_tcp greeting.\n", hostname, portno);
close(sock);
return -1;
}
//socket is valid and ready to use
rtltcp_sock = sock;
//store tuner type for later use in rtltcp_get_tuner_gains
rtltcp_tuner_type = ((uint32_t)header[4] << 24) | ((uint32_t)header[5] << 16) | ((uint32_t)header[6] << 8) | header[7];
fprintf (stderr, "Using rtl_tcp server %s:%d. \n", hostname, portno);
return 0;
}
//rtlsdr_get_tuner_gains for the server's tuner: copies its gain steps, in tenths of a dB, into gains unless gains is NULL, and returns the number of steps
static int rtltcp_get_tuner_gains(int *gains)
{
//rtl_tcp advertises only the gain count, not the values; librtlsdr's gain query requires a local device.
//Unfortunately this means we have to copy librtlsdr's tables here to select the nearest valid gain given the remote device.
//It would be better if rtl_tcp told us the actual list of supported gains, but it doesn't.
static const int e4k[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420 };
static const int fc0012[] = { -99, -40, 71, 179, 192 };
static const int fc0013[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71, 179, 181, 182, 184, 186, 188, 191, 197 };
static const int r82xx[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496 };
static const int none[] = { 0 };
const int *table = none;
int count = 1;
//different tuners have different possible gains
switch (rtltcp_tuner_type) {
case RTLSDR_TUNER_E4000: table = e4k; count = sizeof(e4k) / sizeof(int); break;
case RTLSDR_TUNER_FC0012: table = fc0012; count = sizeof(fc0012) / sizeof(int); break;
case RTLSDR_TUNER_FC0013: table = fc0013; count = sizeof(fc0013) / sizeof(int); break;
case RTLSDR_TUNER_R820T:
case RTLSDR_TUNER_R828D: table = r82xx; count = sizeof(r82xx) / sizeof(int); break;
}
if (gains != NULL) {
memcpy(gains, table, count * sizeof(int));
}
return count;
}
//close the rtl_tcp connection; returns close's result
static int rtltcp_close(void)
{
int r = close(rtltcp_sock);
rtltcp_sock = -1;
return r;
}
//pass rtl_tcp samples to cb until the connection closes or exitflag is set, in the same block size that rtlsdr_read_async delivers by default
static int rtltcp_read_async(rtlsdr_read_async_cb_t cb, void *ctx)
{
static unsigned char buf[RTLTCP_BUF_LENGTH];
while (!exitflag && recv(rtltcp_sock, buf, RTLTCP_BUF_LENGTH, MSG_WAITALL) == RTLTCP_BUF_LENGTH) {
cb(buf, RTLTCP_BUF_LENGTH, ctx);
}
//we are finished (for a variety of reasons). ensure exitflag is set
if (!exitflag) {
fprintf (stderr, "rtl_tcp server closed the connection.\n");
exitflag = 1;
}
return 0;
}
//Each rtlsdr_or_tcp_* function takes the arguments of the librtlsdr function it is named after.
//While an rtl_tcp server is connected, the call goes to the server; otherwise it goes to librtlsdr.
int rtlsdr_or_tcp_close(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? rtltcp_close() : rtlsdr_close(dev);
}
int rtlsdr_or_tcp_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
return rtltcp_sock >= 0 ? rtltcp_read_async(cb, ctx) : rtlsdr_read_async(dev, cb, ctx, buf_num, buf_len);
}
int rtlsdr_or_tcp_cancel_async(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? shutdown(rtltcp_sock, SHUT_RDWR) : rtlsdr_cancel_async(dev);
}
int rtlsdr_or_tcp_reset_buffer(rtlsdr_dev_t *dev)
{
return rtltcp_sock >= 0 ? 0 : rtlsdr_reset_buffer(dev);
}
int rtlsdr_or_tcp_get_tuner_gains(rtlsdr_dev_t *dev, int *gains)
{
return rtltcp_sock >= 0 ? rtltcp_get_tuner_gains(gains) : rtlsdr_get_tuner_gains(dev, gains);
}
int rtlsdr_or_tcp_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_CENTER_FREQ, freq) : rtlsdr_set_center_freq(dev, freq);
}
int rtlsdr_or_tcp_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_SAMPLE_RATE, rate) : rtlsdr_set_sample_rate(dev, rate);
}
int rtlsdr_or_tcp_set_direct_sampling(rtlsdr_dev_t *dev, int on)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_DIRECT_SAMPLING, on) : rtlsdr_set_direct_sampling(dev, on);
}
int rtlsdr_or_tcp_set_offset_tuning(rtlsdr_dev_t *dev, int on)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_OFFSET_TUNING, on) : rtlsdr_set_offset_tuning(dev, on);
}
int rtlsdr_or_tcp_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN_MODE, manual) : rtlsdr_set_tuner_gain_mode(dev, manual);
}
int rtlsdr_or_tcp_set_tuner_gain(rtlsdr_dev_t *dev, int gain)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_TUNER_GAIN, gain) : rtlsdr_set_tuner_gain(dev, gain);
}
int rtlsdr_or_tcp_set_freq_correction(rtlsdr_dev_t *dev, int ppm)
{
return rtltcp_sock >= 0 ? rtltcp_command(RTLTCP_SET_FREQ_CORRECTION, ppm) : rtlsdr_set_freq_correction(dev, ppm);
}