mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
[WIP] working on polyphase channelizer and testing viewer
This commit is contained in:
106
src/dsp/filter/channelizer/ChannelDistributor.java
Normal file
106
src/dsp/filter/channelizer/ChannelDistributor.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package dsp.filter.channelizer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChannelDistributor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelDistributor.class);
|
||||
private Map<Integer,ComplexSampleAssembler> mListenerMap = new HashMap<>();
|
||||
private int mBufferSize;
|
||||
private int mChannelCount;
|
||||
|
||||
public ChannelDistributor(int bufferSize, int channelCount)
|
||||
{
|
||||
mBufferSize = bufferSize;
|
||||
mChannelCount = channelCount;
|
||||
|
||||
}
|
||||
|
||||
public void receive(float[] channels)
|
||||
{
|
||||
for(int channel = 0; channel < channels.length / 2; channel++)
|
||||
{
|
||||
if(mListenerMap.containsKey(channel))
|
||||
{
|
||||
ComplexSampleAssembler channelAssembler = mListenerMap.get(channel);
|
||||
|
||||
int index = channel * 2;
|
||||
channelAssembler.receive(channels[index], channels[index + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(int channel, Listener<ComplexBuffer> listener)
|
||||
{
|
||||
ComplexSampleAssembler channelAssembler = mListenerMap.get(channel);
|
||||
|
||||
if(channelAssembler == null)
|
||||
{
|
||||
channelAssembler = new ComplexSampleAssembler(mBufferSize);
|
||||
mListenerMap.put(channel, channelAssembler);
|
||||
}
|
||||
|
||||
channelAssembler.addListener(listener);
|
||||
}
|
||||
|
||||
public class ComplexSampleAssembler
|
||||
{
|
||||
private Broadcaster<ComplexBuffer> mBroadcaster = new Broadcaster<>();
|
||||
private float[] mBuffer;
|
||||
private int mBufferPointer;
|
||||
private int mBufferSize;
|
||||
private boolean mEnableLogging;
|
||||
|
||||
public ComplexSampleAssembler(int bufferSize)
|
||||
{
|
||||
mBufferSize = bufferSize;
|
||||
mBuffer = new float[mBufferSize];
|
||||
}
|
||||
|
||||
public void receive(float inphase, float quadrature)
|
||||
{
|
||||
mBuffer[mBufferPointer++] = inphase;
|
||||
mBuffer[mBufferPointer++] = inphase;
|
||||
|
||||
if(mBufferPointer >= mBufferSize)
|
||||
{
|
||||
float[] bufferCopy = new float[mBuffer.length];
|
||||
System.arraycopy(mBuffer,0, bufferCopy, 0, mBuffer.length);
|
||||
|
||||
mBroadcaster.receive(new ComplexBuffer(bufferCopy));
|
||||
|
||||
mBufferPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(Listener<ComplexBuffer> listener)
|
||||
{
|
||||
mBroadcaster.addListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
package dsp.filter.channelizer;
|
||||
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.mixer.LowPhaseNoiseOscillator;
|
||||
import dsp.mixer.Oscillator;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
@@ -35,6 +36,8 @@ import spectrum.SpectrumPanel;
|
||||
import spectrum.converter.ComplexDecibelConverter;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -45,14 +48,14 @@ public class ChannelizerViewer extends JFrame
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelizerViewer.class);
|
||||
|
||||
private static final int CHANNEL_BANDWIDTH = 12500;
|
||||
private static final int CHANNEL_FFT_FRAME_RATE = 20;
|
||||
|
||||
private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel());
|
||||
private JPanel mPrimaryPanel;
|
||||
private SpectrumPanel mSpectrumPanel;
|
||||
private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter();
|
||||
private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX);
|
||||
private ChannelPanel mChannelPanel;
|
||||
private Oscillator mOscillator;
|
||||
private JPanel mControlPanel;
|
||||
private ChannelPanel mPrimarySpectrumPanel;
|
||||
private ChannelArrayPanel mChannelPanel;
|
||||
private LowPhaseNoiseOscillator mOscillator;
|
||||
private int mChannelCount;
|
||||
private int mSampleRate;
|
||||
private int mToneFrequency;
|
||||
@@ -62,7 +65,7 @@ public class ChannelizerViewer extends JFrame
|
||||
{
|
||||
mChannelCount = channelCount;
|
||||
mSampleRate = mChannelCount * CHANNEL_BANDWIDTH;
|
||||
mToneFrequency = 37500; //Set to channel 1 as default
|
||||
mToneFrequency = (int)(CHANNEL_BANDWIDTH * 3); //Set to channel 1 as default
|
||||
|
||||
init();
|
||||
}
|
||||
@@ -76,13 +79,13 @@ public class ChannelizerViewer extends JFrame
|
||||
setLocationRelativeTo(null);
|
||||
add(getPrimaryPanel());
|
||||
|
||||
mOscillator = new Oscillator(mToneFrequency, mSampleRate);
|
||||
mOscillator = new LowPhaseNoiseOscillator(mSampleRate, mToneFrequency);
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
|
||||
scheduledExecutorService.scheduleAtFixedRate(new SampleGenerationTask(), 0, 100, TimeUnit.MILLISECONDS);
|
||||
scheduledExecutorService.scheduleAtFixedRate(new SampleGenerationTask(), 0, 50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void setToneFrequency(int frequency)
|
||||
@@ -93,22 +96,14 @@ public class ChannelizerViewer extends JFrame
|
||||
|
||||
private void generateSamples()
|
||||
{
|
||||
int size = mDFTSize.getSize();
|
||||
int size = mChannelCount * CHANNEL_BANDWIDTH / CHANNEL_FFT_FRAME_RATE * 2;
|
||||
|
||||
float[] samples = new float[size * 2];
|
||||
|
||||
for(int x = 0; x < size; x += 2)
|
||||
{
|
||||
samples[x] = mOscillator.getComplex().inphase();
|
||||
samples[x + 1] = mOscillator.getComplex().quadrature();
|
||||
|
||||
mOscillator.rotate();
|
||||
}
|
||||
float[] samples = mOscillator.generate(size);
|
||||
|
||||
ComplexBuffer buffer = new ComplexBuffer(samples);
|
||||
|
||||
getChannelPanel().receive(buffer);
|
||||
mDFTProcessor.receive(buffer);
|
||||
getChannelArrayPanel().receive(buffer);
|
||||
getSpectrumPanel().receive(buffer);
|
||||
}
|
||||
|
||||
private JPanel getPrimaryPanel()
|
||||
@@ -116,34 +111,60 @@ public class ChannelizerViewer extends JFrame
|
||||
if(mPrimaryPanel == null)
|
||||
{
|
||||
mPrimaryPanel = new JPanel();
|
||||
mPrimaryPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[][grow,fill]"));
|
||||
mPrimaryPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[][][grow,fill]"));
|
||||
mPrimaryPanel.add(getSpectrumPanel(), "wrap");
|
||||
mPrimaryPanel.add(getChannelPanel());
|
||||
mPrimaryPanel.add(getControlPanel(), "wrap");
|
||||
mPrimaryPanel.add(getChannelArrayPanel());
|
||||
}
|
||||
|
||||
return mPrimaryPanel;
|
||||
}
|
||||
|
||||
private SpectrumPanel getSpectrumPanel()
|
||||
private ChannelPanel getSpectrumPanel()
|
||||
{
|
||||
if(mSpectrumPanel == null)
|
||||
if(mPrimarySpectrumPanel == null)
|
||||
{
|
||||
mSpectrumPanel = new SpectrumPanel(mSettingsManager);
|
||||
mSpectrumPanel.setPreferredSize(new Dimension(1000, 200));
|
||||
mComplexDecibelConverter.addListener(mSpectrumPanel);
|
||||
mDFTProcessor.addConverter(mComplexDecibelConverter);
|
||||
mDFTProcessor.frequencyChanged(new FrequencyChangeEvent(
|
||||
FrequencyChangeEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mSampleRate));
|
||||
mPrimarySpectrumPanel = new ChannelPanel(mSettingsManager, mSampleRate);
|
||||
mPrimarySpectrumPanel.setPreferredSize(new Dimension(1000, 200));
|
||||
mPrimarySpectrumPanel.setDFTSize(DFTSize.FFT32768);
|
||||
}
|
||||
|
||||
return mSpectrumPanel;
|
||||
return mPrimarySpectrumPanel;
|
||||
}
|
||||
|
||||
private ChannelPanel getChannelPanel()
|
||||
private JPanel getControlPanel()
|
||||
{
|
||||
if(mControlPanel == null)
|
||||
{
|
||||
mControlPanel = new JPanel();
|
||||
|
||||
int maximumFrequency = mSampleRate / 2;
|
||||
|
||||
SpinnerNumberModel model = new SpinnerNumberModel(mToneFrequency, -maximumFrequency, maximumFrequency, CHANNEL_BANDWIDTH );
|
||||
|
||||
model.addChangeListener(new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e)
|
||||
{
|
||||
int frequency = model.getNumber().intValue();
|
||||
mOscillator.setFrequency(frequency);
|
||||
}
|
||||
});
|
||||
|
||||
JSpinner spinner = new JSpinner(model);
|
||||
|
||||
mControlPanel.add(spinner);
|
||||
}
|
||||
|
||||
return mControlPanel;
|
||||
}
|
||||
|
||||
private ChannelArrayPanel getChannelArrayPanel()
|
||||
{
|
||||
if(mChannelPanel == null)
|
||||
{
|
||||
mChannelPanel = new ChannelPanel(getFilter());
|
||||
mChannelPanel = new ChannelArrayPanel(getFilter());
|
||||
}
|
||||
|
||||
return mChannelPanel;
|
||||
@@ -170,13 +191,47 @@ public class ChannelizerViewer extends JFrame
|
||||
return taps;
|
||||
}
|
||||
|
||||
public class ChannelPanel extends JPanel implements Listener<ComplexBuffer>
|
||||
public class ChannelArrayPanel extends JPanel implements Listener<ComplexBuffer>
|
||||
{
|
||||
private PolyphaseChannelizer mPolyphaseChannelizer;
|
||||
private ChannelDistributor mChannelDistributor;
|
||||
|
||||
public ChannelPanel(float[] taps)
|
||||
public ChannelArrayPanel(float[] taps)
|
||||
{
|
||||
int bufferSize = CHANNEL_BANDWIDTH / CHANNEL_FFT_FRAME_RATE;
|
||||
if(bufferSize % 2 == 1)
|
||||
{
|
||||
bufferSize++;
|
||||
}
|
||||
|
||||
mPolyphaseChannelizer = new PolyphaseChannelizer(taps, mChannelCount);
|
||||
mChannelDistributor = new ChannelDistributor(bufferSize, mChannelCount);
|
||||
mPolyphaseChannelizer.setChannelDistributor(mChannelDistributor);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill][grow,fill]", ""));
|
||||
|
||||
for(int x = 0; x < mChannelCount; x++)
|
||||
{
|
||||
ChannelPanel channelPanel = new ChannelPanel(mSettingsManager, CHANNEL_BANDWIDTH * 2);
|
||||
channelPanel.setPreferredSize(new Dimension(250,100));
|
||||
channelPanel.setDFTSize(DFTSize.FFT00512);
|
||||
|
||||
if(x % 4 == 3)
|
||||
{
|
||||
add(channelPanel, "wrap");
|
||||
}
|
||||
else
|
||||
{
|
||||
add(channelPanel);
|
||||
}
|
||||
|
||||
mChannelDistributor.addListener(x, channelPanel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -186,6 +241,36 @@ public class ChannelizerViewer extends JFrame
|
||||
}
|
||||
}
|
||||
|
||||
public class ChannelPanel extends JPanel implements Listener<ComplexBuffer>
|
||||
{
|
||||
private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX);
|
||||
private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter();
|
||||
private SpectrumPanel mSpectrumPanel;
|
||||
|
||||
public ChannelPanel(SettingsManager settingsManager, int sampleRate)
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
||||
mSpectrumPanel = new SpectrumPanel(settingsManager);
|
||||
add(mSpectrumPanel);
|
||||
|
||||
mDFTProcessor.addConverter(mComplexDecibelConverter);
|
||||
mDFTProcessor.frequencyChanged(new FrequencyChangeEvent(
|
||||
FrequencyChangeEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE, sampleRate));
|
||||
mComplexDecibelConverter.addListener(mSpectrumPanel);
|
||||
}
|
||||
|
||||
public void setDFTSize(DFTSize dftSize)
|
||||
{
|
||||
mDFTProcessor.setDFTSize(dftSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ComplexBuffer complexBuffer)
|
||||
{
|
||||
mDFTProcessor.receive(complexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public class SampleGenerator
|
||||
{
|
||||
public SampleGenerator()
|
||||
@@ -218,7 +303,9 @@ public class ChannelizerViewer extends JFrame
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
final ChannelizerViewer frame = new ChannelizerViewer(16);
|
||||
int channelCount = 24;
|
||||
|
||||
final ChannelizerViewer frame = new ChannelizerViewer(channelCount);
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
|
||||
@@ -43,6 +43,7 @@ public class PolyphaseChannelizer implements Listener<ComplexBuffer>
|
||||
private float[] mFilteredSamples;
|
||||
private boolean mToggle = false;
|
||||
private FloatFFT_1D mFFT;
|
||||
private ChannelDistributor mChannelDistributor;
|
||||
|
||||
private DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0000");
|
||||
|
||||
@@ -125,15 +126,15 @@ public class PolyphaseChannelizer implements Listener<ComplexBuffer>
|
||||
|
||||
private void dispatch(float[] channels)
|
||||
{
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
//
|
||||
// for(int x = 0; x < channels.length; x += 2)
|
||||
// {
|
||||
// sb.append((x/2)).append("[")
|
||||
// .append(DECIMAL_FORMAT.format(magnitude(channels[x], channels[x + 1], mChannelCount))).append("] ");
|
||||
// }
|
||||
//
|
||||
// mLog.debug("Out: " + sb.toString());
|
||||
if(mChannelDistributor != null)
|
||||
{
|
||||
mChannelDistributor.receive(channels);
|
||||
}
|
||||
}
|
||||
|
||||
public void setChannelDistributor(ChannelDistributor channelDistributor)
|
||||
{
|
||||
mChannelDistributor = channelDistributor;
|
||||
}
|
||||
|
||||
private double magnitude(float real, float imaginary, int fftSize)
|
||||
|
||||
126
src/dsp/mixer/LowPhaseNoiseOscillator.java
Normal file
126
src/dsp/mixer/LowPhaseNoiseOscillator.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package dsp.mixer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LowPhaseNoiseOscillator
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LowPhaseNoiseOscillator.class);
|
||||
|
||||
private static final double TWO_PI = 2.0 * Math.PI;
|
||||
private static final float THREE_HALVES = 3.0f / 2.0f;
|
||||
private int mSampleRate;
|
||||
private int mFrequency;
|
||||
private float mInphase = 1.0f;
|
||||
private float mQuadrature = 0.0f;
|
||||
private float mPreviousInphase = 1.0f;
|
||||
private float mPreviousQuadrature = 0.0f;
|
||||
private float mCosineAngle;
|
||||
private float mSineAngle;
|
||||
private float mGain = 1.0f;
|
||||
|
||||
/**
|
||||
* Ultra-low phase noise complex oscillator as described in Digital Signal Processing 3e, Lyons, p.786
|
||||
*
|
||||
* @param sampleRate for the oscillator
|
||||
* @param frequency to generate
|
||||
*/
|
||||
public LowPhaseNoiseOscillator(int sampleRate, int frequency)
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
mFrequency = frequency;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
public void setSampleRate(int sampleRate)
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
update();
|
||||
}
|
||||
|
||||
public void setFrequency(int frequency)
|
||||
{
|
||||
mFrequency = frequency;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal values after a frequency or sample rate change
|
||||
*/
|
||||
private void update()
|
||||
{
|
||||
double anglePerSample = TWO_PI * mFrequency / mSampleRate;
|
||||
|
||||
mCosineAngle = (float)Math.cos(anglePerSample);
|
||||
mSineAngle = (float)Math.sin(anglePerSample);
|
||||
}
|
||||
|
||||
private void rotate()
|
||||
{
|
||||
mInphase = ((mPreviousInphase * mCosineAngle) - (mPreviousQuadrature * mSineAngle)) * mGain;
|
||||
mQuadrature = ((mPreviousInphase * mSineAngle) + (mPreviousQuadrature * mCosineAngle)) * mGain;
|
||||
|
||||
mPreviousInphase = mInphase;
|
||||
mPreviousQuadrature = mQuadrature;
|
||||
|
||||
mGain = THREE_HALVES - ((mPreviousInphase * mPreviousInphase) + (mPreviousQuadrature * mPreviousQuadrature));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of samples with length of 2 x sample count containing interleaved inphase and quadrature
|
||||
* samples.
|
||||
*
|
||||
* @param sampleCount number of samples to generate
|
||||
* @return array that is sampleCount * 2 in length
|
||||
*/
|
||||
public float[] generate(int sampleCount)
|
||||
{
|
||||
float[] samples = new float[sampleCount * 2];
|
||||
|
||||
for(int x = 0; x < sampleCount; x++)
|
||||
{
|
||||
int index = x * 2;
|
||||
samples[index] = mInphase;
|
||||
samples[index + 1] = mQuadrature;
|
||||
rotate();
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
LowPhaseNoiseOscillator o = new LowPhaseNoiseOscillator(10, 1);
|
||||
|
||||
float[] samples = o.generate(20);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
sb.append(samples[x]).append(" ").append(samples[x + 1]).append("\n");
|
||||
}
|
||||
|
||||
mLog.debug("Samples:\n" + sb.toString());
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.complex.Complex;
|
||||
import sample.complex.ComplexBuffer;
|
||||
|
||||
public class Oscillator
|
||||
{
|
||||
@@ -110,4 +111,25 @@ public class Oscillator
|
||||
{
|
||||
return mCurrentAngle.real();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the requested number of complex samples and returns them in a complex buffer
|
||||
* @param sampleCount to generate
|
||||
* @return complex buffer containing the requested complex samples
|
||||
*/
|
||||
public ComplexBuffer generateComplexBuffer(int sampleCount)
|
||||
{
|
||||
float[] buffer = new float[sampleCount * 2];
|
||||
|
||||
for(int x = 0; x < sampleCount; x++)
|
||||
{
|
||||
int index = 2 * x;
|
||||
buffer[index] = inphase();
|
||||
buffer[index + 1] = quadrature();
|
||||
|
||||
rotate();
|
||||
}
|
||||
|
||||
return new ComplexBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user