/* JL_UTIL1.C - source code for functions in JL_UTIL1.LIB * * Jeff Lucius * Geophysicist * U.S. Geological Survey * Box 25046 Denver Federal Center MS 964 * Denver, CO 80225-0046 * 303-236-1413 (office), 303-236-1212 (secretary), 303-236-1425 (fax) * email: lucius@usgs.gov * * Revision Dates: August 24, 1999; * * To compile for use in JL_UTIL1.LIB: * icc /F /xnovm /zmod486 /zfloatsync /c JL_UTIL1.C * * /F removes the floating point emulator, reduces binary size * /xnovm specifies no virtual memory manager, disk access is too slow * /zmod486 generates specific 80486 instructions, speeds up program. * /zfloatsync insures the FPU is operand-synchronized with the CPU. * /c compiles only, no linking. * * to remove assert() functions from code also add: * /DNDEBUG * * Functions in this module: * ANSI_there - tests to see if ANSI.SYS is loaded * BandPassFFT1D - bandpass frequency filter of a time series * CreateDir - creates a DOS directory at any allowed level * FftFilterTrace - high/low frequency filter of a time series * FFT1D - performs standard FFT/IFFT of a time series * GetDosVersion - reports DOS version (old style) * InstAttribTrace - calcs inst-amp or inst-pow of a time series * LinFit - least-squares fit to data with a straight line * RealFFT1D - performs "packed" FFT of real function * Sound - generates sound using PC speaker * Spline - cubic spline * Spline2 - cubic spline allowing interp outside of ends * TrimStr - removes blanks/unprintables from start and end of string */ /******************* Includes all required header files *********************/ #include "jl_util1.h" /**************************** Global variables ******************************/ const char *CreateDirMsg[] = { "CreateDir(): No errors.", "CreateDir() ERROR: pathname is empty.", "CreateDir() ERROR: drive letter missing.", "CreateDir() ERROR: drives A and B are not allowed.", "CreateDir() ERROR: improper path; doubled backslashes", "CreateDir() ERROR: unable to count number of directories.", "CreateDir() ERROR: unable to make ALL the directories (some may exist).", "CreateDir() ERROR: directory path too long to add backslash.", } ; const char *LinFitMsg[] = { "LinFit(): No errors.", "LinFit() ERROR: Number of data pairs less than 3.", "LinFit() ERROR: NULL pointer sent to function.", } ; const char *SplineMsg[] = { "Spline(): No errors.", "Spline() ERROR: arrays too small to spline.", "Spline() ERROR: independent variable array not strictly monotonic.", "Spline() ERROR: interpolated independent variable array not strictly monotonic.", "Spline() ERROR: interpolated independent variable array exceed limits.", "Spline() ERROR: unable to allocate temporary storage.", } ; const char *Spline2Msg[] = { "Spline2(): No errors.", "Spline2() ERROR: arrays too small to spline.", "Spline2() ERROR: independent variable array not strictly monotonic.", "Spline2() ERROR: interpolated independent variable array not strictly monotonic.", "Spline2() ERROR: unable to allocate temporary storage.", } ; /******************************* ANSI_there() *******************************/ /* Determine if the ANSI.SYS driver has been installed. * * Parameter list: * void * * Requires: , "jl_defs.h". * Calls: int86, GetDosVersion. * Returns: int = 0 if ANSI.SYS not installed, * 1 if it is installed. * * Author: Jeff Lucius K2 Software Lakewood, CO * Date: February 5, 1996 */ int ANSI_there (void) { union REGS regs; int mode,major_ver,minor_ver; GetDosVersion(&mode,&major_ver,&minor_ver); if (major_ver >= 4) { regs.x.ax = 0x1A00; int86(INT_MULTIPLEX,®s,®s); if (regs.h.al == 0) return 0; /* not installed */ else return 1; } else return 0; } /***************************** BandPassFFT1D() ******************************/ /* High-, low-, or band-pass filter a real time series using a discrete FFT * and half of a Hanning window to smooth the cuttoffs. * FFT filtering uses the "packed" real-values-only FFT modified from "Numerical Recipes in C", so wavenmubers are organized as, assuming trace length == 512: wavenumber = 0,256,1r,1i,2r,2i,3r,3i, ... , 254r,254i,255r,255i array index = 0, 1, 2, 3, 4, 5, 6, 7, ... , 508, 509, 510, 511 where wavenumber: 0 = DC offset, 256 = Nyquist, 1 = fundamental, r = real part, and i = imaginary part. This arangement works becaues the imaginary parts of the DC and Nyquist frequencies have no imaginary component - there can be no phase shift therefore the phase spectrum [inv-tan (imag/real)] must be 0, and the imaginary part must be 0 (division by 0 is illegal). In a "normal" complex FFT, 1024 samples would be required for the 512 sample-length trace. FFT values are "mirrored" around the "Nyquist midpoint" with the sign of the sine transform (imaginary component) values reversed from one side to the other. Cosine transform (real component) values maintain same sign. The "packed" real fft takes advantage of this symmetry, preserving the actual values, and the sign of the "first-half". * * Parameters: * long data_length = length of the real time series. * long hwindow = half-width (one side) of Hanning window to apply, * should be an even number. * long low_cutoff = index of FFT (wavenumber) to start saving coefficients; * coefficients below this value will be smoothed to zero; * long high_cutoff = index of FFT to stop saving cofficients. * coefficients above this value will be smoothed to zero; * double *data = real array of values "data_length" in size to be filtered. * * NOTE: To save time, the calling function must allocate storage for all * arrays and there is little error checking. Here is the check for * valid ranges of the cutoffs: * if (low_cutoff < 0) low_cutoff = 0; * if (high_cutoff < 0) high_cutoff = 0; * if (low_cutoff > trace_length/2) low_cutoff = trace_length/2; * if (high_cutoff > trace_length/2) high_cutoff = trace_length/2; * if (high_cutoff < low_cutoff) high_cutoff = low_cutoff; * * This version calls RealFFT1D() which returns only the positive * frequency half of the FFT, with the base level stored as data[0] * the "Nyquist" value stored as data[1]. * * The Hanning window half-width will be adjusted smaller if too wide * for the low- or high-cutoff specified. * * Requires: , and "jl_util1.h". * Calls: assert, cos, RealFFT1D. * Returns: void, but data[] now contains the band-passed time series. * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: February 1, 1993, January 25, 1996, February 5, 1996 */ void BandPassFFT1D (long data_length,long hwindow,long low_cutoff,long high_cutoff, double *data) { long i; int default_win = 8; /* smooth cutoff over 8 values */ int win1,win2; long nyquist = data_length/2; double step,t,dtemp; static double pi = 3.14159265358979323846; assert(data_length > 1 && data); /* Verify valid ranges for input values */ if (hwindow <= 0) hwindow = default_win; if (hwindow % 2) hwindow++; /* window width must be even */ win1 = win2 = hwindow; if (low_cutoff < win1) win1 = low_cutoff; if ((nyquist-high_cutoff) < win2) win2 = nyquist-high_cutoff; /* User supplied low and high cutoffs assume standard FFT of a 512-long * scan. But this method has packed the positive real and imaginary sets * into the 512-long series. So a low-cutoff of 6 is now at 12. */ low_cutoff *= 2; high_cutoff *= 2; /* Forward transform */ RealFFT1D(data,data_length,1); /* Do high-pass (low-cut) part of filter */ if ((low_cutoff>0) && (low_cutoff<(data_length-2))) /* the valid useful range */ { step = pi/(double)win1; data[0] = 0.0; /* base level or mean */ /* skip over data[1] which is nyquist */ for (i=2; i<=low_cutoff-2*win1; i+=2) { data[i] = 0.0; data[i+1] = 0.0; } for (t=step,i=(low_cutoff-2*win1+2); t0) && (high_cutoff<(data_length-2))) /* the valid useful range */ { step = pi/(double)win2; data[1] = 0.0; /* nyquist */ for (i=high_cutoff+2*win2; i<=data_length-2; i+=2) { data[i] = 0.0; data[i+1] = 0.0; } for (t=step,i=(high_cutoff+2*win2-2); t, "jl_defs.h", "assertjl.h". * Calls: assert, int86. * Returns: void. * * Author: Jeff Lucius K2 Software Lakewood, CO * Date: February 5, 1996 */ void GetDosVersion(int *mode,int *major_ver,int *minor_ver) { union REGS regs; assert(mode && major_ver && minor_ver); *mode = 0; regs.x.bx = 0x0000; regs.x.ax = 0x3306; /* this function not changed by SETVER */ int86(INT_DOS,®s,®s); if (regs.x.bx == 0x0000) /* version less than 5.0 */ { regs.x.ax = 0x3000; int86(INT_DOS,®s,®s); if (regs.h.al == 0) { *major_ver = 1; *minor_ver = 0; /* actually any of the 1.x versions */ } else { *major_ver = regs.h.al; /* for version 3.3, would be 3 */ *minor_ver = regs.h.ah; /* for version 3.3, would be 30 */ } } else { *major_ver = regs.h.bl; *minor_ver = regs.h.bh; *mode = (regs.h.dh & 0x10); } } /*************************** InstAttribTrace() ****************************/ /* Transform trace amplitudes into instantaneous amplitude or power. * The input trace must be an array of doubles previously allocated. * The trace can be optionally smoothed at the ends using a Hanning window * before the forward FFT (the ends are not unsmoothed after the FFT, though). * The time series average is removed before FFT and NOT added back. * * On input, dscan[] holds the original time series and cscan[] is empty. * On output, dscan[] holds the instantanaeous aspect of the time series and * the even (real) array members of cscan[] hold the Hilbert * Transform of the input time series. * * Parameters: * long length = length of the time series * int preproc = TRUE to smooth ends of the time series before FFT. * int attrib == 0, invalid value = no action * == 1, for instantaneous amplitude * != 1, for instantaneous power * double *dscan = [length] allocated work array * double *cscan = [2*length] allocated work array * * NOTE: To save time, the calling function must allocate storage for the * arrays. If the length of the trace IS NOT a power of 2 the function * returns. * * NOTE: There are no error messages! If attrib == 0, or length is not a power * if 2 then the function just returns. * * Requires: , "jl_util1.h", "assertjl.h". * Calls: assert, cos, pow, FFT1D. * Returns: void. * * Author: Jeff Lucius USGS Lakewood, CO lucius@usgs.gov * Date: August 19, 1999 * Revisions: August 24, 1999; */ void InstAttribTrace (long length,int preproc,int attrib,double *dscan, double *cscan) { long i,itemp; /* scratch variables */ double davg; /* arithmetic average of the time series */ double t,step,dtemp; /* scratch variables */ static int pwindow = 24; /* size of Hanning window used to preprocess */ static double twopi = 2 * 3.14159265358979323846; /* Reality check */ assert(length > 1 && dscan && cscan); if (attrib == 0) return; /* do nothing */ /* Check if trace length a power of 2, <= 65536 */ { for (i=1; i<16; i++) { if (length > pow(2,(double)i) && length < pow(2,(double)(i+1))) { return; /* do nothing */ break; } } } /* Find average value of the time series */ davg = 0.0; for (i=0; i IM; IM*-1 -> RE NEG freq: RE*-1 -> IM; IM -> RE */ for (i=0; i imag part */ cscan[i*2] = -dtemp; /* imag *= -1 --> real part */ } else /* negative frequencies */ { cscan[(i*2)+1] = -cscan[(i*2)]; /* real *= -1 --> imag part */ cscan[i*2] = dtemp; /* imag --> real part */ } } /* inverse transform */ FFT1D(cscan,length,-1); /* Hilbert is real part of cscan; [i*2] */ /* calculate instantaneous amp/pow */ for (i=0; i 0) dscan[i] = sqrt(dscan[i]); else dscan[i] = 0.0; } } return; } /****************************** FftFilterTrace() ****************************/ /* High-, low-, or band-pass filter a GPR trace using an FFT filter. * The input trace must be an array of doubles previously allocated. * The trace can be optionally smoothed at the ends using a Hanning window * before the forward FFT (the ends are not unsmoothed after the FFT, though). * * Parameters: * long trace_length = length of the GPR trace. * int preproc = TRUE to smooth ends of trace before FFT. * long low_cutoff = index of FFT (wavenumber) to start saving coefficients; * coefficients below this value will be smoothed to zero; * long high_cutoff = index of FFT to stop saving cofficients; * coefficients above this value will be smoothed to zero; * double *dscan = work array "trace_length" in size. * * NOTE: To save time, the calling function must allocate storage for the * array and there is little error checking. Here is the check for * the cutoffs: * if (low_cutoff < 0) low_cutoff = 0; * if (high_cutoff > trace_length/2) high_cutoff = trace_length/2; * if (high_cutoff < low_cutoff) high_cutoff = low_cutoff; * * Requires: , and "jl_util1.h", "assertjl.h". * Calls: assert, cos, BandPassFFT1D. * Returns: void. * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: November 16, 1993, February 5, 1996 */ void FftFilterTrace (long trace_length,int preproc,long low_cutoff, long high_cutoff,double *dscan) { long i,itemp; /* scratch variables */ double dmean; /* arithmetic mean of the trace */ double t,step,dtemp; /* scratch variables */ static int pwindow = 24; /* Hanning window used to preprocess */ static long fwindow = 8; /* half-width of Hanning window used by filter */ static double twopi = 2 * 3.14159265358979323846; assert(trace_length > 1 && dscan); /* Find average value of the time series */ dmean = 0.0; for (i=0; i. * Calls: sin, FFT1D(). * Returns: void. * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: November 10, 1992 */ void RealFFT1D (double *data,long nn,int isign) { long i,i1,i2,i3,i4,n2p2,n; double c1,c2,h1r,h1i,h2r,h2i; double wr,wi,wpr,wpi,wtemp,theta; double wrth2r,wrth2i,with2r,with2i; static double pi = 3.14159265358979323846; n = nn >> 1; theta = pi/(double)n; c1 = 0.5; if (isign == 1) /* forward transform */ { c2 = -0.5; FFT1D(data,n,1); } else /* set up for inverse transform */ { c2 = 0.5; theta = -theta; } wtemp = sin(0.5*theta); wpr = -2.0*wtemp*wtemp; wpi = sin(theta); wr = 1.0 + wpr; wi = wpi; n2p2 = n + n + 2; for (i = 1; i 0) /* finish forward transform */ { h1r = data[0]; data[0] += data[1]; data[1] = h1r - data[1]; } else /* inverse transform */ { h1r = data[0]; data[0] = c1 * (h1r+data[1]); data[1] = c1 * (h1r-data[1]); FFT1D(data,n,-1); } return; } /********************************** FFT1D() *********************************/ /* FFT modified from "Numerical Recipes in C" so that the array is indexed * from 0. Replaces data[] by its discrete Fourier transform, if isign == 1. * Replaces data[] by its inverse discrete Fourier Transform, if isign == -1. * data[0...2*nn-1] is a double array of complex numbers arranged as real, * imaginary,real,imaginary,real,imaginary, etc. nn MUST be an integer power * of 2 (which is NOT verified). If real data are used then the imaginary * components of data[] must be 0.0; stuff data[] using something like this: * for (i=0; i. * Calls: sin. * Returns: void. * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: November 10, 1992 */ void FFT1D (double *data,long nn,int isign) { long i,j,m,n,mmax,istep,jp1; double wr,wpr,wpi,wi,theta,tempr,tempi; static double twopi = 6.28318530717958647692; n = nn << 1; j = 0; for (i=0; i i) { tempr = data[j]; data[j] = data[i]; data[i] = tempr; tempr = data[j+1]; data[j+1] = data[i+1]; data[i+1] = tempr; } m = nn; while (m >= 2 && j >= m) { j -= m; m >>= 1; } j += m; } mmax=2; while (n > mmax) { istep = mmax << 1; theta = twopi/(double)(isign*mmax); tempr = sin(0.5*theta); wpr = -2.0*tempr*tempr; wpi = sin(theta); wr = 1.0; wi = 0.0; for (m=1; m chdir, mkdir * _splitpath, _MAX_DIR, _MAX_PATH, _MAX_FNAME, _MAX_EXT * strcpy, strchr, strcat, strlen * * Non-library function calls: * none * * Returns: 0 on success, * >0 on error (offset into message string) * -1 if path already exists * const char *CreateDirMsg[] = { "CreateDir(): No errors.", "CreateDir() ERROR: pathname is empty.", "CreateDir() ERROR: drive letter missing.", "CreateDir() ERROR: drives A and B are not allowed.", "CreateDir() ERROR: improper path; doubled backslashes", "CreateDir() ERROR: unable to count number of directories.", "CreateDir() ERROR: unable to make ALL the directories (some may exist).", "CreateDir() ERROR: directory path too long to add backslash." } ; * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: August 3, 1999 */ int CreateDir (char *dirname) { char drive[_MAX_DIR]; char path[_MAX_PATH]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; char copy[_MAX_PATH]; char str[_MAX_PATH]; char *cp,*cp1,*cp2; int ch = '\\' ; /* 92 = '\' */ int len,i,num_dirs,atend,return_val,made; unsigned old_drive,num_drives,new_drive; /* NULL string? */ if (dirname == NULL) return 1; /* add trailing '\' so splitpath() doesn't interpret a file */ len = strlen(dirname); if (dirname[len-1] != ch) { if ( len < (_MAX_PATH-1) ) { dirname[len] = ch; dirname[len+1] = 0; } else { return 7; } } _splitpath(dirname,drive,path,fname,ext); /* Is a drive letter given? */ if (drive[0] == 0) return 2; /* Get current drive and make sure > 1 (C: or higher)*/ new_drive = drive[0]; /* get ANSI representative for letter */ new_drive -= 'A'; if (new_drive < 2) return 3; /* add leading '\' if missing so directory counter works correctly */ if (path[0] != ch) { strcpy(copy,dirname); /* backup for safety */ strcpy(dirname,drive); strcat(dirname,"\\"); strcat(dirname,path); } /* check for 2 or more backslashes */ if (strstr(dirname,"\\\\")) return 4; /* Copy string */ strcpy(str,dirname); /* count directories */ if ( (cp1 = strchr(str,ch)) == NULL ) return 5; cp1++; /* point past first '\' */ num_dirs = 1; while ( (cp1 = strchr(cp1,ch)) != NULL ) { cp1++; /* point past next '\' */ num_dirs++; } /* point to first '\' */ strcpy(str,dirname); if ( (cp1 = strchr(str,ch)) == NULL ) return 5; atend = 0; return_val = 0; made = 0; for (i=0; i, "jl_utils.h" * Calls: sqrt. * Returns: 0 on success, or * >0 if error or warning (offset into an array of message strings). * const char *LinFitMsg[] = { "LinFit(): No errors.", "LinFit() ERROR: Number of data pairs less than 3.", "LinFit() ERROR: NULL pointer sent to function.", } ; * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: February 10, 1997 */ int LinFit (int npts,int mode,double *X,double *Y,double *sigmaY,double *A, double *sigmaA,double *B,double *sigmaB,double *R) { int i; double sum,sumx,sumy,sumxx,sumxy,sumyy,x,y,weight,delta,var; /* Verify calling parameters */ if (npts <=3) return 1; if (X==NULL || Y==NULL || A==NULL || B==NULL || R==NULL || sigmaA==NULL || sigmaB==NULL || (mode>0 && sigmaY==NULL)) return 2; /* Initialize variables */ sum = sumx = sumy = sumxx = sumxy = sumyy = 0.0; if (mode < 0) mode = -1; if (mode > 0) mode = 1; /* Accumulate weighted sums */ for (i=0; i 0) weight = 1.0 / y; break; case 0: weight = 1.0; break; case 1: weight = 1.0 / (sigmaY[i]*sigmaY[i]); break; } sum += weight; sumx += weight * x; sumy += weight * y; sumxx += weight * x * x; sumxy += weight * x * y; sumyy += weight * y * y; } /* Calculate coefficients and standard deviations */ delta = (sum * sumxx) - (sumx * sumx); *A = ((sumxx * sumy) - (sumx * sumxy)) / delta; *B = ((sumxy * sum) - (sumx * sumy)) / delta; switch (mode) { case -1: case 1: var = 1.0; break; case 0: i = npts - 2; var = (sumyy + (*A * *A * sum) + (*B * *B *sumxx) - (2.0 * ((*A *sumy + *B * sumxy) - (*A * *B * sumx))) ) / i; break; } *sigmaA = sqrt((var * sumxx) / delta); *sigmaB = sqrt((var * sum) / delta); *R = ((sum * sumxy) - (sumx * sumy)) / sqrt(delta*((sum*sumyy) - (sumy*sumy))); return 0; } /********************************** Sound() *********************************/ /* The system timer, a 8254 programmable interrupt timer (PIT) or equivalent, * controls the motion of the speaker. The default PIT oscillating * frequency is the system bus oscillator's 14.31818 MHz signal divided * by 12 which is 1.1931817 MHz. For the speaker to generate a frequency * of say 2000 Hz it must be cycled 2000 times a second. The timer * frequency is divided by the desired frequency and Channel 2 of the PIT * is sent this value (programmed) to achieve this. The PIT then waits * that number of oscillations before moving the speaker cone. * * The duration is calculated using the timer tick count stored in the BIOS * data area (0040:006C) and assumes there are 18.2 clicks per second. * If the timer frequency has been increased, this function will not * be "working" in seconds unless the default ISR is invoked appropriately. * Duration is accurate to about 1/18 of a second. * * Parameters: * double freq - speaker output in Hz. * double duration - how long to leave the speaker on in seconds. * * The following must be supplied: * #include or ** for _inbyte, _outbyte ** * * Requires: , . * Calls: IN1, OUT1, PEEK4. * Returns: void. * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: April 5, 1993 */ #define GRDR_TIMERFREQ 1193181.7 /* PIT system timer frequency; 14318180./12. */ #define GRDR_CH0_FREQ 18.206508 /* default Ch.0 interrupt frequency (1193181.7/65536.) */ #define GRDR_DATASEG 0x0040 /* BIOS data area segment */ #define GRDR_OFFSET 0x006C /* offset to timer count LSB in BIOS data area */ #define Beep() Sound(880.0,0.1) #define IN1(port) (int)(_inbyte((unsigned short)(port))) #define OUT1(port,val) (_outbyte((unsigned short)(port),(unsigned char)(val))) #define PEEK4(seg,off) (*(long*)((unsigned)((seg)<<4)+(unsigned)(off))) void Sound (double freq,double duration) { unsigned char portval; unsigned long end; union { unsigned divisor; unsigned char c[2]; } count; /* Verify parameters */ if ((freq!=0.0) && (freq<20.0)) freq = 20.0; if (freq > 20000.0) freq = 20000.0; if (duration < 0.055) duration = 0.055; /* Calculate output value for PIT Channel 2 */ if (freq > 0.0) count.divisor = (unsigned)((double)GRDR_TIMERFREQ/freq); else count.divisor = 0; OUT1(0x43, 0xb6); /* Tell PIT that LSB then MSB is headed for Ch. 2 (using operation mode 3 - square wave, and 16-bit binary counters) */ OUT1(0x42, count.c[0]); /* least significant byte */ OUT1(0x42, count.c[1]); /* most significant byte */ /* Make the sound */ portval = IN1(0x61); /* get I/O port 61h bit settings (8 bits) */ if (freq > 0.0) { end = (unsigned long)(duration * GRDR_CH0_FREQ); OUT1(0x61, portval|0x03); /* set bits 0 and 1 to turn speaker on and direct control through PIT Ch. 2 */ end += (unsigned long)PEEK4(GRDR_DATASEG,GRDR_OFFSET); while ((unsigned long)PEEK4(GRDR_DATASEG,GRDR_OFFSET) < end) ; /* do nothing */ } OUT1(0x61, portval&0xfc); /* clear bits 0 and 1 to turn speaker off */ } #undef GRDR_TIMERFREQ #undef GRDR_CH0_FREQ #undef GRDR_DATASEG #undef GRDR_OFFSET #undef Beep #undef IN1 #undef OUT1 #undef PEEK4 /******************************** Spline() **********************************/ /* This function performs a cubic spline interpolation to get the values * "ynew[i]" as a function of "xnew[i]". "xnew[]" is an independent variable * array assigned values between (and/or inclusive of) the endpoints of the * independent series "x[]". The values "y[i]" are defined for every point * "x[i]". Essentially, "ynew[]" is interpolated along "y[]". * * NOTE: Both "x[]" and "xnew[]" must be strictly monotonically increasing, * with no adjacent values equal. This version of the function * permits monotonically decreasing values, by "reversing" the series * before splining then returning the values to original order. "x[]" * "znew[]" may be monotonic on opposite or the same directions. * * ##################################################################### * # # * # NOTE: Versions before 2/12/97 have 2 MAJOR!!! bugs. # * # 1. When sequences were "reversed" only X was reversed! # * # 2. When xnew was reversed n, rather than nnew, was used. # * # # * ##################################################################### * * Parameters: * Input: * n - size of x[] and y[] arrays. * new - size of xnew[] and ynew[] arrays. * x[] - independent variable array. * y[] - dependent variable array. * xnew[] - interpolated independent variable array. The start and end * of these values should not exceed the start and end of x[]. * Output: * ynew[] - interpolated dependent variable array. * * Requires: , , and attached header. * Calls: malloc, free, sqrt, fabs. * Returns: 0 on success, or * >0 if error or warning (offset into an array of message strings). * const char *SplineMsg[] = { "Spline(): No errors.", "Spline() ERROR: arrays too small to spline.", "Spline() ERROR: independent variable array not strictly monotonic.", "Spline() ERROR: interpolated independent variable array not strictly monotonic.", "Spline() ERROR: interpolated independent variable array exceed limits.", "Spline() ERROR: unable to allocate temporary storage.", } * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: February 12, 1997 */ int Spline (long n,long nnew,double x[],double y[],double xnew[],double ynew[]) { long i, j, ip1, nm1; long halfn = 0; long halfnew = 0; double *s = NULL; double *g = NULL; double *work = NULL; double eps = 1.0e-6; /* allowable error factor */ double xi, xim1, xip1, yi, xx, h, t, w, u, dtemp; /* Pre-qualify input */ if ((n < 3) || (nnew < 1)) return 1; /* are there enough points? */ nm1 = n - 1; if (x[n-1] < x[0]) /* is vector decreasing? */ { halfn = n/2; /* truncation wanted */ for (i=0,j=n-1; i x[nm1])) return 4; /* Allocate storage for work arrays */ s = (double*)malloc(sizeof(double)*n); g = (double*)malloc(sizeof(double)*n); work = (double*)malloc(sizeof(double)*n); if (s==NULL || g==NULL || work==NULL) { if (s) free(s); if (g) free(g); if (work) free(work); if (halfn) { for (i=0,j=n-1; i u) u = h; s[i] += t; } } while (u >= eps); for (i=0; i x[i])); ip1 = i; i--; h = xnew[j] - x[i]; ynew[j] = ( (y[ip1] - y[i]) / (x[ip1] - x[i]) ) * h + y[i] + ( h * (xnew[j] - x[ip1]) ) * ( (s[i] + s[ip1] + s[i] + (g[i] * h)) / 6.0 ); } /* Deallocate storage and return success */ free(s); free(g); free(work); if (halfn) { for (i=0,j=n-1; i, , and attached header. * Calls: malloc, free, sqrt, fabs. * Returns: 0 on success, or * >0 if error or warning (offset into an array of message strings). * const char *Spline2Msg[] = { "Spline2(): No errors.", "Spline2() ERROR: arrays too small to spline.", "Spline2() ERROR: independent variable array not strictly monotonic.", "Spline2() ERROR: interpolated independent variable array not strictly monotonic.", "Spline2() ERROR: unable to allocate temporary storage.", } * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: February 12, 1997 */ int Spline2 (long n,long nnew,double x[],double y[],double xnew[],double ynew[]) { long i, j, ip1, nm1; long halfn = 0; long halfnew = 0; double *s = NULL; double *g = NULL; double *work = NULL; double eps = 1.0e-6; /* allowable error factor */ double xi, xim1, xip1, yi, xx, h, t, w, u, dtemp; /* Pre-qualify input */ if ((n < 3) || (nnew < 1)) return 1; /* are there enough points? */ nm1 = n - 1; if (x[n-1] < x[0]) /* is vector decreasing? */ { halfn = n/2; /* truncation wanted */ for (i=0,j=n-1; i u) u = h; s[i] += t; } } while (u >= eps); for (i=0; i x[nm1])) continue; /* skip */ t = xnew[j]; do { i++; } while ((i < nm1) && (t > x[i])); ip1 = i; i--; h = xnew[j] - x[i]; ynew[j] = ( (y[ip1] - y[i]) / (x[ip1] - x[i]) ) * h + y[i] + ( h * (xnew[j] - x[ip1]) ) * ( (s[i] + s[ip1] + s[i] + (g[i] * h)) / 6.0 ); } /* Deallocate storage and return success */ free(s); free(g); free(work); if (halfn) { for (i=0,j=n-1; i. * Calls: strlen. * Returns: char* to beginning of string. * * Author: Jeff Lucius USGS Mineral Resources Program Lakewood, CO * Date: February 5, 1996 */ char *TrimStr (char *p) { int end,i,len,trail=0,lead=0; if (p == NULL) return p; /* return if NULL string */ if ((end=(len=strlen(p))) == 0) return p; /* count blanks etc. at end */ while ((end>0) && (p[--end]<0x21 || p[end]>0x7E)) trail++; /* strip of blanks etc. by inserting terminating 0 */ if (trail) p[len-=trail] = 0; /* return if NULL string */ if (len == 0) return p; /* count blanks etc. at beginning */ while (p[lead]<0x21 || p[lead]>0x7E) lead++; /* remove leading blanks etc. by shifting characters forward */ if (lead) for (i=lead; i<=len; i++) p[i-lead] = p[i]; /* return pointer to start of string */ return p; }