Import LinuxCNC source tree
This commit is contained in:
1
tests/motion/.gitignore
vendored
Normal file
1
tests/motion/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
result.halsamples
|
||||
6
tests/motion/README
Normal file
6
tests/motion/README
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
The "motion" tests start a full emc in sim mode, with linuxcncrsh as the UI.
|
||||
|
||||
They run G-code programs and use halsampler to monitor the motions that
|
||||
motmod emits to HAL.
|
||||
|
||||
228
tests/motion/g0/checkresult
Executable file
228
tests/motion/g0/checkresult
Executable file
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
from subprocess import *
|
||||
|
||||
|
||||
#
|
||||
# read some stuff out of the INI file
|
||||
#
|
||||
|
||||
# servo period in nanoseconds
|
||||
servo_period_ns = float(Popen(
|
||||
[
|
||||
"inivar",
|
||||
"-var",
|
||||
"SERVO_PERIOD",
|
||||
"-sec",
|
||||
"EMCMOT",
|
||||
"-ini",
|
||||
"%s/motion-test.ini" % os.getenv("TEST_DIR")
|
||||
],
|
||||
stdout=PIPE
|
||||
).communicate()[0])
|
||||
|
||||
cycles_per_second = 1000000000.0 / servo_period_ns
|
||||
|
||||
# max acceleration in inches per second^2
|
||||
max_acceleration_ips2 = float(Popen(
|
||||
[
|
||||
"inivar",
|
||||
"-var",
|
||||
"MAX_ACCELERATION",
|
||||
"-sec",
|
||||
"AXIS_X",
|
||||
"-ini",
|
||||
"%s/motion-test.ini" % os.getenv("TEST_DIR")
|
||||
],
|
||||
stdout=PIPE
|
||||
).communicate()[0])
|
||||
|
||||
# max acceleration in inches per second per servo-thread cycle
|
||||
# A move that never blends (like this lone G0) runs at the full machine
|
||||
# acceleration; the 1/2 parabolic-blend reservation only applies during an
|
||||
# actual blend overlap.
|
||||
max_acceleration_ipspc = (max_acceleration_ips2 / cycles_per_second)
|
||||
|
||||
# max velocity in inches per second
|
||||
max_velocity_ips = float(Popen(
|
||||
[
|
||||
"inivar",
|
||||
"-var",
|
||||
"MAX_VELOCITY",
|
||||
"-sec",
|
||||
"AXIS_X",
|
||||
"-ini",
|
||||
"%s/motion-test.ini" % os.getenv("TEST_DIR")
|
||||
],
|
||||
stdout=PIPE
|
||||
).communicate()[0])
|
||||
|
||||
max_velocity_ipc = max_velocity_ips / cycles_per_second
|
||||
|
||||
|
||||
# read the samples file and turn it into an array, where index i is an
|
||||
# array of the floats found on line i of the samples file
|
||||
samples_filename = sys.argv[1] + ".halsamples"
|
||||
with open(samples_filename) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
samples = []
|
||||
for line in lines:
|
||||
s = [ float(x) for x in line.split() ]
|
||||
samples.append(s)
|
||||
|
||||
|
||||
#
|
||||
# here comes the test proper
|
||||
#
|
||||
|
||||
|
||||
# verify that X starts at 0.000000
|
||||
if samples[0][1] != 0.000000:
|
||||
print("error: sample 0: X starts at %.6f, not at 0.000000" % samples[0][1])
|
||||
sys.exit(1)
|
||||
|
||||
#print("line 0: X starts at 0.000000")
|
||||
|
||||
|
||||
# find where X starts moving (X is the second column, column 1)
|
||||
for i in range(0, len(samples) - 1):
|
||||
if samples[i][1] != samples[i+1][1]:
|
||||
break
|
||||
|
||||
if len(samples) == i+2:
|
||||
print("error: no acceleration detected, did linuxcnc take too long to start moving?")
|
||||
sys.exit(1)
|
||||
|
||||
#print("line %d: accel phase starts" % i)
|
||||
|
||||
|
||||
# now i is the sample before it starts moving
|
||||
# verify accel does not exceed maxaccel
|
||||
# verify vel does not exceed maxvel
|
||||
# verify accel phase stops when vel == maxvel
|
||||
highest_seen_accel_ipc2 = 0
|
||||
old_accel_ipc2 = 0
|
||||
old_v_ipc = 0
|
||||
for i in range(i, len(samples) - 1):
|
||||
|
||||
new_v_ipc = samples[i+1][1] - samples[i][1]
|
||||
accel_ipc2 = new_v_ipc - old_v_ipc
|
||||
|
||||
highest_seen_accel_ipc2 = max(accel_ipc2, highest_seen_accel_ipc2)
|
||||
|
||||
#print("line %d: X=%.6f, v=%.6f i/c (%.6f i/s), a=%.6f i/c^2 (%.6f i/s^2)" % (i, samples[i][1], new_v_ipc, (new_v_ipc * cycles_per_second), accel_ipc2, (accel_ipc2 * cycles_per_second * cycles_per_second)))
|
||||
|
||||
# i hate floating point
|
||||
if ((accel_ipc2 * cycles_per_second) - max_acceleration_ipspc) > 0.0000001:
|
||||
print("error: line %d: detected accel constraint violation!" % i)
|
||||
print("error: detected accel %.6f i/c^2 (%.6f i/s^2)" % (accel_ipc2, (accel_ipc2 * cycles_per_second * cycles_per_second)))
|
||||
print("error: max accel %.6f i/s^2)" % max_acceleration_ips2)
|
||||
sys.exit(1)
|
||||
|
||||
if (new_v_ipc - max_velocity_ipc) > 0.0000001:
|
||||
print("error: line %d: detected vel constraint violation!" % i)
|
||||
print("error: detected vel %.6f i/c (%.6f i/s)" % (new_v_ipc, new_v_ipc * cycles_per_second))
|
||||
print("error: max vel %.6f i/c (%.6f i/s)" % (max_velocity_ipc, max_velocity_ips))
|
||||
sys.exit(1)
|
||||
|
||||
if accel_ipc2 == 0:
|
||||
#print("line %d: accel phase over" % i)
|
||||
break
|
||||
|
||||
old_v_ipc = new_v_ipc
|
||||
|
||||
# verify highest seen accel is very close to max accel
|
||||
if abs(max_acceleration_ipspc - (highest_seen_accel_ipc2 * cycles_per_second)) > 0.0000001:
|
||||
print("error: accel only reached %.6f i/c^2 (%.6f i/s^2)" % (highest_seen_accel_ipc2, (highest_seen_accel_ipc2 * cycles_per_second * cycles_per_second)))
|
||||
print("error: max accel is %.6f i/s^2" % max_acceleration_ips2)
|
||||
sys.exit(1)
|
||||
|
||||
#print(" accel reached but did not exceed 1/2 of max accel of %.6f i/s^2" % max_acceleration_ips2)
|
||||
|
||||
#print("line %d: entering cruise phase, vel=%.6f i/s, max_accel = %.6f i/s^2" % (i, new_v_ipc * cycles_per_second, highest_seen_accel_ipc2 * cycles_per_second * cycles_per_second))
|
||||
|
||||
|
||||
# now i is the first sample of the cruise phase
|
||||
# wait for vel to drop again
|
||||
for i in range(i, len(samples) - 1):
|
||||
new_v_ipc = samples[i+1][1] - samples[i][1]
|
||||
|
||||
if abs(new_v_ipc - old_v_ipc) > 0.0000001:
|
||||
# decel phase starting
|
||||
break
|
||||
|
||||
old_v_ipc = new_v_ipc
|
||||
|
||||
#print("line %d: decel phase starting, old_v=%.6f i/s, new_v=%.6f i/s" % (i, old_v_ipc * cycles_per_second, new_v_ipc * cycles_per_second))
|
||||
|
||||
# now i is the sample before it starts decel
|
||||
# verify accel does not exceed maxaccel
|
||||
# verify vel does not exceed maxvel
|
||||
# verify decel phase stops when vel == 0
|
||||
highest_seen_accel_ipc2 = 0
|
||||
old_accel_ipc2 = 0
|
||||
for i in range(i, len(samples) - 1):
|
||||
|
||||
new_v_ipc = samples[i+1][1] - samples[i][1]
|
||||
accel_ipc2 = new_v_ipc - old_v_ipc
|
||||
|
||||
highest_seen_accel_ipc2 = min(accel_ipc2, highest_seen_accel_ipc2)
|
||||
|
||||
#print("line %d: X=%.6f, v=%.6f i/c (%.6f i/s), a=%.6f i/c^2 (%.6f i/s^2)" % (i, samples[i][1], new_v_ipc, (new_v_ipc * cycles_per_second), accel_ipc2, (accel_ipc2 * cycles_per_second * cycles_per_second)))
|
||||
|
||||
# i hate floating point
|
||||
if ((accel_ipc2 * cycles_per_second) - max_acceleration_ipspc) > 0.0000001:
|
||||
print("error: line %d: detected accel constraint violation!" % i)
|
||||
print("error: detected accel %.6f i/c^2 (%.6f i/s^2)" % (accel_ipc2, accel_ipc2 * cycles_per_second * cycles_per_second))
|
||||
print("error: max accel %.6f i/s^2)" % max_acceleration_ips2)
|
||||
sys.exit(1)
|
||||
|
||||
if (new_v_ipc - max_velocity_ipc) > 0.0000001:
|
||||
print("error: line %d: detected vel constraint violation!" % i)
|
||||
print("error: detected vel %.6f i/c (%.6f i/s)" % (new_v_ipc, new_v_ipc * cycles_per_second))
|
||||
print("error: max vel %.6f i/c (%.6f i/s)" % (max_velocity_ipc, max_velocity_ips))
|
||||
sys.exit(1)
|
||||
|
||||
if new_v_ipc < -0.0000001:
|
||||
print("error: line %d: detected vel undershoot!" % i)
|
||||
print("error: detected vel %.6f i/c (%.6f i/s)" % (new_v_ipc, new_v_ipc * cycles_per_second))
|
||||
sys.exit(1)
|
||||
|
||||
if new_v_ipc == 0:
|
||||
# print("line %d: decel phase over" % i)
|
||||
break
|
||||
|
||||
old_v_ipc = new_v_ipc
|
||||
|
||||
# verify highest seen accel is very close to max accel
|
||||
if abs(max_acceleration_ipspc + (highest_seen_accel_ipc2 * cycles_per_second)) > 0.0000001:
|
||||
print("error: accel only reached %.6f i/c^2 (%.6f i/s^2)" % (highest_seen_accel_ipc2, (highest_seen_accel_ipc2 * cycles_per_second * cycles_per_second)))
|
||||
print("error: max accel is %.6f i/s^2" % max_acceleration_ips2)
|
||||
sys.exit(1)
|
||||
|
||||
#print(" decel reached but did not exceed 1/2 of max accel of %.6f i/s^2" % max_acceleration_ips2)
|
||||
|
||||
|
||||
# verify X stopped at 1.000000
|
||||
if samples[i][1] != 1.000000:
|
||||
print("error: line %d: X stopped at %.6f, not at 1.000000!" % (i, samples[i][1]))
|
||||
sys.exit(1)
|
||||
|
||||
#print(" X reached target of 1.0000")
|
||||
|
||||
|
||||
# verify X doesn't move from now on
|
||||
for i in range(i, len(samples) - 1):
|
||||
if samples[i][1] != samples[i+1][1]:
|
||||
print("error: line %d: X moved!" % i)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
#print("line %d: X did not move after reaching target" % i)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
10
tests/motion/g0/motion-test.hal
Normal file
10
tests/motion/g0/motion-test.hal
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
# sample where motion tells us to go
|
||||
|
||||
loadrt sampler depth=5000 cfg=ff
|
||||
|
||||
addf sampler.0 servo-thread
|
||||
|
||||
net Xpos => sampler.0.pin.0
|
||||
net Ypos => sampler.0.pin.1
|
||||
|
||||
137
tests/motion/g0/motion-test.ini
Normal file
137
tests/motion/g0/motion-test.ini
Normal file
@@ -0,0 +1,137 @@
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = MOTION-TEST
|
||||
|
||||
# Debug level, 0 means no messages. See src/emc/nml_int/emcglb.h for others
|
||||
DEBUG = 0x7FFFFFFF
|
||||
#DEBUG = 0x10
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = linuxcncrsh
|
||||
|
||||
#PROGRAM_PREFIX = /home/seb/emc2/nc_files
|
||||
|
||||
#MAX_FEED_OVERRIDE = 2.0
|
||||
|
||||
[RS274NGC]
|
||||
# File containing interpreter variables
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
|
||||
# Timeout for comm to emcmot, in seconds
|
||||
COMM_TIMEOUT = 4.0
|
||||
|
||||
# BASE_PERIOD is unused in this configuration but specified in LIB:core_sim.hal
|
||||
BASE_PERIOD = 0
|
||||
# Servo task period, in nano-seconds
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[HAL]
|
||||
HALFILE = LIB:core_sim.hal
|
||||
HALFILE = motion-test.hal
|
||||
|
||||
[TRAJ]
|
||||
AXES = 3
|
||||
COORDINATES = X Y Z
|
||||
HOME = 0 0 0
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 1.2
|
||||
MAX_LINEAR_VELOCITY = 4
|
||||
NO_FORCE_HOMING = 1
|
||||
|
||||
# Axes sections ---------------------------------------------------------------
|
||||
|
||||
# First axis
|
||||
[EMCIO]
|
||||
# tool table file
|
||||
TOOL_TABLE = simpockets.tbl
|
||||
TOOL_CHANGE_POSITION = 0 0 2
|
||||
RANDOM_TOOLCHANGER = 1
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_LATCH_VEL = 0.0
|
||||
HOME_USE_INDEX = NO
|
||||
HOME_IGNORE_LIMITS = NO
|
||||
HOME_SEQUENCE = 1
|
||||
|
||||
# Second axis
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_LATCH_VEL = 0.0
|
||||
HOME_USE_INDEX = NO
|
||||
HOME_IGNORE_LIMITS = NO
|
||||
HOME_SEQUENCE = 1
|
||||
|
||||
# Third axis
|
||||
[AXIS_Z]
|
||||
MIN_LIMIT = -4.0
|
||||
MAX_LIMIT = 4.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -4.0
|
||||
MAX_LIMIT = 4.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_OFFSET = 1.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_LATCH_VEL = 0.0
|
||||
HOME_USE_INDEX = NO
|
||||
HOME_IGNORE_LIMITS = NO
|
||||
HOME_SEQUENCE = 0
|
||||
74
tests/motion/g0/test.sh
Executable file
74
tests/motion/g0/test.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
|
||||
#export PATH=$PATH:$EMC2_HOME/tests/helpers
|
||||
#source $EMC2_HOME/tests/helpers/test-functions.sh
|
||||
|
||||
wait_for_pin() {
|
||||
pin="$1"
|
||||
value="$2"
|
||||
maxwait=10 # seconds
|
||||
while [ 0 -lt $maxwait ] \
|
||||
&& [ "$value" != "$(halcmd -s show pin "$pin" | awk '{print $4}')" ]; do
|
||||
sleep 1
|
||||
maxwait=$((maxwait - 1))
|
||||
done
|
||||
if [ 0 -eq $maxwait ] ; then
|
||||
echo "E: waiting for pin $pin timed out"
|
||||
kill "$linuxcncpid"
|
||||
kill "$samplerpid"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
linuxcnc motion-test.ini &
|
||||
linuxcncpid=$!
|
||||
|
||||
# let linuxcnc come up
|
||||
TOGO=80
|
||||
while [ $TOGO -gt 0 ]; do
|
||||
echo "I: trying to connect to linuxcncrsh TOGO=$TOGO"
|
||||
if nc -z localhost 5007; then
|
||||
break
|
||||
fi
|
||||
sleep 0.25
|
||||
TOGO=$((TOGO - 1))
|
||||
done
|
||||
if [ $TOGO -eq 0 ]; then
|
||||
echo "I: connection to linuxcncrsh timed out"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wait_for_pin motion.in-position TRUE
|
||||
|
||||
echo "I: starting to capture data"
|
||||
halsampler -t >| result.halsamples &
|
||||
samplerpid=$!
|
||||
|
||||
(
|
||||
echo "hello EMC mt 1.0"
|
||||
echo "set enable EMCTOO"
|
||||
|
||||
echo "set estop off"
|
||||
echo "set machine on"
|
||||
|
||||
echo "set mode mdi"
|
||||
dist=1
|
||||
echo "set mdi g0x$dist"
|
||||
|
||||
# Wait for movement to complete
|
||||
wait_for_pin joint.0.pos-fb $dist
|
||||
wait_for_pin joint.0.in-position TRUE
|
||||
wait_for_pin joint.1.in-position TRUE
|
||||
|
||||
echo "shutdown"
|
||||
) | nc localhost 5007
|
||||
|
||||
kill "$samplerpid"
|
||||
wait "$samplerpid"
|
||||
echo "I: finished capturing data"
|
||||
|
||||
# wait for linuxcnc to finish
|
||||
wait "$linuxcncpid"
|
||||
|
||||
exit 0
|
||||
|
||||
15
tests/motion/heading/heading-test.ngc
Normal file
15
tests/motion/heading/heading-test.ngc
Normal file
@@ -0,0 +1,15 @@
|
||||
G21 ; Set units to millimeters
|
||||
G64 P0.1 Q0.1 ; Tolerance 0.1mm
|
||||
G90 ; Set to absolute positioning
|
||||
G0 X75 Y50 ; Rapid move to start point (center X50 + radius 25)
|
||||
G2 X75 Y50 I-25 J0 F5000 ; Clockwise circle to start, center is -25mm in X
|
||||
G1 X50 Y75 F5000 ; Feed move reversed X and Y
|
||||
G3 X50 Y75 I25 J0 F5000 ; Counter clockwise circle back, center is +25mm in X
|
||||
G20 ; Set units to inches
|
||||
G90 ; Set to absolute positioning
|
||||
G0 X3 Y2 ; Rapid move to start point (center X2 + radius 1)
|
||||
G2 X3 Y2 I-1 J0 F5000 ; Clockwise circle to start, center is -1" in X
|
||||
G1 X2 Y3 F5000 ; Feed move reversed X and Y
|
||||
G3 X2 Y3 I1 J0 F5000 ; Counter clockwise circle back, center is +1" in X
|
||||
M30
|
||||
M2
|
||||
22
tests/motion/heading/heading.hal
Normal file
22
tests/motion/heading/heading.hal
Normal file
@@ -0,0 +1,22 @@
|
||||
# HAL file for sampling motion heading data
|
||||
|
||||
loadrt sampler depth=400 cfg=ssfffffffbff
|
||||
|
||||
# disable sampler before adding the function
|
||||
setp sampler.0.enable 0
|
||||
|
||||
addf sampler.0 servo-thread
|
||||
|
||||
# Net pins to record to the sampler inputs
|
||||
net line-number motion.interp.line-number => sampler.0.pin.0
|
||||
net motion-type motion.interp.motion-type => sampler.0.pin.1
|
||||
net feedrate motion.interp.feedrate => sampler.0.pin.2
|
||||
net arc-radius motion.interp.arc-radius => sampler.0.pin.3
|
||||
net arc-center-x motion.interp.arc-center-x => sampler.0.pin.4
|
||||
net arc-center-y motion.interp.arc-center-y => sampler.0.pin.5
|
||||
net arc-center-z motion.interp.arc-center-z => sampler.0.pin.6
|
||||
net heading motion.interp.heading => sampler.0.pin.7
|
||||
net normal-heading motion.interp.normal-heading => sampler.0.pin.8
|
||||
net iscircle motion.interp.iscircle => sampler.0.pin.9
|
||||
net x-pos-fb joint.0.pos-fb => sampler.0.pin.10
|
||||
net y-pos-fb joint.1.pos-fb => sampler.0.pin.11
|
||||
1
tests/motion/heading/imperial/expected
Normal file
1
tests/motion/heading/imperial/expected
Normal file
@@ -0,0 +1 @@
|
||||
Completed successfully
|
||||
96
tests/motion/heading/imperial/heading-in.ini
Normal file
96
tests/motion/heading/imperial/heading-in.ini
Normal file
@@ -0,0 +1,96 @@
|
||||
[EMC]
|
||||
DEBUG = 0
|
||||
VERSION = 1.1
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = ../test-ui.py -ngc ../heading-test.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
BASE_PERIOD = 0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[HAL]
|
||||
HALFILE = LIB:core_sim.hal
|
||||
HALFILE = ../heading.hal
|
||||
|
||||
[TRAJ]
|
||||
NO_FORCE_HOMING=1
|
||||
AXES = 3
|
||||
COORDINATES = X Y Z
|
||||
HOME = 0 0 0
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 4
|
||||
MAX_LINEAR_VELOCITY = 20
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 20
|
||||
MAX_ACCELERATION = 120
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 40
|
||||
MAX_ACCELERATION = 120
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 20
|
||||
MAX_ACCELERATION = 120
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 40
|
||||
MAX_ACCELERATION = 120
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Z]
|
||||
MIN_LIMIT = -20.0
|
||||
MAX_LIMIT = 20.0
|
||||
MAX_VELOCITY = 20
|
||||
MAX_ACCELERATION = 120
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 20
|
||||
MAX_ACCELERATION = 120
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -20.0
|
||||
MAX_LIMIT = 20.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
6
tests/motion/heading/imperial/test.sh
Executable file
6
tests/motion/heading/imperial/test.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Run the Imperial config
|
||||
if linuxcnc -r heading-in.ini; then
|
||||
echo "Completed successfully" > result
|
||||
fi
|
||||
|
||||
1
tests/motion/heading/metric/expected
Normal file
1
tests/motion/heading/metric/expected
Normal file
@@ -0,0 +1 @@
|
||||
Completed successfully
|
||||
96
tests/motion/heading/metric/heading-mm.ini
Normal file
96
tests/motion/heading/metric/heading-mm.ini
Normal file
@@ -0,0 +1,96 @@
|
||||
[EMC]
|
||||
DEBUG = 0
|
||||
VERSION = 1.1
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = ../test-ui.py -ngc ../heading-test.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
BASE_PERIOD = 0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[HAL]
|
||||
HALFILE = LIB:core_sim.hal
|
||||
HALFILE = ../heading.hal
|
||||
|
||||
[TRAJ]
|
||||
NO_FORCE_HOMING=1
|
||||
AXES = 3
|
||||
COORDINATES = X Y Z
|
||||
HOME = 0 0 0
|
||||
LINEAR_UNITS = mm
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 100
|
||||
MAX_LINEAR_VELOCITY = 500
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -1000.0
|
||||
MAX_LIMIT = 1000.0
|
||||
MAX_VELOCITY = 500
|
||||
MAX_ACCELERATION = 3000
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 1000
|
||||
MAX_ACCELERATION = 3000
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -1000.0
|
||||
MAX_LIMIT = 1000.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -1000.0
|
||||
MAX_LIMIT = 1000.0
|
||||
MAX_VELOCITY = 500
|
||||
MAX_ACCELERATION = 3000
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 500
|
||||
MAX_ACCELERATION = 3000
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -1000.0
|
||||
MAX_LIMIT = 1000.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Z]
|
||||
MIN_LIMIT = -500.0
|
||||
MAX_LIMIT = 500.0
|
||||
MAX_VELOCITY = 500
|
||||
MAX_ACCELERATION = 3000
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 500
|
||||
MAX_ACCELERATION = 3000
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -500.0
|
||||
MAX_LIMIT = 500.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
7
tests/motion/heading/metric/test.sh
Executable file
7
tests/motion/heading/metric/test.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Run the Metric config
|
||||
if linuxcnc -r heading-mm.ini; then
|
||||
echo "Completed successfully" > result
|
||||
fi
|
||||
|
||||
|
||||
253
tests/motion/heading/test-ui.py
Executable file
253
tests/motion/heading/test-ui.py
Executable file
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import linuxcnc
|
||||
import linuxcnc_util
|
||||
import hal
|
||||
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import math
|
||||
|
||||
|
||||
INTERPTIMEOUT = 5 # Max 5 seconds to wait for the interpreter
|
||||
GCODETIMEOUT = 120 # Max 60 seconds for the gcode file to run12
|
||||
|
||||
#
|
||||
# Sample collection routine
|
||||
#
|
||||
nsamples = 0
|
||||
lastsample = None
|
||||
lastrecnum = 0
|
||||
ctr = 0
|
||||
|
||||
def is_arc_valid(sample, tolerance):
|
||||
"""
|
||||
Validates circularity, radius accuracy, and G2/G3 heading consistency.
|
||||
Returns True on pass, False on failure.
|
||||
"""
|
||||
global ctr
|
||||
# Extract Fields
|
||||
motion_type = sample[1] # 20=G2, 30=G3
|
||||
expected_radius = sample[3]
|
||||
cx, cy = sample[4], sample[5]
|
||||
heading = sample[7]
|
||||
normal_heading = sample[8]
|
||||
x = sample[10]
|
||||
y = sample[11]
|
||||
# 2. Radius Check: Distance from tool (x,y) to center (cx,cy)
|
||||
actual_radius = math.hypot(x - cx, y - cy)
|
||||
if abs(actual_radius - expected_radius) > tolerance:
|
||||
ctr = ctr + 1
|
||||
if ctr > 40:
|
||||
print(f"FAIL Radius after 40 times: Expected {expected_radius}, Got {actual_radius:.4f}")
|
||||
ctr = 0
|
||||
return False
|
||||
ctr = 0
|
||||
# 3. Normal Direction Check: Normal must point from tool to center
|
||||
angle_to_center = math.degrees(math.atan2(cy - y, cx - x)) % 360
|
||||
norm_diff = abs(angle_to_center - (normal_heading % 360))
|
||||
if norm_diff > 180: norm_diff = 360 - norm_diff
|
||||
|
||||
if norm_diff > tolerance:
|
||||
print(f"FAIL Normal: Expected {angle_to_center:.4f}, Got {normal_heading:.4f}")
|
||||
return False
|
||||
# 4. Tangency/Direction Check: (Heading vs Normal)
|
||||
#G2 (20): Heading should be Normal + 90
|
||||
#G3 (30): Heading should be Normal - 90 (or + 270)
|
||||
diff = (heading - normal_heading) % 360
|
||||
|
||||
if motion_type == 20: # G2
|
||||
if abs(diff - 90) > tolerance:
|
||||
print(f"FAIL: G2 normal heading outside of tolerance",diff)
|
||||
return False
|
||||
elif motion_type == 30: # G3
|
||||
if abs(diff - 270) > tolerance:
|
||||
print(f"FAIL: G3 normal heading outside of tolerance",diff)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_heading_valid(sample1, sample2, tolerance=0.1):
|
||||
"""
|
||||
Returns True if the travel direction between two samples
|
||||
matches the reported heading within a given tolerance.
|
||||
"""
|
||||
global ctr
|
||||
if sample1[0] != sample2[0]:
|
||||
ctr = ctr + 1
|
||||
if ctr < 5: #allow to settle on change of segment
|
||||
return True
|
||||
else:
|
||||
ctr = 0 #Reset
|
||||
# Extract coords and reported heading
|
||||
x1, y1 = sample1[10], sample1[11]
|
||||
x2, y2 = sample2[10], sample2[11]
|
||||
reported_heading = sample2[7]
|
||||
|
||||
dx = x2 - x1
|
||||
dy = y2 - y1
|
||||
|
||||
# Avoid calculation if there is no movement (prevents jitter errors)
|
||||
if math.hypot(dx, dy) < 1e-9:
|
||||
return True
|
||||
|
||||
# Calculate angle: atan2(y, x) -> degrees
|
||||
calculated_heading = math.degrees(math.atan2(dy, dx))
|
||||
|
||||
# Normalize to 0-360 if your system doesn't use negative degrees
|
||||
if calculated_heading < 0:
|
||||
calculated_heading += 360
|
||||
|
||||
# Standardize reported_heading to same 0-360 range if needed
|
||||
if reported_heading < 0:
|
||||
reported_heading += 360
|
||||
|
||||
# Handle the "wrap-around" case (e.g., 359.9 is near 0.1)
|
||||
diff = abs(calculated_heading - reported_heading)
|
||||
if diff > 180:
|
||||
diff = 360 - diff
|
||||
return(diff <= tolerance)
|
||||
|
||||
def parse_sample(sample):
|
||||
"""
|
||||
Parses a tuple of sampler data for tests
|
||||
Fields: line-number, motion-type, feedrate, arc-radius, arc-center-x, arc-center-y, arc-center-z,
|
||||
heading, normal-heading, iscircle, x-pos-fb, y-pos-fb
|
||||
"""
|
||||
global lastsample
|
||||
global lastrecnum
|
||||
|
||||
print("{:6d} {}".format(nsamples, sample))
|
||||
|
||||
if None == lastsample or sample[0] != lastsample[0]:
|
||||
return True
|
||||
if sample[1] not in (00,10,20,30):
|
||||
print("Gcode line is not G0,G1,G2 or G3, nothing to do")
|
||||
return True
|
||||
if (sample[1] == 20 or sample[1] == 30):
|
||||
# gcode is G2 but G3
|
||||
if not is_arc_valid(sample, 0.001):
|
||||
print("G2/G3 tests are invalid at record ", lastrecnum)
|
||||
return False
|
||||
if (lastsample[1] == sample[1] == 10): # we have at least 2 G1 records
|
||||
if not is_heading_valid(lastsample, sample, tolerance=0.001):
|
||||
print("G0 heading or other values does not agree with expected value at record ",lastrecnum)
|
||||
return False
|
||||
if (lastsample[1] == sample[1] == 0): # we have at least 2 G0 records
|
||||
if not is_heading_valid(lastsample, sample, tolerance=0.001):
|
||||
print("G0 heading or other values does not agree with expected value at record ",lastrecnum)
|
||||
return False
|
||||
lastrecnum = lastrecnum + 1
|
||||
lastsample = sample
|
||||
|
||||
#
|
||||
# Sample collection routine
|
||||
#
|
||||
def read_sample():
|
||||
global nsamples, lastsample
|
||||
sample = sampler.read()
|
||||
if None == sample:
|
||||
print("Error: read_sample() read None (readable={}, depth={})".format(samples.readable, sampler.depth))
|
||||
sys.exit(1)
|
||||
if sample != lastsample and 0 != sample[0]: # Don't handle duplicates and non-gcode
|
||||
if not parse_sample(sample):
|
||||
print("Test Failed")
|
||||
nsamples += 1
|
||||
lastsample = sample
|
||||
|
||||
#
|
||||
# Test the command line arguments. It should have a .ngc file we can read
|
||||
#
|
||||
ngcfile = None
|
||||
# Crudely parse command like args to find: -ngc file
|
||||
for i in range(1, len(sys.argv)-1):
|
||||
if "-ngc" == sys.argv[i]:
|
||||
ngcfile = sys.argv[i+1]
|
||||
break
|
||||
|
||||
if not ngcfile:
|
||||
print("Missing NGC-file; run with: test-ui.py -ngc ngcfile.ngc")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(ngcfile):
|
||||
print("NGC-file '{}' does not exist".format(ngcfile))
|
||||
sys.exit(1)
|
||||
|
||||
#
|
||||
# Setup a hal component and connect to the sampler stream
|
||||
#
|
||||
h = hal.component("python-ui")
|
||||
sampler = hal.stream(h, hal.sampler_base, "ssfffffffbff")
|
||||
h.ready()
|
||||
|
||||
#
|
||||
# connect to LinuxCNC
|
||||
#
|
||||
|
||||
c = linuxcnc.command()
|
||||
s = linuxcnc.stat()
|
||||
e = linuxcnc.error_channel()
|
||||
|
||||
l = linuxcnc_util.LinuxCNC(command=c, status=s, error=e)
|
||||
# Enable machine
|
||||
c.state(linuxcnc.STATE_ESTOP_RESET)
|
||||
c.state(linuxcnc.STATE_ON)
|
||||
|
||||
# Home all joints
|
||||
c.home(-1)
|
||||
c.wait_complete()
|
||||
l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
#
|
||||
# Setup to run GCode program
|
||||
#
|
||||
c.mode(linuxcnc.MODE_AUTO)
|
||||
c.program_open(ngcfile)
|
||||
|
||||
# Enable sampling
|
||||
hal.set_p("sampler.0.enable", "1")
|
||||
|
||||
# Start the GCode
|
||||
c.auto(linuxcnc.AUTO_RUN, 1)
|
||||
|
||||
# Wait for the interpreter to start running the program
|
||||
start = time.time()
|
||||
while time.time() - start < INTERPTIMEOUT:
|
||||
s.poll()
|
||||
if s.interp_state != linuxcnc.INTERP_IDLE:
|
||||
print("Interpreter active...")
|
||||
break
|
||||
time.sleep(0.01)
|
||||
if s.interp_state == linuxcnc.INTERP_IDLE:
|
||||
print("Timed out starting interpreter")
|
||||
sys.exit(1)
|
||||
|
||||
#
|
||||
# Loop until the program finishes while collecting data
|
||||
#
|
||||
start = time.time()
|
||||
s.poll()
|
||||
while s.interp_state != linuxcnc.INTERP_IDLE and time.time() - start < GCODETIMEOUT:
|
||||
# Continuously drain the sampler buffer
|
||||
while sampler.readable:
|
||||
read_sample()
|
||||
time.sleep(0.01)
|
||||
s.poll()
|
||||
|
||||
if s.interp_state != linuxcnc.INTERP_IDLE:
|
||||
print("Timed out running the GCode program")
|
||||
sys.exit(1)
|
||||
|
||||
# Disable the sampler
|
||||
hal.set_p("sampler.0.enable", "0")
|
||||
|
||||
# Give RT time to finalize the last sample(s)
|
||||
time.sleep(0.01)
|
||||
|
||||
# Drain any remaining samples
|
||||
while sampler.readable:
|
||||
read_sample()
|
||||
# if we get here it all worked!
|
||||
sys.exit(0)
|
||||
|
||||
2
tests/motion/jogwheel-axis/.gitignore
vendored
Normal file
2
tests/motion/jogwheel-axis/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
sim.var
|
||||
sim.var.bak
|
||||
2
tests/motion/jogwheel-axis/checkresult
Executable file
2
tests/motion/jogwheel-axis/checkresult
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exit 0 # test failure is indicated by test.sh exit value
|
||||
99
tests/motion/jogwheel-axis/motion-test.ini
Normal file
99
tests/motion/jogwheel-axis/motion-test.ini
Normal file
@@ -0,0 +1,99 @@
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
DEBUG = 0x0
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = ./test-ui.py
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = sim.var
|
||||
USER_M_PATH = ./subs
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
COMM_TIMEOUT = 4.0
|
||||
BASE_PERIOD = 0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
MDI_QUEUED_COMMANDS=10000
|
||||
|
||||
[HAL]
|
||||
HALFILE = LIB:core_sim.hal
|
||||
POSTGUI_HALFILE = postgui.hal
|
||||
|
||||
[TRAJ]
|
||||
NO_FORCE_HOMING=1
|
||||
AXES = 3
|
||||
COORDINATES = X Y Z
|
||||
HOME = 0 0 0
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 1.2
|
||||
MAX_LINEAR_VELOCITY = 4
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Z]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
HOME_SEQUENCE = 0
|
||||
48
tests/motion/jogwheel-axis/postgui.hal
Normal file
48
tests/motion/jogwheel-axis/postgui.hal
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
#
|
||||
# axis x
|
||||
#
|
||||
|
||||
net axis-x-jog-enable <= test-ui.axis-x-jog-enable
|
||||
net axis-x-jog-enable => axis.x.jog-enable
|
||||
|
||||
net axis-x-jog-counts <= test-ui.axis-x-jog-counts
|
||||
net axis-x-jog-counts => axis.x.jog-counts
|
||||
|
||||
net axis-x-jog-scale <= test-ui.axis-x-jog-scale
|
||||
net axis-x-jog-scale => axis.x.jog-scale
|
||||
|
||||
net Xpos => test-ui.axis-x-position
|
||||
|
||||
|
||||
#
|
||||
# axis y
|
||||
#
|
||||
|
||||
net axis-y-jog-enable <= test-ui.axis-y-jog-enable
|
||||
net axis-y-jog-enable => axis.y.jog-enable
|
||||
|
||||
net axis-y-jog-counts <= test-ui.axis-y-jog-counts
|
||||
net axis-y-jog-counts => axis.y.jog-counts
|
||||
|
||||
net axis-y-jog-scale <= test-ui.axis-y-jog-scale
|
||||
net axis-y-jog-scale => axis.y.jog-scale
|
||||
|
||||
net Ypos => test-ui.axis-y-position
|
||||
|
||||
|
||||
#
|
||||
# axis z
|
||||
#
|
||||
|
||||
net axis-z-jog-enable <= test-ui.axis-z-jog-enable
|
||||
net axis-z-jog-enable => axis.z.jog-enable
|
||||
|
||||
net axis-z-jog-counts <= test-ui.axis-z-jog-counts
|
||||
net axis-z-jog-counts => axis.z.jog-counts
|
||||
|
||||
net axis-z-jog-scale <= test-ui.axis-z-jog-scale
|
||||
net axis-z-jog-scale => axis.z.jog-scale
|
||||
|
||||
net Zpos => test-ui.axis-z-position
|
||||
|
||||
119
tests/motion/jogwheel-axis/test-ui.py
Executable file
119
tests/motion/jogwheel-axis/test-ui.py
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import linuxcnc
|
||||
import linuxcnc_util
|
||||
import hal
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import math
|
||||
|
||||
|
||||
def close_enough(a, b, epsilon=0.000001):
|
||||
return math.fabs(a - b) < epsilon
|
||||
|
||||
|
||||
def jog_axis(axis_letter, counts=1, scale=0.001):
|
||||
timeout = 5.0
|
||||
|
||||
start_pos = { }
|
||||
for a in 'xyz':
|
||||
start_pos[a] = h['axis-%c-position' % a]
|
||||
|
||||
target = h['axis-%c-position' % axis_letter] + (counts * scale)
|
||||
|
||||
h['axis-%c-jog-scale' % axis_letter] = scale
|
||||
h['axis-%c-jog-enable' % axis_letter] = 1
|
||||
h['axis-%c-jog-counts' % axis_letter] += counts
|
||||
|
||||
start_time = time.time()
|
||||
while not close_enough(h['axis-%c-position' % axis_letter], target) and (time.time() - start_time < timeout):
|
||||
#print "axis %c is at %.9f" % (axis_letter, h['axis-%c-position' % axis_letter])
|
||||
time.sleep(0.010)
|
||||
|
||||
h['axis-%c-jog-enable' % axis_letter] = 0
|
||||
|
||||
print("axis %c jogged from %.6f to %.6f (%d counts at scale %.6f)" % (axis_letter, start_pos[axis_letter], h['axis-%c-position' % axis_letter], counts, scale))
|
||||
|
||||
success = True
|
||||
for a in 'xyz':
|
||||
pin_name = 'axis-%c-position' % a
|
||||
if a == axis_letter:
|
||||
if not close_enough(h[pin_name], target):
|
||||
print("axis %c didn't get to target (start=%.6f, target=%.6f, got to %.6f)" % (axis_letter, start_pos[axis_letter], target, h['axis-%c-position' % axis_letter]))
|
||||
success = False
|
||||
else:
|
||||
if h[pin_name] != start_pos[a]:
|
||||
print("axis %c moved from %.6f to %.6f but should not have!" % (a, start_pos[a], h[pin_name]))
|
||||
success = False
|
||||
|
||||
l.wait_for_axis_to_stop(axis_letter)
|
||||
|
||||
if not success:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
#
|
||||
# set up pins
|
||||
# shell out to halcmd to make nets to halui and motion
|
||||
#
|
||||
|
||||
h = hal.component("test-ui")
|
||||
|
||||
h.newpin("axis-x-jog-enable", hal.HAL_BIT, hal.HAL_OUT)
|
||||
h.newpin("axis-x-jog-counts", hal.HAL_S32, hal.HAL_OUT)
|
||||
h.newpin("axis-x-jog-scale", hal.HAL_FLOAT, hal.HAL_OUT)
|
||||
h.newpin("axis-x-position", hal.HAL_FLOAT, hal.HAL_IN)
|
||||
|
||||
h.newpin("axis-y-jog-enable", hal.HAL_BIT, hal.HAL_OUT)
|
||||
h.newpin("axis-y-jog-counts", hal.HAL_S32, hal.HAL_OUT)
|
||||
h.newpin("axis-y-jog-scale", hal.HAL_FLOAT, hal.HAL_OUT)
|
||||
h.newpin("axis-y-position", hal.HAL_FLOAT, hal.HAL_IN)
|
||||
|
||||
h.newpin("axis-z-jog-enable", hal.HAL_BIT, hal.HAL_OUT)
|
||||
h.newpin("axis-z-jog-counts", hal.HAL_S32, hal.HAL_OUT)
|
||||
h.newpin("axis-z-jog-scale", hal.HAL_FLOAT, hal.HAL_OUT)
|
||||
h.newpin("axis-z-position", hal.HAL_FLOAT, hal.HAL_IN)
|
||||
|
||||
h.ready() # mark the component as 'ready'
|
||||
|
||||
os.system("halcmd source ./postgui.hal")
|
||||
|
||||
|
||||
#
|
||||
# connect to LinuxCNC
|
||||
#
|
||||
|
||||
c = linuxcnc.command()
|
||||
s = linuxcnc.stat()
|
||||
e = linuxcnc.error_channel()
|
||||
|
||||
l = linuxcnc_util.LinuxCNC(command=c, status=s, error=e)
|
||||
|
||||
c.state(linuxcnc.STATE_ESTOP_RESET)
|
||||
c.state(linuxcnc.STATE_ON)
|
||||
|
||||
|
||||
# must home to use Teleop mode
|
||||
|
||||
c.home(-1)
|
||||
c.wait_complete()
|
||||
|
||||
l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
c.mode(linuxcnc.MODE_MANUAL)
|
||||
|
||||
|
||||
#
|
||||
# run the test
|
||||
#
|
||||
# These jog_joint() functions will exit with a return value of 1 if
|
||||
# something goes wrong.
|
||||
#
|
||||
|
||||
jog_axis('x', counts=1, scale=0.001)
|
||||
jog_axis('y', counts=10, scale=-0.025)
|
||||
jog_axis('z', counts=-100, scale=0.100)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
3
tests/motion/jogwheel-axis/test.sh
Executable file
3
tests/motion/jogwheel-axis/test.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
exec linuxcnc -r motion-test.ini
|
||||
2
tests/motion/jogwheel-joint/.gitignore
vendored
Normal file
2
tests/motion/jogwheel-joint/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
sim.var
|
||||
sim.var.bak
|
||||
2
tests/motion/jogwheel-joint/checkresult
Executable file
2
tests/motion/jogwheel-joint/checkresult
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exit 0 # test failure is indicated by test.sh exit value
|
||||
96
tests/motion/jogwheel-joint/motion-test.ini
Normal file
96
tests/motion/jogwheel-joint/motion-test.ini
Normal file
@@ -0,0 +1,96 @@
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
DEBUG = 0x0
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = ./test-ui.py
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = sim.var
|
||||
USER_M_PATH = ./subs
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
COMM_TIMEOUT = 4.0
|
||||
BASE_PERIOD = 0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
MDI_QUEUED_COMMANDS=10000
|
||||
|
||||
[HAL]
|
||||
HALFILE = LIB:core_sim.hal
|
||||
POSTGUI_HALFILE = postgui.hal
|
||||
|
||||
[TRAJ]
|
||||
NO_FORCE_HOMING=1
|
||||
AXES = 3
|
||||
COORDINATES = X Y Z
|
||||
HOME = 0 0 0
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 1.2
|
||||
MAX_LINEAR_VELOCITY = 4
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
|
||||
[AXIS_Z]
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 1000.0
|
||||
BACKLASH = 0.000
|
||||
INPUT_SCALE = 4000
|
||||
OUTPUT_SCALE = 1.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
FERROR = 0.050
|
||||
MIN_FERROR = 0.010
|
||||
48
tests/motion/jogwheel-joint/postgui.hal
Normal file
48
tests/motion/jogwheel-joint/postgui.hal
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
#
|
||||
# joint 0
|
||||
#
|
||||
|
||||
net joint-0-jog-enable <= test-ui.joint-0-jog-enable
|
||||
net joint-0-jog-enable => joint.0.jog-enable
|
||||
|
||||
net joint-0-jog-counts <= test-ui.joint-0-jog-counts
|
||||
net joint-0-jog-counts => joint.0.jog-counts
|
||||
|
||||
net joint-0-jog-scale <= test-ui.joint-0-jog-scale
|
||||
net joint-0-jog-scale => joint.0.jog-scale
|
||||
|
||||
net Xpos => test-ui.joint-0-position
|
||||
|
||||
|
||||
#
|
||||
# joint 1
|
||||
#
|
||||
|
||||
net joint-1-jog-enable <= test-ui.joint-1-jog-enable
|
||||
net joint-1-jog-enable => joint.1.jog-enable
|
||||
|
||||
net joint-1-jog-counts <= test-ui.joint-1-jog-counts
|
||||
net joint-1-jog-counts => joint.1.jog-counts
|
||||
|
||||
net joint-1-jog-scale <= test-ui.joint-1-jog-scale
|
||||
net joint-1-jog-scale => joint.1.jog-scale
|
||||
|
||||
net Ypos => test-ui.joint-1-position
|
||||
|
||||
|
||||
#
|
||||
# joint 2
|
||||
#
|
||||
|
||||
net joint-2-jog-enable <= test-ui.joint-2-jog-enable
|
||||
net joint-2-jog-enable => joint.2.jog-enable
|
||||
|
||||
net joint-2-jog-counts <= test-ui.joint-2-jog-counts
|
||||
net joint-2-jog-counts => joint.2.jog-counts
|
||||
|
||||
net joint-2-jog-scale <= test-ui.joint-2-jog-scale
|
||||
net joint-2-jog-scale => joint.2.jog-scale
|
||||
|
||||
net Zpos => test-ui.joint-2-position
|
||||
|
||||
120
tests/motion/jogwheel-joint/test-ui.py
Executable file
120
tests/motion/jogwheel-joint/test-ui.py
Executable file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import linuxcnc
|
||||
import hal
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import math
|
||||
|
||||
|
||||
def wait_for_joint_to_stop(joint_number):
|
||||
pos_pin = 'joint-%d-position' % joint_number
|
||||
start_time = time.time()
|
||||
timeout = 2.0
|
||||
prev_pos = h[pos_pin]
|
||||
while (time.time() - start_time) < timeout:
|
||||
time.sleep(0.1)
|
||||
new_pos = h[pos_pin]
|
||||
if new_pos == prev_pos:
|
||||
return
|
||||
prev_pos = new_pos
|
||||
print("Error: joint didn't stop jogging!")
|
||||
print("joint %d is at %.6f %.6f seconds after reaching target (prev_pos=%.6f)" % (joint_number, h[pos_pin], timeout, prev_pos))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def close_enough(a, b, epsilon=0.000001):
|
||||
return math.fabs(a - b) < epsilon
|
||||
|
||||
|
||||
def jog_joint(joint_number, counts=1, scale=0.001):
|
||||
timeout = 5.0
|
||||
|
||||
start_pos = 3*[0]
|
||||
for j in range(0,3):
|
||||
start_pos[j] = h['joint-%d-position' % j]
|
||||
|
||||
target = h['joint-%d-position' % joint_number] + (counts * scale)
|
||||
|
||||
h['joint-%d-jog-scale' % joint_number] = scale
|
||||
h['joint-%d-jog-enable' % joint_number] = 1
|
||||
h['joint-%d-jog-counts' % joint_number] += counts
|
||||
|
||||
start_time = time.time()
|
||||
while not close_enough(h['joint-%d-position' % joint_number], target) and (time.time() - start_time < timeout):
|
||||
#print "joint %d is at %.9f" % (joint_number, h['joint-%d-position' % joint_number])
|
||||
time.sleep(0.010)
|
||||
|
||||
h['joint-%d-jog-enable' % joint_number] = 0
|
||||
|
||||
print("joint jogged from %.6f to %.6f (%d counts at scale %.6f)" % (start_pos[joint_number], h['joint-%d-position' % joint_number], counts, scale))
|
||||
|
||||
success = True
|
||||
for j in range(0,3):
|
||||
pin_name = 'joint-%d-position' % j
|
||||
if j == joint_number:
|
||||
if not close_enough(h[pin_name], target):
|
||||
print("joint %d didn't get to target (start=%.6f, target=%.6f, got to %.6f)" % (joint_number, start_pos[joint_number], target, h['joint-%d-position' % joint_number]))
|
||||
success = False
|
||||
else:
|
||||
if h[pin_name] != start_pos[j]:
|
||||
print("joint %d moved from %.6f to %.6f but should not have!" % (j, start_pos[j], h[pin_name]))
|
||||
success = False
|
||||
|
||||
wait_for_joint_to_stop(joint_number)
|
||||
|
||||
if not success:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
#
|
||||
# set up pins
|
||||
# shell out to halcmd to make nets to halui and motion
|
||||
#
|
||||
|
||||
h = hal.component("test-ui")
|
||||
|
||||
h.newpin("joint-0-jog-enable", hal.HAL_BIT, hal.HAL_OUT)
|
||||
h.newpin("joint-0-jog-counts", hal.HAL_S32, hal.HAL_OUT)
|
||||
h.newpin("joint-0-jog-scale", hal.HAL_FLOAT, hal.HAL_OUT)
|
||||
h.newpin("joint-0-position", hal.HAL_FLOAT, hal.HAL_IN)
|
||||
|
||||
h.newpin("joint-1-jog-enable", hal.HAL_BIT, hal.HAL_OUT)
|
||||
h.newpin("joint-1-jog-counts", hal.HAL_S32, hal.HAL_OUT)
|
||||
h.newpin("joint-1-jog-scale", hal.HAL_FLOAT, hal.HAL_OUT)
|
||||
h.newpin("joint-1-position", hal.HAL_FLOAT, hal.HAL_IN)
|
||||
|
||||
h.newpin("joint-2-jog-enable", hal.HAL_BIT, hal.HAL_OUT)
|
||||
h.newpin("joint-2-jog-counts", hal.HAL_S32, hal.HAL_OUT)
|
||||
h.newpin("joint-2-jog-scale", hal.HAL_FLOAT, hal.HAL_OUT)
|
||||
h.newpin("joint-2-position", hal.HAL_FLOAT, hal.HAL_IN)
|
||||
|
||||
h.ready() # mark the component as 'ready'
|
||||
|
||||
os.system("halcmd source ./postgui.hal")
|
||||
|
||||
|
||||
#
|
||||
# connect to LinuxCNC
|
||||
#
|
||||
|
||||
c = linuxcnc.command()
|
||||
c.state(linuxcnc.STATE_ESTOP_RESET)
|
||||
c.state(linuxcnc.STATE_ON)
|
||||
c.mode(linuxcnc.MODE_MANUAL)
|
||||
|
||||
|
||||
#
|
||||
# run the test
|
||||
#
|
||||
# These jog_joint() functions will exit with a return value of 1 if
|
||||
# something goes wrong.
|
||||
#
|
||||
|
||||
jog_joint(0, counts=1, scale=0.001)
|
||||
jog_joint(1, counts=10, scale=-0.025)
|
||||
jog_joint(2, counts=-100, scale=0.100)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
3
tests/motion/jogwheel-joint/test.sh
Executable file
3
tests/motion/jogwheel-joint/test.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
exec linuxcnc -r motion-test.ini
|
||||
Reference in New Issue
Block a user