#emc-devel | Logs for 2006-11-05

Back
[00:13:45] <alex_joni> logger_dev: bookmark
[00:13:46] <alex_joni> Just this once .. here's the log: http://www.linuxcnc.org/irc/irc.freenode.net:6667/emcdevel/2006-11-05.txt
[00:17:48] <jmkasunich> alex_joni: still here
[00:17:49] <jmkasunich> ?
[00:17:55] <alex_joni> yup
[00:18:04] <jmkasunich> Traceback (most recent call last):
[00:18:04] <jmkasunich> File "/home/jmkasunich/emcdev/emc2head/bin/axis", line 57, in ?
[00:18:04] <jmkasunich> import gcode
[00:18:04] <jmkasunich> ImportError: /home/jmkasunich/emcdev/emc2head/lib/librs274.so: undefined symbol: _Z17DISABLE_FEED_HOLDv
[00:18:04] <jmkasunich> Shutting down and cleaning up EMC2...
[00:18:10] <jmkasunich> (running sim/axis)
[00:18:17] <alex_joni> bugger... run tkemc for a while
[00:18:22] <alex_joni> I'll fix it in a sec.
[00:24:28] <jmkasunich> alex_joni: the "active g-codes display" in tkemc shows M48 or M49 all the time
[00:24:35] <jmkasunich> it shows M52 when it is on
[00:24:52] <jmkasunich> it shows M53 when it is on
[00:25:01] <jmkasunich> I haven't gotten M50 or M51 to appear there though
[00:25:05] <alex_joni> set M49
[00:25:11] <alex_joni> then set M50
[00:25:20] <jmkasunich> ok
[00:26:00] <jmkasunich> duh, it works correctly
[00:26:03] <jmkasunich> hi ray
[00:26:31] <alex_joni> hi ray
[00:26:59] <jmkasunich> we've been messing with feed override enable, spindle override enable (M48/M49) and some other things
[00:27:14] <jmkasunich> enable/disable for adaptive feed
[00:27:19] <rayh> Hi guys
[00:27:21] <jmkasunich> and for feedhold (which is new)
[00:27:52] <rayh> I do feedhold in mini by setting feedrate override to 0
[00:28:05] <alex_joni> rayh: right.. we plan to do it in HAL too
[00:28:17] <jmkasunich> yeah, and you can do it from HAL by setting adaptive feed to zero
[00:28:28] <jmkasunich> but we're gonna add a HAL bit for feedhold, independent of adaptive feed
[00:28:29] <rayh> Okay
[00:28:38] <rayh> That sounds real good to me.
[00:28:54] <jmkasunich> so we actually have four things that can be enabled or disabled
[00:28:58] <jmkasunich> feed override
[00:29:01] <jmkasunich> spindle override
[00:29:03] <jmkasunich> adaptive feed
[00:29:12] <jmkasunich> feed hold (hal bit)
[00:29:20] <jmkasunich> M48 turns FO and SO on
[00:29:26] <jmkasunich> M49 turns FO and SO off
[00:29:34] <alex_joni> because of the specs
[00:29:39] <jmkasunich> M50 P1 (or M50 alone) turns FO on
[00:29:45] <jmkasunich> M50 P0 turns FO off
[00:29:57] <jmkasunich> M51 P1 or M51 turns SO on
[00:30:04] <jmkasunich> M51 P0 turns SO off
[00:30:10] <jmkasunich> M52 controls AF (same way)
[00:30:19] <jmkasunich> M53 controls FH (same way)(
[00:30:43] <jmkasunich> so you can use 48/49 and get the traditional behavior, or use 50-53 and have individual control of all four things
[00:30:58] <jmkasunich> AF defaults to off, and FH defaults to on
[00:31:09] <jmkasunich> FO and SO of default to on, as they always did
[00:31:44] <rayh> FH defaults to on?? You mean no feedhold
[00:31:52] <jmkasunich> I mean enabled
[00:32:03] <SWPadnos> on = "enable a feedhold switch"
[00:32:11] <jmkasunich> the HAL pin will be zero if not connected, so there will be no hold
[00:32:15] <SWPadnos> not "feed=0"
[00:32:19] <rayh> Okay.
[00:32:23] <jmkasunich> if you connect a signal and make it 1, feed is held
[00:32:54] <jmkasunich> M53 lets you disable that input completely (not something I think you should do, but we don't prohibit it)
[00:33:39] <SWPadnos> that would be useful for e.g. an IR reflow oven conveyor
[00:34:35] <alex_joni> SWPadnos: or for a spraying line
[00:34:36] <jmkasunich> feedhold = bake to a cinder
[00:34:42] <SWPadnos> yep ;)
[00:34:46] <alex_joni> IR detector for workpiece in place
[00:35:11] <rayh> I can see all of that and think it's great.
[00:35:25] <jmkasunich> ok
[00:35:43] <jmkasunich> alex has already made the interp and task changes
[00:35:50] <jmkasunich> I'm gonna start on the RT shortly
[00:36:37] <jmkasunich> all of these enables/disables will get queued with the moves when running a program
[00:36:59] <jmkasunich> so if you do FO off ; G1 <something> ; FO on
[00:37:12] <jmkasunich> Feedrate override will be disabled for that move only
[00:37:23] <jmkasunich> even if the move doesn't happen for a long time because of the queue
[00:37:47] <rayh> The usual way would be to turn it off on one block and back on on the next.
[00:37:54] <jmkasunich> yeah
[00:38:13] <jmkasunich> thats what I meant (consider the semicolons as separating individual blocks)
[00:38:18] <jmkasunich> I should have wrote it on three lines
[00:38:37] <rayh> Oh okay that is how I'd think of it.
[00:39:01] <rayh> although we could make it be the first to be executed on a line.
[00:40:14] <jmkasunich> looks like it is
[00:40:27] <cradek> what do I need to do to help get this stuff working?
[00:40:55] <jmkasunich> cradek: help me a bit when I get to the queue/dequeue part
[00:41:00] <cradek> ok
[00:46:20] <alex_joni> jmkasunich: anything else I can do?
[00:47:01] <jmkasunich> sleep?
[00:47:10] <jmkasunich> I'm checking to see if axis works right
[00:47:19] <jmkasunich> if so, cradek and I will finish up
[00:48:05] <alex_joni> it should.. according to my limited testing
[00:51:41] <jmkasunich> it seems fine
[01:04:42] <alex_joni> jmkasunich: my always on client is not on..
[01:04:56] <alex_joni> so if there's anything left for me to do.. please drop me an email
[01:08:27] <jmkasunich> ok
[01:20:15] <alex_joni> good night all
[01:24:29] <rayh> during configuration tonight I get the following line.
[01:24:59] <rayh> checking whether to build documentation... no
[01:25:11] <jmkasunich> the default has been change to not build the docs
[01:25:17] <jmkasunich> (to speed up the build)
[01:25:28] <rayh> and to change that?
[01:25:31] <jmkasunich> add --enable-build-documentation to your configure command line
[01:25:39] <rayh> okay thanks.
[01:30:19] <rayh> logger_dev, bookmark
[01:30:19] <rayh> Just this once .. here's the log: http://www.linuxcnc.org/irc/irc.freenode.net:6667/emcdevel/2006-11-05.txt
[02:53:38] <jmkasunich> cradek: you around?
[02:56:21] <cradek> yep
[02:56:40] <cradek> around but tired
[02:56:43] <jmkasunich> I've got code done that accepts the emcmot commands, and sets flags in emcmotStatus->enables_new
[02:57:10] <jmkasunich> and I've got folding code done, that uses either enables_new, or enables_queued, to calculate the net scale factors
[02:57:18] <cradek> cool
[02:57:25] <jmkasunich> next step is to queue and dequeue stuff for the TP
[02:57:47] <cradek> so when a move is added, you enqueue enables_new
[02:58:03] <cradek> when a move is dequeued, you write enables_queued
[02:58:10] <jmkasunich> right
[02:58:21] <cradek> ok great
[02:58:23] <jmkasunich> also, when the queue empties, you copy enables_new to enables_queued
[02:58:47] <jmkasunich> if (-1 == tpAddLine(&emcmotDebug->queue, emcmotCommand->pos, emcmotCommand->motion_type, emcmotCommand->vel, emcmotCommand->ini_maxvel, emcmotCommand->acc)) {
[02:58:54] <jmkasunich> that is the current call to enqueue a line
[02:59:15] <jmkasunich> I guess I need to add a arg to that function
[02:59:33] <jmkasunich> then dive into its implementation, add a field to a struct somewhere, .....
[02:59:41] <cradek> yep
[02:59:47] <cradek> oops brb
[03:04:41] <cradek> back
[03:05:05] <jmkasunich> AddCircle and Addline now accept an extra arg, and command.c calls them with enables_new
[03:05:23] <cradek> ok just add it to TC_STRUCT
[03:05:26] <jmkasunich> I'm about to add "unsigned char enables" to TC_STRUCT
[03:05:29] <jmkasunich> ;-)
[03:05:47] <cradek> I think you didn't need me for this...
[03:05:55] <jmkasunich> not true
[03:06:00] <jmkasunich> enqueue is easier then dequeue
[03:06:46] <jmkasunich> ok, enqueue is done
[03:06:52] <jmkasunich> now the fun part
[03:07:00] <jmkasunich> what function actually dequeues stuff?
[03:07:04] <jmkasunich> tpRunCycle maybe?
[03:07:10] <cradek> yes
[03:07:36] <cradek> we want to fix execId at the same time
[03:07:42] <jmkasunich> ok
[03:08:04] <jmkasunich> how would you rather do this? walk me thru it, or have me commit what I have and you run with it?
[03:08:40] <cradek> do you want to do it? I can do it quickly but am also willing to explain it to you instead
[03:08:59] <jmkasunich> I'm open to learning new things
[03:09:21] <jmkasunich> but if its one of those things where doing it by remote control is gonna drive you nuts...
[03:09:22] <cradek> ok find "report our line number to the guis"
[03:09:25] <jmkasunich> (btdt)
[03:09:39] <jmkasunich> got it
[03:09:57] <cradek> we are going to move that down later where we know what blending is happening
[03:10:11] <jmkasunich> so cut that comment and the following line?
[03:10:12] <cradek> so cut that and now find "blend criteria"
[03:10:19] <cradek> yes
[03:10:49] <jmkasunich> blend criteria: we are decel... ?
[03:11:09] <cradek> next one
[03:11:16] <cradek> the if
[03:11:27] <cradek> that if is "we are blending" or "we are not blending"
[03:11:30] <jmkasunich> ok, if(tc->blending ||
[03:11:49] <cradek> in the "we are blending" case see the if testing which motion is contributing more velocity
[03:12:13] <cradek> you can see how distance_to_go works, you'll do exactly the same thing
[03:12:14] <jmkasunich> if(tc->currentvel > nexttc->currentvel) ?
[03:12:18] <cradek> yes
[03:13:36] <cradek> after you're done with those two cases also do the else which is the not-blending case
[03:14:05] <cradek> so for each of the three you should set execId and enables_queued
[03:14:32] <jmkasunich> if(tc->currentvel > nexttc->currentvel) {
[03:14:33] <jmkasunich> tp->motionType = tc->canon_motion_type;
[03:14:33] <jmkasunich> emcmotStatus->distance_to_go = tc->target - tc->progress;
[03:14:33] <jmkasunich> emcmotStatus->enables_queued = tc->enables;
[03:14:33] <jmkasunich> // report our line number to the guis
[03:14:33] <jmkasunich> tp->execId = tc->id;
[03:14:35] <jmkasunich> } else {
[03:14:37] <jmkasunich> tp->motionType = nexttc->canon_motion_type;
[03:14:39] <jmkasunich> emcmotStatus->distance_to_go = nexttc->target - nexttc->progress;
[03:14:41] <jmkasunich> emcmotStatus->enables_queued = nexttc->enables;
[03:14:43] <jmkasunich> // report our line number to the guis
[03:14:45] <jmkasunich> tp->execId = nexttc->id;
[03:14:49] <jmkasunich> }
[03:14:51] <cradek> yep
[03:14:58] <cradek> now the same in the not-blending case
[03:15:14] <jmkasunich> } else {
[03:15:14] <jmkasunich> tp->motionType = tc->canon_motion_type;
[03:15:14] <jmkasunich> emcmotStatus->distance_to_go = tc->target - tc->progress;
[03:15:14] <jmkasunich> tp->currentPos = primary_after;
[03:15:14] <jmkasunich> emcmotStatus->enables_queued = tc->enables;
[03:15:15] <jmkasunich> // report our line number to the guis
[03:15:19] <jmkasunich> tp->execId = tc->id;
[03:15:21] <jmkasunich> }
[03:15:25] <cradek> yep
[03:15:38] <jmkasunich> now, what happens when the queue runs dry?
[03:15:39] <cradek> now something for the no-motion case?
[03:15:52] <cradek> // this means the motion queue is empty. This can represent
[03:16:18] <cradek> (tcqItem returns null)
[03:16:48] <jmkasunich> hmm
[03:17:09] <jmkasunich> is tpRunCycle called continuously when in coord mode?
[03:17:31] <jmkasunich> ( I mean, even before you start running a program, when a program isn't loaded, when you are in MDI, etc)
[03:17:32] <cradek> I think so, isn't that what motion does?
[03:17:56] <jmkasunich> I'd have to study it... that part of motion is taken from emc1 and is a bit unclear
[03:18:05] <jmkasunich> looking now
[03:19:14] <jmkasunich> its called whenever the cubic interpolators are empty (whatever that means)
[03:19:28] <jmkasunich> case EMCMOT_MOTION_COORD:
[03:19:28] <jmkasunich> /* check joint 0 to see if the interpolators are empty */
[03:19:28] <jmkasunich> while (cubicNeedNextPoint(&(joints[0].cubic))) {
[03:19:28] <jmkasunich> /* they're empty, pull next point(s) off Cartesian planner */
[03:19:29] <jmkasunich> /* run coordinated trajectory planning cycle */
[03:19:31] <jmkasunich> tpRunCycle(&emcmotDebug->queue, period);
[03:19:31] <cradek> there's also a block that runs when a motion ends ("this move is ending")
[03:19:42] <cradek> maybe that's what you want?
[03:20:22] <jmkasunich> nope, I want to be copying new to queued all the time unless we're working on a queued move
[03:20:40] <jmkasunich> "working on" includes "paused in the middle of" however
[03:21:09] <cradek> then I think you want "the motion queue is empty" block
[03:21:15] <jmkasunich> how is pause implemented? I
[03:21:29] <cradek> mostly it sets tc->feed_override to zero
[03:21:46] <cradek> ("handle pausing")
[03:23:18] <jmkasunich> ok, and right above that is a line that now says
[03:23:23] <jmkasunich> tc->feed_override = emcmotStatus->net_feed_scale;
[03:23:38] <jmkasunich> (I renamed overallVscale)
[03:23:53] <cradek> ok
[03:25:23] <jmkasunich> this is what I added to the if(!tc) { queue is empty block:
[03:25:29] <jmkasunich> // when not executing a move, use the current enable flags
[03:25:29] <jmkasunich> emcmotStatus->enables_queued = emcmotStatus->enables_new;
[03:25:51] <jmkasunich> are we done?
[03:26:02] <cradek> quite possibly
[03:26:14] <jmkasunich> I still need to add the feedhold HAL pin
[03:26:39] <jmkasunich> that part of the folding code is if 0'ed out right now, but its just plug and chug to add the pin
[03:26:53] <jmkasunich> right now, I init all the enables to zero
[03:27:20] <jmkasunich> I _think_ that the high level code will issue commands to turn on those that are supposed to be on by default
[03:27:25] <jmkasunich> I'll test for that
[03:28:13] <jmkasunich> mind if I send you a diff of what I have right now?
[03:28:30] <cradek> don't want to commit yet?
[03:28:30] <jmkasunich> if you could look it over while I add the HAL pin for feedhold, that would be a great help
[03:28:34] <cradek> ok
[03:28:42] <jmkasunich> I'll pastebin it
[03:28:45] <cradek> ok
[03:32:33] <jmkasunich> http://pastebin.ca/238829
[03:37:29] <cradek> I think Ifollowed it alland it looks good
[03:37:53] <jmkasunich> ok
[03:37:56] <jmkasunich> hal pin added
[03:38:16] <jmkasunich> gonna do a little testing, then commit
[03:38:28] <cradek> great
[03:44:03] <jmkasunich> darn
[03:44:19] <jmkasunich> user space does _not_ send any initial on or off commands
[03:45:01] <jmkasunich> yet the interp sends a string containing the "currently active" g and m codes to the GUIs for display
[03:45:26] <jmkasunich> there is nothing to insure that that string matches the state of the RT enable flags
[03:45:44] <cradek> task should probably set the initial conditions by issuign messages
[03:46:26] <jmkasunich> it sends commands to set the scale factors, for both spindle and feed
[03:46:32] <jmkasunich> but not to turn anything on or off
[03:46:47] <jmkasunich> now the trick is going to be figuring out how to fix that
[03:46:53] <cradek> whine to alex?
[03:46:57] <jmkasunich> I guess I can grep for the scale factor ones
[03:47:03] <jmkasunich> alex is sleeping
[03:47:22] <jmkasunich> I'm gonna be brave (at least until I get lost)
[03:47:29] <cradek> yays
[03:49:18] <cradek> you sure they don't come from the gui?
[03:49:44] <jmkasunich> the spindle scale and feed scale ones?
[03:49:49] <jmkasunich> good point
[03:50:16] <jmkasunich> the current state of M48-53 is maintained in the interp, thats what should send the inital commands to emcmot
[03:51:32] <cradek> Interp::init()
[03:52:47] <jmkasunich> what file is that in ?
[03:53:01] <cradek> rs274ngc_pre.cc
[03:53:35] <jmkasunich> duh, I was grepping with lowercase i at the beginning
[03:54:13] <jmkasunich> wtf language is that written in anyway
[03:54:18] <jmkasunich> way too much uppercase
[03:54:23] <jepler> cporkpork
[03:54:56] <jmkasunich> _setup.adaptive_feed = 0;
[03:54:56] <jmkasunich> _setup.feed_hold = ON; //enable feed hold
[03:55:00] <cradek> look at the same file in emc1 and then see if you feel like complaining
[03:55:01] <jmkasunich> these lines seem interesting
[03:55:12] <cradek> yeah I noticed 0 and ON must be opposites
[03:55:20] <cradek> in this strange universe we're in
[03:55:36] <jmkasunich> thats normal
[03:55:46] <jmkasunich> 0 = false = off
[03:55:52] <jmkasunich> non-zero = true = on
[03:56:04] <jepler> to make it clearer, change it to read: _setup.adaptive_feed = NULL;
[03:56:13] <cradek> no I insist 0 and 1 are opposites, as are ON and OFF
[03:56:51] <jmkasunich> _setup.speed_override = ON;
[03:57:06] <jmkasunich> _setup.feed_override = ON;
[03:57:18] <jmkasunich> so, there are four lines there that set the interp's initial conditions.
[03:57:21] <cradek> typedef bool ON_OFF;
[03:57:28] <cradek> * cradek cries a little
[03:57:31] <jmkasunich> now... how do we get them to send commands
[03:57:46] <cradek> SHOUTYCASE_CANON_CALLS(_setup.whatever)
[03:58:30] <jmkasunich> like this one: SET_FEED_REFERENCE(CANON_XYZ);
[03:58:33] <cradek> USE_LENGTH_UNITS(_setup.length_units);
[03:58:36] <cradek> yes
[03:58:50] <jepler> goodnight guys
[03:58:56] <cradek> night
[03:59:00] <jmkasunich> goodnight
[03:59:01] <cradek> I'm feeling that too
[04:00:44] <jmkasunich> so where are the canon calls listed?
[04:00:51] <cradek> task/emccanon.cc
[04:01:25] <jmkasunich> so DISABLE_FEED_OVERRIDE() and ENABLE_FEED_OVERRIDE()
[04:02:21] <jmkasunich> urk... would have been clearn if it was a single function with an argument
[04:02:29] <jmkasunich> s/clearn/cleaner
[04:02:51] <cradek> that's not how the rest of canon works
[04:03:33] <jmkasunich> yeah, I see FLOOD_ON and FLOOD_OFF
[04:06:03] <jmkasunich> do I dare do this:
[04:06:04] <jmkasunich> if ( _setup.feed_override ) {
[04:06:04] <jmkasunich> ENABLE_FEED_OVERRIDE();
[04:06:04] <jmkasunich> } else {
[04:06:04] <jmkasunich> DISABLE_FEED_OVERRIDE();
[04:06:04] <jmkasunich> }
[04:06:16] <jmkasunich> or is ON gonna bite me in the ass
[04:06:27] <cradek> why bother? just send the right one
[04:06:35] <cradek> since you set it in the above line
[04:06:54] <jmkasunich> I guess I was trying to future proof it
[04:07:18] <jmkasunich> if somebody decides to change the inital state, and changes only the _setup.feed_override = <foo> line
[04:08:04] <cradek> (_setup.feed_override ? ENABLE_FEED_OVERRIDE: DISABLE_FEED_OVERRIDE)();
[04:08:28] <cradek> not that I'm serious of course
[04:08:49] <jmkasunich> same problem
[04:09:06] <cradek> well ON_OFF is a bool
[04:09:14] <cradek> so you'd be fine anyway
[04:09:22] <jmkasunich> ok, thats what worried me
[04:10:10] <jmkasunich> your version is definitely shorter
[04:10:41] <jmkasunich> oh, you are doing funky things with function pointers
[04:10:49] <jmkasunich> I think I'll pass
[04:11:01] <jmkasunich> but I can rewrite the if as a one-liner
[04:14:03] <jmkasunich> I'm so used to using brackets with if and else
[04:14:07] <jmkasunich> is this legal:
[04:14:12] <jmkasunich> if ( _setup.feed_hold ) ENABLE_FEED_HOLD(); else DISABLE_FEED_HOLD();
[04:14:17] <cradek> yes
[04:14:30] <jmkasunich> ok
[04:15:17] <jmkasunich> looks like Interp::init gets called twice
[04:16:26] <cradek> huh
[04:16:27] <jmkasunich> http://pastebin.ca/238923
[04:16:42] <jmkasunich> see lines 47 thru 55
[04:17:14] <cradek> after it goes into coord mode
[04:17:30] <cradek> does it work though?
[04:17:49] <jmkasunich> work as in send the messages?
[04:17:50] <jmkasunich> yes
[04:17:57] <jmkasunich> sends em twice
[04:17:59] <cradek> I mean does the whole scheme work?
[04:18:10] <jmkasunich> well, that will take a few minutes to test
[04:18:21] <jmkasunich> and probably a g-code program or two
[04:19:10] <cradek> I'm losing consciousness here, I'll be anxious to hear about it tomorrow
[04:19:15] <jmkasunich> ok
[04:19:17] <jmkasunich> goodnight
[04:19:20] <cradek> goodnight
[04:26:01] <jmkasunich> damn.... the queue really complicates things
[04:26:10] <jmkasunich> the new stuff seems to work perfectly
[04:26:28] <jmkasunich> but I was looking at the display of "currently active" G and M codes
[04:26:42] <jmkasunich> and wondering why it didn't change on a move by move basis
[04:27:11] <jmkasunich> that list is based on the interp state, and it already read ahead to the end of the program
[04:49:07] <jmkasunich> http://pastebin.ca/238978
[04:49:19] <jmkasunich> interp issue? task issue? who knows...
[11:37:00] <alex_jon1> alex_jon1 is now known as alex_joni
[13:30:25] <cradek> hi alex_joni
[13:30:33] <alex_joni> hi chris
[13:30:38] <alex_joni> seen you've been busy
[13:30:45] <alex_joni> jmk that is <rimshot>
[13:31:03] <cradek> yeah he has been
[13:31:11] <cradek> I only helped a little
[13:31:16] <alex_joni> * alex_joni finally has his irssi back :)
[13:31:18] <cradek> I spot his bug though (out of sequence)
[13:34:09] <alex_joni> heh..nice
[13:34:25] <alex_joni> * alex_joni hasn't looked yet
[13:49:40] <cradek> yeah I fixed it, whee
[13:49:57] <alex_joni> cradek: great.. I'll test when I come back
[13:50:04] <cradek> bye
[14:53:36] <jepler> jmkasunich: yes, the "active" codes have always reflected the extent to which the interpreter has *read* the code, not *executed* it
[15:08:24] <cradek> it's really only useful for MDI
[15:38:40] <jepler> that must be why I hid it on the mdi tab in axis
[15:43:50] <cradek> yeah
[16:09:37] <cradek> has axis always left the last motion line of the program highlighted (in red) when it ends? emctop does say "id" goes to 0
[16:11:58] <cradek> hmm, guess so (2.0.4 does it)
[16:19:12] <jepler> huh, I wonder why
[16:20:15] <jepler> when called with 0, set_current_line() should remove the red
[16:27:08] <jepler> s.motion_line is still 149
[16:27:31] <cradek> oh, I wonder what "id" in emctop is then
[16:30:35] <jepler> using 'id' instead seems to behave the same except that the highlight goes away at the end of the program
[16:31:47] <jepler> axis needs a lathe splash screen
[16:31:55] <cradek> if (emcStatus->motion.traj.id > 0) {
[16:31:55] <cradek> stat->motionLine = emcStatus->motion.traj.id;
[16:31:55] <cradek> }
[17:22:20] <alex_joni> jepler: around?
[17:22:52] <jmkasunich> I just started sim/axis
[17:23:17] <alex_joni> * alex_joni has no running machine :(
[17:23:21] <alex_joni> I can only read code..
[17:23:24] <jmkasunich> emcTaskPlanInit got called twice, and Interp::init got called three times (twice from PlanInit, and once from somewhere else)
[17:23:45] <alex_joni> try Ctrl-R (reload)
[17:23:55] <alex_joni> does taskplaninit get called again?
[17:24:06] <jmkasunich> loading a file called plan init again, and called interp_init twice
[17:24:16] <jepler> alex_joni: everyone keeps asking me that
[17:24:20] <jepler> alex_joni: I'll start saying "no"
[17:24:30] <jmkasunich> reload did the same
[17:24:40] <jmkasunich> emcTaskPlanInit() start
[17:24:40] <jmkasunich> Interp::init() start
[17:24:40] <jmkasunich> Interp::init() end
[17:24:40] <jmkasunich> emcTaskPlanInit() end
[17:24:40] <jmkasunich> Interp::init() start
[17:24:41] <jmkasunich> Interp::init() end
[17:24:59] <alex_joni> start, end?
[17:25:01] <jmkasunich> so, we definitely do _not_ want to be setting defaults in Interp::init()
[17:25:12] <jmkasunich> printfs that I stuck in the functions
[17:25:20] <alex_joni> oh.. ok
[17:25:37] <jmkasunich> plan gets called, it calls interp, that returns, plan returns, then somethign else calls interp, and it returns
[17:25:52] <alex_joni> that's fscked
[17:26:06] <alex_joni> only taskplaninit should call interp::init
[17:26:32] <alex_joni> because it calls it before the custom_g_codes get executed
[17:26:49] <jmkasunich> custom?
[17:26:53] <jmkasunich> you mean startup?
[17:27:00] <alex_joni> sorry.. yes
[17:27:17] <jmkasunich> well, somebody else is calling interp::init
[17:27:22] <jmkasunich> I'll try to figure out what it is
[17:27:30] <alex_joni> * alex_joni tries to find out who
[17:28:26] <jmkasunich> gcodemodule.cc?
[17:29:04] <jepler> it will call Interp::init but not via emcTaskPlanInit
[17:29:10] <jmkasunich> right
[17:29:13] <jepler> it has its own copy of the interpreter, different process and all
[17:29:27] <jepler> run with tkemc
[17:29:28] <alex_joni> ok, so probably a red herring
[17:29:29] <jmkasunich> but that copy can still send canonicals
[17:29:38] <alex_joni> jmkasunich: try with tkemc
[17:29:44] <jmkasunich> which affect the global machine state
[17:29:46] <jmkasunich> alex_joni: I will
[17:29:53] <jepler> no, they don't affect anything outside of axis
[17:30:00] <jmkasunich> I fully expect it will only get called once with tkemc
[17:30:12] <jmkasunich> jepler: why do you say that?
[17:30:13] <alex_joni> jmkasunich: jeff provided empty canon calls
[17:30:14] <jepler> axis provides an implementation of all the canonical functions, they just change state with axis, not within milltask
[17:30:40] <alex_joni> void ENABLE_FEED_OVERRIDE() {}
[17:30:43] <alex_joni> example ;)
[17:31:54] <jmkasunich> ok, I was mixing that up with something else I saw
[17:32:14] <jmkasunich> looking at the commands actually received by RT, I do get _two_ sets of interp_init
[17:32:19] <jmkasunich> but the user space shows three
[17:32:25] <jmkasunich> the third one is the axis one
[17:33:13] <jmkasunich> ok, this is strange
[17:33:27] <jmkasunich> sim/tkemc calls taskplaninit once
[17:33:53] <jmkasunich> sim/axis calls taskplaninit twice, and interpinit three times (last time with dummy canonicals)
[17:34:11] <jmkasunich> sim/tkemc calls taskplaninit once, and interpinit once (to be precise)
[17:34:25] <alex_joni> interpinit is called from taskplaninit
[17:34:28] <alex_joni> so that's ok
[17:34:28] <jmkasunich> right
[17:34:46] <alex_joni> and _after_ that it executes the startup g codes
[17:34:52] <jmkasunich> but why does sim/axis call taskplaninit twice
[17:35:12] <SWPadnos> preview?
[17:35:37] <jmkasunich> maybe
[17:35:58] <jmkasunich> there are about 1000 commands issued between the two calls
[17:36:06] <SWPadnos> what nc file?
[17:36:15] <jmkasunich> the axis splash screen
[17:36:20] <jmkasunich> heh. thats it
[17:36:31] <alex_joni> I can only see the call to taskplaninit in def open_file_guts(f, filtered = False):
[17:36:41] <jmkasunich> axis automatically loads the splash screen, so that is the second call
[17:36:45] <alex_joni> jmkasunich: it auto loads the file
[17:36:47] <alex_joni> right
[17:37:17] <SWPadnos> curiosity: is the splash screen configurable (without changing axis.py)?
[17:37:22] <jmkasunich> hmm - even when I do load a file, tkemc doesn't call task_plan_init again
[17:37:36] <jepler> SWPadnos: you can replace the .ngc file
[17:37:42] <alex_joni> jmkasunich: odd
[17:37:42] <SWPadnos> heh - ok :)
[17:37:54] <jepler> emc2/share/axis/images/axis.ngc
[17:38:11] <jmkasunich> file->reset calls it, but file load doesn't
[17:38:17] <jmkasunich> is there a reload for tkemc?
[17:38:18] <alex_joni> right...
[17:38:23] <alex_joni> no reload
[17:38:29] <SWPadnos> run should do it as well, I'd think
[17:38:33] <alex_joni> only reload tooltable file, that calls it again
[17:39:33] <alex_joni> sorry.. make that var file
[17:39:39] <jepler> I dunno, that call to reset_interpreter() was there in rev 1.1 in axis-historical
[17:39:59] <alex_joni> jepler: it sounds sensitive
[17:40:09] <alex_joni> but it is different than what tkemc & mini do
[17:40:26] <jmkasunich> well, the double init is interesting, but regardless of that, we don't want to be setting the default values of the enables in interp::init
[17:41:20] <alex_joni> same for xemc (like tkemc and mini)
[17:41:40] <jmkasunich> better to have DEFAULT_STARTUP_CODES and INIFILE_STARTUP_CODES, and execute them in that order
[17:42:19] <alex_joni> jmkasunich: you need to set some initial values to those variables
[17:42:29] <jmkasunich> which variables?
[17:42:53] <jmkasunich> the _CODES ones?
[17:42:54] <alex_joni> _setup.*
[17:43:32] <jmkasunich> that sucks
[17:44:01] <jmkasunich> because if all you do is set a value, the interp knows about it, but the rest of the machine doesn't
[17:44:21] <alex_joni> yeah, but the machine should know on the next synch or whatever
[17:44:40] <jmkasunich> what does synch do?
[17:44:52] <alex_joni> lets everyone know about the internal state
[17:44:52] <jmkasunich> does it execute canonicals based on the values of _setup.whatever?
[17:45:05] <jmkasunich> "everyone" doesnt' include the motion controller I bet
[17:45:28] <alex_joni> err no
[17:45:35] <jmkasunich> I rest my case
[17:45:44] <alex_joni> it sets some other _setup.* by GET_foo calls
[17:46:40] <jmkasunich> GET calls don't send anything to the motion controller either I don't think
[17:47:23] <alex_joni> right
[17:47:33] <alex_joni> so it's only outside->interp in synch()
[17:48:35] <jmkasunich> ok, maybe we're doing this backwards
[17:48:42] <jmkasunich> Returned Value: int (INTERP_OK)
[17:48:42] <jmkasunich> Side Effects:
[17:48:42] <jmkasunich> sets the value of many attribute of _setup by calling various
[17:48:42] <jmkasunich> GET_EXTERNAL_xxx functions.
[17:48:43] <jmkasunich> Called By:
[17:48:45] <jmkasunich> Interp::init
[17:48:46] <jmkasunich> external programs
[17:48:48] <jmkasunich> This function gets the _setup world model in synch with the rest of
[17:48:50] <jmkasunich> the controller.
[17:48:53] <jmkasunich> */
[17:48:57] <jmkasunich> interp_synch is intended to set the interp to match the rest of the machine
[17:49:03] <alex_joni> right
[17:49:11] <jmkasunich> we're trying to make the rest of the machine match the interp
[17:49:14] <alex_joni> so maybe set the CANON defaults
[17:49:30] <jmkasunich> maybe we need to add GET_foo functions for the enables, and let interp_synch call them
[17:49:34] <alex_joni> then have the synch update the _setup.* to match GET_FO_ENABLE & co
[17:49:41] <alex_joni> right
[17:49:44] <jmkasunich> then the defaults can be set in the motion controller, and will travel up to the interp
[17:50:06] <jmkasunich> and after that, the startup codes can travel down to the motion controller as normal
[17:50:59] <alex_joni> in the motion controller?
[17:51:14] <jmkasunich> emcmotStatus->enables_new
[17:51:29] <jmkasunich> the GET_ functions should read that from shared memory
[17:51:38] <alex_joni> right
[17:51:42] <alex_joni> most do that
[17:51:50] <jmkasunich> these should too
[17:52:02] <jmkasunich> who's gonna do this (don't want us both editing on the same file)
[17:52:31] <jmkasunich> we have to delete the lines from Interp::init, call the GET functs from Interp::synch, and write the GET functions
[17:52:37] <alex_joni> * alex_joni has no machine he can work on at the moment
[17:52:43] <jmkasunich> ok, I'll do it
[17:52:43] <alex_joni> :(
[17:52:50] <alex_joni> I can double check though
[17:52:57] <jmkasunich> and answer questions
[17:53:22] <jmkasunich> first, where are the GET_ functions declared (.h file)?
[17:53:28] <alex_joni> canon.hh
[17:53:32] <alex_joni> in nml_intf
[17:53:56] <alex_joni> src/nml_intf/canon.hh
[17:54:12] <jmkasunich> oh, the GET_ are canons?
[17:54:14] <jmkasunich> duh
[17:54:21] <alex_joni> yes :)
[17:55:09] <jmkasunich> the new ones should be named GET_EXTERNAL_FO_ENABLE, etc?
[17:56:11] <alex_joni> right
[17:56:13] <jmkasunich> or just GET_FEED_OVERRIDE_ENABLE() ?
[17:56:25] <jmkasunich> to match ENABLE_FEED_OVERRIDE() and DISABLE_FEED_OVERRIDE()
[17:57:21] <alex_joni> seems GET_EXTERNAL_ would match the rest
[17:59:25] <jmkasunich> why can't I find a function in canon.cc that sets the feed override value?
[17:59:47] <alex_joni> it doesn't go through canon
[17:59:55] <alex_joni> because it's not coming from the interp
[17:59:55] <jmkasunich> duh
[18:00:02] <alex_joni> it's coming from GUI to motion ;)
[18:00:07] <jmkasunich> right
[18:00:20] <alex_joni> even if closely related.. way different thing :D
[18:03:08] <jmkasunich> should I use FO, SO, AF, and FH, or spell them out like in the ENABLE_FEED_OVERRIDE(), etc
[18:03:20] <alex_joni> I'd spell them out
[18:03:32] <alex_joni> GET_EXTERNAL_FEED_OVERRIDE() mabye?
[18:03:54] <jmkasunich> that might be interpreted as "get the value", not "get the enable"
[18:04:13] <jmkasunich> letters are cheap, I'll spell them out
[18:04:19] <alex_joni> ok.. perfect
[18:06:30] <jmkasunich> does _setup.speed_override mean feed or spindle?
[18:07:12] <alex_joni> I think spindle
[18:07:18] <alex_joni> but I'm checking now
[18:07:31] <jmkasunich> I hate ambiguity
[18:07:45] <jmkasunich> if that doesn't appear in two many places I'm tempted to change the name
[18:07:48] <alex_joni> if ( _setup.speed_override ) ENABLE_SPEED_OVERRIDE(); else DISABLE_SPEED_OVERRIDE();
[18:07:51] <alex_joni> if ( _setup.feed_override ) ENABLE_FEED_OVERRIDE(); else DISABLE_FEED_OVERRIDE();
[18:07:54] <alex_joni> if ( _setup.adaptive_feed ) ENABLE_ADAPTIVE_FEED(); else DISABLE_ADAPTIVE_FEED();
[18:07:57] <alex_joni> if ( _setup.feed_hold ) ENABLE_FEED_HOLD(); else DISABLE_FEED_HOLD();
[18:08:01] <alex_joni> you wrote those :)
[18:08:04] <alex_joni> lines I mean
[18:08:18] <jmkasunich> duh
[18:08:32] <jmkasunich> but I didn't pick "SPEED" instead of "SPINDLE"
[18:08:38] <alex_joni> right :)
[18:10:38] <jmkasunich> how do you feel about changing SPEED to SPINDLE?
[18:11:16] <alex_joni> I don't like having either ,) but I guess SPINDLE is better
[18:11:22] <jmkasunich> ?
[18:11:38] <alex_joni> * alex_joni is thinking about machines with no SPINDLE
[18:11:51] <jmkasunich> if they have no spindle, then the override is meaningless
[18:12:04] <alex_joni> right
[18:12:52] <jmkasunich> bah, there are others that are just as messed up
[18:12:57] <jmkasunich> GET_EXTERNAL_SPEED
[18:13:02] <jmkasunich> GET_EXTERNAL_SPINDLE
[18:13:18] <jmkasunich> the former gets the spindle speed, the latter gets its turning/not-turning state
[18:13:30] <jmkasunich> I'm not gonna bother messing with it
[18:13:44] <alex_joni> ok :)
[18:14:18] <jmkasunich> ok, changes to interp::init and interp::sync done
[18:14:26] <jmkasunich> and the canons are declared
[18:14:58] <jmkasunich> it should compile, but not link because the canons aren't defined yet
[18:14:58] <alex_joni> emccanon.cc next
[18:15:07] <jmkasunich> checking the compile first
[18:15:56] <jmkasunich> that found a typo ;-)
[18:16:32] <jmkasunich> ok, emccanon.cc
[18:18:33] <jmkasunich> hmm, the emcmotStatus->enables aren't in emcStatus
[18:19:41] <jmkasunich> ok, functions are defined, but they don't do anything
[18:20:00] <jmkasunich> now I think its usrmotintf, to copy data from emcmotStatus to emcStatus
[18:20:14] <alex_joni> emcStatus->motion.
[18:20:49] <jmkasunich> where is the emcStatus struct defined?
[18:21:14] <alex_joni> emc.hh I guess (without looking)
[18:21:25] <alex_joni> emcStatus contains emcmotStatus completely
[18:21:35] <jmkasunich> oh
[18:21:54] <jmkasunich> but emcmotStatus is defined in motion.h
[18:22:03] <jmkasunich> I didn't think every single file included motion.h
[18:22:08] <alex_joni> no
[18:22:13] <alex_joni> hang on.. looking now
[18:23:15] <alex_joni> usrmotReadEmcmotStatus(&emcmotStatus)
[18:23:22] <alex_joni> that's the update from RT to userspace
[18:23:39] <jmkasunich> that makes a copy of the emcmot structure in userspace
[18:23:42] <alex_joni> look at taskintf.cc
[18:24:00] <alex_joni> next you need to copy the 4 flags to the stat-> struct
[18:24:34] <jmkasunich> in emcMotionUpdate?
[18:24:45] <alex_joni> right
[18:25:21] <jmkasunich> so, gotta find the decaration of EMC_MOTION_STAT and add some fields
[18:25:40] <alex_joni> emc.hh
[18:25:47] <alex_joni> but I'd add to EMC_TRAJ_STAT
[18:25:59] <alex_joni> then you can address them as stat->traj.foo
[18:26:15] <alex_joni> class EMC_TRAJ_STAT:public EMC_TRAJ_STAT_MSG {
[18:26:22] <jmkasunich> slow done
[18:26:30] <jmkasunich> down
[18:26:34] <alex_joni> ok..
[18:26:37] <alex_joni> * alex_joni slows down
[18:26:39] <alex_joni> in emc.hh
[18:26:46] <jmkasunich> this is so fscking messed up
[18:26:48] <alex_joni> there is EMC_STAT which contains EMC_MOTION_STAT
[18:27:02] <jmkasunich> let me try to explain it, then tell me where I'm wrong
[18:27:14] <alex_joni> EMC_MOTION_STAT contains another subset of stats: EMC_TRAJ_STAT, EMC_SPINDLE_STAT, etc..
[18:27:19] <jmkasunich> at the bottom is my beloved emcmotStatus structure, as defined in motion.h
[18:27:22] <alex_joni> * alex_joni shuts up and listens
[18:27:23] <jmkasunich> that lives in shared memory
[18:28:16] <jmkasunich> then there is motion/usrmotintf.cc: usrmotReadEmcmotStatus, which makes a copy of the same structure in user space memory
[18:29:02] <alex_joni> agreed so far
[18:29:12] <jmkasunich> then there is task/taskintf.cc: emcMotionUpdate, whick copies the data from the emcmot stuct into an EMC_MOTION_STAT struct
[18:29:33] <jmkasunich> not EMC_TRAJ_STAT!
[18:29:48] <alex_joni> check again ;)
[18:30:00] <jmkasunich> int emcMotionUpdate(EMC_MOTION_STAT * stat)
[18:30:08] <jmkasunich> its passed a ptr to a MOTION_STAT struct
[18:30:14] <alex_joni> stat->spindle.enabled = emcmotStatus.spindle.speed != 0;
[18:30:23] <alex_joni> that copies into the EMC_SPINDLE_STAT
[18:30:42] <alex_joni> because stat->spindle refers to a EMC_SPINDLE_STAT defined inside EMC_MOTION_STAT
[18:30:57] <alex_joni> stat->traj is a EMC_TRAJ_STAT defined inside EMC_MOTION_STAT
[18:31:07] <alex_joni> EMC_MOTION_STAT is like a container
[18:31:10] <jmkasunich> ok
[18:31:15] <jmkasunich> nested structs I understand
[18:31:29] <jmkasunich> but the fact that they are nested sure isn't clear
[18:31:39] <alex_joni> open emc.hh
[18:31:43] <alex_joni> and search for class EMC_MOTION_STAT:public EMC_MOTION_STAT_MSG {
[18:31:43] <alex_joni> public:
[18:31:54] <jmkasunich> yeah
[18:31:57] <jmkasunich> found that
[18:31:59] <alex_joni> // aggregate of motion-related status classes
[18:31:59] <alex_joni> EMC_TRAJ_STAT traj;
[18:31:59] <alex_joni> EMC_AXIS_STAT axis[EMC_AXIS_MAX];
[18:31:59] <alex_joni> EMC_SPINDLE_STAT spindle;
[18:31:59] <alex_joni> int debug;// copy of EMC_DEBUG global
[18:32:01] <alex_joni>
[18:32:04] <alex_joni> };
[18:32:09] <jmkasunich> gawd I hate C++ with a burning passion
[18:32:24] <alex_joni> it's only a bit different :)
[18:32:38] <alex_joni> so either add those 4 flags after the int debug;
[18:32:44] <alex_joni> or add them to EMC_TRAJ_STAT
[18:33:16] <jmkasunich> are there currently _no_ fields that are generic motion?
[18:33:22] <jmkasunich> everything is either traj, axis, or spindle?
[18:33:25] <alex_joni> none that I know
[18:33:37] <alex_joni> debug is the only one
[18:33:48] <jmkasunich> ok, I'll add them to traj
[18:35:36] <alex_joni> hmm.. seems I'll be getting my PC back any minute now :)
[18:37:23] <jmkasunich> ok, fields added to the traj struct
[18:37:52] <jmkasunich> I suppose that means I want to do the copying in emcTrajUpdate(), not emcMotionUpdate()
[18:38:10] <alex_joni> does emcTrajUpdate() copy them from SHM ?
[18:38:22] <jmkasunich> all these update functions copy data from a user space copy of the shmem struct
[18:38:43] <jmkasunich> usrmotReadEmcmotStatus() copies from shmem to the user space copy
[18:38:50] <alex_joni> these globals are set in emcMotionUpdate(), then referenced in
[18:38:51] <alex_joni> emcAxisUpdate(), emcTrajUpdate() to save calls to usrmotReadEmcmotStatus
[18:40:05] <jmkasunich> lame lame lame
[18:40:18] <jmkasunich> there is no reason any of those should be globals
[18:40:24] <jmkasunich> but fsck it, I'll just go along
[18:40:34] <jmkasunich> grumble
[18:41:09] <jmkasunich> all of emcMotion/Traj/AxisUpdate are basically designed to copy from struct A at location A to struct B (differnet layout) at location B
[18:41:20] <jmkasunich> so why don't they accept TWO args, A and B
[18:41:33] <jmkasunich> nooooo.... they use a global for A, and accept only one arg, B
[18:41:40] <jmkasunich> that makes no fscking sense at all
[18:42:33] <dlabriola> Good morning (or afternoon) - I'm new to the group - had a couple of ideas to toss in - was thinking about adding drivers to send/receive info via CAN - partial implementation of CANopen. This would allow communications with CAN based miton controllers as well as other extended IO. I was told this was the right place to discuss it. -- Don Labriola
[18:42:44] <jmkasunich> hi Don
[18:42:50] <dlabriola> morning
[18:43:18] <jmkasunich> go ahead... I'm flailing around a bit at the moment, but I hope to finish soon and be able to give you my full attention
[18:44:30] <dlabriola> I'll let those onboard know I do closed loop control of steppers for QuickSilver Controls - just got done (about 6 months back) with a full CANopen implementation, but have been following EMC for awhile now.
[18:45:16] <dlabriola> We operate the motors as full sin commutated high pole count AC synchronous motors, getting up to about 4000
[18:45:38] <dlabriola> RPM out of them, 4k, 8k, 16k count/rev encoders
[18:46:13] <dlabriola> Have been playing with these since about 1984 or so, so a bit of background on them.
[18:46:16] <jmkasunich> seriously high output freqency
[18:46:35] <jmkasunich> must be lots of overdrive voltage to get good sine waves at that freq
[18:47:05] <dlabriola> that is part of the fun. I actually do all of the current loop by estimation based on motor parameters and commanded voltages, directly controlling the PWM
[18:47:21] <jmkasunich> so current is basically open loop?
[18:48:03] <dlabriola> We use fairly low voltage motors - 1-2v with a 48v supply. The current loop has feedback indirectly via motor position.
[18:48:25] <dlabriola> Some of the motors are as high as 50:1 L/R synthetic
[18:48:59] <jmkasunich> I'm not used to that terminology
[18:49:02] <dlabriola> the control loops run fairly tight - typically +/-1 count dither when tuned up.
[18:49:08] <jmkasunich> I work on vector controlled induction motors normally
[18:50:03] <dlabriola> real L/R drivers use a series resistor to limit the current - say a 5:1 L/R would use a resistor 4 times as much as the motor resistance to raise the current slew rate to speed up the motor.
[18:50:35] <dlabriola> Synthetic L/R is a chopper drive, the ratio is then the ratio of the drive voltage to the motor voltage at full current
[18:50:49] <jmkasunich> ok
[18:50:52] <dlabriola> Gives the same motor currnet rise time as the physical
[18:50:59] <dlabriola> L/R, but with out the heating.
[18:51:05] <jmkasunich> I'm very familar with the concept, just not that name for it
[18:51:34] <dlabriola> Taking the name from PCIM symposium papers - can't claim I came up with it.
[18:51:57] <jmkasunich> Mariss at gecko calls the voltage ratio overdrive, if I recall correctly
[18:52:11] <dlabriola> same idea
[18:52:22] <jmkasunich> in industrial motors, the winding resistance is always so low that "overdrive" is the norm
[18:52:39] <jmkasunich> IR drop is usually only a percent or two of the applied voltage
[18:52:53] <jmkasunich> so thats like 50:1 or 100:1
[18:53:03] <dlabriola> we run up to about 50:1 - 34 frame motor runs about .1 ohm, 16 A from 48v supply, so in the same range.
[18:53:28] <jmkasunich> 16A in a frame 34
[18:53:29] <jmkasunich> wow
[18:53:33] <dlabriola> the fun part goes back to the frequency needed to commutate
[18:53:43] <jmkasunich> I guess you use a few turns of very fat wire, low inductance
[18:53:48] <dlabriola> yea - the 4 stack gives up to about 3200in-oz
[18:54:15] <dlabriola> we've run down to about 250uH or less.
[18:54:43] <dlabriola> We run with a 25khZ chop, so the fun part is getting the current to control well through zero.
[18:55:24] <dlabriola> we use a gated anti-phase to get the best of recirculating drive mode and antiphase drive mode.
[18:55:50] <dlabriola> anti phase makes zero voltage average by swinging between +V and -V with a 50
[18:55:54] <dlabriola> % duty cycle
[18:56:11] <dlabriola> (fat fingers keep hitting the CR too soon)
[18:56:33] <jmkasunich> I think I follow
[18:56:42] <dlabriola> anti phased controlls well through zero, but has a very high ripple current - read that as heating
[18:56:52] <jmkasunich> I'm used to three phase bridges more than dual H bridges, a little different
[18:57:04] <jmkasunich> yes, 50% is the worst for ripple
[18:57:24] <alex_joni> jmkasunich: have a functional devel box now.. need a hand?
[18:57:36] <dlabriola> gating about the center to cause the reverse voltage phase to be the minimum time for the driver
[18:57:47] <jmkasunich> alex_joni: I'm about to make a diff to pastebin for your review
[18:58:21] <alex_joni> ok
[18:58:22] <jmkasunich> it builds, not tested yet
[18:58:36] <dlabriola> the reverse phase may be omitted once the desired voltage is above the minimum dutycycle of the driver.
[18:59:40] <dlabriola> yes, some 2 phase full bridge stuff is easier to do. not too different from use ot the 8 state vector commutation in the TI chips for 3 phase.
[19:00:03] <jmkasunich> alex_joni: http://pastebin.ca/240377
[19:01:04] <Lerneaen_Hydra_> Lerneaen_Hydra_ is now known as Lerneaen_Hydra
[19:01:13] <dlabriola> we also shape the current waveform, only requesting the rate of change of current to get us to the wanted current by the end of the sample interval so we minimize torque disturbances (Jerk = dA/dt ~ Di/dt
[19:01:33] <jmkasunich> dlabriola: are you going to be around later? my wife just got home, its lunchtime
[19:01:52] <alex_joni> jmkasunich: reading now
[19:01:56] <dlabriola> I should be here for a bit if you would like me to put this on hold for a bit
[19:02:05] <dlabriola> do understand
[19:02:19] <jmkasunich> I'll be back in less than an hour probably
[19:02:35] <jmkasunich> anyway, the topic should really be CAN
[19:02:40] <jmkasunich> I know next to nothing about it
[19:02:45] <jmkasunich> back in a bit
[19:02:46] <alex_joni> jmkasunich: minor typo on line 49
[19:02:55] <alex_joni> , and feed hole
[19:03:04] <dlabriola> sounds good - I'll be back into CAN when you return!
[19:08:46] <alex_joni> jmkasunich: remove lines: 87, 133, 192, 200
[19:19:20] <jmkasunich> * jmkasunich is back
[19:19:24] <jmkasunich> (short lunch)
[19:19:34] <jmkasunich> alex_joni: fixes made
[19:19:40] <alex_joni> rest looks good
[19:20:51] <dlabriola> all nodes (in the hardware) monitor the message as well as the various crc field and other rules (bit stuffing to prevent too long of strings of sucessive 1's and 0's) and if any have a problem reading, an error is asserted, and the message is resent.
[19:20:59] <jmkasunich> ok, I'll commit it then
[19:21:11] <jmkasunich> and work on the documentation later for M50-53
[19:21:23] <alex_joni> * alex_joni waits on the commit
[19:21:33] <jmkasunich> oops, we forgot one thing
[19:21:41] <jmkasunich> we're getting the state from the motion controller now
[19:21:43] <alex_joni> jmkasunich: any reason to name this: enables_new ?
[19:21:51] <alex_joni> yeah, you need to init it better :P
[19:21:51] <jmkasunich> but we don't set an initial state down there
[19:21:58] <jmkasunich> (well, we do, but its all zeros)
[19:22:29] <jmkasunich> I thought about enables_current (vs. queued)
[19:22:38] <jmkasunich> I'm open to suggestions
[19:23:04] <alex_joni> enables is not very descriptive to me :/
[19:23:13] <alex_joni> enable_flags_current ?
[19:23:32] <jmkasunich> I guess that works
[19:23:52] <alex_joni> dlabriola: sorry about the constant interruption.. but we were in the middle of something
[19:23:55] <jmkasunich> dlabriola: sorry, a few loose ends to tie up
[19:23:58] <alex_joni> dlabriola: should be over quite soon
[19:24:03] <alex_joni> jmkasunich: heh
[19:24:36] <dlabriola> i'll hang back till you've had a chance to finish with the code ...
[19:26:04] <jmkasunich> alex_joni: I'm gonna leave the names as is right now
[19:26:12] <jmkasunich> I'd rather talk about CAN
[19:26:45] <dlabriola> anyway, it was developed to be hard realtime using arbitration rather than collision (as ethernet does) as is needed for communicatoins in high end German cars for their internal control by wire.
[19:27:51] <alex_joni> jmkasunich: perfect
[19:28:04] <dlabriola> just giving a quick background on how it does what it does. At 1Mbit/sec, longest frame is ~132 microseconds to pipe 64 bytes of data plus message ID and over head. shorter frames (less data) take less time.
[19:28:36] <jmkasunich> alex_joni: you told me to remove pars?
[19:28:50] <alex_joni> the debug printf()'s
[19:29:09] <jmkasunich> oops, my fault
[19:29:15] <jmkasunich> I read line 87 as 85
[19:29:34] <alex_joni> heh.. way no ;)
[19:29:45] <steves_logging> steves_logging is now known as steve_stallings
[19:30:40] <alex_joni> hi steve
[19:30:58] <dlabriola> CANopen sets up a standard to hand out the ID's for various functions: routine communications between various nodes, and high speed data - PDO's producer data objects, which carry their information via their ID's. PDO's are maped one source to zero or more sinks. Also has a special ID (usually 80) reserved to sync data.
[19:31:23] <jmkasunich> arrg - now sim-axis won't start
[19:31:49] <jepler> jmkasunich: runtime linker error?
[19:32:06] <alex_joni> the GET_* need to get added to gcodemodule.cc
[19:32:07] <jmkasunich> Machine configuration file is 'axis.ini'
[19:32:08] <jmkasunich> Starting EMC2...
[19:32:08] <jmkasunich> Traceback (most recent call last):
[19:32:08] <jmkasunich> File "/home/jmkasunich/emcdev/emc2head/bin/axis", line 57, in ?
[19:32:08] <jmkasunich> import gcode
[19:32:08] <jmkasunich> ImportError: /home/jmkasunich/emcdev/emc2head/lib/librs274.so: undefined symbol: _Z33GET_EXTERNAL_ADAPTIVE_FEED_ENABLEv
[19:32:11] <jmkasunich> Shutting down and cleaning up EMC2...
[19:32:21] <alex_joni> jmkasunich: commit it, I'll fix it
[19:32:29] <steve_stallings> hi alex, I perked up when dlabriola joined, interested in distributed motion control
[19:32:39] <jmkasunich> is G49 enabled or disabled?
[19:32:45] <dlabriola> I was looking at using something line the usb to CAN adapter shown on CANUSB.com as the bridge between the pc (desk or laptop) to CAN. - fairly cheap, and buffers up to 32 messages, giving the pc realtime a chance to keep up and not loose messages.
[19:32:57] <alex_joni> G48 enabled, G49 disabled
[19:33:07] <jmkasunich> ok, then something else is wrong
[19:33:16] <jmkasunich> because tkemc came up displaying G49
[19:33:24] <jmkasunich> but I set the flags to enable FO and SO
[19:33:29] <alex_joni> jmkasunich: commit it, I'll check too
[19:33:36] <jmkasunich> ok, here goes
[19:33:44] <alex_joni> * alex_joni needs something to work on ;)
[19:34:30] <dlabriola> driver would just map position/velocity data from/to the CAN bus. There are several easy CAN free software bases for microchip and other 8051 size processors - would make nice way to extend IO cheaply or to add a pendant.
[19:35:01] <alex_joni> how much for a minimal CAN interface?
[19:35:18] <jmkasunich> committed...
[19:35:21] <dlabriola> CAN open also provides heartbeat functions: background message giving status on a regular time base. generally at a low priority to make sure all higher messages have made it
[19:35:33] <alex_joni> jmkasunich: got them
[19:36:05] <dlabriola> the CANUSB one is about $110 or so (last time I got one) - saw another one similar about $45 about a week ago - one on order, waiting to play with it.
[19:36:51] <dlabriola> the micro chip parts for the remote pendants, etc, about a $2 driver, $4 processor, plus power, crystal, etc.
[19:36:58] <jmkasunich> so are can devices sort of master-slave? (master sends out command, slave replies with feedback)
[19:37:10] <dlabriola> they are really peer to peer, only
[19:37:52] <jmkasunich> so in a system with one PC and four motor drives, you could have messages going from drive to drive?
[19:37:58] <dlabriola> a master may assert the ability to change the operational state of all devices "NMT state" to help startup synchronization, or they may each configure and start up themselves.
[19:38:26] <dlabriola> yes - drive to drive. have used it to lock two gantry axis to each other with stall detection on either holding back the other.
[19:38:53] <jmkasunich> that suddenly makes things much more complicated
[19:39:01] <dlabriola> in that case, one is slaved to another, with the master receiving the trajectory requests, and the other following.
[19:39:20] <dlabriola> in this case, it looks like a single drive, the slave goes along for the ride.
[19:39:51] <alex_joni> jmkasunich: AXIS starts up, and comes up with M48 by default
[19:40:02] <steve_stallings> so the current EMC with a 6 axis trajectory planner would talk to one axis as master and it would manage other 5?
[19:40:07] <dlabriola> The system I'd like would use the PC as the master for normal operations, but the motor drives would relay back their status and IO so you can have all the switches you want!
[19:40:36] <dlabriola> that is, you can operate it as master slave, but CAN is not the limiting factor.
[19:40:38] <jmkasunich> so we could pretend its master/slave
[19:40:43] <dlabriola> yes
[19:41:08] <dlabriola> nice thing is that you can push things like error status rather than having to poll for them - clears up bus time
[19:41:24] <jmkasunich> ah
[19:41:44] <dlabriola> I was looking at it because it provides a nice voltage isolated interface for higher level data
[19:41:59] <jmkasunich> so your driver in the PC might have 10 HAL bit pins for various error states, but the status of those 10 bits is not routinely traveling over the wire
[19:42:52] <dlabriola> The Hal inputs for error state might be set up as "send every bit change or at least every 200 ms)
[19:43:00] <alex_joni> jmkasunich: think I found the M48/49 issue
[19:43:17] <jmkasunich> dlabriola: but that is all internal to the driver, right?
[19:43:26] <jmkasunich> alex_joni: I wonder my my sim-axis won't work?
[19:43:31] <dlabriola> so if the external end of travel bit got set, it would be sent immediatly. Yes, the send mode is internal to the driver
[19:43:41] <alex_joni> jmkasunich: didn't cvs up & recompile ?
[19:44:01] <jmkasunich> does sent immediately mean messages are asynchronous?
[19:44:30] <jmkasunich> EMC tends to operate with a heartbeat - commands go out, and feedbacks are read, on a periodic basis
[19:44:46] <jmkasunich> the driver code would most likely be called on a periodic basis too
[19:44:47] <dlabriola> each may be configured to be synchronous or asynchronous. If set up to go out immediatly, would be async.
[19:44:56] <jmkasunich> so I'm not sure how to handle the asynch messages
[19:45:09] <dlabriola> the driver collects up the messages received every x milliseconds and updates the bits
[19:45:21] <jmkasunich> ok
[19:45:30] <jmkasunich> for EMC is usually every 1mS
[19:45:48] <jmkasunich> that is configurable, but 99% of users run the servo thread at 1mS
[19:46:11] <dlabriola> that would be up to about 20 messages - shortest to 7 messages longest.
[19:46:45] <steve_stallings> a typical EMC implementation is configured for the trajectory planner to issue new cubic splines for all 6 axes every 10 mS, then linear interpolate every 1 mS for PID
[19:46:47] <dlabriola> some regular messages going out would program a rate and cycle interleave to keep bus from being saturated.
[19:47:12] <jmkasunich> I would think one message per axis, in and out, per servo period
[19:47:44] <dlabriola> May choose to let the axis interpolate the 10 ms or to take the 1ms update. just how you plumb it in HAL
[19:47:49] <jmkasunich> steve_stallings: I _really_ don't want to start sending cubics around, or doing much of anything at that rate
[19:47:50] <alex_joni> jmkasunich: there's another commit that I just put in, but CIA didn't report it
[19:48:15] <jmkasunich> because there are so many other things that happen at the servo rate
[19:48:27] <jmkasunich> jogs don't use cubics for example
[19:48:42] <steve_stallings> ok, if no cubics, then CAN bus has to be fast enough to be in the middle of a PID loop?
[19:48:46] <jmkasunich> probe logic happens at the servo rate
[19:48:52] <jmkasunich> not in the middle
[19:49:04] <jmkasunich> we can easily send position commands every 1mS, and do the PID in the drive
[19:49:22] <dlabriola> the 10ms rate is probably the preferred. the drivers do their own trajectory interpollation and servo loop at 8kHz - 120us internally
[19:49:26] <steve_stallings> these are the sort of answers I was hoping to stimulate
[19:49:26] <jmkasunich> we still need feedback from the drive, but nor for the PID - just for following error detection and such
[19:49:47] <dlabriola> that is the idea
[19:50:50] <dlabriola> send them position and velocity requests every couple of milliseconds, they can do a nice interpollation, taking advantage of velocity feedforward without differentiating making more noise in the control loop.
[19:51:03] <jmkasunich> if 1mS is too fast for the comm link, I'd be much happier slowing down the servo rate than trying to send things at a different level
[19:51:12] <steve_stallings> so if CAN delivers a new position target every 10 mS, then drives try to produce a linear move to that point in the alloted 10 mS, and internal PID smoothes motion
[19:51:22] <dlabriola> you have it
[19:51:38] <jmkasunich> dlabriola: we generate position only, every servo period (which we could set to whatever is appropriate)
[19:51:56] <jmkasunich> if velocity is needed, it would be (newpos-oldpos)/period
[19:51:57] <steve_stallings> OK, but we already have users unhappy with 10 mS planning and asking for 1 mS
[19:52:08] <alex_joni> isn't 10mS a bit slow?
[19:52:13] <alex_joni> steve_stallings: exactly
[19:52:44] <jmkasunich> IMO, the whole interpolator scheme is left over from the days when it took 2mS to plan a trajectory, and 5mS to do kins calcs
[19:53:06] <jmkasunich> if it was up to me I'd be strongly tempted to dump the "traj rate" completely
[19:53:12] <jmkasunich> and run all of the motion controller at the servo rate
[19:53:18] <dlabriola> the hal manual showed a velocity feedforward used in the control loop. 6 axis would push the bus with position response every 1 ms, would be good at 2 ms. 10 ms was more if the PC could not keep up.
[19:53:23] <alex_joni> jmkasunich: doesn't it run that way?
[19:53:56] <jmkasunich> alex_joni: the tp is only called when the interpolator runs dry
[19:54:10] <alex_joni> dlabriola: you can set anything basicly 1,2,3,4..mS
[19:54:14] <jmkasunich> I'm not entirely sure how that works, cradek is more familiar with that part
[19:54:23] <jmkasunich> 2.365mS ;-)
[19:54:44] <alex_joni> jmkasunich: tried the latest state of emc2?
[19:54:54] <jmkasunich> axis starts now
[19:54:58] <dlabriola> the controllers look every 120uS, would prefer to have some fixed basis so the interpolator does not need to run behind.
[19:54:58] <jmkasunich> didn't try much more than that
[19:55:07] <alex_joni> good.. start tkemc and check for M48
[19:55:22] <jmkasunich> that 120uS heartbeat - how is that synced between multiple axes?
[19:55:41] <jmkasunich> alex_joni: better ;-)
[19:55:44] <alex_joni> I guess they sync on the new message
[19:56:03] <dlabriola> there is also a "synchronous mode" in which the data is sent, then the "sync" (COB-id 80 if I remember) and all axis use the new data simultaneously
[19:56:12] <alex_joni> jmkasunich: write_m_codes was called before synch()
[19:56:23] <alex_joni> it worked ok in axis, because init is called twice :D
[19:57:04] <alex_joni> dlabriola: so basicly sync on a message (received kinda simultanously)
[19:57:22] <alex_joni> "kinda" because of different wire lengths & all
[19:57:29] <jmkasunich> so sync is kind of like a broadcast message?
[19:57:36] <dlabriola> there is also a time message that can be provided by any axis. it is time in microseconds as a 16 bit number. all of the axis sync their 120us cycle to keep the time - akin to a phase lock loop
[19:58:03] <alex_joni> jmkasunich: these all are multicast messages
[19:58:03] <steve_stallings> is the physical bus multidrop or daisy chain?
[19:58:07] <jmkasunich> could that time message be provided by the PC?
[19:58:10] <dlabriola> the sync message is received by all nodes - broad cast - resolved to the nearest cycle.
[19:58:59] <dlabriola> physical bus is is multidrop - 2 twisted pair - wire or with the can drivers
[19:59:24] <jmkasunich> could we do this:
[19:59:25] <dlabriola> any may send it, just need to pick, so the pc would be the logical choice
[19:59:43] <jmkasunich> PC sends one command message per axis, then a time message, then a sync message
[19:59:53] <jmkasunich> each drive then sends a feedback message to the PC
[20:00:20] <jmkasunich> (how do we keep the drive->PC messages from colliding)
[20:00:23] <dlabriola> the time message is typically a slower message - every 200 to 500 ms or so is enough to keep everything locked to a few microseconds or less.
[20:00:35] <alex_joni> jmkasunich: different twisted pair
[20:00:36] <steve_stallings> another issue, if USB<>CAN interface is used there is another imposition of 1 mS granularity and overhead delays
[20:00:46] <jmkasunich> steve_stallings: I know
[20:00:52] <jmkasunich> USB is a nightmare
[20:00:57] <jmkasunich> for RT that is
[20:00:59] <dlabriola> all on one bus - the message id is used to arbitrate the messages.
[20:01:06] <jmkasunich> because there are so many USB chipsets
[20:01:14] <jmkasunich> and the linux USB drivers aren't hard realtime
[20:01:25] <jmkasunich> a PCI based CAN card would be far simpler
[20:01:33] <jmkasunich> (but not so attactive to people with laptops)
[20:01:35] <alex_joni> I don't think there are any hard realtime USB drivers
[20:01:46] <alex_joni> but I guess that depends on the definition of hard RT
[20:01:54] <steve_stallings> comm speed issues are why I keep coming back to using cubic splines, lower data rate needed
[20:02:19] <jmkasunich> steve_stallings: lower thruput, but still hard realtime deadlines
[20:02:29] <steve_stallings> true
[20:02:32] <jmkasunich> and a much messier interface, incompatible with anything else we support
[20:02:48] <steve_stallings> true again, I just a trouble maker at heart
[20:02:54] <dlabriola> I'm looking at putting the cubic spline onto the drives as well. Not too bad in fixed point if the data intervals are fixed.
[20:03:02] <jmkasunich> right now we have a motion controller that outputs positions, and expects feedbacks, at the servo rate
[20:03:16] <jmkasunich> if we can interface CAN to that, it drops right in very cleanly
[20:03:26] <jmkasunich> if not, then we have to change the motion controller internals
[20:03:42] <jmkasunich> dlabriola: there are a _lot_ of issues with the spline approach
[20:04:08] <jmkasunich> for example probing and homing
[20:04:22] <steve_stallings> not to mention local control of homing in drives
[20:04:26] <dlabriola> I was basically planning on starting with updating position and reading back position from the units, but doing the servo (and some interpollation) at the driver.
[20:04:40] <jmkasunich> if the probe hits halfway thru a spline, the existing motion controller abandons the spline, latches the current position, and stops ASAP
[20:05:08] <jmkasunich> I don't want to duplicate all that logic in the drive
[20:05:44] <jmkasunich> drives actually, because probe needs to stop all axes, and capture all positions
[20:06:09] <dlabriola> The drives actually have the ability to run a couple of threads of processing in the background as well as the motion. As to data capture and homing, though of sending homing command to the drive - drive does whole operation if wanted, capturing flag sensors to 120us, and index exactly via hw.
[20:06:35] <jmkasunich> I understand that the drive _can_ do that
[20:07:03] <jmkasunich> but I don't want it to, because that duplicates functionality that we already have for the other 90% of our users who have "dumb" drives
[20:07:28] <dlabriola> Basic proposal for start would be update positions every 2ms or so. Drive interpollates and returns position for error tracking.
[20:07:37] <jmkasunich> that I like ;-)
[20:08:26] <jmkasunich> setting up these drives is non-trivial
[20:08:36] <dlabriola> The smart part of the drive would be used (on a tool changer axis, for example) to intercept an M code, self coordinate the changer axis, and return a "changed" bit.
[20:08:41] <jmkasunich> if they can run a couple of threads of processing, how do you tell them what to run?
[20:08:54] <steve_stallings> yea, like how does EMC support tuning PID in drive
[20:09:12] <jmkasunich> steve_stallings: PIDs are in HAL today
[20:09:26] <jmkasunich> so its trivial to just leave it out for one or more axes
[20:09:31] <steve_stallings> yes, but ini files stuff still needs to get to drives
[20:09:39] <jmkasunich> nope
[20:09:53] <jmkasunich> if you are using a smart drive with some kind of windows (most likely) control software
[20:10:02] <jmkasunich> you'll need to tune it with that software
[20:10:30] <dlabriola> The tuning would be done off line from EMC - the control loop - or may send configuration info. easier to self configure.
[20:10:47] <dlabriola> sorry about the delay - noticed my laptop was just about out of juice.
[20:11:13] <jmkasunich> the "run for the plug before it dies" ;-)
[20:11:56] <dlabriola> The control algorithm we use has some 10 parameters, all and all, including filters and feed forwards, synthized inertial dampers, etc.
[20:12:30] <jmkasunich> you _could_ have the driver export HAL parameters for each of those, and then set them from the ini file
[20:12:40] <jmkasunich> the driver would send a message to the drive whenever any one changes
[20:12:48] <jmkasunich> or you could just use offline tools
[20:12:54] <dlabriola> we routinely run inertial mismatches up to 100 to 1 and I've done up to 3000:1\
[20:13:12] <jmkasunich> do the drive have non-volatile storage for the parameters? or do you need to load them on power up every time?
[20:14:09] <dlabriola> intially, pretend they are ideal steppers and just work. Yes, the drives have 16k nonvolatile, and start running an initialize program and then a user program. This sets up the CAN network locally as well as setting the drive parametes
[20:14:26] <dlabriola> the user program can also save data to non-volatile.
[20:14:54] <jmkasunich> ok
[20:15:21] <dlabriola> The tuning out of the box handles about 80-90% uses, handling open shaft to about 3-5 motor ineria loads without retuning.
[20:15:40] <jmkasunich> ok
[20:15:52] <jmkasunich> I keep forgetting that these are matched motor/drive combos
[20:16:06] <jmkasunich> so much of the tuning is already done, the only variable is inertia and maybe friction
[20:16:24] <dlabriola> The linear slide I mentioned earlier is a stepper linear (cheaper) and the tracking over various moves keeps to about 12um RMS, about 20um peak.
[20:17:17] <jmkasunich> just out of curiosity (cause I'd never be able to affort it) how much does a 800oz-in NEMA34 motor and matching smart drive sell for?
[20:17:50] <dlabriola> The loops are tuned fairly tightly as compared to many other controllers I have seen. The low inductance of the motors and high torque constants of the motors make for tight loops.
[20:20:57] <dlabriola> Have a couple of different choices, but the pair is around $1500 list, deep discount for quantity, if I remeber properly (I'm more in the engineering side than sales), my numbers may be a bit off). cheaper for 23 frame, can get upto 440in-oz there. I can look it up if interested.j
[20:21:52] <jmkasunich> just looking for ballpark
[20:21:59] <jmkasunich> that is about what I expected
[20:22:25] <dlabriola> pretty much so much per watt plus so much for pound of motor!
[20:22:37] <jmkasunich> I have geckpos, $120 per axis, and right now surplus motors that cost between zero and $40 per axis
[20:22:42] <jmkasunich> completely different world
[20:23:23] <jmkasunich> I understand completely about engineers not knowing prices - I have no idea what a customer pays for the stuff I work on
[20:23:51] <jmkasunich> I think its something like $40K for a 1000HP drive, but that could be off by a factor of 2 or more either way
[20:24:22] <dlabriola> Some of playing with EMC would be to take advantage of some of the cheap microchip processors to add things like pendants, etc.
[20:24:47] <dlabriola> Those are nice big drives. What piece of the beast do you play in?
[20:24:57] <jmkasunich> power electronics mostly
[20:25:26] <dlabriola> Don't wear rings or watches around that stuff I take!
[20:25:27] <jmkasunich> I work with the control firmware guys, but I focus on the hardware
[20:25:58] <jmkasunich> well, if you touch 480V with your skin, adding a ring is only going to make it worse
[20:26:04] <jmkasunich> I prefer not to touch it at all
[20:26:17] <dlabriola> Small shop over here, do full range of the embedded side from PCB to electronics, to embedded coded and algorithms...
[20:26:18] <jmkasunich> (so I don't sweat the ring or watch)
[20:26:31] <dlabriola> Yea, that goes beyond hurting!
[20:27:00] <jmkasunich> when I was young and foolish I used to move scope probes around while the drive was powered
[20:27:04] <jmkasunich> not any more
[20:27:10] <jmkasunich> (not after I blew up a probe)
[20:27:45] <dlabriola> I'm fortunate for an electronics background, very dry skin. Can just feel 120, but I avoid it. I've also flashed the ground lead of a scope - mixing the ground reference points on a laser power supply!
[20:28:10] <dlabriola> about a 2kW supply (small compared to what you are playin' with.
[20:28:12] <jmkasunich> some probes have a little grounded ring right next to the tip
[20:28:17] <jmkasunich> for high frequency work
[20:28:27] <dlabriola> I'm familiar
[20:28:28] <jmkasunich> I accidently touched the ring to a busbar while probing
[20:29:01] <dlabriola> how long did the blue "lights" appear in your eyes?
[20:29:01] <jmkasunich> fortunately that was only about a 75HP drive
[20:29:03] <steve_stallings> they make little plastic sheaths for the ring you know..... 8-)
[20:29:06] <jmkasunich> cleared the input fuses
[20:29:18] <jmkasunich> before the arc got too big
[20:29:33] <dlabriola> Glad they made you fuse the beast!
[20:30:27] <jmkasunich> yeah
[20:30:36] <jmkasunich> we take things a lot more seriously now
[20:30:46] <jmkasunich> that incident was about 10 years ago, maybe more
[20:31:41] <jmkasunich> looking at canopen.org
[20:31:45] <dlabriola> Had a friend get bit with a 9J flash lamp cap. Walked away from it - after he came to. Got a free trip to the hospital and a day of watching to make sure he would!
[20:31:58] <jmkasunich> is a remote transmission request (RTR) the way we would get feedback data from the drives?
[20:32:08] <dlabriola> can-cia.org has lots of specs available to public as well.
[20:32:09] <jmkasunich> ouch
[20:32:48] <dlabriola> CAN open tends to prefer to skip the RTRj as it doubles the bus usage. Just let the drives operate on a synchronous basis, or on a timed basis, your choice.
[20:32:59] <jmkasunich> fortunately whenever we've discharged our bus caps I've been on the other side of a steel enclosure
[20:33:06] <jmkasunich> we get up to a couple kilojoules
[20:33:42] <dlabriola> Would be fun to see some of those beasties!
[20:34:44] <jmkasunich> http://www.ab.com/drives/powerflex/700L/
[20:35:09] <dlabriola> most of the effort in the CANopen stack is implementing the CAN dictionary. Makes the drives very flexible, but that come with a bunch of code. EMC would not need all that stuff! (at least not to get started)
[20:35:26] <jmkasunich> so, exactly how do we get started
[20:37:32] <jmkasunich> joules: http://home.att.net/~jmkasunich/Pics/CapBang1.jpg
[20:38:21] <dlabriola> I was looking at the drivers which come with the CANUSB.com device, which come from FTDI (I may have the letters mixed) which has been dedicated to GPL, so that is a good start. The data so send or receive is just a few set up words (COB-ID, message type (RTR/normal, 11bit/29bit ID) and the 8 bytes of message. should not be too much to hob together. Most of it is the framework to hook the data to HAL.
[20:38:48] <jmkasunich> are those drivers realtime tho?
[20:38:54] <dlabriola> That cap could not have been fun to be around.
[20:39:08] <jmkasunich> or do they just ride on the existing non-realtime Linux USB infrastructure?
[20:39:25] <jmkasunich> enclosures are a good thing
[20:39:35] <jmkasunich> that was part of a 2 in series x 10 in parallel bank
[20:39:44] <jmkasunich> when it failed, all 10 in parallel discharged into it
[20:40:07] <dlabriola> presumably built into the latest linux framework, so probably non-realtime Linux. Not sure how linux handles the isochronous USB data.
[20:40:28] <jmkasunich> I have absolutely no clue how to make the USB stuff work in realtime
[20:40:30] <dlabriola> Ouch! did you actually see/hear it go?
[20:40:45] <jmkasunich> not that particular one, I think that was a field failure
[20:40:58] <jmkasunich> I've heard/seen more than enough IGBT failures though
[20:41:03] <jmkasunich> same energy
[20:43:11] <dlabriola> The comments about the USB controllers to the bus have truth to them, just looking at the USB as I'm playing with this stuff on my dual boot laptop. If the positions are only 2ms or so, the basic USB update rate should flush them out in a reasonable time period. Let the time lock synchronization be between drives as well as the SYNC.
[20:44:16] <jmkasunich> but what happens when non-RT linux decides to do something else besides USB updates for 20 or 50 or 200mS?
[20:44:36] <dlabriola> this removes a lot of the hard realtime from the linux, if needed. I'm guessing there is some hardware driven dma powered hardware that is doing most of the work, but I've got to do some digging.
[20:45:01] <jmkasunich> I hate USB
[20:45:13] <dlabriola> The isochronous data driver is what is used to keep sound cards sampling at their 44khz on stereo inputs and outputs.
[20:45:24] <jmkasunich> I assume you don't want a plug-in CAN controller because you want to support laptops?
[20:45:35] <dlabriola> I've had courses, and used them, not done drivers.
[20:46:18] <dlabriola> The USB is an easy way for laptops. Could also go a PC card with the plug in CAN. Really about the same price out there.
[20:46:48] <dlabriola> Just have one of the CAN pieces in my hand already ;o)
[20:46:51] <jmkasunich> does the PC card let you directly talk inb() and outb() to the CAN controller chip?
[20:47:30] <dlabriola> I've got to research those more. I've been looking more at the USB than the PC cards.
[20:47:46] <jmkasunich> direct access to the controller will make things a _lot_ simpler
[20:48:10] <jmkasunich> makes it more like working in an embedded system, where you have control over what happens when
[20:48:24] <dlabriola> The USB driver code looks like reads and writes to buffer RAM with the support chips doing most of the work.
[20:48:56] <dlabriola> Yes I love the embedded side for just that reason. Have to write everything, but you have control over it all, and source to it all.
[20:49:19] <jmkasunich> the realtime parts of EMC are much like that, which is why I like it
[20:49:26] <steve_stallings> there is some real time USB stuff under Linux, but it is not mature or simple, see -- http://developer.berlios.de/projects/usb4rt/
[20:49:53] <jmkasunich> like programming microcontrollers, except you have a sizable fraction of a gigaflop available to do stuff with
[20:50:11] <dlabriola> Minus side of working in DSP is that much of what is needed you write - CANopen was about 22klines - took some 6 months!
[20:50:28] <dlabriola> Current DSP
[20:51:22] <dlabriola> Current DSP is 40Mips, going to 100Mips on next one. Main pain is they are a bit tite on the internal RAM, like to stay internal when inside the drives to keep noise down.
[20:51:53] <dlabriola> The open EMC code is one of the draws that has me interested.
[20:52:13] <jmkasunich> how much have you looked at the EMC code?
[20:52:19] <jmkasunich> in particular, hal drivers?
[20:53:20] <dlabriola> probably 40 hours or so over the last few months, reading docs and code. The HAL drivers do not look too horrible, having looked over some of the driver cards.
[20:53:29] <jmkasunich> steve_stallings: usb4rt isn't very active it seems
[20:53:50] <jmkasunich> their user list archives have about the same number of messages per month that we get per day (or maybe two days)
[20:54:22] <steve_stallings> true, but they are actual users trying to get it working, not idle chat
[20:54:27] <jmkasunich> some of them that need to detect the type of device and export HAL pins to match got pretty big and complex
[20:55:29] <steve_stallings> but I still agree, USB realtime on Linux is not going to grow up enough for us to latch onto
[20:55:45] <dlabriola> I think I was looking at single device type. I think I started looking at the multi device and skipped that portion for now as I did not dive into their hardware to see what was going on.
[20:56:07] <jmkasunich> ppmc, motenc, and m5i20 are the most complex I think
[20:57:41] <dlabriola> I was more looking at a CAN - HAL interface which would take COB-ID and data structure from an .ini file, exporting the inputs and outputs defined as pins, and then collecting and sending/receiving the data based on a thread in the RTE.
[20:58:03] <jmkasunich> RTE?
[20:58:16] <dlabriola> Real time exec - RTI for EMC2
[20:58:28] <jmkasunich> ok, HAL threads in other words
[20:58:38] <dlabriola> yea, slow to the lingo...
[20:58:44] <jmkasunich> no problem
[21:00:14] <dlabriola> Basically, instead of doing a PID control, just send the data and retreive the previous position data already waiting in the FIFO from last cycle. would have to open up the error trip limits a bit as the feedback data is lagging real time by one update cycle, but not a problem as it is not in the closed loop path.
[21:00:36] <jmkasunich> and the limits are in the ini file - you can open them up all you want
[21:00:45] <dlabriola> That was the thought.
[21:00:57] <jmkasunich> http://www.kvaser.com/index.htm
[21:01:31] <jmkasunich> dual CAN on PCMCIA
[21:01:36] <jmkasunich> dunno what it costs
[21:01:47] <alex_joni> kvaser is expensive
[21:02:08] <dlabriola> guessing about 3 bills or so.
[21:02:17] <alex_joni> I think I instructed anonimasu to them a while ago .. he said around 500+$ iirc
[21:02:29] <jmkasunich> http://www.computer-solutions.co.uk/gendev/can-pcmcia.htm
[21:02:35] <dlabriola> ouch
[21:03:02] <alex_joni> EUR240.00
[21:03:12] <jmkasunich> yep
[21:03:17] <jmkasunich> $350 or so
[21:03:24] <alex_joni> 300EUR for the dual channel
[21:03:35] <alex_joni> 315 for one channel with opto
[21:04:43] <dlabriola> The USB to 1 CAN was about $60, if I remember, part of the draw (I've got the web site at work)
[21:04:43] <jmkasunich> national instruments wants over $1000
[21:04:51] <jmkasunich> (I expected them to be a ripoff)
[21:05:18] <alex_joni> 60$ sure seems appealing
[21:05:32] <jmkasunich> if USB didn't suck so bad it would be
[21:05:45] <jmkasunich> using a network to talk to you rnetwork is just wrong
[21:05:46] <alex_joni> * alex_joni is thinking about debugging some CAN apps
[21:06:06] <alex_joni> is bus->network better?
[21:06:12] <jmkasunich> yes
[21:06:23] <jmkasunich> buses work in bytes/words, not packets
[21:06:38] <alex_joni> I know.. was just pulling your leg
[21:06:43] <dlabriola> Not that many chips, but development time. The CANUSB was basically a CAN driver chip, a CAN "UART" (I know that is an oversimplification of it) and a USB dual direction fifo chip. As data is received by the CAN chip, to the fifo, and visa versa.
[21:06:47] <alex_joni> the other case would be bus->net->net
[21:07:11] <alex_joni> dlabriola: any idea how you see it on linux?
[21:07:29] <jmkasunich> the CAN driver is strictly physical layer? like line drivers and receivers, etc?
[21:07:41] <jmkasunich> the "UART" has the CRC and other low level protocol stuff?
[21:07:57] <dlabriola> all matters to the north/south chip support, SATA is technically packets, but plenty fast when given hw support.'
[21:07:58] <alex_joni> dlabriola: does it have a driver ? one that sets up /dev/can ?
[21:08:32] <dlabriola> Yes - just send it the addresss, type/#bytes, data, it does the rest. Send status back if problems.
[21:09:02] <dlabriola> received data has the address (COB-ID), #bytes, data
[21:09:17] <dlabriola> CRC, bit stuffing, etc., all in the background.
[21:10:18] <dlabriola> Yes, http://www.canusb.com/linux/
[21:11:00] <dlabriola> based on
[21:11:24] <dlabriola> based on driver from http://www.ftdichip.com/
[21:11:31] <alex_joni> all in the background doesn't sound too good
[21:11:37] <alex_joni> sounds a lot like non-RT
[21:11:49] <jmkasunich> alex_joni: that stuff is done in hardware
[21:11:55] <alex_joni> ahh.. ok
[21:11:57] <jmkasunich> (CRC, bitstuffing)
[21:12:09] <dlabriola> the chip is doing it in the background - as the data is flowing.
[21:12:22] <dlabriola> i.e. dump the data and go.
[21:12:40] <jmkasunich> so, what next?
[21:12:54] <jmkasunich> do you want to start work on the driver?
[21:13:56] <dlabriola> probably some digging into the CANUSB driver to see how real time it is, and going from there. Wanted to see if there was any interest before getting in too deep. Compared to what the motor driver side has to do, should not be too bad.
[21:14:11] <SWPadnos> out of curiosity, what hardware is needed on the PC to support CAN (like a "can port card")?
[21:14:26] <SWPadnos> USB is by nature not RT enough for EMC (we think)
[21:14:52] <jmkasunich> SWPadnos: yeah, we've already said that
[21:14:57] <SWPadnos> (I need to read the log to see the specifics of how you plan to divvy up work)
[21:14:59] <dlabriola> I was trying to get the environment set up on the laptop to do the compile and keep in sync with EMC development.
[21:15:01] <jmkasunich> PCMCIA CAN cards are $300 and up
[21:15:07] <jmkasunich> the USB widget is $60
[21:15:25] <SWPadnos> ok. that sounds more like what I'd want ;)
[21:15:40] <SWPadnos> also there are AVR and ARM chips (among others) that have USB, CAN, ethernet, etc.
[21:15:40] <jmkasunich> dunno what a PCI -> CAN card costs
[21:15:57] <dlabriola> The USB is real time enough to support music input and output. Just need to keep in sync with their updating.
[21:16:17] <jmkasunich> hmm, CANUSB isn't full bandwidth - from their website:
[21:16:26] <jmkasunich> Our goal is to make it the choice of CAN to USB converters if you work with CAN speeds up to 250Kbit/100% busload, 500Kbit 50% busload or 1Mbit 25% busload.
[21:17:43] <dlabriola> I suppose a kludge could be to fifo to the parallel port and go from there. PCB's not that hard to do (about a day or so).
[21:17:57] <SWPadnos> man - they need to switch from the ATMega 162 to one of the ones with integrated CAN
[21:18:41] <SWPadnos> less expensive too
[21:19:05] <dlabriola> not familiar with ATMega 162
[21:19:21] <SWPadnos> I just noticed that's the microcontroller on the CANUSB dongle
[21:19:30] <SWPadnos> I program AVR chips a lot ;)
[21:19:35] <dlabriola> ah
[21:20:21] <SWPadnos> actually, I don't see the USB jack on this board, so I may be looking at the serial version
[21:20:38] <dlabriola> I do a lot with the TI DSP chips - can actually go up to about 4Mbits/sec, but not following CAN definition!
[21:20:44] <SWPadnos> heh
[21:20:52] <SWPadnos> I've only used the 56E800 series
[21:20:55] <SWPadnos> what a PITA those are
[21:21:40] <dlabriola> The CAN engine in the 'lf240x pretty good, the 'lf280x really nice!
[21:24:02] <dlabriola> I've got about another 15 min until family stuff. - I'll try to do some looking about different CAN cards and look into USB under linux. - worth mapping before madly running in all directions. Anyone else have any knowledge of the USB support under the north/south chips on the mother board?
[21:24:22] <jmkasunich> afraid not
[21:24:40] <SWPadnos> with respect to RT, there's a RTUSB project, I think affiliated with RTNet
[21:25:17] <SWPadnos> for general support, everything "just works". For RT, it's difficult due to the packet timing requirements of the USB spec
[21:25:48] <dlabriola> Let me do some digging around and see what I can come up with. I'll look at RTUSB.
[21:25:51] <SWPadnos> (how much USB crap did you guys already cover? I don't want to bore people with duplicated info)
[21:26:06] <jmkasunich> a bit ;-)
[21:26:10] <SWPadnos> ok :)
[21:26:50] <dlabriola> I was curious what the nominal update rate to the USB is - as it exists.
[21:27:18] <SWPadnos> 1 ms packet rate, fixed
[21:27:42] <SWPadnos> I believe that is partitioned into timeslices for each device
[21:28:10] <dlabriola> That is what I remembered. If a 5 ms or 10 ms sync in the background keeps everything in lock, 1 ms isn't too bad for RT.
[21:28:32] <SWPadnos> well, the way we're used to working, 1ms latency is pretty bad ;)
[21:28:51] <alex_joni> SWPadnos: but remember he's aiming for 10ms
[21:28:56] <SWPadnos> sure
[21:29:02] <SWPadnos> 1ms isn't so bad in that case :)
[21:29:03] <dlabriola> i.e what is the data to be used at the sync time. (project trajectory ahead by 5 ms), then let the drive do the interpolation.
[21:29:20] <dlabriola> just linear interpolation at the drive for now.
[21:29:39] <SWPadnos> I guess in the case of an error, losing synch between drives isn't that big of a problem
[21:29:47] <SWPadnos> since there's already a bigger problem
[21:30:15] <dlabriola> can automatically retries all packets if an error, just need to have the bus not loaded to 100%
[21:30:36] <SWPadnos> the issue is what happens when the accels need to be scaled in tandem - unless emc can tell all the drives to go slower, there's a problem
[21:30:41] <SWPadnos> sure
[21:31:20] <dlabriola> the planner is running at 10ms, isn't that as fast as EMC can react?
[21:31:44] <SWPadnos> no - the EMC planner runs at 1ms these days, and some peoople have it going up to ~8 or 10 KHz
[21:32:11] <steve_stallings> the 10 mS was typical of milling machine users historically, others are running faster
[21:32:18] <dlabriola> ah, must have been reading old documentation!
[21:32:48] <SWPadnos> a lot of people have mentioned using standalone drives per axis, but I think the issue is that emc wants the drives to follow a synchronized *path*, not just get to a specific *position*
[21:32:50] <dlabriola> If the planner is at 10khz, how fast the servo>
[21:33:19] <SWPadnos> there are people who want to run machines at 1000 in/min or so
[21:33:52] <SWPadnos> with exceedingly fast spindles (it's called High Speed Machining or HSM, if you aren't familiar with it)
[21:34:04] <dlabriola> I understand the sunchronized path, that is exactly what I'm interested in.
[21:34:10] <dlabriola> Sounds intersting!
[21:34:12] <SWPadnos> I think 1000 in/min is at the low end for that class of machine, actually
[21:34:13] <SWPadnos> yeah
[21:34:30] <SWPadnos> http://www.datrondynamics.com has some interesting videos :)
[21:35:53] <dlabriola> How fast is the update on the path - i.e. how many points around a curve. Would a cubic intgerpolation on the drive lower the update rate enough?
[21:36:16] <SWPadnos> we probably shouldn't worry about HSM for the moment
[21:37:24] <dlabriola> anyway, think it is worth looking at or too limited?
[21:37:44] <SWPadnos> EMC does the move as a series of positions at the update rate, so essentially it samples the "true" path every period
[21:38:31] <SWPadnos> I think it's (it being USB/CAN/ethernet/whatever) worth looking at, but I have reservations about whether it would work the way I expect
[21:39:07] <dlabriola> which update rate? I understood the path calc as typically happening at a lower sample rate than the PID, but I'm new to EMC and could be really off.
[21:39:29] <SWPadnos> I'm not that new, and I'm probably off as well ;)
[21:39:52] <SWPadnos> I often get confused between "Task" and the trajectory planner :)
[21:40:10] <SWPadnos> * SWPadnos is better at the very low and very high levels :)
[21:41:04] <dlabriola> I'm probably thinking about the trajectory planner - that is really the info needed to be pumped to the drive if the drive can do some interpolation. This makes the path between smooth with out having to force every point out.
[21:42:27] <dlabriola> I've done some pathing based on quadratic sections, a parabolic curve being fairly close to a circle, so not needing too many sections to do a pretty good approximation.
[21:43:14] <SWPadnos> sure. and quadratics are pretty easy in integer-land as well
[21:43:37] <dlabriola> yea, that helps too!
[21:44:58] <SWPadnos> I think the biggest hurdle will be getting nice RT access to a kernel driver (ie, writing to a device node instead of an I/O port)
[21:45:24] <dlabriola> I'll do some digging around to see if I can find a faster connect than USB at a reasonable price. If not, let me look at some of the USB info.
[21:45:39] <SWPadnos> unless you're planning on making "RT-aware" drivers for various USB chips
[21:46:00] <SWPadnos> the PCI cards look good, but they seem to be 5x as expensive, unfortunately
[21:46:31] <dlabriola> The CAN driver I mentioned is supposedly built into the latest linux kernel. The question is if it is real time enough just with the 1ms update for what we need.
[21:47:20] <SWPadnos> the only problem being that the motion controller is a kernel module, so you need to write to a device from within kernel space
[21:47:33] <dlabriola> I've got to go now - I'll try to be about next week. I'm monitoring the emc-develop mailing lists each night.
[21:47:49] <SWPadnos> cool. thanks for the topic :)
[21:47:54] <dlabriola> family arrived. sorry about that.
[21:47:59] <SWPadnos> heh
[21:48:03] <dlabriola> Try to pick it up next week!
[21:48:06] <SWPadnos> I've been painting all day :)
[21:48:11] <SWPadnos> darned families
[21:48:53] <dlabriola> running out to do christmas shopping - we are always Done before thanksgiving - lets us relax in the season!
[21:48:59] <SWPadnos> good plan
[21:49:06] <SWPadnos> I just finish by not starting :)
[22:17:37] <alex_joni> * alex_joni is back
[22:17:41] <alex_joni> did I miss anything?
[22:21:56] <SWPadnos> not a thing
[22:22:03] <alex_joni> ok :)
[22:23:43] <SWPadnos> argh. my cat wants to sit in my chair whether I'm here or not :)
[22:24:40] <alex_joni> you should make some room for it
[22:25:12] <SWPadnos> no need, apparently my lap is sufficient
[22:25:24] <SWPadnos> regardless of size :)
[22:26:30] <alex_joni> :)
[22:27:37] <SWPadnos> now it's dinner time - bbiab
[22:39:21] <alex_joni> hmm tab completion on ./configure is certainly a nice feature ;)
[22:39:43] <alex_joni> * alex_joni loves dapper
[22:50:12] <owhite> alex_joni: you awake?
[22:51:10] <alex_joni> a bit
[22:51:47] <owhite> hey. I got tkemc to read the joystick.
[22:51:53] <alex_joni> nice
[22:52:05] <owhite> yeah its okay. doesnt use the hal at all.
[22:52:18] <owhite> >> insert frowny face <<
[22:52:26] <owhite> but, how should I announce it?
[22:52:36] <owhite> should I bother?
[22:53:28] <alex_joni> depends ..
[22:53:33] <alex_joni> do you think it's usefull?
[22:53:38] <alex_joni> * alex_joni grins :D
[22:54:04] <owhite> yeah. I think it has some promise.
[22:54:16] <owhite> I dont think people would view it as too much of a hack.
[22:54:27] <alex_joni> can you make a diff?
[22:54:38] <alex_joni> cd ~/emc-head/tcl/
[22:54:49] <alex_joni> cvs diff -u > file.diff
[22:55:11] <owhite> I could do that -- what you want that for?
[22:55:19] <alex_joni> to look at the stuff ;)
[22:56:24] <owhite> *rolls eyes*
[22:56:37] <owhite> the addition to the code is easy.
[22:57:01] <alex_joni> does it still work if you have no joystick?
[22:57:01] <owhite> I did an open to joy2key which is C code that gets info from the joystick.
[22:57:18] <alex_joni> or if there's no joy2key installed?
[22:57:32] <alex_joni> would you think it's usefull to release this along with tkemc & emc2?
[22:57:36] <owhite> excelent point.
[22:57:44] <alex_joni> or rather as a simple hack that people want to change themselves?
[22:57:59] <alex_joni> for now I would think a wiki page would be best
[22:58:14] <alex_joni> are you familiar with wikis?
[22:58:32] <owhite> the code is not at a point where it does a check to see if joy2key is installed.
[22:58:41] <owhite> yes, familiar with wikis.
[22:58:49] <alex_joni> wiki.linuxcnc.org
[22:59:01] <owhite> *wonders if the plural of wiki should be wikies*
[22:59:15] <alex_joni> use /me ;)
[22:59:29] <alex_joni> /me wonders if the plural of wiki should be wikies
[22:59:35] <alex_joni> * alex_joni wonders if the plural of wiki should be wikies
[23:02:16] <owhite> oh god my daughter is listening to leonard skynard.
[23:02:28] <owhite> how terribly sad, in a poignant way.
[23:02:45] <owhite> <--- was probably listenign to the same crap at the same age.
[23:05:17] <alex_joni> heh
[23:08:53] <owhite> alright. I'm going to run. you have a good point -- before I advertise the code I should have it handle cases when users dont have joy2key, or a joystick plugged in. thanks for your help yesterday.
[23:41:51] <alex_joni> * alex_joni heads to bed.. good night all