Nemo Libray Scullions and Program Execution Tracing


Introduction

The facilities in this colletion are not (strictly speaking) part of the Nemo Library; they are stand‑alone C source code offerings facilitating the design, construction and testing of programs that use Nemo Library functions.

None of them are meant to be present in the application systems ready to be distributed to end‑users; their purpose is to aid in the development of "round-world" algorithms and functions that implement them, to make the code of Library API demo programs less encoumbered with low‑level details, and to aid in the design and construcion of applications that use Nemo Library.

While the majority of functions listed here need explicit design and build action to craft their replacement in ship‑ready applications, assertions and tracing functions are envoked via macros (NEMO_ASSERT(x), NEMO_PTRACE_xxx(args) and NEMO_GTRACE(args)), and are safe to leave in the production code with judicious #defines of NEMO_ASSERT_ACTIVE, NEMO_PTRACE_ACTIVE and NEMO_GTRACE_ACTIVE in the build of distribution binaries (see the example below). This has a dowside: the compiler is unable to enforce the type checking of NEMO_PTRACE(()) and NEMO_GTRACE(()) argument lists. Carefull checking of calls' arguments againt the calling sequences documented below is therefore strongly encouraged.

Program execution control

Assertions can be performed with a macro that defaults to a no-operation in production binaries - in this case in nemo.h main Library API header file. The macro NEMO_PTRACE((args)) is conditionally redeclared in scullions.h C header file, and the code of the function invoked by the macro resides in nemoPtrace.c. The application programs will typically include a sequence such as:

...
#include <nemo.h>
#define NEMO_ASSERT_ACTIVE       
#define NEMO_PTRACE_ACTIVE  
// #define NEMO_GTRACE_ACTIVE  
#include "../scullions/scullions.h"
...
static const char *progName;    /* for error logging by this source file only */
static const char *progAsof;                 /* executable creation timestamp */
int main (...) {
(many lines of application program code...)
   } 
#include "../scullions/errorExit.c"
#include "../scullions/nemoPtrace.c"
#include "../scullions/nemoGtrace.c"
...

In the above example, compiled binary will perform assertions and text message program tracing, but all graphical tracing code will be excluded. Usually, the binaries that include graphical tracing will also include textual tracing and assertions and assertions will be included if any tracing is ative.

Following functions are invoked directy, and will remain in compiled binaries unless the calls are removed or commented out. The C source files in which the functions are implemented must be included explcitly, usually at the end of the program, as in the above example. Only a brief description of their purpose is provided here; for further details see their well-documented code in the source code file - as specified below:

void errorExit(const char *progName, int line, const char *format, ...);
Output on stderr a line with a reference to source code and fault condition and terminate the execution. (In errorExit.c)
const char *fileBaseNmEx(const char *pathNameExtension);
Return, in the character string pointer to the file name string less leading path part. (In errorExit.c)
const char *yearDayTimeNow(void);
Return, in a static internal array, a pointer to the current time character string, suitable for inclusion in program execution messges. (In errorExit.c)
const char *yearDayTimeMod(const char *fileName);
Return, in a static internal array, a pointer to the string of file last modification (or creation) time, suitable for inclusion of program execution trace messages. (Note platform dependency!). (In errorExit.c)

Disk file access

int getLineCount(FILE *fp);
Two functions eneble the processing of file containing lines of text but avoiding the necessity for an application program to use OS dependent text file processing. This one returns the number of text lines in the file, given a file pointer fp open in normal (i.e., "binary" as opposed to "text") mode). (In binaryText.c)
int getLineChars(FILE *fp, char *line, int nLine);
Fill an invoker-supplied bufer of maximum length given with the line (text) at the current stream file position. (In binaryText.c)
int getLine(char *pLine[], int *nLine, FILE *fp);
Portable re-implementation of POSIX getline() function. (In getLine.c)
char *strTok(char *, const char *delim);
A variant of C library strtok() function, capable of properly scanning lines of ubiquitous .csv ("comma-separated-values") text files. (In strTok.c)
const char *clFileName(int argc, const char *argv[]);
Get next command line positional argument, presumable one of the file names the program is expected to open and operate on. (In clFileOpt.c)
const char *clOption(int argc, const char *argv[], const char *optval[]);
Return next command-line option-keyword string and associated value. (In clFileOpt.c)
int getBlob(const char *fileName, int recordSize, struct fcAsBlob *fB);
Pedestrian - and rather inefficient but portable - simulation of memory mapped files; adequate only for API demo and test programs. The interface with the invoker is via fB argument, pointer to a structure defined as: struct fcAsBlob { void *start; int64_t n; };. (In fileBlob.c)
int putBlob(const char *fileName, int recordSize,
const struct fcAsBlob *fB);
Write the file content from the memory blob back o the file. (In fileBlob.c)
void freeBlob(struct fcAsBlob *fB);
Release the memory ocuppied by the file image. (In fileBlob.c)

