From 0e7f8ff1251e54fef52c2b2ed7554c072a0b3a0a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 29 Dec 2018 01:20:48 +0100 Subject: [PATCH] XTRX input: imported code from xtrx-sdr fork --- cmake/Modules/FindLibXTRX.cmake | 27 + devices/CMakeLists.txt | 6 + devices/xtrx/CMakeLists.txt | 31 + devices/xtrx/devicextrxparam.cpp | 44 + devices/xtrx/devicextrxparam.h | 58 + devices/xtrx/devicextrxshared.cpp | 83 ++ devices/xtrx/devicextrxshared.h | 165 +++ plugins/samplesource/CMakeLists.txt | 6 + plugins/samplesource/xtrxinput/CMakeLists.txt | 55 + plugins/samplesource/xtrxinput/xtrxinput.cpp | 1136 +++++++++++++++ plugins/samplesource/xtrxinput/xtrxinput.h | 261 ++++ .../samplesource/xtrxinput/xtrxinputgui.cpp | 590 ++++++++ plugins/samplesource/xtrxinput/xtrxinputgui.h | 107 ++ .../samplesource/xtrxinput/xtrxinputgui.ui | 1215 +++++++++++++++++ .../xtrxinput/xtrxinputplugin.cpp | 145 ++ .../samplesource/xtrxinput/xtrxinputplugin.h | 55 + .../xtrxinput/xtrxinputsettings.cpp | 120 ++ .../xtrxinput/xtrxinputsettings.h | 71 + .../xtrxinput/xtrxinputthread.cpp | 229 ++++ .../samplesource/xtrxinput/xtrxinputthread.h | 70 + pluginssrv/samplesource/CMakeLists.txt | 6 + .../samplesource/xtrxinput/CMakeLists.txt | 46 + 22 files changed, 4526 insertions(+) create mode 100644 cmake/Modules/FindLibXTRX.cmake create mode 100644 devices/xtrx/CMakeLists.txt create mode 100644 devices/xtrx/devicextrxparam.cpp create mode 100644 devices/xtrx/devicextrxparam.h create mode 100644 devices/xtrx/devicextrxshared.cpp create mode 100644 devices/xtrx/devicextrxshared.h create mode 100644 plugins/samplesource/xtrxinput/CMakeLists.txt create mode 100644 plugins/samplesource/xtrxinput/xtrxinput.cpp create mode 100644 plugins/samplesource/xtrxinput/xtrxinput.h create mode 100644 plugins/samplesource/xtrxinput/xtrxinputgui.cpp create mode 100644 plugins/samplesource/xtrxinput/xtrxinputgui.h create mode 100644 plugins/samplesource/xtrxinput/xtrxinputgui.ui create mode 100644 plugins/samplesource/xtrxinput/xtrxinputplugin.cpp create mode 100644 plugins/samplesource/xtrxinput/xtrxinputplugin.h create mode 100644 plugins/samplesource/xtrxinput/xtrxinputsettings.cpp create mode 100644 plugins/samplesource/xtrxinput/xtrxinputsettings.h create mode 100644 plugins/samplesource/xtrxinput/xtrxinputthread.cpp create mode 100644 plugins/samplesource/xtrxinput/xtrxinputthread.h create mode 100644 pluginssrv/samplesource/xtrxinput/CMakeLists.txt diff --git a/cmake/Modules/FindLibXTRX.cmake b/cmake/Modules/FindLibXTRX.cmake new file mode 100644 index 000000000..ade45c76c --- /dev/null +++ b/cmake/Modules/FindLibXTRX.cmake @@ -0,0 +1,27 @@ +if(NOT LIBXTRX_FOUND) + pkg_check_modules (LIBXTRX_PKG libxtrx) + find_path(LIBXTRX_INCLUDE_DIRS NAMES xtrx_api.h + PATHS + ${LIBXTRX_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(LIBXTRX_LIBRARIES NAMES xtrx + PATHS + ${LIBXTRX_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + +if(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES) + set(LIBXTRX_FOUND TRUE CACHE INTERNAL "libxtrx found") + message(STATUS "Found libxtrx: ${LIBXTRX_INCLUDE_DIRS}, ${LIBXTRX_LIBRARIES}") +else(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES) + set(LIBXTRX_FOUND FALSE CACHE INTERNAL "libxtrx found") + message(STATUS "libxtrx not found.") +endif(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES) + +mark_as_advanced(LIBXTRX_LIBRARIES LIBXTRX_INCLUDE_DIRS) + +endif(NOT LIBXTRX_FOUND) \ No newline at end of file diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt index 422cc588b..8c3de333a 100644 --- a/devices/CMakeLists.txt +++ b/devices/CMakeLists.txt @@ -12,6 +12,7 @@ if (BUILD_DEBIAN) add_subdirectory(perseus) add_subdirectory(plutosdr) add_subdirectory(soapysdr) + add_subdirectory(xtrx) else(BUILD_DEBIAN) find_package(LibBLADERF) if(LIBUSB_FOUND AND LIBBLADERF_FOUND) @@ -29,6 +30,11 @@ else(BUILD_DEBIAN) add_subdirectory(limesdr) endif(LIBUSB_FOUND AND LIMESUITE_FOUND) + find_package(LibXTRX) + if(LIBXTRX_FOUND) + add_subdirectory(xtrx) + endif(LIBXTRX_FOUND) + find_package(LibIIO) if(LIBUSB_FOUND AND LIBIIO_FOUND) add_subdirectory(plutosdr) diff --git a/devices/xtrx/CMakeLists.txt b/devices/xtrx/CMakeLists.txt new file mode 100644 index 000000000..189c1b534 --- /dev/null +++ b/devices/xtrx/CMakeLists.txt @@ -0,0 +1,31 @@ +project(xtrxdevice) + +set(xtrxdevice_SOURCES + devicextrxparam.cpp + devicextrxshared.cpp +) + +set(xtrxdevice_HEADERS + devicextrxparam.h + devicextrxshared.h +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${LIBXTRX_INCLUDE_DIRS} +) + +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_SHARED) + +add_library(xtrxdevice SHARED + ${xtrxdevice_SOURCES} +) + +target_link_libraries(xtrxdevice + ${LIBXTRX_LIBRARIES} + sdrbase +) + +install(TARGETS xtrxdevice DESTINATION lib) diff --git a/devices/xtrx/devicextrxparam.cpp b/devices/xtrx/devicextrxparam.cpp new file mode 100644 index 000000000..249656412 --- /dev/null +++ b/devices/xtrx/devicextrxparam.cpp @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "devicextrxparam.h" + +bool DeviceXTRXParams::open(const char* deviceStr) +{ + int res; + qDebug("DeviceXTRXParams::open: serial: %s", (const char *) deviceStr); + + res = xtrx_open(deviceStr, XTRX_O_RESET | 4, &m_dev); + + if (res) + { + qCritical() << "DeviceXTRXParams::open: cannot open device " << deviceStr; + return false; + } + + + return true; +} + +void DeviceXTRXParams::close() +{ + if (m_dev) + { + xtrx_close(m_dev); + m_dev = 0; + } +} diff --git a/devices/xtrx/devicextrxparam.h b/devices/xtrx/devicextrxparam.h new file mode 100644 index 000000000..c7e2b4233 --- /dev/null +++ b/devices/xtrx/devicextrxparam.h @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef DEVICES_XTRX_DEVICEXTRXPARAM_H_ +#define DEVICES_XTRX_DEVICEXTRXPARAM_H_ + +#include "xtrx_api.h" + + +struct DeviceXTRXParams +{ + struct xtrx_dev *m_dev; //!< device handle + uint32_t m_nbRxChannels; //!< number of Rx channels + uint32_t m_nbTxChannels; //!< number of Tx channels + + unsigned m_log2OvSRRx; + double m_sampleRate; //!< ADC/DAC sample rate + double m_rxFrequency; //!< Rx frequency + double m_txFrequency; //!< Tx frequency + + DeviceXTRXParams() : + m_dev(0), + m_nbRxChannels(2), + m_nbTxChannels(2), + m_log2OvSRRx(0), + m_sampleRate(5e6), + m_rxFrequency(30e6), + m_txFrequency(30e6) + { + } + + /** + * Opens and initialize the device and obtain information (# channels, ranges, ...) + */ + bool open(const char* deviceStr); + void close(); + + struct xtrx_dev *getDevice() { return m_dev; } + + ~DeviceXTRXParams() + { + } +}; + +#endif /* DEVICES_XTRX_DEVICEXTRXPARAM_H_ */ diff --git a/devices/xtrx/devicextrxshared.cpp b/devices/xtrx/devicextrxshared.cpp new file mode 100644 index 000000000..4ae1f7c81 --- /dev/null +++ b/devices/xtrx/devicextrxshared.cpp @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "devicextrxshared.h" + +MESSAGE_CLASS_DEFINITION(DeviceXTRXShared::MsgReportBuddyChange, Message) +MESSAGE_CLASS_DEFINITION(DeviceXTRXShared::MsgReportClockSourceChange, Message) +MESSAGE_CLASS_DEFINITION(DeviceXTRXShared::MsgReportDeviceInfo, Message) + +const float DeviceXTRXShared::m_sampleFifoLengthInSeconds = 0.25; +const int DeviceXTRXShared::m_sampleFifoMinSize = 48000; // 192kS/s knee + + + +double DeviceXTRXShared::set_samplerate(double rate, + double master, + bool output) +{ + if (output) + { + m_outputRate = rate; + + if (master != 0.0) { + m_masterRate = master; + } + } + else + { + m_inputRate = rate; + + if (master != 0.0) { + m_masterRate = master; + } + } + + int res = xtrx_set_samplerate(m_deviceParams->getDevice(), + m_masterRate, + m_inputRate, + m_outputRate, + 0, + 0, + 0, + 0); + + if (res) { + //fprintf(stderr, "Unable to set samplerate, error=%d\n", res); + return 0; + } + + if (output) { + return m_outputRate; + } + + return m_inputRate; +} + +double DeviceXTRXShared::get_temperature() +{ + uint64_t val = 0; + + int res = xtrx_val_get(m_deviceParams->getDevice(), + XTRX_TRX, XTRX_CH_AB, XTRX_BOARD_TEMP, &val); + + if (res) { + //fprintf(stderr, "Unable to set samplerate, error=%d\n", res); + return 0; + } + + return val; +} diff --git a/devices/xtrx/devicextrxshared.h b/devices/xtrx/devicextrxshared.h new file mode 100644 index 000000000..b1441e815 --- /dev/null +++ b/devices/xtrx/devicextrxshared.h @@ -0,0 +1,165 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef DEVICES_LIMESDR_DEVICELIMESDRSHARED_H_ +#define DEVICES_LIMESDR_DEVICELIMESDRSHARED_H_ + +#include +#include "devicextrxparam.h" +#include "util/message.h" + +/** + * Structure shared by a buddy with other buddies + */ +class DeviceXTRXShared +{ +public: + class MsgReportBuddyChange : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getDevSampleRate() const { return m_devSampleRate; } + unsigned getLog2HardDecimInterp() const { return m_log2HardDecimInterp; } + uint64_t getCenterFrequency() const { return m_centerFrequency; } + bool getRxElseTx() const { return m_rxElseTx; } + + static MsgReportBuddyChange* create( + int devSampleRate, + unsigned log2HardDecimInterp, + uint64_t centerFrequency, + bool rxElseTx) + { + return new MsgReportBuddyChange( + devSampleRate, + log2HardDecimInterp, + centerFrequency, + rxElseTx); + } + + private: + int m_devSampleRate; //!< device/host sample rate + unsigned m_log2HardDecimInterp; //!< log2 of hardware decimation or interpolation + uint64_t m_centerFrequency; //!< Center frequency + bool m_rxElseTx; //!< tells which side initiated the message + + MsgReportBuddyChange( + int devSampleRate, + unsigned log2HardDecimInterp, + uint64_t centerFrequency, + bool rxElseTx) : + Message(), + m_devSampleRate(devSampleRate), + m_log2HardDecimInterp(log2HardDecimInterp), + m_centerFrequency(centerFrequency), + m_rxElseTx(rxElseTx) + { } + }; + + class MsgReportClockSourceChange : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getExtClock() const { return m_extClock; } + uint32_t getExtClockFeq() const { return m_extClockFreq; } + + static MsgReportClockSourceChange* create( + bool extClock, + uint32_t m_extClockFreq) + { + return new MsgReportClockSourceChange( + extClock, + m_extClockFreq); + } + + private: + bool m_extClock; //!< True if external clock source + uint32_t m_extClockFreq; //!< Frequency (Hz) of external clock source + + MsgReportClockSourceChange( + bool extClock, + uint32_t m_extClockFreq) : + Message(), + m_extClock(extClock), + m_extClockFreq(m_extClockFreq) + { } + }; + + class MsgReportDeviceInfo : public Message { + MESSAGE_CLASS_DECLARATION + + public: + float getTemperature() const { return m_temperature; } + + static MsgReportDeviceInfo* create(float temperature) + { + return new MsgReportDeviceInfo(temperature); + } + + private: + float m_temperature; + + MsgReportDeviceInfo(float temperature) : + Message(), + m_temperature(temperature) + { } + }; + + class ThreadInterface + { + public: + virtual void startWork() = 0; + virtual void stopWork() = 0; + virtual void setDeviceSampleRate(int sampleRate) = 0; + virtual bool isRunning() = 0; + }; + + DeviceXTRXParams *m_deviceParams; //!< unique hardware device parameters + xtrx_channel_t m_channel; //!< logical device channel number (-1 if none) + ThreadInterface *m_thread; //!< holds the thread address if started else 0 + int m_ncoFrequency; + double m_centerFrequency; + uint32_t m_log2Soft; + bool m_threadWasRunning; //!< flag to know if thread needs to be resumed after suspend + + double m_inputRate; + double m_outputRate; + double m_masterRate; + + static const float m_sampleFifoLengthInSeconds; + static const int m_sampleFifoMinSize; + + DeviceXTRXShared() : + m_deviceParams(0), + m_channel(XTRX_CH_AB), + m_thread(0), + m_ncoFrequency(0), + m_centerFrequency(0), + m_log2Soft(0), + m_threadWasRunning(false), + m_inputRate(0), + m_outputRate(0), + m_masterRate(0) + {} + + ~DeviceXTRXShared() + {} + + double set_samplerate(double rate, double master, bool output); + + double get_temperature(); +}; + +#endif /* DEVICES_LIMESDR_DEVICELIMESDRSHARED_H_ */ diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index 1fbdd8aa9..ca93c0c19 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -54,6 +54,11 @@ if(LIBUSB_FOUND AND RX_SAMPLE_24BIT AND LIBPERSEUS_FOUND) add_subdirectory(perseus) endif(LIBUSB_FOUND AND RX_SAMPLE_24BIT AND LIBPERSEUS_FOUND) +find_package(LibXTRX) +if(LIBXTRX_FOUND) + add_subdirectory(xtrxinput) +endif(LIBXTRX_FOUND) + find_package(LibIIO) if(LIBUSB_FOUND AND LIBIIO_FOUND) add_subdirectory(plutosdrinput) @@ -97,6 +102,7 @@ if (BUILD_DEBIAN) add_subdirectory(rtlsdr) add_subdirectory(sdrdaemonsource) add_subdirectory(sdrplay) + add_subdirectory(xtrxinput) add_subdirectory(soapysdrinput) endif (BUILD_DEBIAN) diff --git a/plugins/samplesource/xtrxinput/CMakeLists.txt b/plugins/samplesource/xtrxinput/CMakeLists.txt new file mode 100644 index 000000000..c7cbbdd45 --- /dev/null +++ b/plugins/samplesource/xtrxinput/CMakeLists.txt @@ -0,0 +1,55 @@ +project(xtrxinput) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(xtrxinput_SOURCES + xtrxinputgui.cpp + xtrxinput.cpp + xtrxinputplugin.cpp + xtrxinputsettings.cpp + xtrxinputthread.cpp +) + +set(xtrxinput_HEADERS + xtrxinputgui.h + xtrxinput.h + xtrxinputplugin.h + xtrxinputsettings.h + xtrxinputthread.h +) + +set(xtrxinput_FORMS + xtrxinputgui.ui +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/devices + ${LIBXTRX_INCLUDE_DIRS} +) + +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +qt5_wrap_ui(xtrxinput_FORMS_HEADERS ${xtrxinput_FORMS}) + +add_library(inputxtrx SHARED + ${xtrxinput_SOURCES} + ${xtrxinput_HEADERS_MOC} + ${xtrxinput_FORMS_HEADERS} +) + + +target_link_libraries(inputxtrx + ${QT_LIBRARIES} + ${LIBXTRX_LIBRARIES} + sdrbase + sdrgui + xtrxdevice +) + +qt5_use_modules(inputxtrx Core Widgets) + +install(TARGETS inputxtrx DESTINATION lib/plugins/samplesource) \ No newline at end of file diff --git a/plugins/samplesource/xtrxinput/xtrxinput.cpp b/plugins/samplesource/xtrxinput/xtrxinput.cpp new file mode 100644 index 000000000..26be437c6 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinput.cpp @@ -0,0 +1,1136 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "xtrx_api.h" + +#include "device/devicesourceapi.h" +#include "device/devicesinkapi.h" +#include "dsp/dspcommands.h" +#include "dsp/filerecord.h" +#include "xtrxinput.h" +#include "xtrxinputthread.h" +#include "xtrx/devicextrxparam.h" +#include "xtrx/devicextrxshared.h" + +MESSAGE_CLASS_DEFINITION(XTRXInput::MsgConfigureXTRX, Message) +MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetStreamInfo, Message) +MESSAGE_CLASS_DEFINITION(XTRXInput::MsgGetDeviceInfo, Message) +MESSAGE_CLASS_DEFINITION(XTRXInput::MsgReportStreamInfo, Message) +MESSAGE_CLASS_DEFINITION(XTRXInput::MsgFileRecord, Message) +MESSAGE_CLASS_DEFINITION(XTRXInput::MsgStartStop, Message) + +XTRXInput::XTRXInput(DeviceSourceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_XTRXInputThread(0), + m_deviceDescription("XTRXInput"), + m_running(false), + m_channelAcquired(false) +{ + + suspendRxBuddies(); + suspendTxBuddies(); + openDevice(); + resumeTxBuddies(); + resumeRxBuddies(); + + m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->addSink(m_fileSink); +} + +XTRXInput::~XTRXInput() +{ + if (m_running) stop(); + m_deviceAPI->removeSink(m_fileSink); + delete m_fileSink; + suspendRxBuddies(); + suspendTxBuddies(); + closeDevice(); + resumeTxBuddies(); + resumeRxBuddies(); +} + +void XTRXInput::destroy() +{ + delete this; +} + +bool XTRXInput::openDevice() +{ + if (!m_sampleFifo.setSize(96000 * 4)) + { + qCritical("XTRXInput::openDevice: could not allocate SampleFifo"); + return false; + } + else + { + qDebug("XTRXInput::openDevice: allocated SampleFifo"); + } + + xtrx_channel_t requestedChannel = m_deviceAPI->getItemIndex() ? XTRX_CH_B : XTRX_CH_A; + + // look for Rx buddies and get reference to common parameters + // if there is a channel left take the first available + if (m_deviceAPI->getSourceBuddies().size() > 0) // look source sibling first + { + qDebug("XTRXInput::openDevice: look in Rx buddies"); + + DeviceSourceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0]; + m_deviceShared = *((DeviceXTRXShared *) sourceBuddy->getBuddySharedPtr()); // copy shared data + DeviceXTRXParams *deviceParams = m_deviceShared.m_deviceParams; // get device parameters + + if (deviceParams == 0) + { + qCritical("XTRXInput::openDevice: cannot get device parameters from Rx buddy"); + return false; // the device params should have been created by the buddy + } + else + { + qDebug("XTRXInput::openDevice: getting device parameters from Rx buddy"); + } + + if (m_deviceAPI->getSourceBuddies().size() == deviceParams->m_nbRxChannels) + { + qCritical("XTRXInput::openDevice: no more Rx channels available in device"); + return false; // no more Rx channels available in device + } + else + { + qDebug("XTRXInput::openDevice: at least one more Rx channel is available in device"); + } + + // check if the requested channel is busy and abort if so (should not happen if device management is working correctly) + + char *busyChannels = new char[deviceParams->m_nbRxChannels]; + memset(busyChannels, 0, deviceParams->m_nbRxChannels); + + for (unsigned int i = 0; i < m_deviceAPI->getSourceBuddies().size(); i++) + { + DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[i]; + DeviceXTRXShared *buddyShared = (DeviceXTRXShared *) buddy->getBuddySharedPtr(); + + if (buddyShared->m_channel == requestedChannel) + { + qCritical("XTRXInput::openDevice: cannot open busy channel %u", requestedChannel); + return false; + } + } + + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel + delete[] busyChannels; + } + // look for Tx buddies and get reference to common parameters + // take the first Rx channel + else if (m_deviceAPI->getSinkBuddies().size() > 0) // then sink + { + qDebug("XTRXInput::openDevice: look in Tx buddies"); + + DeviceSinkAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0]; + m_deviceShared = *((DeviceXTRXShared *) sinkBuddy->getBuddySharedPtr()); // copy parameters + + if (m_deviceShared.m_deviceParams == 0) + { + qCritical("XTRXInput::openDevice: cannot get device parameters from Tx buddy"); + return false; // the device params should have been created by the buddy + } + else + { + qDebug("XTRXInput::openDevice: getting device parameters from Tx buddy"); + } + + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel + } + // There are no buddies then create the first XTRX common parameters + // open the device this will also populate common fields + // take the first Rx channel + else + { + qDebug("XTRXInput::openDevice: open device here"); + + m_deviceShared.m_deviceParams = new DeviceXTRXParams(); + char serial[256]; + strcpy(serial, qPrintable(m_deviceAPI->getSampleSourceSerial())); + + if (!m_deviceShared.m_deviceParams->open(serial)) { + delete m_deviceShared.m_deviceParams; + m_deviceShared.m_deviceParams = 0; + + return false; + } + + m_deviceShared.m_channel = requestedChannel; // acknowledge the requested channel + } + + m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API + + return true; +} + +void XTRXInput::suspendRxBuddies() +{ + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator itSource = sourceBuddies.begin(); + + qDebug("XTRXInput::suspendRxBuddies (%lu)", sourceBuddies.size()); + + for (; itSource != sourceBuddies.end(); ++itSource) + { + DeviceXTRXShared *buddySharedPtr = (DeviceXTRXShared *) (*itSource)->getBuddySharedPtr(); + + if (buddySharedPtr->m_thread && buddySharedPtr->m_thread->isRunning()) + { + buddySharedPtr->m_thread->stopWork(); + buddySharedPtr->m_threadWasRunning = true; + } + else + { + buddySharedPtr->m_threadWasRunning = false; + } + } +} + +void XTRXInput::suspendTxBuddies() +{ + const std::vector& sinkBuddies = m_deviceAPI->getSinkBuddies(); + std::vector::const_iterator itSink = sinkBuddies.begin(); + + qDebug("XTRXInput::suspendTxBuddies (%lu)", sinkBuddies.size()); + + for (; itSink != sinkBuddies.end(); ++itSink) + { + DeviceXTRXShared *buddySharedPtr = (DeviceXTRXShared *) (*itSink)->getBuddySharedPtr(); + + if (buddySharedPtr->m_thread) { + buddySharedPtr->m_thread->stopWork(); + buddySharedPtr->m_threadWasRunning = true; + } + else + { + buddySharedPtr->m_threadWasRunning = false; + } + } +} + +void XTRXInput::resumeRxBuddies() +{ + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator itSource = sourceBuddies.begin(); + + qDebug("XTRXInput::resumeRxBuddies (%lu)", sourceBuddies.size()); + + for (; itSource != sourceBuddies.end(); ++itSource) + { + DeviceXTRXShared *buddySharedPtr = (DeviceXTRXShared *) (*itSource)->getBuddySharedPtr(); + + if (buddySharedPtr->m_threadWasRunning) { + buddySharedPtr->m_thread->startWork(); + } + } +} + +void XTRXInput::resumeTxBuddies() +{ + const std::vector& sinkBuddies = m_deviceAPI->getSinkBuddies(); + std::vector::const_iterator itSink = sinkBuddies.begin(); + + qDebug("XTRXInput::resumeTxBuddies (%lu)", sinkBuddies.size()); + + for (; itSink != sinkBuddies.end(); ++itSink) + { + DeviceXTRXShared *buddySharedPtr = (DeviceXTRXShared *) (*itSink)->getBuddySharedPtr(); + + if (buddySharedPtr->m_threadWasRunning) { + buddySharedPtr->m_thread->startWork(); + } + } +} + +void XTRXInput::closeDevice() +{ + if (m_deviceShared.m_deviceParams->getDevice() == 0) { // was never open + return; + } + + if (m_running) { stop(); } + + m_deviceShared.m_channel = XTRX_CH_AB; + + // No buddies so effectively close the device + + if ((m_deviceAPI->getSinkBuddies().size() == 0) && (m_deviceAPI->getSourceBuddies().size() == 0)) + { + m_deviceShared.m_deviceParams->close(); + delete m_deviceShared.m_deviceParams; + m_deviceShared.m_deviceParams = 0; + } +} + +bool XTRXInput::acquireChannel() +{ + //suspendRxBuddies(); + //suspendTxBuddies(); + + qDebug("XTRXInput::acquireChannel: stream set up on Rx channel %d", m_deviceShared.m_channel); + + //resumeTxBuddies(); + //resumeRxBuddies(); + + m_channelAcquired = true; + return true; +} + +void XTRXInput::releaseChannel() +{ + //suspendRxBuddies(); + //suspendTxBuddies(); + + qDebug("XTRXInput::releaseChannel: Rx channel %d disabled", m_deviceShared.m_channel); + + //resumeTxBuddies(); + //resumeRxBuddies(); + + // The channel will be effectively released to be reused in another device set only at close time + + m_channelAcquired = false; +} + +void XTRXInput::init() +{ + applySettings(m_settings, true, false); +} + +bool XTRXInput::start() +{ + if (!m_deviceShared.m_deviceParams->getDevice()) { + return false; + } + + if (m_running) { stop(); } + + if (!acquireChannel()) + { + return false; + } + + applySettings(m_settings, true); + + // start / stop streaming is done in the thread. + + if ((m_XTRXInputThread = new XTRXInputThread(&m_deviceShared, &m_sampleFifo)) == 0) + { + qFatal("XTRXInput::start: cannot create thread"); + stop(); + return false; + } + else + { + qDebug("XTRXInput::start: thread created"); + } + + m_XTRXInputThread->setLog2Decimation(m_settings.m_log2SoftDecim); + + m_XTRXInputThread->startWork(); + + m_deviceShared.m_thread = m_XTRXInputThread; + m_running = true; + + return true; +} + +void XTRXInput::stop() +{ + qDebug("XTRXInput::stop"); + + if (m_XTRXInputThread != 0) + { + m_XTRXInputThread->stopWork(); + delete m_XTRXInputThread; + m_XTRXInputThread = 0; + } + + m_deviceShared.m_thread = 0; + m_running = false; + + releaseChannel(); +} + +QByteArray XTRXInput::serialize() const +{ + return m_settings.serialize(); +} + +bool XTRXInput::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureXTRX* message = MsgConfigureXTRX::create(m_settings, true); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) + { + MsgConfigureXTRX* messageToGUI = MsgConfigureXTRX::create(m_settings, true); + m_guiMessageQueue->push(messageToGUI); + } + + return success; +} + +const QString& XTRXInput::getDeviceDescription() const +{ + return m_deviceDescription; +} + +int XTRXInput::getSampleRate() const +{ + double rate = m_settings.m_devSampleRate; + return (int)((rate / (1<push(messageToGUI); + } +} + +std::size_t XTRXInput::getChannelIndex() +{ + return m_deviceShared.m_channel; +} + +void XTRXInput::getLORange(float& minF, float& maxF, float& stepF) const +{ + minF = 29e6; + maxF = 3840e6; + stepF = 10; + qDebug("XTRXInput::getLORange: min: %f max: %f step: %f", + minF, maxF, stepF); +} + +void XTRXInput::getSRRange(float& minF, float& maxF, float& stepF) const +{ + minF = 100e3; + maxF = 120e6; + stepF = 10; + qDebug("XTRXInput::getSRRange: min: %f max: %f step: %f", + minF, maxF, stepF); +} + +void XTRXInput::getLPRange(float& minF, float& maxF, float& stepF) const +{ + minF = 500e3; + maxF = 130e6; + stepF = 10; + qDebug("XTRXInput::getLPRange: min: %f max: %f step: %f", + minF, maxF, stepF); +} + +uint32_t XTRXInput::getHWLog2Decim() const +{ + return m_deviceShared.m_deviceParams->m_log2OvSRRx; +} + +bool XTRXInput::handleMessage(const Message& message) +{ + if (MsgConfigureXTRX::match(message)) + { + MsgConfigureXTRX& conf = (MsgConfigureXTRX&) message; + qDebug() << "XTRXInput::handleMessage: MsgConfigureXTRX"; + + if (!applySettings(conf.getSettings(), conf.getForce())) + { + qDebug("XTRXInput::handleMessage config error"); + } + + return true; + } + else if (DeviceXTRXShared::MsgReportBuddyChange::match(message)) + { + DeviceXTRXShared::MsgReportBuddyChange& report = (DeviceXTRXShared::MsgReportBuddyChange&) message; + + if (report.getRxElseTx()) + { + m_settings.m_devSampleRate = report.getDevSampleRate(); + m_settings.m_log2HardDecim = report.getLog2HardDecimInterp(); + m_settings.m_centerFrequency = report.getCenterFrequency(); + } + else + { + m_settings.m_devSampleRate = m_deviceShared.m_inputRate; + m_settings.m_log2HardDecim = log2(m_deviceShared.m_masterRate / m_deviceShared.m_inputRate / 4); + + qDebug() << "XTRXInput::handleMessage: MsgReportBuddyChange:" + << " host_Hz: " << m_deviceShared.m_inputRate + << " rf_Hz: " << m_deviceShared.m_masterRate / 4 + << " m_log2HardDecim: " << m_settings.m_log2HardDecim; + } + + if (m_settings.m_ncoEnable) // need to reset NCO after sample rate change + { + applySettings(m_settings, true, true); + } + + int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0; + + DSPSignalNotification *notif = new DSPSignalNotification( + m_settings.m_devSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif); + + DeviceXTRXShared::MsgReportBuddyChange *reportToGUI = DeviceXTRXShared::MsgReportBuddyChange::create( + m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true); + getMessageQueueToGUI()->push(reportToGUI); + + return true; + } + else if (DeviceXTRXShared::MsgReportClockSourceChange::match(message)) + { + DeviceXTRXShared::MsgReportClockSourceChange& report = (DeviceXTRXShared::MsgReportClockSourceChange&) message; + + m_settings.m_extClock = report.getExtClock(); + m_settings.m_extClockFreq = report.getExtClockFeq(); + + DeviceXTRXShared::MsgReportClockSourceChange *reportToGUI = DeviceXTRXShared::MsgReportClockSourceChange::create( + m_settings.m_extClock, m_settings.m_extClockFreq); + getMessageQueueToGUI()->push(reportToGUI); + + return true; + } + else if (MsgGetStreamInfo::match(message)) + { + qDebug() << "XTRXInput::handleMessage: MsgGetStreamInfo"; + + if (m_deviceAPI->getSampleSourceGUIMessageQueue()) + { + uint64_t fifolevel; + + xtrx_val_get(m_deviceShared.m_deviceParams->getDevice(), + XTRX_RX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevel); + + MsgReportStreamInfo *report = MsgReportStreamInfo::create( + true, + true, + fifolevel, + 65536, + 0, + 0, + 0, + 0, + 0, + 0); + m_deviceAPI->getSampleSourceGUIMessageQueue()->push(report); + } + return true; + } + else if (MsgGetDeviceInfo::match(message)) + { + double temp = 0.0; + if (m_deviceShared.m_deviceParams->getDevice() && ( + (temp = m_deviceShared.get_temperature() / 256.0) != 0.0)) + { + qDebug("XTRXInput::handleMessage: MsgGetDeviceInfo: temperature: %f", temp); + } + else + { + qDebug("XTRXInput::handleMessage: MsgGetDeviceInfo: cannot get temperature"); + } + + // send to oneself + if (m_deviceAPI->getSampleSourceGUIMessageQueue()) { + DeviceXTRXShared::MsgReportDeviceInfo *report = DeviceXTRXShared::MsgReportDeviceInfo::create(temp); + m_deviceAPI->getSampleSourceGUIMessageQueue()->push(report); + } + + // send to source buddies + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator itSource = sourceBuddies.begin(); + + for (; itSource != sourceBuddies.end(); ++itSource) + { + if ((*itSource)->getSampleSourceGUIMessageQueue()) + { + DeviceXTRXShared::MsgReportDeviceInfo *report = DeviceXTRXShared::MsgReportDeviceInfo::create(temp); + (*itSource)->getSampleSourceGUIMessageQueue()->push(report); + } + } + + // send to sink buddies + const std::vector& sinkBuddies = m_deviceAPI->getSinkBuddies(); + std::vector::const_iterator itSink = sinkBuddies.begin(); + + for (; itSink != sinkBuddies.end(); ++itSink) + { + if ((*itSink)->getSampleSinkGUIMessageQueue()) + { + DeviceXTRXShared::MsgReportDeviceInfo *report = DeviceXTRXShared::MsgReportDeviceInfo::create(temp); + (*itSink)->getSampleSinkGUIMessageQueue()->push(report); + } + } + + return true; + } + else if (MsgFileRecord::match(message)) + { + MsgFileRecord& conf = (MsgFileRecord&) message; + qDebug() << "XTRXInput::handleMessage: MsgFileRecord: " << conf.getStartStop(); + + if (conf.getStartStop()) { + m_fileSink->startRecording(); + } else { + m_fileSink->stopRecording(); + } + + return true; + } + else if (MsgStartStop::match(message)) + { + MsgStartStop& cmd = (MsgStartStop&) message; + qDebug() << "XTRXInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); + + if (cmd.getStartStop()) + { + if (m_deviceAPI->initAcquisition()) + { + m_deviceAPI->startAcquisition(); + } + } + else + { + m_deviceAPI->stopAcquisition(); + } + + return true; + } + else + { + return false; + } +} + +static double tia_to_db(unsigned idx) +{ + switch (idx) { + case 1: return 12; + case 2: return 9; + default: return 0; + } +} + +void XTRXInput::apply_gain_auto(double gain) +{ + if (xtrx_set_gain(m_deviceShared.m_deviceParams->getDevice(), + XTRX_CH_AB /*m_deviceShared.m_channel*/, + XTRX_RX_LNA_GAIN, + gain, + NULL) < 0) + { + qDebug("XTRXInput::applySettings: xtrx_set_gain(auto) failed"); + } + else + { + //doCalibration = true; + qDebug() << "XTRXInput::applySettings: Gain (auto) set to " << gain; + } +} + +void XTRXInput::apply_gain_lna(double gain) +{ + if (xtrx_set_gain(m_deviceShared.m_deviceParams->getDevice(), + XTRX_CH_AB /*m_deviceShared.m_channel*/, + XTRX_RX_LNA_GAIN, + gain, + NULL) < 0) + { + qDebug("XTRXInput::applySettings: xtrx_set_gain(LNA) failed"); + } + else + { + qDebug() << "XTRXInput::applySettings: Gain (LNA) set to " << gain; + } +} + +void XTRXInput::apply_gain_tia(double gain) +{ + if (xtrx_set_gain(m_deviceShared.m_deviceParams->getDevice(), + XTRX_CH_AB /*m_deviceShared.m_channel*/, + XTRX_RX_TIA_GAIN, + gain, + NULL) < 0) + { + qDebug("XTRXInput::applySettings: xtrx_set_gain(TIA) failed"); + } + else + { + qDebug() << "XTRXInput::applySettings: Gain (TIA) set to " << gain; + } +} + +void XTRXInput::apply_gain_pga(double gain) +{ + if (xtrx_set_gain(m_deviceShared.m_deviceParams->getDevice(), + XTRX_CH_AB /*m_deviceShared.m_channel*/, + XTRX_RX_TIA_GAIN, + gain, + NULL) < 0) + { + qDebug("XTRXInput::applySettings: xtrx_set_gain(PGA) failed"); + } + else + { + qDebug() << "XTRXInput::applySettings: Gain (PGA) set to " << gain; + } +} + +bool XTRXInput::applySettings(const XTRXInputSettings& settings, bool force, bool forceNCOFrequency) +{ + bool forwardChangeOwnDSP = false; + bool forwardChangeRxDSP = false; + bool forwardChangeAllDSP = false; + bool forwardClockSource = false; + bool ownThreadWasRunning = false; + bool doLPCalibration = false; + bool doChangeSampleRate = false; + bool doChangeFreq = false; + + bool doGainAuto = false; + bool doGainLna = false; + bool doGainTia = false; + bool doGainPga = false; + + // apply settings + + if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) + { + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); + } + + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) + { + m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection); + } + + if ((m_settings.m_pwrmode != settings.m_pwrmode)) { + if (xtrx_val_set(m_deviceShared.m_deviceParams->getDevice(), + XTRX_TRX, XTRX_CH_AB, XTRX_LMS7_PWR_MODE, settings.m_pwrmode) < 0) + { + qCritical("XTRXInput::applySettings: could not set power mode %d", + settings.m_pwrmode); + } + } + + if ((m_settings.m_extClock != settings.m_extClock) || + (settings.m_extClock && (m_settings.m_extClockFreq != settings.m_extClockFreq)) || force) + { + + xtrx_set_ref_clk(m_deviceShared.m_deviceParams->getDevice(), + (settings.m_extClock) ? settings.m_extClockFreq : 0, + (settings.m_extClock) ? XTRX_CLKSRC_EXT : XTRX_CLKSRC_INT); + { + forwardClockSource = true; + doChangeSampleRate = true; + doChangeFreq = true; + qDebug("XTRXInput::applySettings: clock set to %s (Ext: %d Hz)", + settings.m_extClock ? "external" : "internal", + settings.m_extClockFreq); + } + } + + if ((m_settings.m_devSampleRate != settings.m_devSampleRate) + || (m_settings.m_log2HardDecim != settings.m_log2HardDecim) || force) + { + forwardChangeAllDSP = true; //m_settings.m_devSampleRate != settings.m_devSampleRate; + + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) + { + doChangeSampleRate = true; + } + } + + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) + { + if ((m_settings.m_gainMode != settings.m_gainMode) || force) + { + if (settings.m_gainMode == XTRXInputSettings::GAIN_AUTO) + { + doGainAuto = true; + } + else + { + doGainLna = true; + doGainTia = true; + doGainPga = true; + } + } + else if (m_settings.m_gainMode == XTRXInputSettings::GAIN_AUTO) + { + doGainAuto = true; + } + else if (m_settings.m_gainMode == XTRXInputSettings::GAIN_MANUAL) + { + if (m_settings.m_lnaGain != settings.m_lnaGain) + { + doGainLna = true; + } + if (m_settings.m_tiaGain != settings.m_tiaGain) + { + doGainTia = true; + } + if (m_settings.m_pgaGain != settings.m_pgaGain) + { + doGainPga = true; + } + } + } + + if ((m_settings.m_lpfBW != settings.m_lpfBW) || force) + { + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) + { + doLPCalibration = true; + } + } + +#if 0 + if ((m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) || + (m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force) + { + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) + { + if (LMS_SetGFIRLPF(m_deviceShared.m_deviceParams->getDevice(), + LMS_CH_RX, + m_deviceShared.m_channel, + settings.m_lpfFIREnable, + settings.m_lpfFIRBW) < 0) + { + qCritical("XTRXInput::applySettings: could %s and set LPF FIR to %f Hz", + settings.m_lpfFIREnable ? "enable" : "disable", + settings.m_lpfFIRBW); + } + else + { + //doCalibration = true; + qDebug("XTRXInput::applySettings: %sd and set LPF FIR to %f Hz", + settings.m_lpfFIREnable ? "enable" : "disable", + settings.m_lpfFIRBW); + } + } + } +#endif + + + if ((m_settings.m_log2SoftDecim != settings.m_log2SoftDecim) || force) + { + forwardChangeOwnDSP = true; + m_deviceShared.m_log2Soft = settings.m_log2SoftDecim; // for buddies + + if (m_XTRXInputThread != 0) + { + m_XTRXInputThread->setLog2Decimation(settings.m_log2SoftDecim); + qDebug() << "XTRXInput::applySettings: set soft decimation to " << (1<getDevice() != 0 && m_channelAcquired) + { + if (xtrx_set_antenna(m_deviceShared.m_deviceParams->getDevice(), + settings.m_antennaPath) < 0) + { + qCritical("XTRXInput::applySettings: could not set antenna path to %d", + (int) settings.m_antennaPath); + } + else + { + qDebug("XTRXInput::applySettings: set antenna path to %d on channel %d", + (int) settings.m_antennaPath, + m_deviceShared.m_channel); + } + } + } + + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) + { + doChangeFreq = true; + } + + if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency) || + (m_settings.m_ncoEnable != settings.m_ncoEnable) || force) + { + forceNCOFrequency = true; + } + + + m_settings = settings; + + if (doChangeSampleRate) + { + if (m_XTRXInputThread && m_XTRXInputThread->isRunning()) + { + m_XTRXInputThread->stopWork(); + ownThreadWasRunning = true; + } + + suspendRxBuddies(); + suspendTxBuddies(); + + double master = (settings.m_log2HardDecim == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardDecim)); + if (m_deviceShared.set_samplerate(settings.m_devSampleRate, + master, //(settings.m_devSampleRate<m_log2OvSRRx = settings.m_log2HardDecim; + m_deviceShared.m_deviceParams->m_sampleRate = settings.m_devSampleRate; + + doChangeFreq = true; + forceNCOFrequency = true; + qDebug("XTRXInput::applySettings: set sample rate set to %f with oversampling of %d", + settings.m_devSampleRate, + 1<startWork(); + } + } + + if (doLPCalibration) + { + if (xtrx_tune_rx_bandwidth(m_deviceShared.m_deviceParams->getDevice(), + m_deviceShared.m_channel, + m_settings.m_lpfBW, + NULL) < 0) + { + qCritical("XTRXInput::applySettings: could not set LPF to %f Hz", m_settings.m_lpfBW); + } + else + { + qDebug("XTRXInput::applySettings: LPF set to %f Hz", m_settings.m_lpfBW); + } + } + + if (doGainAuto) + { + apply_gain_auto(m_settings.m_gain); + } + if (doGainLna) + { + apply_gain_auto(m_settings.m_lnaGain); + } + if (doGainTia) + { + apply_gain_auto(tia_to_db(m_settings.m_tiaGain)); + } + if (doGainPga) + { + apply_gain_auto(m_settings.m_pgaGain); + } + + if (doChangeFreq) + { + forwardChangeRxDSP = true; + + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) + { + if (xtrx_tune(m_deviceShared.m_deviceParams->getDevice(), + XTRX_TUNE_RX_FDD, + settings.m_centerFrequency, + NULL) < 0) + { + qCritical("XTRXInput::applySettings: could not set frequency to %lu", settings.m_centerFrequency); + } + else + { + //doCalibration = true; + m_deviceShared.m_centerFrequency = settings.m_centerFrequency; // for buddies + qDebug("XTRXInput::applySettings: frequency set to %lu", settings.m_centerFrequency); + } + } + } + + if (forceNCOFrequency) + { + if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired) + { + if (xtrx_tune(m_deviceShared.m_deviceParams->getDevice(), + XTRX_TUNE_BB_RX, + /* m_deviceShared.m_channel, */ + (settings.m_ncoEnable) ? settings.m_ncoFrequency : 0, + NULL) < 0) + { + qCritical("XTRXInput::applySettings: could not %s and set NCO to %d Hz", + settings.m_ncoEnable ? "enable" : "disable", + settings.m_ncoFrequency); + } + else + { + forwardChangeOwnDSP = true; + m_deviceShared.m_ncoFrequency = settings.m_ncoEnable ? settings.m_ncoFrequency : 0; // for buddies + qDebug("XTRXInput::applySettings: %sd and set NCO to %d Hz", + settings.m_ncoEnable ? "enable" : "disable", + settings.m_ncoFrequency); + } + } + } + + + // forward changes to buddies or oneself + + if (forwardChangeAllDSP) + { + qDebug("XTRXInput::applySettings: forward change to all buddies"); + + int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0; + + // send to self first + DSPSignalNotification *notif = new DSPSignalNotification( + m_settings.m_devSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif); + + // send to source buddies + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator itSource = sourceBuddies.begin(); + + for (; itSource != sourceBuddies.end(); ++itSource) + { + DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( + m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true); + (*itSource)->getSampleSourceInputMessageQueue()->push(report); + } + + // send to sink buddies + const std::vector& sinkBuddies = m_deviceAPI->getSinkBuddies(); + std::vector::const_iterator itSink = sinkBuddies.begin(); + + for (; itSink != sinkBuddies.end(); ++itSink) + { + DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( + m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true); + (*itSink)->getSampleSinkInputMessageQueue()->push(report); + } + } + else if (forwardChangeRxDSP) + { + qDebug("XTRXInput::applySettings: forward change to Rx buddies"); + + int sampleRate = m_settings.m_devSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif); + + // send to source buddies + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator itSource = sourceBuddies.begin(); + + for (; itSource != sourceBuddies.end(); ++itSource) + { + DeviceXTRXShared::MsgReportBuddyChange *report = DeviceXTRXShared::MsgReportBuddyChange::create( + m_settings.m_devSampleRate, m_settings.m_log2HardDecim, m_settings.m_centerFrequency, true); + (*itSource)->getSampleSourceInputMessageQueue()->push(report); + } + } + else if (forwardChangeOwnDSP) + { + qDebug("XTRXInput::applySettings: forward change to self only"); + + int sampleRate = m_settings.m_devSampleRate/(1<handleMessage(*notif); // forward to file sink + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); + } + + if (forwardClockSource) + { + // send to source buddies + const std::vector& sourceBuddies = m_deviceAPI->getSourceBuddies(); + std::vector::const_iterator itSource = sourceBuddies.begin(); + + for (; itSource != sourceBuddies.end(); ++itSource) + { + DeviceXTRXShared::MsgReportClockSourceChange *report = DeviceXTRXShared::MsgReportClockSourceChange::create( + m_settings.m_extClock, m_settings.m_extClockFreq); + (*itSource)->getSampleSourceInputMessageQueue()->push(report); + } + + // send to sink buddies + const std::vector& sinkBuddies = m_deviceAPI->getSinkBuddies(); + std::vector::const_iterator itSink = sinkBuddies.begin(); + + for (; itSink != sinkBuddies.end(); ++itSink) + { + DeviceXTRXShared::MsgReportClockSourceChange *report = DeviceXTRXShared::MsgReportClockSourceChange::create( + m_settings.m_extClock, m_settings.m_extClockFreq); + (*itSink)->getSampleSinkInputMessageQueue()->push(report); + } + } + + qDebug() << "XTRXInput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz" + << " device stream sample rate: " << m_settings.m_devSampleRate << "S/s" + << " sample rate with soft decimation: " << m_settings.m_devSampleRate/(1<. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUT_H_ +#define PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUT_H_ + +#include +#include + +#include "dsp/devicesamplesource.h" +#include "xtrx/devicextrxshared.h" +#include "xtrxinputsettings.h" + +class DeviceSourceAPI; +class XTRXInputThread; +struct DeviceXTRXParams; +class FileRecord; + +class XTRXInput : public DeviceSampleSource +{ +public: + class MsgConfigureXTRX : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const XTRXInputSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureXTRX* create(const XTRXInputSettings& settings, bool force) + { + return new MsgConfigureXTRX(settings, force); + } + + private: + XTRXInputSettings m_settings; + bool m_force; + + MsgConfigureXTRX(const XTRXInputSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgGetStreamInfo : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgGetStreamInfo* create() + { + return new MsgGetStreamInfo(); + } + + private: + MsgGetStreamInfo() : + Message() + { } + }; + + class MsgGetDeviceInfo : public Message { + MESSAGE_CLASS_DECLARATION + + public: + static MsgGetDeviceInfo* create() + { + return new MsgGetDeviceInfo(); + } + + private: + MsgGetDeviceInfo() : + Message() + { } + }; + + class MsgReportStreamInfo : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getSuccess() const { return m_success; } + bool getActive() const { return m_active; } + uint32_t getFifoFilledCount() const { return m_fifoFilledCount; } + uint32_t getFifoSize() const { return m_fifoSize; } + uint32_t getUnderrun() const { return m_underrun; } + uint32_t getOverrun() const { return m_overrun; } + uint32_t getDroppedPackets() const { return m_droppedPackets; } + float getSampleRate() const { return m_sampleRate; } + float getLinkRate() const { return m_linkRate; } + uint64_t getTimestamp() const { return m_timestamp; } + + static MsgReportStreamInfo* create( + bool success, + bool active, + uint32_t fifoFilledCount, + uint32_t fifoSize, + uint32_t underrun, + uint32_t overrun, + uint32_t droppedPackets, + float sampleRate, + float linkRate, + uint64_t timestamp + ) + { + return new MsgReportStreamInfo( + success, + active, + fifoFilledCount, + fifoSize, + underrun, + overrun, + droppedPackets, + sampleRate, + linkRate, + timestamp + ); + } + + private: + bool m_success; + // everything from lms_stream_status_t + bool m_active; //!< Indicates whether the stream is currently active + uint32_t m_fifoFilledCount; //!< Number of samples in FIFO buffer + uint32_t m_fifoSize; //!< Size of FIFO buffer + uint32_t m_underrun; //!< FIFO underrun count + uint32_t m_overrun; //!< FIFO overrun count + uint32_t m_droppedPackets; //!< Number of dropped packets by HW + float m_sampleRate; //!< Sampling rate of the stream + float m_linkRate; //!< Combined data rate of all stream of the same direction (TX or RX) + uint64_t m_timestamp; //!< Current HW timestamp + + MsgReportStreamInfo( + bool success, + bool active, + uint32_t fifoFilledCount, + uint32_t fifoSize, + uint32_t underrun, + uint32_t overrun, + uint32_t droppedPackets, + float sampleRate, + float linkRate, + uint64_t timestamp + ) : + Message(), + m_success(success), + m_active(active), + m_fifoFilledCount(fifoFilledCount), + m_fifoSize(fifoSize), + m_underrun(underrun), + m_overrun(overrun), + m_droppedPackets(droppedPackets), + m_sampleRate(sampleRate), + m_linkRate(linkRate), + m_timestamp(timestamp) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + class MsgFileRecord : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgFileRecord* create(bool startStop) { + return new MsgFileRecord(startStop); + } + + protected: + bool m_startStop; + + MsgFileRecord(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + XTRXInput(DeviceSourceAPI *deviceAPI); + virtual ~XTRXInput(); + virtual void destroy(); + + virtual void init(); + virtual bool start(); + virtual void stop(); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } + virtual const QString& getDeviceDescription() const; + virtual int getSampleRate() const; + virtual quint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + + virtual bool handleMessage(const Message& message); + + std::size_t getChannelIndex(); + void getLORange(float& minF, float& maxF, float& stepF) const; + void getSRRange(float& minF, float& maxF, float& stepF) const; + void getLPRange(float& minF, float& maxF, float& stepF) const; + uint32_t getHWLog2Decim() const; + + void apply_gain_auto(double gain); + void apply_gain_lna(double gain); + void apply_gain_tia(double gain); + void apply_gain_pga(double gain); + +private: + DeviceSourceAPI *m_deviceAPI; + QMutex m_mutex; + XTRXInputSettings m_settings; + XTRXInputThread* m_XTRXInputThread; + QString m_deviceDescription; + bool m_running; + DeviceXTRXShared m_deviceShared; + bool m_channelAcquired; + + FileRecord *m_fileSink; //!< File sink to record device I/Q output + + bool openDevice(); + void closeDevice(); + bool acquireChannel(); + void releaseChannel(); + void suspendRxBuddies(); + void resumeRxBuddies(); + void suspendTxBuddies(); + void resumeTxBuddies(); + bool applySettings(const XTRXInputSettings& settings, bool force = false, bool forceNCOFrequency = false); +}; + +#endif /* PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUT_H_ */ diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.cpp b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp new file mode 100644 index 000000000..5b1b162f1 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.cpp @@ -0,0 +1,590 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "xtrxinputgui.h" + +#include +#include + +#include + +#include "ui_xtrxinputgui.h" +#include "gui/colormapper.h" +#include "gui/glspectrum.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "device/devicesourceapi.h" +#include "device/deviceuiset.h" + +XTRXInputGUI::XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent) : + QWidget(parent), + ui(new Ui::XTRXInputGUI), + m_deviceUISet(deviceUISet), + m_settings(), + m_sampleRate(0), + m_lastEngineState((DSPDeviceSourceEngine::State)-1), + m_doApplySettings(true), + m_forceSettings(true), + m_statusCounter(0), + m_deviceStatusCounter(0) +{ + m_XTRXInput = (XTRXInput*) m_deviceUISet->m_deviceSourceAPI->getSampleSource(); + + ui->setupUi(this); + + float minF, maxF, stepF; + + m_XTRXInput->getLORange(minF, maxF, stepF); + ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->centerFrequency->setValueRange(7, ((uint32_t) minF)/1000, ((uint32_t) maxF)/1000); // frequency dial is in kHz + + m_XTRXInput->getSRRange(minF, maxF, stepF); + ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); + ui->sampleRate->setValueRange(8, (uint32_t) minF, (uint32_t) maxF); + + m_XTRXInput->getLPRange(minF, maxF, stepF); + ui->lpf->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); + ui->lpf->setValueRange(6, (minF/1000)+1, maxF/1000); + + ui->lpFIR->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); + ui->lpFIR->setValueRange(5, 1U, 56000U); + + ui->ncoFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + + ui->channelNumberText->setText(tr("#%1").arg(m_XTRXInput->getChannelIndex())); + + ui->hwDecimLabel->setText(QString::fromUtf8("H\u2193")); + ui->swDecimLabel->setText(QString::fromUtf8("S\u2193")); + + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(500); + + displaySettings(); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); +} + +XTRXInputGUI::~XTRXInputGUI() +{ + delete ui; +} + +void XTRXInputGUI::destroy() +{ + delete this; +} + +void XTRXInputGUI::setName(const QString& name) +{ + setObjectName(name); +} + +QString XTRXInputGUI::getName() const +{ + return objectName(); +} + +void XTRXInputGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + sendSettings(); +} + +qint64 XTRXInputGUI::getCenterFrequency() const +{ + return m_settings.m_centerFrequency; +} + +void XTRXInputGUI::setCenterFrequency(qint64 centerFrequency) +{ + m_settings.m_centerFrequency = centerFrequency; + displaySettings(); + sendSettings(); +} + +QByteArray XTRXInputGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool XTRXInputGUI::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + displaySettings(); + m_forceSettings = true; + sendSettings(); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool XTRXInputGUI::handleMessage(const Message& message) +{ + + if (DeviceXTRXShared::MsgReportBuddyChange::match(message)) + { + DeviceXTRXShared::MsgReportBuddyChange& report = (DeviceXTRXShared::MsgReportBuddyChange&) message; + m_settings.m_devSampleRate = report.getDevSampleRate(); + m_settings.m_log2HardDecim = report.getLog2HardDecimInterp(); + + if (report.getRxElseTx()) { + m_settings.m_centerFrequency = report.getCenterFrequency(); + } + + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + + return true; + } + else if (DeviceXTRXShared::MsgReportClockSourceChange::match(message)) + { + DeviceXTRXShared::MsgReportClockSourceChange& report = (DeviceXTRXShared::MsgReportClockSourceChange&) message; + m_settings.m_extClockFreq = report.getExtClockFeq(); + m_settings.m_extClock = report.getExtClock(); + + blockApplySettings(true); + ui->extClock->setExternalClockFrequency(m_settings.m_extClockFreq); + ui->extClock->setExternalClockActive(m_settings.m_extClock); + blockApplySettings(false); + + return true; + } + else if (XTRXInput::MsgReportStreamInfo::match(message)) + { + XTRXInput::MsgReportStreamInfo& report = (XTRXInput::MsgReportStreamInfo&) message; + + if (report.getSuccess()) + { + if (report.getActive()) { + ui->streamStatusLabel->setStyleSheet("QLabel { background-color : green; }"); + } else { + ui->streamStatusLabel->setStyleSheet("QLabel { background-color : blue; }"); + } + + ui->streamLinkRateText->setText(tr("%1 MB/s").arg(QString::number(report.getLinkRate() / 1000000.0f, 'f', 3))); + + if (report.getUnderrun() > 0) { + ui->underrunLabel->setStyleSheet("QLabel { background-color : red; }"); + } else { + ui->underrunLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + if (report.getOverrun() > 0) { + ui->overrunLabel->setStyleSheet("QLabel { background-color : red; }"); + } else { + ui->overrunLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + if (report.getDroppedPackets() > 0) { + ui->droppedLabel->setStyleSheet("QLabel { background-color : red; }"); + } else { + ui->droppedLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + ui->fifoBar->setMaximum(report.getFifoSize()); + ui->fifoBar->setValue(report.getFifoFilledCount()); + ui->fifoBar->setToolTip(tr("FIFO fill %1/%2 samples").arg(QString::number(report.getFifoFilledCount())).arg(QString::number(report.getFifoSize()))); + } + else + { + ui->streamStatusLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + return true; + } + else if (DeviceXTRXShared::MsgReportDeviceInfo::match(message)) + { + DeviceXTRXShared::MsgReportDeviceInfo& report = (DeviceXTRXShared::MsgReportDeviceInfo&) message; + ui->temperatureText->setText(tr("%1C").arg(QString::number(report.getTemperature(), 'f', 0))); + return true; + } + else if (XTRXInput::MsgStartStop::match(message)) + { + XTRXInput::MsgStartStop& notif = (XTRXInput::MsgStartStop&) message; + blockApplySettings(true); + ui->startStop->setChecked(notif.getStartStop()); + blockApplySettings(false); + + return true; + } + return false; +} + +void XTRXInputGUI::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + if (DSPSignalNotification::match(*message)) + { + DSPSignalNotification* notif = (DSPSignalNotification*) message; + m_sampleRate = notif->getSampleRate(); + m_deviceCenterFrequency = notif->getCenterFrequency(); + qDebug("XTRXInputGUI::handleInputMessages: DSPSignalNotification: SampleRate: %d, CenterFrequency: %llu", notif->getSampleRate(), notif->getCenterFrequency()); + updateSampleRateAndFrequency(); + + delete message; + } + else + { + if (handleMessage(*message)) { + delete message; + } + } + } +} + +void XTRXInputGUI::updateADCRate() +{ + uint32_t adcRate = m_settings.m_devSampleRate * (1<adcRateLabel->setText(tr("%1k").arg(QString::number(adcRate / 1000.0f, 'g', 5))); + } else { + ui->adcRateLabel->setText(tr("%1M").arg(QString::number(adcRate / 1000000.0f, 'g', 5))); + } +} + +void XTRXInputGUI::updateSampleRateAndFrequency() +{ + m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5))); +} + +void XTRXInputGUI::displaySettings() +{ + ui->extClock->setExternalClockFrequency(m_settings.m_extClockFreq); + ui->extClock->setExternalClockActive(m_settings.m_extClock); + + ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000); + ui->sampleRate->setValue(m_settings.m_devSampleRate); + + ui->dcOffset->setChecked(m_settings.m_dcBlock); + ui->iqImbalance->setChecked(m_settings.m_iqCorrection); + + ui->hwDecim->setCurrentIndex(m_settings.m_log2HardDecim); + ui->swDecim->setCurrentIndex(m_settings.m_log2SoftDecim); + + updateADCRate(); + + ui->lpf->setValue(m_settings.m_lpfBW / 1000); + + ui->lpFIREnable->setChecked(m_settings.m_lpfFIREnable); + ui->lpFIR->setValue(m_settings.m_lpfFIRBW / 1000); + + ui->gain->setValue(m_settings.m_gain); + ui->gainText->setText(tr("%1").arg(m_settings.m_gain)); + + ui->antenna->setCurrentIndex((int) m_settings.m_antennaPath); + + ui->gainMode->setCurrentIndex((int) m_settings.m_gainMode); + ui->lnaGain->setValue(m_settings.m_lnaGain); + ui->tiaGain->setCurrentIndex(m_settings.m_tiaGain - 1); + ui->pgaGain->setValue(m_settings.m_pgaGain); + + if (m_settings.m_gainMode == XTRXInputSettings::GAIN_AUTO) + { + ui->gain->setEnabled(true); + ui->lnaGain->setEnabled(false); + ui->tiaGain->setEnabled(false); + ui->pgaGain->setEnabled(false); + } + else + { + ui->gain->setEnabled(false); + ui->lnaGain->setEnabled(true); + ui->tiaGain->setEnabled(true); + ui->pgaGain->setEnabled(true); + } + + setNCODisplay(); + + ui->ncoEnable->setChecked(m_settings.m_ncoEnable); +} + +void XTRXInputGUI::setNCODisplay() +{ + int ncoHalfRange = (m_settings.m_devSampleRate * (1<<(m_settings.m_log2HardDecim)))/2; + int lowBoundary = std::max(0, (int) m_settings.m_centerFrequency - ncoHalfRange); + ui->ncoFrequency->setValueRange(7, + lowBoundary/1000, + (m_settings.m_centerFrequency + ncoHalfRange)/1000); // frequency dial is in kHz + ui->ncoFrequency->setValue((m_settings.m_centerFrequency + m_settings.m_ncoFrequency)/1000); +} + +void XTRXInputGUI::sendSettings() +{ + if(!m_updateTimer.isActive()) + m_updateTimer.start(100); +} + +void XTRXInputGUI::updateHardware() +{ + if (m_doApplySettings) + { + qDebug() << "XTRXInputGUI::updateHardware"; + XTRXInput::MsgConfigureXTRX* message = XTRXInput::MsgConfigureXTRX::create(m_settings, m_forceSettings); + m_XTRXInput->getInputMessageQueue()->push(message); + m_forceSettings = false; + m_updateTimer.stop(); + } +} + +void XTRXInputGUI::updateStatus() +{ + int state = m_deviceUISet->m_deviceSourceAPI->state(); + + if(m_lastEngineState != state) + { + switch(state) + { + case DSPDeviceSourceEngine::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DSPDeviceSourceEngine::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DSPDeviceSourceEngine::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); + break; + case DSPDeviceSourceEngine::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage()); + break; + default: + break; + } + + m_lastEngineState = state; + } + + if (m_statusCounter < 1) + { + m_statusCounter++; + } + else + { + XTRXInput::MsgGetStreamInfo* message = XTRXInput::MsgGetStreamInfo::create(); + m_XTRXInput->getInputMessageQueue()->push(message); + m_statusCounter = 0; + } + + if (m_deviceStatusCounter < 10) + { + m_deviceStatusCounter++; + } + else + { + if (m_deviceUISet->m_deviceSourceAPI->isBuddyLeader()) + { + XTRXInput::MsgGetDeviceInfo* message = XTRXInput::MsgGetDeviceInfo::create(); + m_XTRXInput->getInputMessageQueue()->push(message); + } + + m_deviceStatusCounter = 0; + } +} + +void XTRXInputGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void XTRXInputGUI::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + XTRXInput::MsgStartStop *message = XTRXInput::MsgStartStop::create(checked); + m_XTRXInput->getInputMessageQueue()->push(message); + } +} + +void XTRXInputGUI::on_record_toggled(bool checked) +{ + if (checked) { + ui->record->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + XTRXInput::MsgFileRecord* message = XTRXInput::MsgFileRecord::create(checked); + m_XTRXInput->getInputMessageQueue()->push(message); +} + +void XTRXInputGUI::on_centerFrequency_changed(quint64 value) +{ + m_settings.m_centerFrequency = value * 1000; + setNCODisplay(); + sendSettings(); +} + +void XTRXInputGUI::on_ncoFrequency_changed(quint64 value) +{ + m_settings.m_ncoFrequency = (int64_t) value - (int64_t) m_settings.m_centerFrequency/1000; + m_settings.m_ncoFrequency *= 1000; + sendSettings(); +} + +void XTRXInputGUI::on_ncoEnable_toggled(bool checked) +{ + m_settings.m_ncoEnable = checked; + sendSettings(); +} + +void XTRXInputGUI::on_ncoReset_clicked(bool checked __attribute__((unused))) +{ + m_settings.m_ncoFrequency = 0; + ui->ncoFrequency->setValue(m_settings.m_centerFrequency/1000); + sendSettings(); +} + +void XTRXInputGUI::on_dcOffset_toggled(bool checked) +{ + m_settings.m_dcBlock = checked; + sendSettings(); +} + +void XTRXInputGUI::on_iqImbalance_toggled(bool checked) +{ + m_settings.m_iqCorrection = checked; + sendSettings(); +} + +void XTRXInputGUI::on_sampleRate_changed(quint64 value) +{ + m_settings.m_devSampleRate = value; + updateADCRate(); + setNCODisplay(); + sendSettings();} + +void XTRXInputGUI::on_hwDecim_currentIndexChanged(int index) +{ + if ((index <0) || (index > 5)) + return; + m_settings.m_log2HardDecim = index; + updateADCRate(); + setNCODisplay(); + sendSettings(); +} + +void XTRXInputGUI::on_swDecim_currentIndexChanged(int index) +{ + if ((index <0) || (index > 6)) + return; + m_settings.m_log2SoftDecim = index; + sendSettings(); +} + +void XTRXInputGUI::on_lpf_changed(quint64 value) +{ + m_settings.m_lpfBW = value * 1000; + sendSettings(); +} + +void XTRXInputGUI::on_lpFIREnable_toggled(bool checked) +{ + m_settings.m_lpfFIREnable = checked; + sendSettings(); +} + +void XTRXInputGUI::on_lpFIR_changed(quint64 value) +{ + m_settings.m_lpfFIRBW = value * 1000; + + if (m_settings.m_lpfFIREnable) { // do not send the update if the FIR is disabled + sendSettings(); + } +} + +void XTRXInputGUI::on_gainMode_currentIndexChanged(int index) +{ + m_settings.m_gainMode = (XTRXInputSettings::GainMode) index; + + if (index == 0) + { + ui->gain->setEnabled(true); + ui->lnaGain->setEnabled(false); + ui->tiaGain->setEnabled(false); + ui->pgaGain->setEnabled(false); + } + else + { + ui->gain->setEnabled(false); + ui->lnaGain->setEnabled(true); + ui->tiaGain->setEnabled(true); + ui->pgaGain->setEnabled(true); + } + + sendSettings(); +} + +void XTRXInputGUI::on_gain_valueChanged(int value) +{ + m_settings.m_gain = value; + ui->gainText->setText(tr("%1").arg(m_settings.m_gain)); + sendSettings(); +} + +void XTRXInputGUI::on_lnaGain_valueChanged(int value) +{ + m_settings.m_lnaGain = value; + ui->lnaGainText->setText(tr("%1").arg(m_settings.m_lnaGain)); + sendSettings(); +} + +void XTRXInputGUI::on_tiaGain_currentIndexChanged(int index) +{ + m_settings.m_tiaGain = index + 1; + sendSettings(); +} + +void XTRXInputGUI::on_pgaGain_valueChanged(int value) +{ + m_settings.m_pgaGain = value; + ui->pgaGainText->setText(tr("%1").arg(m_settings.m_pgaGain)); + sendSettings(); +} + +void XTRXInputGUI::on_antenna_currentIndexChanged(int index) +{ + m_settings.m_antennaPath = (xtrx_antenna_t) index; + sendSettings(); +} + +void XTRXInputGUI::on_extClock_clicked() +{ + m_settings.m_extClock = ui->extClock->getExternalClockActive(); + m_settings.m_extClockFreq = ui->extClock->getExternalClockFrequency(); + qDebug("XTRXInputGUI::on_extClock_clicked: %u Hz %s", m_settings.m_extClockFreq, m_settings.m_extClock ? "on" : "off"); + sendSettings(); +} + +void XTRXInputGUI::on_pwrmode_currentIndexChanged(int index) +{ + m_settings.m_pwrmode = index; + sendSettings(); +} diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.h b/plugins/samplesource/xtrxinput/xtrxinputgui.h new file mode 100644 index 000000000..133e119c9 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTGUI_H_ +#define PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTGUI_H_ + +#include +#include +#include + +#include "util/messagequeue.h" + +#include "xtrxinput.h" + +class DeviceUISet; + +namespace Ui { +class XTRXInputGUI; +} + +class XTRXInputGUI : public QWidget, public PluginInstanceGUI { + Q_OBJECT + +public: + explicit XTRXInputGUI(DeviceUISet *deviceUISet, QWidget* parent = 0); + virtual ~XTRXInputGUI(); + virtual void destroy(); + + void setName(const QString& name); + QString getName() const; + + void resetToDefaults(); + virtual qint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual bool handleMessage(const Message& message); + +private: + Ui::XTRXInputGUI* ui; + + DeviceUISet* m_deviceUISet; + XTRXInput* m_XTRXInput; //!< Same object as above but gives easy access to XTRXInput methods and attributes that are used intensively + XTRXInputSettings m_settings; + QTimer m_updateTimer; + QTimer m_statusTimer; + int m_sampleRate; + quint64 m_deviceCenterFrequency; //!< Center frequency in device + int m_lastEngineState; + bool m_doApplySettings; + bool m_forceSettings; + int m_statusCounter; + int m_deviceStatusCounter; + MessageQueue m_inputMessageQueue; + + void displaySettings(); + void setNCODisplay(); + void sendSettings(); + void updateSampleRateAndFrequency(); + void updateADCRate(); + void blockApplySettings(bool block); + +private slots: + void handleInputMessages(); + void on_startStop_toggled(bool checked); + void on_record_toggled(bool checked); + void on_centerFrequency_changed(quint64 value); + void on_ncoFrequency_changed(quint64 value); + void on_ncoEnable_toggled(bool checked); + void on_ncoReset_clicked(bool checked); + void on_dcOffset_toggled(bool checked); + void on_iqImbalance_toggled(bool checked); + void on_sampleRate_changed(quint64 value); + void on_hwDecim_currentIndexChanged(int index); + void on_swDecim_currentIndexChanged(int index); + void on_lpf_changed(quint64 value); + void on_lpFIREnable_toggled(bool checked); + void on_lpFIR_changed(quint64 value); + void on_gainMode_currentIndexChanged(int index); + void on_gain_valueChanged(int value); + void on_lnaGain_valueChanged(int value); + void on_tiaGain_currentIndexChanged(int index); + void on_pgaGain_valueChanged(int value); + void on_antenna_currentIndexChanged(int index); + void on_extClock_clicked(); + void on_pwrmode_currentIndexChanged(int index); + + void updateHardware(); + void updateStatus(); +}; + +#endif /* PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTGUI_H_ */ diff --git a/plugins/samplesource/xtrxinput/xtrxinputgui.ui b/plugins/samplesource/xtrxinput/xtrxinputgui.ui new file mode 100644 index 000000000..62a08dd65 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputgui.ui @@ -0,0 +1,1215 @@ + + + XTRXInputGUI + + + + 0 + 0 + 423 + 290 + + + + + 0 + 0 + + + + + 350 + 290 + + + + + Sans Serif + 9 + + + + LimeSDR Input + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Toggle record I/Q samples from device + + + + + + + :/record_off.png + :/record_on.png:/record_off.png + + + + + + + + + + + + 54 + 0 + + + + ADC rate before hardware downsampling (k or MS/s) + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 20 + 50 + false + + + + PointingHandCursor + + + Qt::StrongFocus + + + Main center frequency in kHz + + + + + + + 6 + + + 6 + + + + + + + kHz + + + + + + + + 20 + 0 + + + + Channel number + + + #0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 54 + 0 + + + + Baseband I/Q sample rate kS/s + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + 2 + + + + + Enable the TSP NCO + + + NCO + + + + + + + + 22 + 22 + + + + Reset the NCO to zero frequency + + + R + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + 50 + false + + + + PointingHandCursor + + + Center frequency with NCO engaged (kHz) + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Automatic DC offset removal + + + DC + + + + + + + Automatic IQ imbalance correction + + + IQ + + + + + + + External clock dialog + + + + + + + :/clocksource.png:/clocksource.png + + + + + + + + + Qt::Horizontal + + + + + + + 2 + + + + + Hw + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + 16777215 + + + + TSP hardware decimation factor + + + 0 + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + + + + Sw + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 50 + 16777215 + + + + Software decimation factor + + + 0 + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + 50 + false + + + + PointingHandCursor + + + Device to host sample rate + + + + + + + S/s + + + + + + + + 50 + 0 + + + + + + + 4 + + + + 0 - Save Max + + + + + 1 + + + + + 2 + + + + + 3 - Economy + + + + + 4 - Optimal + + + + + 5 + + + + + 6 + + + + + 7 - Perf Max + + + + + + + + + + Qt::Horizontal + + + + + + + 2 + + + 2 + + + + + LP + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + 50 + false + + + + PointingHandCursor + + + Analog lowpass filer bandwidth (kHz) + + + + + + + kHz + + + + + + + Enable or disable TSP digital FIR lowpass filters + + + FIR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + + + + PointingHandCursor + + + Digital FIR lowpass filers bandwidth (kHz) + + + + + + + kHz + + + + + + + + + 2 + + + 2 + + + + + + 54 + 16777215 + + + + Automatic or Manual gain selection + + + + Aut + + + + + Man + + + + + + + + + 24 + 24 + + + + Automatic global gain (dB) + + + 70 + + + 1 + + + 20 + + + + + + + + 18 + 0 + + + + + 18 + 16777215 + + + + Automatic global gain + + + 20 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + + 24 + 24 + + + + Manual LNA gain + + + 1 + + + 30 + + + 1 + + + 15 + + + + + + + + 18 + 0 + + + + + 18 + 16777215 + + + + Manual LNA gain (dB) + + + 15 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 40 + 16777215 + + + + Manual TIA gain (dB) + + + 1 + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + + 24 + 24 + + + + Manual PGA gain + + + 32 + + + 1 + + + 16 + + + + + + + + 18 + 0 + + + + + 18 + 16777215 + + + + Manual PGA gain (dB) + + + 16 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + :/antenna.png + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Antenna select: No: none, Lo: 700:900M, Hi: 2:2.6G, Wi: wideband, T1: Tx1 LB, T2: Tx2 LB + + + + Lo + + + + + Wide + + + + + Hi + + + + + + + + + + Qt::Horizontal + + + + + + + 2 + + + 2 + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Green when stream is reporting data + + + + + + :/stream.png + + + + + + + + 12 + 0 + + + + Red if underruns + + + background:rgb(79,79,79); + + + U + + + Qt::AlignCenter + + + + + + + + 12 + 0 + + + + Red if overruns + + + background:rgb(79,79,79); + + + O + + + Qt::AlignCenter + + + + + + + + 12 + 0 + + + + Red if dropped packets + + + background:rgb(79,79,79); + + + D + + + Qt::AlignCenter + + + + + + + + 90 + 0 + + + + Stream link rate (MB/s) + + + 000.000 MB/s + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 16777215 + 16 + + + + + 8 + + + + FIFO fill status + + + QProgressBar{border: 2px solid rgb(79, 79, 79); text-align: center;} +QToolTip{background-color: white; color: black;} + + + 0 + + + + + + + Qt::Vertical + + + + + + + + 24 + 0 + + + + Board temperature (degrees C) + + + 00C + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ExternalClockButton + QToolButton +
gui/externalclockbutton.h
+
+
+ + + + +
diff --git a/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp b/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp new file mode 100644 index 000000000..a2632ec93 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputplugin.cpp @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "xtrxinputplugin.h" + +#include + +#include +#include + +#include "xtrx_api.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "device/devicesourceapi.h" + +#ifdef SERVER_MODE +#include "xtrxinput.h" +#else +#include "xtrxinputgui.h" +#endif + +const PluginDescriptor XTRXInputPlugin::m_pluginDescriptor = { + QString("XTRX Input"), + QString("0.0.1"), + QString("(c) Sergey Kostanbaev, Fairwaves"), + QString("https://github.com/xtrx-sdr/sdrangel"), + true, + QString("https://github.com/xtrx-sdr/sdrangel") +}; + +const QString XTRXInputPlugin::m_hardwareID = "XTRX"; +const QString XTRXInputPlugin::m_deviceTypeID = XTRX_DEVICE_TYPE_ID; + +XTRXInputPlugin::XTRXInputPlugin(QObject* parent) : + QObject(parent) +{ +} + +const PluginDescriptor& XTRXInputPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void XTRXInputPlugin::initPlugin(PluginAPI* pluginAPI) +{ + pluginAPI->registerSampleSource(m_deviceTypeID, this); +} + +PluginInterface::SamplingDevices XTRXInputPlugin::enumSampleSources() +{ + SamplingDevices result; + xtrx_device_info_t devs[32]; + int res = xtrx_discovery(devs, 32); + int i; + for (i = 0; i < res; i++) { + DeviceXTRXParams XTRXParams; + for (unsigned int j = 0; j < XTRXParams.m_nbRxChannels; j++) + { + qDebug("XTRXInputPlugin::enumSampleSources: device #%d channel %u: %s", i, j, devs[i].uniqname); + QString displayedName(QString("XTRX[%1:%2] %3").arg(i).arg(j).arg(devs[i].uniqname)); + result.append(SamplingDevice(displayedName, + m_hardwareID, + m_deviceTypeID, + QString(devs[i].uniqname), + i, + PluginInterface::SamplingDevice::PhysicalDevice, + true, + XTRXParams.m_nbRxChannels, + j)); + } + } + return result; +} + +#ifdef SERVER_MODE +PluginInstanceGUI* XTRXInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId __attribute((unused)), + QWidget **widget __attribute((unused)), + DeviceUISet *deviceUISet __attribute((unused))) +{ + return 0; +} +#else +PluginInstanceGUI* XTRXInputPlugin::createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + if(sourceId == m_deviceTypeID) + { + XTRXInputGUI* gui = new XTRXInputGUI(deviceUISet); + *widget = gui; + return gui; + } + else + { + return 0; + } +} +#endif + +bool XTRXInputPlugin::findSerial(const char *lmsInfoStr, std::string& serial) +{ + std::regex serial_reg("serial=([0-9,A-F]+)"); + std::string input(lmsInfoStr); + std::smatch result; + std::regex_search(input, result, serial_reg); + + if (result[1].str().length()>0) + { + serial = result[1].str(); + return true; + } + else + { + return false; + } +} + +DeviceSampleSource *XTRXInputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI) +{ + if (sourceId == m_deviceTypeID) + { + XTRXInput* input = new XTRXInput(deviceAPI); + return input; + } + else + { + return 0; + } +} diff --git a/plugins/samplesource/xtrxinput/xtrxinputplugin.h b/plugins/samplesource/xtrxinput/xtrxinputplugin.h new file mode 100644 index 000000000..493e7ae16 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputplugin.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTPLUGIN_H_ +#define PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTPLUGIN_H_ + +#include +#include "plugin/plugininterface.h" + +class PluginAPI; + +#define XTRX_DEVICE_TYPE_ID "sdrangel.samplesource.xtrx" + +class XTRXInputPlugin : public QObject, public PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID XTRX_DEVICE_TYPE_ID) + +public: + explicit XTRXInputPlugin(QObject* parent = 0); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual SamplingDevices enumSampleSources(); + virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet); + virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI); + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; + static bool findSerial(const char *lmsInfoStr, std::string& serial); +}; + + +#endif /* PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTPLUGIN_H_ */ diff --git a/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp b/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp new file mode 100644 index 000000000..e2970da6a --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputsettings.cpp @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/simpleserializer.h" +#include "xtrxinputsettings.h" + +XTRXInputSettings::XTRXInputSettings() +{ + resetToDefaults(); +} + +void XTRXInputSettings::resetToDefaults() +{ + m_centerFrequency = 435000*1000; + m_devSampleRate = 5e6; + m_log2HardDecim = 1; + m_dcBlock = false; + m_iqCorrection = false; + m_log2SoftDecim = 0; + m_lpfBW = 4.5e6f; + m_lpfFIREnable = false; + m_lpfFIRBW = 2.5e6f; + m_gain = 50; + m_ncoEnable = false; + m_ncoFrequency = 0; + m_antennaPath = XTRX_RX_L; + m_gainMode = GAIN_AUTO; + m_lnaGain = 15; + m_tiaGain = 2; + m_pgaGain = 16; + m_extClock = false; + m_extClockFreq = 0; // Auto + m_pwrmode = 1; +} + +QByteArray XTRXInputSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeDouble(1, m_devSampleRate); + s.writeU32(2, m_log2HardDecim); + s.writeBool(3, m_dcBlock); + s.writeBool(4, m_iqCorrection); + s.writeU32(5, m_log2SoftDecim); + s.writeFloat(7, m_lpfBW); + s.writeBool(8, m_lpfFIREnable); + s.writeFloat(9, m_lpfFIRBW); + s.writeU32(10, m_gain); + s.writeBool(11, m_ncoEnable); + s.writeS32(12, m_ncoFrequency); + s.writeS32(13, (int) m_antennaPath); + s.writeS32(14, (int) m_gainMode); + s.writeU32(15, m_lnaGain); + s.writeU32(16, m_tiaGain); + s.writeU32(17, m_pgaGain); + s.writeBool(18, m_extClock); + s.writeU32(19, m_extClockFreq); + s.writeU32(20, m_pwrmode); + + return s.final(); +} + +bool XTRXInputSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + int intval; + + d.readDouble(1, &m_devSampleRate, 5e6); + d.readU32(2, &m_log2HardDecim, 2); + d.readBool(3, &m_dcBlock, false); + d.readBool(4, &m_iqCorrection, false); + d.readU32(5, &m_log2SoftDecim, 0); + d.readFloat(7, &m_lpfBW, 1.5e6); + d.readBool(8, &m_lpfFIREnable, false); + d.readFloat(9, &m_lpfFIRBW, 1.5e6); + d.readU32(10, &m_gain, 50); + d.readBool(11, &m_ncoEnable, false); + d.readS32(12, &m_ncoFrequency, 0); + d.readS32(13, &intval, 0); + m_antennaPath = (xtrx_antenna_t) intval; + d.readS32(14, &intval, 0); + m_gainMode = (GainMode) intval; + d.readU32(15, &m_lnaGain, 15); + d.readU32(16, &m_tiaGain, 2); + d.readU32(17, &m_pgaGain, 16); + d.readBool(18, &m_extClock, false); + d.readU32(19, &m_extClockFreq, 0); + d.readU32(20, &m_pwrmode, 2); + + return true; + } + else + { + resetToDefaults(); + return false; + } + +} diff --git a/plugins/samplesource/xtrxinput/xtrxinputsettings.h b/plugins/samplesource/xtrxinput/xtrxinputsettings.h new file mode 100644 index 000000000..9d3cc304d --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputsettings.h @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTSETTINGS_H_ +#define PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTSETTINGS_H_ + +#include +#include + +#include "xtrx_api.h" + +/** + * These are the settings individual to each hardware channel or software Rx chain + * Plus the settings to be saved in the presets + */ +struct XTRXInputSettings +{ + typedef enum { + FC_POS_INFRA = 0, + FC_POS_SUPRA, + FC_POS_CENTER + } fcPos_t; + + typedef enum { + GAIN_AUTO, + GAIN_MANUAL + } GainMode; + + // global settings to be saved + uint64_t m_centerFrequency; + double m_devSampleRate; + uint32_t m_log2HardDecim; + // channel settings + bool m_dcBlock; + bool m_iqCorrection; + uint32_t m_log2SoftDecim; + float m_lpfBW; //!< LMS amalog lowpass filter bandwidth (Hz) + bool m_lpfFIREnable; //!< Enable LMS digital lowpass FIR filters + float m_lpfFIRBW; //!< LMS digital lowpass FIR filters bandwidth (Hz) + uint32_t m_gain; //!< Optimally distributed gain (dB) + bool m_ncoEnable; //!< Enable TSP NCO and mixing + int m_ncoFrequency; //!< Actual NCO frequency (the resulting frequency with mixing is displayed) + xtrx_antenna_t m_antennaPath; + GainMode m_gainMode; //!< Gain mode: auto or manual + uint32_t m_lnaGain; //!< Manual LAN gain + uint32_t m_tiaGain; //!< Manual TIA gain + uint32_t m_pgaGain; //!< Manual PGA gain + bool m_extClock; //!< True if external clock source + uint32_t m_extClockFreq; //!< Frequency (Hz) of external clock source + uint32_t m_pwrmode; + + XTRXInputSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif /* PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTSETTINGS_H_ */ diff --git a/plugins/samplesource/xtrxinput/xtrxinputthread.cpp b/plugins/samplesource/xtrxinput/xtrxinputthread.cpp new file mode 100644 index 000000000..d667606eb --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputthread.cpp @@ -0,0 +1,229 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "xtrxinputsettings.h" +#include "xtrxinputthread.h" + +XTRXInputThread::XTRXInputThread(DeviceXTRXShared* shared, + SampleSinkFifo* sampleFifo, + QObject* parent) : + QThread(parent), + m_running(false), + m_convertBuffer(XTRX_BLOCKSIZE), + m_sampleFifo(sampleFifo), + m_log2Decim(0), + m_fcPos(XTRXInputSettings::FC_POS_CENTER), + m_shared(shared) +{ +} + +XTRXInputThread::~XTRXInputThread() +{ + stopWork(); +} + +void XTRXInputThread::startWork() +{ + if (m_running) return; // return if running already + + xtrx_run_params params; + xtrx_run_params_init(¶ms); + + params.dir = XTRX_RX; + params.rx.chs = XTRX_CH_AB; + params.rx.wfmt = XTRX_WF_16; + params.rx.hfmt = XTRX_IQ_INT16; + params.rx.flags |= XTRX_RSP_SISO_MODE; + params.rx_stream_start = 2*8192; + + // TODO: replace this + if (m_shared->m_channel == XTRX_CH_B) + params.rx.flags |= XTRX_RSP_SWAP_AB; + + int res = xtrx_run_ex(m_shared->m_deviceParams->getDevice(), + ¶ms); + + if (res != 0) { + qCritical("XTRXInputThread::startWork: could not start stream err:%d", res); + } else { + usleep(50000); + qDebug("XTRXInputThread::startWork: stream started"); + } + + m_startWaitMutex.lock(); + start(); + while(!m_running) + m_startWaiter.wait(&m_startWaitMutex, 100); + m_startWaitMutex.unlock(); +} + +void XTRXInputThread::stopWork() +{ + if (!m_running) return; // return if not running + + m_running = false; + + int res = xtrx_stop(m_shared->m_deviceParams->getDevice(), XTRX_RX); + wait(); + + + if (res != 0) { + qCritical("XTRXInputThread::stopWork: could not stop stream"); + } else { + usleep(50000); + qDebug("XTRXInputThread::stopWork: stream stopped"); + } +} + +void XTRXInputThread::setLog2Decimation(unsigned int log2_decim) +{ + m_log2Decim = log2_decim; +} + +void XTRXInputThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +void XTRXInputThread::run() +{ + int res; + + m_running = true; + m_startWaiter.wakeAll(); + + void* buffers[1] = { m_buf }; + xtrx_recv_ex_info_t nfo; + nfo.samples = XTRX_BLOCKSIZE; + nfo.buffer_count = 1; + nfo.buffers = (void* const*)buffers; + nfo.flags = RCVEX_DONT_INSER_ZEROS | RCVEX_DROP_OLD_ON_OVERFLOW; + + + while (m_running) + { + //if ((res = LMS_RecvStream(m_stream, (void *) m_buf, XTRX_BLOCKSIZE, &metadata, 1000)) < 0) + res = xtrx_recv_sync_ex(m_shared->m_deviceParams->getDevice(), + &nfo); + + if (res < 0) + { + qCritical("XTRXInputThread::run read error: %d", res); + break; + } + + callback(m_buf, 2 * nfo.out_samples); + } + + m_running = false; +} + +// Decimate according to specified log2 (ex: log2=4 => decim=16) +void XTRXInputThread::callback(const qint16* buf, qint32 len) +{ + SampleVector::iterator it = m_convertBuffer.begin(); + + if (m_log2Decim == 0) + { + m_decimators.decimate1(&it, buf, len); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Decim) + { + case 1: + m_decimators.decimate2_inf(&it, buf, len); + break; + case 2: + m_decimators.decimate4_inf(&it, buf, len); + break; + case 3: + m_decimators.decimate8_inf(&it, buf, len); + break; + case 4: + m_decimators.decimate16_inf(&it, buf, len); + break; + case 5: + m_decimators.decimate32_inf(&it, buf, len); + break; + case 6: + m_decimators.decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Decim) + { + case 1: + m_decimators.decimate2_sup(&it, buf, len); + break; + case 2: + m_decimators.decimate4_sup(&it, buf, len); + break; + case 3: + m_decimators.decimate8_sup(&it, buf, len); + break; + case 4: + m_decimators.decimate16_sup(&it, buf, len); + break; + case 5: + m_decimators.decimate32_sup(&it, buf, len); + break; + case 6: + m_decimators.decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Decim) + { + case 1: + m_decimators.decimate2_cen(&it, buf, len); + break; + case 2: + m_decimators.decimate4_cen(&it, buf, len); + break; + case 3: + m_decimators.decimate8_cen(&it, buf, len); + break; + case 4: + m_decimators.decimate16_cen(&it, buf, len); + break; + case 5: + m_decimators.decimate32_cen(&it, buf, len); + break; + case 6: + m_decimators.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + m_sampleFifo->write(m_convertBuffer.begin(), it); +} diff --git a/plugins/samplesource/xtrxinput/xtrxinputthread.h b/plugins/samplesource/xtrxinput/xtrxinputthread.h new file mode 100644 index 000000000..e2ae68ab1 --- /dev/null +++ b/plugins/samplesource/xtrxinput/xtrxinputthread.h @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. // +// // +// 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 as version 3 of the License, or // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTTHREAD_H_ +#define PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTTHREAD_H_ + +#include +#include +#include + +#include "xtrx_api.h" + +#include "dsp/samplesinkfifo.h" +#include "dsp/decimators.h" +#include "xtrx/devicextrxshared.h" + +#define XTRX_BLOCKSIZE (1<<13) //complex samples per buffer + +class XTRXInputThread : public QThread, public DeviceXTRXShared::ThreadInterface +{ + Q_OBJECT + +public: + XTRXInputThread(DeviceXTRXShared* shared, SampleSinkFifo* sampleFifo, QObject* parent = 0); + ~XTRXInputThread(); + + virtual void startWork(); + virtual void stopWork(); + virtual void setDeviceSampleRate(int sampleRate __attribute__((unused))) {} + virtual bool isRunning() { return m_running; } + void setLog2Decimation(unsigned int log2_decim); + void setFcPos(int fcPos); + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + + qint16 m_buf[2*XTRX_BLOCKSIZE]; //must hold I+Q values of each sample hence 2xcomplex size + SampleVector m_convertBuffer; + SampleSinkFifo* m_sampleFifo; + + unsigned int m_log2Decim; // soft decimation + int m_fcPos; + + Decimators m_decimators; + + DeviceXTRXShared* m_shared; + + void run(); + void callback(const qint16* buf, qint32 len); +}; + + + +#endif /* PLUGINS_SAMPLESOURCE_XTRXINPUT_XTRXINPUTTHREAD_H_ */ diff --git a/pluginssrv/samplesource/CMakeLists.txt b/pluginssrv/samplesource/CMakeLists.txt index 5bcb15917..a4204abbd 100644 --- a/pluginssrv/samplesource/CMakeLists.txt +++ b/pluginssrv/samplesource/CMakeLists.txt @@ -54,6 +54,11 @@ if(LIBUSB_FOUND AND RX_SAMPLE_24BIT AND LIBPERSEUS_FOUND) add_subdirectory(perseus) endif(LIBUSB_FOUND AND RX_SAMPLE_24BIT AND LIBPERSEUS_FOUND) +find_package(LibXTRX) +if(LIBXTRX_FOUND) + add_subdirectory(xtrxinput) +endif(LIBXTRX_FOUND) + find_package(LibIIO) if(LIBUSB_FOUND AND LIBIIO_FOUND) add_subdirectory(plutosdrinput) @@ -97,6 +102,7 @@ if (BUILD_DEBIAN) add_subdirectory(rtlsdr) add_subdirectory(sdrdaemonsource) add_subdirectory(sdrplay) + add_subdirectory(xtrxinput) endif (BUILD_DEBIAN) add_subdirectory(filesource) diff --git a/pluginssrv/samplesource/xtrxinput/CMakeLists.txt b/pluginssrv/samplesource/xtrxinput/CMakeLists.txt new file mode 100644 index 000000000..57ad4652b --- /dev/null +++ b/pluginssrv/samplesource/xtrxinput/CMakeLists.txt @@ -0,0 +1,46 @@ +project(xtrxinput) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set(PLUGIN_PREFIX "../../../plugins/samplesource/xtrxinput") + +set(xtrxinput_SOURCES + ${PLUGIN_PREFIX}/xtrxinput.cpp + ${PLUGIN_PREFIX}/xtrxinputplugin.cpp + ${PLUGIN_PREFIX}/xtrxinputsettings.cpp + ${PLUGIN_PREFIX}/xtrxinputthread.cpp +) + +set(xtrxinput_HEADERS + ${PLUGIN_PREFIX}/xtrxinput.h + ${PLUGIN_PREFIX}/xtrxinputplugin.h + ${PLUGIN_PREFIX}/xtrxinputsettings.h + ${PLUGIN_PREFIX}/xtrxinputthread.h +) + +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/devices + ${LIBXTRX_INCLUDE_DIRS} +) + +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +add_library(inputxtrxsrv SHARED + ${xtrxinput_SOURCES} + ${xtrxinput_HEADERS_MOC} +) + + +target_link_libraries(inputxtrxsrv + ${QT_LIBRARIES} + ${LIBXTRX_LIBRARIES} + sdrbase + xtrxdevice +) + +qt5_use_modules(inputxtrxsrv Core) + +install(TARGETS inputxtrxsrv DESTINATION lib/pluginssrv/samplesource) \ No newline at end of file