From 62656fc45b91db3bc866f9865a5260a65d64d603 Mon Sep 17 00:00:00 2001 From: John Greb Date: Wed, 5 Nov 2014 12:34:33 +0000 Subject: [PATCH] Borrow layer from GnuRadio Plugin. --- CMakeLists.txt | 4 + Readme.md | 3 + plugins/channel/CMakeLists.txt | 5 +- plugins/samplesource/CMakeLists.txt | 7 +- plugins/samplesource/v4l/CMakeLists.txt | 8 +- plugins/samplesource/v4l/v4lplugin.cpp | 2 +- plugins/samplesource/v4l/v4lsource.cpp | 301 ++++++++++++++++++++++++ plugins/samplesource/v4l/v4lsource.h | 74 ++++++ 8 files changed, 398 insertions(+), 6 deletions(-) create mode 100644 plugins/samplesource/v4l/v4lsource.cpp create mode 100644 plugins/samplesource/v4l/v4lsource.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e47d2b91..051c303d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ cmake_minimum_required(VERSION 2.8.9) + +option(KERNEL "Use Linux Kernel Video4Linux Source." OFF) +option(IS96KHZ "Upper SideBand decoder needs 96kHz" OFF) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) project(sdrangelove) diff --git a/Readme.md b/Readme.md index 6608c1d0d..9725c296d 100644 --- a/Readme.md +++ b/Readme.md @@ -4,3 +4,6 @@ For Ubuntu: mkdir out && cd out && cmake ../ && make "librtlsdr-dev" is in the "universe" repo. (utopic 14.10 amd64.) + +Use "cmake ../ -DKERNEL=ON" to build the Linux kernel driver. May support Airspy and Hackrf. + diff --git a/plugins/channel/CMakeLists.txt b/plugins/channel/CMakeLists.txt index d5aabf0fa..ac8f1bec8 100644 --- a/plugins/channel/CMakeLists.txt +++ b/plugins/channel/CMakeLists.txt @@ -3,5 +3,8 @@ project(demod) add_subdirectory(nfm) add_subdirectory(tcpsrc) add_subdirectory(ssb) -add_subdirectory(usb) + +if (IS96KHZ) + add_subdirectory(usb) +endif() #add_subdirectory(tetra) diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 31351330e..7f9e47f6b 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -2,7 +2,6 @@ project(samplesource) find_package(LibUSB) find_package(LibOsmoSDR) -find_package(LibRTLSDR) add_subdirectory(gnuradio) @@ -10,6 +9,12 @@ if(LIBUSB_FOUND AND LIBOSMOSDR_FOUND) add_subdirectory(osmosdr) endif(LIBUSB_FOUND AND LIBOSMOSDR_FOUND) +if(KERNEL AND UNIX) + add_subdirectory(v4l) +else() + find_package(LibRTLSDR) +endif() + if(LIBUSB_FOUND AND LIBRTLSDR_FOUND) add_subdirectory(rtlsdr) endif(LIBUSB_FOUND AND LIBRTLSDR_FOUND) diff --git a/plugins/samplesource/v4l/CMakeLists.txt b/plugins/samplesource/v4l/CMakeLists.txt index baeb1d217..ba6f6c4de 100644 --- a/plugins/samplesource/v4l/CMakeLists.txt +++ b/plugins/samplesource/v4l/CMakeLists.txt @@ -5,6 +5,7 @@ set(v4l_SOURCES v4linput.cpp v4lplugin.cpp v4lthread.cpp + v4lsource.cpp ) set(rtlsdr_HEADERS @@ -12,6 +13,7 @@ set(rtlsdr_HEADERS v4linput.h v4lplugin.h v4lthread.h + v4lsource.h ) set(v4l_FORMS @@ -31,7 +33,7 @@ add_definitions(${QT_DEFINITIONS}) add_definitions(-DQT_PLUGIN) add_definitions(-DQT_SHARED) -#qt4_wrap_cpp(rtlsdr_HEADERS_MOC ${v4l_HEADERS}) +#qt4_wrap_cpp(v4l_HEADERS_MOC ${v4l_HEADERS}) qt5_wrap_ui(v4l_FORMS_HEADERS ${v4l_FORMS}) add_library(inputv4l SHARED @@ -42,8 +44,8 @@ add_library(inputv4l SHARED target_link_libraries(inputv4l ${QT_LIBRARIES} - ${LIBRTLSDR_LIBRARIES} - ${LIBUSB_LIBRARIES} +# ${LIBRTLSDR_LIBRARIES} +# ${LIBUSB_LIBRARIES} sdrbase ) diff --git a/plugins/samplesource/v4l/v4lplugin.cpp b/plugins/samplesource/v4l/v4lplugin.cpp index f46b41d2e..702e4968c 100644 --- a/plugins/samplesource/v4l/v4lplugin.cpp +++ b/plugins/samplesource/v4l/v4lplugin.cpp @@ -47,7 +47,7 @@ PluginInterface::SampleSourceDevices V4LPlugin::enumSampleSources() if(rtlsdr_get_device_usb_strings((uint32_t)i, vendor, product, serial) != 0) continue; - QString displayedName(QString("RTL-SDR #%1 (%2 #%3)").arg(i + 1).arg(product).arg(serial)); + QString displayedName(QString("SDR #%1 (%2 #%3)").arg(i + 1).arg(product).arg(serial)); SimpleSerializer s(1); s.writeS32(1, i); result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.v4l", s.final())); diff --git a/plugins/samplesource/v4l/v4lsource.cpp b/plugins/samplesource/v4l/v4lsource.cpp new file mode 100644 index 000000000..7c809f897 --- /dev/null +++ b/plugins/samplesource/v4l/v4lsource.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2013 Antti Palosaari + * + * 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. + */ + +#include "v4lsource.h" + +/* Control classes */ +#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */ +/* User-class control IDs */ +#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) +#define V4L2_CID_USER_BASE V4L2_CID_BASE + +#define CID_SAMPLING_MODE ((V4L2_CID_USER_BASE | 0xf000) + 0) +#define CID_SAMPLING_RATE ((V4L2_CID_USER_BASE | 0xf000) + 1) +#define CID_SAMPLING_RESOLUTION ((V4L2_CID_USER_BASE | 0xf000) + 2) +#define CID_TUNER_RF ((V4L2_CID_USER_BASE | 0xf000) + 10) +#define CID_TUNER_BW ((V4L2_CID_USER_BASE | 0xf000) + 11) +#define CID_TUNER_IF ((V4L2_CID_USER_BASE | 0xf000) + 12) +#define CID_TUNER_GAIN ((V4L2_CID_USER_BASE | 0xf000) + 13) + +#define V4L2_PIX_FMT_SDR_U8 v4l2_fourcc('D', 'U', '0', '8') /* unsigned 8-bit */ +#define V4L2_PIX_FMT_SDR_U16LE v4l2_fourcc('D', 'U', '1', '6') /* unsigned 16-bit LE */ + +#define CLEAR(x) memset(&(x), 0, sizeof(x)) + +static void xioctl(int fh, unsigned long int request, void *arg) +{ + int ret; + + do { + ret = v4l2_ioctl(fh, request, arg); + } while (ret == -1 && ((errno == EINTR) || (errno == EAGAIN))); + if (ret == -1) { + fprintf(stderr, "error %d\n", errno); + } +} + +namespace kernel{ + + v4l::v4l(const char *filename) + { + struct v4l2_format fmt; + struct v4l2_buffer buf; + struct v4l2_requestbuffers req; + enum v4l2_buf_type type; + unsigned int i; + + recebuf_len = 0; + + // libv4l2 does not know SDR yet + // fd = v4l2_open(filename, O_RDWR | O_NONBLOCK, 0); + fd = open(filename, O_RDWR | O_NONBLOCK, 0); + if (fd < 0) { + perror("Cannot open device"); + } + + pixelformat = V4L2_PIX_FMT_SDR_U8; + //pixelformat = V4L2_PIX_FMT_SDR_U16LE; + + CLEAR(fmt); + fmt.type = V4L2_BUF_TYPE_SDR_CAPTURE; + fmt.fmt.sdr.pixelformat = pixelformat; + xioctl(fd, VIDIOC_S_FMT, &fmt); + if (fmt.fmt.sdr.pixelformat != pixelformat) { + printf("Libv4l didn't accept FLOAT format. Cannot proceed. Pixelformat %4.4s\n", + (char *)&fmt.fmt.sdr.pixelformat); + } + + printf("Selected stream format: %4.4s\n", (char *)&fmt.fmt.sdr.pixelformat); + + CLEAR(req); + req.count = 8; + req.type = V4L2_BUF_TYPE_SDR_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + xioctl(fd, VIDIOC_REQBUFS, &req); + + buffers = (struct buffer*) calloc(req.count, sizeof(*buffers)); + for (n_buffers = 0; n_buffers < req.count; n_buffers++) { + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_SDR_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + xioctl(fd, VIDIOC_QUERYBUF, &buf); + + buffers[n_buffers].length = buf.length; + buffers[n_buffers].start = v4l2_mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, buf.m.offset); + + if (buffers[n_buffers].start == MAP_FAILED) { + perror("mmap"); + } + } + + for (i = 0; i < n_buffers; i++) { + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_SDR_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + xioctl(fd, VIDIOC_QBUF, &buf); + } + + // start streaming + type = V4L2_BUF_TYPE_SDR_CAPTURE; + xioctl(fd, VIDIOC_STREAMON, &type); + } + + /* + * Our virtual destructor. + */ + v4l::~v4l() + { + unsigned int i; + enum v4l2_buf_type type; + + // stop streaming + type = V4L2_BUF_TYPE_SDR_CAPTURE; + xioctl(fd, VIDIOC_STREAMOFF, &type); + + for (i = 0; i < n_buffers; i++) + v4l2_munmap(buffers[i].start, buffers[i].length); + + v4l2_close(fd); + } + + void + v4l::set_samp_rate(double samp_rate) + { + struct v4l2_frequency frequency; + + memset (&frequency, 0, sizeof(frequency)); + frequency.tuner = 0; + frequency.type = V4L2_TUNER_ADC; + frequency.frequency = samp_rate / 1; + + if (v4l2_ioctl(fd, VIDIOC_S_FREQUENCY, &frequency) == -1) + perror("VIDIOC_S_FREQUENCY"); + + return; + } + + void + v4l::set_center_freq(double freq) + { + struct v4l2_frequency frequency; + + memset (&frequency, 0, sizeof(frequency)); + frequency.tuner = 1; + frequency.type = V4L2_TUNER_RF; + frequency.frequency = freq; + + if (v4l2_ioctl(fd, VIDIOC_S_FREQUENCY, &frequency) == -1) + perror("VIDIOC_S_FREQUENCY"); + + return; + } + + void + v4l::set_bandwidth(double bandwidth) + { + struct v4l2_ext_controls ext_ctrls; + struct v4l2_ext_control ext_ctrl; + + memset (&ext_ctrl, 0, sizeof(ext_ctrl)); + ext_ctrl.id = CID_TUNER_BW; + ext_ctrl.value = bandwidth; + + memset (&ext_ctrls, 0, sizeof(ext_ctrls)); + ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_USER; + ext_ctrls.count = 1; + ext_ctrls.controls = &ext_ctrl; + + if (v4l2_ioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) == -1) + perror("VIDIOC_S_EXT_CTRLS"); + + return; + } + + void + v4l::set_tuner_gain(double gain) + { + struct v4l2_ext_controls ext_ctrls; + struct v4l2_ext_control ext_ctrl; + + memset (&ext_ctrl, 0, sizeof(ext_ctrl)); + ext_ctrl.id = CID_TUNER_GAIN; + ext_ctrl.value = gain; + + memset (&ext_ctrls, 0, sizeof(ext_ctrls)); + ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_USER; + ext_ctrls.count = 1; + ext_ctrls.controls = &ext_ctrl; + + if (v4l2_ioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) == -1) + perror("VIDIOC_S_EXT_CTRLS"); + + return; + } + + int + v4l::work(int noutput_items, + void* input_items, + void* output_items) + { + //complex *out = (complex *) output_items; + int ret; + struct timeval tv; + struct v4l2_buffer buf; + fd_set fds; + unsigned int i, items = 0; + float *fptr = (float *) output_items; + +process_buf: + /* process received mmap buffer */ + uint8_t *u8src = (uint8_t *) recebuf_ptr; + uint16_t *u16src = (uint16_t *) recebuf_ptr; + + while (recebuf_len) { + if (pixelformat == V4L2_PIX_FMT_SDR_U8) { + *fptr++ = (*u8src++ - 127.5f) / 127.5f; + *fptr++ = (*u8src++ - 127.5f) / 127.5f; + recebuf_len -= 2; + items++; + recebuf_ptr = u8src; + } else if (pixelformat == V4L2_PIX_FMT_SDR_U16LE) { + *fptr++ = (*u16src++ - 32767.5f) / 32767.5f; + *fptr++ = (*u16src++ - 32767.5f) / 32767.5f; + recebuf_len -= 4; + items++; + recebuf_ptr = u16src; + } else { + recebuf_len = 0; + } + + if (items == noutput_items) + break; + } + + /* enqueue mmap buf after it is processed */ + if (recebuf_len == 0 && items != 0) { + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_SDR_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = recebuf_mmap_index; + xioctl(fd, VIDIOC_QBUF, &buf); + } + + /* signal DSP we have some samples to offer */ + if (items) + return items; + + /* Read data from device */ + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + + // Timeout + tv.tv_sec = 2; + tv.tv_usec = 0; + + ret = select(fd + 1, &fds, NULL, NULL, &tv); + } while ((ret == -1 && (errno = EINTR))); + if (ret == -1) { + perror("select"); + return errno; + } + + /* dequeue mmap buf (receive data) */ + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_SDR_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + xioctl(fd, VIDIOC_DQBUF, &buf); + + /* store buffer in order to handle it during next call */ + recebuf_ptr = buffers[buf.index].start; + recebuf_len = buf.bytesused; + recebuf_mmap_index = buf.index; + /* FIXME: */ + goto process_buf; + + // Tell runtime system how many output items we produced. + return 0; + } + +} /* namespace kernel */ + diff --git a/plugins/samplesource/v4l/v4lsource.h b/plugins/samplesource/v4l/v4lsource.h new file mode 100644 index 000000000..7550010e0 --- /dev/null +++ b/plugins/samplesource/v4l/v4lsource.h @@ -0,0 +1,74 @@ +/* + * Copyright 2013 Antti Palosaari + * + * 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. + */ + +#ifndef INCLUDED_KERNEL_LIBV4L2_X_IMPL_H +#define INCLUDED_KERNEL_LIBV4L2_X_IMPL_H + +#include +#include +#include +#include +#include +#include +#include "/usr/local/include/libv4l2.h" +#include "fcntl.h" +#include + +namespace kernel { + // v4l2_mmap + struct buffer { + void *start; + size_t length; + }; + + class v4l //: public v4l + { + private: + // v4l2 device file handle + int fd; + + // stream / sample format + uint32_t pixelformat; + + struct buffer *buffers; + unsigned int n_buffers; + + // for processing mmap buffer + void *recebuf_ptr; + unsigned int recebuf_len; + unsigned int recebuf_mmap_index; + + public: + v4l(const char *filename); + ~v4l(); + + void set_samp_rate(double samp_rate); + void set_center_freq(double freq); + void set_bandwidth(double bandwidth); + void set_tuner_gain(double gain); + + // Where all the action really happens + int work(int noutput_items, + void* input_items, + void* output_items); + }; +} // namespace kernel + +#endif /* INCLUDED_KERNEL_LIBV4L2_X_IMPL_H */ +