Nemo types as text strings

In program development and tracing while debugging the code it is frequently beneficial to produce messages with "human-readable" representation (C strngs) of binary data types used by the application. Following is a list of functions, all in nemoStrings.c that produce such strings. All functions that follow below return a pointer to a internal static buffer containing the string, so they can be conveniently included in calling sequences of printf() and similar functions.

char *nemo_StrEllCoords(const nemoPtEll *ptEll);
Coordinate string from ellipsoid φ and λ coordinates.
char *nemo_StrEnrCoords(const nemoPtEnr *ptEnr);
Coordinate string from ellipsoid i, j, k coordinates.
char *nemo_StrNcsCoords(const nemoPtNcs *ptNcs);
Coordinate string from NCS (near-conformal sphere i, j, k) coordinates.
char *nemo_StrUs8Coords(nemoPtUs8 ptUs8;
Coordinate string from Us8 coordinates.
char *nemo_StrUs4Coords(nemoPtUs4 pyUs4);
Coordinate string from Us4 coordinates.
char *nemo_StrEllDist(const nemoPtEll *ptEllA, const nemoPtEll *ptEllB);
Geodesic distance between two ellipsoid φ, λ points.
char *nemo_StrEnrDist(const nemoPtEnr *ptEnrA, const nemoPtEnr *ptEnrB);
Geodesic distance between two ellipsoid i, j, k points.
char *nemo_StrNcsDist(const nemoPtNcs *ptNcsA, const nemoPtNcs *ptNcsB);
Geodesic distance between two NCS (near-conformal sphere) points.
char *nemo_StrUs8Dist(nemoPtUs8 ptUs8A, nemoPtUs8 ptUs8B);
Geodesic distance between two Us8 points.
char *nemo_StrCs4Dist(nemoPtUs4 ptUs4A, nemoPtUs4 ptUs4B);
Geodesic distance between two Us4 points.
char *nemo_StrChSqDist(double chSq);
Arc on planet surface from chord squared.
char *nemo_StrUs8Bits(nemoPtUs8 ptUs8);
Bits of Us8 in "Most Significant First" order.
char *nemo_StrSexagesimal(double angle);
Angle (in radians) sexagesimal measure string.

Program tracing with text messages

The mechanism of textual tracing is simple: When the tracing is initialized, a text file is created with the same name as the executing binary appended by "_pTr.txt". It is co‑resident with the executable. The tracing messages are written to the file in a manner similar to debug messages that a typycal C program would write to stderr file. The content of the file can be displayed with "tail" or similar utility that appends newly added text to the terminal window in which it executes.

Tracing is initialized usnig a macro (NEMO_PTRACE_START((args));), writing of messages is similarly performed by a macro (NEMO_PTRACE(()args));), and a macro (NEMO_PTRACE_END(());) is likewise used to close the file and terminate tracing. Since all those macros default to "no-operation", they can be safely left in the application code, provided only that NEMO_PTRACE_ACTIVE is left undefined for the production builds. (The details are provided above, under Assertions...)

