| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-18 12:02:48 +01:00
										 |  |  | // Copyright (C) 2019-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com>          //
 | 
					
						
							|  |  |  | // Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai>                      //
 | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // 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                  //
 | 
					
						
							|  |  |  | // (at your option) any later version.                                           //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // 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/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  | #include <QTimer>
 | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | #include <QDebug>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "dsp/dspcommands.h"
 | 
					
						
							|  |  |  | #include "dsp/spectrumvis.h"
 | 
					
						
							|  |  |  | #include "util/db.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "filesinkmessages.h"
 | 
					
						
							|  |  |  | #include "filesinkbaseband.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MESSAGE_CLASS_DEFINITION(FileSinkBaseband::MsgConfigureFileSinkBaseband, Message) | 
					
						
							|  |  |  | MESSAGE_CLASS_DEFINITION(FileSinkBaseband::MsgConfigureFileSinkWork, Message) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FileSinkBaseband::FileSinkBaseband() : | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     m_channelizer(&m_sink), | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |     m_specMax(0), | 
					
						
							|  |  |  |     m_squelchLevel(0), | 
					
						
							| 
									
										
										
										
											2022-09-15 21:59:42 +02:00
										 |  |  |     m_squelchOpen(false) | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     qDebug("FileSinkBaseband::FileSinkBaseband"); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FileSinkBaseband::~FileSinkBaseband() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     qDebug("FileSinkBaseband::~FileSinkBaseband"); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |     m_inputMessageQueue.clear(); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     stopWork(); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::reset() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  |     m_inputMessageQueue.clear(); | 
					
						
							|  |  |  |     m_sampleFifo.reset(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::startWork() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QMutexLocker mutexLocker(&m_mutex); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     qDebug("FileSinkBaseband::startWork"); | 
					
						
							|  |  |  |     m_timer = new QTimer(); | 
					
						
							|  |  |  |     connect( | 
					
						
							|  |  |  |         m_timer, | 
					
						
							|  |  |  |         &QTimer::timeout, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &FileSinkBaseband::tick | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     m_timer->start(200); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |     QObject::connect( | 
					
						
							|  |  |  |         &m_sampleFifo, | 
					
						
							|  |  |  |         &SampleSinkFifo::dataReady, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &FileSinkBaseband::handleData, | 
					
						
							|  |  |  |         Qt::QueuedConnection | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     QObject::connect( | 
					
						
							|  |  |  |         &m_inputMessageQueue, | 
					
						
							|  |  |  |         &MessageQueue::messageEnqueued, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &FileSinkBaseband::handleInputMessages | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::stopWork() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QMutexLocker mutexLocker(&m_mutex); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     qDebug("FileSinkBaseband::stopWork"); | 
					
						
							|  |  |  |     m_timer->stop(); | 
					
						
							| 
									
										
										
										
											2021-02-27 23:06:58 +01:00
										 |  |  |     m_sink.stopRecording(); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     QObject::disconnect( | 
					
						
							|  |  |  |         &m_inputMessageQueue, | 
					
						
							|  |  |  |         &MessageQueue::messageEnqueued, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &FileSinkBaseband::handleInputMessages | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |     QObject::disconnect( | 
					
						
							|  |  |  |         &m_sampleFifo, | 
					
						
							|  |  |  |         &SampleSinkFifo::dataReady, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &FileSinkBaseband::handleData | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     delete m_timer; | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_sampleFifo.write(begin, end); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::handleData() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  | 		SampleVector::iterator part1begin; | 
					
						
							|  |  |  | 		SampleVector::iterator part1end; | 
					
						
							|  |  |  | 		SampleVector::iterator part2begin; | 
					
						
							|  |  |  | 		SampleVector::iterator part2end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// first part of FIFO data
 | 
					
						
							|  |  |  |         if (part1begin != part1end) { | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |             m_channelizer.feed(part1begin, part1end); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// second part of FIFO data (used when block wraps around)
 | 
					
						
							|  |  |  | 		if(part2begin != part2end) { | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |             m_channelizer.feed(part2begin, part2end); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		m_sampleFifo.readCommit((unsigned int) count); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::handleInputMessages() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Message* message; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((message = m_inputMessageQueue.pop()) != nullptr) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if (handleMessage(*message)) { | 
					
						
							|  |  |  | 			delete message; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FileSinkBaseband::handleMessage(const Message& cmd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (MsgConfigureFileSinkBaseband::match(cmd)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  |         MsgConfigureFileSinkBaseband& cfg = (MsgConfigureFileSinkBaseband&) cmd; | 
					
						
							|  |  |  |         qDebug() << "FileSinkBaseband::handleMessage: MsgConfigureFileSinkBaseband"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         applySettings(cfg.getSettings(), cfg.getForce()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (DSPSignalNotification::match(cmd)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  |         DSPSignalNotification& notif = (DSPSignalNotification&) cmd; | 
					
						
							|  |  |  |         qDebug() << "FileSinkBaseband::handleMessage: DSPSignalNotification:" | 
					
						
							|  |  |  |             << " basebandSampleRate: " << notif.getSampleRate() | 
					
						
							|  |  |  |             << " cnterFrequency: " << notif.getCenterFrequency(); | 
					
						
							|  |  |  |         m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); | 
					
						
							|  |  |  |         m_centerFrequency = notif.getCenterFrequency(); | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |         m_channelizer.setBasebandSampleRate(notif.getSampleRate()); | 
					
						
							|  |  |  |         int desiredSampleRate = m_channelizer.getBasebandSampleRate() / (1<<m_settings.m_log2Decim); | 
					
						
							|  |  |  |         m_channelizer.setChannelization(desiredSampleRate, m_settings.m_inputFrequencyOffset); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |         m_sink.applyChannelSettings( | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |             m_channelizer.getChannelSampleRate(), | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |             desiredSampleRate, | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |             m_channelizer.getChannelFrequencyOffset(), | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |             m_centerFrequency + m_settings.m_inputFrequencyOffset); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (MsgConfigureFileSinkWork::match(cmd)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QMutexLocker mutexLocker(&m_mutex); | 
					
						
							|  |  |  | 		MsgConfigureFileSinkWork& conf = (MsgConfigureFileSinkWork&) cmd; | 
					
						
							|  |  |  |         qDebug() << "FileSinkBaseband::handleMessage: MsgConfigureFileSinkWork: " << conf.isWorking(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 23:06:58 +01:00
										 |  |  |         if (conf.isWorking()) { | 
					
						
							|  |  |  |             m_sink.startRecording(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             m_sink.stopRecording(); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::applySettings(const FileSinkSettings& settings, bool force) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qDebug() << "FileSinkBaseband::applySettings:" | 
					
						
							|  |  |  |         << "m_log2Decim:" << settings.m_log2Decim | 
					
						
							|  |  |  |         << "m_inputFrequencyOffset:" << settings.m_inputFrequencyOffset | 
					
						
							|  |  |  |         << "m_fileRecordName: " << settings.m_fileRecordName | 
					
						
							|  |  |  |         << "m_centerFrequency: " << m_centerFrequency | 
					
						
							|  |  |  |         << "force: " << force; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((settings.m_log2Decim != m_settings.m_log2Decim) | 
					
						
							|  |  |  |      || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |         int desiredSampleRate = m_channelizer.getBasebandSampleRate() / (1<<settings.m_log2Decim); | 
					
						
							|  |  |  |         m_channelizer.setChannelization(desiredSampleRate, settings.m_inputFrequencyOffset); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |         m_sink.applyChannelSettings( | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |             m_channelizer.getChannelSampleRate(), | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |             desiredSampleRate, | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |             m_channelizer.getChannelFrequencyOffset(), | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  |             m_centerFrequency + settings.m_inputFrequencyOffset); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((settings.m_spectrumSquelchMode != m_settings.m_spectrumSquelchMode) || force) { | 
					
						
							|  |  |  |         if (!settings.m_spectrumSquelchMode) { | 
					
						
							|  |  |  |             m_squelchOpen = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((settings.m_spectrumSquelch != m_settings.m_spectrumSquelch) || force) { | 
					
						
							|  |  |  |         m_squelchLevel = CalcDb::powerFromdB(settings.m_spectrumSquelch); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_sink.applySettings(settings, force); | 
					
						
							|  |  |  |     m_settings = settings; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FileSinkBaseband::getChannelSampleRate() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-23 05:46:06 +02:00
										 |  |  |     return m_channelizer.getChannelSampleRate(); | 
					
						
							| 
									
										
										
										
											2020-08-06 10:46:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FileSinkBaseband::tick() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_spectrumSink && m_settings.m_spectrumSquelchMode) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_specMax = m_spectrumSink->getSpecMax(); | 
					
						
							|  |  |  |         bool squelchOpen = m_specMax > m_squelchLevel; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (squelchOpen != m_squelchOpen) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (m_messageQueueToGUI) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 FileSinkMessages::MsgReportSquelch *msg = FileSinkMessages::MsgReportSquelch::create(squelchOpen); | 
					
						
							|  |  |  |                 m_messageQueueToGUI->push(msg); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_settings.m_squelchRecordingEnable) { | 
					
						
							|  |  |  |                 m_sink.squelchRecording(squelchOpen); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         m_squelchOpen = squelchOpen; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |