Fixed jitter in M17 baseband signal generation
This commit is contained in:
parent
ce10edfb47
commit
a3b7b490d4
|
|
@ -57,13 +57,17 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate and transmit the baseband signal obtained by 4FSK modulation of
|
* 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 sync: synchronisation word to be prepended to data block.
|
||||||
* @param data: data block to be transmitted.
|
* @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,
|
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:
|
private:
|
||||||
|
|
||||||
|
|
@ -110,6 +114,8 @@ private:
|
||||||
int16_t *baseband_buffer; ///< Buffer for baseband audio handling.
|
int16_t *baseband_buffer; ///< Buffer for baseband audio handling.
|
||||||
dataBuffer_t *activeBuffer; ///< Half baseband buffer, in transmission.
|
dataBuffer_t *activeBuffer; ///< Half baseband buffer, in transmission.
|
||||||
dataBuffer_t *idleBuffer; ///< Half baseband buffer, free for processing.
|
dataBuffer_t *idleBuffer; ///< Half baseband buffer, free for processing.
|
||||||
|
bool txRunning; ///< Transmission running.
|
||||||
|
bool stopTx; ///< Stop transmission request.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* M17_MODULATOR_H */
|
#endif /* M17_MODULATOR_H */
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,10 @@ void M17Modulator::init()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
baseband_buffer = new int16_t[2 * M17_FRAME_SAMPLES];
|
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();
|
int16_t *ptr = baseband_buffer + activeBuffer->size();
|
||||||
idleBuffer = new (ptr) dataBuffer_t;
|
activeBuffer = new (ptr) dataBuffer_t;
|
||||||
|
txRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void M17Modulator::terminate()
|
void M17Modulator::terminate()
|
||||||
|
|
@ -103,7 +104,8 @@ void M17Modulator::terminate()
|
||||||
}
|
}
|
||||||
|
|
||||||
void M17Modulator::send(const std::array< uint8_t, 2 >& sync,
|
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 sync1 = byteToSymbols(sync[0]);
|
||||||
auto sync2 = byteToSymbols(sync[1]);
|
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);
|
it = std::copy(sym.begin(), sym.end(), it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If last frame, signal stop of transmission
|
||||||
|
if(isLast) stopTx = true;
|
||||||
|
|
||||||
generateBaseband();
|
generateBaseband();
|
||||||
emitBaseband();
|
emitBaseband();
|
||||||
}
|
}
|
||||||
|
|
@ -149,11 +154,29 @@ void M17Modulator::emitBaseband()
|
||||||
idleBuffer->at(i) = shifted_sample;
|
idleBuffer->at(i) = shifted_sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
toneGen_waitForStreamEnd();
|
if(txRunning == false)
|
||||||
std::swap(idleBuffer, activeBuffer);
|
{
|
||||||
|
// 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()),
|
// Check if transmission stop is requested
|
||||||
activeBuffer->size(), M17_RTX_SAMPLE_RATE);
|
if(stopTx == true)
|
||||||
|
{
|
||||||
|
toneGen_stopAudioStream();
|
||||||
|
stopTx = false;
|
||||||
|
txRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::swap(idleBuffer, activeBuffer);
|
||||||
}
|
}
|
||||||
#elif defined(PLATFORM_LINUX)
|
#elif defined(PLATFORM_LINUX)
|
||||||
void M17Modulator::emitBaseband()
|
void M17Modulator::emitBaseband()
|
||||||
|
|
|
||||||
|
|
@ -120,5 +120,6 @@ void M17Transmitter::send(const payload_t& payload, const bool isLast)
|
||||||
|
|
||||||
interleave(frame);
|
interleave(frame);
|
||||||
decorrelate(frame);
|
decorrelate(frame);
|
||||||
modulator.send(DATA_SYNC_WORD, frame);
|
|
||||||
|
modulator.send(DATA_SYNC_WORD, frame, isLast);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ uint32_t beepTableIncr = 0; // "beep" sine table index increment per tick
|
||||||
uint32_t beepTimerCount = 0; // Downcounter for timed "beep"
|
uint32_t beepTimerCount = 0; // Downcounter for timed "beep"
|
||||||
uint8_t beepVolume = 0; // "beep" volume level
|
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;
|
using namespace miosix;
|
||||||
Thread *dmaWaiting = 0;
|
Thread *dmaWaiting = 0;
|
||||||
|
|
@ -98,19 +99,22 @@ void __attribute__((used)) TIM8_TRG_COM_TIM14_IRQHandler()
|
||||||
*/
|
*/
|
||||||
void __attribute__((used)) DMA_Handler()
|
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;
|
| DMA_LIFCR_CTEIF2;
|
||||||
|
|
||||||
TIM7->CR1 = 0; // End of transfer, stop TIM7
|
if(circularMode == false)
|
||||||
TIM3->CCER &= ~TIM_CCER_CC3E; // Turn off compare channel
|
{
|
||||||
|
|
||||||
RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA1EN; // Turn off DMA
|
TIM7->CR1 = 0; // End of transfer, stop TIM7
|
||||||
RCC->APB1ENR &= ~RCC_APB1ENR_TIM7EN; // Turn off TIM7
|
TIM3->CCER &= ~TIM_CCER_CC3E; // Turn off compare channel
|
||||||
__DSB();
|
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();
|
dmaWaiting->IRQwakeup();
|
||||||
if(dmaWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
if(dmaWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
|
||||||
Scheduler::IRQfindNextThread();
|
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,
|
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;
|
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.
|
* Timebase for triggering of DMA transfers.
|
||||||
* Bus frequency is 84MHz.
|
* Bus frequency is 84MHz.
|
||||||
*/
|
*/
|
||||||
uint32_t ratio = (84000000/sampleRate) - 1;
|
uint32_t ratio = (84000000/sampleRate);
|
||||||
|
|
||||||
TIM7->CNT = 0;
|
TIM7->CNT = 0;
|
||||||
TIM7->PSC = 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_MINC // Increment source pointer
|
||||||
| DMA_SxCR_DIR_0 // Memory to peripheral
|
| DMA_SxCR_DIR_0 // Memory to peripheral
|
||||||
| DMA_SxCR_TCIE // Transfer complete interrupt
|
| DMA_SxCR_TCIE // Transfer complete interrupt
|
||||||
| DMA_SxCR_TEIE // Transfer error interrupt
|
| DMA_SxCR_TEIE; // Transfer error interrupt
|
||||||
| DMA_SxCR_EN; // Enable transfer
|
|
||||||
|
|
||||||
|
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_ClearPendingIRQ(DMA1_Stream2_IRQn);
|
||||||
NVIC_SetPriority(DMA1_Stream2_IRQn, 10);
|
NVIC_SetPriority(DMA1_Stream2_IRQn, 10);
|
||||||
NVIC_EnableIRQ(DMA1_Stream2_IRQn);
|
NVIC_EnableIRQ(DMA1_Stream2_IRQn);
|
||||||
|
|
@ -346,13 +359,27 @@ void toneGen_stopAudioStream()
|
||||||
TIM7->CR1 = 0;
|
TIM7->CR1 = 0;
|
||||||
TIM3->CCER &= ~TIM_CCER_CC3E;
|
TIM3->CCER &= ~TIM_CCER_CC3E;
|
||||||
|
|
||||||
// Shut down TIM7 and DMA
|
// Stop DMA transfer and clear pending interrupt flags
|
||||||
RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA1EN;
|
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;
|
RCC->APB1ENR &= ~RCC_APB1ENR_TIM7EN;
|
||||||
|
__DSB();
|
||||||
|
|
||||||
// Unlock tones and wake up the thread waiting for completion
|
// Unlock tones and wake up the thread waiting for completion
|
||||||
tonesLocked = false;
|
tonesLocked = false;
|
||||||
if(dmaWaiting) dmaWaiting->IRQwakeup();
|
if(dmaWaiting)
|
||||||
|
{
|
||||||
|
dmaWaiting->IRQwakeup();
|
||||||
|
dmaWaiting = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear flag for circular double buffered mode
|
||||||
|
circularMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool toneGen_toneBusy()
|
bool toneGen_toneBusy()
|
||||||
|
|
|
||||||
|
|
@ -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 buf: pointer to a buffer containing the audio samples.
|
||||||
* @param len: length of the data buffer.
|
* @param len: length of the data buffer.
|
||||||
* @param sampleRate: sample rate of the audio stream in samples per second.
|
* @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,
|
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
|
* 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
|
* 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();
|
void toneGen_stopAudioStream();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue