add E axis

This commit is contained in:
Nikolay Khabarov
2017-05-27 18:47:01 +03:00
parent 665ba2ad1e
commit a87e2a379b
11 changed files with 238 additions and 147 deletions
+45 -26
View File
@@ -5,7 +5,7 @@ from cnc.coordinates import *
class TestCoordinates(unittest.TestCase):
def setUp(self):
self.default = Coordinates(96, 102, 150)
self.default = Coordinates(96, 102, 150, 228)
def tearDown(self):
pass
@@ -13,110 +13,129 @@ class TestCoordinates(unittest.TestCase):
def test_constructor(self):
# constructor rounds values to 10 digits after the point
self.assertRaises(TypeError, Coordinates)
c = Coordinates(1.00000000005, 2.00000000004, -3.5000000009)
c = Coordinates(1.00000000005, 2.00000000004, -3.5000000009, 0.0)
self.assertEqual(c.x, 1.0000000001)
self.assertEqual(c.y, 2.0)
self.assertEqual(c.z, -3.5000000009)
self.assertEqual(c.e, 0.0)
def test_zero(self):
c = Coordinates(0, 0, 0)
c = Coordinates(0, 0, 0, 0)
self.assertTrue(c.is_zero())
def test_aabb(self):
# aabb - Axis Aligned Bounded Box.
# original method checks if point belongs aabb.
p1 = Coordinates(0, 0, 0)
p2 = Coordinates(2, 2, 2)
c = Coordinates(1, 1, 1)
p1 = Coordinates(0, 0, 0, 0)
p2 = Coordinates(2, 2, 2, 0)
c = Coordinates(1, 1, 1, 0)
self.assertTrue(c.is_in_aabb(p1, p2))
self.assertTrue(c.is_in_aabb(p2, p1))
c = Coordinates(0, 0, 0)
c = Coordinates(0, 0, 0, 0)
self.assertTrue(c.is_in_aabb(p1, p2))
c = Coordinates(2, 2, 2)
c = Coordinates(2, 2, 2, 0)
self.assertTrue(c.is_in_aabb(p1, p2))
c = Coordinates(2, 3, 2)
c = Coordinates(2, 3, 2, 0)
self.assertFalse(c.is_in_aabb(p1, p2))
c = Coordinates(-1, 1, 1)
c = Coordinates(-1, 1, 1, 0)
self.assertFalse(c.is_in_aabb(p1, p2))
c = Coordinates(1, 1, 3)
c = Coordinates(1, 1, 3, 0)
self.assertFalse(c.is_in_aabb(p1, p2))
def test_length(self):
c = Coordinates(-1, 0, 0)
c = Coordinates(-1, 0, 0, 0)
self.assertEqual(c.length(), 1)
c = Coordinates(0, 3, -4)
c = Coordinates(0, 3, -4, 0)
self.assertEqual(c.length(), 5)
c = Coordinates(3, 4, 12)
c = Coordinates(3, 4, 12, 0)
self.assertEqual(c.length(), 13)
c = Coordinates(1, 1, 1, 1)
self.assertEqual(c.length(), 2)
def test_round(self):
# round works in another way then Python's round.
# This round() rounds digits with specified step.
c = Coordinates(1.5, -1.4, 3.05)
r = c.round(1, 1, 1)
c = Coordinates(1.5, -1.4, 3.05, 3.5)
r = c.round(1, 1, 1, 1)
self.assertEqual(r.x, 2.0)
self.assertEqual(r.y, -1.0)
self.assertEqual(r.z, 3.0)
r = c.round(0.25, 0.25, 0.25)
self.assertEqual(r.e, 4.0)
r = c.round(0.25, 0.25, 0.25, 0.25)
self.assertEqual(r.x, 1.5)
self.assertEqual(r.y, -1.5)
self.assertEqual(r.z, 3.0)
self.assertEqual(r.e, 3.5)
def test_max(self):
self.assertEqual(self.default.find_max(), max(self.default.x,
self.default.y,
self.default.z))
self.default.y,
self.default.z,
self.default.e))
# build-in function overriding tests
def test_add(self):
r = self.default + Coordinates(1, 2, 3)
r = self.default + Coordinates(1, 2, 3, 4)
self.assertEqual(r.x, self.default.x + 1)
self.assertEqual(r.y, self.default.y + 2)
self.assertEqual(r.z, self.default.z + 3)
self.assertEqual(r.e, self.default.e + 4)
def test_sub(self):
r = self.default - Coordinates(1, 2, 3)
r = self.default - Coordinates(1, 2, 3, 4)
self.assertEqual(r.x, self.default.x - 1)
self.assertEqual(r.y, self.default.y - 2)
self.assertEqual(r.z, self.default.z - 3)
self.assertEqual(r.e, self.default.e - 4)
def test_mul(self):
r = self.default * 2
self.assertEqual(r.x, self.default.x * 2)
self.assertEqual(r.y, self.default.y * 2)
self.assertEqual(r.z, self.default.z * 2)
self.assertEqual(r.e, self.default.e * 2)
def test_div(self):
r = self.default / 2
self.assertEqual(r.x, self.default.x / 2)
self.assertEqual(r.y, self.default.y / 2)
self.assertEqual(r.z, self.default.z / 2)
self.assertEqual(r.e, self.default.e / 2)
def test_truediv(self):
r = self.default / 3.0
self.assertEqual(r.x, self.default.x / 3.0)
self.assertEqual(r.y, self.default.y / 3.0)
self.assertEqual(r.z, self.default.z / 3.0)
self.assertEqual(r.e, self.default.e / 3.0)
def test_eq(self):
a = Coordinates(self.default.x, self.default.y, self.default.z)
a = Coordinates(self.default.x, self.default.y, self.default.z,
self.default.e)
self.assertTrue(a == self.default)
a = Coordinates(-self.default.x, self.default.y, self.default.z)
a = Coordinates(-self.default.x, self.default.y, self.default.z,
self.default.e)
self.assertFalse(a == self.default)
a = Coordinates(self.default.x, -self.default.y, self.default.z)
a = Coordinates(self.default.x, -self.default.y, self.default.z,
self.default.e)
self.assertFalse(a == self.default)
a = Coordinates(self.default.x, self.default.y, -self.default.z)
a = Coordinates(self.default.x, self.default.y, -self.default.z,
self.default.e)
self.assertFalse(a == self.default)
a = Coordinates(self.default.x, self.default.y, self.default.z,
-self.default.e)
self.assertFalse(a == self.default)
def test_str(self):
self.assertTrue(isinstance(str(self.default), str))
def test_abs(self):
c = Coordinates(-1, -2.5, -99)
c = Coordinates(-1, -2.5, -99, -23)
r = abs(c)
self.assertEqual(r.x, 1.0)
self.assertEqual(r.y, 2.5)
self.assertEqual(r.z, 99.0)
self.assertEqual(r.e, 23.0)
if __name__ == '__main__':
+13 -6
View File
@@ -7,7 +7,7 @@ from cnc.gcode import *
class TestGCode(unittest.TestCase):
def setUp(self):
self.default = Coordinates(-7, 8, 9)
self.default = Coordinates(-7, 8, 9, -10)
def tearDown(self):
pass
@@ -16,28 +16,31 @@ class TestGCode(unittest.TestCase):
# GCode shouldn't be created with constructor, but since it uses
# internally, let's check it.
self.assertRaises(TypeError, GCode)
gc = GCode({"X": "1", "Y": "-2", "Z":"0", "G": "1"})
gc = GCode({"X": "1", "Y": "-2", "Z":"0", "E": 99, "G": "1"})
self.assertEqual(gc.coordinates(self.default, 1).x, 1.0)
self.assertEqual(gc.coordinates(self.default, 1).y, -2.0)
self.assertEqual(gc.coordinates(self.default, 1).z, 0.0)
self.assertEqual(gc.coordinates(self.default, 1).e, 99.0)
def test_parser(self):
gc = GCode.parse_line("G1X2Y-3Z4")
gc = GCode.parse_line("G1X2Y-3Z4E1.5")
self.assertEqual(gc.command(), "G1")
self.assertEqual(gc.coordinates(self.default, 1).x, 2.0)
self.assertEqual(gc.coordinates(self.default, 1).y, -3.0)
self.assertEqual(gc.coordinates(self.default, 1).z, 4.0)
self.assertEqual(gc.coordinates(self.default, 1).e, 1.5)
gc = GCode.parse_line("")
self.assertIsNone(gc)
def test_defaults(self):
# defaults are values which should be returned if corresponding
# value doesn't exist in gcode.
default = Coordinates(11, -12, 14)
default = Coordinates(11, -12, 14, -10)
gc = GCode.parse_line("G1")
self.assertEqual(gc.coordinates(default, 1).x, 11.0)
self.assertEqual(gc.coordinates(default, 1).y, -12.0)
self.assertEqual(gc.coordinates(default, 1).z, 14.0)
self.assertEqual(gc.coordinates(default, 1).e, -10.0)
def test_commands(self):
gc = GCode({"G": "1"})
@@ -48,11 +51,12 @@ class TestGCode(unittest.TestCase):
def test_case_sensitivity(self):
gc = GCode.parse_line("m111")
self.assertEqual(gc.command(), "M111")
gc = GCode.parse_line("g2X3y-4Z5")
gc = GCode.parse_line("g2X3y-4Z5e6")
self.assertEqual(gc.command(), "G2")
self.assertEqual(gc.coordinates(self.default, 1).x, 3.0)
self.assertEqual(gc.coordinates(self.default, 1).y, -4.0)
self.assertEqual(gc.coordinates(self.default, 1).z, 5.0)
self.assertEqual(gc.coordinates(self.default, 1).e, 6.0)
def test_has_coordinates(self):
gc = GCode.parse_line("X2Y-3Z4")
@@ -65,6 +69,8 @@ class TestGCode(unittest.TestCase):
self.assertTrue(gc.has_coordinates())
gc = GCode.parse_line("Z1")
self.assertTrue(gc.has_coordinates())
gc = GCode.parse_line("E1")
self.assertTrue(gc.has_coordinates())
def test_radius(self):
gc = GCode.parse_line("G2I1J2K3")
@@ -78,10 +84,11 @@ class TestGCode(unittest.TestCase):
def test_multiply(self):
# getting coordinates could modify value be specified multiplier.
gc = GCode.parse_line("X2 Y-3 Z4")
gc = GCode.parse_line("X2 Y-3 Z4 E5")
self.assertEqual(gc.coordinates(self.default, 25.4).x, 50.8)
self.assertEqual(gc.coordinates(self.default, 2).y, -6)
self.assertEqual(gc.coordinates(self.default, 0).y, 0)
self.assertEqual(gc.coordinates(self.default, 5).e, 25)
def test_whitespaces(self):
gc = GCode.parse_line("X1 Y2")
+39 -38
View File
@@ -20,28 +20,28 @@ class TestGMachine(unittest.TestCase):
m.do_command(GCode.parse_line("G91"))
m.do_command(GCode.parse_line("X1Y1Z1"))
m.reset()
m.do_command(GCode.parse_line("X3Y4Z5"))
self.assertEqual(m.position(), Coordinates(3, 4, 5))
m.do_command(GCode.parse_line("X3Y4Z5E6"))
self.assertEqual(m.position(), Coordinates(3, 4, 5, 6))
def test_release(self):
# release homes head.
m = GMachine()
m.do_command(GCode.parse_line("X1Y2Z3"))
m.do_command(GCode.parse_line("X1Y2Z3E4"))
m.release()
self.assertEqual(m.position(), Coordinates(0, 0, 0))
self.assertEqual(m.position(), Coordinates(0, 0, 0, 4))
def test_home(self):
m = GMachine()
m.do_command(GCode.parse_line("X1Y2Z3"))
m.do_command(GCode.parse_line("X1Y2Z3E4"))
m.home()
self.assertEqual(m.position(), Coordinates(0, 0, 0))
self.assertEqual(m.position(), Coordinates(0, 0, 0, 4))
def test_none(self):
# GMachine must ignore None commands, since GCode.parse_line()
# returns None if no gcode found in line.
m = GMachine()
m.do_command(None)
self.assertEqual(m.position(), Coordinates(0, 0, 0))
self.assertEqual(m.position(), Coordinates(0, 0, 0, 0))
def test_unknown(self):
# Test commands which doesn't exists
@@ -54,10 +54,10 @@ class TestGMachine(unittest.TestCase):
# Test gcode commands.
def test_g0_g1(self):
m = GMachine()
m.do_command(GCode.parse_line("G0X3Y2Z1"))
self.assertEqual(m.position(), Coordinates(3, 2, 1))
m.do_command(GCode.parse_line("G1X1Y2Z3"))
self.assertEqual(m.position(), Coordinates(1, 2, 3))
m.do_command(GCode.parse_line("G0X3Y2Z1E-2"))
self.assertEqual(m.position(), Coordinates(3, 2, 1, -2))
m.do_command(GCode.parse_line("G1X1Y2Z3E4"))
self.assertEqual(m.position(), Coordinates(1, 2, 3, 4))
self.assertRaises(GMachineException,
m.do_command, GCode.parse_line("G1F-1"))
self.assertRaises(GMachineException,
@@ -86,7 +86,7 @@ class TestGMachine(unittest.TestCase):
m.do_command, GCode.parse_line("G2X99999999Y99999999I1J1"))
self.assertRaises(GMachineException,
m.do_command, GCode.parse_line("G2X2Y2Z99999999I1J1"))
self.assertEqual(m.position(), Coordinates(0, 0, 0))
self.assertEqual(m.position(), Coordinates(0, 0, 0, 0))
self.assertRaises(GMachineException,
m.do_command, GCode.parse_line("G2X4Y4I2J2"))
self.assertRaises(GMachineException,
@@ -94,10 +94,10 @@ class TestGMachine(unittest.TestCase):
m.do_command(GCode.parse_line("G1X1"))
m.do_command(GCode.parse_line("G2J1"))
m.do_command(GCode.parse_line("G3J1"))
self.assertEqual(m.position(), Coordinates(1, 0, 0))
self.assertEqual(m.position(), Coordinates(1, 0, 0, 0))
m.do_command(GCode.parse_line("G1X5Y5"))
m.do_command(GCode.parse_line("G2X0Y0Z5I-2J-2"))
self.assertEqual(m.position(), Coordinates(0, 0, 5))
self.assertEqual(m.position(), Coordinates(0, 0, 5, 0))
def test_g4(self):
@@ -120,47 +120,48 @@ class TestGMachine(unittest.TestCase):
def test_g20_g21(self):
m = GMachine()
m.do_command(GCode.parse_line("G20"))
m.do_command(GCode.parse_line("X3Y2Z1"))
self.assertEqual(m.position(), Coordinates(76.2, 50.8, 25.4))
m.do_command(GCode.parse_line("X3Y2Z1E0.5"))
self.assertEqual(m.position(), Coordinates(76.2, 50.8, 25.4, 12.7))
m.do_command(GCode.parse_line("G21"))
m.do_command(GCode.parse_line("X3Y2Z1"))
self.assertEqual(m.position(), Coordinates(3, 2, 1))
m.do_command(GCode.parse_line("X3Y2Z1E0.5"))
self.assertEqual(m.position(), Coordinates(3, 2, 1, 0.5))
def test_g90_g91(self):
m = GMachine()
m.do_command(GCode.parse_line("G91"))
m.do_command(GCode.parse_line("X1Y1Z1E1"))
m.do_command(GCode.parse_line("X1Y1Z1"))
m.do_command(GCode.parse_line("X1Y1"))
m.do_command(GCode.parse_line("X1"))
self.assertEqual(m.position(), Coordinates(3, 2, 1))
m.do_command(GCode.parse_line("X-1Y-1Z-1"))
self.assertEqual(m.position(), Coordinates(4, 3, 2, 1))
m.do_command(GCode.parse_line("X-1Y-1Z-1E-1"))
m.do_command(GCode.parse_line("G90"))
m.do_command(GCode.parse_line("X1Y1Z1"))
self.assertEqual(m.position(), Coordinates(1, 1, 1))
m.do_command(GCode.parse_line("X1Y1Z1E1"))
self.assertEqual(m.position(), Coordinates(1, 1, 1, 1))
def test_g90_g92(self):
m = GMachine()
m.do_command(GCode.parse_line("G92X100Y100Z100"))
m.do_command(GCode.parse_line("X101Y102Z103"))
self.assertEqual(m.position(), Coordinates(1, 2, 3))
m.do_command(GCode.parse_line("G92X-1Y-1Z-1"))
m.do_command(GCode.parse_line("X1Y1Z1"))
self.assertEqual(m.position(), Coordinates(3, 4, 5))
m.do_command(GCode.parse_line("G92X3Y4Z5"))
m.do_command(GCode.parse_line("X0Y0Z0"))
self.assertEqual(m.position(), Coordinates(0, 0, 0))
m.do_command(GCode.parse_line("G92X100Y100Z100E100"))
m.do_command(GCode.parse_line("X101Y102Z103E104"))
self.assertEqual(m.position(), Coordinates(1, 2, 3, 4))
m.do_command(GCode.parse_line("G92X-1Y-1Z-1E-1"))
m.do_command(GCode.parse_line("X1Y1Z1E1"))
self.assertEqual(m.position(), Coordinates(3, 4, 5, 6))
m.do_command(GCode.parse_line("G92X3Y4Z5E6"))
m.do_command(GCode.parse_line("X0Y0Z0E0"))
self.assertEqual(m.position(), Coordinates(0, 0, 0, 0))
m.do_command(GCode.parse_line("G90"))
m.do_command(GCode.parse_line("X6Y7Z8"))
self.assertEqual(m.position(), Coordinates(6, 7, 8))
m.do_command(GCode.parse_line("X6Y7Z8E9"))
self.assertEqual(m.position(), Coordinates(6, 7, 8, 9))
def test_g53_g91_g92(self):
m = GMachine()
m.do_command(GCode.parse_line("G92X-50Y-60Z-70"))
m.do_command(GCode.parse_line("X-45Y-55Z-65"))
self.assertEqual(m.position(), Coordinates(5, 5, 5))
m.do_command(GCode.parse_line("G92X-50Y-60Z-70E-80"))
m.do_command(GCode.parse_line("X-45Y-55Z-65E-75"))
self.assertEqual(m.position(), Coordinates(5, 5, 5, 5))
m.do_command(GCode.parse_line("G91"))
m.do_command(GCode.parse_line("X-1Y-2Z-3"))
self.assertEqual(m.position(), Coordinates(4, 3, 2))
m.do_command(GCode.parse_line("X-1Y-2Z-3E-4"))
self.assertEqual(m.position(), Coordinates(4, 3, 2, 1))
def test_m3_m5(self):
m = GMachine()
+28 -17
View File
@@ -18,90 +18,101 @@ class TestPulses(unittest.TestCase):
# PulseGenerator should never receive empty movement.
self.assertRaises(ZeroDivisionError,
PulseGeneratorLinear,
Coordinates(0, 0, 0), self.v)
Coordinates(0, 0, 0, 0), self.v)
def test_step(self):
# Check if PulseGenerator returns correctly single step movement.
g = PulseGeneratorLinear(Coordinates(1.0 / STEPPER_PULSES_PER_MM_X, 0, 0),
g = PulseGeneratorLinear(Coordinates(1.0 / STEPPER_PULSES_PER_MM_X,
0, 0, 0),
self.v)
i = 0
for px, py, pz in g:
for px, py, pz, pe in g:
i += 1
self.assertEqual(px, 0)
self.assertEqual(py, None)
self.assertEqual(pz, None)
self.assertEqual(pe, None)
self.assertEqual(i, 1)
g = PulseGeneratorLinear(Coordinates(
1.0 / STEPPER_PULSES_PER_MM_X,
1.0 / STEPPER_PULSES_PER_MM_Y,
1.0 / STEPPER_PULSES_PER_MM_Z),
1.0 / STEPPER_PULSES_PER_MM_Z,
1.0 / STEPPER_PULSES_PER_MM_E),
self.v)
i = 0
for px, py, pz in g:
for px, py, pz, pe in g:
i += 1
self.assertEqual(px, 0)
self.assertEqual(py, 0)
self.assertEqual(pz, 0)
self.assertEqual(pe, 0)
self.assertEqual(i, 1)
def test_linear_with_hal_virtual(self):
# Using hal_virtual module for this test, it already contains plenty
# of asserts for wrong number of pulses, pulse timing issues etc
hal_virtual.move_linear(Coordinates(1, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(25.4, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(25.4, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(25.4, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(1, 0, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(25.4, 0, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(25.4, 0, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(25.4, 0, 0, 0), self.v)
hal_virtual.move_linear(Coordinates(TABLE_SIZE_X_MM,
TABLE_SIZE_Y_MM,
TABLE_SIZE_Z_MM), self.v)
TABLE_SIZE_Z_MM,
100.0), self.v)
def test_twice_faster(self):
# Checks if one axis moves exactly twice faster, pulses are correct.
m = Coordinates(2, 4, 0)
m = Coordinates(2, 4, 0, 0)
g = PulseGeneratorLinear(m, self.v)
i = 0
for px, py, pz in g:
for px, py, pz, pe in g:
if i % 2 == 0:
self.assertNotEqual(px, None)
else:
self.assertEqual(px, None)
self.assertNotEqual(py, None)
self.assertEqual(pz, None)
self.assertEqual(pe, None)
i += 1
self.assertEqual(m.find_max() * STEPPER_PULSES_PER_MM_Y, i)
def test_pulses_count_and_timings(self):
# Check if number of pulses is equal to specified distance.
m = Coordinates(TABLE_SIZE_X_MM, TABLE_SIZE_Y_MM, TABLE_SIZE_Z_MM)
m = Coordinates(TABLE_SIZE_X_MM, TABLE_SIZE_Y_MM, TABLE_SIZE_Z_MM,
100.0)
g = PulseGeneratorLinear(m, self.v)
ix = 0
iy = 0
iz = 0
ie = 0
t = -1
for px, py, pz in g:
for px, py, pz, pe in g:
if px is not None:
ix += 1
if py is not None:
iy += 1
if pz is not None:
iz += 1
v = list(x for x in (px, py, pz) if x is not None)
if pe is not None:
ie += 1
v = list(x for x in (px, py, pz, pe) if x is not None)
self.assertEqual(min(v), max(v))
self.assertLess(t, min(v))
t = max(v)
self.assertEqual(m.x * STEPPER_PULSES_PER_MM_X, ix)
self.assertEqual(m.y * STEPPER_PULSES_PER_MM_Y, iy)
self.assertEqual(m.z * STEPPER_PULSES_PER_MM_Z, iz)
self.assertEqual(m.e * STEPPER_PULSES_PER_MM_E, ie)
self.assertLessEqual(t, g.total_time_s())
def test_acceleration_velocity(self):
# Check if acceleration present in pulses sequence and if velocity
# is correct
m = Coordinates(TABLE_SIZE_X_MM, 0, 0)
m = Coordinates(TABLE_SIZE_X_MM, 0, 0, 0)
g = PulseGeneratorLinear(m, self.v)
i = 0
lx = 0
for px, py, pz in g:
for px, py, pz, pe in g:
if i == 2:
at = px - lx
if i == TABLE_SIZE_X_MM * STEPPER_PULSES_PER_MM_X / 2: