diff options
Diffstat (limited to 'utils/rtl_tcp.c')
-rw-r--r-- | utils/rtl_tcp.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/utils/rtl_tcp.c b/utils/rtl_tcp.c new file mode 100644 index 0000000..317e0f3 --- /dev/null +++ b/utils/rtl_tcp.c @@ -0,0 +1,603 @@ +/* + * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver + * Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de> + * Copyright (C) 2012-2013 by Hoernchen <la@tfc-server.de> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef _WIN32 +#include <unistd.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <fcntl.h> +#else +#include <winsock2.h> +#include "getopt/getopt.h" +#endif + +#include <pthread.h> + +#include "rtl-sdr.h" +#include "convenience/convenience.h" + +#ifdef _WIN32 +#pragma comment(lib, "ws2_32.lib") + +typedef int socklen_t; + +#else +#define closesocket close +#define SOCKADDR struct sockaddr +#define SOCKET int +#define SOCKET_ERROR -1 +#endif + +static SOCKET s; + +static pthread_t tcp_worker_thread; +static pthread_t command_thread; +static pthread_cond_t exit_cond; +static pthread_mutex_t exit_cond_lock; + +static pthread_mutex_t ll_mutex; +static pthread_cond_t cond; + +struct llist { + char *data; + size_t len; + struct llist *next; +}; + +typedef struct { /* structure size must be multiple of 2 bytes */ + char magic[4]; + uint32_t tuner_type; + uint32_t tuner_gain_count; +} dongle_info_t; + +static rtlsdr_dev_t *dev = NULL; + +static int global_numq = 0; +static struct llist *ll_buffers = 0; +static int llbuf_num = 500; + +static volatile int do_exit = 0; + +void usage(void) +{ + printf("rtl_tcp, an I/Q spectrum server for RTL2832 based DVB-T receivers\n\n" + "Usage:\t[-a listen address]\n" + "\t[-p listen port (default: 1234)]\n" + "\t[-f frequency to tune to [Hz]]\n" + "\t[-g gain (default: 0 for auto)]\n" + "\t[-s samplerate in Hz (default: 2048000 Hz)]\n" + "\t[-b number of buffers (default: 15, set by library)]\n" + "\t[-n max number of linked list buffers to keep (default: 500)]\n" + "\t[-d device index (default: 0)]\n" + "\t[-P ppm_error (default: 0)]\n"); + exit(1); +} + +#ifdef _WIN32 +int gettimeofday(struct timeval *tv, void* ignored) +{ + FILETIME ft; + unsigned __int64 tmp = 0; + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + tmp |= ft.dwHighDateTime; + tmp <<= 32; + tmp |= ft.dwLowDateTime; + tmp /= 10; +#ifdef _MSC_VER + tmp -= 11644473600000000Ui64; +#else + tmp -= 11644473600000000ULL; +#endif + tv->tv_sec = (long)(tmp / 1000000UL); + tv->tv_usec = (long)(tmp % 1000000UL); + } + return 0; +} + +BOOL WINAPI +sighandler(int signum) +{ + if (CTRL_C_EVENT == signum) { + fprintf(stderr, "Signal caught, exiting!\n"); + do_exit = 1; + rtlsdr_cancel_async(dev); + return TRUE; + } + return FALSE; +} +#else +static void sighandler(int signum) +{ + fprintf(stderr, "Signal caught, exiting!\n"); + rtlsdr_cancel_async(dev); + do_exit = 1; +} +#endif + +void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) +{ + if(!do_exit) { + struct llist *rpt = (struct llist*)malloc(sizeof(struct llist)); + rpt->data = (char*)malloc(len); + memcpy(rpt->data, buf, len); + rpt->len = len; + rpt->next = NULL; + + pthread_mutex_lock(&ll_mutex); + + if (ll_buffers == NULL) { + ll_buffers = rpt; + } else { + struct llist *cur = ll_buffers; + int num_queued = 0; + + while (cur->next != NULL) { + cur = cur->next; + num_queued++; + } + + if(llbuf_num && llbuf_num == num_queued-2){ + struct llist *curelem; + + free(ll_buffers->data); + curelem = ll_buffers->next; + free(ll_buffers); + ll_buffers = curelem; + } + + cur->next = rpt; + + if (num_queued > global_numq) + printf("ll+, now %d\n", num_queued); + else if (num_queued < global_numq) + printf("ll-, now %d\n", num_queued); + + global_numq = num_queued; + } + pthread_cond_signal(&cond); + pthread_mutex_unlock(&ll_mutex); + } +} + +static void *tcp_worker(void *arg) +{ + struct llist *curelem,*prev; + int bytesleft,bytessent, index; + struct timeval tv= {1,0}; + struct timespec ts; + struct timeval tp; + fd_set writefds; + int r = 0; + + while(1) { + if(do_exit) + pthread_exit(0); + + pthread_mutex_lock(&ll_mutex); + gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec+5; + ts.tv_nsec = tp.tv_usec * 1000; + r = pthread_cond_timedwait(&cond, &ll_mutex, &ts); + if(r == ETIMEDOUT) { + pthread_mutex_unlock(&ll_mutex); + printf("worker cond timeout\n"); + sighandler(0); + pthread_exit(NULL); + } + + curelem = ll_buffers; + ll_buffers = 0; + pthread_mutex_unlock(&ll_mutex); + + while(curelem != 0) { + bytesleft = curelem->len; + index = 0; + bytessent = 0; + while(bytesleft > 0) { + FD_ZERO(&writefds); + FD_SET(s, &writefds); + tv.tv_sec = 1; + tv.tv_usec = 0; + r = select(s+1, NULL, &writefds, NULL, &tv); + if(r) { + bytessent = send(s, &curelem->data[index], bytesleft, 0); + bytesleft -= bytessent; + index += bytessent; + } + if(bytessent == SOCKET_ERROR || do_exit) { + printf("worker socket bye\n"); + sighandler(0); + pthread_exit(NULL); + } + } + prev = curelem; + curelem = curelem->next; + free(prev->data); + free(prev); + } + } +} + +static int set_gain_by_index(rtlsdr_dev_t *_dev, unsigned int index) +{ + int res = 0; + int* gains; + int count = rtlsdr_get_tuner_gains(_dev, NULL); + + if (count > 0 && (unsigned int)count > index) { + gains = malloc(sizeof(int) * count); + count = rtlsdr_get_tuner_gains(_dev, gains); + + res = rtlsdr_set_tuner_gain(_dev, gains[index]); + + free(gains); + } + + return res; +} + +#ifdef _WIN32 +#define __attribute__(x) +#pragma pack(push, 1) +#endif +struct command{ + unsigned char cmd; + unsigned int param; +}__attribute__((packed)); +#ifdef _WIN32 +#pragma pack(pop) +#endif +static void *command_worker(void *arg) +{ + int left, received = 0; + fd_set readfds; + struct command cmd={0, 0}; + struct timeval tv= {1, 0}; + int r = 0; + uint32_t tmp; + + while(1) { + left=sizeof(cmd); + while(left >0) { + FD_ZERO(&readfds); + FD_SET(s, &readfds); + tv.tv_sec = 1; + tv.tv_usec = 0; + r = select(s+1, &readfds, NULL, NULL, &tv); + if(r) { + received = recv(s, (char*)&cmd+(sizeof(cmd)-left), left, 0); + left -= received; + } + if(received == SOCKET_ERROR || do_exit) { + printf("comm recv bye\n"); + sighandler(0); + pthread_exit(NULL); + } + } + switch(cmd.cmd) { + case 0x01: + printf("set freq %d\n", ntohl(cmd.param)); + rtlsdr_set_center_freq(dev,ntohl(cmd.param)); + break; + case 0x02: + printf("set sample rate %d\n", ntohl(cmd.param)); + rtlsdr_set_sample_rate(dev, ntohl(cmd.param)); + break; + case 0x03: + printf("set gain mode %d\n", ntohl(cmd.param)); + rtlsdr_set_tuner_gain_mode(dev, ntohl(cmd.param)); + break; + case 0x04: + printf("set gain %d\n", ntohl(cmd.param)); + rtlsdr_set_tuner_gain(dev, ntohl(cmd.param)); + break; + case 0x05: + printf("set freq correction %d\n", ntohl(cmd.param)); + rtlsdr_set_freq_correction(dev, ntohl(cmd.param)); + break; + case 0x06: + tmp = ntohl(cmd.param); + printf("set if stage %d gain %d\n", tmp >> 16, (short)(tmp & 0xffff)); + rtlsdr_set_tuner_if_gain(dev, tmp >> 16, (short)(tmp & 0xffff)); + break; + case 0x07: + printf("set test mode %d\n", ntohl(cmd.param)); + rtlsdr_set_testmode(dev, ntohl(cmd.param)); + break; + case 0x08: + printf("set agc mode %d\n", ntohl(cmd.param)); + rtlsdr_set_agc_mode(dev, ntohl(cmd.param)); + break; + case 0x09: + printf("set direct sampling %d\n", ntohl(cmd.param)); + rtlsdr_set_direct_sampling(dev, ntohl(cmd.param)); + break; + case 0x0a: + printf("set offset tuning %d\n", ntohl(cmd.param)); + rtlsdr_set_offset_tuning(dev, ntohl(cmd.param)); + break; + case 0x0b: + printf("set rtl xtal %d\n", ntohl(cmd.param)); + rtlsdr_set_xtal_freq(dev, ntohl(cmd.param), 0); + break; + case 0x0c: + printf("set tuner xtal %d\n", ntohl(cmd.param)); + rtlsdr_set_xtal_freq(dev, 0, ntohl(cmd.param)); + break; + case 0x0d: + printf("set tuner gain by index %d\n", ntohl(cmd.param)); + set_gain_by_index(dev, ntohl(cmd.param)); + break; + default: + break; + } + cmd.cmd = 0xff; + } +} + +int main(int argc, char **argv) +{ + int r, opt, i; + char* addr = "127.0.0.1"; + int port = 1234; + uint32_t frequency = 100000000, samp_rate = 2048000; + struct sockaddr_in local, remote; + uint32_t buf_num = 0; + int dev_index = 0; + int dev_given = 0; + int gain = 0; + int ppm_error = 0; + struct llist *curelem,*prev; + pthread_attr_t attr; + void *status; + struct timeval tv = {1,0}; + struct linger ling = {1,0}; + SOCKET listensocket; + socklen_t rlen; + fd_set readfds; + u_long blockmode = 1; + dongle_info_t dongle_info; +#ifdef _WIN32 + WSADATA wsd; + i = WSAStartup(MAKEWORD(2,2), &wsd); +#else + struct sigaction sigact, sigign; +#endif + + while ((opt = getopt(argc, argv, "a:p:f:g:s:b:n:d:P:")) != -1) { + switch (opt) { + case 'd': + dev_index = verbose_device_search(optarg); + dev_given = 1; + break; + case 'f': + frequency = (uint32_t)atofs(optarg); + break; + case 'g': + gain = (int)(atof(optarg) * 10); /* tenths of a dB */ + break; + case 's': + samp_rate = (uint32_t)atofs(optarg); + break; + case 'a': + addr = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'b': + buf_num = atoi(optarg); + break; + case 'n': + llbuf_num = atoi(optarg); + break; + case 'P': + ppm_error = atoi(optarg); + break; + default: + usage(); + break; + } + } + + if (argc < optind) + usage(); + + if (!dev_given) { + dev_index = verbose_device_search("0"); + } + + if (dev_index < 0) { + exit(1); + } + + rtlsdr_open(&dev, (uint32_t)dev_index); + if (NULL == dev) { + fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); + exit(1); + } + +#ifndef _WIN32 + sigact.sa_handler = sighandler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigign.sa_handler = SIG_IGN; + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); + sigaction(SIGPIPE, &sigign, NULL); +#else + SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); +#endif + + /* Set the tuner error */ + verbose_ppm_set(dev, ppm_error); + + /* Set the sample rate */ + r = rtlsdr_set_sample_rate(dev, samp_rate); + if (r < 0) + fprintf(stderr, "WARNING: Failed to set sample rate.\n"); + + /* Set the frequency */ + r = rtlsdr_set_center_freq(dev, frequency); + if (r < 0) + fprintf(stderr, "WARNING: Failed to set center freq.\n"); + else + fprintf(stderr, "Tuned to %i Hz.\n", frequency); + + if (0 == gain) { + /* Enable automatic gain */ + r = rtlsdr_set_tuner_gain_mode(dev, 0); + if (r < 0) + fprintf(stderr, "WARNING: Failed to enable automatic gain.\n"); + } else { + /* Enable manual gain */ + r = rtlsdr_set_tuner_gain_mode(dev, 1); + if (r < 0) + fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); + + /* Set the tuner gain */ + r = rtlsdr_set_tuner_gain(dev, gain); + if (r < 0) + fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); + else + fprintf(stderr, "Tuner gain set to %f dB.\n", gain/10.0); + } + + /* Reset endpoint before we start reading from it (mandatory) */ + r = rtlsdr_reset_buffer(dev); + if (r < 0) + fprintf(stderr, "WARNING: Failed to reset buffers.\n"); + + pthread_mutex_init(&exit_cond_lock, NULL); + pthread_mutex_init(&ll_mutex, NULL); + pthread_mutex_init(&exit_cond_lock, NULL); + pthread_cond_init(&cond, NULL); + pthread_cond_init(&exit_cond, NULL); + + memset(&local,0,sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(port); + local.sin_addr.s_addr = inet_addr(addr); + + listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + r = 1; + setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); + setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); + bind(listensocket,(struct sockaddr *)&local,sizeof(local)); + +#ifdef _WIN32 + ioctlsocket(listensocket, FIONBIO, &blockmode); +#else + r = fcntl(listensocket, F_GETFL, 0); + r = fcntl(listensocket, F_SETFL, r | O_NONBLOCK); +#endif + + while(1) { + printf("listening...\n"); + printf("Use the device argument 'rtl_tcp=%s:%d' in OsmoSDR " + "(gr-osmosdr) source\n" + "to receive samples in GRC and control " + "rtl_tcp parameters (frequency, gain, ...).\n", + addr, port); + listen(listensocket,1); + + while(1) { + FD_ZERO(&readfds); + FD_SET(listensocket, &readfds); + tv.tv_sec = 1; + tv.tv_usec = 0; + r = select(listensocket+1, &readfds, NULL, NULL, &tv); + if(do_exit) { + goto out; + } else if(r) { + rlen = sizeof(remote); + s = accept(listensocket,(struct sockaddr *)&remote, &rlen); + break; + } + } + + setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); + + printf("client accepted!\n"); + + memset(&dongle_info, 0, sizeof(dongle_info)); + memcpy(&dongle_info.magic, "RTL0", 4); + + r = rtlsdr_get_tuner_type(dev); + if (r >= 0) + dongle_info.tuner_type = htonl(r); + + r = rtlsdr_get_tuner_gains(dev, NULL); + if (r >= 0) + dongle_info.tuner_gain_count = htonl(r); + + r = send(s, (const char *)&dongle_info, sizeof(dongle_info), 0); + if (sizeof(dongle_info) != r) + printf("failed to send dongle information\n"); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + r = pthread_create(&tcp_worker_thread, &attr, tcp_worker, NULL); + r = pthread_create(&command_thread, &attr, command_worker, NULL); + pthread_attr_destroy(&attr); + + r = rtlsdr_read_async(dev, rtlsdr_callback, NULL, buf_num, 0); + + pthread_join(tcp_worker_thread, &status); + pthread_join(command_thread, &status); + + closesocket(s); + + printf("all threads dead..\n"); + curelem = ll_buffers; + ll_buffers = 0; + + while(curelem != 0) { + prev = curelem; + curelem = curelem->next; + free(prev->data); + free(prev); + } + + do_exit = 0; + global_numq = 0; + } + +out: + rtlsdr_close(dev); + closesocket(listensocket); + closesocket(s); +#ifdef _WIN32 + WSACleanup(); +#endif + printf("bye!\n"); + return r >= 0 ? r : -r; +} |