20 |
/* |
/* |
21 |
* gc.c - see description below by N5OWK, adopted for xlog October 2001 |
* gc.c - see description below by N5OWK, adopted for xlog October 2001 |
22 |
*/ |
*/ |
23 |
|
|
24 |
/* |
/* |
25 |
* Great Circle. This program is used to determine bearing |
* Great Circle. This program is used to determine bearing |
26 |
* and range to two stations given latitude and longitude. |
* and range to two stations given latitude and longitude. |
58 |
|
|
59 |
#define RADIAN (180.0 / M_PI) |
#define RADIAN (180.0 / M_PI) |
60 |
|
|
61 |
struct { |
struct |
62 |
gdouble miles; /* arc length for 1 degree, various units of measure */ |
{ |
63 |
gchar *text; |
gdouble miles; /* arc length for 1 degree, various units of measure */ |
64 |
} Units[] = { |
gchar *text; |
65 |
{ 60.0, "m"}, |
} |
66 |
{ 111.2, "km"}, |
Units[] = |
67 |
}; |
{ |
68 |
|
{ |
69 |
|
60.0, "m"} |
70 |
|
, |
71 |
|
{ |
72 |
|
111.2, "km"} |
73 |
|
,}; |
74 |
|
|
75 |
/* Error routine */ |
/* Error routine */ |
76 |
static void err(gint type) { |
static void |
77 |
switch(type) { |
err (gint type) |
78 |
case 1: |
{ |
79 |
update_statusbar(_("Latitude Out of Range (90N to 90S)\n")); |
switch (type) |
80 |
break; |
{ |
81 |
case 2: |
case 1: |
82 |
update_statusbar(_("Longitude Out of Range (180W to 180E)\n")); |
update_statusbar (_("Latitude Out of Range (90N to 90S)\n")); |
83 |
break; |
break; |
84 |
case 3: |
case 2: |
85 |
update_statusbar(_("Minutes Out of Range (0 to 59)\n")); |
update_statusbar (_("Longitude Out of Range (180W to 180E)\n")); |
86 |
} |
break; |
87 |
|
case 3: |
88 |
|
update_statusbar (_("Minutes Out of Range (0 to 59)\n")); |
89 |
|
} |
90 |
} |
} |
91 |
|
|
92 |
/* Convert Degrees and Minutes to Decimal */ |
/* Convert Degrees and Minutes to Decimal */ |
93 |
static gdouble dm2dec(gdouble n) { |
static gdouble |
94 |
gdouble t; |
dm2dec (gdouble n) |
95 |
|
{ |
96 |
|
gdouble t; |
97 |
|
|
98 |
t = (gint)n; |
t = (gint) n; |
99 |
n -= t; |
n -= t; |
100 |
n /= .60; |
n /= .60; |
101 |
if (n >= 1.0) { |
if (n >= 1.0) |
102 |
err(3); |
{ |
103 |
return(-1); |
err (3); |
104 |
} |
return (-1); |
105 |
return (n + t); |
} |
106 |
|
return (n + t); |
107 |
} |
} |
108 |
|
|
109 |
/* Parse the input line dd(.mm)[NnSs]ddd(.mm)[EeWw] */ |
/* Parse the input line dd(.mm)[NnSs]ddd(.mm)[EeWw] */ |
110 |
static gint parse(gchar *s, gdouble *lat, gdouble *lon) { |
static gint |
111 |
register gchar *i, *t = NULL, *e; |
parse (gchar * s, gdouble * lat, gdouble * lon) |
112 |
|
{ |
113 |
e = s + strlen(s); |
register gchar *i, *t = NULL, *e; |
|
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); |
|
|
} |
|
114 |
|
|
115 |
if (*lon > 180.0 || *lon < -180.0) { |
e = s + strlen (s); |
116 |
err(2); |
for (i = s; i < e; ++i) |
117 |
return(-1); |
{ |
118 |
} |
switch (*i) |
119 |
|
{ |
120 |
|
case 'n': |
121 |
|
case 'N': |
122 |
|
*i = '\0'; |
123 |
|
t = i + 1; |
124 |
|
*lat = atof (s); |
125 |
|
break; |
126 |
|
case 's': |
127 |
|
case 'S': |
128 |
|
*i = '\0'; |
129 |
|
t = i + 1; |
130 |
|
*lat = -atof (s); |
131 |
|
break; |
132 |
|
case 'e': |
133 |
|
case 'E': |
134 |
|
*i = '\0'; |
135 |
|
*lon = -atof (t); |
136 |
|
break; |
137 |
|
case 'w': |
138 |
|
case 'W': |
139 |
|
*i = '\0'; |
140 |
|
*lon = atof (t); |
141 |
|
break; |
142 |
|
} |
143 |
|
} |
144 |
|
|
145 |
|
*lat = dm2dec (*lat); |
146 |
|
*lon = dm2dec (*lon); |
147 |
|
|
148 |
|
if (*lat > 90.0 || *lat < -90.0) |
149 |
|
{ |
150 |
|
err (1); |
151 |
|
return (-1); |
152 |
|
} |
153 |
|
|
154 |
|
if (*lon > 180.0 || *lon < -180.0) |
155 |
|
{ |
156 |
|
err (2); |
157 |
|
return (-1); |
158 |
|
} |
159 |
|
|
160 |
|
/* Prevent ACOS() Domain Error */ |
161 |
|
if (*lat == 90.0) |
162 |
|
*lat = 89.9; |
163 |
|
if (*lat == -90.0) |
164 |
|
*lat = -89.9; |
165 |
|
|
166 |
/* Prevent ACOS() Domain Error */ |
return (0); |
|
if (*lat == 90.0) *lat = 89.9; |
|
|
if (*lat == -90.0) *lat = -89.9; |
|
|
|
|
|
return(0); |
|
167 |
} |
} |
168 |
|
|
169 |
gchar *gc(gint units, gchar *qth, gchar *dest) |
gchar * |
170 |
|
gc (gint units, gchar * qth, gchar * dest) |
171 |
{ |
{ |
172 |
gdouble tmp, arc, cosaz, az, azsp, azlp, distsp, distlp; |
gdouble tmp, arc, cosaz, az, azsp, azlp, distsp, distlp; |
173 |
gdouble QTH_Lat, QTH_Long, DEST_Lat, DEST_Long, Delta_Long; |
gdouble QTH_Lat, QTH_Long, DEST_Lat, DEST_Long, Delta_Long; |
174 |
gint parseresult = 0; |
gint parseresult = 0; |
175 |
gchar *result = NULL; |
gchar *result = NULL; |
176 |
|
|
177 |
parseresult = parse(g_strdup(qth), &QTH_Lat, &QTH_Long); |
parseresult = parse (g_strdup (qth), &QTH_Lat, &QTH_Long); |
178 |
if (parseresult == -1) return(""); |
if (parseresult == -1) |
179 |
parseresult = parse(g_strdup(dest), &DEST_Lat, &DEST_Long); |
return (""); |
180 |
if (parseresult == -1) return(""); |
parseresult = parse (g_strdup (dest), &DEST_Lat, &DEST_Long); |
181 |
|
if (parseresult == -1) |
182 |
QTH_Lat /= RADIAN; /* Convert variables to Radians */ |
return (""); |
183 |
QTH_Long /= RADIAN; |
|
184 |
DEST_Lat /= RADIAN; |
QTH_Lat /= RADIAN; /* Convert variables to Radians */ |
185 |
DEST_Long /= RADIAN; |
QTH_Long /= RADIAN; |
186 |
|
DEST_Lat /= RADIAN; |
187 |
Delta_Long = DEST_Long - QTH_Long; |
DEST_Long /= RADIAN; |
188 |
|
|
189 |
tmp = (sin(QTH_Lat) * sin(DEST_Lat)) + |
Delta_Long = DEST_Long - QTH_Long; |
190 |
(cos(QTH_Lat) * cos(DEST_Lat) * cos(Delta_Long)); |
|
191 |
|
tmp = (sin (QTH_Lat) * sin (DEST_Lat)) + |
192 |
if (tmp > .999999) |
(cos (QTH_Lat) * cos (DEST_Lat) * cos (Delta_Long)); |
193 |
{ |
|
194 |
/* Station points coincide, use an Omni! */ |
if (tmp > .999999) |
195 |
return(""); |
{ |
196 |
} |
/* Station points coincide, use an Omni! */ |
197 |
else if (tmp < -.999999) |
return (""); |
198 |
{ |
} |
199 |
|
else if (tmp < -.999999) |
200 |
|
{ |
201 |
|
|
202 |
/* |
/* |
203 |
* points are antipodal, he's straight down. |
* points are antipodal, he's straight down. |
204 |
* So take 180 Degrees of arc times 60 nm, |
* So take 180 Degrees of arc times 60 nm, |
205 |
* and you get 10800 nm, or whatever units... |
* and you get 10800 nm, or whatever units... |
206 |
*/ |
*/ |
207 |
return(""); |
return (""); |
208 |
} |
} |
209 |
else |
else |
210 |
{ |
{ |
211 |
arc = acos(tmp); |
arc = acos (tmp); |
212 |
|
|
213 |
/* |
/* |
214 |
* One degree of arc is 60 Nautical miles |
* One degree of arc is 60 Nautical miles |
216 |
* This method is easier than the one in the handbook |
* This method is easier than the one in the handbook |
217 |
*/ |
*/ |
218 |
|
|
219 |
/* Short Path */ |
/* Short Path */ |
220 |
distsp = (Units[units].miles) * (arc * RADIAN); |
distsp = (Units[units].miles) * (arc * RADIAN); |
221 |
/* Long Path */ |
/* Long Path */ |
222 |
distlp = ((Units[units].miles) * 360.0) - distsp; |
distlp = ((Units[units].miles) * 360.0) - distsp; |
223 |
} |
} |
224 |
|
|
225 |
cosaz = (sin(DEST_Lat) - (sin(QTH_Lat) * cos(arc))) / |
cosaz = (sin (DEST_Lat) - (sin (QTH_Lat) * cos (arc))) / |
226 |
(sin(arc) * cos(QTH_Lat)); |
(sin (arc) * cos (QTH_Lat)); |
227 |
|
|
228 |
if (cosaz > .999999) az = 0.0; |
if (cosaz > .999999) |
229 |
else if (cosaz < -.999999) az = 180.0; |
az = 0.0; |
230 |
else az = acos(cosaz) * RADIAN; |
else if (cosaz < -.999999) |
231 |
|
az = 180.0; |
232 |
/* Handbook had the test ">= 0.0" which looks backwards?? */ |
else |
233 |
|
az = acos (cosaz) * RADIAN; |
234 |
if (sin(Delta_Long) < 0.0) |
|
235 |
{ |
/* Handbook had the test ">= 0.0" which looks backwards?? */ |
236 |
azsp = az; |
|
237 |
azlp = 180.0 + az; |
if (sin (Delta_Long) < 0.0) |
238 |
} |
{ |
239 |
else { |
azsp = az; |
240 |
azsp = 360.0 - az; |
azlp = 180.0 + az; |
241 |
azlp = 180.0 - az; |
} |
242 |
} |
else |
243 |
|
{ |
244 |
/* Computations complete, show answer */ |
azsp = 360.0 - az; |
245 |
result = g_strdup_printf(_("\nShort Path: %03.0f deg, %.0f %s\n\ |
azlp = 180.0 - az; |
246 |
Long Path: %03.0f deg, %.0f %s\n"), |
} |
247 |
azsp, distsp, Units[units].text, azlp, distlp, Units[units].text); |
|
248 |
return(result); |
/* Computations complete, show answer */ |
249 |
|
result = g_strdup_printf (_("\nShort Path: %03.0f deg, %.0f %s\n\ |
250 |
|
Long Path: %03.0f deg, %.0f %s\n"), azsp, distsp, Units[units].text, azlp, distlp, Units[units].text); |
251 |
|
return (result); |
252 |
} |
} |