{ TCL/TK Command Line Implementations of Pascal Routines Copyright (C) 2004-2008 Kevan Hashemi, hashemi@brandeis.edu, Brandeis University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. } program lwdaq; { lwdaq is the interface between our Pascal libraries and TCL/TK. It provides init_Lwdaq, which TCL/TK calls when it loads the lwdaq dynamic library. init_Lwdaq installs TCL commands, each of which has a name beginning with lwdaq in lower-case letters. The lower-case letters distinguish these commands from those that we define in TCL/TK scripts, which have names that begin with LWDAQ. This is a program instead of a unit, even though we compile it into a dynamic library. The GPC compiler expects a main program if it is to include the _p_initialize routine in the compiled object. We will need this routine to be present in the lwdaq.o object when we link the final lwdaq.so dynamic library with GCC. For a list of routines registered with TCL by this library, scroll down to lwdaq_init. At the top of each command-line function declaration you will find a comment in braces that describes the function. This comment will be extracted from lwdaq.pas automatically by our Command Maker script, and inserted into an HTML document. The comments appear as they are, in the HTML manual, and so include their own HTML tags, and even anchors. } uses utils,images,transforms,image_manip,rasnik,spot, tcltk,electronics,bcam,wps,shadow; const version_num='7.3'; package_name='lwdaq'; { initialize_pascal starts up the run-time library and calls the initialization routines of all units. The routine is in the pascal run-time library, libgpc.a. } procedure initialize_pascal (argc:integer;argv,envp:pointer); external name '_p_initialize'; { initialize_main is a procedure provided by the pascal compiler. A call to this procedure initializes all the units. } procedure initialize_main; external name '_p__M0_init'; { finalize_pascal shuts down the run-time library. We don't use it at all in our current code, but we include it here in case we use it in some later version. We used it in an early version of this shared library. } procedure finalize_pascal; external name '_p_finalize'; { The following variables we use to implement the utils gui routines for analysis procedures. } var gui_photo_name:short_string='none'; gui_zoom:integer=1; gui_intensify:short_string='exact'; gui_text_name:short_string='stdout'; gui_interp_ptr:pointer=nil; gui_wait_ms:integer=-1; { The following long string variable allows us to dispose of a long string before we copy it into the TCL results string. We copy the dynamically-allocated long string into this static variable, dispose of the dynamic long string, and then copy the long string out of the static variable. If we don't do this, then we run into trouble when we turn on our pointer-tracking. The pointer tracking uses Tcl_Eval and Tcl_PutMessage to execute print commands in the TCL interpreter. These print commands set the TCL results string to an empty string, cancelling our execution of Tcl_SetReturnLongString. } var lwdaq_long_string:long_string; { lwdaq_gui_draw draws the named image into the TK photo named gui_photo_name. The routine calls lwdaq_draw, which, like all the lwdaq TclTk commands, clears the global error_string. We save the initial value of error_string so we can restore it after the update. This restoration means we can call lwdaq_gui_draw anywhere in our code without deleting the existing error_string. } procedure lwdaq_gui_draw(s:short_string); var c,saved_error_string:short_string;error:integer; begin if (gui_photo_name<>'none') and (gui_interp_ptr<>nil) then begin saved_error_string:=error_string; c:=' lwdaq_draw '+s+' '+gui_photo_name +' -intensify '+gui_intensify +' -zoom '+string_from_integer(gui_zoom,0); error:=Tcl_Eval(gui_interp_ptr,c); c:='LWDAQ_update'; error:=Tcl_Eval(gui_interp_ptr,c); error_string:=saved_error_string; end else default_gui_draw(s); end; { lwdaq_gui_wait pauses for gui_wait_ms milliseconds. If gui_wait_ms is -1, the routine opens a window and asks the user to press the button before returning. } procedure lwdaq_gui_wait(s:short_string); var c:short_string;error:integer; begin if (gui_interp_ptr<>nil) then begin if (gui_wait_ms>=0) then writestr(c,'LWDAQ_wait_ms ',gui_wait_ms:1) else writestr(c,'LWDAQ_button_wait "',s,'"'); error:=Tcl_Eval(gui_interp_ptr,c); end; end; { lwdaq_gui_support passes control to the graphical user interface to perform support for display updates and mouse clicks. } procedure lwdaq_gui_support(s:short_string); var c:short_string;error:integer; begin if (gui_interp_ptr<>nil) then begin writestr(c,'LWDAQ_support'); error:=Tcl_Eval(gui_interp_ptr,c); end; end; { lwdaq_gui_writeln writes a string to a text device using the LWDAQ_print routine. The LWDAQ_print routine accepts file names, text widget names, and the names of the standard output (stdout) and standard error (stderr) channels. The name used by lwdaq_gui_writeln is the name stored in the global lwdaq_text_name variable. } procedure lwdaq_gui_writeln(s:short_string); var c:short_string;error:integer; begin c:='LWDAQ_print '+gui_text_name+' "'+s+'"'; error:=Tcl_Eval(gui_interp_ptr,c); if (error<>Tcl_OK) then begin c:='puts stderr "ERROR: Trying to print to '+gui_text_name+'"'; error:=Tcl_Eval(gui_interp_ptr,c); end; end; {
lwdaq_config sets global variables that control the operation of the lwdaq analysis libraries. If you specify no options, lwdaq_config returns a string giving you the current values of all the options, except the -eol option. Each option requires a value, which will be assigned to the global variable names in the option. Here are the options and their expected value types. Boolean variables you specify with 0 for false and 1 for true.
| -stdout_available | Boolean | standard output channel is available |
| -stdin_available | Boolean | standard input channel is available |
| -track_ptrs | Boolean | track memory allocation |
| -text_name | String | text window in which to print messages |
| -photo_name | String | photo in which to draw images and graphs |
| -zoom | Integer | display zoom for images |
| -intensify | String | intensification type for images, none, mild, strong, or exact. |
| -wait_ms | Integer | milliseconds to pause during lwdaq_gui_wait |
| -gamma_correction | Real | image drawing gamma correction |
| -fsr | Integer | field size for real numbers returned in strings. |
| -fsd | Integer | decimal places for real numbers returned in strings. |
| -eol | String | end of line characters for text windows and files. |
| -append_errors | Boolean | Append errors to the global error string instead of over-writing with latest error. |
The analysis routines can write to TK text windows, and draw in TK photos through -text_name and -photo_name. During execution, they can pause to allow you to view the intermediate results for -wait_ms millisconds. If you set -wait_ms to -1, TK will open a window with a Continue button in it, which you must click before the analysis proceeds. The gamma correction sets the gray scale image display gamma correction used by lwdaq_draw.
Many routines return real numbers in strings. These real numbers will have a fixed number of decimal places equal to the global Pascal variable fsd and a total field size equal to the global Pascal variable fsr.
The global error_string variable is used by all the command routines in lwdaq.pas. Each command routine resets error_string and checks it when it's finished. If error_string is not empty, the routine will return an error condition and error_string will be its result. The append_errors option tells the analysis library to append new errors to error_string instead of over-writing previous errors with the new error. By default, append_errors is false.
} function lwdaq_config(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; vp:pointer; begin error_string:=''; lwdaq_config:=Tcl_Error; if (not odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_config ?option value?".'); exit; end; if argc=1 then begin Tcl_SetReturnShortString(interp, ' -stdout_available '+string_from_boolean(stdout_available) +' -stdin_available '+string_from_boolean(stdin_available) +' -append_errors '+string_from_boolean(append_errors) +' -track_ptrs '+string_from_boolean(track_ptrs) +' -text_name '+gui_text_name +' -photo_name '+gui_photo_name +' -zoom '+string_from_integer(gui_zoom,0) +' -intensify '+gui_intensify +' -wait_ms '+string_from_integer(gui_wait_ms,0) +' -gamma_correction '+string_from_real(gamma_correction,0,1) +' -fsr '+string_from_integer(fsr,0) +' -fsd '+string_from_integer(fsd,0)); end else begin arg_index:=1; while (arg_indexlwdaq_image_create creates a new image and returns a unique name for the image, by which the interpreter can identify the image to other lwdaq routines.
| Option | Function | |
|---|---|---|
| -name | Specify the name for the image. | |
| -results | Set the image results string. | |
| -width | The width of the image in pixels. | |
| -height | The height of the image in pixels | |
| -data | Pixel intensity values as a binary array of bytes. | |
| -left | Left column of analysis bounds. | |
| -right | Right column of analysis bounds. | |
| -top | Topm row of analysis bounds. | |
| -bottom | Bottom row of analysis bounds. | |
| -try_header | Try the image data for a legitimate lwdaq-format header. |
The above table lists the options accepted by lwdaq_image_create, and their functions. If you use the -name option and provide the name of a pre-existing image in the lwdaq image list, lwdaq_image_create deletes the pre-existing image. If you specify "-data $value", the routine copies $value into the image's intensity array, starting at the first pixel of the first row. When you combine "-data $value" with "-try_header 1", the routine looks at the first bytes in $value to see if it contains a valid image header, specifying image width and height, as well as analysis bounds and a results string. When the routine looks for the header, it assumes that the bytes in the header specify two-byte integers in big-endian order.
If you have -try_header 0, or if the routine's effort to find a header fails, lwdaq_image_create will look at the values you specify for the analysis bounds with the -left, -top, -right, and -bottom options. A value of −1 directs the routine to place the boundary at the edge of the image. The default values for these options are all −1.
} function lwdaq_image_create(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; const max_side=10000; min_side=10; var option:short_string; arg_index:integer; width,height,data_size,copy_size:integer=-1; left,right,top,bottom:integer=-1; try_header:boolean=false; ihp:image_header_ptr_type; data_obj,data_ptr:pointer=nil; name:short_string=''; results:short_string=''; ip:image_ptr_type=nil; vp:pointer=nil; char_index:integer; q:integer; begin error_string:=''; lwdaq_image_create:=Tcl_Error; if (argc<3) or (not odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_image_create option value ?option value?".'); exit; end; arg_index:=1; while (arg_indexlwdaq_draw transfers the named image into the named TK photo. You pass the lwdaq image name followed by the tk photo name, and then your options in the form ?option value?. When the routine draws the image, it over-writes the first few pixels in the first image row with a header block containing the image dimensions, its analysis bounds, and its results string.
The -intensify option can take four values: mild, strong, exact, and none. Mild intensification displays anything darker than four standard deviations below the mean intensity as black, and anything brighter than four standard deviations above the mean intensity as white. In between black and white the display is linear with pixel brightness. Strong intensification does the same thing, but for a range of two standard deviations from the mean. Exact displays the darkest spot in the image as black and the brightest as white. In all three cases, we calculate the mean, standard deviation, minimum, and maximum intensity of the image within the analysis bounds, not across the entire image.
The -zoom option scales the image as we draw it in the TK photo. If the TK photo is initially smaller than the size required by the zoomed image, the TK photo will expand to accommodate the zoomed image. But if the TK photo is initially larger than required, the TK photo will not contract to the smaller size of the zoomed image. The -zoom option can take any value between 0.1 and 10. But the effective value of -zoom is dicated by the requirements of sub-sampling. If -zoom is greater than 1, we round it to the nearest integer, e, and draw each image pixel on the screen as a block of e×e pixels. If -zoom is less than 1, we round its inverse to the nearest integer, c. We draw only one pixel out of every c pixels in the TK photo. If -zoom = 0.3, we draw every third pixel. If -zoom = 0.4, we draw every third pixel if your computer rounds 1/0.4 to 3, or every second pixel if your computer rounds 1/0.4 to 2.
With -clear set to 1, lwdaq_draw clears the overlay in the lwdaq image before drawing in the TK photo. The overlay may contain a graph or oscilloscope display, or analysis indicator lines. If you don't want these to be displayed, set -clear to 1. But note that whatever was in the overlay will be lost.
By default, -show_bounds is 1, and the routine draws a blue rectangle to show the the image analysis boundaries, which are used by image analysis routines like lwdaq_rasnik and lwdaq_bcam. But with -show_bounds set to 0, this blue rectangle is not drawn. If you want to be sure that you don't have a blue rectangle drawn over your gray-scale image, you should also specify -clear 1, so that lwdaq_draw will clear the image overlay of any pre-existing blue rectangles.
} function lwdaq_draw(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; const min_zoom=0.1; max_zoom=10; var option:short_string; arg_index,char_index:integer; width,height,data_size:integer=0; image_name,photo_name,intensify:short_string=''; ip:image_ptr_type=nil; zoom:real=1; vp,ph:pointer=nil; pib:Tk_PhotoImageBlock; subsampleX,subsampleY,zoomX,zoomY:integer; draw_width,draw_height:integer; clear:boolean=false; show_bounds:boolean=true; saved_bounds:ij_rectangle_type; begin error_string:=''; lwdaq_draw:=Tcl_Error; if (argc<3) or (not odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_draw image photo ?option value?".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); photo_name:=Tcl_ObjShortString(argv[2]); arg_index:=3; while (arg_indexlwdaq_image_contents returns a byte array containing the intensity array from the named image. In the first line of the image the routine records the image dimensions, analysis boundry, and results string. The integers are two-bytes long, and we use big-endian byte ordering, so the high-order byte is first.
If you specify -truncate 1, the routine removes all trailing zero-bytes from the data. When we create a new image to accomodate the same data later, we clear the image intensity array before we copy in the new data, so the image is re-constructed faithfully. This truncation is effective at reducing the size of data files from instruments that don't fill the intensity array with real data, but instead use the intensity array as a place to store one-dimensional data, and use the overlay as a white-board upon which to render the data (like the Voltmeter). If you specify -data_only 1, the routine chops off the leading row of data, leaving only the data from the first pixel of the first row onwards, which is the block of data operated upon by our lwdaq_data_manipulate routines. If you specify -record_size larger than 1, the routine makes sure that the size of the block it returns is divisible by the record size.
} function lwdaq_image_contents(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; image_name:short_string=''; ip,cip:image_ptr_type; vp:pointer; char_index,i,j,ci,cj:integer; truncate,data_only:boolean=false; copy_size:integer=0; record_size:integer=1; cp:pointer; begin error_string:=''; lwdaq_image_contents:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_image_contents image".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; arg_index:=2; while (arg_indexlwdaq_image_destroy disposes of an image. You can specify multiple images, or image name patterns with * and ? wild cards. You can enter multiple image names on the command line, too.
} function lwdaq_image_destroy(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; image_name:short_string=''; vp:pointer; begin error_string:=''; lwdaq_image_destroy:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_image_destroy image".'); exit; end; for arg_index:=1 to argc-1 do begin image_name:=Tcl_ObjShortString(argv[arg_index]); dispose_named_images(image_name); end; if error_string<>'' then Tcl_SetReturnShortString(interp,error_string); lwdaq_image_destroy:=Tcl_OK; end; {lwdaq_photo_contents returns a byte array containing gray-scale intensity array corresponding to a tk photo. The routine uses the red intensity as the gray-scale intensity, which will work in a purely gray-scale image, and assumes that the red intensity is an 8-bit number.
The routine embeds the image dimensions in the first four pixels of the image by over-writing them with j_size-1 and i_size-1 each as two-byte integers in big-endian format. If the image is one that has been previously stored or drawn by lwdaq routines, the first twelve pixels of the first line will already contain the image dimensions, plus the analysis boundaries, all encoded as two-byte big-endian integers. Because the routine already knows for sure what the image dimensions are, it over-writes dimensions in the first row. But it does not over-write the analysis boundaries. These may be correct or incorrect. You can pass this routine's result to lwdaq_image_create, and have the image-creating routine check the first twelve bytes for valid analysis bounds, or ignore these bounds and use newly-specified bounds.
To assemble the 8-bit gray-scale image, the routine uses the lwdaq scratch image. If the routine were to allocate and dispose of an image, the printing activity of the disposal when -track_ptrs is set to 1 would alter the TCL return string.
} function lwdaq_photo_contents(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; photo_name:short_string=''; ip:image_ptr_type; vp,ph:pointer=nil; ihp:image_header_ptr_type=nil; char_index:integer; pib:Tk_PhotoImageBlock; i,j,r:integer=0; pp:^intensity_pixel_type; begin error_string:=''; lwdaq_photo_contents:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_photo_contents photo".'); exit; end; photo_name:=Tcl_ObjShortString(argv[1]); ph:=Tk_FindPhoto(interp,photo_name); if ph=nil then begin Tcl_SetReturnShortString(interp,'Photo "'+photo_name+'" does not exist.'); exit; end; r:=Tk_PhotoGetImage(ph,@pib); with pib do begin dispose_named_images(scratch_image_name); ip:=new_image(height,width); ip^.name:=scratch_image_name; pp:=pointer(pixelptr); for j:=0 to height-1 do begin for i:=0 to width-1 do begin ip^.intensity[j,i]:=pp^; pp:=pointer(cardinal(pp)+pixelSize); end; end; end; with ip^ do begin ihp:=pointer(@intensity); ihp^.i_max:=big_endian_from_local_shortint(i_size-1); ihp^.j_max:=big_endian_from_local_shortint(j_size-1); end; if error_string='' then Tcl_SetReturnByteArray(interp,@ip^.intensity,sizeof(ip^.intensity)) else Tcl_SetReturnShortString(interp,error_string); lwdaq_photo_contents:=Tcl_OK; end; {lwdaq_image_characteristics returns features of the image: the left, top, right, and bottom edges of the analysis boundries, the average, standard deviation, maximum, and minimum values of intensity, and the height and width of the image.
} function lwdaq_image_characteristics(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var image_name,result:short_string=''; ip:image_ptr_type; vp:pointer; begin error_string:=''; lwdaq_image_characteristics:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_image_characteristics image".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; with ip^.analysis_bounds do writestr(result,left:1,' ',top:1,' ',right:1,' ',bottom:1,' ', image_average(ip):3:1,' ',image_amplitude(ip):3:1,' ', image_maximum(ip):3:1,' ',image_minimum(ip):3:1,' ', ip^.j_size:1,' ',ip^.i_size:1); if error_string='' then Tcl_SetReturnShortString(interp,result) else Tcl_SetReturnShortString(interp,error_string); lwdaq_image_characteristics:=Tcl_OK; end; {lwdaq_image_histogram returns a histogram of image intensity within the analysis bounds of an image. The histogram takes the form of an x-y graph in a space-delimited string, with the x-coordinate representing intensity, and the y-coordinate representing frequency. Suppose we apply the histogram routine to a 20×20 image and we assume that the pixel intensities range from 0 to 3. The string "0 100 1 210 2 40 3 50" confirms that there are 400 pixels in the image, 100 with intensity 0, 210 with intensity 1, and so on.
} function lwdaq_image_histogram(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var image_name:short_string=''; lsp:long_string_ptr; hp:xy_graph_ptr_type; ip:image_ptr_type; vp:pointer; i:integer; begin error_string:=''; lwdaq_image_histogram:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_image_histogram image".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; hp:=image_histogram(ip); lsp:=new_long_string; lsp^:=''; for i:=0 to hp^.num_points-1 do writestr(lsp^,lsp^,hp^[i].x:1:0,' ',hp^[i].y:fsr:fsd,' '); dispose_xy_graph(hp); lwdaq_long_string:=lsp^; dispose_long_string(lsp); if error_string='' then Tcl_SetReturnLongString(interp,lwdaq_long_string) else Tcl_SetReturnShortString(interp,error_string); lwdaq_image_histogram:=Tcl_OK; end; {lwdaq_image_profile returns a list of the average intensity in the analysis boundaries along the row or column directions. The profile takes the form of series of numbers in a space-delimited decimal string. The first number of a row profile is the average intensity of pixels in the leftmost column of the analysis boundaries. The last number is the average intensity of the right-most column. The first number of a column profile is the average intensity of the topmost row in the analysis boundaries. The last number is the average intensity of the bottom row. To obtain the row profile, use option -row 1, which is the default. To obtain the column profile, use -row 0.
} function lwdaq_image_profile(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; image_name:short_string=''; lsp:long_string_ptr; pp:x_graph_ptr_type; ip:image_ptr_type; vp:pointer; i:integer; row:boolean=true; begin error_string:=''; lwdaq_image_profile:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_image_profile image".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; arg_index:=2; while (arg_indexlwdaq_image_manipulate returns the name of a new image derived from one or more images passed to lwdaq_image_manipulate. If we set the -replace option to 1, the routine replaces the original image with the new image. The command takes the name of an image in the LWDAQ image list, and the name of a manipulation to be performed upon this image. The currently-supported manipulations are as follows.
| Manipulation Code | Function |
|---|---|
| none | No manipulation of pixels, the new image is the old image. |
| invert | Turn image upside-down by reversing order of pixels. Top-left becomes bottom-right. |
| crop | Crim the image to its analysis boundaries. |
| reverse_rows | Reverse the order of the rows. The top row becomes the bottom row. |
| soec | Swap odd and even columns. This routine corrects some platform-translation errors. |
| grad_i | Magnitude of the horizontal intensity derivative. |
| grad_i_s | Horizontal intensity derivative, signed. |
| grad_j | Magnitude of the vertical intensity derivative. |
| grad_j_s | Vertical intensity derivative, signed. |
| grad | Magnitude of the intensity gradient. |
| negate | Negate the image. Each pixel have value max_intensity-original_intensity. |
| smooth | Smooth with 3×3 box filter and add contrast. |
| copy | Copy the image into a new image. |
| combine | Replaces a portion of the image. |
| subtract | Subtract a second image from the first image. |
| subtract_row | Subtract the row average intensity from all pixels in each row. |
| bounds_subtract | Subtract a second image from the first image within analysis bounds. |
| rasnik | Draw a rasnik pattern in the image. |
The none manipulation does nothing. It does not return a new image. Instead, the none manipulation allows us to manipulate an existing image's analysis boundaries, result string, and overlay pixels.
The combine manipulation allows you to write over the data in an image, starting with the offset'th pixel. You specify offset after the data. The manipulation copies the entire contents of an m-byte binary block into the image, starting at pixel offset, and ending at pixel offset+m-1. If the copy goes past the end of the image array, the manipulation aborts without doing anything, and returns an error.
The crop manipulation extracts the pixels inside the analysis boundaries of the original image, and creates a new image containing only these pixels. The dimensions of the new image will be those of the original analysis boundaries, but with one extra row at the top to accommodate an image header when we save to disk. The new analysis boundaries will include the entire image except for row zero.
The subtract manipulation requires you to name a second image, which will be subtracted from the first to create a third image. The two images must have the same dimensions. All pixels in the second images will be subtracted from the first image. The third image, being the difference, will be the same dimensions as the first two.
The bounds_subtract manipulation is like subtract, but applies the subtraction only within the analysis bounds of the first image. Elsewhere, the difference image is equal to the first image.
The subtract_row manipulation does not require a second images. It is a type of filter function, whereby we subtract the average intensity of each row from the pixels in the row. We thus remove a slope of intensity from top to bottom in the image.
The grad manipulations either return an absolute intensity gradient or a signed intensity gradient. We calculate the horizontal gradient at pixel (i,j) by subtracting the intensity of pixel (i-1,j) from that of pixel (i+1,j). The vertical gradient is (i,j+1) minus (i,j-1). When we return the magnitude of the gradient, the intensity of the gradient image is simply the absolute value of the gradient. When we return the signed gradient, we offset the gradient image intensity by mid_intensity, which is 128 for eight-bit gray scale images. Thus an intensity of 128 means zero gradient, and an intensity of 138 means +10. When the gradient exceeds 127 or -128, we clip its value to 255 and 0 respectively. For more details, see the image_filter and subsequent routine in image_manip.pas.
The rasnik manipulation draws a rasnik pattern in the image. We specify the rasnik pattern with a string of six numbers: origin.x, origin.y, pattern_x_width, pattern_y_width, rotation, and sharpness. The origin is the image coordinates of the top-left corner of one of the squares in the chessboard. Units are pixels, not mircons. The x and y width of the squares are in the near-horizontal and near-vertical direction respectively. Units are pixels again. The rotation is counter-clockwise in milliradians of the pattern with respect to the sensor. With sharpness 1, the pattern has sinusoidal intensity variation from black to white. With sharpness less than 1, the amplitude of the sinusoidal variation decreases in proportion. With sharpness greater than one, the sinusoidal amplitude increases in proportion, but is clipped to black and white intensity, so that we obtain a sharply-defined chessboard.
lwdaq_image_manipulate image_name rasnik "0 0 20 30 2 10"
In the above example, the image would receive a rasnik pattern with origin at the top-left corner of the top-left pixel in the image, each square 20 pixels wide and 30 pixels high, rotated by 2 mrad anti-clockwise, with sharp edges.
| Option | Function |
|---|---|
| -name | The name of the new image will be $value. |
| -results | Set the new image results string to $value. |
| -replace | If $value is 1, delete the original image and replace it with the new one. 0 by default. |
| -clear | if $value is 1, clear overlay of final image, 0 by default. |
| -fill | if $value is 1, fill overlay of final image with white, 0 by default. |
| -paint | paint the analysis bounds with color number $value. |
| -bottom | Set the bottom of the analysis bounds to $value. |
| -top | Set the top of the analysis bounds to $value. |
| -left | Set the left of the analysis bounds to $value. |
| -right | Set the rigth of the analysis bounds to $value. |
With -name you specify the name of the new image created by the manipulation, or the existing image if there is no new image created by the manipulation. Any pre-existing images with this name will be destroyed before the name change occurs.
With -replace 0, the manipulation creates a new image and returns its name. With -replace 1, the manipulation over-writes data in the old image and returns the old image name.
The -paint option instructs lwdaq_image_manipulate to paint the entire area within the analysis bounds with the color given by $value. This value should be a number between 0 and 255. The value 0 is for transparant. Other than the 0-value, the number will be treated like an eight-bit RGB code, with the top three bits for red, the middle three for green, and the bottom three for blue. Thus $E0 (hex E0) is red, $1C is green, and $03 is blue.
In addition to the pixel manipulations, we also have options to change other secondary properties of the image. The table above shows the available manipulation options, each of which is followed by a value in the command line, in the format ?option value?.
When you specify the analysis bounds, a value of −1 is the code for "do nothing". The boundary will remain as it was. This use of the −1 code contasts with that of lwdaq_image_create, where −1 directs lwdaq_image_create to move the boundary to the edge of the image.
} function lwdaq_image_manipulate(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; image_name,second_image_name:short_string=''; name,result:short_string=''; manipulation:short_string='none'; results:short_string=null_code; left,right,top,bottom,paint:integer=-1; replace:boolean=false; clear,fill:boolean=false; ip,nip,ip_2:image_ptr_type; data_obj,data_ptr:pointer=nil; vp:pointer; data_size,offset:integer=-1; begin error_string:=''; lwdaq_image_manipulate:=Tcl_Error; { This routine needs at least three arguments: the routine name, the image name, and the manipulation name. } if (argc<3) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, must be ' +'"lwdaq_image_manipulate image_name manipulation ?option value?".'); exit; end; { Get the image name and manipulation name. } arg_index:=1; image_name:=Tcl_ObjShortString(argv[arg_index]); inc(arg_index); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; manipulation:=Tcl_ObjShortString(argv[arg_index]); inc(arg_index); { Perform the specified manipulation. } if manipulation='copy' then nip:=image_copy(ip) else if manipulation='grad_i' then nip:=image_grad_i(ip) else if manipulation='grad_i_s' then nip:=image_filter(ip,-1,0,1,0,1,0,1) else if manipulation='grad_j' then nip:=image_grad_j(ip) else if manipulation='grad_j_s' then nip:=image_filter(ip,0,1,0,-1,0,1,1) else if manipulation='grad' then nip:=image_grad(ip) else if manipulation='smooth' then nip:=image_filter(ip,1,1,1,1,1,1,0) else if manipulation='negate' then nip:=image_negate(ip) else if manipulation='invert' then nip:=image_invert(ip) else if manipulation='crop' then nip:=image_crop(ip) else if manipulation='reverse_rows' then nip:=image_reverse_rows(ip) else if manipulation='soec' then nip:=image_soec(ip) else if manipulation='subtract_row' then nip:=image_subtract_row_average(ip) else if (manipulation='subtract') or (manipulation='bounds_subtract') then begin if argclwdaq_data_manipulate operates upon the data in an image, and we intend it for use with instruments that store one-dimensional arrays of data in an image's intensity array. Our convention, when using the intensity array in this way, is to start storing data in the first column of the second row. This leaves the first row free for header information when we store the image to disk. We refer to the block of memory starting with the first byte of the second row, and ending with the last byte of the last row, as the data space. We specify bytes in the data space with their byte address, which is zero at the first byte in the data space. The routine does not return a text string. It either returns an error or an empty string. The data manipulations alter the existing image.
| Manipulation | Function |
|---|---|
| write | Writes a block of data into the data space. |
| read | Reads a block of data from the data space. |
| shift | Shifts data towards start of data space. |
| clear | Clears the data. |
| none | No action. |
The write function requires two parameters: the data you wish to write to the data space, and the byte address at which you want the first byte of your data to be written. The following command writes the contents of data to the data space of the image named image_name starting at the first byte in the data space (which is the first pixel in the second row).
lwdaq_data_manipulate image_name write 0 $data
The read function requires two parameters: the number of bytes you wish to read from the data space, and the byte address at which you want to start reading. The following command reads 10000 bytes starting at byte address 100. If the image has 100 pixels per row, the first byte the routine reads will be the first pixel in the third row of the image.
lwdaq_data_manipulate image_name read 100 10000
The shift function requires one parameter: the number of bytes to the left by which you want the data to be shifted. Shifting to the left is in the direction of the start of the data space. If you specify a negative shift, the routine shifts the data to the right, in the direction of the end of the data space.
The clear function takes no parameters. It clears all the byte in the data space to zero.
} function lwdaq_data_manipulate(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option:short_string; arg_index:integer; image_name:short_string=''; manipulation:short_string='none'; ip,nip:image_ptr_type; data_obj,data_ptr:pointer=nil; vp:pointer; data_size,byte_address:integer=-1; shift:integer=0; begin error_string:=''; lwdaq_data_manipulate:=Tcl_Error; { This routine needs at least three arguments: the routine name, the image name, and the manipulation name. } if (argc<3) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, must be ' +'"lwdaq_data_manipulate image_name manipulation ?parameters?".'); exit; end; { Get the image name and manipulation name. } arg_index:=1; image_name:=Tcl_ObjShortString(argv[arg_index]); inc(arg_index); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; manipulation:=Tcl_ObjShortString(argv[arg_index]); inc(arg_index); { Perform the specified manipulation. } if manipulation='write' then begin if argclwdaq_rasnik analyzes rasnik images. Specify the image with -image_name as usual. The routine clears the image overlay for its own use. The routine takes the following options, each of which you specify by giving the option name followed by its value, ?option value?. See the Rasnik Instrument for a description of the options.
| Option | Function |
|---|---|
| -reference_code | Selects the analysis reference point. |
| -reference_x_um | x-coordinate of reference point when -reference_code=3. |
| -reference_y_um | y-coordinate of reference point when -reference_code=3. |
| -orientation_code | Selects the analysis orientation code. |
| -square_size_um | Tells the analysis the mask square size (assumed square). |
| -pixel_size_um | Tells the analysis the pixel size (assumed square) |
| -show_timinig | If 1, print timing report to gui text window. |
| -show_fitting | If <> 0, show fitting stages with delay $value ms. |
| -pattern_only | If 1, return pattern description not rasnik measurement. |
See the Rasnik Instrument Manual for more information about the option values, in particular the reference and orientation code meanings.
With the -pattern_only option set, the routine returns a description of the chessboard pattern it finds in the image. The result string contains seven numbers: origin.x, origin.y, pattern_x_width, pattern_y_width, rotation, error, and extent. The origin values are the image coordinates of the top-left corner of one of the squares in the chessboard. Units are pixels, not mircons. The next two numbers are the width of the squares in the near-horizontal direction and their width in the near-vertical direction. Units are again pixels. The rotation is counter-clockwise in milliradians. The error is an estimate of the fitting accuracy in pixel widths. The extent is the number of squares from the image center over which the pattern extends.
} function lwdaq_rasnik(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; const rnw=8;rnd=3; var ip,iip,jip:image_ptr_type=nil; image_name,result:short_string=''; pp:rasnik_pattern_ptr_type=nil; option:short_string; arg_index:integer; vp:pointer; show_fitting,show_timing,pattern_only:boolean=false; square_size_um:real=120; pixel_size_um:real=10; reference_code,orientation_code:integer=0; rp:rasnik_ptr_type; reference_x_um,reference_y_um:real=0; begin error_string:=''; lwdaq_rasnik:=Tcl_Error; if (argc<2) or (odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_rasnik image ?option value?".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; arg_index:=2; while (arg_index| Option | Function |
|---|---|
| -num_spots | The number of spots the analysis should find. |
| -threshold | Criteria for finding spots, including threshold specification. |
| -color | Color for spot outlining in overlay, default red. |
| -pixel_size_um | Tells the analysis the pixel size (assumed square) |
| -show_timinig | If 1, print timing report to gui text window. |
| -show_pixels | If 1, mark pixels above threshold. |
| -analysis_type | Selects the centroid, ellipse, or line finder. |
| -sort_code | Selects the analysis type. |
| -return_bounds | If 1, return spot bounds instead of position. |
| -return_intensity | If 1, return spot intensity instead of position. |
The lwdaq_bcam routine makes a list of spots in the image. The -threshold string tells lwdaq_bcam how to distinguish background pixels from spot pixels. At the very least, the -threshold string must specify a threshold intensity, or a means of calculating a threshold intensity. All the spot-locating routines called by lwdaq_bcam use the net intensity of pixels, which is the image intensity minus the threshold intensity, with negative values are clipped to zero.
The -threshold string must begin with an integer, t. After t there can be a non-numerical character, which we call the threshold symbol. If there is no threshold symbol, the routine assumes the "*" symbol has been omitted from the string. The "*" symbol tells the routine to use t directly as the intensity threshold. The string "20 *" means the threshold is pixel intensity 20. When combined with the "*" symbol, the threshold must be between 0 and 255 for eight-bit images.
The "%" symbol means that the number is a percentage. The threshold is the minimum image intensity plus a precentage of the difference between the maximum and minimum intensities. The "#" symbol means that the threshold will be the average intensity plus a percentage of the difference between the maximum and average intensities. The "$" symbol means the threshold is a certain number of counts above the average intensity.
Following the symbol or threshold we have two optional parameters that restrict the routine's choice of spots. The first parameter must be an integer. It specifies the required number of pixels above threshold in a spot for acceptance by the routine. Any spots with fewer than this number will be ignored. Their pixels will be marked white in the overlay.
The second parameter must be a real number. It specifies the maximum eccentricity of the spot, which is the maximum ratio of width to height, or height to width. Spots that have greater eccentricity will be rejected by the routine. The second parameter cannot be included without the first, but if you use 0 for the first, the routine ignores the first parameter and moves on to the second.
The lwdaq_bcam routine identifies all distinct sets of contiguous pixels above threshold, eliminates those that do not meet the test criteria, determines the position and intensity of each of remaining set, sorts them in order of decreasing intensity, and eliminates all but the first -num_spots sets. By default, the routine returns the position of each spot in microns with respect to the top-left corner of the image. To convert from pixels to microns, the routine uses -pixel_size_um, and assumes the pixels are square. There are several ways that lwdaq_bcam can calculate the spot position from the net intensity of its pixels.
spot_use_centroid=1; spot_use_ellipse=2; spot_use_vertical_line=3;
With return_bounds=1, the routine does not return the position of the spots. It returns instead the boundaries around the spots. It chooses the same boundaries that it draws in the image overlay. Each spot boundary is given as four integers: left, top, right, and bottom. The left and right integers are column numbers. The top and bottom integers are row numbers. Each spot gets four numbers, and these make up the result string, separated by spaces.
With return_intensity=1, the routine returns a list of the intensity of the spots found in the image. The intensity of each spot is the sum of net intensity of the pixels in the spot. The net intensity is the intensity minus the threshold. The return_intensity option over-rides the return_bounds option.
With analysis_type=1, which is the default, the position of the spot is the weighted centroid of its net intensity. With analysis_type=2, the routine fits an ellipse to the edge of the spot. The position is the center of the ellipse. With analysis_type=3 the routine fits a straight line to the net intensity of the spot and returns the intersection of this straight line with the top of the CCD instead of x, and the anti-clockwise rotation of this line in milliradians instead of y.
The sort_code has the following meanings, and dictates the order in which the spots are returned in the result string.
spot_decreasing_intensity=1; spot_increasing_x=2; spot_increasing_y=3; spot_decreasing_x=4; spot_decreasing_y=5;
Thus with spot_decreasing_x as the value for sort_code, the routine sorts the num_spots brightest spots in order of decreasing x position, which means spots on the right of the image will appear first in the result string.
With show_pixels=0, the default, the routine draws red boxes around the spots. These boxes are of the same size as the spots, or a little bigger if the spots are small. If num_spots=1, the routine draws a cross centered on the spot instead of a box around it. When show_pxels=1, the routine marks all the pixels in each spot, so you can see the pixels that are above threshold and contiguous.
See the BCAM Instrument Manual for more information about the option values.
} function lwdaq_bcam(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; const rnw=8;rnd=3; var ip,nip:image_ptr_type=nil; image_name,result:short_string=''; option:short_string; arg_index,spot_num:integer; vp:pointer; show_timing,show_pixels,return_bounds,return_intensity:boolean=false; pixel_size_um:real=10; color:integer=red_color; num_spots,analysis_type,sort_code:integer=1; slp:spot_list_ptr_type; spot:spot_type; threshold:short_string='50'; begin error_string:=''; lwdaq_bcam:=Tcl_Error; if (argc<2) or (odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_bcam image ?option value?".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; arg_index:=2; while (arg_indexlwdaq_wps analyzes wps images. It clears the overlay for its own use. We describe the analysis in our WPS1 Manual.
| Option | Function |
|---|---|
| -pixel_size_um | Width and height of image pixels in microns. |
| -reference_um | Location of reference line in microns below top edge of top row. |
| -show_timinig | If 1, print timing report to gui text window, default zero. |
| -show_edges | If 1, show edge pixesls in image, defalut zero |
| -num_wires | The number of wires you want the routine to find. |
| -pre_smooth | Smooth the image before you take the derivative. |
| -threshold | Criteria for finding spots, including threshold specification. |
The -threshold string is used in the same way as in lwdaq_bcam. It can contain an intensity threshold or it can define a means to calculate the threshold. The string can also specify the minimum number of pixels a spot must contain, and its maximum eccentricity. Spots that do not meet these criteria will be marked as invalid. In this case, note that the threshold intensity will be applied to the horizontal gradient of the wire image, not the image itself. With -pre_smooth set to 1, the threshold will be applied to the gradient of the smoothed image.
The wire positions are given with respect to a horizontal reference line drawing reference_um microns down from the top edge of the top image row. With show_edges equal to zero (the default value), the routine plots the image's horizontal intensity profile in green and the derivative profile in yellow. But when you set show_edges to 1, the routine no longer plots these two graphs, but instead displays the spots it finds in the derivative image, overlayed upon the original image. The edges of a wire will be covered with colored pixels. White pixels are ones that were part of spots that did not satisfy the -threshold critera.
} function lwdaq_wps(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; const rnw=8;rnd=3; spots_per_wire=2; var ip,iip,sip:image_ptr_type=nil; image_name,result:short_string=''; option:short_string; arg_index:integer; reference_um:real=0; spot_num,num_spots:integer; vp:pointer; show_timing,show_edges,pre_smooth:boolean=false; pixel_size_um:real=10; num_wires,i,j:integer=1; slp:spot_list_ptr_type; pp:x_graph_ptr_type; saved_bounds:ij_rectangle_type; ref_line:ij_line_type; threshold:short_string='50'; begin error_string:=''; lwdaq_wps:=Tcl_Error; if (argc<2) or (odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_wps image ?option value?".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; arg_index:=2; while (arg_indexlwdaq_calibration takes as input an apparatus measurement and a device calibration, and returns a parameter calculation.
} function lwdaq_calibration(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; const rnw=8;rnd=3; var option:short_string; arg_index:integer; vp:pointer; dev_calib,app_meas,rsult:short_string; verbose,check_spread:boolean=false; begin error_string:=''; lwdaq_calibration:=Tcl_Error; if (argc<3) or (not odd(argc)) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, must be ' +'"lwdaq_image_calibration device_calibration apparatus_measurement' +' ?option value?".'); exit; end; arg_index:=3; while (arg_indexlwdaq_sampler steps through the pixels of an image looking for valid samples from a sampling circuit like the ADC Tester (A2100).
} function lwdaq_sampler(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var ip:image_ptr_type=nil; image_name:short_string=''; lsp:long_string_ptr; command,instruction:short_string=''; error:integer; begin error_string:=''; lwdaq_sampler:=Tcl_Error; if argc<>3 then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_sampler image command".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; command:=Tcl_ObjShortString(argv[2]); lsp:=lwdaq_A2100_sampler(ip,command); lwdaq_long_string:=lsp^; dispose_long_string(lsp); if error_string='' then Tcl_SetReturnLongString(interp,lwdaq_long_string) else Tcl_SetReturnShortString(interp,error_string); lwdaq_sampler:=Tcl_OK; end; {lwdaq_gauge analyzes sixteen-bit adc values by calling lwdaq_A2053_gauge. The routine assumes that two numbers specifying the sample period and the number of channels sampled are saved in the input image's results string. The routine leaves these numbers in the results string after it is done. For each gauge channel in the image, the routine returns a result, according to the result specifiers. With -ave 1, the result for each channel includes the average gauge value. With -stdev 1, the result includes the standard deviation of the gauge value. With both set to zero, the result is an empty string. The default values for ave and stdev are 1 and 0 respectively.
} function lwdaq_gauge(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var ip:image_ptr_type=nil; image_name,result:short_string=''; option:short_string; arg_index:integer; vp:pointer; y_min,y_max,t_min,t_max:real=0; ref_bottom:real=0; ref_top:real=100; ac_couple,stdev:boolean=false; ave:boolean=true; begin error_string:=''; lwdaq_gauge:=Tcl_Error; if (argc<2) or (odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_gauge image ?option value?".'); exit; end; image_name:=Tcl_ObjShortString(argv[1]); ip:=image_ptr_from_name(image_name); if not valid_image_ptr(ip) then begin Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.'); exit; end; arg_index:=2; while (arg_index| Option | Value and Effect |
|---|---|
| -x_min | x at left edge, if 0 with x_max, pick minimum value of x. |
| -x_max | x at right edge, if 0 with x_min, pick maximum value of x. |
| -y_min | y at bottom edge, if 0 with y_max, pick minimum value of y. |
| -y_max | y at top edge, if 0 with y_min, pick maximum value of y. |
| -ac_couple | add average y-value to y_min and y_max. |
| -color | integer code for the color. |
| -clear | if 1, clear image overlay before plotting. |
| -fill | if 1, fill image overlay before plotting. |
| -x_div | if > 0, plot vertical divisions spaced by this amount. |
| -y_div | if > 0, plot horizontal divisions spaced by this amount. |
| -entire | if 1, use entire image for plot, if 0, use analysis bounds. |
The color codes for the graph give 255 unique colors. You can try them out to see which ones you like.
} function lwdaq_graph(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var ip:image_ptr_type=nil; gp,ggp:xy_graph_ptr_type=nil; image_name:short_string=''; option:short_string; arg_index:integer; vp:pointer; x_min,x_max,y_min,y_max:real=0; x_div,y_div:real=0; num_points,point_num:integer=0; lsp:long_string_ptr; color:integer=black_color; clear,entire,fill,ac_couple:boolean=false; x,y:real; saved_bounds:ij_rectangle_type; average:real; begin error_string:=''; lwdaq_graph:=Tcl_Error; if (argc<3) or (not odd(argc)) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be "lwdaq_graph data image ?option value?".'); exit; end; arg_index:=3; while (arg_indexThe lwdaq command acts as an entry point into our analysis libraries, making various math functions available at the TCL command line. You specify the routine you wish to call, and pass arguments to the routine in strings or byte arrays or both. Most routines return results as text strings in which real numbers are encoded in characters with a fixed number of decimal places, as defined by the global constants fsr and fsd. You can set both of these with lwdaq_config. Beware that these routines can round small values to zero. In the comments below, we assume that fsr is 8, and fsd is 6.
} function lwdaq (data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer; var option,result,s:short_string=''; slope,intercept,rms_residual,position,interpolation:real; period,amplitude,offset,average:real; a,b:sinusoid_type; gp:xy_graph_ptr_type; gpx,periods:x_graph_ptr_type; lsp:long_string_ptr; M,N:matrix_ptr; i,num_rows,num_elements:integer; begin error_string:=''; lwdaq:=Tcl_Error; if (argc<2) then begin Tcl_SetReturnShortString(interp, 'Wrong number of arguments, must be: "lwdaq option ?args?".'); exit; end; option:=Tcl_ObjShortString(argv[1]); if option='bcam_from_global_point' then begin {Transforms a point in global coordinates to a point in BCAM coordinates. The point in BCAM coordinates is returned as a string of three numbers, the BCAM x, y, and z coordinates of the point. You specify the point in global coordinates with the point parameter, which also takes the form of a string of three numbers, these numbers being the global x, y, and z coordinates of the point whose BCAM coordinates you want to determine. You specify how the BCAM and global coordinate systems relate to one another with the mount string. The mount string contains the global coordinates of the BCAM's kinematic mounting balls. You specify the coordinates of the cone, slot, and flat balls, and for each ball you give its x, y, and z coordinates. In the following example, we transform the global point (0,1,0) into BCAM coordinates when our cone, slot and flat balls have coordinates (0,1,0), (-1,1,-1), and (1,1,-1).
lwdaq bcam_from_global_point "0 1 0" "0 1 0 -1 1 -1 1 1 -1" 0.000000 0.000000 0.000000
For a description of the BCAM coordinate system, and how it is defined with respect to a BCAM's kinematic mounting balls, consult the BCAM User Manual. We usually use millimeters to specify coordinates, because we use millimeters in our BCAM camera and source calibration constants. But the routine will work with any units of length, so long as you use the same units for both the point and the mount strings.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq bcam_from_global_point point mount".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz( bcam_from_global_point( xyz_from_string(Tcl_ObjShortString(argv[2])), kinematic_mount_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='global_from_bcam_point' then begin {Transforms a point in global coordinates to a point in BCAM coordinates. It is the inverse of bcam_from_global_point. You pass it the global coordinates of a point in the point string, and the coordinates of the BCAM's kinematic mounting balls with the mount string. The routine returns the global coordinates of the point.
lwdaq global_from_bcam_point "0 1 0" "0 1 0 -1 1 -1 1 1 -1" 0.000000 2.000000 0.000000
For a description of the BCAM coordinate system, and how it is defined with respect to a BCAM's kinematic mounting balls, consult the BCAM User Manual.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq global_from_bcam_point point mount".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz( global_from_bcam_point( xyz_from_string(Tcl_ObjShortString(argv[2])), kinematic_mount_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='bcam_from_global_vector' then begin {Transforms a vector in global coordinates to a vector in BCAM coordinates. See bcam_from_global_point for more details.
lwdaq bcam_from_global_vector "0 1 0" "0 1 0 -1 1 -1 1 1 -1" 0.000000 1.000000 0.000000
For a description of the BCAM coordinate system, and how it is defined with respect to a BCAM's kinematic mounting balls, consult the BCAM User Manual.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq bcam_from_global_vector vector mount".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz( bcam_from_global_vector( xyz_from_string(Tcl_ObjShortString(argv[2])), kinematic_mount_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='global_from_bcam_vector' then begin {Transforms a vector in global coordinates to a vector in BCAM coordinates. It is the inverse of bcam_from_global_vector.
lwdaq global_from_bcam_vector "0 1 0" "0 1 0 -1 1 -1 1 1 -1" 0.000000 1.000000 0.000000
For a description of the BCAM coordinate system, and how it is defined with respect to a BCAM's kinematic mounting balls, consult the BCAM User Manual.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq global_from_bcam_vector vector mount".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz( global_from_bcam_vector( xyz_from_string(Tcl_ObjShortString(argv[2])), kinematic_mount_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='bcam_source_bearing' then begin {Calculates the line upon which a light source must lie for its image to be centered at spot_center. The line is returned as a string containing six numbers. The first three numbers are the coordinates of the BCAM pivot point in BCAM coordinates in millimeters. The last three numbers are a unit vector in the direction of the line. The BCAM itself you describe with its calibration constants in the camera string. The camera string contains nine elements, as described in the BCAM User Manual. The camera string specifies length in millimeters and rotation in milliradians.
lwdaq bcam_source_bearing "1.72 1.22" "P0001 1 0 0 0 0 1 75 0" 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000
The first element in the camera string is the name of the camera, even though this calculation does not use the camera name. In the example above, P0001 is the camera name, the pivot point is at (1,0,0) in BCAM coordinates, the camera axis is parallel to the BCAM z-axis, the pivot point is 75 mm from the lens, and the CCD rotation is zero. We transform point (1.72, 1.22) on the CCD (dimensions are millimeters) into a bearing that passes through the pivot point (1,0,0) in the direction (0,0,1). The point (1.72,1.22) is our aribitrarily-chosen center of the CCD in all currently-available BCAMs (it is close to the center of the TC255P image sensor, but not exactly at the center). The BCAM camera axis is the line passing through the CCD center and the pivot point.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq bcam_source_bearing spot_center camera".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz_line( bcam_source_bearing( xy_from_string(Tcl_ObjShortString(argv[2])), bcam_camera_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='bcam_source_position' then begin {Calculates the BCAM coordinates of a light source whose image is centered at spot_center, and which we know to lie in the plane z = bcam_z in BCAM coordinates. The routine is similar to bcam_source_bearing, but you specify the BCAM z-coordinate of the source as well, in millimeters. The routine determines the position of the source by calling bcam_source_bearing and intersecting the source bearing with the z=range plane.
lwdaq bcam_source_position "1.72 1.22" 1000 "P0001 1 0 0 0 0 1 75 0" 1.000000 0.000000 1000.000000
Here we see the source is at (1,0,1000) in BCAM coordinates, where all three coordinates are in millimeters. You specify the BCAM itself with its calibration constants using the camera string, just as for bcam_source_bearing.
} if (argc<>5) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq bcam_source_position spot_center bcam_z camera".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz( bcam_source_position( xy_from_string(Tcl_ObjShortString(argv[2])), Tcl_ObjReal(argv[3]), bcam_camera_from_string(Tcl_ObjShortString(argv[4]))))); end else if option='wps_wire_plane' then begin {Calculates the plane that must contain the center-line of a wire given the position and rotation of a wire image in a WPS camera. The units for wire position are millimeters, and for rotation are milliradians. We use the camera's calibration constants to determine the plane. We specify the plane in WPS coordinates, which are defined in the same way as BCAM coordinates, using the positions of the WPS (or BCAM) mounting balls. For a description of the BCAM coordinate system, consult the BCAM User Manual.
lwdaq wps_wire_plane "1.720 1.220" "0.000" "Q0131_1 0 0 0 -10 0 0 0 0 0" 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000
The image position in our example is 1.720 mm from the right and 1.220 mm from the top. This is at the nominal center point of a TC255 image sensor. The wire is rotated by 0 mrad anti-clockwise in the image. The first element in the camera string is the name of the camera, even though this calculation does not use the camera name. In the example above, Q0131_1 is the camera name. It is camera number one on the WPS with serial number Q0131. In this example, the camera pivot point is at (0,0,0) in WPS coordinates, which puts it at the center of the cone ball supporting the WPS. That's clearly impossible, but we're just using simple numbers to illustrate the routine. The center of the image sensor (the CCD) is at (-10,0,0). The x-axis runs directly through the pivot point and the center of the sensor. The rotation of the sensor is (0,0,0), which means the x-axis is perpendicular to the sensor surface. Here is another example.
lwdaq wps_wire_plane "1 1.220" "10.000" "Q0131_1 0 0 0 -10 0 0 0 0 0" 0.000000 0.000000 0.000000 0.071811 0.009974 0.997368
The routine calculates the plane that contains the center of the image and the pivot point. It specifies the plane as the pivot point, which is a point in the plane, and a normal to the plane. The first three numbers in the result are the coordinates of the pivot point. The last three numbers are the normal to the plane. The normal is a unit vector.
} if (argc<>5) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq wps_source_plane wire_center wire_rotation camera".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz_plane( wps_wire_plane( xy_from_string(Tcl_ObjShortString(argv[2])), Tcl_ObjReal(argv[3])/mrad_per_rad, wps_camera_from_string(Tcl_ObjShortString(argv[4]))))); end else if option='xyz_plane_plane_intersection' then begin {Determines the line along which two planes intersect. We specify each plane with a point in the plane and a normal to the plane, making six numbers for each plane.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq xyz_plane_plane_intersection plane_1 plane_2".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz_line( xyz_plane_plane_intersection( xyz_plane_from_string(Tcl_ObjShortString(argv[2])), xyz_plane_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='xyz_line_plane_intersection' then begin {Determines the point at which a line and a plane intersect. We specify the line with a point and a direction. We specify the plane with a point and a normal vector.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq xyz_line_plane_intersection line plane".'); exit; end; Tcl_SetReturnShortString(interp, string_from_xyz( xyz_line_plane_intersection( xyz_line_from_string(Tcl_ObjShortString(argv[2])), xyz_plane_from_string(Tcl_ObjShortString(argv[3]))))); end else if option='straight_line_fit' then begin {Fits a straight line to data, where data contains a string of numbers, alternating between x and y coordinates. The routine returns a string of three numbers: slope, intercept, and rms residual. The rms residual is the standard deviation of the difference between the straight line and the data, in the y-direction. The data "0 3 1 5 2 7 5 13" would represent a straight line with slope 2, intercept 3, and rms residual 0. The result would be "2.000000 3.000000 0.000000".
} if (argc<>3) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq straight_line_fit data".'); exit; end; lsp:=Tcl_ObjLongString(argv[2]); gp:=read_xy_graph(lsp^); straight_line_fit(gp,slope ,intercept,rms_residual); dispose_xy_graph(gp); writestr(lsp^,slope:fsr:fsd,' ',intercept:fsr:fsd,' ',rms_residual:fsr:fsd); lwdaq_long_string:=lsp^; dispose_long_string(lsp); Tcl_SetReturnLongString(interp,lwdaq_long_string); end else if option='ave_stdev' then begin {Calculates the average and standard deviation of data, where data contains a string of numbers. The routine returns the average and standard deviation separated by a space.
} if (argc<>3) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq ave_stdev data".'); exit; end; lsp:=Tcl_ObjLongString(argv[2]); gpx:=read_x_graph(lsp^); writestr(lsp^,average_x_graph(gpx):fsr:fsd,' ',stdev_x_graph(gpx):fsr:fsd); dispose_x_graph(gpx); lwdaq_long_string:=lsp^; dispose_long_string(lsp); Tcl_SetReturnLongString(interp,lwdaq_long_string); end else if option='linear_interpolate' then begin {Interpolates between the two-dimensional points of x_y_data to obtain an estimate of y at x=x_position. If you pass "2.5" for the x position, and "0 0 10 10" for the x-y data, the routine will return "2.500000".
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq linear_interpolate x_position x_y_data".'); exit; end; position:=Tcl_ObjReal(argv[2]); lsp:=Tcl_ObjLongString(argv[3]); gp:=read_xy_graph(lsp^); linear_interpolate(gp,position,interpolation); dispose_xy_graph(gp); writestr(lsp^,interpolation:fsr:fsd); lwdaq_long_string:=lsp^; dispose_long_string(lsp); Tcl_SetReturnLongString(interp,lwdaq_long_string); end else if option='sum_sinusoids' then begin {Adds two sinusoidal waves of the same frequency together. You specify the two waves with their amlitude and phase. The phase must be in radians. The amplitude is dimensionless. The result contains the amplitude and phase of the sum of the two waves. If you pass the numbers "1 0 1 0.1" to the routine, it will return "1.997500 0.050000".
} if (argc<>6) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq sum_sinusoids a.amplitude a.phase b.amplitude b.phase".'); exit; end; a.amplitude:=Tcl_ObjReal(argv[2]); a.phase:=Tcl_ObjReal(argv[3]); b.amplitude:=Tcl_ObjReal(argv[4]); b.phase:=Tcl_ObjReal(argv[5]); a:=sum_sinusoids(a,b); writestr(s,a.amplitude:fsr:fsd,' ',a.phase:fsr:fsd,' '); Tcl_SetReturnShortString(interp,s); end else if option='fourier_term' then begin {Calculates a term in the discrete Fourier transform of waveform by calling calculate_ft_term from utls.pas. You specify the waveform as a string of real numbers. Each represents the value of the waveform at discrete, consecutive moments in time (or space, or some other one-dimensional metric) separated by the sample interval. You specify which term you want to calculate by giving its period in units of sample intervals with the period parameter. The routine returns a string containg the amplitude and phase of the term in the fourier transform corresponding to period. If you pass "2" for the period and "0 1 0 1 0 1" for the data, the routine returns, "1.000000 0.500000". The phase, as you can see, is given in units of sample interval, and its sine is such that you subtract it from the phase of a sinusoid to create the Fourier term. To obtain the zero-frequency (DC) term, which corresponds to period infinity, pass period "0" to the routine. We use "0" as a code for "infinity", since we cannot calculate the discrete fourier transform at period zero. If you want to calculate a fourier transform made up of many fourier terms, try using the fourier_transform routine instead. It is much faster when you have a large waveform and many frequencies in your desired spectrum, because it translates the TCL data string into a Pascal binary string only once.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq fourier_term period waveform".'); exit; end; period:=Tcl_ObjReal(argv[2]); lsp:=Tcl_ObjLongString(argv[3]); gpx:=read_x_graph(lsp^); calculate_ft_term(period,gpx,amplitude,offset); dispose_x_graph(gpx); writestr(lsp^,amplitude:fsr:fsd,' ',offset:fsr:fsd); lwdaq_long_string:=lsp^; dispose_long_string(lsp); Tcl_SetReturnLongString(interp,lwdaq_long_string); end else if option='fourier_transform' then begin {Calculates a series of terms in the discrete Fourier transform of waveform. In effect, this option acts like repeated calls to fourier_term, but is more efficient for large waveform strings, because TCL does not have to copy the string for each term. Instead of passing the routine a single period for a single fourier term, you pass a list of periods. The routine returns a list of terms, each term consisting of the period, amplitude and offset, separated by spaces. To improve its accuracy when calculating terms with non-zero frequency, the routine subtracts the average value of the waveform from each term in the waveform before it calculates terms. In response to period "0", the routine returns the average value of the waveform, so "0" is how you indicate period infinity.
} if (argc<>4) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq fourier_transform periods waveform".'); exit; end; lsp:=Tcl_ObjLongString(argv[2]); periods:=read_x_graph(lsp^); dispose_long_string(lsp); lsp:=Tcl_ObjLongString(argv[3]); gpx:=read_x_graph(lsp^); average:=average_x_graph(gpx); for i:=0 to gpx^.num_points-1 do gpx^[i]:=gpx^[i]-average; lsp^:=''; for i:=0 to periods^.num_points-1 do begin if (periods^[i] = 0) then writestr(lsp^,lsp^,0.0:fsr:fsd,' ',average:fsr:fsd,' ',0.0:fsr:fsd,eol) else begin calculate_ft_term(periods^[i],gpx,amplitude,offset); writestr(lsp^,lsp^,periods^[i]:fsr:fsd,' ',amplitude:fsr:fsd,' ',offset:fsr:fsd,eol); end; end; dispose_x_graph(gpx); dispose_x_graph(periods); lwdaq_long_string:=lsp^; dispose_long_string(lsp); Tcl_SetReturnLongString(interp,lwdaq_long_string); end else if option='matrix_inverse' then begin {Calculates the inverse of a square matrix. You pass the original matrix as a string of real numbers in matrix. The first number should be the top-left element in the matrix, the second number should be the element immediately to the right of the top-left element, and so on, proceeding from left to right, and then downwards to the bottom-right element. The command deduces the dimensions of the matrix from the number of elements, which must be an integer square. For more information about the matrix inverter, see matrix_inverse in utils.pas. The "lwdaq matrix_inverse" routine is inefficient in its use of the matrix_inverse function. The routine spends most of its time translating between TCL strings and Pascal floating point numbers. A 10x10 matrix inversion with random elements takes 1800 μs on our 1 GHz iBook, of which only 100 μs is spent calculating the inverse. The routine returns the inverse as a string of real numbers, in the same format as the original matrix.
} if (argc<>3) then begin Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be ' +'"lwdaq matrix_inverse matrix".'); exit; end; lsp:=Tcl_ObjLongString(argv[2]); num_elements:=word_count(lsp^); num_rows:=trunc(sqrt(num_elements)); if sqrt(num_elements)-num_rows>small_real then begin Tcl_SetReturnShortString(interp,'Non-square matrix.'); exit; end; M:=new(matrix_ptr,num_rows,num_rows); N:=new(matrix_ptr,num_rows,num_rows); read_matrix(lsp^,M^); matrix_inverse(M^,N^); lsp^:=''; write_matrix(lsp^,N^); dispose(M); dispose(N); lwdaq_long_string:=lsp^; dispose_long_string(lsp); Tcl_SetReturnLongString(interp,lwdaq_long_string); end else begin Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of ' +' "bcam_from_global_point global_from_bcam_point' +' global_from_bcam_vector bcam_from_global_vector' +' bcam_source_bearing bcam_source_position' +' wps_wire_plane xyz_plane_plane_intersection xyz_line_plane_intersection' +' straight_line_fit sum_sinusoids linear_interpolate' +' fourier_term fourier_transform matrix_inverse".'); exit; end; if error_string<>'' then Tcl_SetReturnShortString(interp,error_string); lwdaq:=Tcl_OK; end; { lwdaq_init initializes the pascal run-time system, sets the initial values of all variables declared in this program and all its units, and installs the lwdaq commands in the tcl interpreter. } function lwdaq_init(interp:pointer):integer; attribute (name='Lwdaq_Init'); var p:pointer; begin { We try to initialize the TCL and TK stub libraries if USE_TCL_STUBS is defined, as it might be by a compiler option -DUSE_TCL_STUBS. } {$ifdef USE_TCL_STUBS} p:=tcl_initstubs(interp,'8.1',0); if (p=nil) then begin lwdaq_init:=Tcl_Error; exit; end; p:=tk_initstubs(interp,'8.1',0); if (p=nil) then begin lwdaq_init:=Tcl_Error; exit; end; {$endif} initialize_pascal(0,nil,nil); initialize_main; gui_interp_ptr:=interp; gui_draw:=lwdaq_gui_draw; gui_writeln:=lwdaq_gui_writeln; gui_wait:=lwdaq_gui_wait; gui_support:=lwdaq_gui_support; p:=tcl_createobjcommand(interp,'lwdaq',lwdaq,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_config',lwdaq_config,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_draw',lwdaq_draw,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_graph',lwdaq_graph,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_create',lwdaq_image_create,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_contents',lwdaq_image_contents,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_characteristics',lwdaq_image_characteristics,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_histogram',lwdaq_image_histogram,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_profile',lwdaq_image_profile,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_exists',lwdaq_image_exists,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_results',lwdaq_image_results,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_destroy',lwdaq_image_destroy,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_image_manipulate',lwdaq_image_manipulate,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_data_manipulate',lwdaq_data_manipulate,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_photo_contents',lwdaq_photo_contents,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_rasnik',lwdaq_rasnik,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_rasnik_shift',lwdaq_rasnik_shift,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_wps',lwdaq_wps,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_bcam',lwdaq_bcam,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_diagnostic',lwdaq_diagnostic,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_gauge',lwdaq_gauge,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_flowmeter',lwdaq_flowmeter,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_voltmeter',lwdaq_voltmeter,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_rfpm',lwdaq_rfpm,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_inclinometer',lwdaq_inclinometer,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_recorder',lwdaq_recorder,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_calibration',lwdaq_calibration,0,nil); p:=tcl_createobjcommand(interp,'lwdaq_sampler',lwdaq_sampler,0,nil); lwdaq_init:=tcl_pkgprovide(interp,package_name,version_num); end; { lwdaq_unload deletes the above commands from the interpreter. } function lwdaq_unload(interp:pointer;flags:integer):integer; attribute (name='Lwdaq_Unload'); begin lwdaq_unload:=Tcl_Error; end; { lwdaq_safeinit returns an error because we don't have a safe version of the initialization. } function lwdaq_safeinit(interp:pointer):integer; attribute (name='Lwdaq_SafeInit'); begin lwdaq_safeinit:=Tcl_Error; end; { lwdaq_safeunload returns an error because we don't have a safe version of the unload. } function lwdaq_safeunload(interp:pointer;flags:integer):integer; attribute (name='Lwdaq_SafeUnload'); begin lwdaq_safeunload:=Tcl_Error; end; { The main part of the program we never use. See the comments at the top. } begin end.