xhc-whb04b-6
NAME
xhc-whb04b-6 - Userspace jog dial HAL component for the wireless XHC WHB04B-6 USB device.
SYNOPSIS
xhc-whb04b-6 [-h] | [-H] [OPTIONS]
DESCRIPTION
The xhc-whb04b-6 HAL component supports the XHC WHB04B-6, a 6-axis wireless USB pendant. It provides a number of push-buttons, a jogwheel, two rotary buttons for axis and speed / step selection and an ordinary LCD display.
The LCD display, having a very simple firmware interface, indicates the following listed information only. No other information, such as custom data, can be printed.
Activated axis (X, Y, Z, A, B or C).
Current axis position of X, Y, Z and separately of A, B, C.
Whether machine (X, Y, Z, A, B or C) or relative (X1, Y1, Z1, A1, B1 or C1) coordinates are displayed.
Step size or velocity depending on the operating mode (MPG or Step|Continuous).
Feed rate of axis.+
Spindle speed in [r/m].
Machine state such as reset.
Battery level
Wireless signal strength
The pendant display, its rotary selector switch, and the component pin names use designators x,y,z,a,b and c. While this arrangement presumes a machine configured as X, Y, Z, A, B an C, the pins can be assigned independently as required in a HAL configuration.
OPTIONS
-h, --help |
Prints the synopsis and the most commonly used commands. |
-H |
Run xhc-whb04b-6 in HAL-mode instead of interactive mode. When in HAL mode commands from device will be exposed to HAL’s shred memory. Interactive mode is useful for testing device connectivity and debugging. |
-t |
Wait with timeout for USB device then proceed, exit otherwise. Without -t the timeout is implicitly infinite. |
-u, -U |
Show received data from device. With -U received and transmitted data will be printed. Output is prefixed with "usb". |
-p |
Show HAL pins and HAL related messages. Output is prefixed with "hal". |
-e |
Show captured events such as button pressed/released, jog dial, axis rotary button, and feed rotary button event. Output is prefixed with "event". |
-a |
Enable all logging facilities without explicitly specifying each. |
-c |
Enable checksum output which is necessary for debugging the checksum generator function. Do not rely on this feature since it will be removed once the generator is implemented. |
-n |
Force being silent and not printing any output except of errors. This will also inhibit messages prefixed with "init". |
UDEV
The xhc-whb04b-6 executable needs permission for reading the pendant’s USB device.
There may be the need for additional udev rules.
If so, this file
/etc/udev/rules.d/99-xhc-whb04b-6.rules
should be created with the single line
ATTR{idProduct}=="eb93", ATTR{idVendor}=="10ce", MODE="0666", OWNER="root", GROUP="plugdev"
.
Standalone Usage
The xhc-whb04b-6 program can be run from the command line without LinuxCNC to test a pendant. This standalone mode is used to identify the button codes produced for each button press and debug transmitted USB data.
EXAMPLES
xhc-whb04b-6 -ue |
Start in userspace mode (simulation) and prints incoming USB data transfer and generated key pressed/released events. |
xhc-whb04b-6 -p |
Start in userspace mode (simulation) and prints HAL pin names and events distributed to HAL memory. |
xhc-whb04b-6 -Hn |
Start in HAL mode and avoid output, except of errors. |
Hal Usage
Use the -H option to specify HAL mode and other options as required:
loadusr -W xhc-whb04b-6 -H
Input/Output Signals
Note: For each button an output pin is provided even if no functionality is realized with that signal.
For example, to stop a running program the Stop button pin may be directly connected to halui.program.stop.
However, to start/pause/resume a program, the corresponding button toggles besides whb.button.start-pause
also the whb.halui.program.{run,pause,resume} signals accordingly.
Note: The Spindle+/Spindle- buttons do manipulate the spindle override.
The spindle speed is set with the respective combos Fn + Spindle- and FN + Spindle+.
The following tables list all in-/output pins and state which signals they are meant to be connected to.
Axis and Stepgen
Signals utilized for moving axis.
<N> … denotes the axis number, which is of {0, 1, 2, 3, 4, 5}
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.halui.home-all |
bit |
out |
connect to halui.home-all, driven by macro-10 |
whb.halui.joint.<N>.select |
bit |
out |
connect to halui.joint.<N>.select |
whb.axis.<N>.jog-counts |
s32 |
out |
connect to axis.<N>.jog-counts |
whb.axis.<N>.jog-enable |
bit |
out |
connect to axis.<N>.jog-enable |
whb.axis.<N>.jog-scale |
float |
out |
connect to axis.<N>.jog-scale |
whb.axis.<N>.jog-vel-mode |
bit |
out |
connect to axis.<N>.jog-jog-vel-mode |
whb.stepgen.<N>.maxvel |
float |
in |
connect to stepgen.<N>.maxvel |
whb.stepgen.<N>.position-scale |
float |
in |
connect to stepgen.<N>.position-scale |
whb.halui.axis.<N>.pos-feedback |
float |
in |
connect to halui.axis.<N>.pos-feedback |
whb.halui.axis.<N>.pos-relative |
float |
in |
connect to halui.axis.<N>.pos-relative |
Emergency Stop
Signals utilized for safety / Emergency Stop.
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.halui.estop.activate |
bit |
out |
connect to halui.estop.activate |
whb.halui.estop.is-activated |
bit |
in |
connect to halui.estop.is-activated |
whb.halui.estop.reset |
bit |
out |
connect to halui.estop.reset |
Machine
Signals utilized for toggling machine status.
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.halui.machine.on |
bit |
out |
connect to halui.machine.on |
whb.halui.machine.is-on |
bit |
in |
connect to halui.machine.is-on |
whb.halui.machine.off |
bit |
out |
connect to halui.machine.off |
Spindle
Signals utilized for operating a spindle.
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.halui.spindle.start |
bit |
out |
connect to halui.spindle.start |
whb.halui.spindle.is-on |
bit |
in |
connect to halui.spindle.is-on |
whb.halui.spindle.stop |
bit |
out |
connect to halui.spindle.stop |
whb.halui.spindle.forward |
bit |
out |
connect to halui.spindle.forward |
whb.halui.spindle.reverse |
bit |
out |
connect to halui.spindle.reverse |
whb.halui.spindle.decrease |
bit |
out |
connect to halui.spindle.decrease |
whb.halui.spindle.increase |
bit |
out |
connect to halui.spindle.increase |
whb.halui.spindle-override.increase |
bit |
out |
connect to halui.spindle-override.increase |
whb.halui.spindle-override.decrease |
bit |
out |
connect to halui.spindle-override.decrease |
whb.halui.spindle-override.value |
float |
in |
connect to halui.spindle-override.value |
whb.motion.spindle-speed-abs |
float |
in |
connect to motion.spindle-speed-out-abs |
Feed
Signals utilized for operating feed and feed override.
The feed rotary button can serve in
MPG (Manual Pulse Generator),
Step|Continuous mode and at
the special position Lead.
MPG: In this mode jogging is performed at the selected feed rate.
As long the jogwheel turns, the selected axis moves.
Step|Continuous: In this mode the machine moves steps * wheel_counts at the currently selected step size and the
current set feed rate in machine units.
If the commanded position is not reached the machine keeps moving even the jogwheel is not turning.
Lead: Manipulates the feed override - fine grained in MPG mode (1% step), coarse grained in Step|Continuous mode (10% step).
Note: As a consequence of both modes, switching the feed rotary button back from Lead to any other position can be confusing.
Depending on the mode before turning the rotary button, the feed override results in different values.
In MPG the feed rate will change to 100%, 60%, … and so forth. In Step|Continuous mode the feed rate will remain.
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.motion.current-vel |
float |
in |
connect to motion.current-vel |
whb.halui.feed-override.value |
float |
in |
connect to halui.feed-override.value |
whb.halui.feed-override.decrease |
bit |
out |
connect to halui.feed-override.decrease |
whb.halui.feed-override.increase |
bit |
out |
connect to halui.feed-override.increase |
whb.halui.feed-override.scale |
float |
out |
connect to halui.feed-override.scale |
whb.halui.feed-override.direct-val |
bit |
out |
connect to halui.feed-override.direct-val |
whb.halui.feed-override.count-enable |
bit |
out |
connect to halui.feed-override.count-enablev If true, modify feed override when counts changes. |
whb.halui.feed-override.counts |
s32 |
out |
connect to halui.feed-override.counts |
whb.halui.feed-override.min-value |
float |
in |
set min feed override value, i.e. |
whb.halui.feed-override.max-value |
float |
in |
set min feed override value, i.e. |
halui.feed.selected-0.001 |
bit |
out |
True on feed rotary button pointing to 0.001 (2%), false otherwise. |
halui.feed.selected-0.01 |
bit |
out |
True on feed rotary button pointing to 0.01 (5%), false otherwise. |
halui.feed.selected-0.1 |
bit |
out |
True on feed rotary button pointing to 0.1 (10%), false otherwise. |
halui.feed.selected-1.0 |
bit |
out |
True on feed rotary button pointing to 1.0 (30%), false otherwise. |
halui.feed.selected-60 |
bit |
out |
True on feed rotary button pointing to 60%, false otherwise. |
halui.feed.selected-100 |
bit |
out |
True on feed rotary button pointing to 100%, false otherwise. |
halui.feed.selected-lead |
bit |
out |
True on feed rotary button pointing to Lead%, false otherwise. |
Program
Signals for operating program and MDI mode.
whb.halui.program.run | bit | out | connect to halui.program.run Pin for running a program. |
---|---|---|---|
whb.halui.program.is-running |
bit |
in |
connect to halui.program.is-running |
whb.halui.program.pause |
bit |
out |
connect to halui.program.pause Pin for pausing a program. |
whb.halui.program.is-paused |
bit |
in |
connect to halui.program.is-paused |
whb.halui.program.resume |
bit |
out |
connect to halui.program.resume |
whb.halui.program.stop |
bit |
out |
connect to program.stop |
whb.halui.program.is-idle |
bit |
in |
connect to halui.program.is-idle |
whb.halui.mode.auto |
bit |
out |
connect to halui.mode.auto |
whb.halui.mode.is-auto |
bit |
in |
connect to halui.mode.is-auto |
whb.halui.mode.joint |
bit |
out |
connect to halui.mode.joint |
whb.halui.mode.is-joint |
bit |
in |
connect to halui.mode.is-joint |
whb.halui.mode.manual |
bit |
out |
connect to halui.mode.manual |
whb.halui.mode.is-manual |
bit |
in |
connect to halui.mode.is-manual |
whb.halui.mode.mdi |
bit |
out |
connect to halui.mode.mdi |
whb.halui.mode.is-mdi |
bit |
in |
connect to halui.mode.is-mdi |
Buttons
For flexibility reasons each button provides an output pin even if no functionality is realized directly with that signal. The Fn button can be combined with each other push-button. This includes also RESET, Stop, Start/Pause, MPG, Macro-10, and Step|Continuous. By default the more freqent used orange buttons are executed, whereas blue ones (whb.button.macro-<M>) by combining them with Fn (press Fn first then button).
<M> … denotes an arbitrary macro number which is of {1, 2, …, 16}
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.button.reset |
bit |
out |
see whb.halui.estop.{activate, reset} |
whb.button.stop |
bit |
out |
see whb.halui.program.stop |
whb.button.start-pause |
bit |
out |
see whb.halui.program.{run, pause, resume} |
whb.button.feed-plus |
bit |
out |
True on Feed+ button down, false otherwise. |
whb.button.feed-minus |
bit |
out |
True on Feed- button down, false otherwise. |
whb.button.spindle-plus |
bit |
out |
see whb.halui.spindle-override.increase |
whb.button.spindle-minus |
bit |
out |
see whb.halui.spindle-override.decrease |
whb.button.m-home |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.safe-z |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.w-home |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.s-on-off |
bit |
out |
see whb.halui.spindle.{start, stop} |
whb.button.fn |
bit |
out |
True on Fn button down, false otherwise. |
whb.button.probe-z |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-1 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-2 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-3 |
bit |
out |
see whb.halui.spindle.increase |
whb.button.macro-4 |
bit |
out |
see whb.halui.spindle.decrease |
whb.button.macro-5 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-6 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-7 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-8 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-9 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-10 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-11 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-12 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-13 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-14 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-15 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.macro-16 |
bit |
out |
connect to halui.mdi-command-<M> |
whb.button.mode-continuous |
bit |
out |
True on MPG mode button down, false otherwise. |
whb.button.mode-step |
bit |
out |
True on Step|Continuous mode button down, false otherwise. |
Pendant
Component Signal Name | Type | In/Out | Notes |
---|---|---|---|
whb.pendant.is-sleeping |
bit |
out |
True as long pendant is in sleep mode (usually a few seconds after turned off), false otherwise. |
whb.pendant.is-connected |
bit |
out |
True as long pendant is not in sleep mode (turned on), false otherwise. |
HAL Configuration Example
# ###################################################################### # spindle signals - assume halui and motion are loaded # ###################################################################### net spindle.is-on motion.spindle-on net spindle.start halui.spindle.start net spindle.stop halui.spindle.stop net spindle.forward halui.spindle.forward net spindle.reverse halui.spindle.reverse net spindle.runs-forward motion.spindle-forward net spindle.runs-reverse motion.spindle-reverse net spindle.increase halui.spindle.increase net spindle.decrease halui.spindle.decrease net spindle.spindle-override.value halui.spindle-override.value net spindle.spindle-override.increase halui.spindle-override.increase net spindle.spindle-override.decrease halui.spindle-override.decrease net spindle.velocity.abs-rpm motion.spindle-speed-out-abs # ###################################################################### # load pendant components # ###################################################################### loadusr -W xhc-whb04b-6 -H -n # pass jogwheel counts through lowpass to prevent jerking and following errors loadrt ilowpass names=ilowpass.jog.0,ilowpass.jog.1,ilowpass.jog.2 addf ilowpass.jog.0 servo-thread addf ilowpass.jog.1 servo-thread addf ilowpass.jog.2 servo-thread setp ilowpass.jog.0.scale 100 setp ilowpass.jog.1.scale 100 setp ilowpass.jog.2.scale 100 setp ilowpass.jog.0.gain 0.007 setp ilowpass.jog.1.gain 0.007 setp ilowpass.jog.2.gain 0.007 # re-scale low-passed jogwheel counts loadrt scale names=scale.axis.0,scale.axis.1,scale.axis.2 addf scale.axis.0 servo-thread addf scale.axis.1 servo-thread addf scale.axis.2 servo-thread setp scale.axis.0.gain 0.01 setp scale.axis.1.gain 0.01 setp scale.axis.2.gain 0.01 setp scale.axis.0.offset 0 setp scale.axis.1.offset 0 setp scale.axis.2.offset 0 # load toggle component for mist and flood loadrt toggle2nist names=toggle2nist.mist,toggle2nist.flood # ###################################################################### # pendant signal configuration # ###################################################################### # emergency stop signals net pdnt.machine.is-on halui.machine.is-on whb.halui.machine.is-on net pdnt.machine.on whb.halui.machine.on halui.machine.on net pdnt.machine.off whb.halui.machine.off halui.machine.off # program related signals net pdnt.program.is-idle whb.halui.program.is-idle halui.program.is-idle net pdnt.program.is-paused whb.halui.program.is-paused halui.program.is-paused net pdnt.program.is-running whb.halui.program.is-running halui.program.is-running net pdnt.program.resume whb.halui.program.resume halui.program.resume net pdnt.program.pause whb.halui.program.pause halui.program.pause net pdnt.program.run whb.halui.program.run halui.program.run net pdnt.program.stop whb.halui.program.stop halui.program.stop # "selected axis" signals net pdnt.joint.X.select whb.halui.joint.x.select halui.joint.0.select net pdnt.joint.Y.select whb.halui.joint.y.select halui.joint.1.select net pdnt.joint.Z.select whb.halui.joint.z.select halui.joint.2.select # jog signals for step and continuous mode net pdnt.stepgen.00.maxvel hpg.stepgen.00.maxvel whb.stepgen.00.maxvel net pdnt.stepgen.01.maxvel hpg.stepgen.01.maxvel whb.stepgen.01.maxvel net pdnt.stepgen.02.maxvel hpg.stepgen.02.maxvel whb.stepgen.02.maxvel net pdnt.stepgen.00.position-scale hpg.stepgen.00.position-scale whb.stepgen.00.position-scale net pdnt.stepgen.01.position-scale hpg.stepgen.01.position-scale whb.stepgen.01.position-scale net pdnt.stepgen.02.position-scale hpg.stepgen.02.position-scale whb.stepgen.02.position-scale net pdnt.axis.0.jog-scale whb.axis.0.jog-scale scale.axis.0.in net pdnt.axis.1.jog-scale whb.axis.1.jog-scale scale.axis.1.in net pdnt.axis.2.jog-scale whb.axis.2.jog-scale scale.axis.2.in net scale.axis.0.jog-scale scale.axis.0.out axis.0.jog-scale net scale.axis.1.jog-scale scale.axis.1.out axis.1.jog-scale net scale.axis.2.jog-scale scale.axis.2.out axis.2.jog-scale net pdnt.axis.0.jog-counts whb.axis.0.jog-counts ilowpass.jog.0.in net pdnt.axis.1.jog-counts whb.axis.1.jog-counts ilowpass.jog.1.in net pdnt.axis.2.jog-counts whb.axis.2.jog-counts ilowpass.jog.2.in net pdnt.ilowpass.jog.0.jog-counts ilowpass.jog.0.out axis.0.jog-counts net pdnt.ilowpass.jog.1.jog-counts ilowpass.jog.1.out axis.1.jog-counts net pdnt.ilowpass.jog.2.jog-counts ilowpass.jog.2.out axis.2.jog-counts net pdnt.axis.0.jog-enable whb.axis.0.jog-enable axis.0.jog-enable net pdnt.axis.1.jog-enable whb.axis.1.jog-enable axis.1.jog-enable net pdnt.axis.2.jog-enable whb.axis.2.jog-enable axis.2.jog-enable net pdnt.axis.0.jog-vel-mode whb.axis.0.jog-vel-mode axis.0.jog-vel-mode net pdnt.axis.1.jog-vel-mode whb.axis.1.jog-vel-mode axis.1.jog-vel-mode net pdnt.axis.2.jog-vel-mode whb.axis.2.jog-vel-mode axis.2.jog-vel-mode # macro buttons to MDI commands net pdnt.macro.3 whb.button.macro-3 halui.mdi-command-03 net pdnt.home-all whb.halui.home-all halui.home-all # reset, stop, start/pause/resume buttons net pdnt.button.reset whb.button.reset net pdnt.button.stop whb.button.stop net pdnt.button.start-pause whb.button.start-pause # special positions signals net pdnt.button.m-home whb.button.m-home halui.mdi-command-01 net pdnt.button.w-home whb.button.w-home halui.mdi-command-05 net pdnt.button.safe-z whb.button.safe-z halui.mdi-command-00 net pdnt.button.probe-z whb.button.probe-z halui.mdi-command-02 # spindle related signals net spindle.is-on whb.halui.spindle.is-on net spindle.stop whb.halui.spindle.stop net spindle.forward whb.halui.spindle.forward net spindle.reverse whb.halui.spindle.reverse net pdnt.button.spindle-plus whb.button.spindle-plus net pdnt.button.spindle-minus whb.button.spindle-minus net spindle.increase whb.button.macro-3 net spindle.decrease whb.button.macro-4 net spindle.spindle-override.value whb.halui.spindle-override.value net spindle.spindle-override.increase whb.halui.spindle-override.increase net spindle.spindle-override.decrease whb.halui.spindle-override.decrease net spindle.velocity.abs-rpm whb.motion.spindle-speed-abs # machine mode related signals net pdnt.halui.mode.auto whb.halui.mode.auto halui.mode.auto net pdnt.halui.mode.manual whb.halui.mode.manual halui.mode.manual net pdnt.halui.mode.mdi whb.halui.mode.mdi halui.mode.mdi net pdnt.halui.mode.is-auto halui.mode.is-auto whb.halui.mode.is-auto net pdnt.halui.mode.is-manual halui.mode.is-manual whb.halui.mode.is-manual net pdnt.halui.mode.is-mdi halui.mode.is-mdi whb.halui.mode.is-mdi # feed rate related signals net pdnt.halui.feed-override.scale whb.halui.feed-override.scale halui.feed-override.scale net pdnt.halui.feed-override.direct-val whb.halui.feed-override.direct-val halui.feed-override.direct-value net pdnt.halui.feed-override.counts whb.halui.feed-override.counts halui.feed-override.counts # take feed override min/max values from the GUI setp whb.halui.feed-override.min-value [DISPLAY]MIN_FEED_OVERRIDE setp whb.halui.feed-override.max-value [DISPLAY]MAX_FEED_OVERRIDE net pdnt.halui.feed-override.increase whb.halui.feed-override.increase halui.feed-override.increase net pdnt.halui.feed-override.decrease whb.halui.feed-override.decrease halui.feed-override.decrease net pdnt.button.feed-plus whb.button.feed-plus net pdnt.button.feed-minus whb.button.feed-minus # for printing the speed on display, the motion.current-vel velocity is used net pdnt.motion.current-vel motion.current-vel whb.motion.current-vel # axis position related signals net pdnt.halui.axis.0.pos-feedback halui.axis.0.pos-feedback whb.halui.axis.0.pos-feedback net pdnt.halui.axis.1.pos-feedback halui.axis.1.pos-feedback whb.halui.axis.1.pos-feedback net pdnt.halui.axis.2.pos-feedback halui.axis.2.pos-feedback whb.halui.axis.2.pos-feedback net pdnt.halui.axis.0.pos-relative halui.axis.0.pos-relative whb.halui.axis.0.pos-relative net pdnt.halui.axis.1.pos-relative halui.axis.1.pos-relative whb.halui.axis.1.pos-relative net pdnt.halui.axis.2.pos-relative halui.axis.2.pos-relative whb.halui.axis.2.pos-relative # flood and mist toggle toggle signals net pdnt.macro.1 whb.button.macro-1 toggle2nist.mist.in net pdnt.mist.is-on halui.mist.is-on toggle2nist.mist.is-on net pdnt.mist.on toggle2nist.mist.on halui.mist.on net pdnt.mist.off toggle2nist.mist.off halui.mist.off net pdnt.macro.2 whb.button.macro-2 toggle2nist.flood.in net pdnt.flood.is-on halui.flood.is-on toggle2nist.flood.is-on net pdnt.flood.on toggle2nist.flood.on halui.flood.on net pdnt.flood.off toggle2nist.flood.off halui.flood.off
SEE ALSO
xhc-hb04.1(1), xhc-whb04b-6 developer documentation on GitHub
NOTES
The CRC code function is not disclosed by the manufacturer. Ths the CRC value transmitted with each package is not checked yet. Feel free to help us enhance the component.
AUTHORS
This component was started by Raoul Rubien based on predecessor device component xhc-hb04.cc. https://github.com/machinekit/machinekit/graphs/contributors gives you a more complete list of contributors.
REPORTING BUGS
Report bugs via the machinekit forum at https://groups.google.com/forum/#!forum/machinekit
HISTORY
The component was developed accidentally as leisure project. The development started with the xhc-whb04 (4-axis wireless pendant) implementation as reference. 73 & many thanks to the developers who delivered provided an excellent preparatory work!
COPYRIGHT
Copyright (c) 2018 Raoul Rubien. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.