2017-01-08 04:13:20 +01: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>
2017-12-10 01:57:50 +01:00
#include "SWGDeviceSettings.h"
#include "SWGDeviceState.h"
2017-01-08 04:13:20 +01:00
#include "util/simpleserializer.h"
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "device/devicesourceapi.h"
#include "device/devicesinkapi.h"
2017-08-08 22:37:53 +02:00
#include "hackrf/devicehackrfshared.h"
2017-01-08 04:13:20 +01:00
#include "hackrfoutputthread.h"
MESSAGE_CLASS_DEFINITION ( HackRFOutput :: MsgConfigureHackRF , Message )
2017-12-14 18:02:49 +01:00
MESSAGE_CLASS_DEFINITION ( HackRFOutput :: MsgStartStop , Message )
2017-01-08 04:13:20 +01:00
MESSAGE_CLASS_DEFINITION ( HackRFOutput :: MsgReportHackRF , Message )
HackRFOutput :: HackRFOutput ( DeviceSinkAPI * deviceAPI ) :
m_deviceAPI ( deviceAPI ),
m_settings (),
m_dev ( 0 ),
m_hackRFThread ( 0 ),
2017-04-14 01:41:02 +02:00
m_deviceDescription ( "HackRFOutput" ),
m_running ( false )
2017-01-08 04:13:20 +01:00
{
2017-04-14 01:41:02 +02:00
openDevice ();
2017-01-08 04:13:20 +01:00
m_deviceAPI -> setBuddySharedPtr ( & m_sharedParams );
}
HackRFOutput ::~ HackRFOutput ()
{
2017-04-14 01:41:02 +02:00
if ( m_running ) stop ();
closeDevice ();
2017-01-08 04:13:20 +01:00
m_deviceAPI -> setBuddySharedPtr ( 0 );
}
2017-09-16 11:34:25 +02:00
void HackRFOutput :: destroy ()
{
delete this ;
}
2017-04-14 01:41:02 +02:00
bool HackRFOutput :: openDevice ()
2017-01-08 04:13:20 +01:00
{
if ( m_dev != 0 )
{
2017-04-14 01:41:02 +02:00
closeDevice ();
2017-01-08 04:13:20 +01:00
}
2017-01-10 01:02:28 +01:00
m_sampleSourceFifo . resize ( m_settings . m_devSampleRate / ( 1 << ( m_settings . m_log2Interp <= 4 ? m_settings . m_log2Interp : 4 )));
2017-01-08 04:13:20 +01:00
if ( m_deviceAPI -> getSourceBuddies (). size () > 0 )
{
DeviceSourceAPI * buddy = m_deviceAPI -> getSourceBuddies ()[ 0 ];
DeviceHackRFParams * buddySharedParams = ( DeviceHackRFParams * ) buddy -> getBuddySharedPtr ();
if ( buddySharedParams == 0 )
{
2017-04-14 01:41:02 +02:00
qCritical ( "HackRFOutput::openDevice: could not get shared parameters from buddy" );
2017-01-08 04:13:20 +01:00
return false ;
}
2017-04-14 01:41:02 +02:00
if (( m_dev = buddySharedParams -> m_dev ) == 0 ) // device is not opened by buddy
2017-01-08 04:13:20 +01:00
{
2017-04-14 01:41:02 +02:00
qCritical ( "HackRFOutput::openDevice: could not get HackRF handle from buddy" );
return false ;
2017-01-08 04:13:20 +01:00
}
2017-04-14 01:41:02 +02:00
m_sharedParams = * ( buddySharedParams ); // copy parameters from buddy
m_sharedParams . m_dev = m_dev ;
2017-01-08 04:13:20 +01:00
}
2017-04-14 01:41:02 +02:00
else
2017-01-08 04:13:20 +01:00
{
2017-04-14 02:09:36 +02:00
if (( m_dev = DeviceHackRF :: open_hackrf ( qPrintable ( m_deviceAPI -> getSampleSinkSerial ()))) == 0 )
2017-01-08 04:13:20 +01:00
{
2017-04-14 02:09:36 +02:00
qCritical ( "HackRFOutput::openDevice: could not open HackRF %s" , qPrintable ( m_deviceAPI -> getSampleSinkSerial ()));
2017-01-08 04:13:20 +01:00
return false ;
}
m_sharedParams . m_dev = m_dev ;
}
2017-04-14 01:41:02 +02:00
return true ;
}
2017-12-25 12:59:44 +01:00
void HackRFOutput :: init ()
{
applySettings ( m_settings , true );
}
2017-04-14 03:44:49 +02:00
bool HackRFOutput :: start ()
2017-04-14 01:41:02 +02:00
{
if ( ! m_dev ) {
return false ;
}
if ( m_running ) stop ();
2018-02-24 10:29:27 +01:00
m_hackRFThread = new HackRFOutputThread ( m_dev , & m_sampleSourceFifo );
2017-01-08 04:13:20 +01:00
// mutexLocker.unlock();
applySettings ( m_settings , true );
2017-04-14 01:41:02 +02:00
m_hackRFThread -> setLog2Interpolation ( m_settings . m_log2Interp );
2017-01-08 04:13:20 +01:00
m_hackRFThread -> startWork ();
qDebug ( "HackRFOutput::start: started" );
2017-04-14 01:41:02 +02:00
m_running = true ;
2017-01-08 04:13:20 +01:00
return true ;
}
2017-04-14 01:41:02 +02:00
void HackRFOutput :: closeDevice ()
2017-01-08 04:13:20 +01:00
{
2017-04-14 01:41:02 +02:00
if ( m_deviceAPI -> getSourceBuddies (). size () == 0 )
2017-01-08 04:13:20 +01:00
{
2017-04-14 01:41:02 +02:00
qDebug ( "HackRFOutput::closeDevice: closing device since Rx side is not open" );
2017-01-08 04:13:20 +01:00
if ( m_dev != 0 ) // close HackRF
{
hackrf_close ( m_dev );
2017-04-14 01:49:48 +02: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-08 04:13:20 +01:00
}
}
m_sharedParams . m_dev = 0 ;
m_dev = 0 ;
}
2017-04-14 01:41:02 +02: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-12-28 04:04:50 +01:00
QByteArray HackRFOutput :: serialize () const
{
return m_settings . serialize ();
}
bool HackRFOutput :: deserialize ( const QByteArray & data )
{
bool success = true ;
if ( ! m_settings . deserialize ( data ))
{
m_settings . resetToDefaults ();
success = false ;
}
MsgConfigureHackRF * message = MsgConfigureHackRF :: create ( m_settings , true );
m_inputMessageQueue . push ( message );
if ( m_guiMessageQueue )
{
MsgConfigureHackRF * messageToGUI = MsgConfigureHackRF :: create ( m_settings , true );
m_guiMessageQueue -> push ( messageToGUI );
}
return success ;
}
2017-01-08 04:13:20 +01: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 ;
}
2017-12-28 04:04:50 +01:00
void HackRFOutput :: setCenterFrequency ( qint64 centerFrequency )
{
HackRFOutputSettings settings = m_settings ;
settings . m_centerFrequency = centerFrequency ;
MsgConfigureHackRF * message = MsgConfigureHackRF :: create ( settings , false );
m_inputMessageQueue . push ( message );
if ( m_guiMessageQueue )
{
MsgConfigureHackRF * messageToGUI = MsgConfigureHackRF :: create ( settings , false );
m_guiMessageQueue -> push ( messageToGUI );
}
}
2017-01-08 04:13:20 +01:00
bool HackRFOutput :: handleMessage ( const Message & message )
{
if ( MsgConfigureHackRF :: match ( message ))
{
MsgConfigureHackRF & conf = ( MsgConfigureHackRF & ) message ;
qDebug () << "HackRFOutput::handleMessage: MsgConfigureHackRF" ;
2017-10-14 07:13:57 +02:00
bool success = applySettings ( conf . getSettings (), conf . getForce ());
2017-01-08 04:13:20 +01:00
if ( ! success )
{
2017-09-18 00:01:29 +02:00
qDebug ( "HackRFOutput::handleMessage: MsgConfigureHackRF: config error" );
2017-01-08 04:13:20 +01:00
}
return true ;
}
2017-09-18 00:01:29 +02:00
else if ( DeviceHackRFShared :: MsgConfigureFrequencyDelta :: match ( message ))
{
DeviceHackRFShared :: MsgConfigureFrequencyDelta & conf = ( DeviceHackRFShared :: MsgConfigureFrequencyDelta & ) message ;
HackRFOutputSettings newSettings = m_settings ;
newSettings . m_centerFrequency = m_settings . m_centerFrequency + conf . getFrequencyDelta ();
qDebug () << "HackRFOutput::handleMessage: DeviceHackRFShared::MsgConfigureFrequencyDelta: newFreq: " << newSettings . m_centerFrequency ;
applySettings ( newSettings , false );
return true ;
}
2017-12-14 18:02:49 +01:00
else if ( MsgStartStop :: match ( message ))
{
MsgStartStop & cmd = ( MsgStartStop & ) message ;
qDebug () << "HackRFOutput::handleMessage: MsgStartStop: " << ( cmd . getStartStop () ? "start" : "stop" );
if ( cmd . getStartStop ())
{
if ( m_deviceAPI -> initGeneration ())
{
m_deviceAPI -> startGeneration ();
}
}
else
{
m_deviceAPI -> stopGeneration ();
}
return true ;
}
2017-01-08 04:13:20 +01:00
else
{
return false ;
}
}
2017-12-29 01:40:34 +01:00
void HackRFOutput :: setDeviceCenterFrequency ( quint64 freq_hz , qint32 LOppmTenths )
2017-01-08 04:13:20 +01:00
{
2017-01-10 01:02:28 +01:00
qint64 df = (( qint64 ) freq_hz * LOppmTenths ) / 10000000LL ;
2017-01-08 04:13:20 +01:00
freq_hz += df ;
hackrf_error rc = ( hackrf_error ) hackrf_set_freq ( m_dev , static_cast < uint64_t > ( freq_hz ));
if ( rc != HACKRF_SUCCESS )
{
2017-12-29 01:40:34 +01:00
qWarning ( "HackRFOutput::setDeviceCenterFrequency: could not frequency to %llu Hz" , freq_hz );
2017-01-08 04:13:20 +01:00
}
else
{
2017-12-29 01:40:34 +01:00
qWarning ( "HackRFOutput::setDeviceCenterFrequency: frequency set to %llu Hz" , freq_hz );
2017-01-08 04:13:20 +01:00
}
}
bool HackRFOutput :: applySettings ( const HackRFOutputSettings & settings , bool force )
{
// QMutexLocker mutexLocker(&m_mutex);
2017-08-08 21:30:04 +02:00
bool forwardChange = false ;
bool suspendThread = false ;
bool threadWasRunning = false ;
2017-01-08 04:13:20 +01:00
hackrf_error rc ;
qDebug () << "HackRFOutput::applySettings" ;
2017-08-08 21:30:04 +02:00
if (( m_settings . m_devSampleRate != settings . m_devSampleRate ) ||
2017-08-08 23:09:51 +02:00
( m_settings . m_log2Interp != settings . m_log2Interp ) || force )
2017-08-08 21:30:04 +02:00
{
suspendThread = true ;
}
if ( suspendThread )
{
if ( m_hackRFThread )
{
if ( m_hackRFThread -> isRunning ())
{
m_hackRFThread -> stopWork ();
threadWasRunning = true ;
}
}
}
2017-01-10 01:02:28 +01:00
if (( m_settings . m_devSampleRate != settings . m_devSampleRate ) || ( m_settings . m_log2Interp != settings . m_log2Interp ) || force )
{
2017-01-08 04:13:20 +01:00
forwardChange = true ;
2017-08-08 22:37:53 +02:00
int fifoSize = std :: max (
( int ) (( settings . m_devSampleRate / ( 1 << settings . m_log2Interp )) * DeviceHackRFShared :: m_sampleFifoLengthInSeconds ),
DeviceHackRFShared :: m_sampleFifoMinSize );
m_sampleSourceFifo . resize ( fifoSize );
2017-01-10 01:02:28 +01:00
}
if (( m_settings . m_devSampleRate != settings . m_devSampleRate ) || force )
{
2017-01-08 04:13:20 +01:00
if ( m_dev != 0 )
{
2017-01-10 01:02:28 +01:00
rc = ( hackrf_error ) hackrf_set_sample_rate_manual ( m_dev , settings . m_devSampleRate , 1 );
2017-01-08 04:13:20 +01:00
if ( rc != HACKRF_SUCCESS )
{
2017-04-02 00:23:02 +02:00
qCritical ( "HackRFOutput::applySettings: could not set sample rate to %llu S/s: %s" ,
2017-01-10 01:02:28 +01:00
settings . m_devSampleRate ,
2017-01-08 04:13:20 +01:00
hackrf_error_name ( rc ));
}
else
{
2017-08-08 13:12:16 +02:00
qDebug ( "HackRFOutput::applySettings: sample rate set to %llu S/s" ,
settings . m_devSampleRate );
2017-01-08 04:13:20 +01:00
}
}
}
if (( m_settings . m_log2Interp != settings . m_log2Interp ) || force )
{
2017-04-14 01:41:02 +02:00
if ( m_hackRFThread != 0 )
2017-01-08 04:13:20 +01:00
{
2017-01-10 01:02:28 +01:00
m_hackRFThread -> setLog2Interpolation ( settings . m_log2Interp );
qDebug () << "HackRFOutput: set interpolation to " << ( 1 << settings . m_log2Interp );
2017-01-08 04:13:20 +01:00
}
}
if ( force || ( m_settings . m_centerFrequency != settings . m_centerFrequency ) ||
( m_settings . m_LOppmTenths != settings . m_LOppmTenths ))
{
if ( m_dev != 0 )
{
2017-12-29 01:40:34 +01:00
setDeviceCenterFrequency ( settings . m_centerFrequency , settings . m_LOppmTenths );
2017-01-10 01:02:28 +01:00
qDebug () << "HackRFOutput::applySettings: center freq: " << settings . m_centerFrequency << " Hz LOppm: " << settings . m_LOppmTenths ;
2017-01-08 04:13:20 +01:00
}
forwardChange = true ;
}
if (( m_settings . m_vgaGain != settings . m_vgaGain ) || force )
{
if ( m_dev != 0 )
{
2017-01-10 01:02:28 +01:00
rc = ( hackrf_error ) hackrf_set_txvga_gain ( m_dev , settings . m_vgaGain );
2017-01-08 04:13:20 +01:00
if ( rc != HACKRF_SUCCESS )
{
qDebug ( "HackRFOutput::applySettings: hackrf_set_txvga_gain failed: %s" , hackrf_error_name ( rc ));
}
else
{
2017-01-10 01:02:28 +01:00
qDebug () << "HackRFOutput:applySettings: TxVGA gain set to " << settings . m_vgaGain ;
2017-01-08 04:13:20 +01:00
}
}
}
if (( m_settings . m_bandwidth != settings . m_bandwidth ) || force )
{
if ( m_dev != 0 )
{
2017-05-26 17:42:57 +02: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-08 04:13:20 +01: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-10 01:02:28 +01:00
qDebug () << "HackRFInput:applySettings: Baseband BW filter set to " << settings . m_bandwidth << " Hz" ;
2017-01-08 04:13:20 +01:00
}
}
}
if (( m_settings . m_biasT != settings . m_biasT ) || force )
{
if ( m_dev != 0 )
{
2017-01-10 01:02:28 +01:00
rc = ( hackrf_error ) hackrf_set_antenna_enable ( m_dev , ( settings . m_biasT ? 1 : 0 ));
2017-01-08 04:13:20 +01:00
if ( rc != HACKRF_SUCCESS )
{
qDebug ( "HackRFInput::applySettings: hackrf_set_antenna_enable failed: %s" , hackrf_error_name ( rc ));
}
else
{
2017-01-10 01:02:28 +01:00
qDebug () << "HackRFInput:applySettings: bias tee set to " << settings . m_biasT ;
2017-01-08 04:13:20 +01:00
}
}
}
if (( m_settings . m_lnaExt != settings . m_lnaExt ) || force )
{
if ( m_dev != 0 )
{
2017-01-10 01:02:28 +01:00
rc = ( hackrf_error ) hackrf_set_amp_enable ( m_dev , ( settings . m_lnaExt ? 1 : 0 ));
2017-01-08 04:13:20 +01:00
if ( rc != HACKRF_SUCCESS )
{
qDebug ( "HackRFInput::applySettings: hackrf_set_amp_enable failed: %s" , hackrf_error_name ( rc ));
}
else
{
2017-01-10 01:02:28 +01:00
qDebug () << "HackRFInput:applySettings: extra LNA set to " << settings . m_lnaExt ;
2017-01-08 04:13:20 +01:00
}
}
}
2017-08-08 21:30:04 +02:00
if ( threadWasRunning )
{
m_hackRFThread -> startWork ();
}
2017-01-10 01:02:28 +01: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-08 04:13:20 +01:00
if ( forwardChange )
{
2017-01-08 17:39:02 +01:00
int sampleRate = m_settings . m_devSampleRate / ( 1 << m_settings . m_log2Interp );
2017-01-08 04:13:20 +01:00
DSPSignalNotification * notif = new DSPSignalNotification ( sampleRate , m_settings . m_centerFrequency );
2017-09-13 23:42:28 +02:00
m_deviceAPI -> getDeviceEngineInputMessageQueue () -> push ( notif );
2017-01-08 04:13:20 +01:00
}
return true ;
}
2017-12-10 01:57:50 +01:00
2017-12-29 01:40:34 +01:00
int HackRFOutput :: webapiSettingsGet (
SWGSDRangel :: SWGDeviceSettings & response ,
QString & errorMessage __attribute__ (( unused )))
{
response . setHackRfOutputSettings ( new SWGSDRangel :: SWGHackRFOutputSettings ());
2018-02-21 13:50:50 +01:00
response . getHackRfOutputSettings () -> init ();
2017-12-29 01:40:34 +01:00
webapiFormatDeviceSettings ( response , m_settings );
return 200 ;
}
int HackRFOutput :: webapiSettingsPutPatch (
bool force ,
const QStringList & deviceSettingsKeys ,
SWGSDRangel :: SWGDeviceSettings & response , // query + response
QString & errorMessage __attribute__ (( unused )))
{
HackRFOutputSettings settings = m_settings ;
if ( deviceSettingsKeys . contains ( "centerFrequency" )) {
settings . m_centerFrequency = response . getHackRfOutputSettings () -> getCenterFrequency ();
}
if ( deviceSettingsKeys . contains ( "LOppmTenths" )) {
settings . m_LOppmTenths = response . getHackRfOutputSettings () -> getLOppmTenths ();
}
if ( deviceSettingsKeys . contains ( "bandwidth" )) {
settings . m_bandwidth = response . getHackRfOutputSettings () -> getBandwidth ();
}
if ( deviceSettingsKeys . contains ( "vgaGain" )) {
settings . m_vgaGain = response . getHackRfOutputSettings () -> getVgaGain ();
}
if ( deviceSettingsKeys . contains ( "log2Interp" )) {
settings . m_log2Interp = response . getHackRfOutputSettings () -> getLog2Interp ();
}
if ( deviceSettingsKeys . contains ( "devSampleRate" )) {
settings . m_devSampleRate = response . getHackRfOutputSettings () -> getDevSampleRate ();
}
if ( deviceSettingsKeys . contains ( "biasT" )) {
settings . m_biasT = response . getHackRfOutputSettings () -> getBiasT () != 0 ;
}
if ( deviceSettingsKeys . contains ( "lnaExt" )) {
settings . m_lnaExt = response . getHackRfOutputSettings () -> getLnaExt () != 0 ;
}
MsgConfigureHackRF * msg = MsgConfigureHackRF :: create ( settings , force );
m_inputMessageQueue . push ( msg );
if ( m_guiMessageQueue ) // forward to GUI if any
{
MsgConfigureHackRF * msgToGUI = MsgConfigureHackRF :: create ( settings , force );
m_guiMessageQueue -> push ( msgToGUI );
}
webapiFormatDeviceSettings ( response , settings );
return 200 ;
}
void HackRFOutput :: webapiFormatDeviceSettings ( SWGSDRangel :: SWGDeviceSettings & response , const HackRFOutputSettings & settings )
{
response . getHackRfOutputSettings () -> setCenterFrequency ( settings . m_centerFrequency );
response . getHackRfOutputSettings () -> setLOppmTenths ( settings . m_LOppmTenths );
response . getHackRfOutputSettings () -> setBandwidth ( settings . m_bandwidth );
response . getHackRfOutputSettings () -> setVgaGain ( settings . m_vgaGain );
response . getHackRfOutputSettings () -> setLog2Interp ( settings . m_log2Interp );
response . getHackRfOutputSettings () -> setDevSampleRate ( settings . m_devSampleRate );
response . getHackRfOutputSettings () -> setBiasT ( settings . m_biasT ? 1 : 0 );
response . getHackRfOutputSettings () -> setLnaExt ( settings . m_lnaExt ? 1 : 0 );
}
2017-12-10 01:57:50 +01:00
int HackRFOutput :: webapiRunGet (
SWGSDRangel :: SWGDeviceState & response ,
QString & errorMessage __attribute__ (( unused )))
{
m_deviceAPI -> getDeviceEngineStateStr ( * response . getState ());
return 200 ;
}
int HackRFOutput :: webapiRun (
bool run ,
SWGSDRangel :: SWGDeviceState & response ,
QString & errorMessage __attribute__ (( unused )))
{
2017-12-14 23:29:12 +01:00
m_deviceAPI -> getDeviceEngineStateStr ( * response . getState ());
2017-12-14 18:02:49 +01:00
MsgStartStop * message = MsgStartStop :: create ( run );
m_inputMessageQueue . push ( message );
if ( m_guiMessageQueue )
2017-12-10 01:57:50 +01:00
{
2017-12-14 18:02:49 +01:00
MsgStartStop * messagetoGui = MsgStartStop :: create ( run );
m_guiMessageQueue -> push ( messagetoGui );
2017-12-10 01:57:50 +01:00
}
return 200 ;
}