1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 #include "mrilib.h"
8 
9 #define MAX_CHAN 32  /* 02 Aug 2002: cf. plug_realtime.c */
10 
11 static THD_3dim_dataset * RT_dset[MAX_CHAN] ;
12 static float              RT_dt             = 0.0 ;
13 static int                RT_3D             = 0 ;
14 static int                RT_swap2          = 0 ;
15 static char               RT_buf[32768] , RT_com[1024] ;
16 static int                RT_mega = 1 ;
17 
18 /*=============================================================================*/
19 
20 #if 0 /*  ZSS June 2011. Delete useless code after dust has settled.  */
21    #define AFNI_CONTROL_PORT  7954      /* always send control data to AFNI    */
22    #define AFNI_TCP_PORT      7953      /* maybe send image data to AFNI       */
23    /*
24    replace these two with:
25       get_port_named("AFNI_CONTROL_PORT")
26       and
27       get_port_named("AFNI_TCP_PORT")
28    */
29 #endif
30 
31 #define AFNI_OPEN_CONTROL_MODE   1      /* 1st time thru: open control channel */
32 #define AFNI_WAIT_CONTROL_MODE   2      /* waiting for AFNI to open control    */
33 #define AFNI_OPEN_DATA_MODE      3      /* now can open data channel to AFNI   */
34 #define AFNI_CATCHUP_MODE        4      /* waiting for AFNI to open data       */
35 #define AFNI_CONTINUE_MODE       5      /* at last! data channel is ready!     */
36 
37 /*-- global control variables --*/
38 
39 int      AFNI_mode        = 0 ;           /* if > 0, then means AFNI is active  */
40 int      AFNI_use_tcp     = 0 ;           /* if > 0, use TCP/IP to send images */
41 char     AFNI_host[128]   = "localhost" ; /* hostname of CPU AFNI is on       */
42 char     AFNI_iochan[128] = "\0" ;        /* I/O channel name to AFNI        */
43 IOCHAN * AFNI_ioc         = NULL ;        /* ptr to I/O channel itself      */
44 char     AFNI_buf[1024]          ;        /* temporary space               */
45 int      AFNI_verbose     = 0    ;        /* debugging mode               */
46 
47 char     AFNI_infocom[256]= "\0" ;        /* command for AFNI info */
48 
49 /*-- prototypes --*/
50 
51 void RT_start_io(void) ;
52 void RT_exit(void) ;
53 
54 /*-- how to execute a command on another system --*/
55 
56 #ifdef HP
57 # define RSH "remsh"
58 #else
59 # define RSH "rsh"
60 #endif
61 
62 /*=============================================================================*/
63 
RT_exit(void)64 void RT_exit(void)                   /* Function to be called to make sure */
65 {                                    /* the AFNI data channels get closed. */
66    fprintf(stderr,"*** RT_exit: closing data channel to AFNI\n") ;
67    iochan_close(AFNI_ioc) ;
68    return ;
69 }
70 
71 /*-----------------------------------------------------------------------------*/
72 
73 #include <signal.h>
74 
RT_sigfunc(int sig)75 void RT_sigfunc(int sig)   /** signal handler for fatal errors **/
76 {
77    char * sname ;
78    static volatile int fff=0 ;
79    if( fff ) _exit(1) ; else fff = 1 ;
80    switch(sig){
81       default:      sname = "unknown" ; break ;
82       case SIGINT:  sname = "SIGINT"  ; break ;
83       case SIGPIPE: sname = "SIGPIPE" ; break ;
84       case SIGSEGV: sname = "SIGSEGV" ; break ;
85       case SIGBUS:  sname = "SIGBUS"  ; break ;
86       case SIGTERM: sname = "SIGTERM" ; break ;
87    }
88    fprintf(stderr,"\n*** Fatal Signal %d (%s) received\n",sig,sname) ;
89    exit(1) ;
90 }
91 
92 /*****************************************************************************
93   Do I/O startup stuff.
94 
95   At any given moment, this routine is in one of a number of modes
96   (the AFNI_mode variable).
97   The first time in, AFNI_mode == AFNI_OPEN_CONTROL_MODE.  In each mode,
98   certain tasks must be accomplished and this program must be synchronized
99   with AFNI.  When the necessary deeds are done, the routine advances to
100   the next mode.  If the deeds cannot be done when this routine is called,
101   then it will stay in the same mode, and the next time it is called it
102   will try to do them again.  This routine should be called repeatedly
103   until it progresses to the last mode (AFNI_CONTINUE_MODE), which is for
104   normal transmission of images (one at a time) to AFNI.
105 
106   If an error occurs, so that this program can no longer talk to AFNI, then
107   AFNI_mode is set to 0, which means "do nothing further".  The rest of
108   the data acquisition software will continue, but these routines will
109   be stopped dead.
110 ******************************************************************************/
111 
AFNI_start_io(void)112 void AFNI_start_io( void )
113 {
114    int ii , jj ;
115 
116    /***** Check for conditions in which to do nothing *****/
117 
118    if( AFNI_mode <= 0 || AFNI_mode == AFNI_CONTINUE_MODE ) return ;
119 
120    /***** If we are at the first time in,
121           try to open a control socket to talk to AFNI *****/
122 
123    if( AFNI_mode == AFNI_OPEN_CONTROL_MODE ){
124 
125       sprintf( AFNI_iochan , "tcp:%s:%d" ,
126                AFNI_host , get_port_named("AFNI_CONTROL_PORT") ) ;
127 
128       if( AFNI_verbose )
129          fprintf(stderr,"Opening control channel %s to AFNI.\n",AFNI_iochan) ;
130 
131       AFNI_ioc = iochan_init( AFNI_iochan , "w" ) ;
132 
133       if( AFNI_ioc == NULL ){
134          fprintf(stderr,"Can't open control channel %s to AFNI!\a\n",AFNI_iochan) ;
135 #if 0
136          AFNI_mode = 0 ;                       /* disable AFNI */
137 #endif
138          return ;
139       } else {
140          if( AFNI_verbose ) fprintf(stderr,"Entering AFNI_WAIT_CONTROL_MODE.\n") ;
141          AFNI_mode = AFNI_WAIT_CONTROL_MODE ;  /* begin waiting for AFNI connection */
142          iochan_sleep(5) ;                     /* give other program a moment */
143       }
144    }
145 
146    /***** Check if the control socket is connected to AFNI *****/
147 
148    if( AFNI_mode == AFNI_WAIT_CONTROL_MODE ){
149 
150       ii = iochan_writecheck( AFNI_ioc , 1 ) ;  /* Check; wait at most 1 msec */
151 
152       /** if ii == 0, then the channel is still pending,
153           so do nothing; otherwise, take some action.    **/
154 
155       if( ii < 0 ){
156          fprintf(stderr,"Control channel to AFNI failed!\a\n") ;
157          IOCHAN_CLOSE(AFNI_ioc) ;
158          AFNI_mode = 0 ;                    /* disable AFNI */
159          return ;
160       } else if( ii > 0 ){
161          if( AFNI_verbose ) fprintf(stderr,"Control channel connected to AFNI."
162                                            "  Entering AFNI_OPEN_DATA_MODE.\n") ;
163          AFNI_mode = AFNI_OPEN_DATA_MODE ;  /* prepare to send data to AFNI */
164       }
165    }
166 
167    /***** Send the control information, which says
168           how we will talk to AFNI in the future (shmem or TCP/IP),
169           then close the control channel and open this new data channel *****/
170 
171    if( AFNI_mode == AFNI_OPEN_DATA_MODE ){
172 
173       /* decide name of data channel: it can be TCP/IP or shared memory */
174 
175       if( AFNI_use_tcp ) sprintf(AFNI_iochan,"tcp:%s:%d",
176                                  AFNI_host,get_port_named("AFNI_TCP_PORT")) ;
177       else if( RT_mega ) sprintf(AFNI_iochan,"shm:grv:%dM",RT_mega) ;
178       else               sprintf(AFNI_iochan,"shm:grv:50K") ;  /* 11 Dec 2002 */
179 
180       strcpy(AFNI_buf,AFNI_iochan) ;     /* tell AFNI where to read data */
181       if( AFNI_infocom[0] != '\0' ){
182          strcat(AFNI_buf,"\n") ;
183          strcat(AFNI_buf,AFNI_infocom) ; /* tell it where to get 3T info */
184       }
185 
186       if( AFNI_verbose )
187          fprintf(stderr,"Sending control information to AFNI:\n%s\n",AFNI_buf) ;
188 
189       ii = iochan_sendall( AFNI_ioc , AFNI_buf , strlen(AFNI_buf)+1 ) ;
190 
191       /** A negative return is bad news **/
192 
193       if( ii < 0 ){
194          fprintf(stderr,"Transmission of control data to AFNI failed!\a\n") ;
195          IOCHAN_CLOSE(AFNI_ioc) ;
196          AFNI_mode = 0 ;
197          return ;
198       } else {
199          while( ! iochan_clearcheck(AFNI_ioc,2) ) /* wait for control data to clear */
200             iochan_sleep(2) ;
201          IOCHAN_CLOSE(AFNI_ioc) ;                 /* close control channel */
202 
203          if( AFNI_verbose )
204             fprintf(stderr,"Opening data channel %s to AFNI.\n",AFNI_iochan) ;
205 
206          AFNI_ioc = iochan_init( AFNI_iochan , "w" ) ; /* open data channel */
207          if( AFNI_ioc == NULL ){
208             fprintf(stderr,"Can't open data channel %s to AFNI!\a\n",AFNI_iochan) ;
209             AFNI_mode = 0 ;
210             return ;
211          } else {
212             if( AFNI_verbose ) fprintf(stderr,"Entering AFNI_CATCHUP_MODE.\n") ;
213             AFNI_mode = AFNI_CATCHUP_MODE ;
214             iochan_sleep(5) ;                     /* give other program a moment */
215          }
216       }
217    }
218 
219    /***** Wait for the data channel to be connected to AFNI,
220           and then send any images that are reconstructed and ready to go *****/
221 
222    if( AFNI_mode == AFNI_CATCHUP_MODE ){
223 
224       ii = iochan_writecheck( AFNI_ioc , 1 ) ;  /* wait at most 1 msec */
225       if( ii < 0 ){
226          fprintf(stderr,"AFNI data channel aborted before any data was sent!\a\n") ;
227          IOCHAN_CLOSE( AFNI_ioc ) ;
228          AFNI_mode = 0 ;
229          return ;
230       } else if( ii > 0 ){                      /* can now send data to AFNI! */
231          if( AFNI_verbose )
232             fprintf(stderr,"AFNI data channel %s is connected.\n"
233                            "Entering AFNI_CONTINUE_MODE.\n" , AFNI_iochan) ;
234          AFNI_mode = AFNI_CONTINUE_MODE ;
235       }
236    }
237 
238    return ;
239 }
240 
241 /*****************************************************************************/
242 
243 /* 11 Dec 2002: string to mark end of image data,
244                 to enable ending dataset(s) without closing data channel */
245 
246 #define COMMAND_MARKER        "Et Earello Endorenna utulien!!"
247 #define COMMAND_MARKER_LENGTH 30
248 
main(int argc,char * argv[])249 int main( int argc , char * argv[] )
250 {
251    int iarg=1 , ii,tt,kk , nbytes , nbslice , ntran , nzfake=0 ;
252    char *bar , *qar=NULL , *sar ;
253    double start_time=0.0 , left_time , xtime ;
254    char *drive_afni[128] ;
255    int   ndrive=0 ;
256    int   num_chan , cur_chan , cc ;
257    char *note[128] ;   /* 02 Oct 2002 */
258    int   num_note=0 ;
259    int   num_start=0 , jarg , bwait ; /* 11 Dec 2002 */
260    float gyr=0.0 ;                    /* 29 Jan 2004 */
261 
262    /*-- help the ignorant user --*/
263 
264    if( argc < 2 || strcmp(argv[1],"-help") == 0 ){
265       printf(
266         "Usage: rtfeedme [options] dataset [dataset ...]\n"
267         "Test the real-time plugin by sending all the bricks in 'dataset' to AFNI.\n"
268         " * 'dataset' may include a sub-brick selector list.\n"
269         " * If more than one dataset is given, multiple channel acquisition\n"
270         "    will be simulated.  Each dataset must then have the same datum\n"
271         "    and dimensions.\n"
272         " * If you put the flag '-break' between datasets, then the datasets\n"
273         "    in each group will be transmitted in parallel, but the groups\n"
274         "    will be transmitted serially (one group, then another, etc.).\n"
275         "    + For example:\n"
276         "        rtfeedme A+orig B+orig -break C+orig -break D+orig\n"
277         "       will send the A and B datasets in parallel, then send\n"
278         "       the C dataset separately, then send the D dataset separately.\n"
279         "       (That is, there will be 3 groups of datasets.)\n"
280         "    + There is a 1 second delay between the end transmission for\n"
281         "       a group and the start transmission for the next group.\n"
282         "    + You can extend the inter-group delay by using a break option\n"
283         "       of the form '-break_20' to indicate a 20 second delay.\n"
284         "    + Within a group, each dataset must have the same datum and\n"
285         "       same x,y,z,t dimensions.  (Different groups don't need to\n"
286         "       be conformant to each other.)\n"
287         "    + All the options below apply to each group of datasets;\n"
288         "       i.e., they will all get the same notes, drive commands, ....\n"
289         "\n"
290         "Options:\n"
291         "  -host sname =  Send data, via TCP/IP, to AFNI running on the\n"
292         "                 computer system 'sname'.  By default, uses the\n"
293         "                 current system, and transfers data using shared\n"
294         "                 memory.  To send on the current system using\n"
295         "                 TCP/IP, use the system 'localhost'.\n"
296         "\n"
297         "  -dt ms      =  Tries to maintain an inter-transmit interval of\n"
298         "                 'ms' milliseconds.  The default is to send data\n"
299         "                 as fast as possible.\n"
300         "\n"
301         "  -3D         =  Sends data in 3D bricks.  By default, sends in\n"
302         "                 2D slices.\n"
303         "\n"
304         "  -buf m      =  When using shared memory, sets the interprocess\n"
305         "                 communications buffer to 'm' megabytes.  Has no\n"
306         "                 effect if using TCP/IP.  Default is m=1.\n"
307         "                 If you use m=0, then a 50 Kbyte buffer is used.\n"
308         "\n"
309         "  -verbose    =  Be talkative about actions.\n"
310         "  -swap2      =  Swap byte pairs before sending data.\n"
311         "\n"
312         "  -nzfake nz  =  Send 'nz' as the value of nzz (for debugging).\n"
313         "\n"
314         "  -drive cmd  =  Send 'cmd' as a DRIVE_AFNI command; e.g.,\n"
315         "                   -drive 'OPEN_WINDOW A.axialimage'\n"
316         "                 If cmd contains blanks, it must be in 'quotes'.\n"
317         "                 Multiple -drive options may be used.\n"
318         "\n"
319         "  -note sss   =  Send 'sss' as a NOTE to the realtime plugin.\n"
320         "                 Multiple -note options may be used.\n"
321         "\n"
322         "  -gyr v      =  Send value 'v' as the y-range for realtime motion\n"
323         "                 estimation graphing.\n"
324       ) ;
325       exit(0) ;
326    }
327 
328    mainENTRY("rtfeedme") ;
329 
330    /*-- scan arguments --*/
331 
332    while( iarg < argc && argv[iarg][0] == '-' ){
333 
334       if( strcmp(argv[iarg],"-gyr") == 0 ){     /* 29 Jan 2004 */
335         gyr = strtod( argv[++iarg] , NULL ) ;
336         iarg++ ; continue ;
337       }
338 
339       if( strcmp(argv[iarg],"-drive") == 0 ){   /* 30 Jul 2002 */
340          drive_afni[ndrive++] = argv[++iarg] ;
341          iarg++ ; continue ;
342       }
343 
344       if( strcmp(argv[iarg],"-note") == 0 ){    /* 02 Oct 2002 */
345          note[num_note++] = argv[++iarg] ;
346          iarg++ ; continue ;
347       }
348 
349       if( strcmp(argv[iarg],"-nzfake") == 0 ){
350          nzfake = (int) strtod( argv[++iarg] , NULL ) ;
351          iarg++ ; continue ;
352       }
353 
354       if( strcmp(argv[iarg],"-buf") == 0 ){
355          RT_mega = (int) strtod( argv[++iarg] , NULL ) ;
356          if( RT_mega < 0 ){
357             fprintf(stderr,"*** Illegal value after -buf\n") ; exit(1) ;
358          }
359          iarg++ ; continue ;
360       }
361 
362       if( strcmp(argv[iarg],"-host") == 0 ){
363          strcpy( AFNI_host , argv[++iarg] ) ;
364          AFNI_use_tcp = 1 ;
365          iarg++ ; continue ;
366       }
367 
368       if( strcmp(argv[iarg],"-dt") == 0 ){
369          RT_dt = strtod( argv[++iarg] , NULL ) * 0.001 ;
370          iarg++ ; continue ;
371       }
372 
373       if( strcmp(argv[iarg],"-3D") == 0 ){
374          RT_3D = 1 ;
375          iarg++ ; continue ;
376       }
377 
378       if( strcmp(argv[iarg],"-swap2") == 0 ){
379          RT_swap2 = 1 ;
380          iarg++ ; continue ;
381       }
382 
383       if( strcmp(argv[iarg],"-verbose") == 0 ){
384          AFNI_verbose = 1 ;
385          iarg++ ; continue ;
386       }
387 
388       fprintf(stderr,"*** Unrecognized option: %s\n",argv[iarg]) ;
389       exit(1) ;
390    }
391 
392    /*-- this stuff is one-time-only setup of the I/O to AFNI --*/
393 
394    atexit(RT_exit) ;                     /* call this when program ends */
395    AFNI_mode = AFNI_OPEN_CONTROL_MODE ;  /* mode in which to start I/O  */
396 
397    signal(SIGINT ,RT_sigfunc) ;  /* setup signal handler */
398    signal(SIGBUS ,RT_sigfunc) ;  /* for fatal errors */
399    signal(SIGSEGV,RT_sigfunc) ;
400    signal(SIGTERM,RT_sigfunc) ;
401 
402    /*------ 11 Dec 2002: after a -break, will jump back here ------*/
403 
404 Restart:
405 
406    /*-- count datasets up to end of argv or next -break --*/
407 
408    jarg     = iarg ;  /* keep track of where we are starting */
409    num_chan = 0 ;
410    for( ; iarg < argc && strncmp(argv[iarg],"-break",6) != 0 ; iarg++ ) num_chan++;
411    if( num_chan == 0 ){
412      fprintf(stderr,"*** No more datasets!  Free, free, free at last!\n"); exit(0);
413    }
414    if( num_chan > MAX_CHAN ){
415      fprintf(stderr,"*** Too many datasets on command line!\n"); exit(1);
416    }
417 
418    /*-- skip any -break's for when we loop back to Restart --*/
419 
420    for( ; iarg < argc && strncmp(argv[iarg],"-break",6) == 0 ; iarg++ ) ; /* nada */
421 
422    /* check for delay in the form of "-break_XXX" where XXX = # sec to wait */
423 
424    bwait = 1 ;
425    if( iarg < argc && strncmp(argv[iarg-1],"-break_",7) == 0 ){
426      bwait = strtol( argv[iarg-1]+7 , NULL , 10 ) ;
427    }
428    if( bwait < 0 ) bwait = 1 ;
429 
430    num_start++ ;  /* number of times we've been here */
431 
432    /*-- read the input dataset(s) and check them for OK-ositiness --*/
433 
434    for( cc=0 ; cc < num_chan ; cc++ ){
435 
436      RT_dset[cc] = THD_open_dataset( argv[jarg+cc] ) ;
437 
438      if( RT_dset[cc] == NULL ){
439        fprintf(stderr,"*** Can't open dataset %s\n",argv[jarg+cc]); exit(1);
440      }
441 
442      if( cc > 0 ){  /* check for compatibility with #0 */
443 
444 #define ERREX(ee)                                                           \
445   do { fprintf(stderr,"*** " ee ":%s and %s\n",argv[jarg],argv[jarg+cc]) ;  \
446        exit(1) ; } while(0)
447 
448       if( DSET_NX   (RT_dset[0]) != DSET_NX   (RT_dset[cc]) ) ERREX("nx mismatch") ;
449       if( DSET_NY   (RT_dset[0]) != DSET_NY   (RT_dset[cc]) ) ERREX("ny mismatch") ;
450       if( DSET_NZ   (RT_dset[0]) != DSET_NZ   (RT_dset[cc]) ) ERREX("nz mismatch") ;
451 
452       if( DSET_NVALS(RT_dset[0]) != DSET_NVALS(RT_dset[cc]) ) ERREX("nvals mismatch");
453 
454       if( DSET_BRICK_TYPE(RT_dset[0],0) != DSET_BRICK_TYPE(RT_dset[cc],0) )
455                                                               ERREX("datum mismatch");
456      }
457 
458      /* load from disk */
459 
460      DSET_load(RT_dset[cc]) ;
461      if( !DSET_LOADED(RT_dset[cc]) ){
462        fprintf(stderr,"*** Can't load dataset %s\n",argv[jarg+cc]); exit(1);
463      }
464    } /* end of loop over channels (datasets to send in parallel) */
465 
466    /*-- initiate communications with AFNI --*/
467 
468    if( AFNI_verbose ) fprintf(stderr,"--- Starting I/O to AFNI\n") ;
469 
470    AFNI_start_io() ;
471 
472    ii = 1 ;
473    while( AFNI_mode > 0 && AFNI_mode != AFNI_CONTINUE_MODE && ii < 1000 ){
474      iochan_sleep( 300 ) ;  /* 300 msec wait */
475      AFNI_start_io() ;
476      ii++ ;
477    }
478 
479    if( AFNI_mode != AFNI_CONTINUE_MODE ){
480      fprintf(stderr,"\n*** Can't connect to AFNI?!\n") ; exit(1) ;
481    }
482 
483    if( AFNI_verbose )
484      fprintf(stderr,"\n--- Connection to AFNI is ready after %d tries\n",ii) ;
485 
486    /*-- Send dataset control information --*/
487 
488 #define ADDTO_BUF ( strcat(RT_buf,RT_com) , strcat(RT_buf,"\n") )
489 
490    RT_buf[0] = '\0' ;   /* string to hold commands to AFNI realtime plugin */
491 
492    /*** Number of channels [Aug 2002] ***/
493 
494    if( num_chan > 1 ){                         /* default is 1 channel */
495      sprintf(RT_com,"NUM_CHAN %d",num_chan) ;
496      ADDTO_BUF ;
497    }
498 
499    /*** How the data will be sent ***/
500 
501    strcpy(RT_com,"ACQUISITION_TYPE ") ;
502    if( DSET_NVALS(RT_dset[0]) == 1 ){
503      if( RT_3D ) strcat(RT_com,"3D") ;    /* 1 3D array, all at once */
504      else        strcat(RT_com,"2D+z") ;  /* 1 3D array, by slices */
505    } else {
506      if( RT_3D ) strcat(RT_com,"3D+t") ;  /* multi 3D arrays, each all at once */
507      else        strcat(RT_com,"2D+zt") ; /* multi 3D arrays, each by slices */
508    }
509    ADDTO_BUF ;
510 
511    /*** Time step, if needed ***/
512 
513    if( DSET_NVALS(RT_dset[0]) > 1 && DSET_TR(RT_dset[0]) > 0.0 ){
514      float TR = DSET_TR(RT_dset[0]) ;
515      if( DSET_TIMEUNITS(RT_dset[0]) == UNITS_MSEC_TYPE ) TR *= 0.001 ;
516      sprintf( RT_com , "TR %f" , TR ) ;
517      ADDTO_BUF ;
518    }
519 
520    /*** Volume dimensions ***/
521 
522    sprintf( RT_com, "XYFOV %f %f %f", fabs(DSET_DX(RT_dset[0]) * DSET_NX(RT_dset[0])) ,
523                                       fabs(DSET_DY(RT_dset[0]) * DSET_NY(RT_dset[0])) ,
524                                       fabs(DSET_DZ(RT_dset[0]) * DSET_NZ(RT_dset[0]))  ) ;
525    ADDTO_BUF ;
526 
527    /*** Matrix sizes ***/
528 
529    if( nzfake <= 0 ){
530      sprintf( RT_com , "XYMATRIX %d %d %d" , DSET_NX(RT_dset[0]) ,
531                                              DSET_NY(RT_dset[0]) ,
532                                              DSET_NZ(RT_dset[0])  ) ;
533      ADDTO_BUF ;
534    } else {
535      sprintf( RT_com , "XYMATRIX %d %d" , DSET_NX(RT_dset[0]) ,
536                                           DSET_NY(RT_dset[0])  ) ;
537      ADDTO_BUF ;
538      sprintf( RT_com , "ZNUM %d" , nzfake ) ;
539      ADDTO_BUF ;
540    }
541 
542    /*** Data type ***/
543 
544    sprintf( RT_com, "DATUM %s", MRI_TYPE_name[DSET_BRICK_TYPE(RT_dset[0],0)] ) ;
545    ADDTO_BUF ;
546 
547    /*** Slice order ***/
548 
549    if( ! RT_3D ){                         /* this cheapo program always      */
550      strcpy( RT_com , "ZORDER seq" ) ;    /* send slices in sequential order */
551      ADDTO_BUF ;                          /* unlike their true acquisition   */
552    }
553 
554    /*** Axes orientation [e.g., RAI] ***/
555 
556    sprintf( RT_com , "XYZAXES %s %s %s" ,
557             ORIENT_shortstr[ RT_dset[0]->daxes->xxorient ] ,
558             ORIENT_shortstr[ RT_dset[0]->daxes->yyorient ] ,
559             ORIENT_shortstr[ RT_dset[0]->daxes->zzorient ]  ) ;
560    ADDTO_BUF ;
561 
562    /*** Axes offsets [11 Dec 2002] ***/
563 
564    { float xorg,yorg,zorg ;
565      int   xorc,yorc,zorc ;
566 
567      xorg = RT_dset[0]->daxes->xxorg ;
568      yorg = RT_dset[0]->daxes->yyorg ;
569      zorg = RT_dset[0]->daxes->zzorg ;
570 
571      xorc = RT_dset[0]->daxes->xxorient ;
572      yorc = RT_dset[0]->daxes->yyorient ;
573      zorc = RT_dset[0]->daxes->zzorient ;
574 
575      if( ORIENT_sign[xorc] == '+' ) xorc = ORIENT_OPPOSITE(xorc) ;
576      if( ORIENT_sign[yorc] == '+' ) yorc = ORIENT_OPPOSITE(yorc) ;
577      if( ORIENT_sign[zorc] == '+' ) zorc = ORIENT_OPPOSITE(zorc) ;
578 
579      sprintf( RT_com , "XYZFIRST %g%c %g%c %g%c" ,
580               xorg , ORIENT_first[xorc] ,
581               yorg , ORIENT_first[yorc] ,
582               zorg , ORIENT_first[zorc]  ) ;
583      ADDTO_BUF ;
584    }
585 
586    /*** DRIVE_AFNI commands [Jul 2002] ***/
587 
588    for( ii=0 ; ii < ndrive ; ii++ ){
589      sprintf( RT_com , "DRIVE_AFNI %s" , drive_afni[ii] ) ;
590      ADDTO_BUF ;
591    }
592 
593    /*** NOTE commands [02 Oct 2002] ***/
594 
595    for( ii=0 ; ii < num_note ; ii++ ){
596      sprintf( RT_com , "NOTE %s" , note[ii] ) ;
597      ADDTO_BUF ;
598    }
599 
600    /*** GRAPH range commands [29 Jan 2004] ***/
601 
602    if( DSET_NVALS(RT_dset[0]) > 9 ){
603      sprintf( RT_com , "GRAPH_XRANGE %d" , DSET_NVALS(RT_dset[0]) ) ;
604      ADDTO_BUF ;
605      if( gyr > 0.0 ){
606        sprintf( RT_com , "GRAPH_YRANGE %.2f" , gyr ) ;
607        ADDTO_BUF ;
608      }
609    }
610 
611    /*** send metadata buffer to AFNI ***/
612 
613    if( AFNI_verbose )
614       fprintf(stderr,"--- Dataset control info for AFNI:\n%s",RT_buf) ;
615 
616    ii = iochan_sendall( AFNI_ioc , RT_buf , strlen(RT_buf)+1 ) ;
617    if( ii < 0 ){
618      fprintf(stderr,"*** Error sending dataset control info to AFNI\n") ;
619      exit(1) ;
620    }
621 
622    iochan_sleep(128) ;  /* let AFNI digest the results for a while */
623 
624    /*-- compute number of bytes per slice, and per image transmission --*/
625 
626    nbslice = nbytes = mri_datum_size( DSET_BRICK_TYPE(RT_dset[0],0) )
627                       * DSET_NX(RT_dset[0]) * DSET_NY(RT_dset[0]) ;
628 
629    if( RT_3D ) nbytes *= DSET_NZ(RT_dset[0]) ;
630 
631    if( qar != NULL ) free(qar) ;                     /* free old workspace */
632    qar = (char *) malloc( sizeof(char) * nbytes ) ;  /* make new workspace */
633    if( qar == NULL ){
634      fprintf(stderr,"*** Can't malloc workspace!\n"); exit(1);
635    }
636 
637    /*--- send slices or volumes to AFNI ---*/
638 
639    xtime = COX_clock_time() ;                  /* keep track of elapsed time */
640 
641    ntran = DSET_NVALS(RT_dset[0]) * num_chan ; /* number of transmissions: */
642    if( !RT_3D ) ntran *= DSET_NZ(RT_dset[0]) ; /* volumes or slices */
643 
644    for( tt=0 ; tt < DSET_NVALS(RT_dset[0]) ; tt++ ){  /* loop over time points */
645 
646       if( RT_3D ){                          /** send 3D arrays **/
647 
648        for( cc=0 ; cc < num_chan ; cc++ ){  /* loop over channels (datasets) */
649 
650          bar = DSET_ARRAY(RT_dset[cc],tt) ; /* array to send */
651 
652          if( AFNI_verbose )
653            fprintf(stderr,"--- Sending brick %d, channel %02d\n",tt,cc+1) ;
654 
655          if( RT_dt > 0.0 ) start_time = COX_clock_time() ;
656 
657          sar = bar ;
658          if( RT_swap2 ){                    /* swap bytes? */
659            memcpy(qar,sar,nbytes) ; sar = qar ;
660            mri_swap2( nbytes/2 , (short *) sar ) ;
661          }
662 
663          /* send the whole 3D array (sar points to data to transmit) */
664 
665          ii = iochan_sendall( AFNI_ioc , sar , nbytes ) ;
666          if( ii < 0 ){
667            fprintf(stderr,
668                    "*** Error sending brick %d, channel %02d, to AFNI\n",
669                    tt,cc+1) ;
670            exit(1) ;
671          }
672 
673          /* maybe wait for prescribed transmission time */
674 
675          if( RT_dt > 0.0 ){
676            left_time = RT_dt - ( COX_clock_time() - start_time ) ;
677            if( left_time >= 0.001 ){
678              ii = (int) (1000.0 * left_time) ;  /* number of milliseconds */
679              iochan_sleep( ii ) ;
680            }
681          }
682        } /* end of loop over channels */
683 
684       /** send 2D slices from each channel in turn **/
685 
686       } else {
687 
688          for( kk=0 ; kk < DSET_NZ(RT_dset[0]) ; kk++ ){  /* loop over slices */
689           for( cc=0 ; cc < num_chan ; cc++ ){            /* loop over channels */
690 
691             bar = DSET_ARRAY(RT_dset[cc],tt) ;  /* 3D array to get slice from */
692 
693             if( AFNI_verbose )
694               fprintf(stderr,"--- Sending brick %d, slice %d, channel %02d\n",
695                              tt,kk,cc+1) ;
696 
697             if( RT_dt > 0.0 ) start_time = COX_clock_time() ;
698 
699             sar = bar+(kk*nbslice) ;  /* pointer to start of slice data */
700 
701             if( RT_swap2 ){           /* byte swapping */
702                memcpy(qar,sar,nbslice) ; sar = qar ;
703                mri_swap2( nbslice/2 , (short *) sar ) ;
704             }
705 
706             /* send slice data */
707 
708             ii = iochan_sendall( AFNI_ioc , sar , nbslice ) ;
709 
710             if( ii < 0 ){
711               fprintf(stderr,
712                       "*** Error sending slice brick %d, slice %d, channel %02d to AFNI\n",
713                       tt,kk,cc+1) ;
714               exit(1) ;
715             }
716 
717             if( RT_dt > 0.0 ){  /* wait for prescribed transmission time */
718               left_time = RT_dt - ( COX_clock_time() - start_time ) ;
719               if( left_time >= 0.001 ){
720                 ii = (int) (1000.0 * left_time) ;
721                 iochan_sleep( ii ) ;
722               }
723             }
724 
725           } /* end of loop over channels */
726          } /* end of loop over slices */
727 
728       } /* end of if 3D or 2D transmissions */
729 
730       /** unload dataset bricks we just sent **/
731 
732       for( cc=0 ; cc < num_chan ; cc++ )
733         DSET_unload_one( RT_dset[cc] , tt ) ;
734 
735    } /* end of loop over time points */
736 
737    /*-- cleanup --*/
738 
739    xtime = COX_clock_time() - xtime ;  /* total transmit time */
740 
741    for( cc=0 ; cc < num_chan ; cc++ )  /* unload all datasets */
742       DSET_delete( RT_dset[cc] ) ;
743 
744    /* make sure all data is transmitted to AFNI */
745 
746    if( AFNI_verbose ) fprintf(stderr,"--- Clearing buffer") ;
747    iochan_sleep(100) ;
748    while( ! iochan_clearcheck(AFNI_ioc,100) ){
749       if( AFNI_verbose ) fprintf(stderr,".") ;
750    }
751    if( AFNI_verbose ) fprintf(stderr,"\n") ;
752 
753    fprintf(stderr,
754            "--- Elapsed transmit time = %f s (%f per transmit)\n",
755            xtime,xtime/ntran) ;
756 
757    /*-- If we are going to restart, send the end-of-input message. --*/
758    /*-- Note that an entire image must be sent with this message.  --*/
759 
760    if( iarg < argc ){
761      fprintf(stderr,"--- Restarting after '-break'\n") ;
762      memcpy( qar , COMMAND_MARKER , COMMAND_MARKER_LENGTH ) ;
763      iochan_sendall( AFNI_ioc , qar , nbytes ) ;
764      iochan_sleep(1000*bwait) ;  /* let AFNI meditate on that for a while */
765      goto Restart ;
766    }
767 
768    /*-- Otherwise, quit. --*/
769 
770    exit(0) ;
771 }
772