Article 12692 of alt.sources:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!bloom-beacon.mit.edu!news.kei.com!news.mathworks.com!usenet.eel.ufl.edu!newsfeed.internetmci.com!uunet!in1.uu.net!dove.nist.gov!case50.ncsl.nist.gov!dave
From: dave@case50.ncsl.nist.gov (David Flater)
Newsgroups: alt.sources
Subject: XTide 3/4
Followup-To: poster
Date: 7 Aug 1995 13:48:41 GMT
Organization: National Institute of Standards and Technology
Lines: 1032
Message-ID: <4055jp$13f@dove.nist.gov>
References: <4055f1$vb@dove.nist.gov>
NNTP-Posting-Host: case50.ncsl.nist.gov
Keywords: tide

Submitted-by: dave@case50.ncsl.nist.gov
Archive-name: xtide-1.0.1/part03

This program uses the harmonic method to display tide levels either as
a tide clock (showing the current level) or as a graph.  Please refer
to README for more information.

THIS PACKAGE IS AVAILABLE FOR ANONYMOUS FTP FROM:
ftp://speckle.ncsl.nist.gov/flater/sources/xtide-1.0.1.tgz

An illustrated, HTML-ized version of the README is available at:
http://case50.ncsl.nist.gov/xtide/xtide.html

#!/bin/sh
# this is xtide.03 (part 3 of xtide-1.0.1)
# do not concatenate these parts, unpack them in order with /bin/sh
# file harmonics continued
#
if touch 2>&1 | fgrep '[-amc]' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
if test ! -r @shar_seq_.tmp; then
	echo "Please unpack part 1 first!"
	exit 1
fi
(read Scheck
 if test "$Scheck" != 3; then
	echo "Please unpack part $Scheck next!"
	exit 1
 else
	exit 0
 fi
) < @shar_seq_.tmp || exit 1
echo "x - Continuing file harmonics"
sed 's/^X//' << 'SHAR_EOF' >> harmonics &&
X# Second line:  time meridian.
X# Remaining lines:  identifier [whitespace] amplitude [whitespace] epoch.
X# The time meridian takes the format [-]HH:MM and is hours east of
X# Greenwich.  This is your time zone displacement in the _winter_.  Do
X# not include Daylight Savings Time!
X# Amplitude units do not matter; they will not be converted.
X# Epoch is "modified" or "adapted" epoch in degrees, also known as
X# Kappa Prime.
X# The identifiers are for readability only; xtide assumes that they
X# are in the same order as defined above.
X#
X# These data sets are included here without any effort to verify their
X# accuracy.  They are distributed in the hope that they will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
X#
X# Morro, CA constants from the example on page 143 of Manual of
X# Harmonic Analysis and Prediction of Tides, 1940 edition.  The data
X# themselves were computed in 1923 from hourly heights collected in
X# 1919.  Amplitude is in feet.  DWF, dave@case50.ncsl.nist.gov.
XMorro, California
X-08:00
XJ(1)       0.048   246.6
XK(1)       1.001   248.3
XK(2)       0.103    69.2
XL(2)       0.050    47.0
XM(1)       0.041   222.7
XM(2)       1.227    41.7
XM(3)       0.022   359.5
XM(4)       0.053   169.7
XM(6)       0.013    76.8
XM(8)       0.007   215.1
XN(2)       0.260    61.3
X2N(2)      0.035    81.0
XO(1)       0.608   251.6
XOO(1)      0.026   245.0
XP(1)       0.274   251.1
XQ(1)       0.107   247.4
X2Q(1)      0.016   254.9
XR(2)       0.003    54.5
XS(1)       0.000     0.0
XS(2)       0.320    54.0
XS(4)       0.009   180.0
XS(6)       0.006   206.6
XT(2)       0.019    53.5
XLAMBDA(2)  0.009    47.4
XMU(2)      0.025   167.8
XNU(2)      0.065    61.4
XRHO(1)     0.023   253.0
XMK(3)      0.000     0.0
X2MK(3)     0.000     0.0
XMN(4)      0.000     0.0
XMS(4)      0.000     0.0
X2SM(2)     0.000     0.0
XMF         0.000     0.0
XMSF        0.000     0.0
XMM         0.000     0.0
XSA         0.000     0.0
XSSA        0.000     0.0
X#
X# Baltimore, MD, U.S.A.
X#
X# The actual location belonging to these numbers is 39.160 degrees N,
X# 76.347 degrees W.  That is in the middle of the Chesapeake Bay
X# across from the mouth of the Patapsco River.  If xtide is correct,
X# the high tides out there differ from the high tides at Ft. McHenry
X# by as much as half an hour.
X#
X# Source:  N.O.S. least squares harmonic analysis
X# Data:  Jan 1 1994 - Dec 31 1994
X# Amplitude is in feet.
X# DWF, dave@case50.ncsl.nist.gov, 7/25/95.
XBaltimore
X-05:00
XJ(1)       0.016   258.6
XK(1)       0.236   291.9
XK(2)       0.024   221.8
XL(2)       0.026   203.7
XM(1)       0.008   340.3
XM(2)       0.506   192.6
XM(3)       0.007   252.3
XM(4)       0.024   317.8
XM(6)       0.009   292.1
XM(8)       0.002   329.5
XN(2)       0.109   175.3
X2N(2)      0.018   142.3
XO(1)       0.170   298.1
XOO(1)      0.027    19.5
XP(1)       0.067   292.3
XQ(1)       0.036   261.8
X2Q(1)      0.012     8.8
XR(2)       0.003   341.4
XS(1)       0.061   205.6
XS(2)       0.075   214.0
XS(4)       0.002   139.7
XS(6)       0.001    96.9
XT(2)       0.008   140.5
XLAMBDA(2)  0.006   219.9
XMU(2)      0.009   174.9
XNU(2)      0.024   174.4
XRHO(1)     0.008   194.7
XMK(3)      0.019   317.9
X2MK(3)     0.014   313.4
XMN(4)      0.009   296.9
XMS(4)      0.009   343.1
X2SM(2)     0.004    28.9
XMF         0.031    63.8
XMSF        0.100   351.7
XMM         0.020    80.9
XSA         0.336   127.9
XSSA        0.099    53.6
SHAR_EOF
echo "File harmonics is complete" &&
$TOUCH -am 0805102495 harmonics &&
chmod 0600 harmonics ||
echo "restore of harmonics failed"
set `wc -c harmonics`;Wc_c=$1
if test "$Wc_c" != "24861"; then
	echo original size 24861, current size $Wc_c
