diff --git a/plugins/samplesource/v4l-msi/v4lgui.cpp b/plugins/samplesource/v4l-msi/v4lgui.cpp index 802764536..e1f13b5b9 100644 --- a/plugins/samplesource/v4l-msi/v4lgui.cpp +++ b/plugins/samplesource/v4l-msi/v4lgui.cpp @@ -10,7 +10,7 @@ V4LGui::V4LGui(PluginAPI* pluginAPI, QWidget* parent) : m_sampleSource(NULL) { ui->setupUi(this); - ui->centerFrequency->setValueRange(7, 20000U, 2200000U); + ui->centerFrequency->setValueRange(7, 50000U, 1999000U); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); displaySettings(); @@ -82,37 +82,15 @@ bool V4LGui::deserialize(const QByteArray& data) bool V4LGui::handleMessage(Message* message) { - if(V4LInput::MsgReportV4L::match(message)) { - m_gains = ((V4LInput::MsgReportV4L*)message)->getGains(); - displaySettings(); - message->completed(); - return true; - } else { - return false; - } + return false; } void V4LGui::displaySettings() { ui->centerFrequency->setValue(m_generalSettings.m_centerFrequency / 1000); - if(m_gains.size() > 0) { - int dist = abs(m_settings.m_gain - m_gains[0]); - int pos = 0; - for(uint i = 1; i < m_gains.size(); i++) { - if(abs(m_settings.m_gain - m_gains[i]) < dist) { - dist = abs(m_settings.m_gain - m_gains[i]); - pos = i; - } - } - ui->gainText->setText(tr("%1.%2").arg(m_gains[pos] / 10).arg(abs(m_gains[pos] % 10))); - ui->gain->setMaximum(m_gains.size() - 1); - ui->gain->setEnabled(true); - ui->gain->setValue(pos); - } else { - ui->gain->setMaximum(0); - ui->gain->setEnabled(false); - ui->gain->setValue(0); - } + ui->ifgain->setValue(1); + ui->checkBoxL->setChecked(m_settings.m_lna); + ui->checkBoxM->setChecked(m_settings.m_mix); } void V4LGui::sendSettings() @@ -127,12 +105,12 @@ void V4LGui::on_centerFrequency_changed(quint64 value) sendSettings(); } -void V4LGui::on_gain_valueChanged(int value) +void V4LGui::on_ifgain_valueChanged(int value) { - if(value > (int)m_gains.size()) + if(value > 8) return; - int gain = m_gains[value]; - ui->gainText->setText(tr("%1.%2").arg(gain / 10).arg(abs(gain % 10))); + int gain = value * 6; + ui->gainText->setText( tr("%1").arg(gain) ); m_settings.m_gain = gain; sendSettings(); } @@ -143,3 +121,21 @@ void V4LGui::updateHardware() message->submit(m_pluginAPI->getDSPEngineMessageQueue()); m_updateTimer.stop(); } + +void V4LGui::on_checkBoxL_stateChanged(int state) +{ + if (state == Qt::Checked) + m_settings.m_lna = 1; + else + m_settings.m_lna = 0; + sendSettings(); +} + +void V4LGui::on_checkBoxM_stateChanged(int state) +{ + if (state == Qt::Checked) + m_settings.m_mix = 1; + else + m_settings.m_mix = 0; + sendSettings(); +} diff --git a/plugins/samplesource/v4l-msi/v4lgui.h b/plugins/samplesource/v4l-msi/v4lgui.h index 448c97dee..c443ac424 100644 --- a/plugins/samplesource/v4l-msi/v4lgui.h +++ b/plugins/samplesource/v4l-msi/v4lgui.h @@ -44,7 +44,9 @@ private: private slots: void on_centerFrequency_changed(quint64 value); - void on_gain_valueChanged(int value); + void on_ifgain_valueChanged(int value); + void on_checkBoxL_stateChanged(int state); + void on_checkBoxM_stateChanged(int state); void updateHardware(); }; diff --git a/plugins/samplesource/v4l-msi/v4lgui.ui b/plugins/samplesource/v4l-msi/v4lgui.ui index 9254a4a50..6ecfdaa85 100644 --- a/plugins/samplesource/v4l-msi/v4lgui.ui +++ b/plugins/samplesource/v4l-msi/v4lgui.ui @@ -107,6 +107,24 @@ + + + + + + LNA AMP + + + + + + + Mix AMP + + + + + @@ -126,18 +144,12 @@ - - - false - + - LNA amplification + IF amplification - 0 - - - 1 + 8 Qt::Horizontal diff --git a/plugins/samplesource/v4l-msi/v4linput.cpp b/plugins/samplesource/v4l-msi/v4linput.cpp index 074c8f38b..57584b894 100644 --- a/plugins/samplesource/v4l-msi/v4linput.cpp +++ b/plugins/samplesource/v4l-msi/v4linput.cpp @@ -23,16 +23,19 @@ #include "util/simpleserializer.h" MESSAGE_CLASS_DEFINITION(V4LInput::MsgConfigureV4L, Message) -MESSAGE_CLASS_DEFINITION(V4LInput::MsgReportV4L, Message) V4LInput::Settings::Settings() : - m_gain(0) + m_gain(1), + m_lna(0), + m_mix(0) { } void V4LInput::Settings::resetToDefaults() { - m_gain = 0; + m_gain = 1; + m_lna = 0; + m_mix = 0; } QByteArray V4LInput::Settings::serialize() const @@ -53,7 +56,7 @@ bool V4LInput::Settings::deserialize(const QByteArray& data) } if(d.getVersion() == 1) { - d.readS32(1, &m_gain, 0); + d.readS32(1, &m_gain, 1); //d.readS32(2, &m_samplerate, 0); return true; } else { @@ -94,7 +97,7 @@ bool V4LInput::startInput(int device) return false; } - m_deviceDescription = QString("RTL-SDR /dev/swradio0"); + m_deviceDescription = QString("SDRplay /dev/swradio0"); qDebug("V4LInput: start"); //MsgReportV4L::create(m_gains)->submit(m_guiMessageQueue); @@ -122,6 +125,7 @@ const QString& V4LInput::getDeviceDescription() const int V4LInput::getSampleRate() const { + // The output rate is lower than the device rate int result = SAMPLERATE / 4; return result; } @@ -153,15 +157,20 @@ void V4LInput::applySettings(const GeneralSettings& generalSettings, const Setti } if((m_generalSettings.m_centerFrequency != generalSettings.m_centerFrequency) || force) { - m_V4LThread->set_center_freq( (double)(generalSettings.m_centerFrequency - + (SAMPLERATE / 4) )); + m_V4LThread->set_center_freq( (double)generalSettings.m_centerFrequency ); + m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency; } - m_generalSettings.m_centerFrequency = generalSettings.m_centerFrequency; -#if 0 + if((m_settings.m_gain != settings.m_gain) || force) { m_settings.m_gain = settings.m_gain; m_V4LThread->set_tuner_gain((double)m_settings.m_gain); } -#endif + + if((m_settings.m_lna != settings.m_lna) || (m_settings.m_mix != settings.m_mix) || force) { + m_settings.m_lna = settings.m_lna; + m_settings.m_mix = settings.m_mix; + m_V4LThread->set_amps(1, m_settings.m_lna); + m_V4LThread->set_amps(0, m_settings.m_mix); + } } diff --git a/plugins/samplesource/v4l-msi/v4linput.h b/plugins/samplesource/v4l-msi/v4linput.h index 680d65c53..a56f2bf4c 100644 --- a/plugins/samplesource/v4l-msi/v4linput.h +++ b/plugins/samplesource/v4l-msi/v4linput.h @@ -31,7 +31,7 @@ class V4LThread; class V4LInput : public SampleSource { public: struct Settings { - qint32 m_gain; + qint32 m_gain, m_lna, m_mix; Settings(); void resetToDefaults(); @@ -62,26 +62,6 @@ public: { } }; - class MsgReportV4L : public Message { - MESSAGE_CLASS_DECLARATION - - public: - const std::vector& getGains() const { return m_gains; } - - static MsgReportV4L* create(const std::vector& gains) - { - return new MsgReportV4L(gains); - } - - protected: - std::vector m_gains; - - MsgReportV4L(const std::vector& gains) : - Message(), - m_gains(gains) - { } - }; - V4LInput(MessageQueue* msgQueueToGUI); ~V4LInput(); @@ -98,7 +78,6 @@ private: Settings m_settings; V4LThread* m_V4LThread; QString m_deviceDescription; - std::vector m_gains; void applySettings(const GeneralSettings& generalSettings, const Settings& settings, bool force); }; diff --git a/plugins/samplesource/v4l-msi/v4lplugin.cpp b/plugins/samplesource/v4l-msi/v4lplugin.cpp index 4412f19d1..1d9546075 100644 --- a/plugins/samplesource/v4l-msi/v4lplugin.cpp +++ b/plugins/samplesource/v4l-msi/v4lplugin.cpp @@ -1,15 +1,14 @@ #include #include -#include #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "v4lplugin.h" #include "v4lgui.h" const PluginDescriptor V4LPlugin::m_pluginDescriptor = { - QString("V4L Input"), - QString("3.18"), - QString("(c) 2014 John Greb"), + QString("V4L SDRplay Input"), + QString("4.0"), + QString("(c) 2015 John Greb"), QString("http://palosaari.fi/linux/"), true, QString("github.com/hexameron/rtl-sdrangelove") @@ -36,7 +35,7 @@ PluginInterface::SampleSourceDevices V4LPlugin::enumSampleSources() { SampleSourceDevices result; - QString displayedName(QString("Kernel Source #1")); + QString displayedName(QString("Linux V4L (SDRplay)")); SimpleSerializer s(1); s.writeS32(1, 0); result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.v4l", s.final())); diff --git a/plugins/samplesource/v4l-msi/v4lsource.cpp b/plugins/samplesource/v4l-msi/v4lsource.cpp index 0d4a5fd08..7e1b8fc31 100644 --- a/plugins/samplesource/v4l-msi/v4lsource.cpp +++ b/plugins/samplesource/v4l-msi/v4lsource.cpp @@ -29,22 +29,17 @@ #include #include -/* 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 CID_CLASS_RF (0x00a20000) +#define CID_RF_TUNER_CLASS_BASE (0x00a20900) +#define CID_TUNER_BW_AUTO (CID_RF_TUNER_CLASS_BASE + 11) +#define CID_TUNER_BW (CID_RF_TUNER_CLASS_BASE + 12) +#define CID_TUNER_LNA_GAIN (CID_RF_TUNER_CLASS_BASE + 42) +#define CID_TUNER_MIXER_GAIN (CID_RF_TUNER_CLASS_BASE + 52) +#define CID_TUNER_IF_GAIN (CID_RF_TUNER_CLASS_BASE + 62) #define V4L2_PIX_FMT_SDR_U8 v4l2_fourcc('C', 'U', '0', '8') /* unsigned 8-bit Complex*/ #define V4L2_PIX_FMT_SDR_U16LE v4l2_fourcc('C', 'U', '1', '6') /* unsigned 16-bit Complex*/ +#define V4L2_PIX_FMT_SDR_CS14LE v4l2_fourcc('C', 'S', '1', '4') /* signed 14-bit Complex*/ #define CLEAR(x) memset(&(x), 0, sizeof(x)) @@ -63,7 +58,6 @@ static void xioctl(int fh, unsigned long int request, void *arg) void V4LThread::OpenSource(const char *filename) { - struct v4l2_format fmt; struct v4l2_buffer buf; struct v4l2_requestbuffers req; enum v4l2_buf_type type; @@ -77,15 +71,18 @@ V4LThread::OpenSource(const char *filename) qCritical("Cannot open /dev/swradio0 :%d", fd); return; } + set_sample_rate( (double)SAMPLERATE ); + set_center_freq( (double)centerFreq ); + set_bandwidth( (double)IF_BANDWIDTH ); + set_tuner_gain( (double) 6.0); - pixelformat = V4L2_PIX_FMT_SDR_U8; - // RTLSDR has limited ioctls in 3.18, expect fail. - qCritical("Want Pixelformat : CU08"); + struct v4l2_format fmt; + pixelformat = V4L2_PIX_FMT_SDR_CS14LE; CLEAR(fmt); fmt.type = V4L2_BUF_TYPE_SDR_CAPTURE; fmt.fmt.sdr.pixelformat = pixelformat; xioctl(fd, VIDIOC_S_FMT, &fmt); - qCritical("Got Pixelformat : %4.4s", (char *)&fmt.fmt.sdr.pixelformat); + qCritical(" Expecting format CS14LE, got : %4.4s", (char *)&fmt.fmt.sdr.pixelformat); CLEAR(req); req.count = 8; @@ -118,9 +115,6 @@ V4LThread::OpenSource(const char *filename) buf.index = i; xioctl(fd, VIDIOC_QBUF, &buf); } - - set_sample_rate((double)SAMPLERATE); - set_center_freq( centerFreq + (SAMPLERATE / 4) ); // start streaming type = V4L2_BUF_TYPE_SDR_CAPTURE; xioctl(fd, VIDIOC_STREAMON, &type); @@ -151,14 +145,11 @@ V4LThread::set_sample_rate(double samp_rate) memset (&frequency, 0, sizeof(frequency)); frequency.tuner = 0; frequency.type = V4L2_TUNER_ADC; - frequency.frequency = samp_rate / 1; - + frequency.frequency = samp_rate; xioctl(fd, VIDIOC_S_FREQUENCY, &frequency); - - return; } -// Cannot change freq while streaming in Linux 4.0 +// Cannot change freq while streaming void V4LThread::set_center_freq(double freq) { @@ -170,10 +161,7 @@ V4LThread::set_center_freq(double freq) frequency.tuner = 1; frequency.type = V4L2_TUNER_RF; frequency.frequency = freq; - xioctl(fd, VIDIOC_S_FREQUENCY, &frequency); - - return; } void @@ -182,18 +170,26 @@ V4LThread::set_bandwidth(double bandwidth) struct v4l2_ext_controls ext_ctrls; struct v4l2_ext_control ext_ctrl; + if (fd <= 0) + return; + + memset (&ext_ctrl, 0, sizeof(ext_ctrl)); + ext_ctrl.id = CID_TUNER_BW_AUTO; + ext_ctrl.value = 0; + memset (&ext_ctrls, 0, sizeof(ext_ctrls)); + ext_ctrls.ctrl_class = CID_CLASS_RF; + ext_ctrls.count = 1; + ext_ctrls.controls = &ext_ctrl; + xioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls); + 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.ctrl_class = CID_CLASS_RF; ext_ctrls.count = 1; ext_ctrls.controls = &ext_ctrl; - xioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls); - - return; } void @@ -205,19 +201,39 @@ V4LThread::set_tuner_gain(double gain) if (fd <= 0) return; memset (&ext_ctrl, 0, sizeof(ext_ctrl)); - ext_ctrl.id = CID_TUNER_GAIN; + ext_ctrl.id = CID_TUNER_IF_GAIN; ext_ctrl.value = gain; - memset (&ext_ctrls, 0, sizeof(ext_ctrls)); - ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_USER; + ext_ctrls.ctrl_class = CID_CLASS_RF; ext_ctrls.count = 1; ext_ctrls.controls = &ext_ctrl; - xioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls); - - return; } + +void +V4LThread::set_amps(bool lna, bool on) + { + struct v4l2_ext_controls ext_ctrls; + struct v4l2_ext_control ext_ctrl; + + if (fd <= 0) + return; + + memset (&ext_ctrl, 0, sizeof(ext_ctrl)); + memset (&ext_ctrls, 0, sizeof(ext_ctrls)); + if (lna) + ext_ctrl.id = CID_TUNER_LNA_GAIN; + else + ext_ctrl.id = CID_TUNER_MIXER_GAIN; + ext_ctrl.value = on; + ext_ctrls.ctrl_class = CID_CLASS_RF; + ext_ctrls.count = 1; + ext_ctrls.controls = &ext_ctrl; + xioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls); + } + +#define CASTUP (int)(qint16) int V4LThread::work(int noutput_items) { @@ -225,33 +241,36 @@ V4LThread::work(int noutput_items) struct timeval tv; struct v4l2_buffer buf; fd_set fds; - qint16 xreal, yimag; - uint8_t* b; + int xreal, yimag; + uint16_t* b; SampleVector::iterator it; unsigned int pos = 0; - // in is 4*8bit*2(IQ), 8 bytes; out is 1*16bit*2(IQ) , 4bytes + // MSI format is 252 sample pairs :( 63 * 4) * 4bytes it = m_convertBuffer.begin(); - if (recebuf_len > 0) { - b = (uint8_t *) recebuf_ptr; - unsigned int len = noutput_items * 8; - if (len > recebuf_len) - len = recebuf_len; + if (recebuf_len >= 16) { // in bytes + b = (uint16_t *) recebuf_ptr; + unsigned int len = 8 * noutput_items; // decimation (i+q * 4 : cmplx) + if (len * 2 > recebuf_len) + len = recebuf_len / 2; + // Decimate by four for lower cpu usage for (pos = 0; pos < len - 7; pos += 8) { - xreal = b[pos+0] - b[pos+3] + b[pos+7] - b[pos+4]; - yimag = b[pos+1] - b[pos+5] + b[pos+2] - b[pos+6]; - Sample s( xreal << 3, yimag << 3 ); + xreal = CASTUP(b[pos+0]<<2) + CASTUP(b[pos+2]<<2) + + CASTUP(b[pos+4]<<2) + CASTUP(b[pos+6]<<2); + yimag = CASTUP(b[pos+1]<<2) + CASTUP(b[pos+3]<<2) + + CASTUP(b[pos+5]<<2) + CASTUP(b[pos+7]<<2); + Sample s( (qint16)(xreal >> 2) , (qint16)(yimag >> 2) ); *it = s; it++; } m_sampleFifo->write(m_convertBuffer.begin(), it); - recebuf_len -= pos; - recebuf_ptr = (void*)(b + pos); + recebuf_len -= pos * 2; // size of int16 + recebuf_ptr = &b[pos]; } // return now if there is still data in buffer, else free buffer and get another. - if (recebuf_len >= 8) + if (recebuf_len >= 16) return pos / 8; - { // frre buffer, if there was one. + { // free buffer, if there was one. if (pos > 0) { CLEAR(buf); buf.type = V4L2_BUF_TYPE_SDR_CAPTURE; diff --git a/plugins/samplesource/v4l-msi/v4lthread.h b/plugins/samplesource/v4l-msi/v4lthread.h index cf4318761..834e98460 100644 --- a/plugins/samplesource/v4l-msi/v4lthread.h +++ b/plugins/samplesource/v4l-msi/v4lthread.h @@ -24,8 +24,10 @@ #include "dsp/samplefifo.h" #include "dsp/inthalfbandfilter.h" -#define SAMPLERATE 1024000 -#define BLOCKSIZE 4096 +// lowest samplerate in the kernel is 1.2M, but this works better +#define SAMPLERATE 1536000 +#define IF_BANDWIDTH 300000 +#define BLOCKSIZE 8192 class V4LThread : public QThread { Q_OBJECT @@ -41,6 +43,7 @@ public: void set_center_freq(double freq); void set_bandwidth(double bandwidth); void set_tuner_gain(double gain); + void set_amps(bool lna, bool on); int work(int n_items); private: int fd;