McIDAS Programmer's Manual
Version 2006
[Search Manual] [Table of Contents] [Go to Previous] [Go to Next]
The previous two sections introduced you to McIDAS-X from a user's perspective and provided an overview of the McIDAS-X environment. This final section describes how to build applications that will interact with the McIDAS-X environment. In this section, you will learn how to:
This chapter contains references to specific source files. The table below describes the suffixes McIDAS-X uses to name source files.
Suffix | Language | Description |
---|---|---|
You can write McIDAS-X applications in either C or Fortran. This section describes the concepts you need to know to write basic McIDAS-X applications in either language, along with the formats and policies for writing online command helps and setting status codes.
When you write a Fortran program for McIDAS-X, you can't write it as a Fortran MAIN program. Instead, you must place it in the McIDAS-X environment as a subroutine named MAIN0, which is linked with a command jacket called main.
The command jacket main initializes the run-time environment for the command, including:
The first executable line of any McIDAS-X application written in Fortran must be the statement subroutine main0. The code fragment below is a sample command to print the phrase Hello World to the McIDAS-X Text and Command Window.
subroutine main0 call sdest ('Hello World',0) call mccodeset (0) return end |
When writing a C program for McIDAS-X, the main is provided directly in the C code. The first thing you will do when writing a McIDAS-X application in C is to initialize the McIDAS-X environment with a call to the function Mcinit. This function performs the same command environment initialization performed by the Fortran command jacket main. To write McIDAS-X applications in C, your source code should also contain a reference to the McIDAS-X include file mcidas.h.
The code fragment below demonstrates how the same McIDAS-X command to print Hello World is written as a C application.
McIDAS-X uses include files to hold definitions of constants specific to its software. mcidas.h contains system-wide constants and function prototypes. Equivalanet constants for Fortran applications are found in the *.inc files.
The include file mcidas.h contains all the function prototypes for C-callable API routines in the McIDAS-X library. It also contains two constant values that are commonly used in McIDAS-X applications.
Constant value
|
Description
|
---|---|
mcidas.h also includes a group of defined types that you should use to ensure platform independence of data types. Use the defined types below when writing interface routines that will be used between C and Fortran.
Defined type
|
Description
|
---|---|
equivalent to a double precision declaration type in Fortran |
|
To ensure portability of Fortran jackets written in C, the preferred types to use are Fdouble, Fint and Freal, as the INTEGER*2, INTEGER*4, REAL*4 and REAL*8 declarations are not part of the Fortran-77 standard.
In both Hello World examples above, calls were made to the functions mccodeset and Mccodeset. These functions set a status code that can be passed from the application to the calling environment. Currently, the acceptable return codes for McIDAS-X commands are as follows:
Return code
|
Description
|
---|---|
This information may be very useful to the calling environment, especially when using McIDAS-X commands in scripts designed for batch processing. For example, if you have a script that copies satellite images from several different sources and then creates a mosaic of the images, you may not want the script to generate the mosaic until all of the images are available. The processing script will not be able to verify that all of the data is available until all of the commands necessary to acquire the satellite data have returned a status of 0 indicating success.
If a command returns a status of 1, it typically means the user entered the command incorrectly and it will never run properly. A status of 2 is typically returned when data requested by a user is not yet available.
Setting return statuses for McIDAS-X applications became mandatory, beginning with McIDAS-X version 7.0 released in May 1996. Most McIDAS-X applications written before that upgrade do not yet contain the Mccodeset /mccodeset features.
The McIDAS-X command provides users with a quick way to get a listing of a command's structure and available options. The original text for these helps is stored in the source file for each command. You will use one of two template formats when creating online helps. The format for Fortran commands is shown below.
The similar help template for commands written in C is shown below.
The first line of each help contains the command name followed by two dashes (--). These dashes are used as flags to the system for the Alt ? help option described earlier in this chapter.
To learn how to build help files, see Chapter 3, Getting Started in McIDAS-X. |
In the Hello World examples above, several function names are in bold type. These are examples of functions included in the McIDAS-X library. The library contains all the object code for the functions and subroutines that make up the McIDAS-X Application Program Interface (API).
The name of the McIDAS-X library is libmcidas.a.
To provide easy recognition of McIDAS-X functions, the names of all new McIDAS-X functions begin with one of the prefixes below.
Prefix
|
Description
|
---|---|
The McIDAS-X philosophy regarding function prefixes is that once a function is released with an Mc or mc prefix, it is a stable member of the McIDAS-X library and will have neither its functionality nor calling sequence changed. The M0 or m0 prefix is assigned to:
SSEC uses the M0/m0 prefix while testing a routine's functionality and calling sequence. When the function is deemed stable, the prefix is changed to Mc/mc.
SSEC adopted this function prefix naming convention in the spring of 1995. Most of the routines currently found in the McIDAS-X library predate this naming convention. As SSEC modifies existing software, it will make every effort to change the names of older functions where practical.
If new functions are written to replace older, non-prefixed functions, SSEC's policy for removing the old functions from McIDAS-X is as follows:
SSEC has also developed a standard interface documentation block template that you should use for all new McIDAS-X library functions. This format is used to generate the online API function documentation distributed with the McIDAS-X software.
The table below lists and defines each field in the documentation block. More information follows along with examples from the mchmstostr function. If a function doesn't require a description for every field, you should still include all fields in the documentation block and fill the unused fields with the word none.
This field is usually a one-line description of what the function does. The example below is taken from the function mchmstostr.
Name: mchmstostr - Converts a time to a character string |
This field describes the function type value returned, such as float, integer, or subroutine. It also contains any include files associated with the function, and the type and size of each parameter in the calling sequence.
Interface: integer function mchmstostr (integer hms, integer format, character*(*) string) |
This field describes each of the input parameters to a function. It should include any discussion of expected formats and units.
Input: hms - Time in the form hhmmss. format - Output format desired for the string. |
This field describes parameters that pass input to a function and have their values modified within the function. It is important to describe the state of these parameters both on input and output. Since this example doesn't contain parameters in this field, the entry looks like this:
Input and Output: none |
This field describes each of the output parameters to a function. The description should include any discussion of expected formats and units.
Output: string - Destination character string. |
This field describes the possible values that can be returned by this function. The McIDAS-X convention for return values is described below.
A function should have as many unique failure return values as ways the function can fail, as shown in the example below.
Return values: 0 - Success. -1 - Invalid value for hms. -2 - Invalid value for format. -4 - Destination string is not big enough. |
This field describes additional information that is necessary or useful to a programmer. For example, it would be helpful for a programmer to know:
Other useful information may include references to:
An example of this field for mchmstostr is shown below.
Remarks: If the input value for hms is 23444 then: form string 1 02:34:44Z 2 02:34:44 3 02:34:44UTC 4 02:34:44 Z 5 02:34:44 UTC 6 2:34:44 |
This field is used to generate the cross reference list for the online API function documentation. The categories available in McIDAS-X are:
calibration | converter | day/time | display |
event | file | graphic | grid |
image | ingest/decode | met/science | navigation |
point | text | sys_config | system |
user_interface | utility |
In mchmstostr, the categories field looks like this:
Categories: converter day/time |
The next two pages contain the complete interface documentation block templates for both C and Fortran functions. Although the categories field contains the complete list of available entries, you will choose only those appropriate to your function. Text files containing the templates are available on the MUG Web Site.
McIDAS-X applications and functions can be written in either Fortran or C. Because McIDAS has its roots in Fortran, not all functions in the McIDAS-X library have both C and Fortran calling interfaces.
While McIDAS-X attempts to accommodate both languages, C language programs will sometimes need to interface with Fortran-coded functions from the McIDAS-X library. When calling Fortran functions from C applications, you must keep in mind these four important differences between the languages.
Below are examples of how these language differences affect a C application calling a Fortran function. Also provided is an example of writing Fortran-callable routines in C and information about writing Dynamic Link Library modules.
The McIDAS-X Fortran function iyxll converts integer values from frame (tv) line and elements to single precision floating point values for latitude and longitude. The sample Fortran code fragment below calls this function.
integer tvlin, tvele integer onscreen real lat, lon tvlin = 100 tvele = 200 onscreen = iyxll(tvlin, tvele, lat, lon) |
To call the same function from a C application, your code would be similar to this:
The special type declarations Fint and Freal are Fortran integer and real declarations defined in the include file mcidas.h. They are designed to provide platform-independent interfaces between Fortran and C applications.
Since parameters are passed into functions in C by value, you must reference the variables in the calling sequence with the ampersand (&) symbol. Calling iyxll by value, as shown below, would most likely result in a segmentation violation error.
onscreen = iyxll_ (tvlin, tvele, lat, lon); |
The McIDAS-X library function mdsvc, which is coded in Fortran, returns the appropriate real-time MD file given a character string schema type and an integer value representing a Julian day. Below is a sample Fortran code fragment to call this function.
integer mdfile, day character*4 schema schema = 'ISFC' day = 96017 mdfile = mdsvc(schema, day) |
To call the same function from a C application, your code would be similar to this:
Note that the variable schema is passed in with no ampersand character. The function expects the variable to be passed in by address and since schema is already a pointer, no ampersand is required.
Also note that the call to mdsvc_ in C has an additional parameter, schema_length. This argument is passed by value to the Fortran function so the length of the string containing the schema type is known to mdsvc internally.
The conflict of Fortran column-major arrays and C row-major arrays is difficult to resolve. Arrays are contiguous memory segments, no matter how many dimensions they have. The difference between the languages is in how the individual elements of the arrays are arranged and accessed, as described below.
If you declare a Fortran array like this:
integer array(2,3) |
Then to reference the memory segments in increasing order, you will reference them like this:
1,1 2,1 1,2 2,2 1,3 2,3 |
If you declare a C array with the same dimensions:
int array[2][3] |
Then to reference the memory segments in increasing order, you will reference them as shown below. They are shown as one-based values for comparison to the Fortran array even though the C language is actually zero-based.
1,1 1,2 1,3 2,1 2,2 2,3 |
When accessing data stored in multi-dimensional arrays, it is difficult to recognize if an array is stored in Fortran or C. Since most McIDAS-X applications were historically written in Fortran, multi-dimensional data is usually stored in column-major format. To address values for column-major data in C, dimension your arrays as one-dimensional and use the conversion equations below to index to the appropriate location.
The indexing equation for a column-major (Fortran) array is as follows, assuming 1-based indexing:
The indexing equation for a row-major (C) array is as follows, assuming 0-based indexing:
Now that you understand how the C and Fortran languages treat different elements of parameter passing into functions, you need to know how to write Fortran-callable functions in the C language. The example below builds the calling sequence for a unit conversion routine that takes an integer value and unit types and returns a floating point number. The function also returns an integer type for a status code.
In C, the prototype and function would look like this:
int convert (int, char *, char *, float *); /* function prototype */ int convert (int input, char *input_units, char *output_units, float *output) { : : } |
The entire source code for the Fortran-callable version of this function would appear as follows. The important concepts are bolded.
Note that the Fortran-callable function declaration explicitly includes the underscore character (_). The McIDAS-X library function fsalloc converts Fortran strings to C strings. The function strtofs converts C strings to Fortran if you need to pass character string output back to the Fortran program that called this function jacket. Be sure to call the C function with the appropriate data types.
A shell script is a program containing a set of executable commands. Shell scripts are useful for running a series of individual McIDAS-X commands outside of McIDAS-X. Generally, you must invoke the McIDAS-X resident program, mcenv, before running a series of McIDAS-X commands.
For information about writing shell scripts in McIDAS-X, see Appendix H, Running Commands Outside a McIDAS-X Session, in the McIDAS User's Guide. |
It's a fact of life that programs do not always behave as expected. Therefore, you should implement some type of debugging strategy to isolate and repair errors. This section describes the tools available to help you debug applications. McIDAS-X provides two facilities to aid your search for the elusive bug. Additionally, Unix systems provide a variety of symbolic debugging tools, the most common of which is dbx or gdb.
In McIDAS-X, the simplest debugging aide is ddest/Mcdprintf. These functions allow applications to print hidden statements when the third character in the DEV= global keyword is C. Strategically placing debug print statements in your code is an excellent way to identify invalid values or corrupted data in disk files. Use debug messages prudently, however, and avoid putting them inside large loops that may generate thousands of lines of useless messages.
For more information on ddest and Mcdprintf, see the section titled Text messages in Chapter 4, McIDAS-X Utilities. |
Another debug facility provided in McIDAS-X is the C language development tool, M0ASSERT, which is used to trap severe errors in code, such as a memory overlap problem or trying to pass a NULL character pointer into a routine.
M0ASSERT is a macro that takes a boolean expression as input. If the expression resolves to false, a message is printed to stderr similar to this:
Assertion failed: source_file line nnn |
where source_file is the name of the file and nnn is the line number in source_file where the failure occurred. After the message is printed, a call to exit(1) is made to end the program.
M0ASSERT can take two different forms based on compile-time options that you specify. The compile-time default for M0ASSERT is the NULL statement, meaning M0ASSERT does nothing. If you add the option -DM0ASSERT_ON to the compile statement, the preprocessor adds the assertion test.
M0ASSERT is not a substitute for standard error handling. Don't assume that capturing assertion failures will be activated as part of the production version of the software. Also, don't use M0ASSERT to test for conditions that, while rare, could indeed happen. The sample code fragment below demonstrates both good and bad uses of M0ASSERT.
For more information about assertions, read the excellent description provided in Chapter 2 of Writing Solid Code by Steve Maguire, Microsoft Press. |
Most Unix-based systems include the symbolic debugger, dbx. To interactively debug McIDAS-X applications with dbx, you must perform a series of steps, which are necessary for these reasons:
Perform the steps below to set up your environment for interactive debugging. For this description, assume that the development source code is in ~/mcidas/dev, an application is in the source file foo.pgm, and it calls functions found in suba.c and subb.for.
Type: | cp ~/mcidas/dev/foo.pgm ~/mcidas/data/foo.f |
cp ~/mcidas/dev/suba.c ~/mcidas/data/suba.c | |
cp ~/mcidas/dev/subb.for ~/mcidas/data/subb.f |
This section describes some of the common issues that McIDAS-X developers encounter. It will offer suggestions for avoiding platform dependency problems and for planning new software development.
When writing data access applications that may reside on other machines, you must be aware of the differences in byte ordering between big-endian and little-endian machines. These differences are described in the section titled Conversion Utilities in Chapter 4 of this manual. You will use the functions swbyt2 and swbyt4 to switch the byte ordering.
Different platforms may use different methods of representing floating point numbers in memory. Do not store floating point numbers to disk in the native format of the machine if the data may be used on other platforms. Instead, store floating point numbers as scaled integer values.
Historically, the McIDAS-X standard for representing days was to use the Julian day with two digits representing the year of the century in the yyddd format. For example, 1 January 1997 is 97001. From the user's perspective, the convention of the 2-digit year continues into the 21st century. McIDAS-X continues to determine the century by making the most reasonable choice: 97 indicates 1997 (not 2097), whereas 03 indicates 2003 (not 1903). The user can also input the full 4-digit year in the ccyyddd format.
For disk files we use the convention for year of the number of years past 1900. For example, 97 indicates 1997, 103 is for 203. The McIDAS-X library contains many functions for handling this new Julian day format. These functions are described in the section titled Conversion utilities in Chapter 4 of this manual. Use the new representation not only in memory-based applications but also in file structures.
When writing software that deals with geographic locations, verify that your code works properly and efficiently on all quadrants of the globe. For example, even if you think your subsystem will only be used for Northern Hemisphere data, make it robust enough to work in the Southern Hemisphere as well.
The suggestions below will help you when programming in Fortran.
Don't use a NULL string as a substitute for a blank character. For example, don't:
call sdest('',0) |
Instead, do:
call sdest(' ',0) |
Don't concatenate strings of indeterminate lengths in parameter lists.
Put data statements after integer, parameter, common declarations.
Use status= with an open statement. Use NEW, OLD or SCRATCH. The default for status= is usually UNKNOWN, which is defined as an implementation-dependent option. The sample code below shows you what to do.
Fortran compilers don't necessarily pad character literals out to column 72. For example, don't:
call sdest ('this is a very long string I think this will &run way off the end',0) |
Instead, do:
call sdest ('this is a very long string I think '// &'this will run way off the end',0) |
Avoid memory overlapping when assigning character variables. For example, don't:
string(2:20) = string(1:19) |
Verify that you are passing the correct-length floating point representations into functions. For example, if foo is defined as:
integer function foo (input1, input2) implicit none real input1 double precision input2 |
Make certain your code passes arguments to foo like this:
ok = foo (7.0, 9.d0) |
Avoid manipulating parameters passed in as function arguments directly. Many systems cause segmentation violation errors if the value passed in is a constant. For example:
function foo (string) character*(*) string call mcupcase (string) ! convert to uppercase |
If the call to foo looks like the one below, segmentation faults will usually occur because the mcupcase call cannot modify the constant Australia.
status = foo ('Australia') |
Don't write Fortran routines that return character strings as the function return code if you're planning to write C jackets for them.
For example, don't:
character*12 function foo (input) |
Instead, do:
integer function foo (input, output_string) |
Don't embed function calls in concatenation of character strings. For example:
string = 'The temperature is '//cfr(temp) |
If you're using formatted write statements, be sure the data type being printed matches the output declaration type. For example, the code below:
REAL temp temp = 32.0 write (string, FMT='(a20,i4)')'The temperature is ',temp call sdest(string,0) |
Will print The temperature is 0 on some platforms. Instead, use:
write (string, FMT='(a20,f6.2)')'The temperature is ',temp |
Fortran-77 requires that dimension variables' data type be known before encountering an array declaration that uses them. For example, don't:
function foo (array, nrow, ncol) implicit none real array(nrow, ncol) integer nrow, ncol |
Instead, do:
function foo (array, nrow, ncol) implicit none integer nrow, ncol real array(nrow, ncol) |
The only recommended continuation character for column 6 is `&'.
Instead of declaring character constants like this:
character*12 string parameter (string = 'Hello World!') |
Declare them like this:
character*(*) string parameter (string = 'Hello World!') |
The suggestions below will help you when programming in C.
Include string.h, not memory.h when using the mem * functions.
Variables with typedef integral types can be a problem when printing, since printf requires you tell it the size of the value. The solution is to cast all ambiguous types to long, as shown below.
uid_t u; /* usually unsigned */ size_t j; /* usually unsigned */ ssize_t k; /* signed size_t */ printf ("%ld %ld %ld\n", (long) u, (long) j, (long) k); |
Don't use realloc (NULL, size) to be the same as malloc (size). Some compilers don't allow this convention.
Don't use malloc (0) or realloc (p, 0). It may return either a pointer to a segment size of 0 or a NULL pointer. The result is implementation-dependent.
Don't use strlen (p) where p may be NULL, as this causes segmentation violation errors on some platforms.
Don't assume the return value from sprintf is the length of the resulting string.
[Search Manual] [Table of Contents] [Go to Previous] [Go to Next]