/* * xlog - GTK+ logging program for amateur radio operators * Copyright (C) 2001-2002 Joop Stakenborg * * This program is free oftware; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * gc.c - see description below by N5OWK, adopted for xlog October 2001 */ /* * Great Circle. This program is used to determine bearing * and range to two stations given latitude and longitude. * * Ver 1.07 By S. R. Sampson, N5OWK * Public Domain (p) June 1993 * * Ref: "Air Navigation", Air Force Manual 51-40, 1 February 1987 * Ref: "ARRL Satellite Experimenters Handbook", August 1990 * * Usage examples: * * gcb n 35.19n97.27w 0s0e (Moore to Prime/Equator) * gcb n 35.19N97.27W 38.51n77.02W (Moore to Washington D.C., mixed case) * gcb n 33.56n118.24w 55.45n37.35e (L.A. to Moscow) * gcb n 35N70W 35N71W (No decimal points used, all uppercase) * * Modified the program to incorporate short and long path information * from the Satellite Handbook. This version also takes into consideration * the two points being close enough to be in the near-field, and the * antipodal points, which are easily calculated. These last points were * made in discussions with John Allison who makes the nice MAPIT program. */ #include #include #include #include #include #include #include "support.h" #include "types.h" #include "utils.h" #include "gc.h" #define RADIAN (180.0 / M_PI) struct { gdouble miles; /* arc length for 1 degree, various units of measure */ gchar *text; } Units[] = { { 60.0, "m"}, { 111.2, "km"}, }; /* Error routine */ static void err(gint type) { switch(type) { case 1: update_statusbar(_("Latitude Out of Range (90N to 90S)\n")); break; case 2: update_statusbar(_("Longitude Out of Range (180W to 180E)\n")); break; case 3: update_statusbar(_("Minutes Out of Range (0 to 59)\n")); } } /* Convert Degrees and Minutes to Decimal */ static gdouble dm2dec(gdouble n) { gdouble t; t = (gint)n; n -= t; n /= .60; if (n >= 1.0) { err(3); return(-1); } return (n + t); } /* Parse the input line dd(.mm)[NnSs]ddd(.mm)[EeWw] */ static gint parse(gchar *s, gdouble *lat, gdouble *lon) { register gchar *i, *t = NULL, *e; e = s + strlen(s); for (i = s; i < e; ++i) { switch (*i) { case 'n': case 'N': *i = '\0'; t = i + 1; *lat = atof(s); break; case 's': case 'S': *i = '\0'; t = i + 1; *lat = -atof(s); break; case 'e': case 'E': *i = '\0'; *lon = -atof(t); break; case 'w': case 'W': *i = '\0'; *lon = atof(t); break; } } *lat = dm2dec(*lat); *lon = dm2dec(*lon); if (*lat > 90.0 || *lat < -90.0) { err(1); return(-1); } if (*lon > 180.0 || *lon < -180.0) { err(2); return(-1); } /* Prevent ACOS() Domain Error */ if (*lat == 90.0) *lat = 89.9; if (*lat == -90.0) *lat = -89.9; return(0); } gchar *gc(gint units, gchar *qth, gchar *dest) { gdouble tmp, arc, cosaz, az, azsp, azlp, distsp, distlp; gdouble QTH_Lat, QTH_Long, DEST_Lat, DEST_Long, Delta_Long; gint parseresult = 0; gchar *result = NULL; parseresult = parse(g_strdup(qth), &QTH_Lat, &QTH_Long); if (parseresult == -1) return(""); parseresult = parse(g_strdup(dest), &DEST_Lat, &DEST_Long); if (parseresult == -1) return(""); QTH_Lat /= RADIAN; /* Convert variables to Radians */ QTH_Long /= RADIAN; DEST_Lat /= RADIAN; DEST_Long /= RADIAN; Delta_Long = DEST_Long - QTH_Long; tmp = (sin(QTH_Lat) * sin(DEST_Lat)) + (cos(QTH_Lat) * cos(DEST_Lat) * cos(Delta_Long)); if (tmp > .999999) { /* Station points coincide, use an Omni! */ return(""); } else if (tmp < -.999999) { /* * points are antipodal, he's straight down. * So take 180 Degrees of arc times 60 nm, * and you get 10800 nm, or whatever units... */ return(""); } else { arc = acos(tmp); /* * One degree of arc is 60 Nautical miles * at the surface of the earth, 111.2 km, or 69.1 sm * This method is easier than the one in the handbook */ /* Short Path */ distsp = (Units[units].miles) * (arc * RADIAN); /* Long Path */ distlp = ((Units[units].miles) * 360.0) - distsp; } cosaz = (sin(DEST_Lat) - (sin(QTH_Lat) * cos(arc))) / (sin(arc) * cos(QTH_Lat)); if (cosaz > .999999) az = 0.0; else if (cosaz < -.999999) az = 180.0; else az = acos(cosaz) * RADIAN; /* Handbook had the test ">= 0.0" which looks backwards?? */ if (sin(Delta_Long) < 0.0) { azsp = az; azlp = 180.0 + az; } else { azsp = 360.0 - az; azlp = 180.0 - az; } /* Computations complete, show answer */ result = g_strdup_printf(_("\nShort Path: %03.0f deg, %.0f %s\n\ Long Path: %03.0f deg, %.0f %s\n"), azsp, distsp, Units[units].text, azlp, distlp, Units[units].text); return(result); }