From a3b7b490d4907347d4182e306fd1db8a196d6319 Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Thu, 14 Oct 2021 21:33:50 +0200 Subject: [PATCH] Fixed jitter in M17 baseband signal generation --- openrtx/include/protocols/M17/M17Modulator.h | 10 +++- openrtx/src/protocols/M17/M17Modulator.cpp | 37 +++++++++--- openrtx/src/protocols/M17/M17Transmitter.cpp | 3 +- platform/drivers/tones/toneGenerator_MDx.cpp | 59 ++++++++++++++------ platform/drivers/tones/toneGenerator_MDx.h | 6 +- 5 files changed, 87 insertions(+), 28 deletions(-) diff --git a/openrtx/include/protocols/M17/M17Modulator.h b/openrtx/include/protocols/M17/M17Modulator.h index d64947c3..737b0b89 100644 --- a/openrtx/include/protocols/M17/M17Modulator.h +++ b/openrtx/include/protocols/M17/M17Modulator.h @@ -57,13 +57,17 @@ public: /** * Generate and transmit the baseband signal obtained by 4FSK modulation of - * a given block of data. + * a given block of data. When called for the first time, this function + * starts baseband transmission. * * @param sync: synchronisation word to be prepended to data block. * @param data: data block to be transmitted. + * @param isLast: flag signalling that current block is the last one being + * transmitted. */ void send(const std::array< uint8_t, 2 >& sync, - const std::array< uint8_t, 46 >& data); + const std::array< uint8_t, 46 >& data, + const bool isLast = false); private: @@ -110,6 +114,8 @@ private: int16_t *baseband_buffer; ///< Buffer for baseband audio handling. dataBuffer_t *activeBuffer; ///< Half baseband buffer, in transmission. dataBuffer_t *idleBuffer; ///< Half baseband buffer, free for processing. + bool txRunning; ///< Transmission running. + bool stopTx; ///< Stop transmission request. }; #endif /* M17_MODULATOR_H */ diff --git a/openrtx/src/protocols/M17/M17Modulator.cpp b/openrtx/src/protocols/M17/M17Modulator.cpp index b40e5dcb..2da452a5 100644 --- a/openrtx/src/protocols/M17/M17Modulator.cpp +++ b/openrtx/src/protocols/M17/M17Modulator.cpp @@ -89,9 +89,10 @@ void M17Modulator::init() */ baseband_buffer = new int16_t[2 * M17_FRAME_SAMPLES]; - activeBuffer = new (baseband_buffer) dataBuffer_t; + idleBuffer = new (baseband_buffer) dataBuffer_t; int16_t *ptr = baseband_buffer + activeBuffer->size(); - idleBuffer = new (ptr) dataBuffer_t; + activeBuffer = new (ptr) dataBuffer_t; + txRunning = false; } void M17Modulator::terminate() @@ -103,7 +104,8 @@ void M17Modulator::terminate() } void M17Modulator::send(const std::array< uint8_t, 2 >& sync, - const std::array< uint8_t, 46 >& data) + const std::array< uint8_t, 46 >& data, + const bool isLast) { auto sync1 = byteToSymbols(sync[0]); auto sync2 = byteToSymbols(sync[1]); @@ -117,6 +119,9 @@ void M17Modulator::send(const std::array< uint8_t, 2 >& sync, it = std::copy(sym.begin(), sym.end(), it); } + // If last frame, signal stop of transmission + if(isLast) stopTx = true; + generateBaseband(); emitBaseband(); } @@ -149,11 +154,29 @@ void M17Modulator::emitBaseband() idleBuffer->at(i) = shifted_sample; } - toneGen_waitForStreamEnd(); - std::swap(idleBuffer, activeBuffer); + if(txRunning == false) + { + // First run, start transmission + toneGen_playAudioStream(reinterpret_cast< uint16_t *>(baseband_buffer), + 2*M17_FRAME_SAMPLES, M17_RTX_SAMPLE_RATE, true); + txRunning = true; + stopTx = false; + } + else + { + // Transmission is ongoing, syncronise with stream end before proceeding + toneGen_waitForStreamEnd(); - toneGen_playAudioStream(reinterpret_cast< uint16_t *>(activeBuffer->data()), - activeBuffer->size(), M17_RTX_SAMPLE_RATE); + // Check if transmission stop is requested + if(stopTx == true) + { + toneGen_stopAudioStream(); + stopTx = false; + txRunning = false; + } + } + + std::swap(idleBuffer, activeBuffer); } #elif defined(PLATFORM_LINUX) void M17Modulator::emitBaseband() diff --git a/openrtx/src/protocols/M17/M17Transmitter.cpp b/openrtx/src/protocols/M17/M17Transmitter.cpp index 0844eeb2..101123b0 100644 --- a/openrtx/src/protocols/M17/M17Transmitter.cpp +++ b/openrtx/src/protocols/M17/M17Transmitter.cpp @@ -120,5 +120,6 @@ void M17Transmitter::send(const payload_t& payload, const bool isLast) interleave(frame); decorrelate(frame); - modulator.send(DATA_SYNC_WORD, frame); + + modulator.send(DATA_SYNC_WORD, frame, isLast); } diff --git a/platform/drivers/tones/toneGenerator_MDx.cpp b/platform/drivers/tones/toneGenerator_MDx.cpp index c3a75b2c..5a448251 100644 --- a/platform/drivers/tones/toneGenerator_MDx.cpp +++ b/platform/drivers/tones/toneGenerator_MDx.cpp @@ -54,7 +54,8 @@ uint32_t beepTableIncr = 0; // "beep" sine table index increment per tick uint32_t beepTimerCount = 0; // Downcounter for timed "beep" uint8_t beepVolume = 0; // "beep" volume level -bool tonesLocked = false; // If true tone channel is in use by FSK/playback +bool tonesLocked = false; // If true tone channel is in use by FSK/playback +bool circularMode = false; // Circular mode enabled using namespace miosix; Thread *dmaWaiting = 0; @@ -98,19 +99,22 @@ void __attribute__((used)) TIM8_TRG_COM_TIM14_IRQHandler() */ void __attribute__((used)) DMA_Handler() { - DMA1->LIFCR |= DMA_LIFCR_CTCIF2 // Clear interrupt flags + DMA1->LIFCR |= DMA_LIFCR_CTCIF2 // Clear interrupt flags + | DMA_LIFCR_CHTIF2 | DMA_LIFCR_CTEIF2; - TIM7->CR1 = 0; // End of transfer, stop TIM7 - TIM3->CCER &= ~TIM_CCER_CC3E; // Turn off compare channel + if(circularMode == false) + { - RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA1EN; // Turn off DMA - RCC->APB1ENR &= ~RCC_APB1ENR_TIM7EN; // Turn off TIM7 - __DSB(); + TIM7->CR1 = 0; // End of transfer, stop TIM7 + TIM3->CCER &= ~TIM_CCER_CC3E; // Turn off compare channel + RCC->APB1ENR &= ~RCC_APB1ENR_TIM7EN; // Turn off TIM7 + __DSB(); - tonesLocked = false; // Finally, unlock tones + tonesLocked = false; // Finally, unlock tones + } - if(dmaWaiting == 0) return; // Wake up eventual pending threads + if(dmaWaiting == 0) return; // Wake up eventual pending threads dmaWaiting->IRQwakeup(); if(dmaWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) Scheduler::IRQfindNextThread(); @@ -251,7 +255,7 @@ void toneGen_encodeAFSK1200(const uint8_t* buf, const size_t len) } void toneGen_playAudioStream(const uint16_t* buf, const size_t len, - const uint32_t sampleRate) + const uint32_t sampleRate, const bool circMode) { if((buf == NULL) || (len == 0) || (sampleRate == 0)) return; @@ -270,7 +274,7 @@ void toneGen_playAudioStream(const uint16_t* buf, const size_t len, * Timebase for triggering of DMA transfers. * Bus frequency is 84MHz. */ - uint32_t ratio = (84000000/sampleRate) - 1; + uint32_t ratio = (84000000/sampleRate); TIM7->CNT = 0; TIM7->PSC = 0; @@ -291,9 +295,18 @@ void toneGen_playAudioStream(const uint16_t* buf, const size_t len, | DMA_SxCR_MINC // Increment source pointer | DMA_SxCR_DIR_0 // Memory to peripheral | DMA_SxCR_TCIE // Transfer complete interrupt - | DMA_SxCR_TEIE // Transfer error interrupt - | DMA_SxCR_EN; // Enable transfer + | DMA_SxCR_TEIE; // Transfer error interrupt + if(circMode) + { + DMA1_Stream2->CR |= DMA_SxCR_CIRC // Circular buffer mode + | DMA_SxCR_HTIE; // Half transfer interrupt + circularMode = true; + } + + DMA1_Stream2->CR |= DMA_SxCR_EN; // Enable transfer + + // Enable DMA interrupts NVIC_ClearPendingIRQ(DMA1_Stream2_IRQn); NVIC_SetPriority(DMA1_Stream2_IRQn, 10); NVIC_EnableIRQ(DMA1_Stream2_IRQn); @@ -346,13 +359,27 @@ void toneGen_stopAudioStream() TIM7->CR1 = 0; TIM3->CCER &= ~TIM_CCER_CC3E; - // Shut down TIM7 and DMA - RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA1EN; + // Stop DMA transfer and clear pending interrupt flags + DMA1_Stream2->CR = 0; + DMA1_Stream2->M0AR = 0; + DMA1->LIFCR |= DMA_LIFCR_CTCIF2 + | DMA_LIFCR_CHTIF2 + | DMA_LIFCR_CTEIF2; + + // Shut down TIM7 clock RCC->APB1ENR &= ~RCC_APB1ENR_TIM7EN; + __DSB(); // Unlock tones and wake up the thread waiting for completion tonesLocked = false; - if(dmaWaiting) dmaWaiting->IRQwakeup(); + if(dmaWaiting) + { + dmaWaiting->IRQwakeup(); + dmaWaiting = 0; + } + + // Clear flag for circular double buffered mode + circularMode = false; } bool toneGen_toneBusy() diff --git a/platform/drivers/tones/toneGenerator_MDx.h b/platform/drivers/tones/toneGenerator_MDx.h index 70670847..74ecf3f4 100644 --- a/platform/drivers/tones/toneGenerator_MDx.h +++ b/platform/drivers/tones/toneGenerator_MDx.h @@ -113,9 +113,11 @@ void toneGen_encodeAFSK1200(const uint8_t *buf, const size_t len); * @param buf: pointer to a buffer containing the audio samples. * @param len: length of the data buffer. * @param sampleRate: sample rate of the audio stream in samples per second. + * @param circMode: treat buf as a double circular buffer, continuously + * reproducing its content. */ void toneGen_playAudioStream(const uint16_t *buf, const size_t len, - const uint32_t sampleRate); + const uint32_t sampleRate, const bool circMode); /** * When called, this function blocks the execution flow until the reproduction @@ -128,7 +130,7 @@ bool toneGen_waitForStreamEnd(); /** * Interrupt the ongoing reproduction of an audio stream, also making the - * toneGen_playAudioStream return to the caller. + * toneGen_waitForStreamEnd return to the caller. */ void toneGen_stopAudioStream();