mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	ATV Modulator: very basically works
This commit is contained in:
		
							parent
							
								
									62ab9b2f29
								
							
						
					
					
						commit
						1462ce09e1
					
				| @ -1,6 +1,7 @@ | |||||||
| project(mod) | project(mod) | ||||||
| 
 | 
 | ||||||
| add_subdirectory(modam) | add_subdirectory(modam) | ||||||
|  | add_subdirectory(modatv) | ||||||
| add_subdirectory(modnfm) | add_subdirectory(modnfm) | ||||||
| add_subdirectory(modssb) | add_subdirectory(modssb) | ||||||
| add_subdirectory(modwfm) | add_subdirectory(modwfm) | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								plugins/channeltx/modatv/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								plugins/channeltx/modatv/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | project(modatv) | ||||||
|  | 
 | ||||||
|  | set(modatv_SOURCES | ||||||
|  | 	atvmod.cpp | ||||||
|  | 	atvmodgui.cpp | ||||||
|  | 	atvmodplugin.cpp | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | set(modatv_HEADERS | ||||||
|  | 	atvmod.h | ||||||
|  | 	atvmodgui.h | ||||||
|  | 	atvmodplugin.h | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | set(modatv_FORMS | ||||||
|  | 	atvmodgui.ui | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | include_directories( | ||||||
|  | 	. | ||||||
|  | 	${CMAKE_CURRENT_BINARY_DIR} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | add_definitions(${QT_DEFINITIONS}) | ||||||
|  | add_definitions(-DQT_PLUGIN) | ||||||
|  | add_definitions(-DQT_SHARED) | ||||||
|  | 
 | ||||||
|  | qt5_wrap_ui(modatv_FORMS_HEADERS ${modatv_FORMS}) | ||||||
|  | 
 | ||||||
|  | add_library(modatv SHARED | ||||||
|  | 	${modatv_SOURCES} | ||||||
|  | 	${modatv_HEADERS_MOC} | ||||||
|  | 	${modatv_FORMS_HEADERS} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | target_link_libraries(modatv | ||||||
|  | 	${QT_LIBRARIES} | ||||||
|  | 	sdrbase | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | qt5_use_modules(modatv Core Widgets) | ||||||
|  | 
 | ||||||
|  | install(TARGETS modatv DESTINATION lib/plugins/channeltx) | ||||||
							
								
								
									
										305
									
								
								plugins/channeltx/modatv/atvmod.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								plugins/channeltx/modatv/atvmod.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,305 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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/upchannelizer.h" | ||||||
|  | #include "atvmod.h" | ||||||
|  | 
 | ||||||
|  | MESSAGE_CLASS_DEFINITION(ATVMod::MsgConfigureATVMod, Message) | ||||||
|  | 
 | ||||||
|  | const float ATVMod::m_blackLevel = 0.3f; | ||||||
|  | const float ATVMod::m_spanLevel = 0.7f; | ||||||
|  | const int ATVMod::m_levelNbSamples = 10000; // every 10ms
 | ||||||
|  | 
 | ||||||
|  | ATVMod::ATVMod() : | ||||||
|  |     m_evenImage(true), | ||||||
|  |     m_tvSampleRate(1000000), | ||||||
|  |     m_settingsMutex(QMutex::Recursive), | ||||||
|  |     m_horizontalCount(0), | ||||||
|  |     m_lineCount(0) | ||||||
|  | { | ||||||
|  |     setObjectName("ATVMod"); | ||||||
|  | 
 | ||||||
|  |     m_config.m_outputSampleRate = 1000000; | ||||||
|  |     m_config.m_inputFrequencyOffset = 0; | ||||||
|  |     m_config.m_rfBandwidth = 1000000; | ||||||
|  |     m_config.m_atvModInput = ATVModInputBarChart; | ||||||
|  |     m_config.m_atvStd = ATVStdPAL625; | ||||||
|  | 
 | ||||||
|  |     applyStandard(); | ||||||
|  | 
 | ||||||
|  |     m_interpolatorDistanceRemain = 0.0f; | ||||||
|  |     m_interpolatorDistance = 1.0f; | ||||||
|  | 
 | ||||||
|  |     apply(true); | ||||||
|  | 
 | ||||||
|  |     m_movingAverage.resize(16, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ATVMod::~ATVMod() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::configure(MessageQueue* messageQueue, | ||||||
|  |             Real rfBandwidth, | ||||||
|  |             ATVStd atvStd, | ||||||
|  |             ATVModInput atvModInput, | ||||||
|  |             Real uniformLevel, | ||||||
|  |             bool channelMute) | ||||||
|  | { | ||||||
|  |     Message* cmd = MsgConfigureATVMod::create(rfBandwidth, atvStd, atvModInput, uniformLevel); | ||||||
|  |     messageQueue->push(cmd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::pullAudio(int nbSamples) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::pull(Sample& sample) | ||||||
|  | { | ||||||
|  |     Complex ci; | ||||||
|  | 
 | ||||||
|  |     m_settingsMutex.lock(); | ||||||
|  | 
 | ||||||
|  |     if (m_tvSampleRate == m_running.m_outputSampleRate) // no interpolation nor decimation
 | ||||||
|  |     { | ||||||
|  |         modulateSample(); | ||||||
|  |         pullFinalize(m_modSample, sample); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         if (m_interpolatorDistance > 1.0f) // decimate
 | ||||||
|  |         { | ||||||
|  |             modulateSample(); | ||||||
|  | 
 | ||||||
|  |             while (!m_interpolator.decimate(&m_interpolatorDistanceRemain, m_modSample, &ci)) | ||||||
|  |             { | ||||||
|  |                 modulateSample(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, m_modSample, &ci)) | ||||||
|  |             { | ||||||
|  |                 modulateSample(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         m_interpolatorDistanceRemain += m_interpolatorDistance; | ||||||
|  |         pullFinalize(ci, sample); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::pullFinalize(Complex& ci, Sample& sample) | ||||||
|  | { | ||||||
|  |     ci *= m_carrierNco.nextIQ(); // shift to carrier frequency
 | ||||||
|  | 
 | ||||||
|  |     m_settingsMutex.unlock(); | ||||||
|  | 
 | ||||||
|  |     Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); | ||||||
|  |     magsq /= (1<<30); | ||||||
|  |     m_movingAverage.feed(magsq); | ||||||
|  | 
 | ||||||
|  |     sample.m_real = (FixReal) ci.real(); | ||||||
|  |     sample.m_imag = (FixReal) ci.imag(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::modulateSample() | ||||||
|  | { | ||||||
|  |     Real t; | ||||||
|  | 
 | ||||||
|  |     pullVideo(t); | ||||||
|  |     calculateLevel(t); | ||||||
|  | 
 | ||||||
|  |     // TODO For now do AM 95%
 | ||||||
|  |     m_modSample.real((t*0.95 + 1.0f) * 16384.0f); // modulate and scale zero frequency carrier
 | ||||||
|  |     m_modSample.imag(0.0f); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::pullVideo(Real& sample) | ||||||
|  | { | ||||||
|  |     if ((m_lineCount < 21) || (m_lineCount > 621) || ((m_lineCount > 309) && (m_lineCount < 335))) | ||||||
|  |     { | ||||||
|  |         pullVSyncLine(m_horizontalCount - (m_pointsPerSync + m_pointsPerBP), sample); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         pullImageLine(sample); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m_horizontalCount < m_nbHorizPoints) | ||||||
|  |     { | ||||||
|  |         m_horizontalCount++; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         if (m_lineCount < m_nbLines) | ||||||
|  |         { | ||||||
|  |             m_lineCount++; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             m_lineCount = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         m_horizontalCount = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::calculateLevel(Real& sample) | ||||||
|  | { | ||||||
|  |     if (m_levelCalcCount < m_levelNbSamples) | ||||||
|  |     { | ||||||
|  |         m_peakLevel = std::max(std::fabs(m_peakLevel), sample); | ||||||
|  |         m_levelSum += sample * sample; | ||||||
|  |         m_levelCalcCount++; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         qreal rmsLevel = std::sqrt(m_levelSum / m_levelNbSamples); | ||||||
|  |         //qDebug("NFMMod::calculateLevel: %f %f", rmsLevel, m_peakLevel);
 | ||||||
|  |         emit levelChanged(rmsLevel, m_peakLevel, m_levelNbSamples); | ||||||
|  |         m_peakLevel = 0.0f; | ||||||
|  |         m_levelSum = 0.0f; | ||||||
|  |         m_levelCalcCount = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::start() | ||||||
|  | { | ||||||
|  |     qDebug() << "ATVMod::start: m_outputSampleRate: " << m_config.m_outputSampleRate | ||||||
|  |             << " m_inputFrequencyOffset: " << m_config.m_inputFrequencyOffset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::stop() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ATVMod::handleMessage(const Message& cmd) | ||||||
|  | { | ||||||
|  |     if (UpChannelizer::MsgChannelizerNotification::match(cmd)) | ||||||
|  |     { | ||||||
|  |         UpChannelizer::MsgChannelizerNotification& notif = (UpChannelizer::MsgChannelizerNotification&) cmd; | ||||||
|  | 
 | ||||||
|  |         m_config.m_outputSampleRate = notif.getSampleRate(); | ||||||
|  |         m_config.m_inputFrequencyOffset = notif.getFrequencyOffset(); | ||||||
|  | 
 | ||||||
|  |         apply(); | ||||||
|  | 
 | ||||||
|  |         qDebug() << "ATVMod::handleMessage: MsgChannelizerNotification:" | ||||||
|  |                 << " m_outputSampleRate: " << m_config.m_outputSampleRate | ||||||
|  |                 << " m_inputFrequencyOffset: " << m_config.m_inputFrequencyOffset; | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     else if (MsgConfigureATVMod::match(cmd)) | ||||||
|  |     { | ||||||
|  |         MsgConfigureATVMod& cfg = (MsgConfigureATVMod&) cmd; | ||||||
|  | 
 | ||||||
|  |         m_config.m_rfBandwidth = cfg.getRFBandwidth(); | ||||||
|  |         m_config.m_atvModInput = cfg.getATVModInput(); | ||||||
|  |         m_config.m_atvStd = cfg.getATVStd(); | ||||||
|  |         m_config.m_uniformLevel = cfg.getUniformLevel(); | ||||||
|  | 
 | ||||||
|  |         apply(); | ||||||
|  | 
 | ||||||
|  |         qDebug() << "ATVMod::handleMessage: MsgConfigureATVMod:" | ||||||
|  |                 << " m_rfBandwidth: " << m_config.m_rfBandwidth | ||||||
|  |                 << " m_atvStd: " << (int) m_config.m_atvStd | ||||||
|  |                 << " m_atvModInput: " << (int) m_config.m_atvModInput | ||||||
|  |                 << " m_uniformLevel: " << m_config.m_uniformLevel; | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::apply(bool force) | ||||||
|  | { | ||||||
|  |     if ((m_config.m_outputSampleRate != m_running.m_outputSampleRate) || | ||||||
|  |         (m_config.m_atvStd != m_running.m_atvStd) || | ||||||
|  |         (m_config.m_rfBandwidth != m_running.m_rfBandwidth) || force) | ||||||
|  |     { | ||||||
|  |         int rateMultiple = getSampleRateMultiple(m_config.m_atvStd); | ||||||
|  |         m_tvSampleRate = (m_config.m_outputSampleRate / rateMultiple) * rateMultiple; | ||||||
|  | 
 | ||||||
|  |         m_settingsMutex.lock(); | ||||||
|  | 
 | ||||||
|  |         if (m_tvSampleRate > 0) | ||||||
|  |         { | ||||||
|  |             m_interpolatorDistanceRemain = 0; | ||||||
|  |             m_interpolatorDistance = (Real) m_tvSampleRate / (Real) m_config.m_outputSampleRate; | ||||||
|  |             m_interpolator.create(48, m_tvSampleRate, m_config.m_rfBandwidth / 2.2, 3.0); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             m_tvSampleRate = m_config.m_outputSampleRate; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         applyStandard(); // set all timings
 | ||||||
|  |         m_settingsMutex.unlock(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if ((m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) || | ||||||
|  |         (m_config.m_outputSampleRate != m_running.m_outputSampleRate)) | ||||||
|  |     { | ||||||
|  |         m_settingsMutex.lock(); | ||||||
|  |         m_carrierNco.setFreq(m_config.m_inputFrequencyOffset, m_config.m_outputSampleRate); | ||||||
|  |         m_settingsMutex.unlock(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_running.m_outputSampleRate = m_config.m_outputSampleRate; | ||||||
|  |     m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; | ||||||
|  |     m_running.m_rfBandwidth = m_config.m_rfBandwidth; | ||||||
|  |     m_running.m_atvModInput = m_config.m_atvModInput; | ||||||
|  |     m_running.m_atvStd = m_config.m_atvStd; | ||||||
|  |     m_running.m_uniformLevel = m_config.m_uniformLevel; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ATVMod::getSampleRateMultiple(ATVStd std) | ||||||
|  | { | ||||||
|  |     switch(std) | ||||||
|  |     { | ||||||
|  |     case ATVStdPAL625: | ||||||
|  |     default: | ||||||
|  |         return 1000000; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVMod::applyStandard() | ||||||
|  | { | ||||||
|  |     int rateMultiple = getSampleRateMultiple(m_config.m_atvStd); | ||||||
|  |     m_pointsPerTU = m_tvSampleRate / rateMultiple; | ||||||
|  | 
 | ||||||
|  |     switch(m_config.m_atvStd) | ||||||
|  |     { | ||||||
|  |     case ATVStdPAL625: | ||||||
|  |     default: | ||||||
|  |         m_pointsPerSync = 5 * m_pointsPerTU;         // would be 4.7 us actually
 | ||||||
|  |         m_pointsPerBP = 5 * m_pointsPerTU;           // would be 4.7 us actually
 | ||||||
|  |         m_pointsPerFP = 2 * m_pointsPerTU;           // would be 1.5 us actually
 | ||||||
|  |         m_pointsPerFSync = 3 * m_pointsPerTU;        // would be 2.3 us actually
 | ||||||
|  |         m_pointsPerLine = (64 - 12) * m_pointsPerTU; // what is left in a 64 us line
 | ||||||
|  |         m_pointsPerBar = 10 * m_pointsPerTU;         // set a bar length to 10 us (~5 bars per line)
 | ||||||
|  |         m_nbLines = 625; | ||||||
|  |         m_interlaced = true; | ||||||
|  |         m_nbHorizPoints = 64 * m_pointsPerTU; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										301
									
								
								plugins/channeltx/modatv/atvmod.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								plugins/channeltx/modatv/atvmod.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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/>.          //
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | #ifndef PLUGINS_CHANNELTX_MODATV_ATVMOD_H_ | ||||||
|  | #define PLUGINS_CHANNELTX_MODATV_ATVMOD_H_ | ||||||
|  | 
 | ||||||
|  | #include <QObject> | ||||||
|  | #include <QMutex> | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #include "dsp/basebandsamplesource.h" | ||||||
|  | #include "dsp/nco.h" | ||||||
|  | #include "dsp/interpolator.h" | ||||||
|  | #include "dsp/movingaverage.h" | ||||||
|  | #include "util/message.h" | ||||||
|  | 
 | ||||||
|  | class ATVMod : public BasebandSampleSource { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     typedef enum | ||||||
|  |     { | ||||||
|  |         ATVStdPAL625 | ||||||
|  |     } ATVStd; | ||||||
|  | 
 | ||||||
|  |     typedef enum | ||||||
|  |     { | ||||||
|  |         ATVModInputUniform, | ||||||
|  |         ATVModInputBarChart, | ||||||
|  |         ATVModInputGradient | ||||||
|  |     } ATVModInput; | ||||||
|  | 
 | ||||||
|  |     ATVMod(); | ||||||
|  |     ~ATVMod(); | ||||||
|  | 
 | ||||||
|  |     void configure(MessageQueue* messageQueue, | ||||||
|  |             Real rfBandwidth, | ||||||
|  |             ATVStd atvStd, | ||||||
|  |             ATVModInput atvModInput, | ||||||
|  |             Real uniformLevel, | ||||||
|  |             bool channelMute); | ||||||
|  | 
 | ||||||
|  |     virtual void pull(Sample& sample); | ||||||
|  |     virtual void pullAudio(int nbSamples); // this is used for video signal actually
 | ||||||
|  |     virtual void start(); | ||||||
|  |     virtual void stop(); | ||||||
|  |     virtual bool handleMessage(const Message& cmd); | ||||||
|  | 
 | ||||||
|  |     Real getMagSq() const { return m_movingAverage.average(); } | ||||||
|  | 
 | ||||||
|  |     static int getSampleRateMultiple(ATVStd std); | ||||||
|  | 
 | ||||||
|  | signals: | ||||||
|  |     /**
 | ||||||
|  |      * Level changed | ||||||
|  |      * \param rmsLevel RMS level in range 0.0 - 1.0 | ||||||
|  |      * \param peakLevel Peak level in range 0.0 - 1.0 | ||||||
|  |      * \param numSamples Number of audio samples analyzed | ||||||
|  |      */ | ||||||
|  |     void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     class MsgConfigureATVMod : public Message | ||||||
|  |     { | ||||||
|  |         MESSAGE_CLASS_DECLARATION | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         Real getRFBandwidth() const { return m_rfBandwidth; } | ||||||
|  |         ATVStd getATVStd() const { return m_atvStd; } | ||||||
|  |         ATVModInput getATVModInput() const { return m_atvModInput; } | ||||||
|  |         Real getUniformLevel() const { return m_uniformLevel; } | ||||||
|  | 
 | ||||||
|  |         static MsgConfigureATVMod* create( | ||||||
|  |             Real rfBandwidth, | ||||||
|  |             ATVStd atvStd, | ||||||
|  |             ATVModInput atvModInput, | ||||||
|  |             Real uniformLevel) | ||||||
|  |         { | ||||||
|  |             return new MsgConfigureATVMod(rfBandwidth, atvStd, atvModInput, uniformLevel); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  |         Real        m_rfBandwidth; | ||||||
|  |         ATVStd      m_atvStd; | ||||||
|  |         ATVModInput m_atvModInput; | ||||||
|  |         Real    m_uniformLevel; | ||||||
|  | 
 | ||||||
|  |         MsgConfigureATVMod( | ||||||
|  |                 Real rfBandwidth, | ||||||
|  |                 ATVStd atvStd, | ||||||
|  |                 ATVModInput atvModInput, | ||||||
|  |                 Real uniformLevel) : | ||||||
|  |             Message(), | ||||||
|  |             m_rfBandwidth(rfBandwidth), | ||||||
|  |             m_atvStd(atvStd), | ||||||
|  |             m_atvModInput(atvModInput), | ||||||
|  |             m_uniformLevel(uniformLevel) | ||||||
|  |         { } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     struct Config | ||||||
|  |     { | ||||||
|  |         int         m_outputSampleRate;     //!< sample rate from channelizer
 | ||||||
|  |         qint64      m_inputFrequencyOffset; //!< offset from baseband center frequency
 | ||||||
|  |         Real        m_rfBandwidth;          //!< Bandwidth of modulated signal
 | ||||||
|  |         ATVStd      m_atvStd;               //!< Standard
 | ||||||
|  |         ATVModInput m_atvModInput;          //!< Input source type
 | ||||||
|  |         Real        m_uniformLevel;         //!< Percentage between black and white for uniform screen display
 | ||||||
|  | 
 | ||||||
|  |         Config() : | ||||||
|  |             m_outputSampleRate(-1), | ||||||
|  |             m_inputFrequencyOffset(0), | ||||||
|  |             m_rfBandwidth(0), | ||||||
|  |             m_atvStd(ATVStdPAL625), | ||||||
|  |             m_atvModInput(ATVModInputBarChart), | ||||||
|  |             m_uniformLevel(0.5f) | ||||||
|  |         { } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Config m_config; | ||||||
|  |     Config m_running; | ||||||
|  | 
 | ||||||
|  |     NCO m_carrierNco; | ||||||
|  |     Complex m_modSample; | ||||||
|  |     Interpolator m_interpolator; | ||||||
|  |     Real m_interpolatorDistance; | ||||||
|  |     Real m_interpolatorDistanceRemain; | ||||||
|  |     int      m_tvSampleRate;     //!< sample rate for generating signal
 | ||||||
|  |     uint32_t m_pointsPerSync;    //!< number of line points for the horizontal sync
 | ||||||
|  |     uint32_t m_pointsPerBP;      //!< number of line points for the back porch
 | ||||||
|  |     uint32_t m_pointsPerLine;    //!< number of line points for the image line
 | ||||||
|  |     uint32_t m_pointsPerFP;      //!< number of line points for the front porch
 | ||||||
|  |     uint32_t m_pointsPerFSync;   //!< number of line points for the field first sync
 | ||||||
|  |     uint32_t m_pointsPerBar;     //!< number of line points for a bar of the bar chart
 | ||||||
|  |     uint32_t m_pointsPerTU;      //!< number of line points per time unit
 | ||||||
|  |     uint32_t m_nbLines;          //!< number of lines per complete frame
 | ||||||
|  |     uint32_t m_nbHorizPoints;    //!< number of line points per horizontal line
 | ||||||
|  |     bool     m_interlaced;       //!< true if image is interlaced (2 half frames per frame)
 | ||||||
|  |     bool     m_evenImage; | ||||||
|  |     QMutex   m_settingsMutex; | ||||||
|  |     int      m_horizontalCount;  //!< current point index on line
 | ||||||
|  |     int      m_lineCount;        //!< current line index in frame
 | ||||||
|  | 
 | ||||||
|  |     MovingAverage<Real> m_movingAverage; | ||||||
|  |     quint32 m_levelCalcCount; | ||||||
|  |     Real m_peakLevel; | ||||||
|  |     Real m_levelSum; | ||||||
|  | 
 | ||||||
|  |     static const float m_blackLevel; | ||||||
|  |     static const float m_spanLevel; | ||||||
|  |     static const int m_levelNbSamples; | ||||||
|  | 
 | ||||||
|  |     void apply(bool force = false); | ||||||
|  |     void pullFinalize(Complex& ci, Sample& sample); | ||||||
|  |     void pullVideo(Real& sample); | ||||||
|  |     void calculateLevel(Real& sample); | ||||||
|  |     void modulateSample(); | ||||||
|  |     void applyStandard(); | ||||||
|  | 
 | ||||||
|  |     inline void pullImageLine(Real& sample) | ||||||
|  |     { | ||||||
|  |         if (m_horizontalCount < m_pointsPerSync) // sync pulse
 | ||||||
|  |         { | ||||||
|  |             sample = 0.0f; // ultra-black
 | ||||||
|  |         } | ||||||
|  |         else if (m_horizontalCount < m_pointsPerSync + m_pointsPerBP) // back porch
 | ||||||
|  |         { | ||||||
|  |             sample = 0.3f; // black
 | ||||||
|  |         } | ||||||
|  |         else if (m_horizontalCount < m_pointsPerSync + m_pointsPerBP + m_pointsPerLine) | ||||||
|  |         { | ||||||
|  |             int pointIndex = m_horizontalCount - (m_pointsPerSync + m_pointsPerBP); | ||||||
|  | 
 | ||||||
|  |             switch(m_running.m_atvModInput) | ||||||
|  |             { | ||||||
|  |             case ATVModInputBarChart: | ||||||
|  |                 sample = (pointIndex / m_pointsPerBar) * (m_spanLevel/5.0f) + m_blackLevel; | ||||||
|  |                 break; | ||||||
|  |             case ATVModInputGradient: | ||||||
|  |                 sample = (pointIndex / m_pointsPerLine) * m_spanLevel + m_blackLevel; | ||||||
|  |                 break; | ||||||
|  |             case ATVModInputUniform: | ||||||
|  |             default: | ||||||
|  |                 sample = m_spanLevel * m_running.m_uniformLevel + m_blackLevel; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else // front porch
 | ||||||
|  |         { | ||||||
|  |             sample = m_blackLevel; // black
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     inline void pullVSyncLine(int pointIndex, Real& sample) | ||||||
|  |     { | ||||||
|  |         switch (m_lineCount) | ||||||
|  |         { | ||||||
|  |         case 0:   // __|__|
 | ||||||
|  |         case 1: | ||||||
|  |         case 313: | ||||||
|  |         case 314: | ||||||
|  |         { | ||||||
|  |             int halfIndex = m_horizontalCount % (m_nbHorizPoints/2); | ||||||
|  | 
 | ||||||
|  |             if (halfIndex < (m_nbHorizPoints/2) - m_pointsPerSync) // ultra-black
 | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else // black
 | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  |         case 2:   // __||XX
 | ||||||
|  |             if (m_horizontalCount < (m_nbHorizPoints/2) - m_pointsPerSync) | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else if (m_horizontalCount < (m_nbHorizPoints/2)) | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |             else if (m_horizontalCount < (m_nbHorizPoints/2) + m_pointsPerFSync) | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case 3:   // |XX|XX
 | ||||||
|  |         case 4: | ||||||
|  |         case 310: | ||||||
|  |         case 311: | ||||||
|  |         case 315: | ||||||
|  |         case 316: | ||||||
|  |         case 622: | ||||||
|  |         case 623: | ||||||
|  |         case 624: | ||||||
|  |         { | ||||||
|  |             int halfIndex = m_horizontalCount % (m_nbHorizPoints/2); | ||||||
|  | 
 | ||||||
|  |             if (halfIndex < m_pointsPerFSync) // ultra-black
 | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else // black
 | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  |         case 312: // |XX__|
 | ||||||
|  |             if (m_horizontalCount < m_pointsPerFSync) | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else if (m_horizontalCount < (m_nbHorizPoints/2)) | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |             else if (m_horizontalCount < m_nbHorizPoints - m_pointsPerSync) | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         default: // black images
 | ||||||
|  |             if (m_horizontalCount < m_pointsPerSync) | ||||||
|  |             { | ||||||
|  |                 sample = 0.0f; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 sample = m_blackLevel; | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* PLUGINS_CHANNELTX_MODATV_ATVMOD_H_ */ | ||||||
							
								
								
									
										339
									
								
								plugins/channeltx/modatv/atvmodgui.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								plugins/channeltx/modatv/atvmodgui.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,339 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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 <QDockWidget> | ||||||
|  | #include <QMainWindow> | ||||||
|  | #include <QFileDialog> | ||||||
|  | #include <QTime> | ||||||
|  | #include <QDebug> | ||||||
|  | 
 | ||||||
|  | #include "device/devicesinkapi.h" | ||||||
|  | #include "dsp/upchannelizer.h" | ||||||
|  | 
 | ||||||
|  | #include "dsp/threadedbasebandsamplesource.h" | ||||||
|  | #include "ui_atvmodgui.h" | ||||||
|  | #include "plugin/pluginapi.h" | ||||||
|  | #include "util/simpleserializer.h" | ||||||
|  | #include "util/db.h" | ||||||
|  | #include "gui/basicchannelsettingswidget.h" | ||||||
|  | #include "dsp/dspengine.h" | ||||||
|  | #include "mainwindow.h" | ||||||
|  | #include "atvmodgui.h" | ||||||
|  | 
 | ||||||
|  | const QString ATVModGUI::m_channelID = "sdrangel.channeltx.modatv"; | ||||||
|  | 
 | ||||||
|  | ATVModGUI* ATVModGUI::create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI) | ||||||
|  | { | ||||||
|  |     ATVModGUI* gui = new ATVModGUI(pluginAPI, deviceAPI); | ||||||
|  | 	return gui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::destroy() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::setName(const QString& name) | ||||||
|  | { | ||||||
|  | 	setObjectName(name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QString ATVModGUI::getName() const | ||||||
|  | { | ||||||
|  | 	return objectName(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | qint64 ATVModGUI::getCenterFrequency() const { | ||||||
|  | 	return m_channelMarker.getCenterFrequency(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::setCenterFrequency(qint64 centerFrequency) | ||||||
|  | { | ||||||
|  | 	m_channelMarker.setCenterFrequency(centerFrequency); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::resetToDefaults() | ||||||
|  | { | ||||||
|  | 	blockApplySettings(true); | ||||||
|  | 
 | ||||||
|  | 	ui->rfBW->setValue(12); | ||||||
|  | 	ui->uniformLevel->setValue(35); | ||||||
|  | 	ui->volume->setValue(10); | ||||||
|  | 	ui->standard->setCurrentIndex(0); | ||||||
|  | 	ui->inputSelect->setCurrentIndex(0); | ||||||
|  | 	ui->deltaFrequency->setValue(0); | ||||||
|  | 
 | ||||||
|  | 	blockApplySettings(false); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QByteArray ATVModGUI::serialize() const | ||||||
|  | { | ||||||
|  | 	SimpleSerializer s(1); | ||||||
|  | 
 | ||||||
|  | 	s.writeS32(1, m_channelMarker.getCenterFrequency()); | ||||||
|  | 	s.writeS32(2, ui->rfBW->value()); | ||||||
|  | 	s.writeS32(3, ui->uniformLevel->value()); | ||||||
|  | 	s.writeS32(4, ui->standard->currentIndex()); | ||||||
|  |     s.writeS32(5, ui->inputSelect->currentIndex()); | ||||||
|  | 	s.writeU32(6, m_channelMarker.getColor().rgb()); | ||||||
|  | 	s.writeS32(7, ui->volume->value()); | ||||||
|  | 
 | ||||||
|  | 	return s.final(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ATVModGUI::deserialize(const QByteArray& data) | ||||||
|  | { | ||||||
|  | 	SimpleDeserializer d(data); | ||||||
|  | 
 | ||||||
|  | 	if(!d.isValid()) | ||||||
|  |     { | ||||||
|  | 		resetToDefaults(); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if(d.getVersion() == 1) | ||||||
|  |     { | ||||||
|  | 		QByteArray bytetmp; | ||||||
|  | 		quint32 u32tmp; | ||||||
|  | 		qint32 tmp; | ||||||
|  | 
 | ||||||
|  | 		blockApplySettings(true); | ||||||
|  | 		m_channelMarker.blockSignals(true); | ||||||
|  | 
 | ||||||
|  | 		d.readS32(1, &tmp, 0); | ||||||
|  | 		m_channelMarker.setCenterFrequency(tmp); | ||||||
|  | 		d.readS32(2, &tmp, 4); | ||||||
|  | 		ui->rfBW->setValue(tmp); | ||||||
|  | 		d.readS32(3, &tmp, 100); | ||||||
|  | 		ui->uniformLevel->setValue(tmp); | ||||||
|  | 		d.readS32(4, &tmp, 0); | ||||||
|  | 		ui->standard->setCurrentIndex(tmp); | ||||||
|  |         d.readS32(5, &tmp, 0); | ||||||
|  |         ui->inputSelect->setCurrentIndex(tmp); | ||||||
|  | 
 | ||||||
|  |         if(d.readU32(6, &u32tmp)) | ||||||
|  |         { | ||||||
|  | 			m_channelMarker.setColor(u32tmp); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         d.readS32(7, &tmp, 10); | ||||||
|  |         ui->volume->setValue(tmp); | ||||||
|  | 
 | ||||||
|  |         blockApplySettings(false); | ||||||
|  | 		m_channelMarker.blockSignals(false); | ||||||
|  | 
 | ||||||
|  | 		applySettings(); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  | 		resetToDefaults(); | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ATVModGUI::handleMessage(const Message& message) | ||||||
|  | { | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::viewChanged() | ||||||
|  | { | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::handleSourceMessages() | ||||||
|  | { | ||||||
|  |     Message* message; | ||||||
|  | 
 | ||||||
|  |     while ((message = m_atvMod->getOutputMessageQueue()->pop()) != 0) | ||||||
|  |     { | ||||||
|  |         if (handleMessage(*message)) | ||||||
|  |         { | ||||||
|  |             delete message; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_deltaMinus_toggled(bool minus) | ||||||
|  | { | ||||||
|  | 	int deltaFrequency = m_channelMarker.getCenterFrequency(); | ||||||
|  | 	bool minusDelta = (deltaFrequency < 0); | ||||||
|  | 
 | ||||||
|  | 	if (minus ^ minusDelta) // sign change
 | ||||||
|  | 	{ | ||||||
|  | 		m_channelMarker.setCenterFrequency(-deltaFrequency); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_deltaFrequency_changed(quint64 value) | ||||||
|  | { | ||||||
|  | 	if (ui->deltaMinus->isChecked()) { | ||||||
|  | 		m_channelMarker.setCenterFrequency(-value); | ||||||
|  | 	} else { | ||||||
|  | 		m_channelMarker.setCenterFrequency(value); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_rfBW_valueChanged(int value) | ||||||
|  | { | ||||||
|  | 	ui->rfBWText->setText(QString("%1 MHz").arg(value / 10.0, 0, 'f', 1)); | ||||||
|  | 	m_channelMarker.setBandwidth(value * 100000); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_uniformLevel_valueChanged(int value) | ||||||
|  | { | ||||||
|  | 	ui->uniformLevelText->setText(QString("%1").arg(value)); | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_inputSelect_currentIndexChanged(int index) | ||||||
|  | { | ||||||
|  |     applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_volume_valueChanged(int value) | ||||||
|  | { | ||||||
|  |     ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); | ||||||
|  |     applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::on_channelMute_toggled(bool checked) | ||||||
|  | { | ||||||
|  | 	applySettings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::onWidgetRolled(QWidget* widget, bool rollDown) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::onMenuDoubleClicked() | ||||||
|  | { | ||||||
|  | 	if(!m_basicSettingsShown) { | ||||||
|  | 		m_basicSettingsShown = true; | ||||||
|  | 		BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_channelMarker, this); | ||||||
|  | 		bcsw->show(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ATVModGUI::ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent) : | ||||||
|  | 	RollupWidget(parent), | ||||||
|  | 	ui(new Ui::ATVModGUI), | ||||||
|  | 	m_pluginAPI(pluginAPI), | ||||||
|  | 	m_deviceAPI(deviceAPI), | ||||||
|  | 	m_channelMarker(this), | ||||||
|  | 	m_basicSettingsShown(false), | ||||||
|  | 	m_doApplySettings(true), | ||||||
|  | 	m_channelPowerDbAvg(20,0), | ||||||
|  |     m_recordLength(0), | ||||||
|  |     m_recordSampleRate(48000), | ||||||
|  |     m_samplesCount(0), | ||||||
|  |     m_tickCount(0), | ||||||
|  |     m_enableNavTime(false) | ||||||
|  | { | ||||||
|  | 	ui->setupUi(this); | ||||||
|  | 	setAttribute(Qt::WA_DeleteOnClose, true); | ||||||
|  | 	connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); | ||||||
|  | 	connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked())); | ||||||
|  | 
 | ||||||
|  | 	m_atvMod = new ATVMod(); | ||||||
|  | 	m_channelizer = new UpChannelizer(m_atvMod); | ||||||
|  | 	m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); | ||||||
|  | 	//m_pluginAPI->addThreadedSink(m_threadedChannelizer);
 | ||||||
|  |     m_deviceAPI->addThreadedSource(m_threadedChannelizer); | ||||||
|  | 
 | ||||||
|  | 	connect(&m_pluginAPI->getMainWindow()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); | ||||||
|  | 
 | ||||||
|  | 	ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold)); | ||||||
|  | 
 | ||||||
|  | 	//m_channelMarker = new ChannelMarker(this);
 | ||||||
|  | 	m_channelMarker.setColor(Qt::white); | ||||||
|  | 	m_channelMarker.setBandwidth(5000); | ||||||
|  | 	m_channelMarker.setCenterFrequency(0); | ||||||
|  | 	m_channelMarker.setVisible(true); | ||||||
|  | 
 | ||||||
|  | 	connect(&m_channelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); | ||||||
|  | 
 | ||||||
|  | 	m_deviceAPI->registerChannelInstance(m_channelID, this); | ||||||
|  |     m_deviceAPI->addChannelMarker(&m_channelMarker); | ||||||
|  |     m_deviceAPI->addRollupWidget(this); | ||||||
|  | 
 | ||||||
|  |     resetToDefaults(); | ||||||
|  | 
 | ||||||
|  | 	connect(m_atvMod->getOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); | ||||||
|  |     connect(m_atvMod, SIGNAL(levelChanged(qreal, qreal, int)), ui->volumeMeter, SLOT(levelChanged(qreal, qreal, int))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ATVModGUI::~ATVModGUI() | ||||||
|  | { | ||||||
|  |     m_deviceAPI->removeChannelInstance(this); | ||||||
|  | 	m_deviceAPI->removeThreadedSource(m_threadedChannelizer); | ||||||
|  | 	delete m_threadedChannelizer; | ||||||
|  | 	delete m_channelizer; | ||||||
|  | 	delete m_atvMod; | ||||||
|  | 	//delete m_channelMarker;
 | ||||||
|  | 	delete ui; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::blockApplySettings(bool block) | ||||||
|  | { | ||||||
|  |     m_doApplySettings = !block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::applySettings() | ||||||
|  | { | ||||||
|  | 	if (m_doApplySettings) | ||||||
|  | 	{ | ||||||
|  | 		setTitleColor(m_channelMarker.getColor()); | ||||||
|  | 
 | ||||||
|  | 		m_channelizer->configure(m_channelizer->getInputMessageQueue(), | ||||||
|  | 		    m_channelizer->getOutputSampleRate(), | ||||||
|  | 			m_channelMarker.getCenterFrequency()); | ||||||
|  | 
 | ||||||
|  | 		ui->deltaFrequency->setValue(abs(m_channelMarker.getCenterFrequency())); | ||||||
|  | 		ui->deltaMinus->setChecked(m_channelMarker.getCenterFrequency() < 0); | ||||||
|  | 
 | ||||||
|  | 		m_atvMod->configure(m_atvMod->getInputMessageQueue(), | ||||||
|  | 			ui->rfBW->value() * 100000.0f, | ||||||
|  | 			(ATVMod::ATVStd) ui->standard->currentIndex(), | ||||||
|  | 			(ATVMod::ATVModInput) ui->inputSelect->currentIndex(), | ||||||
|  | 			ui->uniformLevel->value() / 100.0f, | ||||||
|  | 			ui->channelMute->isChecked()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::leaveEvent(QEvent*) | ||||||
|  | { | ||||||
|  | 	blockApplySettings(true); | ||||||
|  | 	m_channelMarker.setHighlighted(false); | ||||||
|  | 	blockApplySettings(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::enterEvent(QEvent*) | ||||||
|  | { | ||||||
|  | 	blockApplySettings(true); | ||||||
|  | 	m_channelMarker.setHighlighted(true); | ||||||
|  | 	blockApplySettings(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModGUI::tick() | ||||||
|  | { | ||||||
|  | 	Real powDb = CalcDb::dbPower(m_atvMod->getMagSq()); | ||||||
|  | 	m_channelPowerDbAvg.feed(powDb); | ||||||
|  | 	ui->channelPower->setText(QString::number(m_channelPowerDbAvg.average(), 'f', 1)); | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								plugins/channeltx/modatv/atvmodgui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								plugins/channeltx/modatv/atvmodgui.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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/>.          //
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | #ifndef PLUGINS_CHANNELTX_MODTV_ATVMODGUI_H_ | ||||||
|  | #define PLUGINS_CHANNELTX_MODTV_ATVMODGUI_H_ | ||||||
|  | 
 | ||||||
|  | #include "gui/rollupwidget.h" | ||||||
|  | #include "plugin/plugingui.h" | ||||||
|  | #include "dsp/channelmarker.h" | ||||||
|  | #include "dsp/movingaverage.h" | ||||||
|  | #include "atvmod.h" | ||||||
|  | 
 | ||||||
|  | class PluginAPI; | ||||||
|  | class DeviceSinkAPI; | ||||||
|  | 
 | ||||||
|  | class ThreadedBasebandSampleSource; | ||||||
|  | class UpChannelizer; | ||||||
|  | class ATVMod; | ||||||
|  | 
 | ||||||
|  | namespace Ui { | ||||||
|  |     class ATVModGUI; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ATVModGUI : public RollupWidget, public PluginGUI { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     static ATVModGUI* create(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI); | ||||||
|  |     void destroy(); | ||||||
|  | 
 | ||||||
|  |     void setName(const QString& name); | ||||||
|  |     QString getName() const; | ||||||
|  |     virtual qint64 getCenterFrequency() const; | ||||||
|  |     virtual void setCenterFrequency(qint64 centerFrequency); | ||||||
|  | 
 | ||||||
|  |     void resetToDefaults(); | ||||||
|  |     QByteArray serialize() const; | ||||||
|  |     bool deserialize(const QByteArray& data); | ||||||
|  | 
 | ||||||
|  |     virtual bool handleMessage(const Message& message); | ||||||
|  | 
 | ||||||
|  |     static const QString m_channelID; | ||||||
|  | 
 | ||||||
|  | private slots: | ||||||
|  |     void viewChanged(); | ||||||
|  |     void handleSourceMessages(); | ||||||
|  | 
 | ||||||
|  |     void on_deltaFrequency_changed(quint64 value); | ||||||
|  |     void on_deltaMinus_toggled(bool minus); | ||||||
|  |     void on_rfBW_valueChanged(int value); | ||||||
|  |     void on_uniformLevel_valueChanged(int value); | ||||||
|  |     void on_inputSelect_currentIndexChanged(int index); | ||||||
|  |     void on_volume_valueChanged(int value); | ||||||
|  |     void on_channelMute_toggled(bool checked); | ||||||
|  | 
 | ||||||
|  |     void onWidgetRolled(QWidget* widget, bool rollDown); | ||||||
|  |     void onMenuDoubleClicked(); | ||||||
|  | 
 | ||||||
|  |     void tick(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Ui::ATVModGUI* ui; | ||||||
|  |     PluginAPI* m_pluginAPI; | ||||||
|  |     DeviceSinkAPI* m_deviceAPI; | ||||||
|  |     ChannelMarker m_channelMarker; | ||||||
|  |     bool m_basicSettingsShown; | ||||||
|  |     bool m_doApplySettings; | ||||||
|  | 
 | ||||||
|  |     ThreadedBasebandSampleSource* m_threadedChannelizer; | ||||||
|  |     UpChannelizer* m_channelizer; | ||||||
|  |     ATVMod* m_atvMod; | ||||||
|  |     MovingAverage<Real> m_channelPowerDbAvg; | ||||||
|  | 
 | ||||||
|  |     QString m_fileName; | ||||||
|  |     quint32 m_recordLength; | ||||||
|  |     int m_recordSampleRate; | ||||||
|  |     int m_samplesCount; | ||||||
|  |     std::size_t m_tickCount; | ||||||
|  |     bool m_enableNavTime; | ||||||
|  | 
 | ||||||
|  |     explicit ATVModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* parent = NULL); | ||||||
|  |     virtual ~ATVModGUI(); | ||||||
|  | 
 | ||||||
|  |     void blockApplySettings(bool block); | ||||||
|  |     void applySettings(); | ||||||
|  | 
 | ||||||
|  |     void leaveEvent(QEvent*); | ||||||
|  |     void enterEvent(QEvent*); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif /* PLUGINS_CHANNELTX_MODAM_AMMODGUI_H_ */ | ||||||
							
								
								
									
										635
									
								
								plugins/channeltx/modatv/atvmodgui.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										635
									
								
								plugins/channeltx/modatv/atvmodgui.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,635 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>ATVModGUI</class> | ||||||
|  |  <widget class="RollupWidget" name="ATVModGUI"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>342</width> | ||||||
|  |     <height>363</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="sizePolicy"> | ||||||
|  |    <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> | ||||||
|  |     <horstretch>0</horstretch> | ||||||
|  |     <verstretch>0</verstretch> | ||||||
|  |    </sizepolicy> | ||||||
|  |   </property> | ||||||
|  |   <property name="minimumSize"> | ||||||
|  |    <size> | ||||||
|  |     <width>0</width> | ||||||
|  |     <height>0</height> | ||||||
|  |    </size> | ||||||
|  |   </property> | ||||||
|  |   <property name="font"> | ||||||
|  |    <font> | ||||||
|  |     <family>Sans Serif</family> | ||||||
|  |     <pointsize>9</pointsize> | ||||||
|  |    </font> | ||||||
|  |   </property> | ||||||
|  |   <property name="focusPolicy"> | ||||||
|  |    <enum>Qt::StrongFocus</enum> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>ATV Modulator</string> | ||||||
|  |   </property> | ||||||
|  |   <widget class="QWidget" name="settingsContainer" native="true"> | ||||||
|  |    <property name="geometry"> | ||||||
|  |     <rect> | ||||||
|  |      <x>10</x> | ||||||
|  |      <y>10</y> | ||||||
|  |      <width>320</width> | ||||||
|  |      <height>341</height> | ||||||
|  |     </rect> | ||||||
|  |    </property> | ||||||
|  |    <property name="minimumSize"> | ||||||
|  |     <size> | ||||||
|  |      <width>280</width> | ||||||
|  |      <height>0</height> | ||||||
|  |     </size> | ||||||
|  |    </property> | ||||||
|  |    <property name="windowTitle"> | ||||||
|  |     <string>Settings</string> | ||||||
|  |    </property> | ||||||
|  |    <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |     <property name="spacing"> | ||||||
|  |      <number>3</number> | ||||||
|  |     </property> | ||||||
|  |     <property name="leftMargin"> | ||||||
|  |      <number>2</number> | ||||||
|  |     </property> | ||||||
|  |     <property name="topMargin"> | ||||||
|  |      <number>2</number> | ||||||
|  |     </property> | ||||||
|  |     <property name="rightMargin"> | ||||||
|  |      <number>2</number> | ||||||
|  |     </property> | ||||||
|  |     <property name="bottomMargin"> | ||||||
|  |      <number>2</number> | ||||||
|  |     </property> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="deltaFreqPowLayout"> | ||||||
|  |       <item> | ||||||
|  |        <layout class="QHBoxLayout" name="deltaFrequencyLayout"> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QToolButton" name="deltaMinus"> | ||||||
|  |           <property name="toolTip"> | ||||||
|  |            <string>Frequency shift direction</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>...</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="icon"> | ||||||
|  |            <iconset> | ||||||
|  |             <selectedoff>:/plus.png</selectedoff> | ||||||
|  |             <selectedon>:/minus.png</selectedon> | ||||||
|  |            </iconset> | ||||||
|  |           </property> | ||||||
|  |           <property name="checkable"> | ||||||
|  |            <bool>true</bool> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="ValueDial" name="deltaFrequency" native="true"> | ||||||
|  |           <property name="sizePolicy"> | ||||||
|  |            <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> | ||||||
|  |             <horstretch>0</horstretch> | ||||||
|  |             <verstretch>0</verstretch> | ||||||
|  |            </sizepolicy> | ||||||
|  |           </property> | ||||||
|  |           <property name="minimumSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>32</width> | ||||||
|  |             <height>16</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="font"> | ||||||
|  |            <font> | ||||||
|  |             <family>Monospace</family> | ||||||
|  |             <pointsize>12</pointsize> | ||||||
|  |            </font> | ||||||
|  |           </property> | ||||||
|  |           <property name="cursor"> | ||||||
|  |            <cursorShape>SizeVerCursor</cursorShape> | ||||||
|  |           </property> | ||||||
|  |           <property name="focusPolicy"> | ||||||
|  |            <enum>Qt::StrongFocus</enum> | ||||||
|  |           </property> | ||||||
|  |           <property name="toolTip"> | ||||||
|  |            <string>Demod shift frequency from center in Hz</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QLabel" name="deltaUnits"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>Hz </string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <spacer name="horizontalSpacer"> | ||||||
|  |           <property name="orientation"> | ||||||
|  |            <enum>Qt::Horizontal</enum> | ||||||
|  |           </property> | ||||||
|  |           <property name="sizeHint" stdset="0"> | ||||||
|  |            <size> | ||||||
|  |             <width>40</width> | ||||||
|  |             <height>20</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |          </spacer> | ||||||
|  |         </item> | ||||||
|  |        </layout> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <layout class="QHBoxLayout" name="channelPowerLayout"> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QLabel" name="channelPower"> | ||||||
|  |           <property name="toolTip"> | ||||||
|  |            <string>Channel power</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="layoutDirection"> | ||||||
|  |            <enum>Qt::RightToLeft</enum> | ||||||
|  |           </property> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>0.0</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QLabel" name="channelPowerUnits"> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string> dB</string> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QToolButton" name="channelMute"> | ||||||
|  |           <property name="toolTip"> | ||||||
|  |            <string>Mute/Unmute channel</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string>...</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="icon"> | ||||||
|  |            <iconset resource="../../../sdrbase/resources/res.qrc"> | ||||||
|  |             <normaloff>:/txon.png</normaloff> | ||||||
|  |             <normalon>:/txoff.png</normalon>:/txon.png</iconset> | ||||||
|  |           </property> | ||||||
|  |           <property name="checkable"> | ||||||
|  |            <bool>true</bool> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |        </layout> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="rfBandwidthLayout"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QComboBox" name="standard"> | ||||||
|  |         <item> | ||||||
|  |          <property name="text"> | ||||||
|  |           <string>PAL625L</string> | ||||||
|  |          </property> | ||||||
|  |         </item> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="label"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>RFBW</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QSlider" name="rfBW"> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Demodulator (RF) bandwidth</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="minimum"> | ||||||
|  |          <number>1</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="maximum"> | ||||||
|  |          <number>100</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="pageStep"> | ||||||
|  |          <number>1</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="value"> | ||||||
|  |          <number>10</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="rfBWText"> | ||||||
|  |         <property name="minimumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>50</width> | ||||||
|  |           <height>0</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>1.0 MHz</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <widget class="Line" name="line_4"> | ||||||
|  |       <property name="orientation"> | ||||||
|  |        <enum>Qt::Horizontal</enum> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="volumeLayout"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="volLabel"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Vol</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QDial" name="volume"> | ||||||
|  |         <property name="maximumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>24</width> | ||||||
|  |           <height>24</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Audio input gain</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="maximum"> | ||||||
|  |          <number>20</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="pageStep"> | ||||||
|  |          <number>1</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="value"> | ||||||
|  |          <number>10</number> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="volumeText"> | ||||||
|  |         <property name="minimumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>25</width> | ||||||
|  |           <height>0</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Audio input gain value</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="statusTip"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>1.0</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="LevelMeterVU" name="volumeMeter" native="true"> | ||||||
|  |         <property name="sizePolicy"> | ||||||
|  |          <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> | ||||||
|  |           <horstretch>0</horstretch> | ||||||
|  |           <verstretch>0</verstretch> | ||||||
|  |          </sizepolicy> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Level (% full range) top trace: average, bottom trace: instantaneous peak, tip: peak hold</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="recordFileSelectLayout"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QComboBox" name="inputSelect"> | ||||||
|  |         <item> | ||||||
|  |          <property name="text"> | ||||||
|  |           <string>Uniform</string> | ||||||
|  |          </property> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <property name="text"> | ||||||
|  |           <string>H Bars</string> | ||||||
|  |          </property> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <property name="text"> | ||||||
|  |           <string>Gradient</string> | ||||||
|  |          </property> | ||||||
|  |         </item> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QDial" name="uniformLevel"> | ||||||
|  |         <property name="maximumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>24</width> | ||||||
|  |           <height>24</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Tone frequency</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="minimum"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="maximum"> | ||||||
|  |          <number>100</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="pageStep"> | ||||||
|  |          <number>1</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="value"> | ||||||
|  |          <number>50</number> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="uniformLevelText"> | ||||||
|  |         <property name="minimumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>24</width> | ||||||
|  |           <height>0</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Tone frequency (kHz)</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>50</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="Line" name="line_2"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Vertical</enum> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <spacer name="horizontalSpacer_3"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |         <property name="sizeHint" stdset="0"> | ||||||
|  |          <size> | ||||||
|  |           <width>40</width> | ||||||
|  |           <height>20</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |        </spacer> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <widget class="Line" name="line_3"> | ||||||
|  |       <property name="orientation"> | ||||||
|  |        <enum>Qt::Horizontal</enum> | ||||||
|  |       </property> | ||||||
|  |      </widget> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="fileNameLayout"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="recordFileText"> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>...</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="playControllLayout"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QPushButton" name="showFileDialog"> | ||||||
|  |         <property name="minimumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>24</width> | ||||||
|  |           <height>24</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="maximumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>24</width> | ||||||
|  |           <height>24</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Open record file (48 kHz 32 bit float LE mono)</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string/> | ||||||
|  |         </property> | ||||||
|  |         <property name="icon"> | ||||||
|  |          <iconset resource="../../../sdrbase/resources/res.qrc"> | ||||||
|  |           <normaloff>:/preset-load.png</normaloff>:/preset-load.png</iconset> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="ButtonSwitch" name="playLoop"> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Play record file in a loop</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>...</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="icon"> | ||||||
|  |          <iconset resource="../../../sdrbase/resources/res.qrc"> | ||||||
|  |           <normaloff>:/playloop.png</normaloff>:/playloop.png</iconset> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="ButtonSwitch" name="play"> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Record file play/pause</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>...</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="icon"> | ||||||
|  |          <iconset resource="../../../sdrbase/resources/res.qrc"> | ||||||
|  |           <normaloff>:/play.png</normaloff> | ||||||
|  |           <normalon>:/pause.png</normalon> | ||||||
|  |           <disabledoff>:/play.png</disabledoff> | ||||||
|  |           <disabledon>:/play.png</disabledon>:/play.png</iconset> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <spacer name="horizontalSpacer_2"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |         <property name="sizeHint" stdset="0"> | ||||||
|  |          <size> | ||||||
|  |           <width>40</width> | ||||||
|  |           <height>20</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |        </spacer> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="Line" name="linePlay1"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Vertical</enum> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="relTimeText"> | ||||||
|  |         <property name="enabled"> | ||||||
|  |          <bool>false</bool> | ||||||
|  |         </property> | ||||||
|  |         <property name="minimumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>90</width> | ||||||
|  |           <height>0</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Record time from start</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>00:00:00.000</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="Line" name="linePlay2"> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Vertical</enum> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="recordLengthText"> | ||||||
|  |         <property name="enabled"> | ||||||
|  |          <bool>false</bool> | ||||||
|  |         </property> | ||||||
|  |         <property name="minimumSize"> | ||||||
|  |          <size> | ||||||
|  |           <width>60</width> | ||||||
|  |           <height>0</height> | ||||||
|  |          </size> | ||||||
|  |         </property> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Total record time</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>00:00:00</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |     <item> | ||||||
|  |      <layout class="QHBoxLayout" name="horizontalLayout_nav"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QSlider" name="navTimeSlider"> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string>Record file time navigator</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="maximum"> | ||||||
|  |          <number>100</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="pageStep"> | ||||||
|  |          <number>1</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="orientation"> | ||||||
|  |          <enum>Qt::Horizontal</enum> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </item> | ||||||
|  |    </layout> | ||||||
|  |   </widget> | ||||||
|  |  </widget> | ||||||
|  |  <customwidgets> | ||||||
|  |   <customwidget> | ||||||
|  |    <class>RollupWidget</class> | ||||||
|  |    <extends>QWidget</extends> | ||||||
|  |    <header>gui/rollupwidget.h</header> | ||||||
|  |    <container>1</container> | ||||||
|  |   </customwidget> | ||||||
|  |   <customwidget> | ||||||
|  |    <class>ValueDial</class> | ||||||
|  |    <extends>QWidget</extends> | ||||||
|  |    <header>gui/valuedial.h</header> | ||||||
|  |    <container>1</container> | ||||||
|  |   </customwidget> | ||||||
|  |   <customwidget> | ||||||
|  |    <class>LevelMeterVU</class> | ||||||
|  |    <extends>QWidget</extends> | ||||||
|  |    <header>gui/levelmeter.h</header> | ||||||
|  |    <container>1</container> | ||||||
|  |   </customwidget> | ||||||
|  |   <customwidget> | ||||||
|  |    <class>ButtonSwitch</class> | ||||||
|  |    <extends>QToolButton</extends> | ||||||
|  |    <header>gui/buttonswitch.h</header> | ||||||
|  |   </customwidget> | ||||||
|  |  </customwidgets> | ||||||
|  |  <resources> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |   <include location="../../../sdrbase/resources/res.qrc"/> | ||||||
|  |  </resources> | ||||||
|  |  <connections/> | ||||||
|  | </ui> | ||||||
							
								
								
									
										68
									
								
								plugins/channeltx/modatv/atvmodplugin.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								plugins/channeltx/modatv/atvmodplugin.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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 <QtPlugin> | ||||||
|  | #include <QAction> | ||||||
|  | #include "plugin/pluginapi.h" | ||||||
|  | 
 | ||||||
|  | #include "atvmodgui.h" | ||||||
|  | #include "atvmodplugin.h" | ||||||
|  | 
 | ||||||
|  | const PluginDescriptor ATVModPlugin::m_pluginDescriptor = { | ||||||
|  |     QString("ATV Modulator"), | ||||||
|  |     QString("3.3.0"), | ||||||
|  |     QString("(c) Edouard Griffiths, F4EXB"), | ||||||
|  |     QString("https://github.com/f4exb/sdrangel"), | ||||||
|  |     true, | ||||||
|  |     QString("https://github.com/f4exb/sdrangel") | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ATVModPlugin::ATVModPlugin(QObject* parent) : | ||||||
|  |     QObject(parent) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const PluginDescriptor& ATVModPlugin::getPluginDescriptor() const | ||||||
|  | { | ||||||
|  |     return m_pluginDescriptor; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModPlugin::initPlugin(PluginAPI* pluginAPI) | ||||||
|  | { | ||||||
|  |     m_pluginAPI = pluginAPI; | ||||||
|  | 
 | ||||||
|  |     // register ATV modulator
 | ||||||
|  |     m_pluginAPI->registerTxChannel(ATVModGUI::m_channelID, this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PluginGUI* ATVModPlugin::createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI) | ||||||
|  | { | ||||||
|  |     if(channelName == ATVModGUI::m_channelID) | ||||||
|  |     { | ||||||
|  |         ATVModGUI* gui = ATVModGUI::create(m_pluginAPI, deviceAPI); | ||||||
|  |         return gui; | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ATVModPlugin::createInstanceModATV(DeviceSinkAPI *deviceAPI) | ||||||
|  | { | ||||||
|  |     ATVModGUI* gui = ATVModGUI::create(m_pluginAPI, deviceAPI); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										47
									
								
								plugins/channeltx/modatv/atvmodplugin.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								plugins/channeltx/modatv/atvmodplugin.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // 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/>.          //
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | #ifndef PLUGINS_CHANNELTX_MODATV_ATVMODPLUGIN_H_ | ||||||
|  | #define PLUGINS_CHANNELTX_MODATV_ATVMODPLUGIN_H_ | ||||||
|  | 
 | ||||||
|  | #include <QObject> | ||||||
|  | #include "plugin/plugininterface.h" | ||||||
|  | 
 | ||||||
|  | class DeviceSinkAPI; | ||||||
|  | 
 | ||||||
|  | class ATVModPlugin : public QObject, PluginInterface { | ||||||
|  |     Q_OBJECT | ||||||
|  |     Q_INTERFACES(PluginInterface) | ||||||
|  |     Q_PLUGIN_METADATA(IID "sdrangel.channeltx.atvmod") | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit ATVModPlugin(QObject* parent = NULL); | ||||||
|  | 
 | ||||||
|  |     const PluginDescriptor& getPluginDescriptor() const; | ||||||
|  |     void initPlugin(PluginAPI* pluginAPI); | ||||||
|  | 
 | ||||||
|  |     PluginGUI* createTxChannel(const QString& channelName, DeviceSinkAPI *deviceAPI); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     static const PluginDescriptor m_pluginDescriptor; | ||||||
|  | 
 | ||||||
|  |     PluginAPI* m_pluginAPI; | ||||||
|  | 
 | ||||||
|  | private slots: | ||||||
|  |     void createInstanceModATV(DeviceSinkAPI *deviceAPI); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif /* PLUGINS_CHANNELTX_MODATV_ATVMODPLUGIN_H_ */ | ||||||
							
								
								
									
										37
									
								
								plugins/channeltx/modatv/modatv.pro
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								plugins/channeltx/modatv/modatv.pro
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | #-------------------------------------------------------- | ||||||
|  | # | ||||||
|  | # Pro file for Android and Windows builds with Qt Creator | ||||||
|  | # | ||||||
|  | #-------------------------------------------------------- | ||||||
|  | 
 | ||||||
|  | TEMPLATE = lib | ||||||
|  | CONFIG += plugin | ||||||
|  | 
 | ||||||
|  | QT += core gui widgets multimedia | ||||||
|  | 
 | ||||||
|  | TARGET = modatv | ||||||
|  | 
 | ||||||
|  | DEFINES += USE_SSE2=1 | ||||||
|  | QMAKE_CXXFLAGS += -msse2 | ||||||
|  | DEFINES += USE_SSE4_1=1 | ||||||
|  | QMAKE_CXXFLAGS += -msse4.1 | ||||||
|  | 
 | ||||||
|  | INCLUDEPATH += $$PWD | ||||||
|  | INCLUDEPATH += ../../../sdrbase | ||||||
|  | 
 | ||||||
|  | CONFIG(Release):build_subdir = release | ||||||
|  | CONFIG(Debug):build_subdir = debug | ||||||
|  | 
 | ||||||
|  | SOURCES += atvmod.cpp\ | ||||||
|  | 	atvmodgui.cpp\ | ||||||
|  | 	atvmodplugin.cpp | ||||||
|  | 
 | ||||||
|  | HEADERS += atvmod.h\ | ||||||
|  | 	atvmodgui.h\ | ||||||
|  | 	atvmodplugin.h | ||||||
|  | 
 | ||||||
|  | FORMS += atvmodgui.ui | ||||||
|  | 
 | ||||||
|  | LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase | ||||||
|  | 
 | ||||||
|  | RESOURCES = ../../../sdrbase/resources/res.qrc | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user