/************************************************************************/ /* SimpleResp: Generates Simplified Response From Database */ /************************************************************************/ /* Author: Stephane Zuzlewski UC Berkeley Seismological Laboratory stephane@seismo.berkeley.edu Purpose: Modification History: Date Ver Who What --------------------------------------------------------------------- 2002/05/13 1.0 SMZ Initial coding. 2003/01/02 1.01 SMZ Added # sign on header line. 2003/03/05 1.1 SMZ Added support for non HT populated networks with external file. 2003/08/25 1.11 SMZ Now supports coalesced time spans. 2004/10/20 1.2 SMZ New option to generate Simple_Response file from Channel_Data & Simple_Response tables. 2004/11/22 1.3 SMZ Added new option to compute simple response from poles & zeroes 2006/06/01 1.4 SMZ Database connection string can now be specified in an environment variable. 2006/11/06 1.5 SMZ Doesn't touch the StaCorrections table anymore. 2009/04/06 1.6 SMZ Applied changes provided by Taka to handle Metrozet sensors. 2014/06/16 2.0 SMZ Removed external channels file. Removed legacy HT generation. Now generates information for all channels. 2022/03/22 2.01 SMZ Added fixes from Taka (STS-5). */ /************************************************************************/ #ifndef lint static char sccsid[] = "%W% %G% %U%"; #endif #include #include #include #include #include #include #include "complex.h" EXEC ORACLE OPTION (ORACA=YES); #define CONNECT_STRING "user/password@dbname" #define END_OF_QUERY 1403 #define VERSION "2.01" #define TPI (8.*atan(1.)) #define PI (4.*atan(1.)) #define CABS(c) (hypot (c.real, c.imag)) #define log2(x) ((double)(log(x)/M_LN2)) char *syntax[] = { "%s version " VERSION " -- Generates Simplified Response From IR Schema.", "%s [-h] [[[-v] [-p]] | [-t]] []", " where:", " -h Help - prints syntax message.", " -v Debugging Mode.", " -p Population Mode (Replace simple_response table contents).", " -t Outputs contents of simple_response table to file.", " Environment variable DB_CONNECT --> Overwrites default database connection string.", NULL }; #define info stdout #define LINELEN 255 EXEC SQL INCLUDE sqlca.h; typedef char asciz[20]; typedef char vc2_arr[11]; EXEC SQL BEGIN DECLARE SECTION; char user_pwd[80]; char sta[7]; /* Station code */ char net[9]; /* Network code */ char usernet[9]; /* User Network Code */ char seedchan[4]; /* SEED channel code */ char location[3]; /* Location code */ char ondate[20]; /* Start date */ char offdate[20]; /* Off date */ double digi_sens; /* Digitizer sensitivity */ double hp_filter; /* Corner frequency of high-pass filter */ double lp_filter; /* Corner frequency of low-pass filter */ double nat_freq; /* Sensor natural frequency */ double damping; /* Fraction of critical damping */ double dip; /* Dip */ double azimuth; /* Azimuth */ int unit; /* Units */ char unitname[80]; /* Units Name */ double overall_gain; /* Overall Gain */ double samprate; /* Sampling Rate */ char gain_units[21]; /* Units of gain */ double dAO; /* A0 Normalization factor */ int pz_key; /* Poles & Zeroes key */ double r_value; /* Real value */ double i_value; /* Imaginary value */ int nb_zeros; /* Number of zeroes */ int nb_poles; /* Number of poles */ short ind_nf; /* Indicator */ short ind_hfc; /* Indicator */ short ind_lfc; /* Indicator */ short ind_dc; /* Indicator */ short ind_az; /* Indicator */ short ind_dp; /* Indicator */ short ind_ds; /* Indicator */ short ind_sp; /* Indicator */ char SQL_Command[1024]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE sql_stmt STATEMENT; /************************************************************************/ /* External variables and symbols. */ /************************************************************************/ char *cmdname; /* program name from command line. */ long SQLCODE; void sql_error(); /* Handles unrecoverable errors */ /************************************************************************/ /* print_syntax: */ /* Print the syntax description of program. */ /************************************************************************/ int print_syntax (char *cmd, /* program name. */ char *syntax[], /* syntax array. */ FILE *fp) /* FILE ptr for output. */ { int i; for (i=0; syntax[i] != NULL; i++) { fprintf (fp, syntax[i], cmd); fprintf (fp, "\n"); } return (0); } /************************************************************************/ /* Remove_Blank: */ /* */ /* Removes trailing blanks in a string. */ /* Returns new string */ /************************************************************************/ char* Remove_Blank (char *s) { char *p = s + strlen (s); while (--p >= s) if (*p == ' ') *p = '\0'; else break; return (s); } /************************************************************************/ /* Convert_Date: */ /* */ /* Function to convert a julian date to a Oracle date */ /************************************************************************/ char* Convert_Date (char d[20]) { EXEC SQL BEGIN DECLARE SECTION; static char nd[20]; EXEC SQL END DECLARE SECTION; EXEC SQL SELECT TO_CHAR (TO_DATE (:d, 'YYYY/MM/DD HH24:mi:ss'), 'YYYY.DDD.HH24:mi:ss') INTO :nd FROM DUAL; Remove_Blank (nd); return (nd); } /************************************************************************/ /* GenerateFromTable */ /* */ /* Function that generates the simple response information from the*/ /* simple_response and channel_data tables only. */ /************************************************************************/ int GenerateFromTable (FILE *f, char net[9]) { char nondate[20], noffdate[20]; /* Header Line */ fprintf (f, "#Net Sta Cha Loc Ondate Offdate Natural Freq. HP Corner LP Corner Damping Gain Azi Dip Units\n"); if (!strcmp (net, "")) sprintf (SQL_Command, "SELECT net, sta, seedchan, location, ondate, offdate, natural_frequency, high_freq_corner, low_freq_corner, damping_constant, gain, gain_units FROM Simple_Response ORDER BY net, sta, seedchan, location, ondate"); else sprintf (SQL_Command, "SELECT net, sta, seedchan, location, ondate, offdate, natural_frequency, high_freq_corner, low_freq_corner, damping_constant, gain, gain_units FROM Simple_Response WHERE net = '%s' ORDER BY net, sta, seedchan, location, ondate", net); EXEC SQL PREPARE sql_stmt FROM :SQL_Command; EXEC SQL DECLARE sr_c CURSOR FOR sql_stmt; EXEC SQL OPEN sr_c; EXEC SQL FETCH sr_c INTO :net, :sta, :seedchan, :location, :ondate, :offdate, :nat_freq:ind_nf, :lp_filter:ind_hfc, :hp_filter:ind_lfc, :damping:ind_dc, :overall_gain, :unitname; while (sqlca.sqlcode != END_OF_QUERY) { Remove_Blank (net); Remove_Blank (sta); Remove_Blank (seedchan); Remove_Blank (location); Remove_Blank (ondate); Remove_Blank (offdate); Remove_Blank (unitname); if (!strcmp (location, "")) strcpy (location, " "); EXEC SQL SELECT azimuth, dip INTO :azimuth:ind_az, :dip:ind_dp FROM Channel_Data WHERE net = :net AND sta = :sta AND seedchan = :seedchan AND location = :location AND :ondate = ondate; strcpy (nondate , Convert_Date (ondate)); strcpy (noffdate, Convert_Date (offdate)); if (!strcmp (location, " ")) strcpy (location, "--"); if (ind_nf == (-1)) nat_freq = 0; if (ind_hfc == (-1)) lp_filter = 0; if (ind_lfc == (-1)) hp_filter = 0; if (ind_dc == (-1)) damping = 0; if (ind_az == (-1)) azimuth = 0; if (ind_dp == (-1)) dip = 0; fprintf (f, "%-3s %-5s %-3s %-2s %-s %-s\t%-8.6f\t%8.6f\t%8.6f\t%8.6f\t%7.5E\t%7.2f\t%7.2f\t%-s\n", net, sta, seedchan, location, nondate, noffdate, nat_freq, hp_filter, lp_filter, damping, overall_gain, azimuth, dip, unitname); EXEC SQL FETCH sr_c INTO :net, :sta, :seedchan, :location, :ondate, :offdate, :nat_freq:ind_nf, :lp_filter:ind_hfc, :hp_filter:ind_lfc, :damping:ind_dc, :overall_gain, :unitname; } EXEC SQL CLOSE sr_c; return (1); } /************************************************************************/ /* ComputeCorner */ /* */ /* Function that computes the HP and LP corner frequencies. */ /************************************************************************/ int ComputeCorner (int nf, double freq[4096], double amp[4096], double *f_hp, double *f_lp) { double oort, a_max, nf_hp, nf_lp; int i, i_max; oort = 1./sqrt (2.); a_max = 0.; i_max = 0; nf_hp = 0; nf_lp = 0; for (i=1; i <= nf; i++) if (amp[i] > a_max) { a_max = amp[i]; i_max = i; } if (i_max == 1) nf_hp = 0; else for (i=i_max; i >= 1; i--) if (amp[i] < oort) { nf_hp = freq[i] + ((freq[i+1] - freq[i])/(amp[i+1] - amp[i]))*(oort - amp[i]); break; } nf_lp = freq[nf]; for (i=i_max; i <= nf - 1; i++) if (amp[i] < oort) { nf_lp = freq[i-1] + ((freq[i-1] - freq[i])/(amp[i-1] - amp[i]))*(amp[i-1] - oort); break; } *f_lp = nf_lp; *f_hp = nf_hp; return (1); } /************************************************************************/ /* ComputeSR */ /* */ /* Function that generates the simple response information from the*/ /* poles & zeroes. */ /* */ /* Input Parameters: */ /* Units */ /* Complex poles */ /* Complex zeroes */ /* Number of poles */ /* Number of zeroes */ /* Frequency units */ /* A0 normalization factors */ /* Number of A0 normalization factors */ /* Sample rate */ /* */ /* Computed Parameters: */ /* Corner frequencies (Hz) */ /* Fraction of critical damping */ /* Natural Frequency */ /************************************************************************/ int ComputeSR (char *units, double cpr[256], double cpi[256], double czr[256], double czi[256], int np, int nz, char norm, double A0[64], int iA0, double sps, double *ulp_filter, double *uhp_filter, double *udamping, double *unat_freq) { double fs[64], hs[64], delf[64], amp[4096], freq[4096]; double ws, f, w; int i, j, k, nf, nfs, idfsmin; DCOMPLEX citf, ciw; double f_hp, f_lp; double k_max, nyquist_freq; int ik_max, ik_min; ik_max = 0; k_max = 0; nyquist_freq = sps / 2.0; k_max = 100.0*log10(nyquist_freq); ik_max = (int)(k_max); f_hp = f_lp = 0; /* Calculate candidate fs, hs from complex conjugate pole pairs */ nfs = 0; if (np >= 2) { for (i=1; i <= np-1; i++) { if ( ((cpr[i] == cpr[i+1]) && (cpi[i] == -cpi[i+1])) && ((cpr[i] <= 0) && (cpr[i+1] <= 0)) ) { nfs++; ws = sqrt ((cpr[i]*cpr[i]) + (cpi[i]*cpi[i])); fs[nfs] = ws/TPI; hs[nfs] = -cpr[i]/ws; } } } /* Calculate response */ k = 0; ik_min = -400; for (i=ik_min; i <= ik_max; i++) { k++; if (i < 0) f = 1./(pow (10, (fabs((double)i)/100.))); else if (i == 0) f = 1.; else f = pow (10, ((double)i/100.)); if (norm == 'F') w = f; else w = TPI*f; ciw = dcmplx (0., w); citf = dcmplx (1., 0.); for (j = 1; j <= iA0; j++) citf = dcmult (citf, dcmplx (A0[j], 0.)); if (nz > 0) for (j=1;j <= nz;j++) citf = dcmult (citf, dcsub (ciw, dcmplx (czr[j], czi[j]))); if (np > 0) for (j=1;j <= np;j++) citf = dcdiv (citf, dcsub (ciw, dcmplx (cpr[j], cpi[j]))); amp[k] = sqrt (((citf.real)*(citf.real)) + ((citf.imag)*(citf.imag))); freq[k] = f; } nf = ik_max - ik_min + 1; ComputeCorner (nf, freq, amp, &f_hp, &f_lp); if (!strcmp (units, "M/S")) { if (nfs >= 1) for (i=1; i <= nfs; i++) delf[i] = fabs (f_hp - fs[i]); } else { if (nfs >= 1) for (i=1; i <= nfs; i++) delf[i] = fabs (f_lp - fs[i]); } if (nfs >= 1) { idfsmin = 1; if (nfs > 1) for (i=2; i <= nfs; i++) if (delf[i] < delf[idfsmin]) idfsmin = i; *unat_freq = fs[idfsmin]; *udamping = hs[idfsmin]; } else { *unat_freq = 0.; *udamping = 0.; } /* Check if f_lp is limited by FIR filtration */ if (f_lp > (sps * .4)) f_lp = sps * .4; /* Check if f_hp >= f_lp */ if (f_hp >= f_lp) f_hp = f_lp; *ulp_filter = f_hp; *uhp_filter = f_lp; return (1); } /************************************************************************/ /* Main function */ /************************************************************************/ main (int argc, char *argv[]) { int FlagNet; char nondate[20], noffdate[20]; int FlagVerbose = 0; int FlagPopulation = 0; int FlagTable = 0; FILE* fout; int i; char connect_string[255]; double cpr[256], cpi[256], czr[256], czi[256], A0[64]; int np = 0, nz = 0, iA0 = 0; int iz = 1, ip = 1; double ulp_filter, uhp_filter, udamping, unat_freq; /* Variables needed for getopt. */ extern char *optarg; extern int optind, opterr; int c; char *p; oraca.orastxtf = ORASTFERR; cmdname = ((p = strrchr(*argv,'/')) != NULL) ? ++p : *argv; strcpy (connect_string, CONNECT_STRING); /* Parse command line options. */ while ( (c = getopt(argc,argv,"hvpt")) != -1) switch (c) { case '?': case 'h': print_syntax(cmdname,syntax,info); exit(0); break; case 'v': FlagVerbose = 1; break; case 'p': FlagPopulation = 1; break; case 't': FlagTable = 1; break; default: fprintf (info, "Unknown option: -%c\n", c); exit(1); } /* Skip over all options and their arguments. */ argv = &(argv[optind]); argc -= optind; /* Reading Arguments */ strcpy (usernet, ""); if (argc == 1) FlagNet = 0; else if (argc == 2) { FlagNet = 1; strcpy (usernet, argv[1]); } else { print_syntax(cmdname,syntax,info); exit (1); } /* Opening output file */ if ((fout = fopen (argv[0], "w+t")) == NULL) { printf ("Error: Couldn't open file %s\n", argv[0]); exit (-1); } /* Connect to ORACLE. */ if ((p = (char *) getenv ("DB_CONNECT")) != NULL) strcpy (connect_string, p); EXEC SQL WHENEVER SQLERROR DO sql_error(); strcpy (user_pwd, connect_string); EXEC SQL CONNECT :user_pwd; /* Generate from tables? */ if (FlagTable == 1) { GenerateFromTable (fout, usernet); EXEC SQL COMMIT WORK RELEASE; exit (1); } /* Delete rows from Simple_Response Table */ if (FlagPopulation) { if (FlagNet == 0) { EXEC SQL DELETE FROM SIMPLE_RESPONSE; } else { EXEC SQL DELETE FROM SIMPLE_RESPONSE WHERE NET = :usernet; } } fprintf (fout, "#Net Sta Cha Loc Ondate Offdate Natural Freq. HP Corner LP Corner Damping Gain Azi Dip Units\n"); /* Database Query */ if (FlagNet == 0) strcpy (SQL_Command, "SELECT STA, NET, SEEDCHAN, LOCATION, ONDATE, OFFDATE, UNIT_SIGNAL, SAMPRATE FROM CHANNEL_DATA ORDER BY NET, STA, SEEDCHAN, LOCATION, ONDATE"); else sprintf (SQL_Command, "SELECT STA, NET, SEEDCHAN, LOCATION, ONDATE, OFFDATE, UNIT_SIGNAL, SAMPRATE FROM CHANNEL_DATA WHERE NET = '%s' ORDER BY NET, STA, SEEDCHAN, LOCATION, ONDATE", usernet); /* Declaring cursor */ EXEC SQL PREPARE sql_stmt FROM :SQL_Command; EXEC SQL DECLARE chacursor CURSOR FOR sql_stmt; /* Opening cursor */ EXEC SQL OPEN chacursor; EXEC SQL FETCH chacursor INTO :sta, :net, :seedchan, :location, :ondate, :offdate, :unit, :samprate:ind_sp; while (sqlca.sqlcode != END_OF_QUERY) { Remove_Blank (sta); Remove_Blank (net); Remove_Blank (seedchan); Remove_Blank (location); Remove_Blank (ondate); Remove_Blank (offdate); if (!strcmp (location, "")) strcpy (location, " "); if (FlagVerbose) printf ("%s %s %s %s %s %s\n", net, sta, seedchan, location, ondate, offdate); EXEC SQL SELECT NAME INTO :unitname FROM D_UNIT WHERE ID = :unit; Remove_Blank (unitname); if (ind_sp == (-1)) samprate = 0; else if (samprate < 0.) samprate = (-1.)/samprate; np = nz = iA0 = 0; iz = ip = 1; nb_poles = nb_zeros = 0; /* Retrieving AO normalization factor */ EXEC SQL DECLARE cao CURSOR FOR SELECT AO, pz_key FROM Poles_Zeros WHERE net = :net AND STA = :sta AND seedchan = :seedchan AND location = :location AND ondate = :ondate; EXEC SQL OPEN cao; EXEC SQL FETCH cao INTO :dAO, :pz_key; while (sqlca.sqlcode != END_OF_QUERY) { /* Retrieving number of poles */ EXEC SQL SELECT COUNT(*) INTO :nb_poles FROM PZ_Data WHERE key = :pz_key AND type = 'P'; /* Retrieving number of zeros */ EXEC SQL SELECT COUNT(*) INTO :nb_zeros FROM PZ_Data WHERE key = :pz_key AND type = 'Z'; A0[++iA0] = dAO; np += nb_poles; nz += nb_zeros; /* Retrieving zeros */ for (i=1;i<=nb_zeros;i++) { EXEC SQL SELECT r_value, i_value INTO :r_value, :i_value FROM PZ_Data WHERE key = :pz_key AND type = 'Z' AND row_key = :i; czr[iz] = r_value; czi[iz] = i_value; iz++; } /* Retrieving poles */ for (i=nb_zeros+1;i<=nb_zeros+nb_poles;i++) { EXEC SQL SELECT r_value, i_value INTO :r_value, :i_value FROM PZ_Data WHERE key = :pz_key AND type = 'P' AND row_key = :i; cpr[ip] = r_value; cpi[ip] = i_value; ip++; } EXEC SQL FETCH cao INTO :dAO, :pz_key; } EXEC SQL CLOSE cao; lp_filter = hp_filter = damping = nat_freq = 0; overall_gain = 1; dip = azimuth = digi_sens = 0; ind_nf = ind_dc = ind_hfc = ind_lfc = -1; if ((np > 0) || (nz > 0)) { ComputeSR (unitname, cpr, cpi, czr, czi, np, nz, 'R', A0, iA0, samprate, &ulp_filter, &uhp_filter, &udamping, &unat_freq); lp_filter = ulp_filter; hp_filter = uhp_filter; damping = udamping; nat_freq = unat_freq; ind_nf = ind_dc = ind_hfc = ind_lfc = 0; } /* Retrieving Overall Gain */ EXEC SQL SELECT SENSITIVITY INTO :overall_gain FROM SENSITIVITY WHERE STA = :sta AND NET = :net AND SEEDCHAN = :seedchan AND LOCATION = :location AND ONDATE = :ondate AND STAGE_SEQ = 0; /* Retrieving dip & azimuth */ ind_az = ind_dp = 0; EXEC SQL SELECT DIP, AZIMUTH INTO :dip:ind_dp, :azimuth:ind_az FROM CHANNEL_DATA WHERE STA = :sta AND NET = :net AND SEEDCHAN = :seedchan AND LOCATION = :location AND ONDATE = :ondate; /* Retrieving Digitizer Gain */ digi_sens = 0; EXEC SQL SELECT SENSITIVITY INTO :digi_sens FROM SENSITIVITY WHERE STA = :sta AND NET = :net AND SEEDCHAN = :seedchan AND LOCATION = :location AND ONDATE = :ondate AND STAGE_SEQ = (SELECT MIN(STAGE_SEQ) FROM COEFFICIENTS WHERE STA = :sta AND NET = :net AND SEEDCHAN = :seedchan AND LOCATION = :location AND ONDATE = :ondate); if (digi_sens == 0) ind_ds = -1; else ind_ds = 0; if ((!strcmp (location, " ")) || (!strcmp (location, ""))) strcpy (location, "--"); strcpy (nondate , Convert_Date (ondate)); strcpy (noffdate, Convert_Date (offdate)); if (ind_az == (-1)) azimuth = 0; if (ind_dp == (-1)) dip = 0; fprintf (fout, "%-3s %-5s %-3s %-2s %-s %-s\t%-8.6f\t%8.6f\t%8.6f\t%8.6f\t%7.5E\t%7.2f\t%7.2f\tDU/%-s\n", net, sta, seedchan, location, nondate, noffdate, nat_freq, lp_filter, hp_filter, damping, overall_gain, azimuth, dip, unitname); /* Populating Simple_Response Table */ sprintf (gain_units, "DU/%s", unitname); if (!strcmp (location, "--")) strcpy (location, " "); if (FlagPopulation) { /* printf ("[%s][%s][%s][%s][%f][%f][%f][%s][%f][%f][%s][%s][%f]\n", net, sta, seedchan, location, nat_freq, damping, overall_gain, gain_units, lp_filter, hp_filter, ondate, offdate, digi_sens); */ EXEC SQL INSERT INTO Simple_Response (net, sta, seedchan, channel, channelsrc, location, natural_frequency, damping_constant, gain ,gain_units, low_freq_corner, high_freq_corner, ondate, offdate, lddate, dlogsens) VALUES (:net, :sta, :seedchan, :seedchan, 'SEED', :location, :nat_freq:ind_nf, :damping:ind_dc, :overall_gain, :gain_units, :lp_filter:ind_hfc, :hp_filter:ind_lfc, :ondate, :offdate, SYSDATE, :digi_sens:ind_ds); } EXEC SQL FETCH chacursor INTO :sta, :net, :seedchan, :location, :ondate, :offdate, :unit, :samprate:ind_sp; } EXEC SQL CLOSE chacursor; fclose (fout); /* Disconnect from the database. */ EXEC SQL COMMIT WORK RELEASE; exit(0); } /************************************************************************/ /* sql_error: */ /* Handle errors. Exit on any error */ /************************************************************************/ void sql_error() { char msg[512]; size_t buf_len, msg_len; EXEC SQL WHENEVER SQLERROR CONTINUE; buf_len = sizeof(msg); sqlglm(msg, &buf_len, &msg_len); sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; oraca.orastxt.orastxtc[oraca.orastxt.orastxtl] = '\0'; oraca.orasfnm.orasfnmc[oraca.orasfnm.orasfnml] = '\0'; printf("\n\n%s", sqlca.sqlerrm.sqlerrmc); printf("in \"%s\"\n", oraca.orastxt.orastxtc); printf("on line %d of %s.\n\n", oraca.oraslnr, oraca.orasfnm.orasfnmc); EXEC SQL ROLLBACK WORK RELEASE; exit(1); }