================================================ Realtime AFNI control information: What it needs ================================================ AFNI needs some information about the acquisition in order to properly construct a dataset from the images. This information is sent to AFNI as a series of command strings. A sample set of command strings is given below: ACQUISITION_TYPE 2D+zt TR 5.0 XYFOV 240.0 240.0 112.0 ZNUM 16 XYZAXES S-I A-P L-R DATUM short XYMATRIX 64 64 The commands can be given in any order. Each command takes up a single line of input (i.e., commands are separated by the '\n' character in the input buffer, and the whole set of commands is terminated by the usual '\0'). Each command line has one or more arguments. The full list of possible command strings and their arguments is: ACQUISITION_TYPE arg This command tells AFNI how the image data will be formatted: arg = 2D+z -> a single 3D volume, one slice at a time 2D+zt -> multiple 3D volumes, one slice at a time [the default] 3D -> a single 3D volume, all at once 3D+t -> multiple 3D volumes, one full volume at a time *This command is not required, since there is a default. NAME arg or PREFIX arg This command tells AFNI what name to use for the new dataset. *It is not required, since AFNI will generate a name if none is given. TR arg This command tells AFNI what the imaging TR is, in seconds. The default value, if this command is not given, is 1.0. *It is recommended that this command be used, so that the dataset has the correct header information. But this command is not required. ZDELTA dz This command tells AFNI the slice thickness, in mm. *This command, or the next one, MUST be used, so that the correct size of the dataset along the z-axis size known. XYFOV xx yy [zz] This command tells AFNI the size of the images, in mm. The first value ('xx') is the x-axis dimension, and the second value ('yy') is the y-axis dimension. If the third value ('zz') is present, then it is the z-axis dimension (slab thickness of all slices). *This command MUST be used to at least to give the sizes of the dataset along the x- and y-axes. If 'zz' is not given, then the ZDELTA command is also required. *If 'yy'==0, then it is taken to be the same as 'xx' (square images). ZFIRST zz[d] Specifies the location of the first slice, along the z-axis, in mm. The value 'zz' gives the offset. The optional code 'd' gives the direction that distance 'zz' applies. The values allowed for the single character 'd' are I = inferior S = superior A = anterior P = posterior R = right L = left *This command is optional - if not given, then the volume will be centered about z=0 (which is what always happens for the x- and y-axes). If the direction code 'd' is given, then it must agree with the sense of the z-axis given in the XYZAXES command. When more than one dataset is being acquired in a scanning session, then getting ZFIRST correct is important so that the AFNI datasets will be properly positioned relative to each other (e.g., so you can overlay SPGR and EPI data correctly). XYZFIRST xx[d] yy[d] zz[d] This new option (10 Dec 2002) lets you set the offsets of the dataset volume on all 3 axes. It is very similar to ZFIRST above, but you give values for all axes. For example: XYZAXES S-I A-P L-R XYZFIRST 30 20A 50R sets the x-origin to 30S (since no direction code was given for x), the y-origin to 20A, and the z-origin to 50R. Since the z-axis is L-R and starts in the R hemisphere, these sagittal slices are all in the R hemisphere. If the 'R' code had been left off the '50R', then the z-origin would have been set to 50L. Note that the origin is the CENTER of the first voxel. *This command is optional. If it is given along with ZFIRST (why?), then whichever one comes last wins (for the z-axis). XYMATRIX nx ny [nz] Specifies the size of the images to come, in pixels: nx = number of pixels along x-axis ny = number of pixels along y-axis nz = number of pixels along z-axis (optional here) *This command is required. If 'nz' is not given here, then it must be given using the ZNUM command. ZNUM nz Specifies the number of pixels along the z-axis (slice direction). *This value must be given, either with XYMATRIX or ZNUM. *Note that AFNI cannot handle single-slice datasets! DATUM typ Specifies the type of data in the images: typ = short -> 16 bit signed integers [the default] float -> 32 bit IEEE floats byte -> 8 bit unsigned integers complex -> 64 bit IEEE complex values (real/imag pairs) *This command is not required, as long as the data are really shorts. The amount of data read for each image will be determined by this command, the XYMATRIX dimensions, and the ACQUISITION_TYPE (whether 2D or 3D data is being sent). BYTEORDER order This new command string (27 Jun 2003) tells the realtime plugin the byte order (endian) that the image data is in. If the byte order is different from that of the machine afni is running on, the realtime plugin will perform byte swapping on the images as they are read in. order = LSB_FIRST -> least significant byte first (little endian) = MSB_FIRST -> most significant byte first (big endian) *This command is not required. Without this command, image bytes will not be swapped. *This command works for DATUM type of short, int, float or complex. ZORDER arg Specifies the order in which the slices will be read. arg = alt -> alternating order (e.g., slices are presented to AFNI in order 1 3 5 7 9 2 4 6 8, when nz=9). = seq -> sequential order (e.g., slices are presented to AFNI in order 1 2 3 4 5 6 7 8 9, when nz=9). *This command is not required, since 'alt' is the default. It will be ignored if a 3D ACQUISITION_TYPE is used. XYZAXES xcode ycode zcode Specifies the orientation of the 3D volume data being sent to AFNI. Each of the 3 codes specifies one axis orientation, along which the corresponding pixel coordinate increases. The possible codes are: I-S (or IS) -> inferior-to-superior S-I (or SI) -> superior-to-inferior A-P (or AP) -> anterior-to-posterior P-A (or PA) -> posterior-to-anterior R-L (or RL) -> right-to-left L-R (or LR) -> left-to-right For example, "XYZAXES S-I A-P L-R" specifies a sagittal set of slices, with the slice acquisition order being left-to-right. (In this example, if ZFIRST is used, the 'd' code in that command must be either 'L' or 'R'.) The 3 different axes codes must point in different spatial directions (e.g., you can't say "XYZAXES S-I A-P I-S"). *This command is required, so that AFNI knows the orientation of the slices in space. GRAPH_XRANGE x_range Specifies the bounding range of the horizontal axis on the 3D motion correction graph window (which is measured in repetitions). The actual range will be [0, x_range]. E.g. "GRAPH_XRANGE 120". GRAPH_YRANGE y_range Specifies the bounding range of the vertical axis on the 3D motion correction graph window (the units will vary). The actual range will be [-y_range, +y_range]. E.g. "GRAPH_YRANGE 2.3". If both GRAPH_XRANGE and GRAPH_YRANGE are given, then no final (scaled) motion correction graph will appear. GRAPH_EXPR expression Allows the user to replace the 6 default 3D motion correction graphs with a single graph, where the 'expression' is evaluated at each step based on the 6 motion parameters at that step. The variables 'a' through 'f' are used to represent dx, dy, dz, roll, pitch and yaw, respectively. E.g. GRAPH_EXPR sqrt((a*a+b*b+c*c+d*d+e*e+f*f)/6) See '3dcalc -help' for more information on expressions. ** Note that spaces should NOT be used in the expression. NUM_CHAN nc Specifies the number of independent image "channels" that will be sent to AFNI. Each channel goes into a separate dataset. Channel images are interleaved; for example, if nc=3, then image #1 -> datataset #1 image #2 -> datataset #2 image #3 -> datataset #3 image #4 -> datataset #1 image #5 -> datataset #2 et cetera. For 2D acquisitions, each slice is one "image" in the list above. For 3D acquisitions, each volume is one "image". All channels will have the same datum type, the same xyz dimensions, and so on. * This command is optional, since the default value of nc is 1. DRIVE_AFNI command You can also pass commands to control AFNI (e.g., open windows) in the image prolog. See README.driver for the list of command strings. More than one DRIVE_AFNI command can be used in the realtime prolog. * This command is optional. DRIVE_WAIT command This command works exactly like DRIVE_AFNI, except that the real-time plugin waits for the next complete volume to execute the command. The purpose is to execute the command after the relevant data has arrived. NOTE text to attach to dataset This command lets you attach text notes to the dataset(s) being created by the realtime plugin. All the text after "NOTE ", up to (not including) the next '\n', will be attached as a text note. More than one NOTE can be given. If you want to send a multiline note, then you have to convert the '\n' characters in the note text to '\a' or '\f' characters (ASCII 7 and 12 (decimal), respectively). Any '\a' or '\f' characters in the text will be converted to '\n' characters before the note is processed. OBLIQUE_XFORM m0 m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 This command is to send an IJK_TO_DICOM_REAL oblique transformation matrix, consisting of 16 floats in row-major order, to be applied to all resulting datasets (i.e. stored in the daxes->ijk_to_dicom_real structure). ============================================== How AFNI reads realtime command and image data ============================================== This stuff is all carried out in the image source program (e.g., Rx_xpi). Most of the current source code is in file ep_afni.c, for operation at the MCW Bruker 3 Tesla scanner. Also see the sample program rtfeedme.c. Step 1: The image source program opens a TCP/IP socket to the system running AFNI, on port 7954 - the realtime AFNI plugin is listening there. AFNI checks if the host that opened the connection is on its "trust list". When this socket is ready then ... Step 2: The image source program tells AFNI from where it should really get its data. A control string is written to the 7954 socket. The first line of this control string specifies whether to use a TCP/IP socket for the data channel, or to use shared memory. If there is a second line on the control string, then it is the name of an "info program" that AFNI should run to get the command information described above. At the old MCW Bruker 3 Tesla scanner, these commands are generated by the program 3T_toafni.c, which runs a script on the 3T60 console computer to get values from ParaVision, and then takes that information and formats most of the control commands for realtime AFNI. In the ep_afni.c routines, the name of the info program is stored in string variable AFNI_infocom, which is initialized in ep_afni.h to be "3T_toafni". If this string is NOT sent, then AFNI will try to get the image metadata from the image data stream (cf. Step 3, below). When AFNI reads the control string from the 7954 socket, it then closes down the 7954 socket and opens the data channel (TCP/IP or shared memory) that the first line of the control string specified. If the second line of the control string specified an info program to get the command strings, this program will not be run until the first image data arrives at AFNI. There are 2 reasons for separating the data channel from the control socket. First, if the image source program is one the same system as AFNI, then shared memory can be used for the data channel. However, I wanted AFNI to be able to be on a separate system from the image source program, so I also wanted to allow for transport of image data via a socket. At the beginning, AFNI doesn't know where it will get the data from, so the initial connection must be via a socket, but later it might want to switch to shared memory. Second, in principal AFNI could acquire data from more than one image source at a time. This is not yet implemented, but keeping the initial control socket separated from the actual data stream makes this a possibility. (The control socket is only used briefly, since only a few bytes are transmitted along it.) Step 3: Once the data channel to AFNI is open, the image source program can send image data to AFNI (this is done in AFNI_send_image() in ep_afni.c). Before the first image is sent, there must be at least one AFNI command string sent along the data channel. In the way I've set up ep_afni.c for the MCW Bruker 3T, two commands are actually sent here just before the first image: DATUM short XYMATRIX nx ny All the rest of the commands come from 3T_toafni. The reason for this separation is that 3T_toafni doesn't actually know how the user chose to reconstruct the images (e.g., 64x64 acquisition could be reconstructed to 128x128 image). The information given here is the minimal amount needed for AFNI to compute how many bytes in the data channel go with each image. This MUST be present here so that AFNI can read and buffer image data from the data channel. If the image source program knows ALL the information that AFNI needs, then there is no need for the info program. In such a case, all the command strings for AFNI can be collected into one big string (with '\n' line separators and the usual '\0' terminator) and sent to AFNI just before the first image data. This "Do it all at once" approach (MUCH simpler than using an info program to get the command strings) would require some small changes to routine AFNI_send_image() in ep_afni.c. "Do it all at once" is the approach taken by the realtime simulation program rtfeedme.c, which will take an AFNI dataset apart and transmit it to the realtime plugin. If the "Do it all at once" option is not practical, then an alternative info program to 3T_toafni must be developed for each new scanner+computer setup. Note that the info program writes its output command strings to stdout, which will be captured by AFNI. After the initial command information is sent down the data channel, everything that follows down the data channel must be raw image information - no more commands and no headers. For example, if you have 64x64 images of shorts, then each set of 8192 bytes (after the terminal '\0' of the initial command string) is taken as an image. If an info program was specified on the 7954 socket, then it will be run by AFNI (in a forked sub-process) at this time. Until it completes, AFNI will just buffer the image data it receives, since it doesn't know how to assemble the images into 3D volumes (e.g., it doesn't know the number of slices). When the data channel connection is closed (usually because the image source program exits), then AFNI will write the new dataset to disk. This is why there is no command to AFNI to tell it how many volumes to acquire - it will just add them to the dataset until there is no more data. AFNI will then start to listen on the TCP/IP 7954 port for another control connection, so it can acquire another dataset. ** If you want to start a new acquisition WITHOUT shutting down the data channel connection, there is a hack-ish way to do so. THe way the plugin is written, it reads an entire image's (2D or 3D) worth of data whenever it can get it. If the first 30 bytes of this data is the ASCII string "Et Earello Endorenna utulien!!" (without the quotes), then this is a marker that the acquisition is over, the datasets are to be saved, and the data channel is to be made ready for a new set of AFNI command strings that describe the next realtime acquisition. It is important to note that when you send this "end of acquisition" marker string, that an entire image's worth of data must be sent, even though only the first 30 bytes matter. ====================== Hosts that AFNI trusts ====================== AFNI checks the incoming IP address of socket connections to see if the host is on the "trust list". The default trust list is 141.106.106 = any MCW Biophysics computer (we're very trustworthy) 127.0.0.1 = localhost 192.168 = private class B networks (this is a reserved set of addresses that should not be visible to the Internet) You can add to this list by defining the environment variable as in the example below (before starting AFNI): setenv AFNI_TRUSTHOST 123.45.67 This means that any IP address starting with the above string will be acceptable. If you want to add more than one possibility, then you can also use environment variables AFNI_TRUSTHOST_1, AFNI_TRUSTHOST_2, up to AFNI_TRUSTHOST_99. (That should be enough - how trusting do you really want to be?) If you want to remove the builtin trust for MCW Biophysics, you'll have to edit file thd_trusthost.c. Note that while AFNI also makes uses of NIML_TRUSTHOST_* variables, plug_realtime does not. You cannot use hostnames for this purpose - only actual IP addresses in the dotted form, as shown above. (What I'll do when IPv6 becomes widely used, I don't know. Yet.)