mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
P25 - added parsing of packet payload data for confirmed PDUs.
This commit is contained in:
@@ -107,12 +107,25 @@ public class BitSetBuffer extends BitSet
|
||||
{
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the pointer to a specific value
|
||||
* @param index
|
||||
*/
|
||||
public void setPointer( int index )
|
||||
{
|
||||
mPointer = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current pointer position left (negative adjustment) or
|
||||
* right (positive adjustment)
|
||||
*/
|
||||
public void adjustPointer( int adjustment )
|
||||
{
|
||||
mPointer += adjustment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to construct a new BitSetBuffer, preloaded with the bits
|
||||
* from the preload parameter, and then filled with the bits from the
|
||||
|
||||
94
src/buffer/CircularBuffer.java
Normal file
94
src/buffer/CircularBuffer.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package buffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public class CircularBuffer<T> implements Listener<T>
|
||||
{
|
||||
private ArrayList<T> mElements;
|
||||
private Listener<T> mListener;
|
||||
private int mPointer;
|
||||
private int mSize;
|
||||
|
||||
|
||||
/**
|
||||
* Circular Buffer - stores elements in an array list. Once the buffer is
|
||||
* full, new samples replace older samples and older samples are broadcast
|
||||
* to an optional listener.
|
||||
*
|
||||
* @param size - size of buffer
|
||||
*/
|
||||
public CircularBuffer( int size )
|
||||
{
|
||||
mSize = size;
|
||||
mElements = new ArrayList<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element stored at index. Note: the index argument refers to
|
||||
* the internal storage index and does not indicate a temporal relationship
|
||||
* among elements. For example, if you get indexes 1, 2, and 3, you may be
|
||||
* getting ( oldest - 1, oldest, newest ) elements, if the internal pointer
|
||||
* is currently pointing to and will insert the next element at index 4.
|
||||
*
|
||||
* @param index - element index
|
||||
*
|
||||
* @return element or null
|
||||
*/
|
||||
public T get( int index )
|
||||
{
|
||||
if( index < mElements.size() )
|
||||
{
|
||||
return mElements.get( index );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
public List<T> getElements()
|
||||
{
|
||||
return Collections.unmodifiableList( mElements );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( T element )
|
||||
{
|
||||
T previous = null;
|
||||
|
||||
if( mElements.size() > mPointer )
|
||||
{
|
||||
previous = mElements.get( mPointer );
|
||||
|
||||
mElements.set( mPointer, element );
|
||||
}
|
||||
else
|
||||
{
|
||||
mElements.add( element );
|
||||
}
|
||||
|
||||
mPointer++;
|
||||
|
||||
if( mPointer >= mSize )
|
||||
{
|
||||
mPointer = 0;
|
||||
}
|
||||
|
||||
if( previous != null && mListener != null )
|
||||
{
|
||||
mListener.receive( previous );
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<T> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ public class ComplexSampleBufferAssembler
|
||||
*/
|
||||
public void put( ComplexSample sample )
|
||||
{
|
||||
mByteBuffer.putShort( (short)sample.left() );
|
||||
mByteBuffer.putShort( (short)sample.right() );
|
||||
mByteBuffer.putShort( (short)( sample.left() * Short.MAX_VALUE ) );
|
||||
mByteBuffer.putShort( (short)( sample.right() * Short.MAX_VALUE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -144,7 +144,7 @@ public class P25Interleave
|
||||
|
||||
int start = 0;
|
||||
int end = 196;
|
||||
String interleaved = "0010110111100011001010101100001000011110101110000110001000010010111100101101100100100101011101100011000101110010111100101110111100100111101010111011011001010111001011100001011100101000000010100100";
|
||||
String interleaved = "0010010100000000000000000000000000000000000010100011001011111010101010101010101010101010010111101010100011010000000000000000000000000000110001011010011010101010101010101010101010101010100101100101";
|
||||
|
||||
BitSetBuffer b = new BitSetBuffer( end );
|
||||
|
||||
|
||||
@@ -121,7 +121,6 @@ public class Trellis_3_4_Rate
|
||||
* and the flushing 000 node, otherwise there was an error */
|
||||
if( path != null && path.getNodes().size() == 50 )
|
||||
{
|
||||
mLog.debug( path.toString() );
|
||||
/* Clear the original message bits */
|
||||
message.clear( start, end );
|
||||
|
||||
@@ -641,7 +640,7 @@ public class Trellis_3_4_Rate
|
||||
*/
|
||||
public static void main( String[] args )
|
||||
{
|
||||
String raw = "0010001001110110000100100000101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000001101000100011101011011000001110110000001101111";
|
||||
String raw = "12:58:51.635 DEBUG dsp.fsk.P25MessageFramer - AFTER DEINTERLEAVE: 00100110000011001010010101111101000101010110100111111100101011000111011011000110000000000000000000000111111101011000001000001111000110000000000010101011110110010000000001011011000000010100011110110001000010100100011111000000000100000000000000000000010000000000001100000000000000011010101010101010101010100010001010000110010111111101101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000010111001001000011100101011010101010101001010011";
|
||||
|
||||
BitSetBuffer message = BitSetBuffer.load( raw );
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import decode.p25.reference.DataUnitID;
|
||||
import decode.p25.reference.Vendor;
|
||||
import edac.CRC;
|
||||
import edac.CRCP25;
|
||||
|
||||
@@ -14,6 +15,12 @@ public class PDUConfirmedMessage extends PDUMessage
|
||||
public final static Logger mLog =
|
||||
LoggerFactory.getLogger( PDUConfirmedMessage.class );
|
||||
|
||||
public static final int SEQUENCE_RESET_FLAG = 128; //SYN
|
||||
public static final int[] PACKET_SEQUENCE_NUMBER = { 129,130,131 };
|
||||
public static final int FINAL_FRAGMENT_FLAG = 132;
|
||||
public static final int[] FRAGMENT_SEQUENCE_NUMBER = { 133,134,135 };
|
||||
public static final int PACKET_DATA_START = 176;
|
||||
|
||||
public PDUConfirmedMessage( BitSetBuffer message, DataUnitID duid,
|
||||
AliasList aliasList )
|
||||
{
|
||||
@@ -57,29 +64,162 @@ public class PDUConfirmedMessage extends PDUMessage
|
||||
sb.append( getNAC() );
|
||||
sb.append( " " );
|
||||
sb.append( getDUID().getLabel() );
|
||||
|
||||
sb.append( " CRC " );
|
||||
sb.append( " " );
|
||||
sb.append( getDirection() );
|
||||
sb.append( " CRC[" );
|
||||
sb.append( getErrorStatus() );
|
||||
|
||||
sb.append( " LLID:" );
|
||||
sb.append( "] LLID:" );
|
||||
sb.append( getLogicalLinkID() );
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( getConfirmation() );
|
||||
sb.append( " " );
|
||||
sb.append( getDirection() );
|
||||
sb.append( " FMT:" );
|
||||
sb.append( getFormat().getLabel() );
|
||||
sb.append( " SAP:" );
|
||||
sb.append( getServiceAccessPoint().name() );
|
||||
sb.append( " VEND:" );
|
||||
sb.append( getVendor().getLabel() );
|
||||
sb.append( " BLKS TO FOLLOW:" );
|
||||
sb.append( getServiceAccessPoint().getLabel() );
|
||||
|
||||
if( getVendor() != Vendor.STANDARD )
|
||||
{
|
||||
sb.append( " VEND:" );
|
||||
sb.append( getVendor().getLabel() );
|
||||
}
|
||||
|
||||
sb.append( " PACKET #" );
|
||||
sb.append( getPacketSequenceNumber() );
|
||||
|
||||
if( isFinalFragment() && getFragmentSequenceNumber() == 0 )
|
||||
{
|
||||
sb.append( ".C" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "." );
|
||||
sb.append( getFragmentSequenceNumber() );
|
||||
|
||||
if( isFinalFragment() )
|
||||
{
|
||||
sb.append( "C" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( getPacket() );
|
||||
|
||||
sb.append( " BLOCKS:" );
|
||||
sb.append( getBlocksToFollowCount() );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bytes/octets contained in the message, exclusive of the data
|
||||
* header.
|
||||
*/
|
||||
public int getOctetCount()
|
||||
{
|
||||
/* 16 octets per block, and 12 octets in final block */
|
||||
int count = getBlocksToFollowCount() * 16 - 4;
|
||||
|
||||
count -= getPadOctetCount();
|
||||
|
||||
count -= getDataHeaderOffset();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the receiver should accept this packet, despite the
|
||||
* apparent out-of-order packet and fragment sequence numbering. This
|
||||
* indicates that something happened to the synchronization between
|
||||
* the tower and the radio and now the tower commands the radio to reset
|
||||
* the sequence numbers and accept this packet/fragment instead of discarding
|
||||
* it as a duplicate.
|
||||
*/
|
||||
public boolean isSequenceReset()
|
||||
{
|
||||
return mMessage.get( SEQUENCE_RESET_FLAG );
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet sequence number. Numbers run from 0 to 7 and will continually
|
||||
* roll-over if there are more than 8 total packets.
|
||||
*/
|
||||
public int getPacketSequenceNumber()
|
||||
{
|
||||
return mMessage.getInt( PACKET_SEQUENCE_NUMBER );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this packet fragment is the final fragment
|
||||
*/
|
||||
public boolean isFinalFragment()
|
||||
{
|
||||
return mMessage.get( FINAL_FRAGMENT_FLAG );
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet fragment sequence number
|
||||
*/
|
||||
public int getFragmentSequenceNumber()
|
||||
{
|
||||
return mMessage.getInt( FRAGMENT_SEQUENCE_NUMBER );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hex string representation of the packet data, including packet header
|
||||
* data if present.
|
||||
*/
|
||||
public String getPacket()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int dataHeaderOffset = getDataHeaderOffset();
|
||||
|
||||
if( dataHeaderOffset > 0 )
|
||||
{
|
||||
sb.append( "HDR:" );
|
||||
|
||||
for( int x = 0; x < dataHeaderOffset; x++ )
|
||||
{
|
||||
sb.append( getPacketOctet( x ) );
|
||||
}
|
||||
|
||||
sb.append( " " );
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
|
||||
/* Append octets inserting a space between each 32-bit value */
|
||||
for( int x = 0; x < getOctetCount(); x++ )
|
||||
{
|
||||
sb.append( getPacketOctet( x + dataHeaderOffset ) );
|
||||
|
||||
counter++;
|
||||
|
||||
if( counter >= 4 )
|
||||
{
|
||||
sb.append( " " );
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the octet identified by the 0-indexed 'octet' argument location.
|
||||
* If there is a Data Header Offset, the 0 index points to the first octet
|
||||
* of the data header. Otherwise, the 0 index points to the first octet
|
||||
* of the packet. You must account for the data header offset when
|
||||
* specifying the octet.
|
||||
*/
|
||||
public String getPacketOctet( int octet )
|
||||
{
|
||||
int block = (int)( octet / 16 );
|
||||
int offset = octet % 16;
|
||||
|
||||
int start = PACKET_DATA_START + ( block * 144 ) + ( offset * 8 );
|
||||
|
||||
return mMessage.getHex( start, start + 7, 2 );
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
|
||||
@@ -133,12 +133,7 @@ public class PDUMessage extends P25Message
|
||||
|
||||
public int getPadOctetCount()
|
||||
{
|
||||
if( getFormat() == PDUFormat.UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL )
|
||||
{
|
||||
return mMessage.getInt( PAD_OCTET_COUNT );
|
||||
}
|
||||
|
||||
return 0;
|
||||
return mMessage.getInt( PAD_OCTET_COUNT );
|
||||
}
|
||||
|
||||
public Opcode getOpcode()
|
||||
@@ -153,11 +148,6 @@ public class PDUMessage extends P25Message
|
||||
|
||||
public int getDataHeaderOffset()
|
||||
{
|
||||
if( getFormat() == PDUFormat.UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL )
|
||||
{
|
||||
return mMessage.getInt( DATA_HEADER_OFFSET );
|
||||
}
|
||||
|
||||
return 0;
|
||||
return mMessage.getInt( DATA_HEADER_OFFSET );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package decode.p25.reference;
|
||||
|
||||
public enum ServiceAccessPoint
|
||||
{
|
||||
UNENCRYPTED_USER_DATA( "0", 0 ),
|
||||
ENCRYPTED_USER_DATA( "1", 1 ),
|
||||
CIRCUIT_DATA( "2", 2 ),
|
||||
CIRCUIT_DATA_CONTROL( "3", 3 ),
|
||||
PACKET_DATA( "4", 4 ),
|
||||
ADDRESS_RESOLUTION_PROTOCOL( "5", 5 ),
|
||||
SNDCP_PACKET_DATA_CONTROL( "6", 6 ),
|
||||
UNENCRYPTED_USER_DATA( "USER DATA", 0 ),
|
||||
ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ),
|
||||
CIRCUIT_DATA( "CIRCUIT DATA", 2 ),
|
||||
CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ),
|
||||
PACKET_DATA( "PACKET DATA", 4 ),
|
||||
ADDRESS_RESOLUTION_PROTOCOL( "ADDRESS RESOLUTION PROTOCOL", 5 ),
|
||||
SNDCP_PACKET_DATA_CONTROL( "SNDCP PACKET DATA CONTROL", 6 ),
|
||||
SAP_7( "7", 7 ),
|
||||
SAP_8( "8", 8 ),
|
||||
SAP_9( "9", 9 ),
|
||||
@@ -33,17 +33,17 @@ public enum ServiceAccessPoint
|
||||
SAP_28( "28", 28 ),
|
||||
SAP_29( "29", 29 ),
|
||||
SAP_30( "30", 30 ),
|
||||
EXTENDED_ADDRESS( "31", 31 ),
|
||||
REGISTRATION_AND_AUTHORIZATION( "32", 32 ),
|
||||
CHANNEL_REASSIGNMENT( "33", 33 ),
|
||||
SYSTEM_CONFIGURATION( "34", 34 ),
|
||||
MR_LOOPBACK( "35", 35 ),
|
||||
MR_STATISTICS( "36", 36 ),
|
||||
MR_OUT_OF_SERVICE( "37", 37 ),
|
||||
MR_PAGING( "38", 38 ),
|
||||
MR_CONFIGURATION( "39", 39 ),
|
||||
UNENCRYPTED_KEY_MANAGEMENT_MESSAGE( "40", 40 ),
|
||||
ENCRYPTED_KEY_MANAGEMENT_MESSAGE( "41", 41 ),
|
||||
EXTENDED_ADDRESS( "EXTENDED ADDRESS", 31 ),
|
||||
REGISTRATION_AND_AUTHORIZATION( "REGISTRATION AND AUTHORIZATION", 32 ),
|
||||
CHANNEL_REASSIGNMENT( "CHANNEL REASSIGNMENT", 33 ),
|
||||
SYSTEM_CONFIGURATION( "SYSTEM CONFIGURATION", 34 ),
|
||||
MR_LOOPBACK( "MOBILE RADIO LOOPBACK", 35 ),
|
||||
MR_STATISTICS( "MOBILE RADIO STATISTICS", 36 ),
|
||||
MR_OUT_OF_SERVICE( "MOBILE RADIO OUT OF SERVICE", 37 ),
|
||||
MR_PAGING( "MOBILE RADIO PAGING", 38 ),
|
||||
MR_CONFIGURATION( "MOBILE RADIO CONFIGURATION", 39 ),
|
||||
UNENCRYPTED_KEY_MANAGEMENT_MESSAGE( "UNENCRYPTED KEY MANAGEMENT", 40 ),
|
||||
ENCRYPTED_KEY_MANAGEMENT_MESSAGE( "ENCRYPTED KEY MANAGEMENT", 41 ),
|
||||
SAP_42( "42", 42 ),
|
||||
SAP_43( "43", 43 ),
|
||||
SAP_44( "44", 44 ),
|
||||
@@ -63,7 +63,7 @@ public enum ServiceAccessPoint
|
||||
SAP_58( "58", 58 ),
|
||||
SAP_59( "59", 59 ),
|
||||
SAP_60( "60", 60 ),
|
||||
UNENCRYPTED_TRUNKING_CONTROL( "UNENCRYPTED TRUNKING CONTROL", 61 ),
|
||||
UNENCRYPTED_TRUNKING_CONTROL( "TRUNKING CONTROL", 61 ),
|
||||
SAP_62( "62", 62 ),
|
||||
ENCRYPTED_TRUNKING_CONTROL( "ENCRYPTED TRUNKING CONTROL", 63 ),
|
||||
UKNOWN( "UNKN", -1 );
|
||||
|
||||
@@ -399,8 +399,10 @@ public class P25MessageFramer implements Listener<Dibit>
|
||||
if( mThreeQuarterRate.decode( mMessage,
|
||||
mMessage.size() - 196, mMessage.size() ) )
|
||||
{
|
||||
/* Resize the message to account for removing 48 + 4 parity bits */
|
||||
/* Resize the message and adjust the message pointer
|
||||
* to account for removing 48 + 4 parity bits */
|
||||
mMessage.setSize( mMessage.size() - 52 );
|
||||
mMessage.adjustPointer( -52 );
|
||||
|
||||
int blocks = mMessage.getInt( PDUMessage.BLOCKS_TO_FOLLOW );
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ public class CRCP25
|
||||
*/
|
||||
public static CRC checkCRC9( BitSetBuffer message, int messageStart )
|
||||
{
|
||||
int calculated = 0x1FF; //Initial fill of all ones
|
||||
int calculated = 0x0; //Initial fill of all ones
|
||||
|
||||
/* Iterate the set bits and XOR running checksum with lookup value */
|
||||
for (int i = message.nextSetBit( messageStart );
|
||||
@@ -353,7 +353,16 @@ public class CRCP25
|
||||
|
||||
int checksum = message.getInt( messageStart + 7, messageStart + 15 );
|
||||
|
||||
return ( calculated ^ checksum ) == 0 ? CRC.PASSED : CRC.FAILED_CRC;
|
||||
int residual = calculated ^ checksum;
|
||||
|
||||
// mLog.debug( "CALC:" + calculated + " CHECK:" + checksum + " RESID:" + residual );
|
||||
|
||||
if( residual == 0 || residual == 0x1FF )
|
||||
{
|
||||
return CRC.PASSED;
|
||||
}
|
||||
|
||||
return CRC.FAILED_CRC;
|
||||
}
|
||||
|
||||
|
||||
@@ -432,7 +441,7 @@ public class CRCP25
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
String raw = "000000010110100101010001000000000100010100000000000000000010000000011001001011100000000000000000011111110001000101001011100101110000101000110011";
|
||||
String raw = "000000001000001100000001010001111011000100001010010001111100000000000101000000000000000001000000000000110000000000000001101010101010101010101010";
|
||||
|
||||
BitSetBuffer message = BitSetBuffer.load( raw );
|
||||
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,11 +55,11 @@ import decode.p25.P25Decoder;
|
||||
import decode.passport.PassportDecoder;
|
||||
import decode.tait.Tait1200Decoder;
|
||||
|
||||
public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
public class AudioSourceFrame extends JInternalFrame implements PositionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( WaveSourceFrame.class );
|
||||
LoggerFactory.getLogger( AudioSourceFrame.class );
|
||||
|
||||
private WaveSource mSource;
|
||||
private JDesktopPane mDesktop;
|
||||
@@ -67,7 +67,7 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
private JLabel mCurrentPosition = new JLabel( "0" );
|
||||
private JComboBox<DecoderType> mComboDecoders;
|
||||
|
||||
public WaveSourceFrame( WaveSource source, JDesktopPane desktop )
|
||||
public AudioSourceFrame( WaveSource source, JDesktopPane desktop )
|
||||
{
|
||||
mSource = source;
|
||||
mSource.addListener( this );
|
||||
@@ -79,7 +79,7 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
|
||||
private void initGui()
|
||||
{
|
||||
setTitle( "Wave Source [" + mSource.getSampleType().toString() + "]" );
|
||||
setTitle( "Audio Source [" + mSource.getSampleType().toString() + "]" );
|
||||
setPreferredSize( new Dimension( 600, 150 ) );
|
||||
setSize( 600, 150 );
|
||||
|
||||
@@ -125,6 +125,8 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
|
||||
public class AddDecoderButton extends JButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public AddDecoderButton()
|
||||
{
|
||||
super( "Add" );
|
||||
@@ -167,6 +169,8 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
case TAIT_1200:
|
||||
decoder = new Tait1200Decoder( null );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( decoder != null )
|
||||
@@ -184,6 +188,8 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
|
||||
public class NextSampleButton extends JButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private WaveSource mSource;
|
||||
private int mCount;
|
||||
|
||||
@@ -207,7 +213,7 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
mLog.error( "Viewer - error trying to fetch next [" +
|
||||
mCount + "] samples", e );
|
||||
|
||||
JOptionPane.showMessageDialog( WaveSourceFrame.this,
|
||||
JOptionPane.showMessageDialog( AudioSourceFrame.this,
|
||||
"Cannot read " + mCount + " more samples [" +
|
||||
e.getLocalizedMessage() + "]",
|
||||
"Wave File Error",
|
||||
@@ -220,6 +226,8 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
|
||||
public class JumpToField extends JTextField
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private WaveSource mSource;
|
||||
|
||||
public JumpToField( WaveSource source )
|
||||
@@ -248,7 +256,7 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener
|
||||
mLog.error( "WaveSourceFrame - exception during jump "
|
||||
+ "to", e );
|
||||
|
||||
JOptionPane.showMessageDialog( WaveSourceFrame.this,
|
||||
JOptionPane.showMessageDialog( AudioSourceFrame.this,
|
||||
"Can't jump to position [" + getText() + "]",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE );
|
||||
240
src/instrument/gui/BasebandSourceFrame.java
Normal file
240
src/instrument/gui/BasebandSourceFrame.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 instrument.gui;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDesktopPane;
|
||||
import javax.swing.JInternalFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import source.wave.ComplexWaveSource;
|
||||
import source.wave.WaveSource;
|
||||
import source.wave.WaveSource.PositionListener;
|
||||
|
||||
public class BasebandSourceFrame extends JInternalFrame implements PositionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( BasebandSourceFrame.class );
|
||||
|
||||
private ComplexWaveSource mSource;
|
||||
private JDesktopPane mDesktop;
|
||||
|
||||
private JLabel mCurrentPosition = new JLabel( "0" );
|
||||
private JComboBox<Panels> mComboPanels;
|
||||
|
||||
public BasebandSourceFrame( ComplexWaveSource source, JDesktopPane desktop )
|
||||
{
|
||||
mSource = source;
|
||||
mSource.addListener( this );
|
||||
|
||||
mDesktop = desktop;
|
||||
|
||||
initGui();
|
||||
}
|
||||
|
||||
private void initGui()
|
||||
{
|
||||
setTitle( "Baseband Source [" + mSource.getSampleType().toString() + "]" );
|
||||
setPreferredSize( new Dimension( 600, 150 ) );
|
||||
setSize( 600, 150 );
|
||||
|
||||
setResizable( true );
|
||||
setClosable( true );
|
||||
setIconifiable( true );
|
||||
setMaximizable( false );
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout( new MigLayout() );
|
||||
|
||||
JLabel fileLabel = new JLabel( "File: " + mSource.getFile().getName() );
|
||||
fileLabel.setToolTipText( mSource.getFile().getName() );
|
||||
panel.add( fileLabel, "span,wrap" );
|
||||
|
||||
panel.add( new JLabel( "Jump:" ) );
|
||||
panel.add( new JumpToField( mSource ) );
|
||||
panel.add( new NextSampleButton( mSource, ">", 1 ) );
|
||||
panel.add( new NextSampleButton( mSource, "> 100 >", 100 ) );
|
||||
panel.add( new NextSampleButton( mSource, "> 1000 >", 1000 ) );
|
||||
panel.add( new JLabel( "Posn:" ) );
|
||||
panel.add( mCurrentPosition, "wrap" );
|
||||
|
||||
/* ComboBox: Viewer Panels */
|
||||
mComboPanels = new JComboBox<Panels>(
|
||||
new DefaultComboBoxModel<Panels>( Panels.values() ) );
|
||||
|
||||
panel.add( new JLabel( "Panels:" ) );
|
||||
panel.add( mComboPanels, "span 3,grow" );
|
||||
panel.add( new AddPanelButton(), "grow" );
|
||||
|
||||
add( panel );
|
||||
}
|
||||
|
||||
public class AddPanelButton extends JButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public AddPanelButton()
|
||||
{
|
||||
super( "Add" );
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent arg0 )
|
||||
{
|
||||
Panels selected =
|
||||
(Panels)mComboPanels.getSelectedItem();
|
||||
|
||||
mLog.info( "Selected:" + selected.name() );
|
||||
|
||||
switch( selected )
|
||||
{
|
||||
case CONSTELLATION:
|
||||
ConstellationPanel con = new ConstellationPanel();
|
||||
mDesktop.add( con );
|
||||
con.setVisible( true );
|
||||
|
||||
if( mSource != null )
|
||||
{
|
||||
mSource.addListener( (Listener<ComplexSample>)con );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public class NextSampleButton extends JButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private WaveSource mSource;
|
||||
private int mCount;
|
||||
|
||||
public NextSampleButton( WaveSource source, String label, int count )
|
||||
{
|
||||
super( label );
|
||||
mSource = source;
|
||||
mCount = count;
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent arg0 )
|
||||
{
|
||||
try
|
||||
{
|
||||
mSource.next( mCount );
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
mLog.error( "Viewer - error trying to fetch next [" +
|
||||
mCount + "] samples", e );
|
||||
|
||||
JOptionPane.showMessageDialog( BasebandSourceFrame.this,
|
||||
"Cannot read " + mCount + " more samples [" +
|
||||
e.getLocalizedMessage() + "]",
|
||||
"Wave File Error",
|
||||
JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public class JumpToField extends JTextField
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private WaveSource mSource;
|
||||
|
||||
public JumpToField( WaveSource source )
|
||||
{
|
||||
super( "0" );
|
||||
|
||||
mSource = source;
|
||||
|
||||
setMinimumSize( new Dimension( 100, getHeight() ) );
|
||||
|
||||
addFocusListener( new FocusListener()
|
||||
{
|
||||
@Override
|
||||
public void focusGained( FocusEvent arg0 ) {}
|
||||
|
||||
@Override
|
||||
public void focusLost( FocusEvent arg0 )
|
||||
{
|
||||
try
|
||||
{
|
||||
long position = Long.parseLong( getText() );
|
||||
mSource.jumpTo( position );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "WaveSourceFrame - exception during jump "
|
||||
+ "to", e );
|
||||
|
||||
JOptionPane.showMessageDialog( BasebandSourceFrame.this,
|
||||
"Can't jump to position [" + getText() + "]",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void positionUpdated( long position, boolean reset )
|
||||
{
|
||||
mCurrentPosition.setText( String.valueOf( position ) );
|
||||
|
||||
if( reset )
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum Panels
|
||||
{
|
||||
CONSTELLATION;
|
||||
}
|
||||
}
|
||||
76
src/instrument/gui/ConstellationPanel.java
Normal file
76
src/instrument/gui/ConstellationPanel.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 instrument.gui;
|
||||
|
||||
import gui.control.ConstellationViewer;
|
||||
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.JInternalFrame;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import source.wave.WaveSource.PositionListener;
|
||||
|
||||
public class ConstellationPanel extends JInternalFrame
|
||||
implements PositionListener, Listener<ComplexSample>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ConstellationPanel.class );
|
||||
|
||||
private ConstellationViewer mConstellation =
|
||||
new ConstellationViewer( 48000, 4800 );
|
||||
|
||||
public ConstellationPanel()
|
||||
{
|
||||
initGui();
|
||||
}
|
||||
|
||||
private void initGui()
|
||||
{
|
||||
setLayout( new MigLayout( "insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]" ) );
|
||||
|
||||
setTitle( "Constellation View" );
|
||||
setPreferredSize( new Dimension( 300, 300 ) );
|
||||
setSize( 300, 300 );
|
||||
|
||||
setResizable( true );
|
||||
setClosable( true );
|
||||
setIconifiable( true );
|
||||
setMaximizable( false );
|
||||
|
||||
add( mConstellation );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void positionUpdated( long position, boolean reset )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( ComplexSample sample )
|
||||
{
|
||||
mConstellation.receive( sample );
|
||||
}
|
||||
}
|
||||
@@ -51,21 +51,11 @@ public class Viewer
|
||||
public Viewer()
|
||||
{
|
||||
initGUI();
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
mFrame.setVisible( true );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
public void setVisible( boolean visible )
|
||||
{
|
||||
mFrame.setVisible( visible );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,10 +136,15 @@ public class Viewer
|
||||
source = floatSource;
|
||||
|
||||
mLog.info( "File opened as float wave file" );
|
||||
|
||||
AudioSourceFrame sourcePanel =
|
||||
new AudioSourceFrame( source, mDesktop );
|
||||
sourcePanel.setVisible( true );
|
||||
mDesktop.add( sourcePanel );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "Couldn't open file as float source", e);
|
||||
mLog.error( "Couldn't open file as float single-channel source" );
|
||||
}
|
||||
|
||||
if( source == null )
|
||||
@@ -163,6 +158,13 @@ public class Viewer
|
||||
source = complex;
|
||||
|
||||
mLog.info( "File opened as complex wave file" );
|
||||
|
||||
BasebandSourceFrame sourcePanel =
|
||||
new BasebandSourceFrame( complex, mDesktop );
|
||||
|
||||
sourcePanel.setVisible( true );
|
||||
|
||||
mDesktop.add( sourcePanel );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
@@ -174,20 +176,34 @@ public class Viewer
|
||||
{
|
||||
mSource = source;
|
||||
|
||||
WaveSourceFrame sourcePanel =
|
||||
new WaveSourceFrame( source, mDesktop );
|
||||
sourcePanel.setVisible( true );
|
||||
mDesktop.add( sourcePanel );
|
||||
}
|
||||
}
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
final Viewer viewer = new Viewer();
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
viewer.setVisible( true );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
public class SourceFileItem extends JMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SourceFileItem()
|
||||
{
|
||||
super( "Open" );
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
@@ -127,10 +128,14 @@ public class ComplexWaveSource extends WaveSource
|
||||
|
||||
mBytesPerFrame = mInputStream.getFormat().getFrameSize();
|
||||
|
||||
if( mBytesPerFrame != 2 || mInputStream.getFormat().getChannels() != 2 )
|
||||
AudioFormat format = mInputStream.getFormat();
|
||||
|
||||
if( format.getChannels() != 2 || format.getSampleSizeInBits() != 16 )
|
||||
{
|
||||
throw new IOException( "Unsupported Wave Format - requires two "
|
||||
+ "channels with 16-bit samples" );
|
||||
throw new IOException( "Unsupported Wave Format - EXPECTED: 2 " +
|
||||
"channels 16-bit samples FOUND: " +
|
||||
mInputStream.getFormat().getChannels() + " channels " +
|
||||
mInputStream.getFormat().getSampleSizeInBits() + "-bit samples" );
|
||||
}
|
||||
|
||||
mBuffer = new byte[ mBytesPerFrame * BUFFER_SAMPLE_SIZE ];
|
||||
|
||||
Reference in New Issue
Block a user