NEMO_PTRACE_START
Initialize text tracing by opening the message text file. If the file exists, it is appended to, so that a single trace file can containg a permanent record of a number of program executions, possible after multiple recompiles. The first argument progName is the base name of the executable the second progAsof is the string returned by yearDayTimeMod() Nemo scullion; the textual year.yearDay and hour:minute timestamp denoting its compile time:
NEMO_PTRACE_START((progName, progAsof));
NEMO_PTRACE
Mesage output is performed using a variable argument list where the first argument is the "format string" and the arguments that follow are variables to be written in textual form under the control of the format; similar to the C library printf() functions - for example:
NEMO_PTRACE(("Map scale: %.1f", gMap.scale));
NEMO_PTRACE_END
Close the file and terminate tracing. The macro takes no arguments:
NEMO_PTRACE_END(());

Program tracing with graphical output

During its execution, the program can display point and line objects (and their colecttions) to a .png file, which can be displayed and continously updated by any image viewer capable of detecting that the content of the file has been changed and updates the image display window when that happens.

This implmentation uses Cairo graphics library (https://www.cairographics.org/) to create and update the .png file, and image viewer utilitiy such as feh (under Linux, or ImageGlass, Honeytable,... (?) under MS Windows) to (re)display graphical output as the program executes. The file is named same as the binary, appended by "_gTr.png". It is co‑resident with the executable.

Graphical output is creted by calls such as NEMO_GTRACE((argumentList));, where the variable argument list is dependent on the first argument in the list; the enumerated constant "operation‑code". Implemented operation codes, the operations' purposes and the argument lists are documented below:

NEMO_GTR_START
Initialize graphical tracing, acquire graphical back‑end resources and create the output .png file. progName is the name used as the base of the output .png file name, width and height are dimensions of the display surface in pixels and pixelsPerMeter defines the final display device pixel density. Note that all display dimension related arguments are double variables - as required by the Cairo Library API. If an exact map scale (display to planetary surface) is desired, the coordinate transfrmation pipeline must be given the exat pixel/meter value; otherwise a value that best approximates pixel pitch on most displays the tracing will be done should be used (NEMO_PIXEL_DENSITY or similar):
NEMO_GTRACE((NEMO_GTR_START, const char *progName, double width,
double height, double pixelsPerMeter));
NEMO_GTR_MAPGEOM
Initialize ot change coordinate transformation pipeline from planetary surface to the display window. Only Gnomonic, Stereographic, Orthographic and Stylindrical projections are implemented for graphical tracing. In case of the first three projections the center coordinates are those of projection tangency point. In case of the last one, the latitude of center is ignored, and the coordinates are only used to determine the longitude of central meridian of the cylindrical projection; while the scale is completely ignored (value of 1.0 may be given) - as it is determined solely by the display width and height provided previously:
NEMO_GTRACE((NEMO_GTR_MAPGEOM, int projection, nemoPtNcs *center,
double scale));
NEMO_GTR_COLOR
Change the default foreground color. The tracing specifies colors using colorName string. The application can choose either of the two common name sets by including either nemoX11Colors.h or nemoCss2Colors.h C header:
NEMO_GTRACE((NEMO_GTR_COLOR, const char *colorName));
NEMO_GTR_MARKERSIZE
Tracing displays a point object using only a single "symbol" (or "marker"): more or less "fat pixel". The size argument, markerSize is roughly equivalent to a display pixel size, but graphical back‑end requires it to be a real number (i.e., a double) and it (by default) performs "antialiasing"; thus the effect of the argument magnitude may not be exactly what was expected:
NEMO_GTRACE((NEMO_GTR_MARKERSIZE, double markerSize));
NEMO_GTR_LINEWIDTH
Change the default line width. The argument lnWidth is roughly equivalent to display pixel size:
NEMO_GTRACE((NEMO_GTR_LINEWIDTH, double lnWidth));
NEMO_GTR_BACKGROUND
Clear the graphical display surface background. The background can be either the content of an external .png file (bgFileName) of the same width and height ad the current tracing display, or it can be a color specified via colorName argument; which method is used depends on which one of the two arguments is non‑NULL:
NEMO_GTRACE((NEMO_GTR_BACKGROUND, const char *bgFileName,
const char *colorName));
NEMO_GTR_PAUSE
Pause program execution by a period given as milliSeconds:
NEMO_GTRACE((NEMO_GTR_PAUSE, int milliSeconds));
NEMO_GTR_WAIT
Suspend program execution until the [Enter] key is pressed:
NEMO_GTRACE((NEMO_GTR_WAIT));
NEMO_GTR_END
Release all grapical resources used by the back‑end and terminate graphicl tracing:
NEMO_GTRACE((NEMO_GTR_END));
NEMO_GTR_WRITE
Write the content of the display surface to the disk file. If the argument pngFileName is NULL the default (see above) tracing .png file name will be updated. Note that (as documented below) the update of the default tracing .png file is done automatically only after the collection (a file, a memoy blob) of points or lines is traced, but not when a sigle point or line segment is traced; this gives the program the ability to control how often the default .png file is changed (and, presumably, re‑displayed):
NEMO_GTRACE((NEMO_GTR_WRITE, const char *pngFileName))
NEMO_GTR_POINT
Display a single point location. The point coordinates are on the Nemo near‑conformal sphere, and may be specified using either nemoPtNcs (by a non‑NULL ptNcsA argument) or by Us8 cordinates using ptUs8A argument, which is ignored if the fomer is non‑NULL. If the colorName is NULL display will be done with the default foreground color and marker size. If colorName is given, a valid markerSize must also be given and both will be used as graphical attributes to render the point. The default .png file is not updated by this opetation:
NEMO_GTRACE((NEMO_GTR_POINT, const nemoPtNcs *ptNcs, nemoPtUs8 ptUs8,
const char *colorName, double markerSize));
NEMO_GTR_LINESEG
Display a single line segment. Other than providing two pairs of arguments (ptNcsA, ptUs8A for start and ptNcsA, ptUs8A for end of the segment) this operation behaves exactly like the one above:
NEMO_GTRACE((NEMO_GTR_LINESEG, const nemoPtNcs *ptNcsA, nemoPtUs8 ptUs8A,
const nemoPtNcs *ptNcsB, nemoPtUs8 ptUs8B,
const char *colorName, double lineWidth));
NEMO_GTR_PTBLOB
A "blob" in this document is a memory resident table of records, each consisting of an integer number of 8‑byte "blocks". The first block in the record is a Us8 poit coordinate of a point to be displayed using the default graphical attributes. ptsUs8 is the base of the table; nRecords is the number of records in the table, and the recordBlocks specifies hw many 8‑byte blocks a record consists of. The operation displays the point with the Us8 coordinates in the first block and ignores the rest (if any) of the blocks in the record. If the Us8 coordinate of the point to be displayed is "undefined" (cf.: NEMO_Us8IsUndef()), the point is skipped. Once all points are displayed, the content of the default trace .png file is updated:
NEMO_GTRACE((NEMO_GTR_PTBLOB, const nemoPtUs8 *ptsUs8,
int64_t nRecords, int recordBlocks));
NEMO_GTR_LNBLOB
The operation performs the display of polylines from the memory "blob" like the one defined above. The "undefined" point coordinates are considered to represent polyline terminators:
NEMO_GTRACE((NEMO_GTR_LNBLOB, const nemoPtUs8 *ptsUs8,
int64_t nRecords, int recordBlocks));
NEMO_GTR_PTFILE
The operation performs the display of a set of points in the same manner as NEMO_GTR_PTBLOB, except that the table is stored in a ptFn disk file. Since the file size is implicitly given, there is no need for the invoker ro provide the count of records, only the count of 8‑byte blocks in each record:
NEMO_GTRACE((NEMO_GTR_PTFILE, const char *ptFn, int recordBlocks));
NEMO_GTR_LNFILE
The operation performs the display of a set of poly-lines in the same manner as NEMO_GTR_LNBLOB, except that the table is stored in a lnFn disk file. The polyline terminators are again the undefined Us8 coordinatesss:
NEMO_GTRACE((NEMO_GTR_LNFILE, const char *lnFn, int recordBlocks));

The content is published under Creative Commons ByNcNd license.
Date of first publication: 2025.244 last revision: 2025.248 author: [Hrvoje Lukatela]