1221 lines
30 KiB
C
1221 lines
30 KiB
C
/*
|
|
*
|
|
* wmWeather-1.31 (C) 1999 Mike Henderson (mghenderson@lanl.gov)
|
|
*
|
|
* - Shows Local Weather conditions
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* This program is free software; 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, 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see the file COPYING); if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* ToDo:
|
|
*
|
|
* - Add a GTK popup window to display data in a nicer way. Currently just use
|
|
* xmessage...
|
|
*
|
|
* - Add "current conditions" graphic (as background?). I.e. one of those little
|
|
* cartoons that show clouds or sun with rain or snow, etc. on it...
|
|
*
|
|
* - Scrolling line to display "sundry" parameters.
|
|
*
|
|
* - Or maybe auto-switch between panels at some user-defined rate?
|
|
*
|
|
*
|
|
* Changes:
|
|
*
|
|
* Version 1.31 - released May 4, 1999.
|
|
* fixed some conversion bugs in wind speed..
|
|
*
|
|
* Version 1.30 - released April 13, 1999.
|
|
* Fixed a bug whereby the App would crash when trying to gain input
|
|
* focus under non-WindowMaker WMs (focus is now grabbed by
|
|
* `PointerRoot' not `iconwin').
|
|
*
|
|
* Added StationID and `time-of-last-update' labels. To do this I needed
|
|
* to shrink the fonts down and scrunch them together a bit more.
|
|
*
|
|
* Added new command line option to change their color;
|
|
* -tc <color>
|
|
*
|
|
* Added code to properly decode wind speed when in MPS.
|
|
*
|
|
* Fixed bug in beaufort wind speed calcs.
|
|
*
|
|
* Version 1.29 - released March 17, 1999.
|
|
* Reorganized wmgeneral.c and renamed it xutils.c (wmgeneral.h
|
|
* -> xutils.h as well ). Also moved it into the same directory as wmWeather.
|
|
* Now, the openXwindow is split into 2 parts. You first need to call
|
|
* initXwindow(argc, argv). This allows us to check the display depth
|
|
* before we commit to a particular pixmap (this will be useful in my
|
|
* other DockApps to dynamically set appropriate pixmaps based on depth).
|
|
* Got rid of alot of the other routines that I never use.
|
|
*
|
|
* Added 4 more command line option to set the colors of the text:
|
|
*
|
|
* -bc <color> for setting the BackGround color.
|
|
* -lc <color> for setting the Label color.
|
|
* -dc <color> for setting the Data color.
|
|
* -wgc <color> for setting the Wind Gust color.
|
|
*
|
|
* Also cleaned up the pixmap to minimize the number of colors used.
|
|
*
|
|
* Changed metric toggle to work with a key press (any key).
|
|
*
|
|
* Added double click support. Now double clicking does the following:
|
|
*
|
|
* Double Mouse Left: pops up the fully decoded METAR file
|
|
* in xmessage.
|
|
*
|
|
* Double Mouse Middle: Currently undefined.
|
|
*
|
|
* Double Mouse Right: Forces a new update (i.e. download.)
|
|
*
|
|
*
|
|
* Version 1.28 - released March 9, 1999.
|
|
* Changed -celsius (-c) option to -metric (-m). Naming makes more
|
|
* sense that way...
|
|
*
|
|
* Added -W option to display WindChill instead of DewPoint.
|
|
* Since Windchill is not always available, we only show it if its
|
|
* available. If its not, we paste up DewPoint as default.
|
|
*
|
|
* Also added -mps option to display wind speed in units of
|
|
* meters/second (when in -metric mode).
|
|
*
|
|
* Version 1.27 - released March 8, 1999.
|
|
* fixed bug in speed calculation when wind is gusting.
|
|
*
|
|
* Version 1.26 - released February 24, 1999.
|
|
* Added -delay option.
|
|
*
|
|
* Version 1.25 - released February 16, 1999.
|
|
* Added Wind speeds on the 'Beaufort scale'
|
|
* Thanks to Paul Martin <pm@zetnet.net> for this addition.
|
|
*
|
|
* Version 1.24 - released February 12, 1999.
|
|
* Added --passive-ftp option to wget.
|
|
*
|
|
* Version 1.23 - released February 2, 1999.
|
|
* Few more bug fixes...
|
|
* Added support for different Pressure units...
|
|
*
|
|
*
|
|
* Version 1.22 - released February 1, 1999.
|
|
* Fixed minor bug in direction abbreviations. Added a bit more to man
|
|
* page.
|
|
*
|
|
* Version 1.21 - released January 29, 1999.
|
|
* Fixed a problem in the perl script. Made the file paths absolute.
|
|
*
|
|
* Version 1.2 - released January 29, 1999.
|
|
* Added Wind speed line. Ended up decoding the Raw METAR line.
|
|
* Fixed a few bugs...
|
|
* Changed location of files from /tmp to ~/.wmWeatherReports
|
|
* Changed units of pressure and wind speed to mmHg and km/h
|
|
* when Metric is set. (Really should change the flag to -metric).
|
|
*
|
|
* Version 1.1 - released January 25, 1999.
|
|
* Bug fixes...
|
|
* Added command line switch to display Temp's in deg. C
|
|
*
|
|
*
|
|
* Version 1.0 - released January 19, 1999.
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Includes
|
|
*/
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <X11/X.h>
|
|
#include <X11/xpm.h>
|
|
#include "xutils.h"
|
|
#include "wmWeather_master.xpm"
|
|
#include "wmWeather_mask.xbm"
|
|
|
|
|
|
|
|
/*
|
|
* Delay between refreshes (in microseconds)
|
|
*/
|
|
#define DELAY 10000L
|
|
#define WMWEATHER_VERSION "1.31"
|
|
#define DEFAULT_UPDATEDELAY 900L
|
|
|
|
|
|
|
|
|
|
void ParseCMDLine(int argc, char *argv[]);
|
|
void ButtonPressEvent(XButtonEvent *);
|
|
void KeyPressEvent(XKeyEvent *);
|
|
char *StringToUpper(char *);
|
|
|
|
|
|
char StationID[10];
|
|
int UpToDate = 0;
|
|
int ShowWindChill = 0;
|
|
int WindChillAvail = 1;
|
|
int Metric = 0;
|
|
int Beaufort = 0;
|
|
int ForceUpdate = 1;
|
|
int ForceDownload = 1;
|
|
int PressureUnits = 0;
|
|
int MetersPerSecond = 0;
|
|
double PressureConv = 1.0;
|
|
long UpdateDelay;
|
|
int GotFirstClick1, GotDoubleClick1;
|
|
int GotFirstClick2, GotDoubleClick2;
|
|
int GotFirstClick3, GotDoubleClick3;
|
|
int DblClkDelay;
|
|
|
|
|
|
/*
|
|
* In a more readable form the Compass directions are:
|
|
* static char *CompassDirection[] = { "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
|
|
* "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}
|
|
* We convert to digits in the sequence `NWSE' so we dont have to put all these
|
|
* combinations into the pixmap.
|
|
*/
|
|
static char *CompassDirection[] = { "0", "003", "03", "303", "3", "323", "23", "223",
|
|
"2", "221", "21", "121", "1", "101", "01", "001"};
|
|
|
|
|
|
|
|
char LabelColor[30] = "#79bdbf";
|
|
char WindGustColor[30] = "#ff0000";
|
|
char DataColor[30] = "#ffbf50";
|
|
char BackColor[30] = "#181818";
|
|
char StationTimeColor[30] = "#c5a6ff";
|
|
|
|
|
|
|
|
/*
|
|
* main
|
|
*/
|
|
int main(int argc, char *argv[]) {
|
|
|
|
struct tm *tTime;
|
|
XEvent event;
|
|
int n, s, m, dt1, dt2, dt3, yd;
|
|
int i, j, len;
|
|
char dir[5];
|
|
int Year, Month, Day;
|
|
int Hours, Mins, Secs;
|
|
int UpdateLTHour = 0.0, UpdateLTMin = 0.0, UpdateUTHour, UpdateUTMin;
|
|
long CurrentLocalTime;
|
|
double UpdateUT, UpdateLT, UT, LT, DT, hour24();
|
|
|
|
|
|
double jd(), CurrentJD;
|
|
char command[1024], Line[512], FileName[10];
|
|
int Tens, q, digit, chr;
|
|
double Pressure, Temperature, sgn, Humidity, DewPoint, WindChill, val;
|
|
double Direction, Speed;
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Parse any command line arguments.
|
|
*/
|
|
ParseCMDLine(argc, argv);
|
|
|
|
|
|
|
|
/*
|
|
* Do the window opening in 2 stages. After the initXwindow() call,
|
|
* we know what the Depth of the Display is. We can then pick an appropriate
|
|
* XPM to use. I.e. may want to use one that has fewer colors to make the App work
|
|
* better on a low-color 8-bit display.
|
|
*/
|
|
initXwindow(argc, argv);
|
|
openXwindow(argc, argv, wmWeather_master, wmWeather_mask_bits, wmWeather_mask_width,
|
|
wmWeather_mask_height, BackColor, LabelColor, WindGustColor, DataColor, StationTimeColor);
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Loop until we die
|
|
*/
|
|
n = 32000;
|
|
s = 32000;
|
|
m = 32000;
|
|
dt1 = 32000;
|
|
dt2 = 32000;
|
|
dt3 = 32000;
|
|
DblClkDelay = 32000;
|
|
UpToDate = 0;
|
|
while(1) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Keep track of # of seconds
|
|
*/
|
|
if (m > 100){
|
|
|
|
m = 0;
|
|
++dt1;
|
|
++dt2;
|
|
++dt3;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Increment counter
|
|
*/
|
|
++m;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Double Click Delays
|
|
* Keep track of click events. If Delay too long, set GotFirstClick's to False.
|
|
*/
|
|
if (DblClkDelay > 15) {
|
|
|
|
DblClkDelay = 0;
|
|
GotFirstClick1 = 0; GotDoubleClick1 = 0;
|
|
GotFirstClick2 = 0; GotDoubleClick2 = 0;
|
|
GotFirstClick3 = 0; GotDoubleClick3 = 0;
|
|
|
|
} else {
|
|
|
|
++DblClkDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Process any pending X events.
|
|
*/
|
|
while(XPending(display)){
|
|
XNextEvent(display, &event);
|
|
switch(event.type){
|
|
case Expose:
|
|
RedrawWindow();
|
|
break;
|
|
case ButtonPress:
|
|
ButtonPressEvent(&event.xbutton);
|
|
break;
|
|
case KeyPress:
|
|
KeyPressEvent(&event.xkey);
|
|
break;
|
|
case ButtonRelease:
|
|
break;
|
|
case EnterNotify:
|
|
XSetInputFocus(display, PointerRoot, RevertToParent, CurrentTime);
|
|
break;
|
|
case LeaveNotify:
|
|
XSetInputFocus(display, PointerRoot, RevertToParent, CurrentTime);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Check the Current Conditions file every (approx.) several seconds.
|
|
* Can significantly reduce this frequency later. But its
|
|
* easier to debug this way...
|
|
* Do this before trying to download again! The file may be there and it
|
|
* may be Up-To-Date!
|
|
*/
|
|
if ((dt2 > 5)||(ForceUpdate)){
|
|
|
|
dt2 = 0;
|
|
|
|
/*
|
|
* Compute Current Julian Date
|
|
*/
|
|
CurrentLocalTime = time(CurrentTime);
|
|
tTime = gmtime(&CurrentLocalTime);
|
|
Year = tTime->tm_year+1900;
|
|
Month = tTime->tm_mon+1;
|
|
Day = tTime->tm_mday;
|
|
Hours = tTime->tm_hour;
|
|
Mins = tTime->tm_min;
|
|
Secs = tTime->tm_sec;
|
|
UT = (double)Hours + (double)Mins/60.0 + (double)Secs/3600.0;
|
|
CurrentJD = jd(Year, Month, Day, UT);
|
|
|
|
CurrentLocalTime = time(CurrentTime);
|
|
tTime = localtime(&CurrentLocalTime);
|
|
Year = tTime->tm_year+1900;
|
|
Month = tTime->tm_mon+1;
|
|
Day = tTime->tm_mday;
|
|
Hours = tTime->tm_hour;
|
|
Mins = tTime->tm_min;
|
|
Secs = tTime->tm_sec;
|
|
LT = (double)Hours + (double)Mins/60.0 + (double)Secs/3600.0;
|
|
|
|
DT = UT - LT;
|
|
if (DT > 24.0) DT -= 24.0;
|
|
if (DT < 0.00) DT += 24.0;
|
|
|
|
|
|
|
|
/*
|
|
* Read in weather data
|
|
*/
|
|
sprintf(FileName, "%s/.wmWeatherReports/%s.dat", getenv("HOME"), StationID);
|
|
if ((fp = fopen(FileName, "r")) != NULL){
|
|
|
|
fgets(Line, 512, fp);
|
|
fgets(Line, 512, fp);
|
|
fgets(Line, 512, fp);
|
|
fscanf(fp, "%d:%d", &UpdateUTHour, &UpdateUTMin);
|
|
if (UpdateUTHour != 99){
|
|
UpdateUT = UpdateUTHour + UpdateUTMin/60.0;
|
|
UpdateLT = UpdateUT - DT;
|
|
if (UpdateLT < 0.0) UpdateLT += 24.0;
|
|
if (UpdateLT > 24.0) UpdateLT -= 24.0;
|
|
UpdateLTHour = (int)UpdateLT;
|
|
UpdateLTMin = (int)((UpdateLT - (double)UpdateLTHour)*60.0 + 0.5);
|
|
if (UpdateLTMin >= 60){
|
|
++UpdateLTHour;
|
|
if (UpdateLTHour >= 24) UpdateLTHour = 0;
|
|
UpdateLTMin = 0;
|
|
}
|
|
} else {
|
|
UpdateLTHour = 99;
|
|
UpdateLTMin = 99;
|
|
}
|
|
|
|
fscanf(fp, "%lf", &Temperature);
|
|
if (Metric) Temperature = (Temperature-32.0)*5.0/9.0;
|
|
fscanf(fp, "%lf", &DewPoint);
|
|
if (Metric) DewPoint = (DewPoint-32.0)*5.0/9.0;
|
|
|
|
fscanf(fp, "%lf", &WindChill);
|
|
/*
|
|
* If WindChill is not available, revert to DewPoint
|
|
*/
|
|
WindChillAvail = (WindChill < -900.0) ? 0 : 1;
|
|
if (Metric) WindChill = (WindChill-32.0)*5.0/9.0;
|
|
|
|
fscanf(fp, "%lf", &Pressure); Pressure += 0.005;
|
|
Pressure *= PressureConv;
|
|
fscanf(fp, "%lf", &Humidity);
|
|
fscanf(fp, "%lf", &Direction);
|
|
fscanf(fp, "%lf", &Speed);
|
|
if (Metric){
|
|
if (MetersPerSecond) Speed *= 0.4473;
|
|
else if (!Beaufort) Speed *= 1.609;
|
|
}
|
|
fclose(fp);
|
|
|
|
} else {
|
|
|
|
Temperature = -9999.0;
|
|
DewPoint = -9999.0;
|
|
WindChill = -9999.0;
|
|
Humidity = -9999.0;
|
|
Pressure = -9999.0;
|
|
Direction = -9999.0;
|
|
Speed = -9999.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Draw window.
|
|
*/
|
|
if ( (dt3 > 5) || ForceUpdate){
|
|
|
|
|
|
dt3 = 0;
|
|
|
|
|
|
|
|
/*
|
|
* Clear window.
|
|
*/
|
|
copyXPMArea(5, 69, 54, 54, 5, 5);
|
|
|
|
|
|
/*
|
|
* Paste up Station ID and time of last update.
|
|
*/
|
|
|
|
q = 0;
|
|
chr = (int)StationID[0] - 65; copyXPMArea(chr*5+2, 128, 5, 6, 7+q, 6); q+= 5;
|
|
chr = (int)StationID[1] - 65; copyXPMArea(chr*5+2, 128, 5, 6, 7+q, 6); q+= 5;
|
|
chr = (int)StationID[2] - 65; copyXPMArea(chr*5+2, 128, 5, 6, 7+q, 6); q+= 5;
|
|
chr = (int)StationID[3] - 65; copyXPMArea(chr*5+2, 128, 5, 6, 7+q, 6); q+= 5;
|
|
|
|
if (UpdateLTHour != 99){
|
|
q = 0;
|
|
Tens = (int)(UpdateLTHour);
|
|
copyXPMArea(Tens/10*5+2, 135, 5, 6, 36+q, 6); q+= 5;
|
|
copyXPMArea(Tens%10*5+2, 135, 5, 6, 36+q, 6); q+= 5;
|
|
copyXPMArea(53, 135, 1, 6, 36+q, 6); q+= 2;
|
|
Tens = (int)(UpdateLTMin);
|
|
copyXPMArea(Tens/10*5+2, 135, 5, 6, 36+q, 6); q+= 5;
|
|
copyXPMArea(Tens%10*5+2, 135, 5, 6, 36+q, 6); q+= 5;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Paste up Temperature.
|
|
*/
|
|
if ((Temperature > -999.0)&&(Temperature < 1000.0)){
|
|
sgn = (Temperature < 0.0) ? -1.0 : 1.0;
|
|
Temperature *= sgn;
|
|
Temperature = (double)((int)(Temperature + 0.5));
|
|
q = 0;
|
|
if (Temperature >= 100.0){
|
|
if (sgn < 0.0) { copyXPMArea(0*5+66, 35, 5, 6, 25, 15); q += 5; }
|
|
digit = (int)(Temperature/100.0);
|
|
copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 15); q+= 5;
|
|
Tens = (int)(Temperature-digit*100.0);
|
|
copyXPMArea(Tens/10*5+66, 57, 5, 6, 25+q, 15); q+= 5;
|
|
copyXPMArea(Tens%10*5+66, 57, 5, 6, 25+q, 15); q+= 5;
|
|
} else {
|
|
if (sgn < 0.0) { copyXPMArea(0*5+66, 35, 5, 6, 25, 15); q += 5; }
|
|
Tens = (int)(Temperature);
|
|
if (Tens >= 10) { copyXPMArea(Tens/10*5+66, 57, 5, 6, 25+q, 15); q+= 5; }
|
|
copyXPMArea(Tens%10*5+66, 57, 5, 6, 25+q, 15); q+= 5;
|
|
}
|
|
if (Metric){
|
|
copyXPMArea(72, 34, 3, 3, 25+q, 14); q += 4;
|
|
copyXPMArea(81, 35, 4, 6, 25+q, 15);
|
|
} else {
|
|
copyXPMArea(72, 34, 8, 7, 25+q, 14);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Paste up DewPoint or WindChill Temperature.
|
|
*/
|
|
if (ShowWindChill && WindChillAvail){
|
|
val = WindChill;
|
|
copyXPMArea(66, 87, 17, 8, 5, 24);
|
|
} else {
|
|
val = DewPoint;
|
|
copyXPMArea(5, 87, 17, 8, 5, 24);
|
|
}
|
|
|
|
if ((val > -999.0)&&(val < 1000.0)){
|
|
sgn = (val < 0.0) ? -1.0 : 1.0;
|
|
val *= sgn;
|
|
val = (double)((int)(val + 0.5));
|
|
q = 0;
|
|
if (val >= 100.0){
|
|
if (sgn < 0.0) { copyXPMArea(0*5+66, 57, 5, 6, 25, 24); q += 5; }
|
|
digit = (int)(val/100.0);
|
|
copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 24); q+= 5;
|
|
Tens = (int)(val-digit*100.0);
|
|
copyXPMArea(Tens/10*5+66, 57, 5, 6, 25+q, 24); q+= 5;
|
|
copyXPMArea(Tens%10*5+66, 57, 5, 6, 25+q, 24); q+= 5;
|
|
} else {
|
|
if (sgn < 0.0) { copyXPMArea(0*5+66, 35, 5, 6, 25, 24); q += 5; }
|
|
Tens = (int)(val);
|
|
if (Tens >= 10) { copyXPMArea(Tens/10*5+66, 57, 5, 6, 25+q, 24); q+= 5; }
|
|
copyXPMArea(Tens%10*5+66, 57, 5, 6, 25+q, 24); q+= 5;
|
|
}
|
|
if (Metric){
|
|
copyXPMArea(72, 34, 3, 3, 25+q, 23); q += 4;
|
|
copyXPMArea(81, 35, 4, 6, 25+q, 24);
|
|
} else {
|
|
copyXPMArea(72, 34, 8, 7, 25+q, 23);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Paste up Pressure.
|
|
*/
|
|
q = 0; s= 0;
|
|
if ((Pressure > 0.0)&&(Pressure <= 10000.0)){
|
|
val = Pressure;
|
|
|
|
digit = (int)(val/1000.0);
|
|
if (digit > 0) { copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 33); q += 5; }
|
|
val -= (double)digit*1000;
|
|
|
|
digit = (int)(val/100.0);
|
|
if ((digit > 0)||(Pressure > 999.0)) { copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 33); q += 5; }
|
|
val -= (double)digit*100;
|
|
|
|
digit = (int)(val/10.0);
|
|
if ((digit > 0)||(Pressure > 99.0)) { copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 33); q += 5; }
|
|
val -= (double)digit*10;
|
|
|
|
digit = (int)val;
|
|
copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 33); q += 5;
|
|
val -= (double)digit;
|
|
|
|
|
|
if ((PressureUnits != 2)||(!Metric)){
|
|
|
|
copyXPMArea(10*5+66+1, 57, 4, 6, 25+q, 33); q += 4;
|
|
|
|
val *= 10; digit = (int)val;
|
|
copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 33); q += 5;
|
|
val -= (double)digit;
|
|
|
|
if (!Metric){
|
|
val *= 10; digit = (int)val;
|
|
copyXPMArea(digit*5+66, 57, 5, 6, 25+q, 33); q += 5;
|
|
/* val -= (double)digit; */
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Paste up Humidity.
|
|
*/
|
|
q = 0;
|
|
if ((Humidity > -999.0)&&(Humidity <= 100.0)){
|
|
if (Humidity == 100.0){
|
|
copyXPMArea(1*5+66, 57, 5, 6, 25+q, 42); q += 5;
|
|
copyXPMArea(0*5+66, 57, 5, 6, 25+q, 42); q += 5;
|
|
copyXPMArea(0*5+66, 57, 5, 6, 25+q, 42); q += 5;
|
|
} else {
|
|
Tens = (int)(Humidity);
|
|
if (Tens >= 10) { copyXPMArea(Tens/10*5+66, 57, 5, 6, 25+q, 42); q += 5; }
|
|
copyXPMArea(Tens%10*5+66, 57, 5, 6, 25+q, 42); q += 5;
|
|
}
|
|
copyXPMArea(121, 57, 5, 6, 25+q, 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Paste up Wind Info.
|
|
*/
|
|
if ((Direction == 0.0)&&(Speed == 0.0)){
|
|
|
|
/*
|
|
* Just write out `Calm' if both values are 0
|
|
*/
|
|
copyXPMArea(66, 4, 23, 7, 25, 51);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* If the Direction < 0 this means that there was a "Wind direction
|
|
* Variability Group" found in the METAR code. I.e. Direction is variable.
|
|
* I think value should be the average. In any case flag it by making it a
|
|
* different color.
|
|
*
|
|
* Likewise, if Speed < 0, this means that a "Gusty" modifier was found
|
|
* in the RAW METAR code, and the value is only the average of the low
|
|
* and high values given. Again, flag it in a different color...
|
|
*
|
|
*/
|
|
if ((Direction >= -360.0)&&(Direction <= 360.0)){
|
|
sgn = (Direction < 0.0) ? -1.0 : 1.0;
|
|
yd = (sgn < 0.0) ? 50 : 43;
|
|
Direction *= sgn;
|
|
q = 0;
|
|
i = (int)(Direction/360.0*16.0 + 0.5);
|
|
if (i>15) i = 0;
|
|
strcpy(dir, CompassDirection[i]);
|
|
len = strlen(dir);
|
|
for (j=0; j<len; ++j){
|
|
digit = (int)dir[j] - 48;
|
|
copyXPMArea(digit*5+66, yd, 5, 6, 25+q, 51); q+= 5;
|
|
}
|
|
q += 2;
|
|
} else if (Direction > 0.0){
|
|
|
|
/*
|
|
* In this case, the wind direction is variable with speed < 6 Knots.
|
|
* A numerical direction is not given in these cases. Just write out 'VRB'.
|
|
*/
|
|
q = 0;
|
|
copyXPMArea(4*5+66, 43, 5, 6, 25+q, 51); q+= 5;
|
|
copyXPMArea(5*5+66, 43, 5, 6, 25+q, 51); q+= 5;
|
|
copyXPMArea(6*5+66, 43, 5, 6, 25+q, 51); q+= 9;
|
|
}
|
|
|
|
if (Metric && Beaufort) {
|
|
int beau = 0;
|
|
int spd;
|
|
|
|
sgn = (Speed < 0.0) ? -1.0 : 1.0;
|
|
spd = (int)(sgn * (int)Speed);
|
|
if (spd > 1) { beau = 1; }
|
|
if (spd > 3) { beau = 2; }
|
|
if (spd > 4) { beau = 3; }
|
|
if (spd > 10) { beau = 4; }
|
|
if (spd > 16) { beau = 5; }
|
|
if (spd > 21) { beau = 6; }
|
|
if (spd > 27) { beau = 7; }
|
|
if (spd > 33) { beau = 8; }
|
|
if (spd > 40) { beau = 9; }
|
|
if (spd > 47) { beau = 10; }
|
|
if (spd > 55) { beau = 11; }
|
|
if (spd > 63) { beau = 12; }
|
|
if (spd > 71) { beau = 13; }
|
|
Speed = sgn * (double) beau;
|
|
q++; copyXPMArea(76, 35, 4, 6, 25+q, 51); q+= 6;
|
|
}
|
|
|
|
|
|
if ((Speed > -999.0)&&(Speed < 1000.0)){
|
|
sgn = (Speed < 0.0) ? -1.0 : 1.0;
|
|
yd = (sgn < 0.0) ? 64 : 57;
|
|
Speed *= sgn;
|
|
if (Speed >= 100.0){
|
|
digit = (int)(Speed/100.0);
|
|
copyXPMArea(digit*5+66, yd, 5, 6, 25+q, 51); q+= 5;
|
|
Tens = (int)(Speed-digit*100.0);
|
|
copyXPMArea(Tens/10*5+66, yd, 5, 6, 25+q, 51); q+= 5;
|
|
copyXPMArea(Tens%10*5+66, yd, 5, 6, 25+q, 51); q+= 5;
|
|
} else {
|
|
Tens = (int)(Speed);
|
|
if (Tens >= 10) { copyXPMArea(Tens/10*5+66, yd, 5, 6, 25+q, 51); q+= 5; }
|
|
copyXPMArea(Tens%10*5+66, yd, 5, 6, 25+q, 51); q+= 5;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Make changes visible
|
|
*/
|
|
RedrawWindow();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Reset "force update" flag
|
|
*/
|
|
ForceUpdate = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Check every 5 min if the values are not up to date...
|
|
*/
|
|
/*
|
|
* We still need to add a flashing LED to warn about
|
|
* times that are out of date. Also need to determine if it is uptodate...
|
|
*/
|
|
UpToDate = 0;
|
|
if (((!UpToDate)&&(dt1 > UpdateDelay)) || ForceDownload){
|
|
|
|
dt1 = 0;
|
|
|
|
/*
|
|
* Execute Perl script to grab the Latest METAR Report
|
|
*/
|
|
sprintf(command, "GrabWeather %s &", StationID);
|
|
system(command);
|
|
|
|
ForceDownload = 0;
|
|
ForceUpdate = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Wait for next update
|
|
*/
|
|
usleep(DELAY);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* ParseCMDLine()
|
|
*/
|
|
void ParseCMDLine(int argc, char *argv[]) {
|
|
|
|
int i;
|
|
void print_usage();
|
|
|
|
StationID[0] = '\0';
|
|
PressureUnits = 0;
|
|
MetersPerSecond = 0;
|
|
ShowWindChill = 0;
|
|
UpdateDelay = DEFAULT_UPDATEDELAY;
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
if (!strcmp(argv[i], "-display")){
|
|
|
|
++i;
|
|
|
|
} else if (!strcmp(argv[i], "-bc")){
|
|
|
|
if ((i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
fprintf(stderr, "wmWeather: No color found\n");
|
|
print_usage();
|
|
exit(-1);
|
|
}
|
|
strcpy(BackColor, argv[++i]);
|
|
|
|
} else if (!strcmp(argv[i], "-tc")){
|
|
|
|
if ((i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
fprintf(stderr, "wmWeather: No color found\n");
|
|
print_usage();
|
|
exit(-1);
|
|
}
|
|
strcpy(StationTimeColor, argv[++i]);
|
|
|
|
} else if (!strcmp(argv[i], "-lc")){
|
|
|
|
if ((i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
fprintf(stderr, "wmWeather: No color found\n");
|
|
print_usage();
|
|
exit(-1);
|
|
}
|
|
strcpy(LabelColor, argv[++i]);
|
|
|
|
} else if (!strcmp(argv[i], "-wgc")){
|
|
|
|
if ((i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
fprintf(stderr, "wmWeather: No color found\n");
|
|
print_usage();
|
|
exit(-1);
|
|
}
|
|
strcpy(WindGustColor, argv[++i]);
|
|
|
|
} else if (!strcmp(argv[i], "-dc")){
|
|
|
|
if ((i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
fprintf(stderr, "wmWeather: No color found\n");
|
|
print_usage();
|
|
exit(-1);
|
|
}
|
|
strcpy(DataColor, argv[++i]);
|
|
|
|
} else if (!strcmp(argv[i], "-beaufort")){
|
|
|
|
Beaufort = 1;
|
|
|
|
} else if (!strcmp(argv[i], "-mps")){
|
|
|
|
MetersPerSecond = 1;
|
|
|
|
} else if (!strcmp(argv[i], "-W")){
|
|
|
|
ShowWindChill = 1;
|
|
|
|
} else if ((!strcmp(argv[i], "-metric"))||(!strcmp(argv[i], "-m"))){
|
|
|
|
Metric = 1;
|
|
|
|
} else if (!strcmp(argv[i], "-kPa")){
|
|
|
|
PressureUnits = 1;
|
|
PressureConv = 3.38639;
|
|
|
|
} else if (!strcmp(argv[i], "-hPa")){
|
|
|
|
PressureUnits = 2;
|
|
PressureConv = 33.8639;
|
|
|
|
} else if (!strcmp(argv[i], "-mmHg")){
|
|
|
|
PressureUnits = 3;
|
|
PressureConv = 25.4;
|
|
|
|
} else if ((!strcmp(argv[i], "-station"))||(!strcmp(argv[i], "-s"))){
|
|
|
|
if ((i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
fprintf(stderr, "wmWeather: No METAR station ID found\n");
|
|
print_usage();
|
|
exit(-1);
|
|
}
|
|
strcpy(StationID, StringToUpper(argv[++i]));
|
|
|
|
} else if (!strcmp(argv[i], "-delay")) {
|
|
|
|
if( (i+1 >= argc)||(argv[i+1][0] == '-')) {
|
|
|
|
fprintf(stderr,"You must give a time with the -delay option.\n");
|
|
print_usage();
|
|
exit(-1);
|
|
|
|
} else if(sscanf(argv[i+1], "%ld", &UpdateDelay) != 1) {
|
|
|
|
fprintf(stderr,"Dont understand the delay time you have entered (%s).\n", argv[i+1]);
|
|
print_usage();
|
|
exit(-1);
|
|
|
|
}
|
|
/*
|
|
* Convert Time to seconds
|
|
*/
|
|
UpdateDelay *= 60;
|
|
++i;
|
|
|
|
} else {
|
|
|
|
print_usage();
|
|
exit(-11);
|
|
|
|
}
|
|
}
|
|
|
|
if (StationID[0] == '\0') {
|
|
fprintf(stderr, "\nwmWeather: You must specify a METAR station code\n\n");
|
|
print_usage();
|
|
exit(1);
|
|
}
|
|
|
|
if ((Metric)&&(PressureUnits == 0)){
|
|
PressureUnits = 3;
|
|
PressureConv = 25.4;
|
|
}
|
|
|
|
if (!Metric){
|
|
PressureConv = 1.0;
|
|
}
|
|
|
|
if (Beaufort && MetersPerSecond){
|
|
fprintf(stderr, "\nwmWeather: You cant use both -beaufort and -mps together.\n\n");
|
|
print_usage();
|
|
exit(1);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void print_usage(){
|
|
|
|
printf("\nwmWeather version: %s\n", WMWEATHER_VERSION);
|
|
printf("\nusage: wmWeather -s <StationID> [-display <Display>] [-h] [-metric] [-kPa] [-hPa] [-mmHg]\n");
|
|
printf(" [-beaufort] [-mps] [-W] [-delay <Time in Minutes>] [-bc <color>]\n");
|
|
printf(" [-lc <color>] [-dc <color>] [-wgc <color>] [-tc <color>]\n\n");
|
|
printf("\t-display <Display>\t\tUse alternate X display.\n\n");
|
|
printf("\t-h\t\t\t\tDisplay help screen.\n");
|
|
printf("\n\t-station <METAR StationID>\n");
|
|
printf("\t-s <METAR StationID>\tThe 4-character METAR Station ID.\n\n");
|
|
printf("\t-W\t\t\t\tIf available, display WindChill instead\n");
|
|
printf("\t\t\t\t\tof DewPoint Temperature.\n");
|
|
printf("\t-metric\n");
|
|
printf("\t-m\t\t\t\tDisplay variables in metric units.\n\n");
|
|
printf("\t-kPa\t\t\t\tWhen toggled to metric display, show pressure\n");
|
|
printf("\t\t\t\t\tin units of kPa.\n\n");
|
|
printf("\t-hPa\t\t\t\tWhen toggled to metric display, show pressure\n");
|
|
printf("\t\t\t\t\tin units of hPa.\n\n");
|
|
printf("\t-mmHg\t\t\t\tWhen toggled to metric display, show pressure\n");
|
|
printf("\t\t\t\t\tin units of mmHg. (This is the default for metric).\n\n");
|
|
printf("\t-beaufort\t\t\tWhen toggled to metric display, show windspeed\n");
|
|
printf("\t\t\t\t\ton the Beaufort scale. (default is km/h.)\n\n");
|
|
printf("\t-mps\t\t\t\tWhen toggled to metric display, show windspeed\n");
|
|
printf("\t\t\t\t\tin units of meters/second. (default is km/h.)\n\n");
|
|
printf("\t-bc <color>\t\t\tBackground color. (#7e9e69 is a greenish LCD color).\n\n");
|
|
printf("\t-lc <color>\t\t\tLabel color.\n\n");
|
|
printf("\t-dc <color>\t\t\tData color.\n\n");
|
|
printf("\t-wgc <color>\t\t\tGusty-wind/variable-direction color.\n\n");
|
|
printf("\t-tc <color>\t\t\tStation ID and Time color.\n\n");
|
|
printf("\t-delay <Time in Minutes>\tOverride time (in minutes) between updates (default\n");
|
|
printf("\t\t\t\t\tis %ld minutes). (Times are approximate.)\n", DEFAULT_UPDATEDELAY/60);
|
|
printf("\n\nTo find out more about the METAR/TAF system and to find the \n");
|
|
printf("METAR code for your location, look at:\n\n");
|
|
printf(" http://www.nws.noaa.gov/oso/oso1/oso12/metar.htm \n\n");
|
|
printf("for NOAA's ""National Weather Service METAR/TAF Information"" page.\n");
|
|
printf("To locate your site ID go to NOAA's ""Meteorological Station Information\nLookup"" page at:\n\n");
|
|
printf(" http://www.nws.noaa.gov/oso/siteloc.shtml\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Compute the Julian Day number for the given date.
|
|
* Julian Date is the number of days since noon of Jan 1 4713 B.C.
|
|
*/
|
|
double jd(ny, nm, nd, UT)
|
|
int ny, nm, nd;
|
|
double UT;
|
|
{
|
|
double A, B, C, D, JD, day;
|
|
|
|
day = nd + UT/24.0;
|
|
|
|
|
|
if ((nm == 1) || (nm == 2)){
|
|
ny = ny - 1;
|
|
nm = nm + 12;
|
|
}
|
|
|
|
if (((double)ny+nm/12.0+day/365.25)>=(1582.0+10.0/12.0+15.0/365.25)){
|
|
A = ((int)(ny / 100.0));
|
|
B = 2.0 - A + (int)(A/4.0);
|
|
}
|
|
else{
|
|
B = 0.0;
|
|
}
|
|
if (ny < 0.0){
|
|
C = (int)((365.25*(double)ny) - 0.75);
|
|
}
|
|
else{
|
|
C = (int)(365.25*(double)ny);
|
|
}
|
|
|
|
D = (int)(30.6001*(double)(nm+1));
|
|
|
|
|
|
JD = B + C + D + day + 1720994.5;
|
|
return(JD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* This routine handles button presses.
|
|
*
|
|
* - Left Mouse single click toggles Deg F/C for temperatures.
|
|
* - Some other click event should display the full METAR report -- lots of
|
|
* juicy stuff in there... Should bring up a separate window...
|
|
*
|
|
*
|
|
*/
|
|
void ButtonPressEvent(XButtonEvent *xev){
|
|
|
|
char Command[80];
|
|
|
|
/*
|
|
* Process single clicks.
|
|
*/
|
|
DblClkDelay = 0;
|
|
if ((xev->button == Button1) && (xev->type == ButtonPress)){
|
|
|
|
if (GotFirstClick1) GotDoubleClick1 = 1;
|
|
else GotFirstClick1 = 1;
|
|
|
|
} else if ((xev->button == Button2) && (xev->type == ButtonPress)){
|
|
|
|
if (GotFirstClick2) GotDoubleClick2 = 1;
|
|
else GotFirstClick2 = 1;
|
|
|
|
} else if ((xev->button == Button3) && (xev->type == ButtonPress)){
|
|
|
|
if (GotFirstClick3) GotDoubleClick3 = 1;
|
|
else GotFirstClick3 = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* We got a double click on Mouse Button1 (i.e. the left one)
|
|
*/
|
|
if (GotDoubleClick1) {
|
|
GotFirstClick1 = 0;
|
|
GotDoubleClick1 = 0;
|
|
sprintf(Command, "xmessage -center -file %s/.wmWeatherReports/%s.TXT &", getenv("HOME"), StationID);
|
|
system(Command);
|
|
}
|
|
|
|
|
|
/*
|
|
* We got a double click on Mouse Button2 (i.e. the left one)
|
|
*/
|
|
if (GotDoubleClick2) {
|
|
GotFirstClick2 = 0;
|
|
GotDoubleClick2 = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* We got a double click on Mouse Button3 (i.e. the left one)
|
|
*/
|
|
if (GotDoubleClick3) {
|
|
GotFirstClick3 = 0;
|
|
GotDoubleClick3 = 0;
|
|
ForceDownload = 1;
|
|
ForceUpdate = 1;
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* This routine handles key presses.
|
|
*
|
|
*/
|
|
void KeyPressEvent(XKeyEvent *xev){
|
|
|
|
Metric = !Metric;
|
|
|
|
|
|
|
|
if (Metric){
|
|
|
|
switch(PressureUnits){
|
|
case 0: PressureConv = 25.4;
|
|
break;
|
|
|
|
case 1: PressureConv = 3.38639;
|
|
break;
|
|
|
|
case 2: PressureConv = 33.8639;
|
|
break;
|
|
|
|
case 3: PressureConv = 25.4;
|
|
break;
|
|
}
|
|
|
|
} else if (!Metric){
|
|
|
|
PressureConv = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ForceUpdate = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
char *StringToUpper(char *String) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < strlen(String); i++)
|
|
String[i] = toupper(String[i]);
|
|
|
|
return String;
|
|
|
|
}
|
|
|
|
|
|
|
|
|