|
# coding: utf-8
|
|
|
|
require 'test/unit'
|
|
|
|
### GENERAL UTILITY FUNCTIONS ###
|
|
|
|
#
|
|
# Calculate allowable relative error for two floats
|
|
# See: http://whynotwiki.com/Ruby_/_Numbers
|
|
#
|
|
def rel_epsilon(c1,c2)
|
|
minerrlimit = 100.0 * Float::MIN
|
|
maxabs = [c1.abs, c2.abs].max
|
|
rel = maxabs * Float::EPSILON * 25
|
|
if rel < minerrlimit
|
|
rel = minerrlimit
|
|
end
|
|
return rel
|
|
end
|
|
|
|
def assert_in_epsilon(c1,c2,message="")
|
|
assert_in_delta(c1,c2,rel_epsilon(c1,c2),message)
|
|
end
|
|
|
|
def assert_phasor_in_epsilon(c1,c2,message="")
|
|
c1_abs, c1_angle = c1.polar
|
|
c2_abs, c2_angle = c2.polar
|
|
assert_in_delta(c1_abs,c2_abs,rel_epsilon(c1_abs,c2_abs),message)
|
|
assert_in_delta(c1_angle,c2_angle,rel_epsilon(c1_angle,c2_angle),message)
|
|
end
|
|
|
|
|
|
### TESTS ###
|
|
|
|
# Complex tests
|
|
class TestComplex < Test::Unit::TestCase
|
|
require 'complex' unless defined?(Complex)
|
|
|
|
def test_complex_coercion
|
|
p = Phasor.new(Math.sqrt(2),Math::PI/4)
|
|
c = Complex.new(1,1)
|
|
assert_block "Couldn't coerce complex into phasor" do
|
|
@x, @y = c.coerce(p)
|
|
end
|
|
assert_kind_of Phasor, @x
|
|
assert_kind_of Phasor, @y
|
|
assert_equal @x, @y
|
|
end
|
|
|
|
def test_complex_equals_phasor
|
|
p = Phasor.new(Math.sqrt(2),Math::PI/4)
|
|
c = Complex.new(1,1)
|
|
assert c == p
|
|
end
|
|
|
|
end
|
|
|
|
class TestPhasor < Test::Unit::TestCase
|
|
require '../lib/phasor' unless defined?(Phasor)
|
|
|
|
# Numeric tests
|
|
def test_numeric_phase
|
|
assert_instance_of Phasor, 0.1.phase
|
|
assert_equal 5, 5.phase.angle
|
|
end
|
|
|
|
# Phasor tests
|
|
def test_scalar
|
|
assert Phasor.scalar?(1)
|
|
assert Phasor.scalar?(1.0)
|
|
assert Phasor.scalar?(Rational(1,2)) if defined?(Rational)
|
|
end
|
|
|
|
def test_initialization
|
|
p = Phasor.new(1,2)
|
|
assert_equal 1, p.abs
|
|
assert_equal 2, p.angle
|
|
assert_raise TypeError do
|
|
Phasor.new(p)
|
|
end
|
|
assert_instance_of Phasor, Phasor.new(3,4)
|
|
end
|
|
|
|
def test_aliases
|
|
p = Phasor.new(1,2)
|
|
assert_equal 1, p.amp
|
|
assert_equal 2, p.phase
|
|
assert_equal 2, p.arg
|
|
end
|
|
|
|
def test_coordinate_access
|
|
p = Phasor.new(1,Math::PI/4)
|
|
assert_in_epsilon Math.sqrt(0.5), p.real
|
|
assert_in_epsilon Math.sqrt(0.5), p.imag
|
|
end
|
|
|
|
def test_conversions
|
|
p = Phasor(3,-4) # Fix angle wrap later
|
|
#p = Phasor(3,3)
|
|
assert_phasor_in_epsilon(p, p.to_complex.to_phasor)
|
|
end
|
|
|
|
def test_addition
|
|
z1 = Complex(9,3).to_phasor
|
|
z2 = Complex(3,7).to_phasor
|
|
assert_phasor_in_epsilon(Complex(12,10), (z1+z2).to_complex)
|
|
d = Phasor(3,0)
|
|
e = Phasor(4,Math::PI/2)
|
|
assert_phasor_in_epsilon(Complex(3,4).to_phasor, d+e)
|
|
a = Complex(1,2).to_phasor
|
|
b = Complex(3,4).to_phasor
|
|
assert_phasor_in_epsilon(Complex(4,6), (a+b).to_complex)
|
|
v1 = Phasor(100, 1.0471975511966) # 60°
|
|
v2 = Phasor(130, 2.44346095279206) # 140°
|
|
assert_phasor_in_epsilon(Phasor(177.242355601984, 1.85434312605519), v1+v2)
|
|
assert_phasor_in_epsilon(v1+v2, v2+v1)
|
|
z = Complex(1,1)
|
|
q = Phasor(1, 0)
|
|
assert_nothing_raised(NoMethodError) { a+b+z+q }
|
|
end
|
|
|
|
def test_scalar_addition
|
|
a = Phasor(4,Math::PI/2)
|
|
b = 3
|
|
assert_phasor_in_epsilon(Complex(3,4).to_phasor, a+b)
|
|
c = Complex(6,4).to_phasor
|
|
d = -2
|
|
assert_phasor_in_epsilon(Complex(4,4).to_phasor, c+d)
|
|
end
|
|
|
|
def test_substraction
|
|
z1 = Complex(9,3).to_phasor
|
|
z2 = Complex(3,7).to_phasor
|
|
assert_phasor_in_epsilon(Complex(6,-4), (z1-z2).to_complex)
|
|
d = Phasor(3,0)
|
|
e = Phasor(4,Math::PI/2)
|
|
assert_phasor_in_epsilon(Complex(3,-4).to_phasor, d-e)
|
|
a = Complex(1,2).to_phasor
|
|
b = Complex(3,4).to_phasor
|
|
assert_phasor_in_epsilon(Complex(-2.0,-2.0), (a-b).to_complex)
|
|
z = Complex(1,1)
|
|
q = Phasor(1, 0)
|
|
assert_nothing_raised(NoMethodError) { a-b-z-q }
|
|
end
|
|
|
|
def test_scalar_substraction
|
|
a = Phasor(4,Math::PI/2)
|
|
b = 3
|
|
assert_phasor_in_epsilon(Complex(3,-4).to_phasor, b-a)
|
|
c = Complex(6,4).to_phasor
|
|
d = -2
|
|
assert_phasor_in_epsilon(Complex(8,4).to_phasor, c-d)
|
|
end
|
|
|
|
def test_multiplication
|
|
a = Phasor(1,2)
|
|
b = Phasor(3,4)
|
|
c = Phasor(3,6)
|
|
s = 0.5
|
|
r = Rational(1,2) if defined?(Rational)
|
|
assert_equal c, a*b
|
|
assert_equal a*b, b*a
|
|
assert_equal Phasor(0.5, 2), a * s, "Multiplication by scalar failed"
|
|
assert_equal Phasor(0.5, 2), a * r, "Multiplication by rational failed" if defined?(Rational)
|
|
end
|
|
|
|
def test_division
|
|
a = Phasor(1,2)
|
|
b = Phasor(3,4)
|
|
c = Phasor(3,6)
|
|
s = 0.5
|
|
r = Rational(1,2) if defined?(Rational)
|
|
assert_equal a, c/b
|
|
assert_equal b, c/a
|
|
assert_equal 1, b/b
|
|
assert_equal Phasor(2, 2), a / s, "Division by scalar failed"
|
|
assert_equal Phasor(2, 2), a / r, "Division by rational failed" if defined?(Rational)
|
|
end
|
|
|
|
def test_exponentation
|
|
x = Phasor(0.5**(1.0/6), Math::PI/6)
|
|
w = x**6
|
|
assert_phasor_in_epsilon(Phasor(0.5, Math::PI), w)
|
|
assert_equal(x, w**(1.0/6))
|
|
p = Phasor(3,Math::PI/4)
|
|
q = Phasor(4,Math::PI/5)
|
|
c = p.to_complex
|
|
d = q.to_complex
|
|
assert_phasor_in_epsilon (c**d).to_phasor, p**q
|
|
z1 = Complex(9,3).to_phasor
|
|
z2 = Complex(3,7).to_phasor
|
|
assert_phasor_in_epsilon (z1.to_complex ** z2.to_complex).to_phasor, z1 ** z2
|
|
end
|
|
|
|
def test_modulo
|
|
x = Phasor(0.5**(1.0/6), Math::PI/6)
|
|
w = x**6
|
|
end
|
|
|
|
def test_imaginary_number
|
|
z = Complex(1,1)
|
|
i = Phasor(1, Math::PI/2)
|
|
assert_equal Complex::I, Phasor::I
|
|
assert_equal Complex(-1,1), i*z
|
|
end
|
|
end
|
|
|
|
|
|
class TestComplexBasics < Test::Unit::TestCase
|
|
|
|
def test_plus_integer
|
|
assert_phasor_in_epsilon(Complex(8,7).to_phasor,Complex(3,4).to_phasor + Complex(5,3).to_phasor)
|
|
assert_phasor_in_epsilon(Complex(5,1).to_phasor,Complex(4,1).to_phasor + 1)
|
|
assert_phasor_in_epsilon(Complex(5,1).to_phasor,1 + Complex(4,1).to_phasor)
|
|
end
|
|
|
|
def test_minus_integer
|
|
assert_phasor_in_epsilon(Complex(2,4).to_phasor, Complex(3,2).to_phasor - Complex(1,-2).to_phasor)
|
|
assert_phasor_in_epsilon(Complex(4,5).to_phasor, Complex(5,5).to_phasor - 1)
|
|
assert_phasor_in_epsilon(Complex(1,-2).to_phasor, 2 - Complex(1,2).to_phasor)
|
|
end
|
|
|
|
def test_times_integer
|
|
assert_phasor_in_epsilon(Complex(-1,0).to_phasor,Complex(0,1).to_phasor * Complex(0,1).to_phasor)
|
|
assert_phasor_in_epsilon(Complex(3,3).to_phasor, Complex(1,1).to_phasor * 3)
|
|
assert_phasor_in_epsilon(Complex(4,-2).to_phasor, 2 * Complex(2,-1).to_phasor)
|
|
end
|
|
|
|
def test_plus_float
|
|
assert_phasor_in_epsilon(Complex(1.0,4.0).to_phasor,Complex(2.0,2.0).to_phasor+Complex(-1.0,2.0).to_phasor)
|
|
end
|
|
|
|
def test_minus_float
|
|
assert_phasor_in_epsilon(Complex(3.0,0.0).to_phasor,Complex(2.0,2.0).to_phasor-Complex(-1.0,2.0).to_phasor)
|
|
end
|
|
|
|
def test_times_float
|
|
assert_phasor_in_epsilon(Complex(-2,1).to_phasor,Complex(1,2).to_phasor*Complex(0,1).to_phasor)
|
|
end
|
|
|
|
def test_division_float
|
|
[Complex(1.0,25.0).to_phasor, Complex(8,97).to_phasor, Complex(Float::MAX/2,
|
|
Float::MAX/3).to_phasor,
|
|
Complex(100.0 / Float::MAX, 200.0 / Float::MAX).to_phasor].each { | c |
|
|
assert_phasor_in_epsilon(Complex(1.0,0.0).to_phasor, c / c)
|
|
}
|
|
assert_phasor_in_epsilon(Complex(-1.0,0.0).to_phasor,Complex(0.0,1.0).to_phasor / Complex(0.0,-1.0).to_phasor)
|
|
end
|
|
|
|
def test_eps_log
|
|
assert_phasor_in_epsilon(Complex(2.0,1.0).to_phasor,Math.log(Math.exp(Complex(2.0,1.0).to_phasor)))
|
|
end
|
|
end
|
|
|
|
class TestMath < Test::Unit::TestCase
|
|
def test_log
|
|
p = Phasor(3,Math::PI/6)
|
|
assert_equal(Math.log(p.to_complex).to_phasor, Math.flog(p))
|
|
end
|
|
|
|
def test_exp
|
|
p = Phasor(3,0.5)
|
|
assert_phasor_in_epsilon(Math.exp(p.to_complex).to_phasor, Math.fexp(p))
|
|
assert_phasor_in_epsilon(Math.exp(Complex::I).to_phasor, Math.fexp(Phasor::I))
|
|
assert_phasor_in_epsilon(Phasor(1), Math.fexp(2*Math::PI*Phasor::I))
|
|
end
|
|
end
|