mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	SDRdaemonFEC support: interim state (1). Compile except UI. Bumped version to 2.1.0 where necessary
This commit is contained in:
		
							parent
							
								
									4386e0fbab
								
							
						
					
					
						commit
						e418e68bd9
					
				| @ -240,6 +240,7 @@ set(sdrbase_HEADERS | ||||
| 	sdrbase/util/export.h | ||||
| 	sdrbase/util/message.h | ||||
| 	sdrbase/util/messagequeue.h | ||||
| 	sdrbase/util/movingaverage.h | ||||
| 	sdrbase/util/prettyprint.h | ||||
| 	sdrbase/util/syncmessenger.h | ||||
| 	sdrbase/util/samplesourceserializer.h | ||||
|  | ||||
							
								
								
									
										35
									
								
								cmake/Modules/FindCM256.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								cmake/Modules/FindCM256.cmake
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| INCLUDE(FindPkgConfig) | ||||
| PKG_CHECK_MODULES(PC_CM256 "libcm256") | ||||
| 
 | ||||
| FIND_PATH(CM256_INCLUDE_DIR | ||||
|     NAMES cm256/cm256.h | ||||
|     HINTS ${PC_CM256_INCLUDE_DIR} | ||||
|     ${CMAKE_INSTALL_PREFIX}/include | ||||
|     ${LIBCM256_INSTALL_PREFIX}/include | ||||
|     PATHS | ||||
|     /usr/local/include | ||||
|     /usr/include | ||||
| ) | ||||
| 
 | ||||
| FIND_LIBRARY(CM256_LIBRARIES | ||||
|     NAMES cm256 libcm256 | ||||
|     HINTS ${PC_CM256_LIBDIR} | ||||
|     ${CMAKE_INSTALL_PREFIX}/lib | ||||
|     ${CMAKE_INSTALL_PREFIX}/lib64 | ||||
|     PATHS | ||||
|     ${CM256_INCLUDE_DIR}/../lib | ||||
|     /usr/local/lib | ||||
|     /usr/lib | ||||
| ) | ||||
| 
 | ||||
| if(CM256_INCLUDE_DIR AND CM256_LIBRARIES) | ||||
|     set(CM256_FOUND TRUE CACHE INTERNAL "CM256 found") | ||||
|     message(STATUS "Found CM256: ${CM256_INCLUDE_DIR}, ${CM256_LIBRARIES}") | ||||
| else(CM256_INCLUDE_DIR AND CM256_LIBRARIES) | ||||
|     set(CM256_FOUND FALSE CACHE INTERNAL "CM256 found") | ||||
|     message(STATUS "CM256 not found") | ||||
| endif(CM256_INCLUDE_DIR AND CM256_LIBRARIES) | ||||
| 
 | ||||
| INCLUDE(FindPackageHandleStandardArgs) | ||||
| FIND_PACKAGE_HANDLE_STANDARD_ARGS(CM256 DEFAULT_MSG CM256_LIBRARIES CM256_INCLUDE_DIR) | ||||
| MARK_AS_ADVANCED(CM256_LIBRARIES CM256_INCLUDE_DIR) | ||||
| @ -47,7 +47,11 @@ endif(LIBUSB_FOUND AND LIBHACKRF_FOUND) | ||||
| #    add_subdirectory(sdrdaemon)     | ||||
| #endif(LIBNANOMSG_FOUND) | ||||
| 
 | ||||
| find_package(CM256) | ||||
| if(CM256_FOUND) | ||||
|     add_subdirectory(sdrdaemonfec) | ||||
| endif(CM256_FOUND) | ||||
| 
 | ||||
| add_subdirectory(filesource) | ||||
| add_subdirectory(sdrdaemon) | ||||
| add_subdirectory(sdrdaemonfec) | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| project(sdrdaemonfec) | ||||
| 
 | ||||
| find_package(LZ4) | ||||
| find_package(LibNANOMSG) | ||||
| 
 | ||||
| set(sdrdaemonfec_SOURCES | ||||
| @ -43,13 +42,13 @@ add_library(inputsdrdaemonfec SHARED | ||||
| ) | ||||
| 
 | ||||
| target_include_directories(inputsdrdaemonfec PUBLIC | ||||
|     ${LZ4_INCLUDE_DIRS} | ||||
|     ${CM256_INCLUDE_DIR} | ||||
|     ${LIBNANOMSG_INCLUDE_DIR} | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries(inputsdrdaemonfec | ||||
| 	${QT_LIBRARIES} | ||||
| 	${LZ4_LIBRARIES} | ||||
| 	${CM256_LIBRARIES} | ||||
| 	${LIBNANOMSG_LIBRARIES} | ||||
| 	sdrbase | ||||
| ) | ||||
|  | ||||
| @ -29,425 +29,268 @@ const int SDRdaemonFECBuffer::m_rawBufferLengthSeconds = 8; // should be even | ||||
| const int SDRdaemonFECBuffer::m_rawBufferMinNbFrames = 50; | ||||
| 
 | ||||
| SDRdaemonFECBuffer::SDRdaemonFECBuffer(uint32_t throttlems) : | ||||
| 	m_throttlemsNominal(throttlems), | ||||
| 	m_rawSize(0), | ||||
| 	m_rawBuffer(0), | ||||
| 	m_sampleRateStream(0), | ||||
| 	m_sampleRate(0), | ||||
| 	m_sampleBytes(2), | ||||
| 	m_sampleBits(12), | ||||
| 	m_sync(false), | ||||
| 	m_syncLock(false), | ||||
| 	m_lz4(false), | ||||
| 	m_nbBlocks(0), | ||||
| 	m_bytesInBlock(0), | ||||
| 	m_dataCRC(0), | ||||
| 	m_inCount(0), | ||||
| 	m_lz4InCount(0), | ||||
| 	m_lz4InSize(0), | ||||
| 	m_lz4InBuffer(0), | ||||
| 	m_lz4OutBuffer(0), | ||||
| 	m_frameSize(0), | ||||
| 	m_bufferLenSec(0.0), | ||||
| 	m_nbLz4Decodes(0), | ||||
| 	m_nbLz4SuccessfulDecodes(0), | ||||
| 	m_nbLz4CRCOK(0), | ||||
| 	m_nbLastLz4SuccessfulDecodes(0), | ||||
| 	m_nbLastLz4CRCOK(0), | ||||
| 	m_writeIndex(0), | ||||
| 	m_readIndex(0), | ||||
| 	m_readSize(0), | ||||
| 	m_readBuffer(0), | ||||
|     m_autoFollowRate(false), | ||||
|     m_autoCorrBuffer(false), | ||||
|     m_skewTest(false), | ||||
|     m_skewCorrection(false), | ||||
|     m_resetIndexes(false), | ||||
|     m_readCount(0), | ||||
|     m_writeCount(0), | ||||
|     m_nbCycles(0), | ||||
|     m_nbReads(0), | ||||
|     m_balCorrection(0), | ||||
|     m_balCorrLimit(0) | ||||
|         m_frameHead(0), | ||||
|         m_decoderSlotHead(nbDecoderSlots/2), | ||||
|         m_curNbBlocks(0), | ||||
|         m_curNbRecovery(0), | ||||
|         m_throttlemsNominal(throttlems), | ||||
|         m_readIndex(0), | ||||
|         m_readBuffer(0), | ||||
|         m_readSize(0), | ||||
|         m_bufferLenSec(0.0f) | ||||
| { | ||||
| 	m_currentMeta.init(); | ||||
| 	m_framesNbBytes = nbDecoderSlots * sizeof(BufferFrame) * m_iqSampleSize; | ||||
| 	m_wrDeltaEstimate = m_framesNbBytes / 2; | ||||
| } | ||||
| 
 | ||||
| SDRdaemonFECBuffer::~SDRdaemonFECBuffer() | ||||
| { | ||||
| 	if (m_rawBuffer) { | ||||
| 		delete[] m_rawBuffer; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_lz4InBuffer) { | ||||
| 		delete[] m_lz4InBuffer; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_lz4OutBuffer) { | ||||
| 		delete[] m_lz4OutBuffer; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_readBuffer) { | ||||
| 		delete[] m_readBuffer; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::updateBufferSize(uint32_t sampleRate) | ||||
| void SDRdaemonFECBuffer::initDecoderSlotsAddresses() | ||||
| { | ||||
| 	uint32_t rawSize = sampleRate * m_iqSampleSize * m_rawBufferLengthSeconds; // store worth of this seconds of samples at this sample rate
 | ||||
| 
 | ||||
| 	if ((m_frameSize > 0) && (rawSize / m_frameSize < m_rawBufferMinNbFrames)) | ||||
| 	{ | ||||
| 		rawSize = m_rawBufferMinNbFrames * m_frameSize; // ensure a minimal size of this times the write block size so that auto follow ups work fine
 | ||||
| 	} | ||||
| 
 | ||||
| 	if (rawSize != m_rawSize) | ||||
| 	{ | ||||
| 		m_rawSize = rawSize; | ||||
|         m_balCorrLimit = sampleRate / 50; // +/- 20 ms correction max per read
 | ||||
|         m_bufferLenSec = m_rawSize / (sampleRate * m_iqSampleSize); | ||||
| 
 | ||||
| 		if (m_rawBuffer) { | ||||
| 			delete[] m_rawBuffer; | ||||
| 		} | ||||
| 
 | ||||
| 		m_rawBuffer = new uint8_t[m_rawSize]; | ||||
|         resetIndexes(); | ||||
| 
 | ||||
| 		qDebug() << "SDRdaemonBuffer::updateBufferSize:" | ||||
| 			<< " sampleRate: " << sampleRate | ||||
| 			<< " m_frameSize: " << m_frameSize | ||||
| 			<< " m_rawSize: " << m_rawSize; | ||||
| 	} | ||||
|     for (int i = 0; i < nbDecoderSlots; i++) | ||||
|     { | ||||
|         for (int j = 0; j < nbOriginalBlocks - 1; j++) | ||||
|         { | ||||
|             m_decoderSlots[i].m_originalBlockPtrs[j] = &m_frames[i].m_blocks[j]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::updateLZ4Sizes(uint32_t frameSize) | ||||
| void SDRdaemonFECBuffer::initDecodeAllSlots() | ||||
| { | ||||
| 	uint32_t maxInputSize = LZ4_compressBound(frameSize); | ||||
| 
 | ||||
| 	if (m_lz4InBuffer) { | ||||
| 		delete[] m_lz4InBuffer; | ||||
| 	} | ||||
| 
 | ||||
| 	m_lz4InBuffer = new uint8_t[maxInputSize]; | ||||
| 
 | ||||
| 	if (m_lz4OutBuffer) { | ||||
| 		delete[] m_lz4OutBuffer; | ||||
| 	} | ||||
| 
 | ||||
| 	m_lz4OutBuffer = new uint8_t[frameSize]; | ||||
|     for (int i = 0; i < nbDecoderSlots; i++) | ||||
|     { | ||||
|         m_decoderSlots[i].m_blockCount = 0; | ||||
|         m_decoderSlots[i].m_recoveryCount = 0; | ||||
|         m_decoderSlots[i].m_decoded = false; | ||||
|         m_decoderSlots[i].m_blockZero.m_metaData.init(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::updateReadBufferSize(uint32_t length) | ||||
| void SDRdaemonFECBuffer::initReadIndex() | ||||
| { | ||||
| 	if (m_readBuffer) { | ||||
| 		delete[] m_readBuffer; | ||||
| 	} | ||||
| 
 | ||||
| 	m_readBuffer = new uint8_t[length]; | ||||
|     m_readIndex = ((m_decoderSlotHead + (nbDecoderSlots/2)) % nbDecoderSlots) * sizeof(BufferFrame); | ||||
|     m_wrDeltaEstimate = m_framesNbBytes / 2; | ||||
| } | ||||
| 
 | ||||
| bool SDRdaemonFECBuffer::readMeta(char *array, uint32_t length) | ||||
| void SDRdaemonFECBuffer::initDecodeSlot(int slotIndex) | ||||
| { | ||||
| 	assert(length >= sizeof(MetaData) + 8); | ||||
| 	MetaData *metaData = (MetaData *) array; | ||||
| 
 | ||||
| 	if (m_crc64.calculate_crc((uint8_t *) array, sizeof(MetaData) - 8) == metaData->m_crc) | ||||
| 	{ | ||||
| 		// sync condition:
 | ||||
| 		if (m_currentMeta.m_blockSize > 0) | ||||
| 		{ | ||||
| 			uint32_t nbBlocks = m_currentMeta.m_nbBytes / m_currentMeta.m_blockSize; | ||||
| 			m_syncLock = nbBlocks + (m_lz4 ? 2 : 1) == m_nbBlocks; | ||||
| 			//qDebug("SDRdaemonBuffer::readMeta: m_nbBlocks: %d:%d %s", nbBlocks, m_nbBlocks, (m_syncLock ? "locked" : "unlocked"));
 | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			m_syncLock = false; | ||||
| 		} | ||||
| 
 | ||||
| 		memcpy((void *) &m_dataCRC, (const void *) &array[sizeof(MetaData)], 8); | ||||
| 		m_nbBlocks = 0; | ||||
| 		m_inCount = 0; | ||||
| 
 | ||||
| 		if (!m_lz4 && !(m_currentMeta == *metaData)) | ||||
| 		{ | ||||
| 			printMeta(QString("SDRdaemonBuffer::readMeta"), metaData); | ||||
| 		} | ||||
| 
 | ||||
| 		m_currentMeta = *metaData; | ||||
| 
 | ||||
| 		// sanity checks
 | ||||
| 		if (metaData->m_blockSize == m_udpPayloadSize) // sent blocksize matches given blocksize
 | ||||
| 		{ | ||||
| 			m_sampleBytes = metaData->m_sampleBytes & 0x0F; | ||||
| 			uint32_t frameSize = m_iqSampleSize * metaData->m_nbSamples * metaData->m_nbBlocks; | ||||
| 			int sampleRate = metaData->m_sampleRate; | ||||
| 
 | ||||
|             if (sampleRate != m_sampleRateStream) // change of nominal stream sample rate
 | ||||
| 			{ | ||||
| 				updateBufferSize(sampleRate); | ||||
| 				m_sampleRateStream = sampleRate; | ||||
| 				m_sampleRate = sampleRate; | ||||
| 			} | ||||
| 
 | ||||
|             // auto skew rate compensation
 | ||||
|             if (m_autoFollowRate) | ||||
|             { | ||||
| 				if (m_skewCorrection) | ||||
| 				{ | ||||
| 					int64_t deltaRate = (m_writeCount - m_readCount) / (m_nbCycles * m_rawBufferLengthSeconds * m_iqSampleSize); | ||||
| 					m_sampleRate = ((m_sampleRate + deltaRate) / m_iqSampleSize) * m_iqSampleSize; // ensure it is a multiple of the I/Q sample size
 | ||||
| 					resetIndexes(); | ||||
| 				} | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|             	m_sampleRate = sampleRate; | ||||
|             } | ||||
| 
 | ||||
|             // Reset indexes if requested
 | ||||
|             if (m_resetIndexes) | ||||
|             { | ||||
|                 resetIndexes(); | ||||
|                 m_resetIndexes = false; | ||||
|             } | ||||
| 
 | ||||
| 			if (metaData->m_sampleBytes & 0x10) | ||||
| 			{ | ||||
| 				m_lz4 = true; | ||||
| 				m_lz4InSize = metaData->m_nbBytes; // compressed input size
 | ||||
| 				m_lz4InCount = 0; | ||||
| 
 | ||||
| 				if (frameSize != m_frameSize) | ||||
| 				{ | ||||
| 					updateLZ4Sizes(frameSize); | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				m_lz4 = false; | ||||
| 			} | ||||
| 
 | ||||
| 			if (frameSize != m_frameSize) { | ||||
| 				m_frameSize = frameSize; | ||||
| 				updateBufferSize(m_sampleRate); | ||||
| 			} | ||||
| 
 | ||||
| 			m_sync = true; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			m_sync = false; | ||||
| 		} | ||||
| 
 | ||||
| 		return m_sync; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
|     int pseudoWriteIndex = slotIndex * sizeof(BufferFrame); | ||||
|     m_wrDeltaEstimate = pseudoWriteIndex - m_readIndex; | ||||
|     // collect stats before voiding the slot
 | ||||
|     m_curNbBlocks = m_decoderSlots[slotIndex].m_blockCount; | ||||
|     m_curNbRecovery = m_decoderSlots[slotIndex].m_recoveryCount; | ||||
|     m_avgNbBlocks(m_curNbBlocks); | ||||
|     m_avgNbRecovery(m_curNbRecovery); | ||||
|     // void the slot
 | ||||
|     m_decoderSlots[slotIndex].m_blockCount = 0; | ||||
|     m_decoderSlots[slotIndex].m_recoveryCount = 0; | ||||
|     m_decoderSlots[slotIndex].m_decoded = false; | ||||
|     m_decoderSlots[slotIndex].m_blockZero.m_metaData.init(); | ||||
|     memset((void *) m_decoderSlots[slotIndex].m_blockZero.m_samples, 0, samplesPerBlockZero * sizeof(Sample)); | ||||
|     memset((void *) m_frames[slotIndex].m_blocks, 0, (nbOriginalBlocks - 1) * samplesPerBlock * sizeof(Sample)); | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::writeData(char *array, uint32_t length) | ||||
| { | ||||
| 	if ((m_sync) && (m_nbBlocks > 0)) | ||||
| 	{ | ||||
| 		if (m_lz4) | ||||
| 		{ | ||||
| 			writeDataLZ4(array, length); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			writeToRawBufferUncompressed(array, length); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|     assert(length == udpSize); | ||||
| 
 | ||||
| uint8_t *SDRdaemonFECBuffer::readData(int32_t length) | ||||
| { | ||||
|     // auto compensation calculations
 | ||||
|     if (m_skewTest && ((m_readIndex + length) > (m_rawSize / 2))) | ||||
|     bool dataAvailable = false; | ||||
|     SuperBlock *superBlock = (SuperBlock *) array; | ||||
|     int frameIndex = superBlock->header.frameIndex; | ||||
|     int decoderIndex = frameIndex % nbDecoderSlots; | ||||
| 
 | ||||
|     if (m_frameHead == -1) // initial state
 | ||||
|     { | ||||
|         // auto follow sample rate calculation
 | ||||
|         int dIndex = (m_readIndex - m_writeIndex > 0 ? m_readIndex - m_writeIndex : m_writeIndex - m_readIndex); // absolute delta
 | ||||
|         m_skewCorrection = (dIndex < m_rawSize / 10); // close by 10%
 | ||||
|         m_nbCycles++; | ||||
|         // auto R/W balance calculation
 | ||||
|         if ((m_nbReads > 5*m_rawBufferLengthSeconds) && m_autoCorrBuffer) | ||||
|         m_decoderSlotHead = decoderIndex; // new decoder slot head
 | ||||
|         m_frameHead = frameIndex; | ||||
|         initReadIndex(); // reset read index
 | ||||
|         initDecodeAllSlots(); // initialize all slots
 | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         int frameDelta = m_frameHead - frameIndex; | ||||
| 
 | ||||
|         if (frameDelta < 0) | ||||
|         { | ||||
|             int32_t dBytes; | ||||
|             int32_t dI = (m_rawSize / 2) - m_readIndex; // delta of read index to the middle of buffer (positive)
 | ||||
| 
 | ||||
|             if (m_readIndex > m_writeIndex) { // write leads
 | ||||
|                 dBytes = m_writeIndex + dI; // positive from start of buffer + delta read index
 | ||||
|             } else { // read leads
 | ||||
|                 dBytes = m_writeIndex - (int32_t) m_rawSize + dI; // negative from end of buffer minus delta read index
 | ||||
|             if (-frameDelta < nbDecoderSlots) // new frame head not too new
 | ||||
|             { | ||||
|                 m_decoderSlotHead = decoderIndex; // new decoder slot head
 | ||||
|                 m_frameHead = frameIndex; | ||||
|                 dataAvailable = true; | ||||
|                 initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
 | ||||
|             } | ||||
| 
 | ||||
|             m_balCorrection = (m_balCorrection / 4) + ((int32_t) dBytes / (int32_t) (m_nbReads * m_iqSampleSize)); // correction is in number of samples. Alpha = 0.25
 | ||||
| 
 | ||||
|             if (m_balCorrection < -m_balCorrLimit) { | ||||
|                 m_balCorrection = -m_balCorrLimit; | ||||
|             } else if (m_balCorrection > m_balCorrLimit) { | ||||
|                 m_balCorrection = m_balCorrLimit; | ||||
|             else if (-frameDelta <= sizeof(uint16_t) - nbDecoderSlots) // loss of sync start over
 | ||||
|             { | ||||
|                 m_decoderSlotHead = frameIndex % nbDecoderSlots; // new decoder slot head
 | ||||
|                 decoderIndex = m_decoderSlotHead; | ||||
|                 m_frameHead = frameIndex; | ||||
|                 initReadIndex(); // reset read index
 | ||||
|                 initDecodeAllSlots(); // re-initialize all slots
 | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_balCorrection = 0; | ||||
|             if (frameDelta > sizeof(uint16_t) - nbDecoderSlots) // new frame head not too new
 | ||||
|             { | ||||
|                 m_decoderSlotHead = decoderIndex; // new decoder slot head
 | ||||
|                 m_frameHead = frameIndex; | ||||
|                 dataAvailable = true; | ||||
|                 initDecodeSlot(decoderIndex); // collect stats and re-initialize current slot
 | ||||
|             } | ||||
|             else if (frameDelta >= nbDecoderSlots) // loss of sync start over
 | ||||
|             { | ||||
|                 m_decoderSlotHead = frameIndex % nbDecoderSlots; // new decoder slot head
 | ||||
|                 decoderIndex = m_decoderSlotHead; | ||||
|                 m_frameHead = frameIndex; | ||||
|                 initReadIndex(); // reset read index
 | ||||
|                 initDecodeAllSlots(); // re-initialize all slots
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         m_nbReads = 0; | ||||
|         // un-arm
 | ||||
|         m_skewTest = false; | ||||
|     } | ||||
| 
 | ||||
|     m_readCount += length; | ||||
|     m_nbReads++; | ||||
|     // decoderIndex should now be correctly set
 | ||||
| 
 | ||||
| 	if (m_readIndex + length < m_rawSize) | ||||
| 	{ | ||||
| 		uint32_t readIndex = m_readIndex; | ||||
| 		m_readIndex += length; | ||||
| 		return &m_rawBuffer[readIndex]; | ||||
| 	} | ||||
| 	else if (m_readIndex + length == m_rawSize) | ||||
| 	{ | ||||
| 		uint32_t readIndex = m_readIndex; | ||||
| 		m_readIndex = 0; | ||||
|         m_skewTest = true; // re-arm
 | ||||
| 		return &m_rawBuffer[readIndex]; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (length > m_readSize) | ||||
| 		{ | ||||
| 			updateReadBufferSize(length); | ||||
| 			m_readSize = length; | ||||
| 		} | ||||
|     int blockIndex = superBlock->header.blockIndex; | ||||
|     int blockHead = m_decoderSlots[decoderIndex].m_blockCount; | ||||
| 
 | ||||
| 		std::memcpy((void *) m_readBuffer, (const void *) &m_rawBuffer[m_readIndex], m_rawSize - m_readIndex); | ||||
| 		length -= m_rawSize - m_readIndex; | ||||
| 		std::memcpy((void *) &m_readBuffer[m_rawSize - m_readIndex], (const void *) m_rawBuffer, length); | ||||
| 		m_readIndex = length; | ||||
|         m_skewTest = true; // re-arm
 | ||||
|         return m_readBuffer; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::writeDataLZ4(const char *array, uint32_t length) | ||||
| { | ||||
|     if (m_lz4InCount + length < m_lz4InSize) | ||||
|     if (blockHead < nbOriginalBlocks) // not enough blocks to decode -> store data
 | ||||
|     { | ||||
|     	std::memcpy((void *) &m_lz4InBuffer[m_lz4InCount], (const void *) array, length); | ||||
|         m_lz4InCount += length; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         std::memcpy((void *) &m_lz4InBuffer[m_lz4InCount], (const void *) array, m_lz4InSize - m_lz4InCount); // copy rest of data in compressed Buffer
 | ||||
|         m_lz4InCount += length; | ||||
|     } | ||||
| 
 | ||||
|     if (m_lz4InCount >= m_lz4InSize) // full input compressed block retrieved
 | ||||
|     { | ||||
|         if (m_nbLz4Decodes == 100) | ||||
|         if (blockIndex == 0) // first block with meta
 | ||||
|         { | ||||
|             qDebug() << "SDRdaemonBuffer::writeAndReadLZ4:" | ||||
|                << " decoding: " << m_nbLz4CRCOK | ||||
|                << ":" << m_nbLz4SuccessfulDecodes | ||||
|                << "/" <<  m_nbLz4Decodes; | ||||
| 
 | ||||
|             m_nbLastLz4SuccessfulDecodes = m_nbLz4SuccessfulDecodes; | ||||
|             m_nbLastLz4CRCOK = m_nbLz4CRCOK; | ||||
|         	m_nbLz4Decodes = 0; | ||||
|         	m_nbLz4SuccessfulDecodes = 0; | ||||
|             m_nbLz4CRCOK = 0; | ||||
|             SuperBlockZero *superBlockZero = (SuperBlockZero *) array; | ||||
|             m_decoderSlots[decoderIndex].m_blockZero = superBlockZero->protectedBlock; | ||||
|             m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Block = (void *) &m_decoderSlots[decoderIndex].m_blockZero; | ||||
|             memcpy((void *) m_frames[decoderIndex].m_blockZero.m_samples, | ||||
|                     (const void *) m_decoderSlots[decoderIndex].m_blockZero.m_samples, | ||||
|                     samplesPerBlockZero * sizeof(Sample)); | ||||
|         } | ||||
|         else if (blockIndex < nbOriginalBlocks) // normal block
 | ||||
|         { | ||||
|             m_frames[decoderIndex].m_blocks[blockIndex - 1] = superBlock->protectedBlock; | ||||
|             m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Block = (void *) &m_frames[decoderIndex].m_blocks[blockIndex - 1]; | ||||
|         } | ||||
|         else // redundancy block
 | ||||
|         { | ||||
|             m_decoderSlots[decoderIndex].m_recoveryBlocks[m_decoderSlots[decoderIndex].m_recoveryCount] = superBlock->protectedBlock; | ||||
|             m_decoderSlots[decoderIndex].m_recoveryCount++; | ||||
|         } | ||||
| 
 | ||||
|         writeToRawBufferLZ4(); | ||||
| 		m_lz4InCount = 0; | ||||
|         m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[blockHead].Index = blockIndex; | ||||
|         m_decoderSlots[decoderIndex].m_blockCount++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::writeToRawBufferLZ4() | ||||
| { | ||||
|     uint64_t crc64 = m_crc64.calculate_crc(m_lz4InBuffer, m_lz4InSize); | ||||
| 
 | ||||
|     if (memcmp(&crc64, &m_dataCRC, 8) == 0) | ||||
|     else if (!m_decoderSlots[decoderIndex].m_decoded) // ready to decode
 | ||||
|     { | ||||
|         m_nbLz4CRCOK++; | ||||
|         if (m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks < 0) // block zero has not been received
 | ||||
|         { | ||||
|             m_paramsCM256.RecoveryCount = m_currentMeta.m_nbFECBlocks; // take last value for number of FEC blocks
 | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_paramsCM256.RecoveryCount = m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks; | ||||
|         } | ||||
| 
 | ||||
|         if (m_decoderSlots[decoderIndex].m_recoveryCount > 0) // recovery data used
 | ||||
|         { | ||||
|             if (cm256_decode(m_paramsCM256, m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks)) // failure to decode
 | ||||
|             { | ||||
|                 qDebug("SDRdaemonFECBuffer::writeAndRead: CM256 decode error"); | ||||
|             } | ||||
|             else // success to decode
 | ||||
|             { | ||||
|                 int nbOriginalBlocks = m_decoderSlots[decoderIndex].m_blockCount - m_decoderSlots[decoderIndex].m_recoveryCount; | ||||
| 
 | ||||
|                 for (int ir = 0; ir < m_decoderSlots[decoderIndex].m_recoveryCount; ir++) // recover lost blocks
 | ||||
|                 { | ||||
|                     int blockIndex = m_decoderSlots[decoderIndex].m_cm256DescriptorBlocks[nbOriginalBlocks+ir].Index; | ||||
| 
 | ||||
|                     if (blockIndex == 0) | ||||
|                     { | ||||
|                         ProtectedBlockZero *recoveredBlockZero = (ProtectedBlockZero *) &m_decoderSlots[decoderIndex].m_recoveryBlocks[ir]; | ||||
|                         m_decoderSlots[decoderIndex].m_blockZero.m_metaData = recoveredBlockZero->m_metaData; | ||||
|                         memcpy((void *) m_frames[decoderIndex].m_blockZero.m_samples, | ||||
|                                 (const void *) recoveredBlockZero->m_samples, | ||||
|                                 samplesPerBlockZero * sizeof(Sample)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         m_frames[decoderIndex].m_blocks[blockIndex - 1] =  m_decoderSlots[decoderIndex].m_recoveryBlocks[ir]; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_nbFECBlocks >= 0) // meta data valid
 | ||||
|         { | ||||
|             if (!(m_decoderSlots[decoderIndex].m_blockZero.m_metaData == m_currentMeta)) | ||||
|             { | ||||
|                 int sampleRate =  m_decoderSlots[decoderIndex].m_blockZero.m_metaData.m_sampleRate; | ||||
| 
 | ||||
|                 if (sampleRate > 0) { | ||||
|                     m_bufferLenSec = (float) m_framesNbBytes / (float) sampleRate; | ||||
|                 } | ||||
| 
 | ||||
|                 printMeta("SDRdaemonFECBuffer::writeData", &m_decoderSlots[decoderIndex].m_blockZero.m_metaData); // print for change other than timestamp
 | ||||
|             } | ||||
| 
 | ||||
|             m_currentMeta = m_decoderSlots[decoderIndex].m_blockZero.m_metaData; // renew current meta
 | ||||
|         } | ||||
| 
 | ||||
|         m_decoderSlots[decoderIndex].m_decoded = true; | ||||
|     } | ||||
|     else | ||||
| } | ||||
| 
 | ||||
| uint8_t *SDRdaemonFECBuffer::readData(int32_t length) | ||||
| { | ||||
|     uint8_t *buffer = (uint8_t *) m_frames; | ||||
|     uint32_t readIndex = m_readIndex; | ||||
| 
 | ||||
|     if (m_readIndex + length < m_framesNbBytes) // ends before buffer bound
 | ||||
|     { | ||||
|     	return; | ||||
|         m_readIndex += length; | ||||
|         return &buffer[readIndex]; | ||||
|     } | ||||
|     else if (m_readIndex + length == m_framesNbBytes) // ends at buffer bound
 | ||||
|     { | ||||
|         m_readIndex = 0; | ||||
|         return &buffer[readIndex]; | ||||
|     } | ||||
|     else // ends after buffer bound
 | ||||
|     { | ||||
|         if (length > m_readSize) // reallocate composition buffer if necessary
 | ||||
|         { | ||||
|             if (m_readBuffer) { | ||||
|                 delete[] m_readBuffer; | ||||
|             } | ||||
| 
 | ||||
|     int compressedSize = LZ4_decompress_fast((const char*) m_lz4InBuffer, (char*) m_lz4OutBuffer, m_frameSize); | ||||
|     m_nbLz4Decodes++; | ||||
|             m_readBuffer = new uint8_t[length]; | ||||
|             m_readSize = length; | ||||
|         } | ||||
| 
 | ||||
|     if (compressedSize == m_lz4InSize) | ||||
| 	{ | ||||
|     	m_nbLz4SuccessfulDecodes++; | ||||
|     	writeToRawBufferUncompressed((const char *) m_lz4OutBuffer, m_frameSize); | ||||
| 	} | ||||
|         std::memcpy((void *) m_readBuffer, (const void *) &buffer[m_readIndex], m_framesNbBytes - m_readIndex); // copy end of buffer
 | ||||
|         length -= m_framesNbBytes - m_readIndex; | ||||
|         std::memcpy((void *) &m_readBuffer[m_framesNbBytes - m_readIndex], (const void *) buffer, length); // copy start of buffer
 | ||||
|         m_readIndex = length; | ||||
|         return m_readBuffer; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::writeToRawBufferUncompressed(const char *array, uint32_t length) | ||||
| { | ||||
| 	// TODO: handle the 1 byte per I or Q sample
 | ||||
| 	if (m_writeIndex + length < m_rawSize) | ||||
| 	{ | ||||
| 		std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, length); | ||||
| 		m_writeIndex += length; | ||||
| 	} | ||||
| 	else if (m_writeIndex + length == m_rawSize) | ||||
| 	{ | ||||
| 		std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, length); | ||||
| 		m_writeIndex = 0; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		std::memcpy((void *) &m_rawBuffer[m_writeIndex], (const void *) array, m_rawSize - m_writeIndex); | ||||
| 		length -= m_rawSize - m_writeIndex; | ||||
| 		std::memcpy((void *) m_rawBuffer, (const void *) &array[m_rawSize - m_writeIndex], length); | ||||
| 		m_writeIndex = length; | ||||
| 	} | ||||
| 
 | ||||
|     m_writeCount += length; | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::resetIndexes() | ||||
| { | ||||
|     m_writeIndex = 0; | ||||
|     m_readIndex = m_rawSize / 2; | ||||
|     m_readCount = 0; | ||||
|     m_writeCount = 0; | ||||
|     m_nbCycles = 0; | ||||
|     m_skewTest = false; | ||||
|     m_skewCorrection = false; | ||||
|     m_nbReads = 0; | ||||
|     m_balCorrection = 0; | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::updateBlockCounts(uint32_t nbBytesReceived) | ||||
| { | ||||
| 	m_nbBlocks += m_bytesInBlock + nbBytesReceived > m_udpPayloadSize ? 1 : 0; | ||||
| 	m_bytesInBlock = m_bytesInBlock + nbBytesReceived > m_udpPayloadSize ? nbBytesReceived : m_bytesInBlock + nbBytesReceived; | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECBuffer::printMeta(const QString& header, MetaData *metaData) | ||||
| void SDRdaemonFECBuffer::printMeta(const QString& header, MetaDataFEC *metaData) | ||||
| { | ||||
| 	qDebug() << header << ": " | ||||
| 		<< "|" << metaData->m_centerFrequency | ||||
| 		<< ":" << metaData->m_sampleRate | ||||
| 		<< ":" << (int) (metaData->m_sampleBytes & 0xF) | ||||
| 		<< ":" << (int) metaData->m_sampleBits | ||||
| 		<< ":" << metaData->m_blockSize | ||||
| 		<< ":" << metaData->m_nbSamples | ||||
| 		<< "||" << metaData->m_nbBlocks | ||||
| 		<< ":" << metaData->m_nbBytes | ||||
| 		<< "|" << metaData->m_tv_sec | ||||
| 		<< ":" << metaData->m_tv_usec; | ||||
|             << "|" << metaData->m_centerFrequency | ||||
|             << ":" << metaData->m_sampleRate | ||||
|             << ":" << (int) (metaData->m_sampleBytes & 0xF) | ||||
|             << ":" << (int) metaData->m_sampleBits | ||||
|             << ":" << (int) metaData->m_nbOriginalBlocks | ||||
|             << ":" << (int) metaData->m_nbFECBlocks | ||||
|             << "|" << metaData->m_tv_sec | ||||
|             << ":" << metaData->m_tv_usec | ||||
|             << "|"; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -19,70 +19,100 @@ | ||||
| 
 | ||||
| #include <QString> | ||||
| #include <cstdlib> | ||||
| #include "cm256.h" | ||||
| #include "util/movingaverage.h" | ||||
| 
 | ||||
| #include "util/CRC64.h" | ||||
| 
 | ||||
| #define SDRDAEMONFEC_UDPSIZE 512            // UDP payload size
 | ||||
| #define SDRDAEMONFEC_NBORIGINALBLOCKS 128   // number of sample blocks per frame excluding FEC blocks
 | ||||
| #define SDRDAEMONFEC_NBDECODERSLOTS 4       // power of two sub multiple of uint16_t size. A too large one is superfluous.
 | ||||
| 
 | ||||
| class SDRdaemonFECBuffer | ||||
| { | ||||
| public: | ||||
| public: | ||||
| #pragma pack(push, 1) | ||||
| 	struct MetaData | ||||
| 	{ | ||||
|         // critical data
 | ||||
| 		uint32_t m_centerFrequency;   //!< center frequency in kHz
 | ||||
| 		uint32_t m_sampleRate;        //!< sample rate in Hz
 | ||||
| 		uint8_t  m_sampleBytes;       //!< MSB(4): indicators, LSB(4) number of bytes per sample
 | ||||
| 		uint8_t  m_sampleBits;        //!< number of effective bits per sample
 | ||||
| 		uint16_t m_blockSize;         //!< payload size
 | ||||
| 		uint32_t m_nbSamples;         //!< number of samples in a hardware block
 | ||||
|         // end of critical data
 | ||||
| 		uint16_t m_nbBlocks;          //!< number of hardware blocks in the frame
 | ||||
| 		uint32_t m_nbBytes;           //!< total number of bytes in the frame
 | ||||
| 		uint32_t m_tv_sec;            //!< seconds of timestamp at start time of frame processing
 | ||||
| 		uint32_t m_tv_usec;           //!< microseconds of timestamp at start time of frame processing
 | ||||
| 		uint64_t m_crc;               //!< 64 bit CRC of the above
 | ||||
|     struct MetaDataFEC | ||||
|     { | ||||
|         uint32_t m_centerFrequency;   //!<  4 center frequency in kHz
 | ||||
|         uint32_t m_sampleRate;        //!<  8 sample rate in Hz
 | ||||
|         uint8_t  m_sampleBytes;       //!<  9 MSB(4): indicators, LSB(4) number of bytes per sample
 | ||||
|         uint8_t  m_sampleBits;        //!< 10 number of effective bits per sample
 | ||||
|         uint8_t  m_nbOriginalBlocks;  //!< 11 number of blocks with original (protected) data
 | ||||
|         uint8_t  m_nbFECBlocks;       //!< 12 number of blocks carrying FEC
 | ||||
|         uint32_t m_tv_sec;            //!< 16 seconds of timestamp at start time of super-frame processing
 | ||||
|         uint32_t m_tv_usec;           //!< 20 microseconds of timestamp at start time of super-frame processing
 | ||||
| 
 | ||||
| 		bool operator==(const MetaData& rhs) | ||||
| 		{ | ||||
| 		    return (memcmp((const void *) this, (const void *) &rhs, 20) == 0); // Only the 20 first bytes are relevant (critical)
 | ||||
| 		} | ||||
|         bool operator==(const MetaDataFEC& rhs) | ||||
|         { | ||||
|             return (memcmp((const void *) this, (const void *) &rhs, 12) == 0); // Only the 12 first bytes are relevant
 | ||||
|         } | ||||
| 
 | ||||
| 		void init() | ||||
| 		{ | ||||
| 			memset((void *) this, 0, sizeof(MetaData)); | ||||
| 		} | ||||
|         void init() | ||||
|         { | ||||
|             memset((void *) this, 0, sizeof(MetaDataFEC)); | ||||
|             m_nbFECBlocks = -1; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 		void operator=(const MetaData& rhs) | ||||
| 		{ | ||||
| 			memcpy((void *) this, (const void *) &rhs, sizeof(MetaData)); | ||||
| 		} | ||||
| 	}; | ||||
|     struct Sample | ||||
|     { | ||||
|         uint16_t i; | ||||
|         uint16_t q; | ||||
|     }; | ||||
| 
 | ||||
|     struct Header | ||||
|     { | ||||
|         uint16_t frameIndex; | ||||
|         uint8_t  blockIndex; | ||||
|         uint8_t  filler; | ||||
|     }; | ||||
| 
 | ||||
|     static const int samplesPerBlock = (SDRDAEMONFEC_UDPSIZE - sizeof(Header)) / sizeof(Sample); | ||||
|     static const int samplesPerBlockZero = samplesPerBlock - (sizeof(MetaDataFEC) / sizeof(Sample)); | ||||
| 
 | ||||
|     struct ProtectedBlock | ||||
|     { | ||||
|         Sample samples[samplesPerBlock]; | ||||
|     }; | ||||
| 
 | ||||
|     struct SuperBlock | ||||
|     { | ||||
|         Header         header; | ||||
|         ProtectedBlock protectedBlock; | ||||
|     }; | ||||
| 
 | ||||
|     struct ProtectedBlockZero | ||||
|     { | ||||
|         MetaDataFEC m_metaData; | ||||
|         Sample      m_samples[samplesPerBlockZero]; | ||||
|     }; | ||||
| 
 | ||||
|     struct SuperBlockZero | ||||
|     { | ||||
|         Header             header; | ||||
|         ProtectedBlockZero protectedBlock; | ||||
|     }; | ||||
| #pragma pack(pop) | ||||
| 
 | ||||
| 	SDRdaemonFECBuffer(uint32_t throttlems); | ||||
| 	~SDRdaemonFECBuffer(); | ||||
| 
 | ||||
| 	bool readMeta(char *array, uint32_t length);  //!< Attempt to read meta. Returns true if meta block
 | ||||
| 	// R/W operations
 | ||||
| 	void writeData(char *array, uint32_t length); //!< Write data into buffer.
 | ||||
| 	uint8_t *readData(int32_t length); | ||||
| 	void updateBlockCounts(uint32_t nbBytesReceived); | ||||
| 	uint8_t *readData(int32_t length);            //!< Read data from buffer
 | ||||
| 
 | ||||
| 	const MetaData& getCurrentMeta() const { return m_currentMeta; } | ||||
| 	uint32_t getSampleRateStream() const { return m_sampleRateStream; } | ||||
| 	uint32_t getSampleRate() const { return m_sampleRate; } | ||||
| 	bool isSync() const { return m_sync; } | ||||
| 	bool isSyncLocked() const { return m_syncLock; } | ||||
| 	uint32_t getFrameSize() const { return m_frameSize; } | ||||
| 	bool isLz4Compressed() const { return m_lz4; } | ||||
| 	float getCompressionRatio() const { return (m_frameSize > 0 ? (float) m_lz4InSize / (float) m_frameSize : 1.0); } | ||||
| 	uint32_t getLz4DataCRCOK() const { return m_nbLastLz4CRCOK; } | ||||
| 	uint32_t getLz4SuccessfulDecodes() const { return m_nbLastLz4SuccessfulDecodes; } | ||||
| 	float getBufferLengthInSecs() const { return m_bufferLenSec; } | ||||
| 	void setAutoFollowRate(bool autoFollowRate) { m_autoFollowRate = autoFollowRate; } | ||||
|     void setAutoCorrBuffer(bool autoCorrBuffer) { m_autoCorrBuffer = autoCorrBuffer; } | ||||
|     void setResetIndexes() { m_resetIndexes = true; } | ||||
|     int32_t getRWBalanceCorrection() const { return m_balCorrection; } | ||||
| 	// meta data
 | ||||
| 	const MetaDataFEC& getCurrentMeta() const { return m_currentMeta; } | ||||
|     const MetaDataFEC& getOutputMeta() const { return m_outputMeta; } | ||||
| 
 | ||||
|     // stats
 | ||||
|     int getCurNbBlocks() const { return m_curNbBlocks; } | ||||
|     int getCurNbRecovery() const { return m_curNbRecovery; } | ||||
|     float getAvgNbBlocks() const { return m_avgNbBlocks; } | ||||
|     float getAvgNbRecovery() const { return m_avgNbRecovery; } | ||||
| 
 | ||||
| 
 | ||||
|     float getBufferLengthInSecs() const { return m_bufferLenSec; } | ||||
| 
 | ||||
|     /** Get buffer gauge value in % of buffer size ([-50:50])
 | ||||
|      *  [-50:0] : write leads or read lags | ||||
| @ -90,9 +120,9 @@ public: | ||||
|      */ | ||||
|     inline int32_t getBufferGauge() const | ||||
|     { | ||||
|         if (m_rawSize) | ||||
|         if (m_framesNbBytes) | ||||
|         { | ||||
|             int32_t val = ((m_writeIndex - m_readIndex) * 100) / (int32_t) m_rawSize; | ||||
|             int32_t val = (m_wrDeltaEstimate * 100) / (int32_t) m_framesNbBytes; | ||||
| 
 | ||||
|             if (val < -50) { | ||||
|                 return val + 100; // read leads (positive)
 | ||||
| @ -115,63 +145,61 @@ public: | ||||
| 	static const int m_rawBufferMinNbFrames; //!< Minimum number of frames for the length of buffer
 | ||||
| 
 | ||||
| private: | ||||
| 	void updateBufferSize(uint32_t sampleRate); | ||||
| 	void updateLZ4Sizes(uint32_t frameSize); | ||||
| 	void updateReadBufferSize(uint32_t length); | ||||
| 	void writeDataLZ4(const char *array, uint32_t length); | ||||
| 	void writeToRawBufferLZ4(); | ||||
| 	void writeToRawBufferUncompressed(const char *array, uint32_t length); | ||||
|     void resetIndexes(); | ||||
|     static const int udpSize = SDRDAEMONFEC_UDPSIZE; | ||||
|     static const int nbOriginalBlocks = SDRDAEMONFEC_NBORIGINALBLOCKS; | ||||
|     static const int nbDecoderSlots = SDRDAEMONFEC_NBDECODERSLOTS; | ||||
| 
 | ||||
|     static void printMeta(const QString& header, MetaData *metaData); | ||||
| #pragma pack(push, 1) | ||||
|     struct BufferBlockZero | ||||
|     { | ||||
|         Sample m_samples[samplesPerBlockZero]; | ||||
|     }; | ||||
| 
 | ||||
|     struct BufferFrame | ||||
|     { | ||||
|         BufferBlockZero m_blockZero; | ||||
|         ProtectedBlock  m_blocks[nbOriginalBlocks - 1]; | ||||
|     }; | ||||
| #pragma pack(pop) | ||||
| 
 | ||||
|     struct DecoderSlot | ||||
|     { | ||||
|         ProtectedBlockZero   m_blockZero; | ||||
|         ProtectedBlock*      m_originalBlockPtrs[nbOriginalBlocks]; | ||||
|         ProtectedBlock       m_recoveryBlocks[nbOriginalBlocks]; // max size
 | ||||
|         cm256_block          m_cm256DescriptorBlocks[nbOriginalBlocks]; | ||||
|         int                  m_blockCount; //!< total number of blocks received for this frame
 | ||||
|         int                  m_recoveryCount; //!< number of recovery blocks received
 | ||||
|         bool                 m_decoded; //!< true if decoded
 | ||||
|     }; | ||||
| 
 | ||||
|     MetaDataFEC m_currentMeta;                   //!< Stored current meta data
 | ||||
|     MetaDataFEC m_outputMeta;                    //!< Meta data corresponding to currently served frame
 | ||||
|     cm256_encoder_params m_paramsCM256;          //!< CM256 decoder parameters block
 | ||||
|     DecoderSlot          m_decoderSlots[nbDecoderSlots]; //!< CM256 decoding control/buffer slots
 | ||||
|     BufferFrame          m_frames[nbDecoderSlots];       //!< Samples buffer
 | ||||
|     int                  m_framesNbBytes;                //!< Number of bytes in samples buffer
 | ||||
|     int                  m_decoderSlotHead;      //!< index of the current head frame slot in decoding slots
 | ||||
|     int                  m_frameHead;            //!< index of the current head frame sent
 | ||||
|     int                  m_curNbBlocks;          //!< (stats) instantaneous number of blocks received
 | ||||
|     int                  m_curNbRecovery;        //!< (stats) instantaneous number of recovery blocks used
 | ||||
|     MovingAverage<int, int, 10> m_avgNbBlocks;   //!< (stats) average number of blocks received
 | ||||
|     MovingAverage<int, int, 10> m_avgNbRecovery; //!< (stats) average number of recovery blocks used
 | ||||
|     int                  m_readIndex;            //!< current byte read index in frames buffer
 | ||||
|     int                  m_wrDeltaEstimate;      //!< Sampled estimate of write to read indexes difference
 | ||||
| 
 | ||||
| 	uint32_t m_throttlemsNominal;  //!< Initial throttle in ms
 | ||||
| 	uint32_t m_rawSize;            //!< Size of the raw samples buffer in bytes
 | ||||
|     uint8_t  *m_rawBuffer;         //!< Buffer for raw samples obtained from UDP (I/Q not in a formal I/Q structure)
 | ||||
|     uint32_t m_sampleRateStream;   //!< Current sample rate from the stream meta data
 | ||||
|     uint32_t m_sampleRate;         //!< Current actual sample rate in Hz
 | ||||
| 	uint8_t  m_sampleBytes;        //!< Current number of bytes per I or Q sample
 | ||||
| 	uint8_t  m_sampleBits;         //!< Current number of effective bits per sample
 | ||||
|     uint8_t* m_readBuffer;         //!< Read buffer to hold samples when looping back to beginning of raw buffer
 | ||||
|     uint32_t m_readSize;           //!< Read buffer size
 | ||||
| 
 | ||||
| 	bool m_sync;             //!< Meta data acquired
 | ||||
| 	bool m_syncLock;         //!< Meta data expected (Stream synchronized)
 | ||||
| 	bool m_lz4;              //!< Stream is compressed with LZ4
 | ||||
| 	MetaData m_currentMeta;  //!< Stored current meta data
 | ||||
| 	CRC64 m_crc64;           //!< CRC64 calculator
 | ||||
|     uint32_t m_nbBlocks;     //!< Number of UDP blocks received in the current frame
 | ||||
|     uint32_t m_bytesInBlock; //!< Number of bytes received in the current UDP block
 | ||||
|     uint64_t m_dataCRC;      //!< CRC64 of the data block
 | ||||
| 	uint32_t m_inCount;      //!< Current position of uncompressed input
 | ||||
|     uint32_t m_lz4InCount;   //!< Current position in LZ4 input buffer
 | ||||
|     uint32_t m_lz4InSize;    //!< Size in bytes of the LZ4 input data
 | ||||
|     uint8_t *m_lz4InBuffer;  //!< Buffer for LZ4 compressed input
 | ||||
|     uint8_t *m_lz4OutBuffer; //!< Buffer for LZ4 uncompressed output
 | ||||
|     uint32_t m_frameSize;    //!< Size in bytes of one uncompressed frame
 | ||||
|     float    m_bufferLenSec; //!< Raw buffer length in seconds
 | ||||
|     uint32_t m_nbLz4Decodes; | ||||
|     uint32_t m_nbLz4SuccessfulDecodes; | ||||
|     uint32_t m_nbLz4CRCOK; | ||||
|     uint32_t m_nbLastLz4SuccessfulDecodes; | ||||
|     uint32_t m_nbLastLz4CRCOK; | ||||
|     float    m_bufferLenSec; | ||||
| 
 | ||||
| 	int32_t  m_writeIndex;   //!< Current write position in the raw samples buffer
 | ||||
| 	int32_t  m_readIndex;    //!< Current read position in the raw samples buffer
 | ||||
| 	uint32_t m_readSize;     //!< Read buffer size
 | ||||
| 	uint8_t  *m_readBuffer;  //!< Read buffer to hold samples when looping back to beginning of raw buffer
 | ||||
|     void initDecoderSlotsAddresses(); | ||||
|     void initDecodeAllSlots(); | ||||
|     void initReadIndex(); | ||||
|     void initDecodeSlot(int slotIndex); | ||||
| 
 | ||||
|     bool     m_autoFollowRate; //!< Auto follow stream sample rate else stick with meta data sample rate
 | ||||
|     bool     m_autoCorrBuffer; //!< Auto correct buffer read / write balance (attempt to ...)
 | ||||
|     bool     m_skewTest; | ||||
|     bool     m_skewCorrection; //!< Do a skew rate correction at next meta data reception
 | ||||
|     bool     m_resetIndexes;   //!< Do a reset indexes at next meta data reception
 | ||||
| 
 | ||||
|     int64_t  m_readCount;    //!< Number of bytes read for auto skew compensation
 | ||||
|     int64_t  m_writeCount;   //!< Number of bytes written for auto skew compensation
 | ||||
|     uint32_t m_nbCycles;     //!< Number of buffer cycles since start of auto skew compensation byte counting
 | ||||
| 
 | ||||
|     uint32_t m_nbReads;      //!< Number of buffer reads since start of auto R/W balance correction period
 | ||||
|     int32_t  m_balCorrection; //!< R/W balance correction in number of samples
 | ||||
|     int32_t  m_balCorrLimit; //!< Correction absolute value limit in number of samples
 | ||||
|     static void printMeta(const QString& header, MetaDataFEC *metaData); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -303,24 +303,24 @@ bool SDRdaemonFECGui::handleMessage(const Message& message) | ||||
| 		updateWithStreamData(); | ||||
| 		return true; | ||||
| 	} | ||||
| 	else if (SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming::match(message)) | ||||
| 	else if (SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming::match(message)) | ||||
| 	{ | ||||
| 		m_startingTimeStamp.tv_sec = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).get_tv_sec(); | ||||
| 		m_startingTimeStamp.tv_usec = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).get_tv_usec(); | ||||
| 		m_syncLocked = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getSyncLock(); | ||||
| 		m_frameSize = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getFrameSize(); | ||||
| 		m_lz4 = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4Compression(); | ||||
| 		m_startingTimeStamp.tv_sec = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).get_tv_sec(); | ||||
| 		m_startingTimeStamp.tv_usec = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).get_tv_usec(); | ||||
| 		m_syncLocked = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getSyncLock(); | ||||
| 		m_frameSize = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getFrameSize(); | ||||
| 		m_lz4 = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4Compression(); | ||||
| 
 | ||||
| 		if (m_lz4) { | ||||
| 			m_compressionRatio = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4CompressionRatio(); | ||||
| 			m_compressionRatio = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4CompressionRatio(); | ||||
| 		} else { | ||||
| 			m_compressionRatio = 1.0; | ||||
| 		} | ||||
| 
 | ||||
| 		m_nbLz4DataCRCOK = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4DataCRCOK(); | ||||
| 		m_nbLz4SuccessfulDecodes = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getLz4SuccessfulDecodes(); | ||||
| 		m_bufferLengthInSecs = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getBufferLengthInSecs(); | ||||
|         m_bufferGauge = ((SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming&)message).getBufferGauge(); | ||||
| 		m_nbLz4DataCRCOK = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4DataCRCOK(); | ||||
| 		m_nbLz4SuccessfulDecodes = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getLz4SuccessfulDecodes(); | ||||
| 		m_bufferLengthInSecs = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getBufferLengthInSecs(); | ||||
|         m_bufferGauge = ((SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming&)message).getBufferGauge(); | ||||
| 
 | ||||
| 		updateWithStreamTime(); | ||||
| 		return true; | ||||
|  | ||||
| @ -304,12 +304,12 @@ | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="streamLayout"> | ||||
|      <item> | ||||
|       <widget class="QToolButton" name="streamLocked"> | ||||
|       <widget class="QToolButton" name="framesComplete"> | ||||
|        <property name="enabled"> | ||||
|         <bool>false</bool> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Stream locked status i.e. synced with meta data</string> | ||||
|         <string>Frames complete</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>...</string> | ||||
| @ -320,58 +320,6 @@ | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="frameSizeText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Frame size in kB</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>0000</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream1"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="sampleRateStreamText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>65</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Nominal sample rate from stream meta data (kS/s)</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>00000.00</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream2"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="sampleRateText"> | ||||
|        <property name="minimumSize"> | ||||
| @ -392,33 +340,7 @@ | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream3"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="skewRateText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>50</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Sample rate skew from stream nominal rate (%)</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>-00.00</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream4"> | ||||
|       <widget class="Line" name="lineStream1"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
| @ -443,6 +365,74 @@ | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream2"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="curNbBlocksText"> | ||||
|        <property name="toolTip"> | ||||
|         <string>Total number of blocks retrieved per frame</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>128</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream3"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="avgNbBlocksText"> | ||||
|        <property name="toolTip"> | ||||
|         <string>Average number of blocks retrieved per frame</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>000.0</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream4"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="curNbRecoveryText"> | ||||
|        <property name="toolTip"> | ||||
|         <string>Number of recovery blocks used per frame</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>000</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineStream5"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="avgNbRecoveryText"> | ||||
|        <property name="toolTip"> | ||||
|         <string>Average number of recovery blocks used per frame</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>000.0</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="horizontalSpacer"> | ||||
|        <property name="orientation"> | ||||
| @ -465,177 +455,6 @@ | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="lz4Layout"> | ||||
|      <item> | ||||
|       <widget class="QToolButton" name="lz4Compressed"> | ||||
|        <property name="enabled"> | ||||
|         <bool>false</bool> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Stream is compressed with LZ4</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>...</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset resource="../../../sdrbase/resources/res.qrc"> | ||||
|          <normaloff>:/compressed.png</normaloff>:/compressed.png</iconset> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="compressionRatioText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>50</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Stream comopression ratio (compressed / original)</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>0.00</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineLz41"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="dataCRCOKText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Data CRC OK (%)</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>0</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineLz42"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="lz4DecodesOKText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>LZ4 successful decodes (%)</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>0</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="Line" name="lineLz43"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="bufferLenSecsText"> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>30</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Main buffer lenth in seconds</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>00.0</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </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> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="resetIndexes"> | ||||
|        <property name="maximumSize"> | ||||
|         <size> | ||||
|          <width>22</width> | ||||
|          <height>20</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="toolTip"> | ||||
|         <string>Reset buffer indexes</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string/> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset resource="../../../sdrbase/resources/res.qrc"> | ||||
|          <normaloff>:/recycle.png</normaloff>:/recycle.png</iconset> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="ButtonSwitch" name="autoCorrBuffer"> | ||||
|        <property name="toolTip"> | ||||
|         <string>Auto maintain buffer read / write balance</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>B</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="ButtonSwitch" name="autoFollowRate"> | ||||
|        <property name="toolTip"> | ||||
|         <string>Auto follow actual stream sample rate</string> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>R</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="Line" name="line"> | ||||
|      <property name="orientation"> | ||||
|  | ||||
| @ -37,7 +37,7 @@ MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgConfigureSDRdaemonResetIndexes, M | ||||
| MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgConfigureSDRdaemonStreamTiming, Message) | ||||
| MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonAcquisition, Message) | ||||
| MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonStreamData, Message) | ||||
| MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming, Message) | ||||
| MESSAGE_CLASS_DEFINITION(SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming, Message) | ||||
| 
 | ||||
| SDRdaemonFECInput::SDRdaemonFECInput(const QTimer& masterTimer, DeviceAPI *deviceAPI) : | ||||
|     m_deviceAPI(deviceAPI), | ||||
|  | ||||
| @ -200,77 +200,65 @@ public: | ||||
| 		{ } | ||||
| 	}; | ||||
| 
 | ||||
| 	class MsgReportSDRdaemonStreamTiming : public Message { | ||||
| 	class MsgReportSDRdaemonFECStreamTiming : public Message { | ||||
| 		MESSAGE_CLASS_DECLARATION | ||||
| 
 | ||||
| 	public: | ||||
| 		uint32_t get_tv_sec() const { return m_tv_sec; } | ||||
| 		uint32_t get_tv_usec() const { return m_tv_usec; } | ||||
| 		bool getSyncLock() const { return m_syncLock; } | ||||
| 		uint32_t getFrameSize() const { return m_frameSize; } | ||||
| 		float getBufferLengthInSecs() const { return m_bufferLenSec; } | ||||
| 		bool getLz4Compression() const { return m_lz4; } | ||||
| 		float getLz4CompressionRatio() const { return m_compressionRatio; } | ||||
| 		uint32_t getLz4DataCRCOK() const  { return m_nbLz4CRCOK; } | ||||
| 		uint32_t getLz4SuccessfulDecodes() const { return m_nbLz4SuccessfulDecodes; } | ||||
|         int32_t getBufferGauge() const { return m_bufferGauge; } | ||||
|         int getCurNbBlocks() const { return m_curNbBlocks; } | ||||
|         int getCurNbRecovery() const { return m_curNbRecovery; } | ||||
|         float getAvgNbBlocks() const { return m_avgNbBlocks; } | ||||
|         float getAvgNbRecovery() const { return m_avgNbRecovery; } | ||||
| 
 | ||||
| 		static MsgReportSDRdaemonStreamTiming* create(uint32_t tv_sec, | ||||
| 		static MsgReportSDRdaemonFECStreamTiming* create(uint32_t tv_sec, | ||||
| 				uint32_t tv_usec, | ||||
| 				bool syncLock, | ||||
| 				uint32_t frameSize, | ||||
| 				float bufferLenSec, | ||||
| 				bool lz4, | ||||
| 				float compressionRatio, | ||||
| 				uint32_t nbLz4CRCOK, | ||||
|                 uint32_t nbLz4SuccessfulDecodes, | ||||
|                 int32_t bufferGauge) | ||||
|                 int32_t bufferGauge, | ||||
|                 int curNbBlocks, | ||||
|                 int curNbRecovery, | ||||
|                 float avgNbBlocks, | ||||
|                 float avgNbRecovery) | ||||
| 		{ | ||||
| 			return new MsgReportSDRdaemonStreamTiming(tv_sec, | ||||
| 			return new MsgReportSDRdaemonFECStreamTiming(tv_sec, | ||||
| 					tv_usec, | ||||
| 					syncLock, | ||||
| 					frameSize, | ||||
| 					bufferLenSec, | ||||
| 					lz4, | ||||
| 					compressionRatio, | ||||
| 					nbLz4CRCOK, | ||||
|                     nbLz4SuccessfulDecodes, | ||||
|                     bufferGauge); | ||||
|                     bufferGauge, | ||||
|                     curNbBlocks, | ||||
|                     curNbRecovery, | ||||
|                     avgNbBlocks, | ||||
|                     avgNbRecovery); | ||||
| 		} | ||||
| 
 | ||||
| 	protected: | ||||
| 		uint32_t m_tv_sec; | ||||
| 		uint32_t m_tv_usec; | ||||
| 		bool m_syncLock; | ||||
| 		uint32_t m_frameSize; | ||||
| 		float m_bufferLenSec; | ||||
| 		bool m_lz4; | ||||
| 		float m_compressionRatio; | ||||
| 		uint32_t m_nbLz4CRCOK; | ||||
| 		uint32_t m_nbLz4SuccessfulDecodes; | ||||
|         int32_t m_bufferGauge; | ||||
| 		float    m_bufferLenSec; | ||||
|         int32_t  m_bufferGauge; | ||||
|         int      m_curNbBlocks; | ||||
|         int      m_curNbRecovery; | ||||
|         float    m_avgNbBlocks; | ||||
|         float    m_avgNbRecovery; | ||||
| 
 | ||||
| 		MsgReportSDRdaemonStreamTiming(uint32_t tv_sec, | ||||
| 		MsgReportSDRdaemonFECStreamTiming(uint32_t tv_sec, | ||||
| 				uint32_t tv_usec, | ||||
| 				bool syncLock, | ||||
| 				uint32_t frameSize, | ||||
| 				float bufferLenSec, | ||||
| 				bool lz4, | ||||
| 				float compressionRatio, | ||||
| 				uint32_t nbLz4CRCOK, | ||||
|                 uint32_t nbLz4SuccessfulDecodes, | ||||
|                 int32_t bufferGauge) : | ||||
|                 int32_t bufferGauge, | ||||
|                 int curNbBlocks, | ||||
|                 int curNbRecovery, | ||||
|                 float avgNbBlocks, | ||||
|                 float avgNbRecovery) : | ||||
| 			Message(), | ||||
| 			m_tv_sec(tv_sec), | ||||
| 			m_tv_usec(tv_usec), | ||||
| 			m_syncLock(syncLock), | ||||
| 			m_frameSize(frameSize), | ||||
| 			m_bufferLenSec(bufferLenSec), | ||||
| 			m_lz4(lz4), | ||||
| 			m_compressionRatio(compressionRatio), | ||||
| 			m_nbLz4CRCOK(nbLz4CRCOK), | ||||
|             m_nbLz4SuccessfulDecodes(nbLz4SuccessfulDecodes), | ||||
|             m_bufferGauge(bufferGauge) | ||||
|             m_bufferGauge(bufferGauge), | ||||
|             m_curNbBlocks(curNbBlocks), | ||||
|             m_curNbRecovery(curNbRecovery), | ||||
|             m_avgNbBlocks(avgNbBlocks), | ||||
|             m_avgNbRecovery(avgNbRecovery) | ||||
| 		{ } | ||||
| 	}; | ||||
| 
 | ||||
|  | ||||
| @ -26,7 +26,7 @@ | ||||
| 
 | ||||
| const PluginDescriptor SDRdaemonFECPlugin::m_pluginDescriptor = { | ||||
| 	QString("SDRdaemon input"), | ||||
| 	QString("2.0.0"), | ||||
| 	QString("2.1.0"), | ||||
| 	QString("(c) Edouard Griffiths, F4EXB"), | ||||
| 	QString("https://github.com/f4exb/sdrangel"), | ||||
| 	true, | ||||
|  | ||||
| @ -133,60 +133,16 @@ void SDRdaemonFECUDPHandler::dataReadyRead() | ||||
| 	{ | ||||
| 		qint64 pendingDataSize = m_dataSocket->pendingDatagramSize(); | ||||
| 		m_udpReadBytes = m_dataSocket->readDatagram(m_udpBuf, pendingDataSize, &m_remoteAddress, 0); | ||||
| 		processData(); | ||||
| 
 | ||||
| 		if (m_udpReadBytes == SDRdaemonFECBuffer::udpSize) { | ||||
| 		    processData(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECUDPHandler::processData() | ||||
| { | ||||
| 	if (m_udpReadBytes < 0) | ||||
| 	{ | ||||
| 		qDebug() << "SDRdaemonThread::processData: read failed"; | ||||
| 	} | ||||
| 	else if (m_udpReadBytes > 0) | ||||
| 	{ | ||||
| 		m_sdrDaemonBuffer.updateBlockCounts(m_udpReadBytes); | ||||
| 
 | ||||
| 		if (m_sdrDaemonBuffer.readMeta(m_udpBuf, m_udpReadBytes)) | ||||
| 		{ | ||||
| 			const SDRdaemonFECBuffer::MetaData& metaData =  m_sdrDaemonBuffer.getCurrentMeta(); | ||||
| 			bool change = false; | ||||
| 			m_tv_sec = metaData.m_tv_sec; | ||||
| 			m_tv_usec = metaData.m_tv_usec; | ||||
| 
 | ||||
| 			uint32_t sampleRate = m_sdrDaemonBuffer.getSampleRate(); | ||||
| 
 | ||||
| 			if (m_samplerate != sampleRate) | ||||
| 			{ | ||||
| 				setSamplerate(sampleRate); | ||||
| 				m_samplerate = sampleRate; | ||||
| 				change = true; | ||||
| 			} | ||||
| 
 | ||||
| 			if (m_centerFrequency != metaData.m_centerFrequency) | ||||
| 			{ | ||||
| 				m_centerFrequency = metaData.m_centerFrequency; | ||||
| 				change = true; | ||||
| 			} | ||||
| 
 | ||||
| 			if (change) | ||||
| 			{ | ||||
| 				DSPSignalNotification *notif = new DSPSignalNotification(m_samplerate, m_centerFrequency * 1000); // Frequency in Hz for the DSP engine
 | ||||
| 				m_deviceAPI->getDeviceInputMessageQueue()->push(notif); | ||||
| 				SDRdaemonFECInput::MsgReportSDRdaemonStreamData *report = SDRdaemonFECInput::MsgReportSDRdaemonStreamData::create( | ||||
| 					m_sdrDaemonBuffer.getSampleRateStream(), | ||||
| 					m_samplerate, | ||||
| 					m_centerFrequency * 1000, // Frequency in Hz for the GUI
 | ||||
| 					m_tv_sec, | ||||
| 					m_tv_usec); | ||||
| 				m_outputMessageQueueToGUI->push(report); | ||||
| 			} | ||||
| 		} | ||||
| 		else if (m_sdrDaemonBuffer.isSync()) | ||||
| 		{ | ||||
| 			m_sdrDaemonBuffer.writeData(m_udpBuf, m_udpReadBytes); | ||||
| 		} | ||||
| 	} | ||||
|     m_sdrDaemonBuffer.writeData(m_udpBuf, m_udpReadBytes); | ||||
| } | ||||
| 
 | ||||
| void SDRdaemonFECUDPHandler::setSamplerate(uint32_t samplerate) | ||||
| @ -223,15 +179,10 @@ void SDRdaemonFECUDPHandler::tick() | ||||
|     { | ||||
|         m_throttlems = throttlems; | ||||
|         m_readLengthSamples = (m_sdrDaemonBuffer.getSampleRate() * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000; | ||||
|         m_readLengthSamples += m_sdrDaemonBuffer.getRWBalanceCorrection(); | ||||
|         m_readLength = m_readLengthSamples * SDRdaemonFECBuffer::m_iqSampleSize; | ||||
|         m_throttleToggle = !m_throttleToggle; | ||||
|     } | ||||
| 
 | ||||
|     if (m_autoCorrBuffer) { | ||||
|     	m_readLengthSamples += m_sdrDaemonBuffer.getRWBalanceCorrection(); | ||||
|     } | ||||
| 
 | ||||
| 	// read samples directly feeding the SampleFifo (no callback)
 | ||||
|     m_sampleFifo->write(reinterpret_cast<quint8*>(m_sdrDaemonBuffer.readData(m_readLength)), m_readLength); | ||||
|     m_samplesCount += m_readLengthSamples; | ||||
| @ -243,17 +194,15 @@ void SDRdaemonFECUDPHandler::tick() | ||||
| 	else | ||||
| 	{ | ||||
| 		m_tickCount = 0; | ||||
| 		SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming *report = SDRdaemonFECInput::MsgReportSDRdaemonStreamTiming::create( | ||||
| 		SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming *report = SDRdaemonFECInput::MsgReportSDRdaemonFECStreamTiming::create( | ||||
| 			m_tv_sec, | ||||
| 			m_tv_usec, | ||||
| 			m_sdrDaemonBuffer.isSyncLocked(), | ||||
| 			m_sdrDaemonBuffer.getFrameSize(), | ||||
| 			m_sdrDaemonBuffer.getBufferLengthInSecs(), | ||||
| 			m_sdrDaemonBuffer.isLz4Compressed(), | ||||
| 			m_sdrDaemonBuffer.getCompressionRatio(), | ||||
| 			m_sdrDaemonBuffer.getLz4DataCRCOK(), | ||||
|             m_sdrDaemonBuffer.getLz4SuccessfulDecodes(), | ||||
|             m_sdrDaemonBuffer.getBufferGauge()); | ||||
|             m_sdrDaemonBuffer.getBufferGauge(), | ||||
|             m_sdrDaemonBuffer.getCurNbBlocks(), | ||||
|             m_sdrDaemonBuffer.getCurNbRecovery(), | ||||
|             m_sdrDaemonBuffer.getAvgNbBlocks(), | ||||
|             m_sdrDaemonBuffer.getAvgNbRecovery()); | ||||
| 		m_outputMessageQueueToGUI->push(report); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -84,7 +84,7 @@ | ||||
|    <item> | ||||
|     <widget class="QLabel" name="label_2"> | ||||
|      <property name="text"> | ||||
|       <string><html><head/><body><p>Version 2.0.0 - Copyright (C) 2015-2016 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a> This is a complete redesign from RTL-SDRangelove at <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/hexameron/rtl-sdrangelove</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in RTL-SDRangelove</p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string> | ||||
|       <string><html><head/><body><p>Version 2.1.0 - Copyright (C) 2015-2016 Edouard Griffiths, F4EXB. </p><p>Code at <a href="https://github.com/f4exb/sdrangel"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/f4exb/sdrangel</span></a> This is a complete redesign from RTL-SDRangelove at <a href="https://github.com/hexameron/rtl-sdrangelove"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/hexameron/rtl-sdrangelove</span></a></p><p>Many thanks to the original developers:</p><p>The osmocom developer team - especially horizon, Hoernchen &amp; tnt.</p><p>Christian Daniel from maintech GmbH.</p><p>John Greb (hexameron) for the contributions in RTL-SDRangelove</p><p>The following rules apply to the SDRangel main application and libsdrbase:<br/>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; either version 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/"><span style=" text-decoration: underline; color:#0000ff;">http://www.gnu.org/licenses/</span></a>.</p><p>For the license of installed plugins, look into the plugin list.</p></body></html></string> | ||||
|      </property> | ||||
|      <property name="wordWrap"> | ||||
|       <bool>true</bool> | ||||
|  | ||||
| @ -333,7 +333,7 @@ void MainWindow::savePresetSettings(Preset* preset, int tabIndex) | ||||
| void MainWindow::createStatusBar() | ||||
| { | ||||
|     QString qtVersionStr = QString("Qt %1 ").arg(QT_VERSION_STR); | ||||
|     m_showSystemWidget = new QLabel("SDRangel v2.0.0 " + qtVersionStr + QSysInfo::prettyProductName(), this); | ||||
|     m_showSystemWidget = new QLabel("SDRangel v2.1.0 " + qtVersionStr + QSysInfo::prettyProductName(), this); | ||||
|     statusBar()->addPermanentWidget(m_showSystemWidget); | ||||
| 
 | ||||
| 	m_dateTimeWidget = new QLabel(tr("Date"), this); | ||||
|  | ||||
							
								
								
									
										61
									
								
								sdrbase/util/movingaverage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								sdrbase/util/movingaverage.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| ///////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // SDRdaemon - send I/Q samples read from a SDR device over the network via UDP      //
 | ||||
| //             with FEC protection. GNUradio interface.                              //
 | ||||
| //                                                                                   //
 | ||||
| // http://stackoverflow.com/questions/10990618/calculate-rolling-moving-average-in-c //
 | ||||
| //                                                                                   //
 | ||||
| // Copyright (C) 2016 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 GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_ | ||||
| #define GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_ | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| template <typename T, typename Total, int N> | ||||
| class MovingAverage | ||||
| { | ||||
|   public: | ||||
|     MovingAverage() | ||||
|       : m_num_samples(0), m_total(0) | ||||
|     { } | ||||
| 
 | ||||
|     void operator()(T sample) | ||||
|     { | ||||
|         if (m_num_samples < N) | ||||
|         { | ||||
|             m_samples[m_num_samples++] = sample; | ||||
|             m_total += sample; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             T& oldest = m_samples[m_num_samples++ % N]; | ||||
|             m_total += sample - oldest; | ||||
|             oldest = sample; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     operator double() const { return m_num_samples > 0 ? m_total / std::min(m_num_samples, N) : 0.0d; } | ||||
|     operator float() const { return m_num_samples > 0 ? m_total / std::min(m_num_samples, N) : 0.0f; } | ||||
| 
 | ||||
|   private: | ||||
|     T m_samples[N]; | ||||
|     int m_num_samples; | ||||
|     Total m_total; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #endif /* GR_SDRDAEMONFEC_LIB_MOVINGAVERAGE_H_ */ | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user