fi
# ============= xtide.c ==============
echo "x - extracting xtide.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > xtide.c &&
X/*  XTide  Harmonic tide clock and tide predictor
X    Version 1.0.1  DWF  8/5/95
X
X    This program uses the harmonic method to display tide levels
X    either as a tide clock (showing the current level) or as a graph.
X    All of the data and constants are read in from the harmonics file.
X    Please refer to README for more information.
X
X
X    Copyright (C) 1995  David Flater.
X
X    This program is free software; you can redistribute it and/or modify
X    it under the terms of the GNU General Public License as published by
X    the Free Software Foundation; either version 2 of the License, or
X    (at your option) any later version.
X
X    This program is distributed in the hope that it will be useful,
X    but WITHOUT ANY WARRANTY; without even the implied warranty of
X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X    GNU General Public License for more details.
X
X    You should have received a copy of the GNU General Public License
X    along with this program; if not, write to the Free Software
X    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X
X    The tide prediction algorithm used in this program was developed
X    with United States Government funding, so no proprietary rights
X    can be attached to it.  For more information, refer to the
X    following publications:
X
X    Manual of Harmonic Analysis and Prediction of Tides.  Special
X    Publication No. 98, Revised (1940) Edition.  United States
X    Government Printing Office, 1941.
X
X    Computer Applications to Tides in the National Ocean Survey.
X    Supplement to Manual of Harmonic Analysis and Prediction of Tides
X    (Special Publication No. 98).  National Ocean Service, National
X    Oceanic and Atmospheric Administration, U.S. Department of
X    Commerce, January 1982.
X
X
X    Parts of this program were carried over from XGrav, which was
X    based on XSwarm, which was based on something else, ad infinitum.
X*/
X
X#define VERSION "1.0"
X#define PATCHLEVEL 1
X
X#include "config.h"
X#include <X11/Xlib.h>
X#include <X11/Xatom.h>
X#include <X11/Xutil.h>
X
X#include <stdio.h>
X#include <math.h>
X#include <stdlib.h>
X#include <signal.h>
X#include <unistd.h>
X#include <time.h>
X#include <assert.h>
X#include <string.h>
X#include <sys/time.h>
X
X#define WINWIDTH 84       /* default window width */
X#define GRAPHWIDTH 960
X#define WINHEIGHT 300     /* default window height */
X
X/* How much water at lowest tide; how much sky at highest tide. */
X#define margin 0.05
X
X/* Assorted variables */
X
Xlong fgrise_color, fgfall_color, bg_color, fgtext_color;
Xchar *fgrise_color_arg = NULL, *fgfall_color_arg = NULL, *bg_color_arg = NULL,
X  *fgtext_color_arg = NULL, *hfile_name = NULL, *display_name = NULL,
X  location[80], next_ht_text[13], next_ht_date[11];
Xint winX, winY, screen, testmode = 0, graphmode = 0, rising = -1, year = 0,
X  lines = 1, hairy = 0, tstep = 180, meridian = 0, tadjust = 0, utc = 0,
X  list = 0, redraw = 0, checkyear = 0;
Xunsigned int winW, winH;
XGC fgrise_color_gc, fgfall_color_gc, bg_color_gc, fgtext_color_gc;
XDisplay *display;
XColormap colormap;
XWindow window;
XAtom kill_atom, protocol_atom;
XFont font;
Xtime_t next_ht = 0, faketime = 0, epoch = 0;
Xdouble prev_tide, this_tide, gstretch = 1.0;
X
X/* Tide-prediction storage -- cst == constituent */
X
Xint num_csts = 0, num_nodes = 0, num_epochs = 0, first_year = 0;
Xdouble *cst_speeds, **cst_epochs, **cst_nodes, *loc_amp, *loc_epoch,
X  *work, amplitude;
X#define linelen 3000
X
X/* Allocate tide-prediction storage except for node factors and epochs. */
Xvoid
Xallocate_cst ()
X{
X  assert (num_csts);
X  assert (cst_speeds = (double *) malloc (num_csts * sizeof (double)));
X  assert (loc_amp = (double *) malloc (num_csts * sizeof (double)));
X  assert (loc_epoch = (double *) malloc (num_csts * sizeof (double)));
X  assert (work = (double *) malloc (num_csts * sizeof (double)));
X}
X
X/* Allocate cst_nodes. */
Xvoid
Xallocate_nodes ()
X{
X  int a;
X  assert (num_csts);
X  assert (num_nodes);
X  assert (cst_nodes = (double **) malloc (num_csts * sizeof (double *)));
X  for (a=0;a<num_csts;a++)
X    assert (cst_nodes[a] = (double *) malloc (num_nodes * sizeof (double)));
X}
X
X/* Allocate cst_epochs. */
Xvoid
Xallocate_epochs ()
X{
X  int a;
X  assert (num_csts);
X  assert (num_epochs);
X  assert (cst_epochs = (double **) malloc (num_csts * sizeof (double *)));
X  for (a=0;a<num_csts;a++)
X    assert (cst_epochs[a] = (double *) malloc (num_epochs * sizeof (double)));
X}
X
X/* Get rid of trailing garbage in linrec */
Xchar *
Xnojunk (char linrec[linelen])
X{
X  char *a;
X  a = &(linrec[strlen(linrec)]);
X  while (a > linrec)
X    if (*(a-1) == '\n' || *(a-1) == '\r' || *(a-1) == ' ')
X      *(--a) = '\0';
X    else
X      break;
X  return linrec;
X}
X
X/* Read a line from the harmonics file, skipping comment lines */
Xint
Xnext_line (FILE *fp, char linrec[linelen], int end_ok)
X{
X  do {
X    if (!fgets (linrec, linelen, fp)) {
X      if (end_ok)
X        return 0;
X      else {
X        fprintf (stderr, "Unexpected end of harmonics file '%s'\n", hfile);
X        exit (-1);
X      }
X    }
X  } while (linrec[0] == '#');
X  return 1;
X}
X
X/* Calculate time_t of the epoch. */
Xvoid
Xset_epoch ()
X{
X  struct tm ht;
X  if (year < first_year || year >= first_year + num_epochs) {
X    fprintf (stderr, "Xtide:  Don't have equilibrium arguments for %d\n", year);
X    exit (-1);
X  }
X  ht.tm_year = year - 1900;
X  ht.tm_sec = ht.tm_min = ht.tm_hour = ht.tm_mon = 0;
X  ht.tm_mday = 1;
X  ht.tm_isdst = 0;
X#if defined(sun) && !defined(__svr4__)
X  /* Old SunOS */
X  /* epoch = timegm (&ht); */   /* This function is off by a mile! */
X  {
X    struct tm *sunos;
X    epoch = timelocal (&ht);
X    sunos = localtime (&epoch);
X    epoch -= sunos->tm_gmtoff;
X  }
X#else
X  assert ((epoch = mktime (&ht)) != -1);
X  /* Determine the local time zone offset and correct for it */
X  /* Taking timezone from gettimeofday didn't work (always zero) */
X  localtime (&epoch);
X  epoch += timezone;
X#endif
X}
X
X/* Load harmonics data */
Xvoid
Xload_data ()
X{
X  FILE *fp;
X  char linrec[linelen], junk[80];
X  int a, b;
X  if (!(fp = fopen (hfile_name, "r"))) {
X    perror ("fopen");
X    fprintf (stderr, "Could not open harmonics file '%s'\n", hfile);
X    exit (-1);
X  }
X  next_line (fp, linrec, 0);
X  assert (sscanf (linrec, "%d", &num_csts) == 1);
X  allocate_cst ();
X  /* Load constituent speeds */
X  for (a=0;a<num_csts;a++) {
X    next_line (fp, linrec, 0);
X    assert (sscanf (linrec, "%s %lf", junk, &(cst_speeds[a])) == 2);
X    cst_speeds[a] /= 3600.0;  /* Convert to degrees per second */
X  }
X  /* Get first year for nodes and epochs */
X  next_line (fp, linrec, 0);
X  assert (sscanf (linrec, "%d", &first_year) == 1);
X
X  /* Load epoch table */
X  next_line (fp, linrec, 0);
X  assert (sscanf (linrec, "%d", &num_epochs) == 1);
X  allocate_epochs ();
X  for (a=0;a<num_csts;a++) {
X    assert (fscanf (fp, "%s", linrec) == 1);
X    for (b=0;b<num_epochs;b++)
X      assert (fscanf (fp, "%lf", &(cst_epochs[a][b])) == 1);
X  }
X  /* Sanity check */
X  assert (fscanf (fp, "%s", linrec) == 1);
X  assert (!strcmp (linrec, "*END*"));
X
X  /* Load node factor table */
X  next_line (fp, linrec, 0);  /* Skip lingering CR from *END* */
X  next_line (fp, linrec, 0);
X  assert (sscanf (linrec, "%d", &num_nodes) == 1);
X  allocate_nodes ();
X  for (a=0;a<num_csts;a++) {
X    assert (fscanf (fp, "%s", linrec) == 1);
X    for (b=0;b<num_nodes;b++)
X      assert (fscanf (fp, "%lf", &(cst_nodes[a][b])) == 1);
X  }
X  /* Sanity check */
X  assert (fscanf (fp, "%s", linrec) == 1);
X  assert (!strcmp (linrec, "*END*"));
X
X  /* List locations and exit? */
X  if (list) {
X    next_line (fp, linrec, 0);  /* Skip lingering CR from *END* */
X    while (next_line (fp, linrec, 1)) {
X      printf ("%s\n", nojunk(linrec));
X      for (a=0;a<=num_csts;a++)
X        next_line (fp, linrec, 0);
X    }
X    fclose (fp);
X    exit (0);
X  }
X
X  /* Load harmonic constants for desired location */
X  while (next_line (fp, linrec, 1)) {
X    if (strcmp (nojunk(linrec), location))
X      continue;
X    /* Get meridian */
X    next_line (fp, linrec, 0);
X    assert (sscanf (linrec, "%d:%d", &a, &b) == 2);
X    meridian = 3600 * a + 60 * b;
X    /* Get constituents */
X    for (a=0;a<num_csts;a++) {
X      char junk[80];
X      next_line (fp, linrec, 0);
X      assert (sscanf (linrec, "%s %lf %lf", junk, &(loc_amp[a]),
X        &(loc_epoch[a])) == 3);
X      assert (loc_amp[a] >= 0.0);
X    }
X    fclose (fp);
X    return;
X  }
X  fprintf (stderr, "Could not find location '%s' in harmonics file\n",
X    location);
X  exit (-1);
X}
X
X/* Check for errors in equilibrium arguments by inferring values from
X   neighboring years and comparing with the stored values. */
Xvoid
Xcheck_epoch ()
X{
X  double inferred, stored, error;
X  int a, final_year;
X  time_t prev_epoch, next_epoch;
X  final_year = first_year + num_epochs - 1;
X  if (checkyear < first_year || checkyear > final_year) {
X    fprintf (stderr, "Xtide:  Don't have equilibrium arguments for %d\n",
X      checkyear);
X    exit (-1);
X  }
X  if (checkyear != first_year) {
X    year = checkyear - 1;
X    set_epoch ();
X    prev_epoch = epoch;
X  }
X  if (checkyear != final_year) {
X    year = checkyear + 1;
X    set_epoch ();
X    next_epoch = epoch;
X  }
X  year = checkyear;
X  set_epoch ();
X  puts ("#  Stored  Inferred  Error");
X  for (a=0;a<num_csts;a++) {
X    if (checkyear == first_year)
X      inferred = fmod (cst_speeds[a] * (-(long)(next_epoch - epoch)) +
X      cst_epochs[a][1], 360.0);
X    else if (checkyear == final_year)
X      inferred = fmod (cst_speeds[a] * (epoch - prev_epoch) +
X      cst_epochs[a][final_year-1-first_year], 360.0);
X    else {
X      double t1, t2;
X      t1 = fmod (cst_speeds[a] * (-(long)(next_epoch - epoch)) +
X        cst_epochs[a][checkyear+1-first_year], 360.0);
X      t2 = fmod (cst_speeds[a] * (epoch - prev_epoch) +
X        cst_epochs[a][checkyear-1-first_year], 360.0);
X      if (t1 < 0.0)
X        t1 += 360.0;
X      if (t2 < 0.0)
X        t2 += 360.0;
X      inferred = (t1 + t2) / 2.0;
X      if (fabs (inferred - t1) > 90.0 || fabs (inferred - t2) > 90.0)
X        inferred = fmod (inferred + 180.0, 360.0);
X    }
X    stored = cst_epochs[a][checkyear-first_year];
X    if (inferred < 0.0)
X      inferred += 360.0;
X    error = fabs (stored - inferred);
X    if (fabs (stored + 360.0 - inferred) < error)
X      error = fabs (stored + 360.0 - inferred);
X    if (fabs (stored - inferred - 360.0) < error)
X      error = fabs (stored - inferred - 360.0);
X    printf ("%d  %010.6f  %010.6f  %f\n", a, stored, inferred, error);
X  }
X}
X
X/* Convert YYYY:MM:DD:HH:MM to time_t.  Don't know what happens to DST. */
Xtime_t
Xparse_time_string (char *time_string)
X{
X  struct tm ht;
X  time_t temp;
X  assert (sscanf (time_string, "%d:%d:%d:%d:%d",
X    &(ht.tm_year), &(ht.tm_mon), &(ht.tm_mday), &(ht.tm_hour),
X    &(ht.tm_min)) == 5);
X  ht.tm_sec = 0;
X  (ht.tm_mon)--;
X  ht.tm_year -= 1900;
X  ht.tm_isdst = -1;
X  assert ((temp = mktime (&ht)) != -1);
X  return temp;
X}
X
X/* Return the year for a time_t. */
Xint
Xyearoftimet (time_t t)
X{
X  return ((gmtime (&t))->tm_year) + 1900;
X}
X
Xlong
XGetColor (char *color)
X{
X  XColor cdef;
X  assert (XParseColor (display, colormap, color, &cdef));
X  assert (XAllocColor (display, colormap, &cdef));
X  return (cdef.pixel);
X}
X
Xvoid
Xdie ()
X{
X  XDestroyWindow(display, window);
X  XFreeGC(display, fgrise_color_gc);
X  XFreeGC(display, fgfall_color_gc);
X  XFreeGC(display, fgtext_color_gc);
X  XFreeGC(display, bg_color_gc);
X  XUnloadFont(display, font);
X  exit (0);
X}
X
X/* Handle kill events and resizes. */
Xvoid
XHandleEvent (XEvent *event)
X{
X  switch (event->type) {
X  case ClientMessage: {
X    XClientMessageEvent *ev = (XClientMessageEvent *) event;
X    if (ev->message_type == protocol_atom &&
X    ev->data.l[0] == kill_atom)
X      die ();
X    break;
X  }
X  case ConfigureNotify: {
X    XConfigureEvent *ev = (XConfigureEvent *) event;
X    winW = ev->width;
X    winH = ev->height;
X    winX = ev->x;
X    winY = ev->y;
X    break;
X  }
X  case Expose: {
X    redraw = 1;
X  }}
X}
X
X/* Create and setup the window. */
Xvoid
XCreate_Window (char *geometry)
X{
X  XSetWindowAttributes xswa;
X  XSizeHints sizehint;
X  XWMHints wmhints;
X
X  xswa.event_mask = 0;
X  xswa.background_pixel = bg_color;
X  window = XCreateWindow(display, RootWindow (display, screen),
X            winX, winY, 
X            winW, winH, 0, 
X            DefaultDepth (display, screen), 
X            InputOutput, DefaultVisual (display, screen),
X            CWEventMask | CWBackPixel, &xswa);
X
X  sizehint.x = winX;
X  sizehint.y = winY;
X  sizehint.width = winW;
X  sizehint.height = winH;
X  sizehint.min_width = (graphmode ? GRAPHWIDTH : WINWIDTH);
X  sizehint.min_height = WINHEIGHT;
X
X  if (geometry)
X    sizehint.flags = USPosition | USSize | PMinSize;
X  else
X    sizehint.flags = PPosition | PSize | PMinSize;
X  XSetNormalHints(display, window, &sizehint);
X
X  protocol_atom = XInternAtom (display, "WM_PROTOCOLS", False);
X  kill_atom = XInternAtom (display, "WM_DELETE_WINDOW", False);
X  XSetWMProtocols(display, window, &kill_atom, 1);
X
X  XChangeProperty (display, window, XA_WM_NAME, XA_STRING, 8,
X    PropModeReplace, location, strlen(location));
X
X  wmhints.flags = InputHint;
X  wmhints.input = True;
X  XSetWMHints(display, window, &wmhints);
X  XMapWindow(display, window);
X  XSelectInput(display, window, StructureNotifyMask | ExposureMask);
X
X  fgrise_color_gc = XCreateGC (display, window, 0, NULL); 
X  XSetForeground (display, fgrise_color_gc, fgrise_color); 
X  XSetBackground (display, fgrise_color_gc, bg_color);
X
X  fgfall_color_gc = XCreateGC (display, window, 0, NULL); 
X  XSetForeground (display, fgfall_color_gc, fgfall_color); 
X  XSetBackground (display, fgfall_color_gc, bg_color);
X
X  fgtext_color_gc = XCreateGC (display, window, 0, NULL); 
X  XSetForeground (display, fgtext_color_gc, fgtext_color); 
X  XSetBackground (display, fgtext_color_gc, bg_color);
X
X  bg_color_gc = XCreateGC (display, window, 0, NULL); 
X  XCopyGC (display, fgrise_color_gc, 0xffffffff, bg_color_gc); 
X  XSetForeground (display, bg_color_gc, bg_color);
X
X  XSetWindowBackground (display, window, bg_color);
X
X  /* I hope this is specific enough to prevent portability problems. */
X  font = XLoadFont (display, "-*-fixed-medium-r-*-*-13-*-*-*-*-*-iso8859-*");
X  XSetFont (display, fgtext_color_gc, font);
X}
X
X/* Figure out normalized multipliers for constituents for a particular
X   year.  Save amplitude for drawing unit lines. */
Xvoid
Xfigure_multipliers ()
X{
X  int a;
X  amplitude = 0.0;
X  if (year < first_year || year >= first_year + num_nodes) {
X    fprintf (stderr, "Xtide:  Don't have node factors for %d\n", year);
X    exit (-1);
X  }
X  for (a=0;a<num_csts;a++)
X    amplitude += (work[a] = loc_amp[a] * cst_nodes[a][year-first_year]);
X  assert (amplitude != 0.0);
X  for (a=0;a<num_csts;a++)
X    work[a] /= amplitude;
X}
X
X/* Doh...  Forgot to convert to radians. */
Xdouble
Xcos_degrees (double x)
X{
X  return cos (x * M_PI / 180.0);
X}
X
X/* Re-initialize for a different year */
Xvoid
Xhappy_new_year (int new_year)
X{
X  year = new_year;
X  figure_multipliers ();
X  set_epoch ();
X}
X
X/* Calculate the normalized tide (-1.0 .. 1.0) for a given time. */
Xdouble
Xtime2tide (time_t t)
X{
X  double tide = 0.0;
X  int a, new_year = yearoftimet (t);
X  if (new_year != year)
X    happy_new_year (new_year);
X  for (a=0;a<num_csts;a++)
X    tide += work[a] *
X      cos_degrees (cst_speeds[a] * ((long)(t - epoch) - meridian) +
X      cst_epochs[a][year-first_year] - loc_epoch[a]);
X  return tide;
X}
X
X/* Convert a normalized tide to a Y coordinate in the window. */
Xint
Xtide2wl (double tide)
X{
X  return (int)(((double)winH * (1.0 - 2.0 * margin)) * (-tide) * 0.5
X    + (double)winH * 0.5);
X}
X
X/* Find a high tide.  It suffices to count forward one minute at a time
X   until the apex is located. */
Xtime_t
Xnext_high_tide (time_t tm)
X{
X  double p, q;
X  int found_slope = 0;
X  p = time2tide (tm);
X  tm += 60;
X  q = time2tide (tm);
X  tm += 60;
X  while (1) {
X    if (q < p)
X      if (found_slope)
X        return tm-60;
X    if (p < q)
X      found_slope = 1;
X    p = q;
X    q = time2tide (tm);
X    tm += 60;
X  }
X}
X
X/* Same thing, but do both high and low tides. */
Xtime_t
Xnext_high_or_low_tide (time_t tm)
X{
X  double p, q;
X  int slope = 0;
X  p = time2tide (tm);
X  tm += 60;
X  q = time2tide (tm);
X  tm += 60;
X  if (p < q)
X    slope = 1;
X  while (1) {
X    if ((slope == 1 && q < p) || (slope == 0 && p < q))
X      return tm-60;
X    p = q;
X    q = time2tide (tm);
X    tm += 60;
X  }
X}
X
X/* Determine next high tide and set up text string for write_high_tide.
X   This has now been kluged to do both high and low tides in graph mode,
X   but the same variables are used.... */
Xvoid
Xupdate_high_tide ()
X{
X  struct tm *t;
X  if (graphmode)
X    next_ht = next_high_or_low_tide (next_ht);
X  else if (testmode)
X    next_ht = next_high_tide (faketime);
X  else
X    next_ht = next_high_tide (time (NULL));
X  if (tadjust) {
X    time_t nht_adjust = next_ht + tadjust;
X    t = gmtime (&nht_adjust);
X    strftime (next_ht_text, 13, "%I:%M %p", t);
X  } else {
X    if (utc) {
X      t = gmtime (&next_ht);
X      strftime (next_ht_text, 13, "%H:%M UTC", t);
X    } else {
X      t = localtime (&next_ht);
X      strftime (next_ht_text, 13, "%I:%M %p %Z", t);
X    }
X  }
X  strftime (next_ht_date, 11, "%Y-%m-%d", t);
X  if (next_ht_text[0] == '0')
X#if defined(sun) && !defined(__svr4__)
X  /* Old SunOS */
X  {
X    int a;
X    for (a=0;a<12;a++)
X      next_ht_text[a] = next_ht_text[a+1];
X  }
X#else
X    memmove (next_ht_text, next_ht_text+1, 12);
X#endif
X}
X
X/* Text is centered around x.  Will break horribly if the font changes. */
Xvoid
Xcenter_text (int x, int y, char *text)
X{
X  int l = strlen (text);
X  XDrawString (display, window, fgtext_color_gc, x - 7 * l / 2, y,
X    text, l);
X}
X
X/* Draw text telling when the high tide is. */
Xvoid
Xwrite_high_tide (int x)
X{
X  char temp[23];
X  if (graphmode) {
X    center_text (x, 9, next_ht_date);
X    center_text (x, 21, next_ht_text);
X    return;
X  }
X  if (winW < 155) {
X    center_text (x, 9, "High Tide");
X    center_text (x, 21, next_ht_text);
X    return;
X  }
X  sprintf (temp, "High Tide %s", next_ht_text);
X  center_text (x, 9, temp);
X}
X
X/* The unit lines are centered around the middle of the tide curve since we
X   have no idea how deep the water actually is except in a relative way. */
Xvoid
Xdraw_unit_lines ()
X{
X  int a, b, len;
X  if (!lines)
X    return;
X  /* Depth lines for amplitude units */
X  if (graphmode)
X    len = winW;
X  else
X    len = 6;
X  b = tide2wl (0.0);
X  XDrawLine (display, window, fgtext_color_gc, 0, b, len, b);
X  for (a=1;a<=(int)amplitude;a++) {
X    b = tide2wl (((double)a) / amplitude);
X    XDrawLine (display, window, fgtext_color_gc, 0, b, len, b);
X    b = tide2wl ((-(double)a) / amplitude);
X    XDrawLine (display, window, fgtext_color_gc, 0, b, len, b);
X  }
X}
X
X/* Tick marks for hours along X axis */
Xvoid
Xdraw_tick_marks ()
X{
X  int a, b;
X  if (!lines)
X    return;
X  if ((!graphmode) && (!hairy))
X    return;
X  b = winW>>1;
X  XDrawLine (display, window, fgtext_color_gc, b, winH-7, b, winH-1);
X  for (a=1;a<=(int)((winW>>1) * tstep / 3600);a++) {
X    b = (winW>>1) + a * 3600 / tstep;
X    XDrawLine (display, window, fgtext_color_gc, b, winH-7, b, winH-1);
X    b = (winW>>1) - a * 3600 / tstep;
X    XDrawLine (display, window, fgtext_color_gc, b, winH-7, b, winH-1);
X  }
X}
X
X/* Redraw the window for the normal tide clock. */
Xvoid
Xset_water_level ()
X{
X  int nwl;
X  time_t this_time;
X  prev_tide = this_tide;
X  if (testmode)
X    this_time = faketime;
X  else
X    this_time = time (NULL);
X  if (this_time > next_ht)
X    update_high_tide ();
X  this_tide = time2tide (this_time);
X  nwl = tide2wl (this_tide);
X  if (rising == -1) {
X    /* prev_tide is unreliable on startup */
X    if (this_tide < time2tide (this_time+1))
X      rising = 1;
X    else
X      rising = 0;
X  } else {
X    if (prev_tide < this_tide)
X      rising = 1;
X    if (prev_tide > this_tide)
X      rising = 0;
X  }
X  XFillRectangle (display, window, bg_color_gc, 0, 0, winW, nwl-1);
X  if (rising)
X    XFillRectangle (display, window, fgrise_color_gc, 0, nwl, winW, winH);
X  else
X    XFillRectangle (display, window, fgfall_color_gc, 0, nwl, winW, winH);
X
X  write_high_tide (winW>>1);
X  draw_unit_lines();
X  draw_tick_marks();
X}
X
X/* Redraw the window for the hairy tide clock. */
Xvoid
Xset_hairy_water_level ()
X{
X  int nwl, looper, w2;
X  time_t this_time;
X  w2 = winW>>1;
X  if (testmode)
X    this_time = faketime;
X  else
X    this_time = time (NULL);
X  if (this_time > next_ht)
X    update_high_tide ();
X  prev_tide = time2tide (this_time - tstep * (w2+1));
X  for (looper=0;looper<winW;looper++) {
X    this_tide = time2tide (this_time - tstep * (w2 - looper));
X    nwl = tide2wl (this_tide);
X    XDrawLine (display, window, bg_color_gc, looper, 0, looper,
X      nwl-1);
X    if (this_tide < prev_tide)
X      XDrawLine (display, window, fgfall_color_gc, looper, winH, looper,
X        nwl);
X    else
X      XDrawLine (display, window, fgrise_color_gc, looper, winH, looper,
X        nwl);
X    prev_tide = this_tide;
X  }
X  write_high_tide (winW>>1);
X  draw_unit_lines();
X  draw_tick_marks();
X  /* X marks the spot */
X  nwl = tide2wl (time2tide (this_time));
X  XDrawLine (display, window, fgtext_color_gc, w2, nwl-3, w2, nwl+3);
X  XDrawLine (display, window, fgtext_color_gc, w2-3, nwl, w2+3, nwl);
X}
X
X/* Pound on X server to get all events handled. */
Xvoid
Xgrind_x ()
X{
X  XEvent xev;
X  if (!testmode)
X    XSync (display, False);
X  while (XPending (display)) {
X    XNextEvent (display, &xev);
X    HandleEvent (&xev);
X  }
X}
X
X/* Don't miss events that happen on startup. */
Xvoid
Xwait_for_event ()
X{
X  do
X    sleep (1);
X  while (!XPending (display));
X  grind_x ();
X}
X
X/* Doze is like sleep except that it always wakes up for X Events. */
Xvoid
Xdoze (int seconds)
X{
X  for (;(seconds && (!XPending (display)));seconds--)
X    sleep (1);
X}
X
X/* Draw graph; don't return. */
Xvoid
Xdraw_graph ()
X{
X  int a;
X  time_t start = faketime;
X  /* Initialize the amplitude before drawing the depth lines. */
X  happy_new_year (yearoftimet (start));
X  while (1) {
X    faketime = start;
X    /* Draw depth lines first so that graph will overlay them */
X    draw_unit_lines();
X    for (a=0;a<winW;a++) {
X      XDrawLine (display, window, fgrise_color_gc, a, winH, a,
X        tide2wl (time2tide (faketime)));
X      faketime += tstep;
X    }
X    XSync (display, False);
X    if (XPending (display))
X      goto harmful;
X    next_ht = start;
X    update_high_tide ();
X    while (next_ht < start + winW*tstep) {
X      write_high_tide ((next_ht - start) / tstep);
X      XSync (display, False);
X      update_high_tide ();
X      if (XPending (display))
X        goto harmful;
X    }
X    draw_tick_marks();
X    XSync (display, False);
X    redraw = 0;
Xconsidered:
X    while (!XPending (display))
X      sleep (1);
Xharmful:
X    grind_x ();
X    if (!redraw)
X      goto considered;
X  }
X}
X
Xint
Xmain(int argc, char **argv)
X{
X  int option;
X  char *geometry = NULL;
X
X  strcpy (location, deflocation);
X  hfile_name = hfile;
X  option = 1;
X  while (option < argc) {
X    if (!strcmp (argv[option], "-display")) {
X      option++;
X      display_name = argv[option++];
X    } else if (!strcmp (argv[option], "-geometry")) {
X      option++;
X      geometry = argv[option++];
X    } else if (!strcmp (argv[option], "-fgrise")) {
X      option++;
X      fgrise_color_arg = argv[option++];
X    } else if (!strcmp (argv[option], "-fgfall")) {
X      option++;
X      fgfall_color_arg = argv[option++];
X    } else if (!strcmp (argv[option], "-fgtext")) {
X      option++;
X      fgtext_color_arg = argv[option++];
X    } else if (!strcmp (argv[option], "-hfile")) {
X      option++;
X      hfile_name = argv[option++];
X    } else if (!strcmp (argv[option], "-bg")) {
X      option++;
X      bg_color_arg = argv[option++];
X    } else if (!strcmp (argv[option], "-check")) {
SHAR_EOF
echo "End of xtide-1.0.1 part 3"
echo "File xtide.c is continued in part 4"
echo "4" > @shar_seq_.tmp
exit 0


