Fixed jitter in M17 baseband signal generation

This commit is contained in:
Silvano Seva 2021-10-14 21:33:50 +02:00
parent ce10edfb47
commit a3b7b490d4
5 changed files with 87 additions and 28 deletions

View File

@ -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 */

View File

@ -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()

View File

@ -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);
}

View File

@ -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()

View File

@ -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();