2017-09-18 17:47:36 -04:00
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
# include <QDebug>
# include "dsp/dspcommands.h"
# include "device/devicesourceapi.h"
# include "device/devicesinkapi.h"
# include "plutosdr/deviceplutosdrparams.h"
# include "plutosdr/deviceplutosdrbox.h"
# include "plutosdroutput.h"
# include "plutosdroutputthread.h"
# define PLUTOSDR_BLOCKSIZE_SAMPLES (32*1024) //complex samples per buffer (must be multiple of 64)
MESSAGE_CLASS_DEFINITION ( PlutoSDROutput : : MsgConfigurePlutoSDR , Message )
PlutoSDROutput : : PlutoSDROutput ( DeviceSinkAPI * deviceAPI ) :
m_deviceAPI ( deviceAPI ) ,
m_deviceDescription ( " PlutoSDROutput " ) ,
2017-09-19 18:21:06 -04:00
m_settings ( ) ,
2017-09-18 17:47:36 -04:00
m_running ( false ) ,
m_plutoTxBuffer ( 0 ) ,
m_plutoSDROutputThread ( 0 )
{
suspendBuddies ( ) ;
openDevice ( ) ;
resumeBuddies ( ) ;
}
PlutoSDROutput : : ~ PlutoSDROutput ( )
{
suspendBuddies ( ) ;
closeDevice ( ) ;
resumeBuddies ( ) ;
}
void PlutoSDROutput : : destroy ( )
{
delete this ;
}
bool PlutoSDROutput : : start ( )
{
if ( ! m_deviceShared . m_deviceParams - > getBox ( ) ) {
return false ;
}
if ( m_running ) stop ( ) ;
applySettings ( m_settings , true ) ;
// start / stop streaming is done in the thread.
if ( ( m_plutoSDROutputThread = new PlutoSDROutputThread ( PLUTOSDR_BLOCKSIZE_SAMPLES , m_deviceShared . m_deviceParams - > getBox ( ) , & m_sampleSourceFifo ) ) = = 0 )
{
qFatal ( " PlutoSDROutput::start: cannot create thread " ) ;
stop ( ) ;
return false ;
}
else
{
qDebug ( " PlutoSDROutput::start: thread created " ) ;
}
m_plutoSDROutputThread - > setLog2Interpolation ( m_settings . m_log2Interp ) ;
m_plutoSDROutputThread - > startWork ( ) ;
m_deviceShared . m_thread = m_plutoSDROutputThread ;
m_running = true ;
return true ;
}
void PlutoSDROutput : : stop ( )
{
if ( m_plutoSDROutputThread ! = 0 )
{
m_plutoSDROutputThread - > stopWork ( ) ;
delete m_plutoSDROutputThread ;
m_plutoSDROutputThread = 0 ;
}
m_deviceShared . m_thread = 0 ;
m_running = false ;
}
const QString & PlutoSDROutput : : getDeviceDescription ( ) const
{
return m_deviceDescription ;
}
int PlutoSDROutput : : getSampleRate ( ) const
{
return ( m_settings . m_devSampleRate / ( 1 < < m_settings . m_log2Interp ) ) ;
}
quint64 PlutoSDROutput : : getCenterFrequency ( ) const
{
return m_settings . m_centerFrequency ;
}
bool PlutoSDROutput : : handleMessage ( const Message & message )
{
if ( MsgConfigurePlutoSDR : : match ( message ) )
{
MsgConfigurePlutoSDR & conf = ( MsgConfigurePlutoSDR & ) message ;
qDebug ( ) < < " PlutoSDROutput::handleMessage: MsgConfigurePlutoSDR " ;
if ( ! applySettings ( conf . getSettings ( ) , conf . getForce ( ) ) )
{
qDebug ( " PlutoSDROutput::handleMessage config error " ) ;
}
return true ;
}
2017-09-20 12:25:54 -04:00
else if ( DevicePlutoSDRShared : : MsgCrossReportToBuddy : : match ( message ) ) // message from buddy
{
DevicePlutoSDRShared : : MsgCrossReportToBuddy & conf = ( DevicePlutoSDRShared : : MsgCrossReportToBuddy & ) message ;
m_settings . m_devSampleRate = conf . getDevSampleRate ( ) ;
m_settings . m_lpfFIRlog2Interp = conf . getLpfFiRlog2IntDec ( ) ;
m_settings . m_lpfFIRBW = conf . getLpfFirbw ( ) ;
m_settings . m_LOppmTenths = conf . getLoPPMTenths ( ) ;
PlutoSDROutputSettings newSettings = m_settings ;
newSettings . m_lpfFIREnable = conf . isLpfFirEnable ( ) ;
applySettings ( newSettings ) ;
2017-09-20 14:52:41 -04:00
return true ;
2017-09-20 12:25:54 -04:00
}
2017-09-18 17:47:36 -04:00
else
{
return false ;
}
}
bool PlutoSDROutput : : openDevice ( )
{
2017-09-19 18:21:06 -04:00
//m_sampleSourceFifo.resize(m_settings.m_devSampleRate/(1<<(m_settings.m_log2Interp <= 4 ? m_settings.m_log2Interp : 4)));
m_sampleSourceFifo . resize ( 32 * PLUTOSDR_BLOCKSIZE_SAMPLES ) ;
2017-09-18 17:47:36 -04:00
// look for Rx buddy and get reference to common parameters
if ( m_deviceAPI - > getSourceBuddies ( ) . size ( ) > 0 ) // then sink
{
qDebug ( " PlutoSDROutput::openDevice: look at Rx buddy " ) ;
DeviceSourceAPI * sourceBuddy = m_deviceAPI - > getSourceBuddies ( ) [ 0 ] ;
m_deviceShared = * ( ( DevicePlutoSDRShared * ) sourceBuddy - > getBuddySharedPtr ( ) ) ; // copy parameters
if ( m_deviceShared . m_deviceParams = = 0 )
{
qCritical ( " PlutoSDROutput::openDevice: cannot get device parameters from Rx buddy " ) ;
return false ; // the device params should have been created by the buddy
}
else
{
qDebug ( " PlutoSDROutput::openDevice: getting device parameters from Rx buddy " ) ;
}
}
// There is no buddy then create the first PlutoSDR common parameters
// open the device this will also populate common fields
else
{
qDebug ( " PlutoSDROutput::openDevice: open device here " ) ;
m_deviceShared . m_deviceParams = new DevicePlutoSDRParams ( ) ;
char serial [ 256 ] ;
strcpy ( serial , qPrintable ( m_deviceAPI - > getSampleSinkSerial ( ) ) ) ;
m_deviceShared . m_deviceParams - > open ( serial ) ;
}
m_deviceAPI - > setBuddySharedPtr ( & m_deviceShared ) ; // propagate common parameters to API
// acquire the channel
DevicePlutoSDRBox * plutoBox = m_deviceShared . m_deviceParams - > getBox ( ) ;
plutoBox - > openTx ( ) ;
m_plutoTxBuffer = plutoBox - > createTxBuffer ( PLUTOSDR_BLOCKSIZE_SAMPLES * 2 , false ) ; // PlutoSDR buffer size is counted in number of I or Q samples not the combination
return true ;
}
void PlutoSDROutput : : closeDevice ( )
{
if ( m_deviceShared . m_deviceParams - > getBox ( ) = = 0 ) { // was never open
return ;
}
if ( m_deviceAPI - > getSourceBuddies ( ) . size ( ) = = 0 )
{
m_deviceShared . m_deviceParams - > close ( ) ;
delete m_deviceShared . m_deviceParams ;
m_deviceShared . m_deviceParams = 0 ;
}
}
void PlutoSDROutput : : suspendBuddies ( )
{
// suspend Rx buddy's thread
for ( unsigned int i = 0 ; i < m_deviceAPI - > getSourceBuddies ( ) . size ( ) ; i + + )
{
DeviceSourceAPI * buddy = m_deviceAPI - > getSourceBuddies ( ) [ i ] ;
DevicePlutoSDRShared * buddyShared = ( DevicePlutoSDRShared * ) buddy - > getBuddySharedPtr ( ) ;
if ( buddyShared - > m_thread ) {
buddyShared - > m_thread - > stopWork ( ) ;
}
}
}
void PlutoSDROutput : : resumeBuddies ( )
{
// resume Rx buddy's thread
for ( unsigned int i = 0 ; i < m_deviceAPI - > getSourceBuddies ( ) . size ( ) ; i + + )
{
DeviceSourceAPI * buddy = m_deviceAPI - > getSourceBuddies ( ) [ i ] ;
DevicePlutoSDRShared * buddyShared = ( DevicePlutoSDRShared * ) buddy - > getBuddySharedPtr ( ) ;
if ( buddyShared - > m_thread ) {
buddyShared - > m_thread - > startWork ( ) ;
}
}
}
bool PlutoSDROutput : : applySettings ( const PlutoSDROutputSettings & settings , bool force )
{
bool forwardChangeOwnDSP = false ;
bool forwardChangeOtherDSP = false ;
bool suspendOwnThread = false ;
bool ownThreadWasRunning = false ;
bool suspendAllOtherThreads = false ; // All others means Rx in fact
DevicePlutoSDRBox * plutoBox = m_deviceShared . m_deviceParams - > getBox ( ) ;
// determine if buddies threads or own thread need to be suspended
// changes affecting all buddies can occur if
// - device to host sample rate is changed
// - FIR filter is enabled or disabled
// - FIR filter is changed
// - LO correction is changed
if ( ( m_settings . m_devSampleRate ! = settings . m_devSampleRate ) | |
( m_settings . m_lpfFIREnable ! = settings . m_lpfFIREnable ) | |
( m_settings . m_lpfFIRlog2Interp ! = settings . m_lpfFIRlog2Interp ) | |
( settings . m_lpfFIRBW ! = m_settings . m_lpfFIRBW ) | |
( settings . m_lpfFIRGain ! = m_settings . m_lpfFIRGain ) | |
( m_settings . m_LOppmTenths ! = settings . m_LOppmTenths ) | | force )
{
suspendAllOtherThreads = true ;
suspendOwnThread = true ;
}
else
{
suspendOwnThread = true ;
}
if ( suspendAllOtherThreads )
{
const std : : vector < DeviceSourceAPI * > & sourceBuddies = m_deviceAPI - > getSourceBuddies ( ) ;
std : : vector < DeviceSourceAPI * > : : const_iterator itSink = sourceBuddies . begin ( ) ;
for ( ; itSink ! = sourceBuddies . end ( ) ; + + itSink )
{
DevicePlutoSDRShared * buddySharedPtr = ( DevicePlutoSDRShared * ) ( * itSink ) - > getBuddySharedPtr ( ) ;
if ( buddySharedPtr - > m_thread ) {
buddySharedPtr - > m_thread - > stopWork ( ) ;
buddySharedPtr - > m_threadWasRunning = true ;
}
else
{
buddySharedPtr - > m_threadWasRunning = false ;
}
}
}
if ( suspendOwnThread )
{
if ( m_plutoSDROutputThread & & m_plutoSDROutputThread - > isRunning ( ) )
{
m_plutoSDROutputThread - > stopWork ( ) ;
ownThreadWasRunning = true ;
}
}
// apply settings
// Change affecting device sample rate chain and other buddies
if ( ( m_settings . m_devSampleRate ! = settings . m_devSampleRate ) | |
( m_settings . m_lpfFIREnable ! = settings . m_lpfFIREnable ) | |
( m_settings . m_lpfFIRlog2Interp ! = settings . m_lpfFIRlog2Interp ) | |
( settings . m_lpfFIRBW ! = m_settings . m_lpfFIRBW ) | |
( settings . m_lpfFIRGain ! = m_settings . m_lpfFIRGain ) | | force )
{
plutoBox - > setFIR ( settings . m_devSampleRate , settings . m_lpfFIRlog2Interp , DevicePlutoSDRBox : : USE_TX , settings . m_lpfFIRBW , settings . m_lpfFIRGain ) ;
plutoBox - > setFIREnable ( settings . m_lpfFIREnable ) ; // eventually enable/disable FIR
plutoBox - > setSampleRate ( settings . m_devSampleRate ) ; // and set end point sample rate
plutoBox - > getTxSampleRates ( m_deviceSampleRates ) ; // pick up possible new rates
qDebug ( ) < < " PlutoSDRInput::applySettings: BBPLL(Hz): " < < m_deviceSampleRates . m_bbRateHz
< < " DAC: " < < m_deviceSampleRates . m_addaConnvRate
< < " <-HB3- " < < m_deviceSampleRates . m_hb3Rate
< < " <-HB2- " < < m_deviceSampleRates . m_hb2Rate
< < " <-HB1- " < < m_deviceSampleRates . m_hb1Rate
< < " <-FIR- " < < m_deviceSampleRates . m_firRate ;
forwardChangeOtherDSP = true ;
forwardChangeOwnDSP = ( m_settings . m_devSampleRate ! = settings . m_devSampleRate ) ;
}
if ( ( m_settings . m_log2Interp ! = settings . m_log2Interp ) | | force )
{
2017-09-19 18:21:06 -04:00
m_sampleSourceFifo . resize ( ( 32 * PLUTOSDR_BLOCKSIZE_SAMPLES ) / ( 1 < < settings . m_log2Interp ) ) ;
2017-09-18 17:47:36 -04:00
if ( m_plutoSDROutputThread ! = 0 )
{
m_plutoSDROutputThread - > setLog2Interpolation ( settings . m_log2Interp ) ;
2017-09-19 18:21:06 -04:00
qDebug ( ) < < " PlutoSDROutput::applySettings: set soft interpolation in thread to " < < ( 1 < < settings . m_log2Interp ) ;
2017-09-18 17:47:36 -04:00
}
forwardChangeOwnDSP = true ;
}
if ( ( m_settings . m_LOppmTenths ! = settings . m_LOppmTenths ) | | force )
{
plutoBox - > setLOPPMTenths ( settings . m_LOppmTenths ) ;
2017-09-19 02:33:26 -04:00
forwardChangeOtherDSP = true ;
2017-09-18 17:47:36 -04:00
}
std : : vector < std : : string > params ;
bool paramsToSet = false ;
2017-09-25 13:11:13 -04:00
if ( force | | ( m_settings . m_centerFrequency ! = settings . m_centerFrequency )
| | ( m_settings . m_transverterMode ! = settings . m_transverterMode )
| | ( m_settings . m_transverterDeltaFrequency ! = settings . m_transverterDeltaFrequency ) )
2017-09-18 17:47:36 -04:00
{
2017-09-25 13:11:13 -04:00
qint64 deviceCenterFrequency = settings . m_centerFrequency ;
deviceCenterFrequency - = settings . m_transverterMode ? settings . m_transverterDeltaFrequency : 0 ;
deviceCenterFrequency = deviceCenterFrequency < 0 ? 0 : deviceCenterFrequency ;
params . push_back ( QString ( tr ( " out_altvoltage1_TX_LO_frequency=%1 " ) . arg ( deviceCenterFrequency ) ) . toStdString ( ) ) ;
2017-09-18 17:47:36 -04:00
paramsToSet = true ;
forwardChangeOwnDSP = true ;
2017-09-25 13:11:13 -04:00
qDebug ( ) < < " PlutoSDROutput::applySettings: center freq: " < < settings . m_centerFrequency < < " Hz "
< < " device center freq: " < < deviceCenterFrequency < < " Hz " ;
2017-09-18 17:47:36 -04:00
}
if ( ( m_settings . m_lpfBW ! = settings . m_lpfBW ) | | force )
{
2017-09-19 02:33:26 -04:00
params . push_back ( QString ( tr ( " out_voltage_rf_bandwidth=%1 " ) . arg ( settings . m_lpfBW ) ) . toStdString ( ) ) ;
2017-09-18 17:47:36 -04:00
paramsToSet = true ;
}
if ( ( m_settings . m_antennaPath ! = settings . m_antennaPath ) | | force )
{
QString rfPortStr ;
2017-09-19 02:33:26 -04:00
PlutoSDROutputSettings : : translateRFPath ( settings . m_antennaPath , rfPortStr ) ;
params . push_back ( QString ( tr ( " out_voltage0_rf_port_select=%1 " ) . arg ( rfPortStr ) ) . toStdString ( ) ) ;
2017-09-18 17:47:36 -04:00
paramsToSet = true ;
}
2017-09-19 02:33:26 -04:00
if ( ( m_settings . m_att ! = settings . m_att ) | | force )
2017-09-18 17:47:36 -04:00
{
2017-09-19 02:33:26 -04:00
float attF = settings . m_att * 0.25f ;
params . push_back ( QString ( tr ( " out_voltage0_hardwaregain=%1 " ) . arg ( attF ) ) . toStdString ( ) ) ;
2017-09-18 17:47:36 -04:00
paramsToSet = true ;
}
if ( paramsToSet )
{
plutoBox - > set_params ( DevicePlutoSDRBox : : DEVICE_PHY , params ) ;
}
m_settings = settings ;
if ( suspendAllOtherThreads )
{
2017-09-19 02:33:26 -04:00
const std : : vector < DeviceSourceAPI * > & sourceBuddies = m_deviceAPI - > getSourceBuddies ( ) ;
std : : vector < DeviceSourceAPI * > : : const_iterator itSource = sourceBuddies . begin ( ) ;
2017-09-18 17:47:36 -04:00
2017-09-19 02:33:26 -04:00
for ( ; itSource ! = sourceBuddies . end ( ) ; + + itSource )
2017-09-18 17:47:36 -04:00
{
2017-09-19 02:33:26 -04:00
DevicePlutoSDRShared * buddySharedPtr = ( DevicePlutoSDRShared * ) ( * itSource ) - > getBuddySharedPtr ( ) ;
2017-09-18 17:47:36 -04:00
if ( buddySharedPtr - > m_threadWasRunning ) {
buddySharedPtr - > m_thread - > startWork ( ) ;
}
}
}
if ( suspendOwnThread )
{
if ( ownThreadWasRunning ) {
2017-09-19 02:33:26 -04:00
m_plutoSDROutputThread - > startWork ( ) ;
2017-09-18 17:47:36 -04:00
}
}
2017-09-19 02:33:26 -04:00
// TODO: forward changes to other (Rx) DSP
2017-09-18 17:47:36 -04:00
if ( forwardChangeOtherDSP )
{
2017-09-19 02:33:26 -04:00
qDebug ( " PlutoSDROutput::applySettings: forwardChangeOtherDSP " ) ;
2017-09-20 12:25:54 -04:00
const std : : vector < DeviceSourceAPI * > & sourceBuddies = m_deviceAPI - > getSourceBuddies ( ) ;
std : : vector < DeviceSourceAPI * > : : const_iterator itSource = sourceBuddies . begin ( ) ;
for ( ; itSource ! = sourceBuddies . end ( ) ; + + itSource )
{
DevicePlutoSDRShared : : MsgCrossReportToBuddy * msg = DevicePlutoSDRShared : : MsgCrossReportToBuddy : : create (
settings . m_devSampleRate ,
settings . m_lpfFIREnable ,
settings . m_lpfFIRlog2Interp ,
settings . m_lpfFIRBW ,
settings . m_LOppmTenths ) ;
if ( ( * itSource ) - > getSampleSourceGUIMessageQueue ( ) )
{
DevicePlutoSDRShared : : MsgCrossReportToBuddy * msgToGUI = new DevicePlutoSDRShared : : MsgCrossReportToBuddy ( * msg ) ;
( * itSource ) - > getSampleSourceGUIMessageQueue ( ) - > push ( msgToGUI ) ;
}
( * itSource ) - > getSampleSourceInputMessageQueue ( ) - > push ( msg ) ;
}
2017-09-18 17:47:36 -04:00
}
if ( forwardChangeOwnDSP )
{
2017-09-19 02:33:26 -04:00
qDebug ( " PlutoSDROutput::applySettings: forward change to self " ) ;
2017-09-18 17:47:36 -04:00
2017-09-19 02:33:26 -04:00
int sampleRate = m_settings . m_devSampleRate / ( 1 < < m_settings . m_log2Interp ) ;
2017-09-18 17:47:36 -04:00
DSPSignalNotification * notif = new DSPSignalNotification ( sampleRate , m_settings . m_centerFrequency ) ;
m_deviceAPI - > getDeviceEngineInputMessageQueue ( ) - > push ( notif ) ;
}
return true ;
}
2017-09-19 02:33:26 -04:00
void PlutoSDROutput : : getRSSI ( std : : string & rssiStr )
2017-09-18 17:47:36 -04:00
{
DevicePlutoSDRBox * plutoBox = m_deviceShared . m_deviceParams - > getBox ( ) ;
2017-09-19 02:33:26 -04:00
if ( ! plutoBox - > getTxRSSI ( rssiStr , 0 ) ) {
2017-09-18 17:47:36 -04:00
rssiStr = " xxx dB " ;
}
}
2017-09-19 02:33:26 -04:00
bool PlutoSDROutput : : fetchTemperature ( )
2017-09-18 17:47:36 -04:00
{
DevicePlutoSDRBox * plutoBox = m_deviceShared . m_deviceParams - > getBox ( ) ;
return plutoBox - > fetchTemp ( ) ;
}
2017-09-19 02:33:26 -04:00
float PlutoSDROutput : : getTemperature ( )
2017-09-18 17:47:36 -04:00
{
DevicePlutoSDRBox * plutoBox = m_deviceShared . m_deviceParams - > getBox ( ) ;
return plutoBox - > getTemp ( ) ;
}