#2526 Polyphase Channelizer gain imbalance across synthesis of two adjacent channels. (#2527)

- Fix two-channel synthesis asymmetry across polyphase channel boundaries.
- TwoChannelSynthesizerM2: replace incorrect 2-path polyphase split (which applied
  different responses to each channel) with channel sum + prototype filter
  modulated to the channel boundary (+FS/4).
- ComplexPolyphaseChannelizerM2: start on the middle block map when taps per
  channel is odd so adjacent channel outputs are phase coherent.
- TwoChannelOutputProcessor: translate channel 2 by FS/2 using the absolute
  block index parity derived from the even-sized dispatch batches.
- SynthesizerViewer: translate channel 2 before synthesis.
- Add TwoChannelSynthesizerM2Test for flat, symmetric boundary response.

Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
Denny Sheirer
2026-10-29 08:51:37 -04:00
committed by GitHub
parent 9a6dc2b0a0
commit 248fe3e4f7
6 changed files with 275 additions and 122 deletions

View File

@@ -264,6 +264,7 @@ public class SynthesizerViewer extends JFrame
private TwoChannelSynthesizerM2 mSynthesizer;
private FS4DownConverter mFS4DownConverter = new FS4DownConverter();
private int mSamplesPerCycle = CHANNEL_SAMPLE_RATE / DATA_GENERATOR_FRAME_RATE;
private boolean mOddSample = false;
public DataGenerationManager()
{
@@ -284,7 +285,23 @@ public class SynthesizerViewer extends JFrame
ComplexSamples channel1Buffer = getChannel1ControlPanel().getOscillator().generateComplexSamples(mSamplesPerCycle, 0l);
ComplexSamples channel2Buffer = getChannel2ControlPanel().getOscillator().generateComplexSamples(mSamplesPerCycle, 0l);
ComplexSamples synthesizedBuffer = mSynthesizer.process(channel1Buffer, channel2Buffer);
//Translate channel 2 by FS/2 so that it sits adjacent to channel 1 for synthesis
float[] i2 = channel2Buffer.i().clone();
float[] q2 = channel2Buffer.q().clone();
for(int x = 0; x < i2.length; x++)
{
if(mOddSample)
{
i2[x] = -i2[x];
q2[x] = -q2[x];
}
mOddSample = !mOddSample;
}
ComplexSamples synthesizedBuffer = mSynthesizer.process(channel1Buffer,
new ComplexSamples(i2, q2, channel2Buffer.timestamp()));
getChannel1Panel().receive(new FloatNativeBuffer(channel1Buffer));
getChannel2Panel().receive(new FloatNativeBuffer(channel2Buffer));