2017-01-07 22:13:20 -05: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 "hackrfoutput.h"
# include <string.h>
# include <errno.h>
# include <QDebug>
# include "util/simpleserializer.h"
# include "dsp/dspcommands.h"
# include "dsp/dspengine.h"
# include "device/devicesourceapi.h"
# include "device/devicesinkapi.h"
# include "hackrfoutputgui.h"
# include "hackrfoutputthread.h"
MESSAGE_CLASS_DEFINITION ( HackRFOutput : : MsgConfigureHackRF , Message )
MESSAGE_CLASS_DEFINITION ( HackRFOutput : : MsgReportHackRF , Message )
HackRFOutput : : HackRFOutput ( DeviceSinkAPI * deviceAPI ) :
m_deviceAPI ( deviceAPI ) ,
m_settings ( ) ,
m_dev ( 0 ) ,
m_hackRFThread ( 0 ) ,
2017-04-13 19:41:02 -04:00
m_deviceDescription ( " HackRFOutput " ) ,
m_running ( false )
2017-01-07 22:13:20 -05:00
{
2017-04-13 19:41:02 -04:00
openDevice ( ) ;
2017-01-07 22:13:20 -05:00
m_deviceAPI - > setBuddySharedPtr ( & m_sharedParams ) ;
}
HackRFOutput : : ~ HackRFOutput ( )
{
2017-04-13 19:41:02 -04:00
if ( m_running ) stop ( ) ;
closeDevice ( ) ;
2017-01-07 22:13:20 -05:00
m_deviceAPI - > setBuddySharedPtr ( 0 ) ;
}
2017-04-13 19:41:02 -04:00
bool HackRFOutput : : openDevice ( )
2017-01-07 22:13:20 -05:00
{
if ( m_dev ! = 0 )
{
2017-04-13 19:41:02 -04:00
closeDevice ( ) ;
2017-01-07 22:13:20 -05:00
}
2017-01-09 19:02:28 -05:00
m_sampleSourceFifo . resize ( m_settings . m_devSampleRate / ( 1 < < ( m_settings . m_log2Interp < = 4 ? m_settings . m_log2Interp : 4 ) ) ) ;
2017-01-07 22:13:20 -05:00
if ( m_deviceAPI - > getSourceBuddies ( ) . size ( ) > 0 )
{
DeviceSourceAPI * buddy = m_deviceAPI - > getSourceBuddies ( ) [ 0 ] ;
DeviceHackRFParams * buddySharedParams = ( DeviceHackRFParams * ) buddy - > getBuddySharedPtr ( ) ;
if ( buddySharedParams = = 0 )
{
2017-04-13 19:41:02 -04:00
qCritical ( " HackRFOutput::openDevice: could not get shared parameters from buddy " ) ;
2017-01-07 22:13:20 -05:00
return false ;
}
2017-04-13 19:41:02 -04:00
if ( ( m_dev = buddySharedParams - > m_dev ) = = 0 ) // device is not opened by buddy
2017-01-07 22:13:20 -05:00
{
2017-04-13 19:41:02 -04:00
qCritical ( " HackRFOutput::openDevice: could not get HackRF handle from buddy " ) ;
return false ;
2017-01-07 22:13:20 -05:00
}
2017-04-13 19:41:02 -04:00
m_sharedParams = * ( buddySharedParams ) ; // copy parameters from buddy
m_sharedParams . m_dev = m_dev ;
2017-01-07 22:13:20 -05:00
}
2017-04-13 19:41:02 -04:00
else
2017-01-07 22:13:20 -05:00
{
2017-04-13 20:09:36 -04:00
if ( ( m_dev = DeviceHackRF : : open_hackrf ( qPrintable ( m_deviceAPI - > getSampleSinkSerial ( ) ) ) ) = = 0 )
2017-01-07 22:13:20 -05:00
{
2017-04-13 20:09:36 -04:00
qCritical ( " HackRFOutput::openDevice: could not open HackRF %s " , qPrintable ( m_deviceAPI - > getSampleSinkSerial ( ) ) ) ;
2017-01-07 22:13:20 -05:00
return false ;
}
m_sharedParams . m_dev = m_dev ;
}
2017-04-13 19:41:02 -04:00
return true ;
}
2017-04-13 21:44:49 -04:00
bool HackRFOutput : : start ( )
2017-04-13 19:41:02 -04:00
{
if ( ! m_dev ) {
return false ;
}
if ( m_running ) stop ( ) ;
2017-01-07 22:13:20 -05:00
if ( ( m_hackRFThread = new HackRFOutputThread ( m_dev , & m_sampleSourceFifo ) ) = = 0 )
{
qFatal ( " HackRFOutput::start: out of memory " ) ;
stop ( ) ;
return false ;
}
// mutexLocker.unlock();
applySettings ( m_settings , true ) ;
2017-04-13 19:41:02 -04:00
m_hackRFThread - > setSamplerate ( m_settings . m_devSampleRate ) ;
m_hackRFThread - > setLog2Interpolation ( m_settings . m_log2Interp ) ;
2017-01-07 22:13:20 -05:00
m_hackRFThread - > startWork ( ) ;
qDebug ( " HackRFOutput::start: started " ) ;
2017-04-13 19:41:02 -04:00
m_running = true ;
2017-01-07 22:13:20 -05:00
return true ;
}
2017-04-13 19:41:02 -04:00
void HackRFOutput : : closeDevice ( )
2017-01-07 22:13:20 -05:00
{
2017-04-13 19:41:02 -04:00
if ( m_deviceAPI - > getSourceBuddies ( ) . size ( ) = = 0 )
2017-01-07 22:13:20 -05:00
{
2017-04-13 19:41:02 -04:00
qDebug ( " HackRFOutput::closeDevice: closing device since Rx side is not open " ) ;
2017-01-07 22:13:20 -05:00
if ( m_dev ! = 0 ) // close HackRF
{
hackrf_close ( m_dev ) ;
2017-04-13 19:49:48 -04:00
//hackrf_exit(); // TODO: this may not work if several HackRF Devices are running concurrently. It should be handled globally in the application
2017-01-07 22:13:20 -05:00
}
}
m_sharedParams . m_dev = 0 ;
m_dev = 0 ;
}
2017-04-13 19:41:02 -04:00
void HackRFOutput : : stop ( )
{
qDebug ( " HackRFOutput::stop " ) ;
// QMutexLocker mutexLocker(&m_mutex);
if ( m_hackRFThread ! = 0 )
{
m_hackRFThread - > stopWork ( ) ;
delete m_hackRFThread ;
m_hackRFThread = 0 ;
}
m_running = false ;
}
2017-01-07 22:13:20 -05:00
const QString & HackRFOutput : : getDeviceDescription ( ) const
{
return m_deviceDescription ;
}
int HackRFOutput : : getSampleRate ( ) const
{
int rate = m_settings . m_devSampleRate ;
return ( rate / ( 1 < < m_settings . m_log2Interp ) ) ;
}
quint64 HackRFOutput : : getCenterFrequency ( ) const
{
return m_settings . m_centerFrequency ;
}
bool HackRFOutput : : handleMessage ( const Message & message )
{
if ( MsgConfigureHackRF : : match ( message ) )
{
MsgConfigureHackRF & conf = ( MsgConfigureHackRF & ) message ;
qDebug ( ) < < " HackRFOutput::handleMessage: MsgConfigureHackRF " ;
bool success = applySettings ( conf . getSettings ( ) , false ) ;
if ( ! success )
{
qDebug ( " HackRFOutput::handleMessage: config error " ) ;
}
return true ;
}
else
{
return false ;
}
}
2017-01-09 19:02:28 -05:00
void HackRFOutput : : setCenterFrequency ( quint64 freq_hz , qint32 LOppmTenths )
2017-01-07 22:13:20 -05:00
{
2017-01-09 19:02:28 -05:00
qint64 df = ( ( qint64 ) freq_hz * LOppmTenths ) / 10000000LL ;
2017-01-07 22:13:20 -05:00
freq_hz + = df ;
hackrf_error rc = ( hackrf_error ) hackrf_set_freq ( m_dev , static_cast < uint64_t > ( freq_hz ) ) ;
if ( rc ! = HACKRF_SUCCESS )
{
qWarning ( " HackRFOutput::setCenterFrequency: could not frequency to %llu Hz " , freq_hz ) ;
}
else
{
qWarning ( " HackRFOutput::setCenterFrequency: frequency set to %llu Hz " , freq_hz ) ;
}
}
bool HackRFOutput : : applySettings ( const HackRFOutputSettings & settings , bool force )
{
// QMutexLocker mutexLocker(&m_mutex);
bool forwardChange = false ;
hackrf_error rc ;
qDebug ( ) < < " HackRFOutput::applySettings " ;
2017-01-09 19:02:28 -05:00
if ( ( m_settings . m_devSampleRate ! = settings . m_devSampleRate ) | | ( m_settings . m_log2Interp ! = settings . m_log2Interp ) | | force )
{
2017-01-07 22:13:20 -05:00
forwardChange = true ;
2017-01-09 19:02:28 -05:00
// FIFO size:
// 1 s length up to interpolation by 16
// 2 s for interpolation by 32
m_sampleSourceFifo . resize ( settings . m_devSampleRate / ( 1 < < ( settings . m_log2Interp < = 4 ? settings . m_log2Interp : 4 ) ) ) ;
}
if ( ( m_settings . m_devSampleRate ! = settings . m_devSampleRate ) | | force )
{
2017-01-07 22:13:20 -05:00
if ( m_dev ! = 0 )
{
2017-01-09 19:02:28 -05:00
rc = ( hackrf_error ) hackrf_set_sample_rate_manual ( m_dev , settings . m_devSampleRate , 1 ) ;
2017-01-07 22:13:20 -05:00
if ( rc ! = HACKRF_SUCCESS )
{
2017-04-01 18:23:02 -04:00
qCritical ( " HackRFOutput::applySettings: could not set sample rate to %llu S/s: %s " ,
2017-01-09 19:02:28 -05:00
settings . m_devSampleRate ,
2017-01-07 22:13:20 -05:00
hackrf_error_name ( rc ) ) ;
}
else
{
2017-04-13 19:41:02 -04:00
if ( m_hackRFThread ! = 0 )
{
qDebug ( " HackRFOutput::applySettings: sample rate set to %llu S/s " ,
settings . m_devSampleRate ) ;
m_hackRFThread - > setSamplerate ( settings . m_devSampleRate ) ;
}
2017-01-07 22:13:20 -05:00
}
}
}
if ( ( m_settings . m_log2Interp ! = settings . m_log2Interp ) | | force )
{
2017-04-13 19:41:02 -04:00
if ( m_hackRFThread ! = 0 )
2017-01-07 22:13:20 -05:00
{
2017-01-09 19:02:28 -05:00
m_hackRFThread - > setLog2Interpolation ( settings . m_log2Interp ) ;
qDebug ( ) < < " HackRFOutput: set interpolation to " < < ( 1 < < settings . m_log2Interp ) ;
2017-01-07 22:13:20 -05:00
}
}
if ( force | | ( m_settings . m_centerFrequency ! = settings . m_centerFrequency ) | |
( m_settings . m_LOppmTenths ! = settings . m_LOppmTenths ) )
{
if ( m_dev ! = 0 )
{
2017-01-09 19:02:28 -05:00
setCenterFrequency ( settings . m_centerFrequency , settings . m_LOppmTenths ) ;
qDebug ( ) < < " HackRFOutput::applySettings: center freq: " < < settings . m_centerFrequency < < " Hz LOppm: " < < settings . m_LOppmTenths ;
2017-01-07 22:13:20 -05:00
}
forwardChange = true ;
}
if ( ( m_settings . m_vgaGain ! = settings . m_vgaGain ) | | force )
{
if ( m_dev ! = 0 )
{
2017-01-09 19:02:28 -05:00
rc = ( hackrf_error ) hackrf_set_txvga_gain ( m_dev , settings . m_vgaGain ) ;
2017-01-07 22:13:20 -05:00
if ( rc ! = HACKRF_SUCCESS )
{
qDebug ( " HackRFOutput::applySettings: hackrf_set_txvga_gain failed: %s " , hackrf_error_name ( rc ) ) ;
}
else
{
2017-01-09 19:02:28 -05:00
qDebug ( ) < < " HackRFOutput:applySettings: TxVGA gain set to " < < settings . m_vgaGain ;
2017-01-07 22:13:20 -05:00
}
}
}
if ( ( m_settings . m_bandwidth ! = settings . m_bandwidth ) | | force )
{
if ( m_dev ! = 0 )
{
2017-05-26 11:42:57 -04:00
uint32_t bw_index = hackrf_compute_baseband_filter_bw_round_down_lt ( settings . m_bandwidth + 1 ) ; // +1 so the round down to lower than yields desired bandwidth
2017-01-07 22:13:20 -05:00
rc = ( hackrf_error ) hackrf_set_baseband_filter_bandwidth ( m_dev , bw_index ) ;
if ( rc ! = HACKRF_SUCCESS )
{
qDebug ( " HackRFInput::applySettings: hackrf_set_baseband_filter_bandwidth failed: %s " , hackrf_error_name ( rc ) ) ;
}
else
{
2017-01-09 19:02:28 -05:00
qDebug ( ) < < " HackRFInput:applySettings: Baseband BW filter set to " < < settings . m_bandwidth < < " Hz " ;
2017-01-07 22:13:20 -05:00
}
}
}
if ( ( m_settings . m_biasT ! = settings . m_biasT ) | | force )
{
if ( m_dev ! = 0 )
{
2017-01-09 19:02:28 -05:00
rc = ( hackrf_error ) hackrf_set_antenna_enable ( m_dev , ( settings . m_biasT ? 1 : 0 ) ) ;
2017-01-07 22:13:20 -05:00
if ( rc ! = HACKRF_SUCCESS )
{
qDebug ( " HackRFInput::applySettings: hackrf_set_antenna_enable failed: %s " , hackrf_error_name ( rc ) ) ;
}
else
{
2017-01-09 19:02:28 -05:00
qDebug ( ) < < " HackRFInput:applySettings: bias tee set to " < < settings . m_biasT ;
2017-01-07 22:13:20 -05:00
}
}
}
if ( ( m_settings . m_lnaExt ! = settings . m_lnaExt ) | | force )
{
if ( m_dev ! = 0 )
{
2017-01-09 19:02:28 -05:00
rc = ( hackrf_error ) hackrf_set_amp_enable ( m_dev , ( settings . m_lnaExt ? 1 : 0 ) ) ;
2017-01-07 22:13:20 -05:00
if ( rc ! = HACKRF_SUCCESS )
{
qDebug ( " HackRFInput::applySettings: hackrf_set_amp_enable failed: %s " , hackrf_error_name ( rc ) ) ;
}
else
{
2017-01-09 19:02:28 -05:00
qDebug ( ) < < " HackRFInput:applySettings: extra LNA set to " < < settings . m_lnaExt ;
2017-01-07 22:13:20 -05:00
}
}
}
2017-01-09 19:02:28 -05:00
m_settings . m_devSampleRate = settings . m_devSampleRate ;
m_settings . m_log2Interp = settings . m_log2Interp ;
m_settings . m_centerFrequency = settings . m_centerFrequency ;
m_settings . m_LOppmTenths = settings . m_LOppmTenths ;
m_settings . m_vgaGain = settings . m_vgaGain ;
m_settings . m_bandwidth = settings . m_bandwidth ;
m_settings . m_biasT = settings . m_biasT ;
m_settings . m_lnaExt = settings . m_lnaExt ;
2017-01-07 22:13:20 -05:00
if ( forwardChange )
{
2017-01-08 11:39:02 -05:00
int sampleRate = m_settings . m_devSampleRate / ( 1 < < m_settings . m_log2Interp ) ;
2017-01-07 22:13:20 -05:00
DSPSignalNotification * notif = new DSPSignalNotification ( sampleRate , m_settings . m_centerFrequency ) ;
m_deviceAPI - > getDeviceInputMessageQueue ( ) - > push ( notif ) ;
}
return true ;
}