/*******************************************************************************
* 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 sample.complex;
import java.io.Serializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Complex sample and related utility methods
*/
public class Complex implements Serializable
{
private final static Logger mLog =
LoggerFactory.getLogger( Complex.class );
private static final long serialVersionUID = 1L;
private float mLeft;
private float mRight;
public Complex( float left, float right )
{
mLeft = left;
mRight = right;
}
public Complex()
{
this( 0.0f, 0.0f );
}
public void setInphase( float inphase )
{
mLeft = inphase;
}
public void setQuadrature( float quadrature )
{
mRight = quadrature;
}
public void setValues( float inphase, float quadrature )
{
mLeft = inphase;
mRight = quadrature;
}
public Complex copy()
{
return new Complex( mLeft, mRight );
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "I:" );
sb.append( mLeft );
sb.append( " Q:" );
sb.append( mRight );
return sb.toString();
}
/**
* Returns a new sample representing the conjugate of this one
*/
public Complex conjugate()
{
return new Complex( mLeft, -mRight );
}
/**
* Multiplies this sample by the scalor value
*/
public void multiply( float scalor )
{
mLeft *= scalor;
mRight *= scalor;
}
public static Complex multiply( Complex sample, float scalor )
{
return new Complex( sample.left() * scalor,
sample.right() * scalor );
}
/**
* Calculates the inphase component of multiplying two complex numbers, A and B.
*/
public static float multiplyInphase( float inphaseA, float quadratureA, float inphaseB, float quadratureB )
{
return ( inphaseA * inphaseB ) - ( quadratureA * quadratureB );
}
/**
* Calculates the quadrature component of multiplying two complex numbers, A and B.
*/
public static float multiplyQuadrature( float inphaseA, float quadratureA, float inphaseB, float quadratureB )
{
return ( quadratureA * inphaseB ) + ( inphaseA * quadratureB );
}
/**
* Multiplies this sample by the multiplier sample
*/
public void multiply( Complex multiplier )
{
float inphase = multiplyInphase( inphase(), quadrature(),
multiplier.inphase(), multiplier.quadrature() );
float quadrature = multiplyQuadrature( inphase(), quadrature(),
multiplier.inphase(), multiplier.quadrature() );
mLeft = inphase;
mRight = quadrature;
}
/**
* Multiplies both samples returning a new sample with the results
*/
public static Complex multiply( Complex sample1, Complex sample2 )
{
float inphase = multiplyInphase( sample1.inphase(), sample1.quadrature(),
sample2.inphase(), sample2.quadrature() );
float quadrature = multiplyQuadrature( sample1.inphase(), sample1.quadrature(),
sample2.inphase(), sample2.quadrature() );
return new Complex( inphase, quadrature );
}
public static Complex multiply( Complex sample,
Float inphase, Float quadrature )
{
float i = multiplyInphase( sample.inphase(), sample.quadrature(),
inphase, quadrature );
float q = multiplyQuadrature( sample.inphase(), sample.quadrature(),
inphase, quadrature );
return new Complex( i, q );
}
public static Complex multiply( Float inphase, Float quadrature, Complex sample )
{
float i = multiplyInphase( inphase, quadrature, sample.inphase(), sample.quadrature() );
float q = multiplyQuadrature( inphase, quadrature, sample.inphase(), sample.quadrature() );
return new Complex( i, q );
}
public static Complex multiply( float inphaseA, float quadratureA, float inphaseB, float quadratureB )
{
float i = multiplyInphase( inphaseA, quadratureA, inphaseB, quadratureB );
float q = multiplyQuadrature( inphaseA, quadratureA, inphaseB, quadratureB );
return new Complex( i, q );
}
/**
* Adds the adder sample value to this sample
*/
public void add( Complex adder )
{
mLeft += adder.left();
mRight += adder.right();
}
/**
* Adds the two complex samples returning a new complex sample with the result
*/
public static Complex add( Complex first, Complex second )
{
return new Complex( first.left() + second.left(),
first.right() + second.right() );
}
/**
* Adds the two complex samples returning a new complex sample with the result
*/
public static Complex subtract( Complex first, Complex second )
{
return new Complex( first.left() - second.left(),
first.right() - second.right() );
}
/**
* Magnitude of this sample
*/
public float magnitude()
{
return (float)Math.sqrt( magnitudeSquared() );
}
public static float magnitude( float inphase, float quadrature )
{
return (float)Math.sqrt( ( inphase * inphase ) + ( quadrature * quadrature ) );
}
/**
* Magnitude squared of this sample
*/
public float magnitudeSquared()
{
return norm();
}
/**
* Norm of this sample = ( i * i ) + ( q * q )
*/
public float norm()
{
return (float)( ( inphase() * inphase() ) +
( quadrature() * quadrature() ) );
}
/**
* Returns the vector length to 1 (unit circle)
*/
public void normalize()
{
float magnitude = magnitude();
if( magnitude != 0 )
{
multiply( (float)( 1.0f / magnitude() ) );
}
}
/**
* Returns the vector length to 1 (unit circle),
* avoiding square root multiplication
*/
public void fastNormalize()
{
multiply( (float)( 1.95f - magnitudeSquared() ) );
}
public float left()
{
return mLeft;
}
public float right()
{
return mRight;
}
public float inphase()
{
return mLeft;
}
/**
* Absolute value of in-phase component
*/
public float inPhaseAbsolute()
{
return Math.abs( mLeft );
}
public float quadrature()
{
return mRight;
}
/**
* Absolute value of quadrature component
*/
public float quadratureAbsolute()
{
return Math.abs( mRight );
}
public float x()
{
return mLeft;
}
public float y()
{
return mRight;
}
public float real()
{
return mLeft;
}
/**
* Absolute value of real component
*/
public float realAbsolute()
{
return Math.abs( mLeft );
}
public float imaginary()
{
return mRight;
}
/**
* Absolute value of imaginary component
*/
public float imaginaryAbsolute()
{
return Math.abs( mRight );
}
/**
* Returns the greater absolute value between left and right values
*/
public float maximumAbsolute()
{
if( Math.abs( mLeft ) > Math.abs( mRight ) )
{
return Math.abs( mLeft );
}
else
{
return Math.abs( mRight );
}
}
/**
* Creates a new complex sample representing the angle with unit circle
* magnitude
* @param angle in radians
* @return
*/
public static Complex fromAngle( float angle )
{
return new Complex( (float)Math.cos( angle ),
(float)Math.sin( angle ) );
}
/**
* Angle of this sample in radians
*/
public float angle()
{
return (float)Math.atan2( y(), x() );
}
/**
* Constrains the i and q quantities to +/- value.
*
* @param value - maximum absolute value
*/
public void clip( float value )
{
if( mLeft > value )
{
mLeft = value;
}
else if( mLeft < -value )
{
mLeft = -value;
}
if( mRight > value )
{
mRight = value;
}
else if( mRight < -value )
{
mRight = -value;
}
}
/**
* Angle of this sample in degrees
*/
public float polarAngle()
{
return (float)Math.toDegrees( angle() );
}
/**
* Provides an approximate magnitude value for this sample.
*/
public float envelope()
{
return envelope( mLeft, mRight );
}
public static float envelope( float inphase, float quadrature )
{
float inphaseAbsolute = Math.abs( inphase );
float quadratureAbsolute = Math.abs( quadrature );
if( inphaseAbsolute > quadratureAbsolute )
{
return inphaseAbsolute + ( 0.4f * quadratureAbsolute );
}
else
{
return quadratureAbsolute + ( 0.4f * inphaseAbsolute );
}
}
public static void main( String[] args )
{
double angle = Math.PI; //180 degrees
Complex s1 = new Complex( (float)Math.sin( angle ), (float)Math.cos( angle ) );
Complex tap = new Complex( 1.0f, -0.1f );
Complex convolved = Complex.multiply( s1, tap );
mLog.debug( "s: " + s1.toString() +
" t: " + tap.toString() +
" convolved: " + convolved.toString() );
}
}