Reconstruction filter after ext-audio upsampling in FM mode

CFMUpSampler zero-stuffs 8kHz to 24kHz with no lowpass after it,
leaving imaging artifacts in the reconstructed ext audio. Filter it
with a second, separately-stated pass of the same 300-2700Hz design
already used for TX shaping, right where it comes out of the upsampler.
This commit is contained in:
Cort Buffington 2026-08-12 07:43:23 -05:00
parent 4c7e8fbe46
commit 384c5d559d
2 changed files with 11 additions and 0 deletions

8
FM.cpp
View File

@ -71,6 +71,9 @@ m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460),
m_dsFilterStage1( 724, 1448, 724, 32768, -37895, 21352),//same design, separate state
m_dsFilterStage2(32768, 0,-32768, 32768, -50339, 19052),
m_dsFilterStage3(32768, -65536, 32768, 32768, -64075, 31460),
m_usFilterStage1( 724, 1448, 724, 32768, -37895, 21352),//same design, separate state
m_usFilterStage2(32768, 0,-32768, 32768, -50339, 19052),
m_usFilterStage3(32768, -65536, 32768, 32768, -64075, 31460),
m_blanking(),
m_accessMode(1U),
m_linkMode(false),
@ -125,6 +128,7 @@ void CFM::repeaterSamples(bool cos, q15_t* samples, const uint16_t* rssi, uint8_
q15_t currentExtSample;
bool inputExt = m_inputExtRB.getSample(currentExtSample);//always consume the external input data so it does not overflow
currentExtSample = m_usFilterStage3.filter(m_usFilterStage2.filter(m_usFilterStage1.filter(currentExtSample)));
inputExt = inputExt && m_extEnabled;
switch (m_accessMode) {
@ -270,6 +274,7 @@ void CFM::linkSamples(bool cos, q15_t* samples, uint8_t length)
// few samples past that point
q15_t currentExtSample = 0;
bool inputExt = m_inputExtRB.getSample(currentExtSample);//always consume the external input data so it does not overflow
currentExtSample = m_usFilterStage3.filter(m_usFilterStage2.filter(m_usFilterStage1.filter(currentExtSample)));
inputExt = inputExt && m_extEnabled;
switch (m_accessMode) {
@ -423,6 +428,9 @@ void CFM::reset()
m_dsFilterStage1.reset();
m_dsFilterStage2.reset();
m_dsFilterStage3.reset();
m_usFilterStage1.reset();
m_usFilterStage2.reset();
m_usFilterStage3.reset();
m_squelch.reset();
m_needReverse = false;

3
FM.h
View File

@ -83,6 +83,9 @@ private:
CFMDirectFormI m_dsFilterStage1; // anti-alias filter for the downsampler, own state
CFMDirectFormI m_dsFilterStage2;
CFMDirectFormI m_dsFilterStage3;
CFMDirectFormI m_usFilterStage1; // reconstruction filter for the upsampler, own state
CFMDirectFormI m_usFilterStage2;
CFMDirectFormI m_usFilterStage3;
CFMBlanking m_blanking;
uint8_t m_accessMode;
bool m_linkMode;