mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
P25 - added parsing of packet payload data for confirmed PDUs.
This commit is contained in:
198
src/gui/control/ConstellationViewer.java
Normal file
198
src/gui/control/ConstellationViewer.java
Normal file
@@ -0,0 +1,198 @@
|
||||
package gui.control;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import buffer.CircularBuffer;
|
||||
|
||||
public class ConstellationViewer extends JPanel
|
||||
implements Listener<ComplexSample>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ConstellationViewer.class );
|
||||
|
||||
private int mSampleRate;
|
||||
private int mSymbolRate;
|
||||
private float mSamplesPerSymbol;
|
||||
private float mCounter = 0;
|
||||
private float mOffset = 0;
|
||||
private CircularBuffer<ComplexSample> mBuffer =
|
||||
new CircularBuffer<ComplexSample>( 5000 );
|
||||
private ComplexSample mPrevious = new ComplexSample( 1, 1 );
|
||||
|
||||
public ConstellationViewer( int sampleRate, int symbolRate )
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
mSymbolRate = symbolRate;
|
||||
mSamplesPerSymbol = (float)mSampleRate / (float)mSymbolRate;
|
||||
|
||||
initGui();
|
||||
}
|
||||
|
||||
private void initGui()
|
||||
{
|
||||
setPreferredSize( new Dimension( 200,200 ) );
|
||||
|
||||
addMouseListener( new MouseListener()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
if( SwingUtilities.isRightMouseButton( e ) )
|
||||
{
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
menu.add( new TimingOffsetItem( (int)( mSamplesPerSymbol * 10 ), (int)( mOffset * 10 ) ) );
|
||||
|
||||
menu.show( ConstellationViewer.this, e.getX(), e.getY() );
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased( MouseEvent e ) {}
|
||||
public void mousePressed( MouseEvent e ) {}
|
||||
public void mouseExited( MouseEvent e ) {}
|
||||
public void mouseEntered( MouseEvent e ) {}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( ComplexSample sample )
|
||||
{
|
||||
mBuffer.receive( sample );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timing offset ( 0 <> SamplesPerSymbol ) for selecting which
|
||||
* sample to plot, within the symbol timeframe. Values greater than the
|
||||
* samples per symbol value will simply wrap or delay into the next symbol
|
||||
* period.
|
||||
*
|
||||
* @param offset
|
||||
*/
|
||||
public void setOffset( float offset )
|
||||
{
|
||||
mOffset = offset;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent( Graphics g )
|
||||
{
|
||||
super.paintComponent( g );
|
||||
|
||||
Graphics2D graphics = (Graphics2D) g;
|
||||
|
||||
// graphics.setBackground( Color.BLACK );
|
||||
graphics.setColor( Color.BLUE );
|
||||
|
||||
List<ComplexSample> samples = mBuffer.getElements();
|
||||
|
||||
double centerX = (double)getHeight() / 2.0d;
|
||||
double centerY = (double)getWidth() / 2.0d;
|
||||
|
||||
// double scale = centerX / 2.0d;
|
||||
|
||||
double scale = 0.5d;
|
||||
|
||||
mCounter = 0;
|
||||
|
||||
for( ComplexSample sample: samples )
|
||||
{
|
||||
|
||||
if( mCounter > ( mOffset + mSamplesPerSymbol ) )
|
||||
{
|
||||
/**
|
||||
* Multiply the current sample against the complex conjugate of the
|
||||
* previous sample to derive the phase delta between the two samples
|
||||
*
|
||||
* Negating the previous sample quadrature produces the conjugate
|
||||
*/
|
||||
double i = ( sample.inphase() * mPrevious.inphase() ) -
|
||||
( sample.quadrature() * -mPrevious.quadrature() );
|
||||
double q = ( sample.quadrature() * mPrevious.inphase() ) +
|
||||
( sample.inphase() * -mPrevious.quadrature() );
|
||||
|
||||
mLog.debug( "i:" + i + " q:" + q );
|
||||
double angle;
|
||||
|
||||
//Check for divide by zero
|
||||
if( i == 0 )
|
||||
{
|
||||
angle = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Use the arcus tangent of imaginary (q) divided by real (i) to
|
||||
* get the phase angle (+/-) which was directly manipulated by the
|
||||
* original message waveform during the modulation. This value now
|
||||
* serves as the instantaneous amplitude of the demodulated signal
|
||||
*/
|
||||
double denominator = 1.0d / i;
|
||||
angle = Math.atan( (double)q * denominator );
|
||||
}
|
||||
|
||||
Ellipse2D.Double ellipse =
|
||||
new Ellipse2D.Double( centerX - ( i * scale ),
|
||||
centerY - ( q * scale ), 4, 4 );
|
||||
|
||||
graphics.draw( ellipse );
|
||||
|
||||
mCounter -= mSamplesPerSymbol;
|
||||
}
|
||||
|
||||
mCounter++;
|
||||
|
||||
// mLog.debug( "Plotting x:" + ( centerX - ( sample.x() * scale ) ) +
|
||||
// " y:" + ( centerY - ( sample.y() * scale ) ) + " for sample " + sample.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
public class TimingOffsetItem extends JSlider implements ChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public TimingOffsetItem( int maxValue, int currentValue )
|
||||
{
|
||||
super( JSlider.HORIZONTAL, 0, maxValue, currentValue );
|
||||
|
||||
setMajorTickSpacing( 10 );
|
||||
setMinorTickSpacing( 5 );
|
||||
setPaintTicks( true );
|
||||
setPaintLabels( true );
|
||||
|
||||
addChangeListener( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
int value = ((JSlider)event.getSource()).getValue();
|
||||
|
||||
setOffset( (float)value / 10.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user