From 9fba88de8fc452b45f64269af14ab7a7afba9e92 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 23 Jul 2014 18:59:40 +0200 Subject: [PATCH] qv4l2: add SDR visualization. Just show a simple waveform of the first 1025 samples of the I and Q samples in each captured buffer. It's simple but remarkably effective. Currently only the CU8 format is supported. Signed-off-by: Hans Verkuil --- utils/qv4l2/general-tab.cpp | 16 ++--- utils/qv4l2/qv4l2.cpp | 142 ++++++++++++++++++++++++++++++++++++++++++-- utils/qv4l2/qv4l2.h | 2 + 3 files changed, 148 insertions(+), 12 deletions(-) diff --git a/utils/qv4l2/general-tab.cpp b/utils/qv4l2/general-tab.cpp index d7c4ea4..e94d99f 100644 --- a/utils/qv4l2/general-tab.cpp +++ b/utils/qv4l2/general-tab.cpp @@ -201,7 +201,7 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *paren m_audioOutDevice = NULL; } - if (isRadio()) + if (!isSDR() && isRadio()) goto done; addTitle("Format Settings"); @@ -219,7 +219,7 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *paren updateVideoOutput(); else updateVideoInput(); - } else { + } else if (!isSDR()) { formatSection(fmt); } @@ -262,11 +262,13 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *paren cropSection(); } - if (m_isOutput) - updateVideoOutput(); - else - updateVideoInput(); - updateVidFormat(); + if (!isSDR()) { + if (m_isOutput) + updateVideoOutput(); + else + updateVideoInput(); + updateVidFormat(); + } done: QGridLayout::addWidget(new QWidget(parent), rowCount(), 0, 1, n); diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp index 72847d1..c4c071a 100644 --- a/utils/qv4l2/qv4l2.cpp +++ b/utils/qv4l2/qv4l2.cpp @@ -62,6 +62,9 @@ extern "C" { #include +#define SDR_WIDTH 1024 +#define SDR_HEIGHT 512 + ApplicationWindow::ApplicationWindow() : m_capture(NULL), m_pxw(25), @@ -279,8 +282,7 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen) m_tabs->setFocus(); m_convertData = v4lconvert_create(g_fd()); bool canStream = g_fd() >= 0 && v4l_type_is_capture(g_type()) && - !has_radio_rx() && !has_radio_tx() && - !has_sdr_cap(); + !has_radio_rx() && !has_radio_tx(); m_capStartAct->setEnabled(canStream); m_saveRawAct->setEnabled(canStream); m_snapshotAct->setEnabled(canStream && has_vid_cap()); @@ -425,7 +427,7 @@ bool ApplicationWindow::startCapture() { startAudio(); - if (m_genTab->isRadio()) { + if (!m_genTab->isSDR() && m_genTab->isRadio()) { s_priority(m_genTab->usePrio()); return true; } @@ -585,6 +587,107 @@ void ApplicationWindow::capVbiFrame() refresh(); } +void ApplicationWindow::capSdrFrame() +{ + cv4l_buffer buf(m_queue); + __u8 *data = NULL; + int s = 0; + + switch (m_capMethod) { + case methodRead: + s = read(m_frameData, m_sdrSize); + if (s < 0) { + if (errno != EAGAIN) { + error("read"); + m_capStartAct->setChecked(false); + } + return; + } + data = m_frameData; + break; + + case methodMmap: + case methodUser: + if (dqbuf(buf)) { + if (errno == EAGAIN) + return; + error("dqbuf"); + m_capStartAct->setChecked(false); + return; + } + if (buf.g_flags() & V4L2_BUF_FLAG_ERROR) { + qbuf(buf); + return; + } + data = (__u8 *)m_queue.g_dataptr(buf.g_index(), 0); + s = buf.g_bytesused(); + break; + } + if (s != m_sdrSize) { + error("incorrect sdr size"); + m_capStartAct->setChecked(false); + return; + } + if (showFrames()) { + unsigned width = m_sdrSize / 2 - 1; + + if (SDR_WIDTH < width) + width = SDR_WIDTH; + + m_capImage->fill(0); + /* + * Draw two waveforms, each consisting of the first 'width + 1' samples + * of the buffer, the top is for the I, the bottom is for the Q values. + */ + for (unsigned i = 0; i < 2; i++) { + unsigned start = 255 - data[i]; + + for (unsigned x = 0; x < width; x++) { + unsigned next = 255 - data[2 + 2 * x + i]; + unsigned low = start < next ? start : next; + unsigned high = start > next ? start : next; + __u8 *q = m_capImage->bits() + x * 3 + + (i * 256 + low) * m_capImage->bytesPerLine(); + + while (low++ <= high) { + q[0] = 255; + q[1] = 255; + q[2] = 255; + q += m_capImage->bytesPerLine(); + } + start = next; + } + } + } + + if (m_capMethod != methodRead) + qbuf(buf); + + QString status, curStatus; + struct timeval tv, res; + + if (m_frame == 0) + gettimeofday(&m_tv, NULL); + gettimeofday(&tv, NULL); + timersub(&tv, &m_tv, &res); + if (res.tv_sec) { + m_fps = (100 * (m_frame - m_lastFrame)) / + (res.tv_sec * 100 + res.tv_usec / 10000); + m_lastFrame = m_frame; + m_tv = tv; + } + status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps); + if (showFrames()) + m_capture->setFrame(m_capImage->width(), m_capImage->height(), + m_capDestFormat.fmt.pix.pixelformat, m_capImage->bits(), NULL, status); + + curStatus = statusBar()->currentMessage(); + if (curStatus.isEmpty() || curStatus.startsWith("Frame: ")) + statusBar()->showMessage(status); + if (m_frame == 1) + refresh(); +} + void ApplicationWindow::capFrame() { cv4l_buffer buf(m_queue); @@ -728,7 +831,7 @@ void ApplicationWindow::stopCapture() s_priority(V4L2_PRIORITY_DEFAULT); - if (m_genTab->isRadio()) + if (!m_genTab->isSDR() && m_genTab->isRadio()) return; v4l2_encoder_cmd cmd; @@ -864,7 +967,7 @@ void ApplicationWindow::closeCaptureWin() void ApplicationWindow::capStart(bool start) { - if (m_genTab->isRadio()) { + if (!m_genTab->isSDR() && m_genTab->isRadio()) { if (start) startCapture(); else @@ -958,6 +1061,35 @@ void ApplicationWindow::capStart(bool start) return; } + if (m_genTab->isSDR()) { + cv4l_fmt fmt; + + if (g_fmt(fmt)) { + error("could not obtain an VBI format\n"); + return; + } + if (fmt.fmt.sdr.pixelformat != V4L2_SDR_FMT_CU8) { + error("only CU8 is supported for SDR\n"); + return; + } + m_sdrSize = fmt.fmt.sdr.buffersize; + m_frameData = new unsigned char[m_sdrSize]; + m_capImage = new QImage(SDR_WIDTH, SDR_HEIGHT, dstFmt); + m_capImage->fill(0); + m_capture->setWindowSize(QSize(SDR_WIDTH, SDR_HEIGHT)); + m_capture->setFrame(m_capImage->width(), m_capImage->height(), + m_capDestFormat.fmt.pix.pixelformat, m_capImage->bits(), NULL, "No frame"); + if (showFrames()) + m_capture->show(); + + statusBar()->showMessage("No frame"); + if (startCapture()) { + m_capNotifier = new QSocketNotifier(g_fd(), QSocketNotifier::Read, m_tabs); + connect(m_capNotifier, SIGNAL(activated(int)), this, SLOT(capSdrFrame())); + } + return; + } + m_capSrcFormat.s_type(g_type()); g_fmt(m_capSrcFormat); s_fmt(m_capSrcFormat); diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h index d8ab231..46a7265 100644 --- a/utils/qv4l2/qv4l2.h +++ b/utils/qv4l2/qv4l2.h @@ -127,6 +127,7 @@ private slots: void ctrlEvent(); void snapshot(); void capVbiFrame(); + void capSdrFrame(); void saveRaw(bool); void setRenderMethod(bool); void setBlending(bool); @@ -229,6 +230,7 @@ private: unsigned m_vbiWidth; unsigned m_vbiHeight; struct vbi_handle m_vbiHandle; + int m_sdrSize; unsigned m_frame; unsigned m_lastFrame; unsigned m_fps; -- 2.7.4