Skip to content

Commit

Permalink
tests/pll_correction: fix integer overflow error; desensitize
Browse files Browse the repository at this point in the history
A MK [Jenkins build for an unrelated PR][1] failed in this test,
meaning that this test's parameters are too restrictive, even for
non-RT POSIX threads.

This commit:
- Fixes an integer overflow that caused it to fail on
  systems with very high jitter
- Desensitizes the test by only requiring most of the final samples to
  be within 1% of the total phase offset

Other changes:
- Add README.md
- Parameterize PERIOD & NUMSAMPS

[1]: https://jenkins.machinekit.io/job/machinekit-PR-builder/68/console
  • Loading branch information
zultron committed May 18, 2018
1 parent 2d0e425 commit 3a4e3cb
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 42 deletions.
20 changes: 20 additions & 0 deletions tests/pll_correction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
This tests @sittner's [add-task-pll-functions.patch][1] as ported to
Machinekit in [PR #1373][2].

The test .hal file starts the custom `pll_correction` component, and a
`halsampler` takes `numsamps*4` samples (`numsamps` configured in
`params.py.sh`).

The `pll_correction.comp` HAL component sets the phase to ten times
the period. The first `numsamps*3` samples should be enough time to
lock the phase, and the final `numsamps` samples are counted to see
how many are within 1% of the expected phase difference.

Finally, the python `checkresult` script parses the `halsampler`
output, checking the final sample. Most of the samples' PLL phase
error must be within 1% of the total phase shift, or the test is
deemed to have failed. (This is set very loosely in order to succeed
even on systems with pathological jitter.)

[1]: https://github.com/sittner/linuxcnc-ethercat/blob/c84a868b/patches/add-task-pll-functions.patch
[2]: https://github.com/machinekit/machinekit/pull/1373
51 changes: 23 additions & 28 deletions tests/pll_correction/checkresult
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
#!/usr/bin/python

import sys
import os
import sys, os

# Params from test.hal
period = 1000000
# Source params.py.sh
execfile(os.path.join(os.path.dirname(__file__), "params.py.sh"))
# Params from pll_correction.hal
period = int(PERIOD)
# Params from pll_correction.comp
numsamps = 1000
numsamps = int(NUMSAMPS)
# Number of periods in the future to lock onto
pll_periods = int(PLL_PERIODS)

os.chdir(os.path.dirname(os.path.realpath(__file__)))

# Read last line of file
with open("result", 'r') as f:
with open("stderr", 'a') as log:
for line in f:
line = line.rstrip('[ \n]')
(cycle_count, period_actual, pll_err, samp_avg, phase_diff) = (
[int(s) for s in line.split(' ')])
for line in f:
line = line.rstrip('[ \n]')

(cycle_count, period_actual, pll_err, samp_avg, phase_diff, mode) = (
[int(s) for s in line.split(' ')])

if cycle_count == 2*numsamps:
log.write("%s\n" % line)
if abs(samp_avg) > period/100:
log.write("0 PLL didn't converge: abs(%d) > %d/100\n" % (
samp_avg, period))
sys.exit(-1)
else:
log.write("0 PLL converged: abs(%d) <= %d/100\n" % (
samp_avg, period))
if cycle_count == 4*numsamps:
log.write("%s\n" % line)
if abs(samp_avg) > period/100:
log.write("10 PLL didn't converge: abs(%d) > %d/100\n" % (
samp_avg, period))
sys.exit(-1)
else:
log.write("10 PLL converged: abs(%d) <= %d/100\n" % (
samp_avg, period))
target = period * pll_periods
if mode < 500:
sys.stderr.write(
"PLL didn't converge: samples w/ <1%% error = %d/%d\n" %
(mode,numsamps))
with open("result", "r") as f: # Dump results to stderr
for line in f:
sys.stderr.write(line)
sys.exit(-1)
10 changes: 10 additions & 0 deletions tests/pll_correction/params.py.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file is sourced both by test.sh and checkresult (python)!!!

# Period
PERIOD=1000000

# Number of samples; max 1000 (or adjust `samps` in `pll_correction.comp`)
NUMSAMPS=1000

# Number of periods in the future to lock onto
PLL_PERIODS=10
28 changes: 19 additions & 9 deletions tests/pll_correction/pll_correction.comp
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ pin out u32 ref_lo;
pin out u32 cycle_count = 0;

// Buffer for average
variable int numsamps = 1000;
// - Number of samples here must match numsamps in checkresult
// - Number of samples here must be 1/4 halsampler -n ### in pll_correction.hal
pin in u32 numsamps;
variable int samps[1000];
variable int samp_last;
pin out s32 samp_avg;

// Mode: number of samples where pll error under 1% of period
pin out u32 mode;

// PLL settings
pin out bit pll_on;
variable int pll_periods = 10; // Lock on to this many phases in the future
pin in u32 pll_periods; // Lock on to this many phases in the future
// Monitor phase difference
pin out s32 phase_diff;

Expand Down Expand Up @@ -66,21 +70,27 @@ FUNCTION(_) {
phase_diff = time(now) - time(time_base);

// Incremental phase error
pll_err = phase_diff - (
// Maintain 0 err in beginning, period*pll_periods later
!(pll_on = (cycle_count > numsamps*2)) ? 0 : period*pll_periods);
pll_err = phase_diff - period*pll_periods;

// Adjust phase
rtapi_task_pll_set_correction(-pll_err);

// Get reference (unused)
time_set(ref, rtapi_task_pll_get_reference());

// Averages
// Average
samps[(samp_last++)%numsamps] = pll_err;
samp_avg = 0;
for (int i=0; i<min(numsamps,samp_last); i++) samp_avg += samps[i];
samp_avg /= min(numsamps,samp_last);
int avgsamps = min(numsamps,samp_last);
for (int i=0; i<min(numsamps,samp_last); i++)
// Avoid integer overflow by dividing first (round result)
samp_avg += (samps[i] + avgsamps/2)/avgsamps;

// Mode
mode = 0;
for (int i=0; i<min(numsamps,samp_last); i++)
if (samps[i] < period*0.01)
mode++;

// Updates for next cycle
time_set(prev, time(now));
Expand Down
13 changes: 8 additions & 5 deletions tests/pll_correction/pll_correction.hal
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@

loadrt threads name1=t period1=1000000
# The period1 here must match period in checkresult
loadrt threads name1=t period1=$(PERIOD)

# Load pc comp
loadrt pll_correction names=pc
setp pc.numsamps $(NUMSAMPS)
setp pc.pll-periods $(PLL_PERIODS)
addf pc t

# Load sampler comp
loadrt sampler cfg=ussss depth=2000
loadrt sampler cfg=ussssu depth=2000
addf sampler.0 t

# Net pc signals to sampler
Expand All @@ -15,13 +17,14 @@ net period-actual pc.period-actual => sampler.0.pin.1
net pll-err pc.pll-err => sampler.0.pin.2
net samp-avg pc.samp-avg => sampler.0.pin.3
net phase-diff pc.phase-diff => sampler.0.pin.4
net mode pc.mode => sampler.0.pin.5

# Start threads and wait for userland sampler to exit
start

# Load userland sampler comp
# - LinuxCNC
#loadusr -Wn halsampler halsampler -N halsampler -n 100
#loadusr -Wn halsampler halsampler -N halsampler -n 2000
# - Machinekit
loadusr -w halsampler -n 4000
loadusr -w halsampler -n $(NUMSAMPS_X4)

4 changes: 4 additions & 0 deletions tests/pll_correction/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@

COMP="$(which halcompile || which comp)"
$COMP --install pll_correction.comp 1>&2
# Read parameters
. params.py.sh
export NUMSAMPS PERIOD PLL_PERIODS
export NUMSAMPS_X4=$(($NUMSAMPS * 4))
halrun -f pll_correction.hal

0 comments on commit 3a4e3cb

Please sign in to comment.