1 /*
2 *
3 * XASTIR, Amateur Station Tracking and Information Reporting
4 * Copyright (C) 1999,2000 Frank Giannandrea
5 * Copyright (C) 2000-2019 The Xastir Group
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Look at the README for more information on the program.
22 */
23
24
25 //
26 // The code currently supports these types of locally-connected or
27 // network-connected weather stations:
28 //
29 // Peet Brothers Ultimeter 2000 (Set to Data logging mode)
30 // Peet Brothers Ultimeter 2000 (Set to Packet mode)
31 // Peet Brothers Ultimeter 2000 (Set to Complete Record Mode)
32 // Peet Brothers Ultimeter-II
33 // Qualimetrics Q-Net?
34 // Radio Shack WX-200/Huger WM-918/Oregon Scientific WM-918
35 // Dallas One-Wire Weather Station (via OWW network daemon)
36 // Davis Weather Monitor II/Wizard III/Vantage Pro (via meteo/db2APRS link)
37 //
38
39
40 // Need to modify code to use WX_rain_gauge_type. Peet brothers.
41 // See http://www.peetbros.com, FAQ's and owner's manuals for
42 // details:
43 //
44 // Peet Bros Ultimeter II: 0.1"/0.5mm or 0.01"/0.25mm
45 // Divide by 10 or 100 from the serial output.
46 //
47 // Peet Bros Ultimeter 2000, 800, & 100: 0.01"/0.25mm or 0.1mm
48 // If 0.01" gauge, divide by 100. If 0.1mm gauge, convert to
49 // proper English units.
50
51
52
53 #ifdef HAVE_CONFIG_H
54 #include "config.h"
55 #endif // HAVE_CONFIG_H
56
57 #include "snprintf.h"
58
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <math.h>
62
63 #if TIME_WITH_SYS_TIME
64 #include <sys/time.h>
65 #include <time.h>
66 #else // TIME_WITH_SYS_TIME
67 #if HAVE_SYS_TIME_H
68 #include <sys/time.h>
69 #else // HAVE_SYS_TIME_H
70 #include <time.h>
71 #endif // HAVE_SYS_TIME_H
72 #endif // TIME_WITH_SYS_TIME
73
74 #include <string.h>
75
76 #include <Xm/XmAll.h>
77
78 #include "wx.h"
79 #include "main.h"
80 #include "xastir.h"
81 #include "interface.h"
82 #include "lang.h"
83 #include "util.h"
84
85 // Must be last include file
86 #include "leak_detection.h"
87
88
89
90 #define MAX_RAW_WX_STRING 800
91
92 char wx_station_type[100];
93 char raw_wx_string[MAX_RAW_WX_STRING+1];
94
95 #define MAX_WX_STRING 300
96 #define WX_TYPE 'X'
97
98 /* rain totals */
99 float rain_minute[60]; // Total rain for each min. of last hour, hundredths of an inch
100 float rain_minute_total = 0.0; // Total for last hour, hundredths of an inch
101 int rain_minute_last_write = -1; // Write pointer for rain_minute[] array, set to an invalid number
102 float rain_00 = 0.0; // hundredths of an inch
103 float rain_24 = 0.0; // hundredths of an inch
104 float rain_base[24]; // hundredths of an inch
105 int rain_check = 0; // Flag for re-checking rain_total each hour
106
107 float wind_chill = 0; // holder for wind chill calc
108
109 // Gust totals
110 float gust[60]; // High wind gust for each min. of last hour
111 int gust_write_ptr = 0;
112 int gust_read_ptr = 0;
113 int gust_last_write = 0;
114
115
116 /* Other WX station data */
117 char wx_dew_point[10];
118 char wx_dew_point_on;
119 char wx_high_wind[10];
120 char wx_high_wind_on;
121 char wx_wind_chill[10];
122 char wx_wind_chill_on;
123 char wx_three_hour_baro[10]; // hPa
124 char wx_three_hour_baro_on;
125 char wx_hi_temp[10];
126 char wx_hi_temp_on;
127 char wx_low_temp[10];
128 char wx_low_temp_on;
129 char wx_heat_index[10];
130 char wx_heat_index_on;
131
132
133
134
135
136 /***********************************************************/
137 /* clear rain data */
138 /***********************************************************/
clear_rain_data(void)139 void clear_rain_data(void)
140 {
141 int x;
142
143
144 // Clear rain_base queue (starting rain total for each hour)
145 for ( x = 0; x < 24; x++ )
146 {
147 rain_base[x] = 0.0;
148 }
149
150 rain_00 = 0.0;
151 rain_24 = 0.0;
152 rain_check = 0; // Set flag so we'll recheck rain_total
153 // a few times at start of each hour
154
155 // Clear hourly rain queue
156 for ( x = 0; x < 60; x++ )
157 {
158 rain_minute[x] = 0.0;
159 }
160
161 rain_minute_total = 0.0;
162 rain_minute_last_write = 70; // Invalid number so we'll know we're just starting.
163 }
164
165
166
167
168
169 /**************************************************************/
170 /* compute_rain_hour - rolling average for the last 59.x */
171 /* minutes of rain. I/O numbers are in hundredths of an inch.*/
172 /* Output is in "rain_minute_total", a global variable. */
173 /**************************************************************/
compute_rain_hour(float rain_total)174 void compute_rain_hour(float rain_total)
175 {
176 int current, j;
177 float lowest;
178
179
180 // Deposit the _starting_ rain_total for each minute into a separate bucket.
181 // Subtract lowest rain_total in queue from current rain_total to get total
182 // for the last hour.
183
184
185 current = get_minutes(); // Fetch the current minute value. Use this as an index
186 // into our minute "buckets" where we store the data.
187
188
189 rain_minute[ (current + 1) % 60 ] = 0.0; // Zero out the next bucket (probably have data in
190 // there from the previous hour).
191
192
193 if (rain_minute[current] == 0.0) // If no rain_total stored yet in this minute's bucket
194 {
195 rain_minute[current] = rain_total; // Write into current bucket
196 }
197
198
199 // Find the lowest non-zero value for rain_total. The max value is "rain_total".
200 lowest = rain_total; // Set to maximum to get things going
201 for (j = 0; j < 60; j++)
202 {
203 if ( (rain_minute[j] > 0.0) && (rain_minute[j] < lowest) ) // Found a lower non-zero value?
204 {
205 lowest = rain_minute[j]; // Snag it
206 }
207 }
208 // Found it, subtract the two to get total for the last hour
209 rain_minute_total = rain_total - lowest;
210
211 if (debug_level & 2)
212 {
213 fprintf(stderr,"Rain_total:%0.2f Hourly:%0.2f (Low:%0.2f) ", rain_total, rain_minute_total, lowest);
214 }
215 }
216
217
218
219
220
221 /***********************************************************/
222 /* compute_rain - compute rain totals from the total rain */
223 /* so far. rain_total (in hundredths of an inch) keeps on */
224 /* incrementing. */
225 /***********************************************************/
compute_rain(float rain_total)226 void compute_rain(float rain_total)
227 {
228 int current, i;
229 float lower;
230
231
232 // Skip the routine if input is outlandish (Negative value, zero, or 512 inches!).
233 // We seem to get occasional 0.0 packets from wx200d. This makes them go away.
234 if ( (rain_total <= 0.0) || (rain_total > 51200.0) )
235 {
236 return;
237 }
238
239 compute_rain_hour(rain_total);
240
241 current = get_hours();
242
243 // Set rain_base: The first rain_total for each hour.
244 if (rain_base[current] == 0.0) // If we don't have a start value yet for this hour,
245 {
246 rain_base[current] = rain_total; // save it away.
247 rain_check = 0; // Set up rain_check so we'll do the following
248 // "else" clause a few times at start of each hour.
249 }
250 else // rain_base has been set, is it wrong? We recheck three times at start of hour.
251 {
252 if (rain_check < 3)
253 {
254 rain_check++;
255 // Is rain less than base? It shouldn't be.
256 if (rain_total < rain_base[current])
257 {
258 rain_base[current] = rain_total;
259 }
260
261 // Difference greater than 10 inches since last reading? It shouldn't be.
262 if (fabs(rain_total - rain_base[current]) > 1000.0) // Remember: Hundredths of an inch
263 {
264 rain_base[current] = rain_total;
265 }
266 }
267 }
268 rain_base[ (current + 1) % 24 ] = 0.0; // Clear next hour's index.
269
270
271 // Compute total rain in last 24 hours: Really we'll compute the total rain
272 // in the last 23 hours plus the portion of an hour we've completed (Sum up
273 // all 24 of the hour totals). This isn't the perfect way to do it, but to
274 // really do it right we'd need finer increments of time (to get closer to
275 // the goal of 24 hours of rain).
276 lower = rain_total;
277 for ( i = 0; i < 24; i++ ) // Find the lowest non-zero rain_base value in last 24 hours
278 {
279 if ( (rain_base[i] > 0.0) && (rain_base[i] < lower) )
280 {
281 lower = rain_base[i];
282 }
283 }
284 rain_24 = rain_total - lower; // Hundredths of an inch
285
286
287 // Compute rain since midnight. Note that this uses whatever local time was set
288 // on the machine. It might not be local midnight if your box is set to GMT.
289 lower = rain_total;
290 for ( i = 0; i <= current; i++ ) // Find the lowest non-zero rain_base value since midnight
291 {
292 if ( (rain_base[i] > 0.0) && (rain_base[i] < lower) )
293 {
294 lower = rain_base[i];
295 }
296 }
297 rain_00 = rain_total - lower; // Hundredths of an inch
298
299 // It is the responsibility of the calling program to save
300 // the new totals in the data structure for our station.
301 // We don't return anything except in global variables.
302
303
304 if (debug_level & 2)
305 {
306 fprintf(stderr,"24hrs:%0.2f ", rain_24);
307 fprintf(stderr,"rain_00:%0.2f\n", rain_00);
308 }
309 }
310
311
312
313
314
315 /**************************************************************/
316 /* compute_gust - compute max wind gust during last 5 minutes */
317 /* */
318 /**************************************************************/
compute_gust(float wx_speed,float UNUSED (last_speed),time_t * last_speed_time)319 float compute_gust(float wx_speed, float UNUSED(last_speed), time_t *last_speed_time)
320 {
321 float computed_gust;
322 int current, j;
323
324
325 // Deposit max gust for each minute into a different bucket.
326 // Check all buckets for max gust within the last five minutes
327 // (Really 4 minutes plus whatever portion of a minute we've completed).
328
329 current = get_minutes(); // Fetch the current minute value. We use this as an index
330 // into our minute "buckets" where we store the data.
331
332 // If we haven't started collecting yet, set up to do so
333 if (gust_read_ptr == gust_write_ptr) // We haven't started yet
334 {
335 gust_write_ptr = current; // Set to write into current bucket
336 gust_last_write = current;
337
338 gust_read_ptr = current - 1; // Set read pointer back one, modulus 60
339 if (gust_read_ptr < 0)
340 {
341 gust_read_ptr = 59;
342 }
343
344 gust[gust_write_ptr] = 0.0; // Zero the max gust
345 gust[gust_read_ptr] = 0.0; // for both buckets.
346
347 //WE7U: Debug
348 //gust[gust_write_ptr] = 45.9;
349 }
350
351 // Check whether we've advanced at least one minute yet
352 if (current != gust_write_ptr) // We've advanced to a different minute
353 {
354 gust_write_ptr = current; // Start writing into a new bucket.
355 gust[gust_write_ptr] = 0.0; // Zero the new bucket
356
357 // Check how many bins of real data we have currently. Note that this '5' is
358 // correct, as we just advanced "current" to the next minute. We're just pulling
359 // along the read_ptr behind us if we have 5 bins worth of data by now.
360 if ( ((gust_read_ptr + 5) % 60) == gust_write_ptr) // We have 5 bins of real data
361 {
362 gust_read_ptr = (gust_read_ptr + 1) % 60; // So advance the read pointer,
363 }
364
365 // Check for really bad pointers, perhaps the weather station got
366 // unplugged for a while or it's just REALLY slow at sending us data?
367 // We're checking to see if gust_last_write happened in the previous
368 // minute. If not, we skipped a minute or more somewhere.
369 if ( ((gust_last_write + 1) % 60) != current )
370 {
371 // We lost some time somewhere: Reset the pointers, older gust data is
372 // lost. Start over collecting new gust data.
373
374 gust_read_ptr = current - 1; // Set read pointer back one, modulus 60
375 if (gust_read_ptr < 0)
376 {
377 gust_read_ptr = 59;
378 }
379
380 gust[gust_read_ptr] = 0.0;
381 }
382 gust_last_write = current;
383 }
384
385 // Is current wind speed higher than the current minute bucket?
386 if (wx_speed > gust[gust_write_ptr])
387 {
388 gust[gust_write_ptr] = wx_speed; // Save it in the bucket
389 }
390
391 // Read the last (up to) five buckets and find the max gust
392 computed_gust=gust[gust_write_ptr];
393 j = gust_read_ptr;
394 while (j != ((gust_write_ptr + 1) % 60) )
395 {
396 if ( computed_gust < gust[j] )
397 {
398 computed_gust = gust[j];
399 }
400 j = (j + 1) % 60;
401 }
402
403 if (debug_level & 2)
404 {
405 j = gust_read_ptr;
406 while (j != ((gust_write_ptr + 1) % 60) )
407 {
408 fprintf(stderr,"%0.2f ", gust[j]);
409 j = (j + 1) % 60;
410 }
411 fprintf(stderr,"gust:%0.2f\n", computed_gust);
412 }
413
414 *last_speed_time = sec_now();
415 return(computed_gust);
416 }
417
418
419
420
421
422 //
423 // cycle_weather - keep the weather queues moving even if data from
424 // weather station is scarce. This is called from main.c:UpdateTime()
425 // on a periodic basis. This routine also does the 30 second timestamp
426 // for the log files.
427 //
cycle_weather(void)428 void cycle_weather(void)
429 {
430 DataRow *p_station;
431 WeatherRow *weather;
432 float last_speed, computed_gust;
433 time_t last_speed_time;
434
435
436 // Find my own local weather data
437 if (search_station_name(&p_station,my_callsign,1))
438 {
439 if (p_station->weather_data != NULL) // If station has WX data
440 {
441 weather = p_station->weather_data;
442 // Cycle the rain queues, feed in the last rain total we had
443 (void)compute_rain((float)atof(weather->wx_rain_total));
444
445
446 // Note: Some weather stations provide the per-hour, 24-hour,
447 // and since-midnight rain rates already. Further, some stations
448 // don't even provide total rain (Davis APRS DataLogger and the
449 // db2APRS program), so anything we compute here is actually wrong.
450 // Do NOT clobber these if so. This flag is set in fill_wx_data
451 // when the station provides its data.
452
453 if (weather->wx_compute_rain_rates)
454 {
455 // Hourly rain total
456 xastir_snprintf(weather->wx_rain,
457 sizeof(weather->wx_rain),
458 "%0.2f",
459 rain_minute_total);
460
461 // Last 24 hour rain
462 xastir_snprintf(weather->wx_prec_24,
463 sizeof(weather->wx_prec_24),
464 "%0.2f",
465 rain_24);
466
467 // Rain since midnight
468 xastir_snprintf(weather->wx_prec_00,
469 sizeof(weather->wx_prec_00),
470 "%0.2f",
471 rain_00);
472 }
473 else
474 {
475 // LaCrosse stations don't provide the since-midnight
476 // numbers and so this will be blank.
477 // So if we have blanks here, fill it in.
478 if ( weather->wx_prec_00[0] == '\0' &&
479 weather->wx_rain_total[0] != '\0')
480 {
481
482 // Rain since midnight
483 xastir_snprintf(weather->wx_prec_00,
484 sizeof(weather->wx_prec_00),
485 "%0.2f",
486 rain_00);
487 }
488 }
489
490
491 /* get last gust speed */
492 if (strlen(weather->wx_gust) > 0)
493 {
494 /* get last speed */
495 last_speed = (float)atof(weather->wx_gust);
496 last_speed_time = weather->wx_speed_sec_time;
497 }
498 else
499 {
500 last_speed = 0.0;
501 }
502
503 /* wind speed */
504 computed_gust = compute_gust((float)atof(weather->wx_speed),
505 last_speed,
506 &last_speed_time);
507 weather->wx_speed_sec_time = sec_now();
508 xastir_snprintf(weather->wx_gust,
509 sizeof(weather->wx_gust),
510 "%03d",
511 (int)(computed_gust + 0.5)); // Cheater's way of rounding
512 }
513 }
514 }
515
516
517
518
519
520 /***********************************************************/
521 /* clear other wx data */
522 /***********************************************************/
clear_local_wx_data(void)523 void clear_local_wx_data(void)
524 {
525 memset(wx_dew_point,0,sizeof(wx_dew_point));
526 wx_dew_point_on = 0;
527
528 memset(wx_high_wind,0,sizeof(wx_high_wind));
529 wx_high_wind_on = 0;
530
531 memset(wx_wind_chill,0,sizeof(wx_wind_chill));
532 wx_wind_chill_on = 0;
533
534 memset(wx_three_hour_baro,0,sizeof(wx_three_hour_baro));
535 wx_three_hour_baro_on = 0;
536
537 memset(wx_hi_temp,0,sizeof(wx_hi_temp));
538 wx_hi_temp_on = 0;
539
540 memset(wx_low_temp,0,sizeof(wx_low_temp));
541 wx_low_temp_on = 0;
542
543 memset(wx_heat_index,0,sizeof(wx_heat_index));
544 wx_heat_index_on = 0;
545 }
546
547
548
549
550
551 /***************************************************/
552 /* Check last WX data received - clear data if old */
553 /***************************************************/
wx_last_data_check(void)554 void wx_last_data_check(void)
555 {
556 DataRow *p_station;
557
558 p_station = NULL;
559 if (search_station_name(&p_station,my_callsign,1))
560 {
561 if (p_station->weather_data != NULL)
562 if (p_station->weather_data->wx_speed_sec_time+360 < sec_now())
563 if (p_station->weather_data->wx_gust[0] != 0)
564 xastir_snprintf(p_station->weather_data->wx_gust,
565 sizeof(p_station->weather_data->wx_gust),
566 "%03d",
567 0);
568 }
569 }
570
571
572
573
574
575 //*********************************************************************
576 // Decode Peet Brothers Ultimeter 2000 weather data (Data logging mode)
577 //
578 // This function is called from db.c:data_add() only. Used for
579 // decoding incoming packets, not for our own weather station data.
580 //
581 // The Ultimeter 2000 can be in any of three modes, Data Logging Mode,
582 // Packet Mode, or Complete Record Mode. This routine handles only
583 // the Data Logging Mode.
584 //*********************************************************************
decode_U2000_L(int from,unsigned char * data,WeatherRow * weather)585 void decode_U2000_L(int from, unsigned char *data, WeatherRow *weather)
586 {
587 time_t last_speed_time;
588 float last_speed;
589 float computed_gust;
590 char temp_data1[10];
591 char *temp_conv;
592
593 last_speed = 0.0;
594 last_speed_time = 0;
595 computed_gust = 0.0;
596
597 if (debug_level & 1)
598 {
599 fprintf(stderr,"APRS WX3 Peet Bros U-2k (data logging mode): |%s|\n", data);
600 }
601
602 weather->wx_type = WX_TYPE;
603 xastir_snprintf(weather->wx_station,
604 sizeof(weather->wx_station),
605 "U2k");
606
607 /* get last gust speed */
608 if (strlen(weather->wx_gust) > 0 && !from)
609 {
610 /* get last speed */
611 last_speed = (float)atof(weather->wx_gust);
612 last_speed_time = weather->wx_speed_sec_time;
613 }
614
615 // 006B 00 58
616 // 00A4 00 46 01FF 380E 2755 02C1 03E8 ---- 0052 04D7 0001 007BM
617 // ^ ^ ^ ^ ^ ^ ^
618 // 0 6 8 12 16 24 40
619 /* wind speed */
620 if (data[0] != '-') // '-' signifies invalid data
621 {
622 substr(temp_data1,(char *)data,4);
623 xastir_snprintf(weather->wx_speed,
624 sizeof(weather->wx_speed),
625 "%03.0f",
626 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
627 if (from)
628 {
629 weather->wx_speed_sec_time = sec_now();
630 }
631 else
632 {
633 /* local station */
634 computed_gust = compute_gust((float)atof(weather->wx_speed),
635 last_speed,
636 &last_speed_time);
637 weather->wx_speed_sec_time = sec_now();
638 xastir_snprintf(weather->wx_gust,
639 sizeof(weather->wx_gust),
640 "%03d",
641 (int)(0.5 + computed_gust)); // Cheater's way of rounding
642 }
643 }
644 else
645 {
646 if (!from)
647 {
648 weather->wx_speed[0] = 0;
649 }
650 }
651
652 /* wind direction */
653 //
654 // Note that the first two digits here may be 00, or may be FF
655 // if a direction calibration has been entered. We should zero
656 // them.
657 //
658 if (data[4] != '-') // '-' signifies invalid data
659 {
660 substr(temp_data1,(char *)(data+4),4);
661 temp_data1[0] = '0';
662 temp_data1[1] = '0';
663 xastir_snprintf(weather->wx_course,
664 sizeof(weather->wx_course),
665 "%03.0f",
666 ((strtol(temp_data1,&temp_conv,16)/256.0)*360.0));
667 }
668 else
669 {
670 xastir_snprintf(weather->wx_course,
671 sizeof(weather->wx_course),
672 "000");
673 if (!from)
674 {
675 weather->wx_course[0]=0;
676 }
677 }
678
679 /* outdoor temp */
680 if (data[8] != '-') // '-' signifies invalid data
681 {
682 int temp4;
683
684 substr(temp_data1,(char *)(data+8),4);
685 temp4 = (int)strtol(temp_data1,&temp_conv,16);
686
687 if (temp_data1[0] > '7') // Negative value, convert
688 {
689 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
690 }
691
692 xastir_snprintf(weather->wx_temp,
693 sizeof(weather->wx_temp),
694 "%03d",
695 (int)((float)((temp4<<16)/65536)/10.0));
696
697 }
698 else
699 {
700 if (!from)
701 {
702 weather->wx_temp[0]=0;
703 }
704 }
705
706 /* rain total long term */
707 if (data[12] != '-') // '-' signifies invalid data
708 {
709 substr(temp_data1,(char *)(data+12),4);
710 xastir_snprintf(weather->wx_rain_total,
711 sizeof(weather->wx_rain_total),
712 "%0.2f",
713 strtol(temp_data1,&temp_conv,16)/100.0);
714 if (!from)
715 {
716 /* local station */
717 compute_rain((float)atof(weather->wx_rain_total));
718 /*last hour rain */
719 xastir_snprintf(weather->wx_rain,
720 sizeof(weather->wx_rain),
721 "%0.2f",
722 rain_minute_total);
723 /*last 24 hour rain */
724 xastir_snprintf(weather->wx_prec_24,
725 sizeof(weather->wx_prec_24),
726 "%0.2f",
727 rain_24);
728 /* rain since midnight */
729 xastir_snprintf(weather->wx_prec_00,
730 sizeof(weather->wx_prec_00),
731 "%0.2f",
732 rain_00);
733 }
734 }
735 else
736 {
737 if (!from)
738 {
739 weather->wx_rain_total[0]=0;
740 }
741 }
742
743 /* baro */
744 if (data[16] != '-') // '-' signifies invalid data
745 {
746 substr(temp_data1,(char *)(data+16),4);
747 xastir_snprintf(weather->wx_baro,
748 sizeof(weather->wx_baro),
749 "%0.1f",
750 strtol(temp_data1,&temp_conv,16)/10.0);
751 }
752 else
753 {
754 if (!from)
755 {
756 weather->wx_baro[0]=0;
757 }
758 }
759
760
761 /* outdoor humidity */
762 if (data[24] != '-') // '-' signifies invalid data
763 {
764 substr(temp_data1,(char *)(data+24),4);
765 xastir_snprintf(weather->wx_hum,
766 sizeof(weather->wx_hum),
767 "%03.0f",
768 (strtol(temp_data1,&temp_conv,16)/10.0));
769 }
770 else
771 {
772 if (!from)
773 {
774 weather->wx_hum[0]=0;
775 }
776 }
777
778 /* todays rain total */
779 if (data[40] != '-') // '-' signifies invalid data
780 {
781 if (from)
782 {
783 substr(temp_data1,(char *)(data+40),4);
784 xastir_snprintf(weather->wx_prec_00,
785 sizeof(weather->wx_prec_00),
786 "%0.2f",
787 strtol(temp_data1,&temp_conv,16)/100.0);
788 }
789 }
790 else
791 {
792 if (!from)
793 {
794 weather->wx_prec_00[0] = 0;
795 }
796 }
797 }
798
799
800
801
802
803 //********************************************************************
804 // Decode Peet Brothers Ultimeter 2000 weather data (Packet mode)
805 //
806 // This function is called from db.c:data_add() only. Used for
807 // decoding incoming packets, not for our own weather station data.
808 //
809 // The Ultimeter 2000 can be in any of three modes, Data Logging Mode,
810 // Packet Mode, or Complete Record Mode. This routine handles only
811 // the Packet Mode.
812 //********************************************************************
decode_U2000_P(int from,unsigned char * data,WeatherRow * weather)813 void decode_U2000_P(int from, unsigned char *data, WeatherRow *weather)
814 {
815 time_t last_speed_time;
816 float last_speed;
817 float computed_gust;
818 char temp_data1[10];
819 char *temp_conv;
820 int len;
821
822 last_speed = 0.0;
823 last_speed_time = 0;
824 computed_gust = 0.0;
825 len = (int)strlen((char *)data);
826
827 if (debug_level & 1)
828 {
829 fprintf(stderr,"APRS WX5 Peet Bros U-2k Packet (Packet mode): |%s|\n",data);
830 }
831
832 weather->wx_type = WX_TYPE;
833 xastir_snprintf(weather->wx_station,
834 sizeof(weather->wx_station),
835 "U2k");
836
837 /* get last gust speed */
838 if (strlen(weather->wx_gust) > 0 && !from)
839 {
840 /* get last speed */
841 last_speed = (float)atof(weather->wx_gust);
842 last_speed_time = weather->wx_speed_sec_time;
843 }
844
845 // $ULTW 0031 00 37 02CE 0069 ---- 0000 86A0 0001 ---- 011901CC 0000 0005
846 // ^ ^ ^ ^ ^ ^ ^ ^
847 // 0 6 8 12 16 32 44 48
848
849 /* wind speed peak over last 5 min */
850 if (data[0] != '-') // '-' signifies invalid data
851 {
852 substr(temp_data1,(char *)data,4);
853 if (from)
854 {
855 xastir_snprintf(weather->wx_gust,
856 sizeof(weather->wx_gust),
857 "%03.0f",
858 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
859 /* this may be the only wind data */
860 xastir_snprintf(weather->wx_speed,
861 sizeof(weather->wx_speed),
862 "%03.0f",
863 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
864 }
865 else
866 {
867 /* local station and may be the only wind data */
868 if (len < 51)
869 {
870 xastir_snprintf(weather->wx_speed,
871 sizeof(weather->wx_speed),
872 "%03.0f",
873 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
874 computed_gust = compute_gust((float)atof(weather->wx_speed),
875 last_speed,
876 &last_speed_time);
877 weather->wx_speed_sec_time = sec_now();
878 xastir_snprintf(weather->wx_gust,
879 sizeof(weather->wx_gust),
880 "%03d",
881 (int)(0.5 + computed_gust));
882 }
883 }
884 }
885 else
886 {
887 if (!from)
888 {
889 weather->wx_gust[0] = 0;
890 }
891 }
892
893 /* wind direction */
894 //
895 // Note that the first two digits here may be 00, or may be FF
896 // if a direction calibration has been entered. We should zero
897 // them.
898 //
899 if (data[4] != '-') // '-' signifies invalid data
900 {
901 substr(temp_data1,(char *)(data+4),4);
902 temp_data1[0] = '0';
903 temp_data1[1] = '0';
904 xastir_snprintf(weather->wx_course,
905 sizeof(weather->wx_course),
906 "%03.0f",
907 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
908 }
909 else
910 {
911 xastir_snprintf(weather->wx_course,
912 sizeof(weather->wx_course),
913 "000");
914 if (!from)
915 {
916 weather->wx_course[0] = 0;
917 }
918 }
919
920 /* outdoor temp */
921 if (data[8] != '-') // '-' signifies invalid data
922 {
923 int temp4;
924
925 substr(temp_data1,(char *)(data+8),4);
926 temp4 = (int)strtol(temp_data1,&temp_conv,16);
927
928 if (temp_data1[0] > '7') // Negative value, convert
929 {
930 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
931 }
932
933 xastir_snprintf(weather->wx_temp,
934 sizeof(weather->wx_temp),
935 "%03d",
936 (int)((float)((temp4<<16)/65536)/10.0));
937 }
938 else
939 {
940 if (!from)
941 {
942 weather->wx_temp[0] = 0;
943 }
944 }
945 /* todays rain total (on some units) */
946 if ((data[44]) != '-') // '-' signifies invalid data
947 {
948 if (from)
949 {
950 substr(temp_data1,(char *)(data+44),4);
951 xastir_snprintf(weather->wx_prec_00,
952 sizeof(weather->wx_prec_00),
953 "%0.2f",
954 strtol(temp_data1,&temp_conv,16)/100.0);
955 }
956 }
957 else
958 {
959 if (!from)
960 {
961 weather->wx_prec_00[0] = 0;
962 }
963 }
964
965 /* rain total long term */
966 if (data[12] != '-') // '-' signifies invalid data
967 {
968 substr(temp_data1,(char *)(data+12),4);
969 xastir_snprintf(weather->wx_rain_total,
970 sizeof(weather->wx_rain_total),
971 "%0.2f",
972 strtol(temp_data1,&temp_conv,16)/100.0);
973 if (!from)
974 {
975 /* local station */
976 compute_rain((float)atof(weather->wx_rain_total));
977 /*last hour rain */
978 xastir_snprintf(weather->wx_rain,
979 sizeof(weather->wx_rain),
980 "%0.2f",
981 rain_minute_total);
982 /*last 24 hour rain */
983 xastir_snprintf(weather->wx_prec_24,
984 sizeof(weather->wx_prec_24),
985 "%0.2f",
986 rain_24);
987 /* rain since midnight */
988 xastir_snprintf(weather->wx_prec_00,
989 sizeof(weather->wx_prec_00),
990 "%0.2f",
991 rain_00);
992 }
993 }
994 else
995 {
996 if (!from)
997 {
998 weather->wx_rain_total[0] = 0;
999 }
1000 }
1001
1002 /* baro */
1003 if (data[16] != '-') // '-' signifies invalid data
1004 {
1005 substr(temp_data1,(char *)(data+16),4);
1006 xastir_snprintf(weather->wx_baro,
1007 sizeof(weather->wx_baro),
1008 "%0.1f",
1009 strtol(temp_data1,&temp_conv,16)/10.0);
1010 }
1011 else
1012 {
1013 if (!from)
1014 {
1015 weather->wx_baro[0] = 0;
1016 }
1017 }
1018
1019 /* outdoor humidity */
1020 if (data[32] != '-') // '-' signifies invalid data
1021 {
1022 substr(temp_data1,(char *)(data+32),4);
1023 xastir_snprintf(weather->wx_hum,
1024 sizeof(weather->wx_hum),
1025 "%03.0f",
1026 strtol(temp_data1,&temp_conv,16)/10.0);
1027 }
1028 else
1029 {
1030 if (!from)
1031 {
1032 weather->wx_hum[0] = 0;
1033 }
1034 }
1035
1036 /* 1 min wind speed avg */
1037 if (len > 48 && (data[48]) != '-') // '-' signifies invalid data
1038 {
1039 substr(temp_data1,(char *)(data+48),4);
1040 xastir_snprintf(weather->wx_speed,
1041 sizeof(weather->wx_speed),
1042 "%03.0f",
1043 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
1044 if (from)
1045 {
1046 weather->wx_speed_sec_time = sec_now();
1047 }
1048 else
1049 {
1050 /* local station */
1051 computed_gust = compute_gust((float)atof(weather->wx_speed),
1052 last_speed,
1053 &last_speed_time);
1054 weather->wx_speed_sec_time = sec_now();
1055 xastir_snprintf(weather->wx_gust,
1056 sizeof(weather->wx_gust),
1057 "%03d",
1058 (int)(0.5 + computed_gust));
1059 }
1060 }
1061 else
1062 {
1063 if (!from)
1064 {
1065 if (len > 48)
1066 {
1067 weather->wx_speed[0] = 0;
1068 }
1069 }
1070 }
1071 }
1072
1073
1074
1075
1076
1077 //*****************************************************************
1078 // Decode Peet Brothers Ultimeter-II weather data
1079 //
1080 // This function is called from db.c:data_add() only. Used for
1081 // decoding incoming packets, not for our own weather station data.
1082 //*****************************************************************
decode_Peet_Bros(int from,unsigned char * data,WeatherRow * weather,int type)1083 void decode_Peet_Bros(int from, unsigned char *data, WeatherRow *weather, int type)
1084 {
1085 time_t last_speed_time;
1086 float last_speed;
1087 float computed_gust;
1088 char temp_data1[10];
1089 char *temp_conv;
1090
1091 last_speed = 0.0;
1092 computed_gust = 0.0;
1093 last_speed_time = 0;
1094
1095 if (debug_level & 1)
1096 {
1097 fprintf(stderr,"APRS WX4 Peet Bros U-II: |%s|\n",data);
1098 }
1099
1100 weather->wx_type = WX_TYPE;
1101 xastir_snprintf(weather->wx_station,
1102 sizeof(weather->wx_station),
1103 "UII");
1104
1105 // '*' = MPH
1106 // '#' = km/h
1107 //
1108 // # 5 0B 75 0082 0082
1109 // * 7 00 76 0000 0000
1110 // ^ ^ ^ ^
1111 // rain [1/100 inch ?]
1112 // outdoor temp
1113 // wind speed [mph / km/h]
1114 // wind dir
1115
1116 /* wind direction */
1117 //
1118 // 0x00 is N
1119 // 0x04 is E
1120 // 0x08 is S
1121 // 0x0C is W
1122 //
1123 substr(temp_data1,(char *)data,1);
1124 xastir_snprintf(weather->wx_course,
1125 sizeof(weather->wx_course),
1126 "%03.0f",
1127 (strtol(temp_data1,&temp_conv,16)/16.0)*360.0);
1128
1129 /* get last gust speed */
1130 if (strlen(weather->wx_gust) > 0 && !from)
1131 {
1132 /* get last speed */
1133 last_speed = (float)atof(weather->wx_gust);
1134 last_speed_time = weather->wx_speed_sec_time;
1135 }
1136
1137 /* wind speed */
1138 substr(temp_data1,(char *)(data+1),2);
1139 if (type == APRS_WX4) // '#' speed in km/h, convert to mph
1140 {
1141 xastir_snprintf(weather->wx_speed,
1142 sizeof(weather->wx_speed),
1143 "%03d",
1144 (int)(0.5 + (float)(strtol(temp_data1,&temp_conv,16)*0.62137)));
1145 }
1146 else // type == APRS_WX6, '*' speed in mph
1147 {
1148 xastir_snprintf(weather->wx_speed,
1149 sizeof(weather->wx_speed),
1150 "%03.0f",
1151 0.5 + (1.0 * strtol(temp_data1,&temp_conv,16)) );
1152 }
1153
1154 if (from)
1155 {
1156 weather->wx_speed_sec_time = sec_now();
1157 }
1158 else
1159 {
1160 /* local station */
1161 computed_gust = compute_gust((float)atof(weather->wx_speed),
1162 last_speed,
1163 &last_speed_time);
1164 weather->wx_speed_sec_time = sec_now();
1165 xastir_snprintf(weather->wx_gust,
1166 sizeof(weather->wx_gust),
1167 "%03d",
1168 (int)(0.5 + computed_gust));
1169 }
1170
1171 /* outdoor temp */
1172 if (data[3] != '-') // '-' signifies invalid data
1173 {
1174 int temp4;
1175
1176 substr(temp_data1,(char *)(data+3),2);
1177 temp4 = (int)strtol(temp_data1,&temp_conv,16);
1178
1179 if (temp_data1[0] > '7') // Negative value, convert
1180 {
1181 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
1182 }
1183
1184 xastir_snprintf(weather->wx_temp,
1185 sizeof(weather->wx_temp),
1186 "%03.0f",
1187 (float)(temp4-56) );
1188 }
1189 else
1190 {
1191 if (!from)
1192 {
1193 weather->wx_temp[0] = 0;
1194 }
1195 }
1196
1197 // Rain divided by 100 for readings in hundredth of an inch
1198 if (data[5] != '-') // '-' signifies invalid data
1199 {
1200 substr(temp_data1,(char *)(data+5),4);
1201 xastir_snprintf(weather->wx_rain_total,
1202 sizeof(weather->wx_rain_total),
1203 "%0.2f",
1204 strtol(temp_data1,&temp_conv,16)/100.0);
1205 if (!from)
1206 {
1207 /* local station */
1208 compute_rain((float)atof(weather->wx_rain_total));
1209 /*last hour rain */
1210 xastir_snprintf(weather->wx_rain,
1211 sizeof(weather->wx_rain),
1212 "%0.2f",
1213 rain_minute_total);
1214 /*last 24 hour rain */
1215 xastir_snprintf(weather->wx_prec_24,
1216 sizeof(weather->wx_prec_24),
1217 "%0.2f",
1218 rain_24);
1219 /* rain since midnight */
1220 xastir_snprintf(weather->wx_prec_00,
1221 sizeof(weather->wx_prec_00),
1222 "%0.2f",
1223 rain_00);
1224 }
1225 }
1226 else
1227 {
1228 if (!from)
1229 {
1230 weather->wx_rain_total[0] = 0;
1231 }
1232 }
1233 }
1234
1235
1236
1237
1238
1239 /**************************************************/
1240 /* RSW num convert. For Radio Shack WX-200, */
1241 /* converts two decimal nibbles into integer */
1242 /* number. */
1243 /**************************************************/
rswnc(unsigned char c)1244 int rswnc(unsigned char c)
1245 {
1246 return( (int)( (c>>4) & 0x0f) * 10 + (int)(c&0x0f) );
1247 }
1248
1249
1250
1251
1252
1253 //*********************************************************
1254 // wx fill data field
1255 // from: 0=local station, 1=regular decode (other stations)
1256 // type: type of WX packet
1257 // data: the packet of WX data
1258 // fill: the station data to fill
1259 //
1260 // This function is called only by wx.c:wx_decode()
1261 //
1262 // It is always called with a first parameter of 0, so we
1263 // use this only for our own serially-connected or network
1264 // connected weather station, not for decoding other
1265 // people's weather packets.
1266 //*********************************************************
1267 //
1268 // Note that the length of "data" can be up to MAX_DEVICE_BUFFER,
1269 // which is currently set to 4096.
1270 //
wx_fill_data(int from,int type,unsigned char * data,DataRow * fill)1271 void wx_fill_data(int from, int type, unsigned char *data, DataRow *fill)
1272 {
1273 time_t last_speed_time;
1274 float last_speed;
1275 float computed_gust;
1276 int temp1;
1277 int temp2;
1278 int temp3;
1279 float temp_temp;
1280 char temp[MAX_DEVICE_BUFFER+1];
1281 char temp_data1[10];
1282 char *temp_conv;
1283 int len;
1284 int t2;
1285 int hidx_temp;
1286 int rh2;
1287 int hi_hum;
1288 int heat_index;
1289 WeatherRow *weather;
1290 float tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp9,tmp10,tmp11,tmp12,tmp13,tmp14,tmp15,tmp16,tmp17,tmp18,tmp19;
1291 int tmp7,tmp8;
1292 int dallas_type = 19;
1293
1294
1295 last_speed=0.0;
1296 computed_gust=0.0;
1297 last_speed_time=0;
1298
1299
1300 len=(int)strlen((char*)data);
1301
1302 weather = fill->weather_data; // should always be defined
1303
1304 switch (type)
1305 {
1306
1307 //WE7U
1308 /////////////////////////////////////
1309 // Dallas One-Wire Weather Station //
1310 /////////////////////////////////////
1311
1312 // KB1MTS - Added values for T13 thru T19 for humidity and barometer,
1313 // however only current values (not min or max) are used.
1314
1315
1316 case (DALLAS_ONE_WIRE):
1317
1318 if (debug_level & 1)
1319 {
1320 fprintf(stderr,"APRS WX Dallas One-Wire %s:<%s>\n",fill->call_sign,data);
1321 }
1322
1323 weather->wx_type=WX_TYPE;
1324 xastir_snprintf(weather->wx_station,
1325 sizeof(weather->wx_station),
1326 "OWW");
1327
1328 if (19 == sscanf((const char *)data,
1329 "%f %f %f %f %f %f %d %d %f %f %f %f %f %f %f %f %f %f %f",
1330 &tmp1,
1331 &tmp2,
1332 &tmp3,
1333 &tmp4,
1334 &tmp5,
1335 &tmp6,
1336 &tmp7,
1337 &tmp8,
1338 &tmp9,
1339 &tmp10,
1340 &tmp11,
1341 &tmp12,
1342 &tmp13,
1343 &tmp14,
1344 &tmp15,
1345 &tmp16,
1346 &tmp17,
1347 &tmp18,
1348 &tmp19))
1349 {
1350 dallas_type = 19;
1351 }
1352 else if (12 == sscanf((const char *)data,
1353 "%f %f %f %f %f %f %d %d %f %f %f %f",
1354 &tmp1,
1355 &tmp2,
1356 &tmp3,
1357 &tmp4,
1358 &tmp5,
1359 &tmp6,
1360 &tmp7,
1361 &tmp8,
1362 &tmp9,
1363 &tmp10,
1364 &tmp11,
1365 &tmp12))
1366 {
1367 dallas_type = 12;
1368 }
1369 else
1370 {
1371 fprintf(stderr,"wx_fill_data:sscanf parsing error\n");
1372 }
1373
1374
1375 // The format of the data originates here:
1376 // http://weather.henriksens.net/
1377
1378 // tmp1: primary temp (C)
1379 // tmp2: temp max (C)
1380 // tmp3: temp min (C)
1381 // tmp4: anemometer (mps)
1382 // tmp5: anemometer gust (peak speed MS)
1383 // tmp6: anemometer speed max * 0.447040972 (max speed MS)
1384 // tmp7: vane bearing - 1 (current wind direction)
1385 // tmp8: vane mode (max dir)
1386 // tmp9: rain rate
1387 // tmp10: rain total today
1388 // tmp11: rain total week
1389 // tmp12: rain since month
1390 // tmp13: Current Humidity
1391 // tmp14: Max Humidity
1392 // tmp15: Min Humidity
1393 // tmp16: Current Barometer
1394 // tmp17: Max Barometer
1395 // tmp18: Min Barometer
1396 // tmp19: Barometer Rate
1397
1398 // Temperature
1399 xastir_snprintf(weather->wx_temp,
1400 sizeof(weather->wx_temp),
1401 "%03d",
1402 (int)(tmp1 * 9.0 / 5.0 + 32.0 + 0.5));
1403 //fprintf(stderr,"Read: %2.1f C, Storing: %s F\n",tmp1,weather->wx_temp);
1404
1405 // Wind direction. Each vane increment equals 22.5 degrees.
1406 xastir_snprintf(weather->wx_course,
1407 sizeof(weather->wx_course),
1408 "%03d",
1409 (int)(tmp7 * 22.5 + 0.5));
1410
1411 // Check for course = 0. Change to 360.
1412 if (strncmp(weather->wx_course,"000",3) == 0)
1413 {
1414 xastir_snprintf(weather->wx_course,
1415 sizeof(weather->wx_course),
1416 "360");
1417 }
1418
1419 // Wind speed. We get it in meters per second, store it
1420 // in mph.
1421 tmp4 = tmp4 * 3600.0 / 1000.0; // kph
1422 tmp4 = tmp4 * 0.62137; // mph
1423 xastir_snprintf(weather->wx_speed,
1424 sizeof(weather->wx_speed),
1425 "%03d",
1426 (int)(tmp4 + 0.5));
1427
1428 if (dallas_type == 19)
1429 {
1430 // Humidity. This is received by percentage.
1431 xastir_snprintf(weather->wx_hum,
1432 sizeof(weather->wx_hum),
1433 "%2.1f", (double)(tmp13));
1434
1435 // Barometer. Sent in inHg
1436 xastir_snprintf(weather->wx_baro,
1437 sizeof(weather->wx_baro),
1438 "%4.4f", (float)(tmp16 * 33.864));
1439 }
1440
1441
1442 // Rain: I don't have a rain gauge, and I couldn't tell from the
1443 // "OWW" docs exactly which of the four rain fields did what. If
1444 // someone can help me with that I'll add rain gauge code for the
1445 // Dallas One-Wire.
1446
1447
1448
1449 break;
1450
1451 ////////////////////////////////
1452 // Peet Brothers Ultimeter-II //
1453 ////////////////////////////////
1454 case (APRS_WX4): // '#', Wind speed in km/h
1455 case (APRS_WX6): // '*', Wind speed in mph
1456
1457 // This one assumes 0.1" rain gauge. Must correct in software if
1458 // any other type is used.
1459
1460 if (debug_level & 1)
1461 {
1462 fprintf(stderr,"APRS WX4 Peet Bros U-II %s:<%s>\n",fill->call_sign,data);
1463 }
1464
1465 weather->wx_type=WX_TYPE;
1466 xastir_snprintf(weather->wx_station,
1467 sizeof(weather->wx_station),
1468 "UII");
1469
1470 /* wind direction */
1471 //
1472 // 0x00 is N
1473 // 0x04 is E
1474 // 0x08 is S
1475 // 0x0C is W
1476 //
1477 substr(temp_data1,(char *)(data+1),1);
1478 xastir_snprintf(weather->wx_course,
1479 sizeof(weather->wx_course),
1480 "%03.0f",
1481 (strtol(temp_data1,&temp_conv,16)/16.0)*360.0);
1482
1483 // Check for course == 0. Change to 360.
1484 if (strncmp(weather->wx_course,"000",3) == 0)
1485 {
1486 xastir_snprintf(weather->wx_course,
1487 sizeof(weather->wx_course),
1488 "360");
1489 }
1490
1491 /* get last gust speed */
1492 if (strlen(weather->wx_gust) > 0 && !from) // From local station
1493 {
1494 /* get last speed */
1495 last_speed=(float)atof(weather->wx_gust);
1496 last_speed_time=weather->wx_speed_sec_time;
1497 }
1498
1499 /* wind speed */
1500 substr(temp_data1,(char *)(data+2),2);
1501 if (type == APRS_WX4) // '#', Data is in km/h, convert to mph
1502 {
1503 xastir_snprintf(weather->wx_speed,
1504 sizeof(weather->wx_speed),
1505 "%03d",
1506 (int)(0.5 + (float)(strtol(temp_data1,&temp_conv,16)*0.62137)));
1507 }
1508 else // APRS_WX6 or '*', Data is in MPH
1509 {
1510 xastir_snprintf(weather->wx_speed,
1511 sizeof(weather->wx_speed),
1512 "%03.0f",
1513 0.5 + (strtol(temp_data1,&temp_conv,16)*1.0) );
1514 }
1515
1516 if (from) // From remote station
1517 {
1518 weather->wx_speed_sec_time = sec_now();
1519 }
1520 else
1521 {
1522 /* local station */
1523 computed_gust = compute_gust((float)atof(weather->wx_speed),
1524 last_speed,
1525 &last_speed_time);
1526 weather->wx_speed_sec_time = sec_now();
1527 xastir_snprintf(weather->wx_gust,
1528 sizeof(weather->wx_gust),
1529 "%03d",
1530 (int)(0.5 + computed_gust));
1531 }
1532
1533 /* outdoor temp */
1534 if (data[4]!='-') // '-' signifies invalid data
1535 {
1536 int temp4;
1537
1538 substr(temp_data1,(char *)(data+4),2);
1539 temp4 = (int)strtol(temp_data1,&temp_conv,16);
1540
1541 if (temp_data1[0] > '7') // Negative value, convert
1542 {
1543 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
1544 }
1545
1546 xastir_snprintf(weather->wx_temp,
1547 sizeof(weather->wx_temp),
1548 "%03.0f",
1549 (float)(temp4-56) );
1550 }
1551 else
1552 {
1553 if (!from) // From local station
1554 {
1555 weather->wx_temp[0]=0;
1556 }
1557 }
1558
1559 /* rain div by 100 for readings 0.1 inch */
1560 // What? Shouldn't this be /10?
1561
1562 if (data[6]!='-') // '-' signifies invalid data
1563 {
1564 substr(temp_data1,(char *)(data+6),4);
1565 if (!from) // From local station
1566 {
1567 switch (WX_rain_gauge_type)
1568 {
1569 case 1: // 0.1" rain gauge
1570 xastir_snprintf(weather->wx_rain_total,
1571 sizeof(weather->wx_rain_total),
1572 "%0.2f",
1573 strtol(temp_data1,&temp_conv,16)*10.0);
1574 break;
1575 case 3: // 0.1mm rain gauge
1576 xastir_snprintf(weather->wx_rain_total,
1577 sizeof(weather->wx_rain_total),
1578 "%0.2f",
1579 strtol(temp_data1,&temp_conv,16)/2.54);
1580 break;
1581 case 2: // 0.01" rain gauge
1582 case 0: // No conversion
1583 default:
1584 xastir_snprintf(weather->wx_rain_total,
1585 sizeof(weather->wx_rain_total),
1586 "%0.2f",
1587 strtol(temp_data1,&temp_conv,16)*1.0);
1588 break;
1589 }
1590 /* local station */
1591 compute_rain((float)atof(weather->wx_rain_total));
1592 weather->wx_compute_rain_rates=1;
1593 /*last hour rain */
1594 xastir_snprintf(weather->wx_rain,
1595 sizeof(weather->wx_rain),
1596 "%0.2f",
1597 rain_minute_total);
1598 /*last 24 hour rain */
1599 xastir_snprintf(weather->wx_prec_24,
1600 sizeof(weather->wx_prec_24),
1601 "%0.2f",
1602 rain_24);
1603 /* rain since midnight */
1604 xastir_snprintf(weather->wx_prec_00,
1605 sizeof(weather->wx_prec_00),
1606 "%0.2f",
1607 rain_00);
1608 }
1609 }
1610 else
1611 {
1612 if (!from) // From local station
1613 {
1614 weather->wx_rain_total[0]=0;
1615 }
1616 }
1617 break;
1618
1619
1620
1621 ///////////////////////////////////////////////////////
1622 // Peet Brothers Ultimeter 2000 in data logging mode //
1623 ///////////////////////////////////////////////////////
1624 case (APRS_WX3):
1625 if (debug_level & 1)
1626 {
1627 fprintf(stderr,"APRS WX3 Peet Bros U-2k (data logging mode) %s:<%s>\n",fill->call_sign,data+2);
1628 }
1629
1630 weather->wx_type=WX_TYPE;
1631 xastir_snprintf(weather->wx_station,
1632 sizeof(weather->wx_station),
1633 "U2k");
1634
1635 /* get last gust speed */
1636 if (strlen(weather->wx_gust) > 0 && !from) // From local station
1637 {
1638 /* get last speed */
1639 last_speed=(float)atof(weather->wx_gust);
1640 last_speed_time=weather->wx_speed_sec_time;
1641 }
1642
1643 /* wind speed */
1644 if (data[2]!='-') // '-' signifies invalid data
1645 {
1646 substr(temp_data1,(char *)(data+2),4);
1647 xastir_snprintf(weather->wx_speed,
1648 sizeof(weather->wx_speed),
1649 "%03.0f",
1650 0.5 + ((strtol(temp_data1,&temp_conv,16) /10.0)*0.62137));
1651 if (from) // From remote station
1652 {
1653 weather->wx_speed_sec_time = sec_now();
1654 }
1655 else
1656 {
1657 /* local station */
1658 computed_gust = compute_gust((float)atof(weather->wx_speed),
1659 last_speed,
1660 &last_speed_time);
1661 weather->wx_speed_sec_time = sec_now();
1662 xastir_snprintf(weather->wx_gust,
1663 sizeof(weather->wx_gust),
1664 "%03d",
1665 (int)(0.5 + computed_gust));
1666 }
1667 }
1668 else
1669 {
1670 if (!from) // From local station
1671 {
1672 weather->wx_speed[0]=0;
1673 }
1674 }
1675
1676 /* wind direction */
1677 //
1678 // Note that the first two digits here may be 00, or may
1679 // be FF if a direction calibration has been entered.
1680 // We should zero them.
1681 //
1682 if (data[6]!='-') // '-' signifies invalid data
1683 {
1684 substr(temp_data1,(char *)(data+6),4);
1685 // Zero out the first two bytes
1686 temp_data1[0] = '0';
1687 temp_data1[1] = '0';
1688 xastir_snprintf(weather->wx_course,
1689 sizeof(weather->wx_course),
1690 "%03.0f",
1691 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
1692
1693 // Check for course = 0. Change to 360.
1694 if (strncmp(weather->wx_course,"000",3) == 0)
1695 {
1696 xastir_snprintf(weather->wx_course,
1697 sizeof(weather->wx_course),
1698 "360");
1699 }
1700
1701 }
1702 else
1703 {
1704 xastir_snprintf(weather->wx_course,
1705 sizeof(weather->wx_course),
1706 "000");
1707 if (!from) // From local station
1708 {
1709 weather->wx_course[0]=0;
1710 }
1711 }
1712
1713 /* outdoor temp */
1714 if (data[10]!='-') // '-' signifies invalid data
1715 {
1716 int temp4;
1717
1718 substr(temp_data1,(char *)(data+10),4);
1719 temp4 = (int)strtol(temp_data1,&temp_conv,16);
1720
1721 if (temp_data1[0] > '7') // Negative value, convert
1722 {
1723 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
1724 }
1725
1726 xastir_snprintf(weather->wx_temp,
1727 sizeof(weather->wx_temp),
1728 "%03d",
1729 (int)((float)((temp4<<16)/65536)/10.0));
1730 }
1731 else
1732 {
1733 if (!from) // From local station
1734 {
1735 weather->wx_temp[0]=0;
1736 }
1737 }
1738
1739 /* rain total long term */
1740 if (data[14]!='-') // '-' signifies invalid data
1741 {
1742 substr(temp_data1,(char *)(data+14),4);
1743 if (!from) // From local station
1744 {
1745 switch (WX_rain_gauge_type)
1746 {
1747 case 1: // 0.1" rain gauge
1748 xastir_snprintf(weather->wx_rain_total,
1749 sizeof(weather->wx_rain_total),
1750 "%0.2f",
1751 strtol(temp_data1,&temp_conv,16)*10.0);
1752 break;
1753 case 3: // 0.1mm rain gauge
1754 xastir_snprintf(weather->wx_rain_total,
1755 sizeof(weather->wx_rain_total),
1756 "%0.2f",
1757 strtol(temp_data1,&temp_conv,16)/2.54);
1758 break;
1759 case 2: // 0.01" rain gauge
1760 case 0: // No conversion
1761 default:
1762 xastir_snprintf(weather->wx_rain_total,
1763 sizeof(weather->wx_rain_total),
1764 "%0.2f",
1765 strtol(temp_data1,&temp_conv,16)*1.0);
1766 break;
1767 }
1768 /* local station */
1769 compute_rain((float)atof(weather->wx_rain_total));
1770 weather->wx_compute_rain_rates=1;
1771 /*last hour rain */
1772 xastir_snprintf(weather->wx_rain,
1773 sizeof(weather->wx_rain),
1774 "%0.2f",
1775 rain_minute_total);
1776 /*last 24 hour rain */
1777 xastir_snprintf(weather->wx_prec_24,
1778 sizeof(weather->wx_prec_24),
1779 "%0.2f",
1780 rain_24);
1781 /* rain since midnight */
1782 xastir_snprintf(weather->wx_prec_00,
1783 sizeof(weather->wx_prec_00),
1784 "%0.2f",
1785 rain_00);
1786 }
1787 }
1788 else
1789 {
1790 if (!from) // From local station
1791 {
1792 weather->wx_rain_total[0]=0;
1793 }
1794 }
1795
1796 /* baro */
1797 if (data[18]!='-') // '-' signifies invalid data
1798 {
1799 substr(temp_data1,(char *)(data+18),4);
1800 xastir_snprintf(weather->wx_baro,
1801 sizeof(weather->wx_baro),
1802 "%0.1f",
1803 strtol(temp_data1,&temp_conv,16)/10.0);
1804 }
1805
1806 /* outdoor humidity */
1807 if (data[26]!='-') // '-' signifies invalid data
1808 {
1809 substr(temp_data1,(char *)(data+26),4);
1810 xastir_snprintf(weather->wx_hum,
1811 sizeof(weather->wx_hum),
1812 "%03.0f",
1813 strtol(temp_data1,&temp_conv,16)/10.0);
1814 }
1815 else
1816 {
1817 if (!from) // From local station
1818 {
1819 weather->wx_hum[0]=0;
1820 }
1821 }
1822
1823 // Isn't this replaced by the above switch-case?
1824 // No, I don't think so. We can get these packets over
1825 // RF as well.
1826 /* todays rain total */
1827 if (strlen((const char *)data) > 45)
1828 {
1829 if (data[42]!='-') // '-' signifies invalid data
1830 {
1831 if (from) // From remote station
1832 {
1833 substr(temp_data1,(char *)(data+42),4);
1834 xastir_snprintf(weather->wx_prec_00,
1835 sizeof(weather->wx_prec_00),
1836 "%0.2f",
1837 strtol(temp_data1,&temp_conv,16)/100.0);
1838 }
1839 }
1840 else
1841 {
1842 if (!from) // From local station
1843 {
1844 weather->wx_prec_00[0]=0;
1845 }
1846 }
1847 }
1848 break;
1849
1850
1851
1852 /////////////////////////////////////////////////
1853 // Peet Brothers Ultimeter 2000 in packet mode //
1854 /////////////////////////////////////////////////
1855 case(APRS_WX5):
1856 if (debug_level & 1)
1857 {
1858 fprintf(stderr,"APRS WX5 Peet Bros U-2k Packet (Packet mode) %s:<%s>\n",fill->call_sign,data);
1859 }
1860
1861 weather->wx_type=WX_TYPE;
1862 xastir_snprintf(weather->wx_station,
1863 sizeof(weather->wx_station),
1864 "U2k");
1865
1866 /* get last gust speed */
1867 if (strlen(weather->wx_gust) > 0 && !from) // From local station
1868 {
1869 /* get last speed */
1870 last_speed=(float)atof(weather->wx_gust);
1871 last_speed_time=weather->wx_speed_sec_time;
1872 }
1873
1874 /* wind speed peak over last 5 min */
1875 if (data[5]!='-') // '-' signifies invalid data
1876 {
1877 substr(temp_data1,(char *)(data+5),4);
1878 if (from) // From remote station
1879 {
1880 xastir_snprintf(weather->wx_gust,
1881 sizeof(weather->wx_gust),
1882 "%03.0f",
1883 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
1884 /* this may be the only wind data */
1885 xastir_snprintf(weather->wx_speed,
1886 sizeof(weather->wx_speed),
1887 "%03.0f",
1888 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
1889 }
1890 else
1891 {
1892 /* local station and may be the only wind data */
1893 if (len<56)
1894 {
1895 xastir_snprintf(weather->wx_speed,
1896 sizeof(weather->wx_speed),
1897 "%03.0f",
1898 0.5 + ( strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
1899 computed_gust = compute_gust((float)atof(weather->wx_speed),
1900 last_speed,
1901 &last_speed_time);
1902 weather->wx_speed_sec_time = sec_now();
1903 xastir_snprintf(weather->wx_gust,
1904 sizeof(weather->wx_gust),
1905 "%03d",
1906 (int)(0.5 + computed_gust));
1907 }
1908 }
1909 }
1910 else
1911 {
1912 if (!from) // From local station
1913 {
1914 weather->wx_gust[0]=0;
1915 }
1916 }
1917
1918 /* wind direction */
1919 //
1920 // Note that the first two digits here may be 00, or may
1921 // be FF if a direction calibration has been entered.
1922 // We should zero them.
1923 //
1924 if (data[9]!='-') // '-' signifies invalid data
1925 {
1926 substr(temp_data1,(char *)(data+9),4);
1927 temp_data1[0] = '0';
1928 temp_data1[1] = '0';
1929 xastir_snprintf(weather->wx_course,
1930 sizeof(weather->wx_course),
1931 "%03.0f",
1932 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
1933
1934 // Check for course = 0. Change to 360.
1935 if (strncmp(weather->wx_course,"000",3) == 0)
1936 {
1937 xastir_snprintf(weather->wx_course,
1938 sizeof(weather->wx_course),
1939 "360");
1940 }
1941
1942 }
1943 else
1944 {
1945 xastir_snprintf(weather->wx_course,
1946 sizeof(weather->wx_course),
1947 "000");
1948 if (!from) // From local station
1949 {
1950 weather->wx_course[0]=0;
1951 }
1952 }
1953
1954 /* outdoor temp */
1955 if (data[13]!='-') // '-' signifies invalid data
1956 {
1957 int temp4;
1958
1959 substr(temp_data1,(char *)(data+13),4);
1960 temp4 = (int)strtol(temp_data1,&temp_conv,16);
1961
1962 if (temp_data1[0] > '7') // Negative value, convert
1963 {
1964 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
1965 }
1966
1967 xastir_snprintf(weather->wx_temp,
1968 sizeof(weather->wx_temp),
1969 "%03d",
1970 (int)((float)((temp4<<16)/65536)/10.0));
1971 }
1972 else
1973 {
1974 if (!from) // From local station
1975 {
1976 weather->wx_temp[0]=0;
1977 }
1978 }
1979 /* todays rain total (on some units) */
1980 if (data[49]!='-') // '-' signifies invalid data
1981 {
1982 if (from) // From remote station
1983 {
1984 substr(temp_data1,(char *)(data+49),4);
1985 xastir_snprintf(weather->wx_prec_00,
1986 sizeof(weather->wx_prec_00),
1987 "%0.2f",
1988 strtol(temp_data1,&temp_conv,16)/100.0);
1989 }
1990 }
1991 else
1992 {
1993 if (!from) // From local station
1994 {
1995 weather->wx_prec_00[0]=0;
1996 }
1997 }
1998
1999 /* rain total long term */
2000 if (data[17]!='-') // '-' signifies invalid data
2001 {
2002 substr(temp_data1,(char *)(data+17),4);
2003 if (!from) // From local station
2004 {
2005 switch (WX_rain_gauge_type)
2006 {
2007 case 1: // 0.1" rain gauge
2008 xastir_snprintf(weather->wx_rain_total,
2009 sizeof(weather->wx_rain_total),
2010 "%0.2f",
2011 strtol(temp_data1,&temp_conv,16)*10.0);
2012 break;
2013 case 3: // 0.1mm rain gauge
2014 xastir_snprintf(weather->wx_rain_total,
2015 sizeof(weather->wx_rain_total),
2016 "%0.2f",
2017 strtol(temp_data1,&temp_conv,16)/2.54);
2018 break;
2019 case 2: // 0.01" rain gauge
2020 case 0: // No conversion
2021 default:
2022 xastir_snprintf(weather->wx_rain_total,
2023 sizeof(weather->wx_rain_total),
2024 "%0.2f",
2025 strtol(temp_data1,&temp_conv,16)*1.0);
2026 break;
2027 }
2028 /* local station */
2029 compute_rain((float)atof(weather->wx_rain_total));
2030 weather->wx_compute_rain_rates=1;
2031 /*last hour rain */
2032 xastir_snprintf(weather->wx_rain,
2033 sizeof(weather->wx_rain),
2034 "%0.2f",
2035 rain_minute_total);
2036 /*last 24 hour rain */
2037 xastir_snprintf(weather->wx_prec_24,
2038 sizeof(weather->wx_prec_24),
2039 "%0.2f",
2040 rain_24);
2041 /* rain since midnight */
2042 xastir_snprintf(weather->wx_prec_00,
2043 sizeof(weather->wx_prec_00),
2044 "%0.2f",
2045 rain_00);
2046 }
2047 }
2048 else
2049 {
2050 if (!from) // From local station
2051 {
2052 weather->wx_rain_total[0]=0;
2053 }
2054 }
2055
2056 /* baro */
2057 if (data[21]!='-') // '-' signifies invalid data
2058 {
2059 substr(temp_data1,(char *)(data+21),4);
2060 xastir_snprintf(weather->wx_baro,
2061 sizeof(weather->wx_baro),
2062 "%0.1f",
2063 strtol(temp_data1, &temp_conv, 16)/10.0);
2064 }
2065 else
2066 {
2067 if (!from) // From local station
2068 {
2069 weather->wx_baro[0]=0;
2070 }
2071 }
2072
2073 /* outdoor humidity */
2074 if (data[37]!='-') // '-' signifies invalid data
2075 {
2076 substr(temp_data1,(char *)(data+37),4);
2077 xastir_snprintf(weather->wx_hum,
2078 sizeof(weather->wx_hum),
2079 "%03.0f",
2080 strtol(temp_data1,&temp_conv,16)/10.0);
2081 }
2082 else
2083 {
2084 if (!from) // From local station
2085 {
2086 weather->wx_hum[0]=0;
2087 }
2088 }
2089
2090 /* 1 min wind speed avg */
2091 if (len>53 && (data[53]) != '-') // '-' signifies invalid data
2092 {
2093 substr(temp_data1,(char *)(data+53),4);
2094 xastir_snprintf(weather->wx_speed,
2095 sizeof(weather->wx_speed),
2096 "%03.0f",
2097 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2098 if (from) // From remote station
2099 {
2100 weather->wx_speed_sec_time = sec_now();
2101 }
2102 else
2103 {
2104 /* local station */
2105 computed_gust = compute_gust((float)atof(weather->wx_speed),
2106 last_speed,
2107 &last_speed_time);
2108 weather->wx_speed_sec_time = sec_now();
2109 xastir_snprintf(weather->wx_gust,
2110 sizeof(weather->wx_gust),
2111 "%03d",
2112 (int)(0.5 + computed_gust));
2113 }
2114 }
2115 else
2116 {
2117 if (!from) // From local station
2118 {
2119 if (len>53)
2120 {
2121 weather->wx_speed[0]=0;
2122 }
2123 }
2124 }
2125 break;
2126
2127
2128
2129 //////////////////////////////////////////////////////////
2130 // Peet Brothers Ultimeter 2000 in complete record mode //
2131 //////////////////////////////////////////////////////////
2132 //
2133 // In this mode most fields are 4-bytes two's complement. A
2134 // few fields are 2-bytes wide.
2135 //
2136 // <http://www.peetbros.com/HTML_Pages/faqs.htm>
2137 //
2138 case(PEET_COMPLETE):
2139 if (debug_level & 1)
2140 {
2141 fprintf(stderr,"Peet Bros U-2k Packet (Complete Record Mode) %s:<%s>\n",fill->call_sign,data);
2142 }
2143
2144 if (!from) // From local station
2145 {
2146 int done_with_wx_speed = 0;
2147
2148
2149 /* decode only for local station */
2150 weather->wx_type=WX_TYPE;
2151 xastir_snprintf(weather->wx_station,
2152 sizeof(weather->wx_station),
2153 "U2k");
2154
2155
2156 if (data[12]!='-') // '-' signifies invalid data
2157 {
2158 substr(temp_data1,(char *)(data+12),4);
2159 xastir_snprintf(weather->wx_gust,
2160 sizeof(weather->wx_gust),
2161 "%03.0f",
2162 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2163 }
2164 else
2165 {
2166 weather->wx_gust[0]=0;
2167 }
2168
2169
2170 // Check whether field 115 is available at bytes 452
2171 // through 455. If so, that's the one-minute wind
2172 // speed average in 0.1kph, which matches the APRS
2173 // spec except for the units (which should be MPH).
2174 if ( (len >= 456) && (data[452] != '-') ) // '-' signifies invalid data
2175 {
2176 substr(temp_data1,(char *)(data+452),4);
2177 xastir_snprintf(weather->wx_speed,
2178 sizeof(weather->wx_speed),
2179 "%03.0f",
2180 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2181 done_with_wx_speed++;
2182 }
2183
2184
2185 // Some Peet units don't have that particular wind
2186 // speed field, so snag what wind speed we can from
2187 // the other wind speed fields, which don't quite
2188 // match the APRS spec as they're instantaneous
2189 // values, not one-minute sustained speeds.
2190
2191
2192 // KG9AE
2193 // Peet Bros CR mode wind values should be selected based on which are highest.
2194 /* Wind Speed fields 1, 34, and 71. Wind direction fields 2, 35, 72. */
2195 if (data[4] !='-') // '-' signifies invalid data
2196 {
2197 substr(temp_data1, (char *)data+4, 4);
2198 temp1 = 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137;
2199 }
2200 else
2201 {
2202 temp1=0;
2203 }
2204 if (data[136] !='-') // '-' signifies invalid data
2205 {
2206 substr(temp_data1, (char *)data+136, 4);
2207 temp2 = 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137;
2208 }
2209 else
2210 {
2211 temp2=0;
2212 }
2213 if (data[284] !='-') // '-' signifies invalid data
2214 {
2215 substr(temp_data1, (char *)data+284, 4);
2216 temp3 = 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137;
2217 }
2218 else
2219 {
2220 temp3=0;
2221 }
2222
2223 // fprintf(stderr,"WIND: wind1 %d, wind2 %d, wind3 %d\n", temp1, temp2, temp3);
2224
2225 // Select wind speed and direction based on which
2226 // wind speed is the highest. Ugh, surely there's a
2227 // way to make this pretty. A function might be
2228 // better.
2229 if ( temp1 >= temp2 && temp1 >= temp3 )
2230 {
2231 // fprintf(stderr,"WIND: ***\n");
2232
2233 /* wind speed */
2234 if (!done_with_wx_speed)
2235 {
2236 substr(temp_data1,(char *)(data+4),4);
2237 xastir_snprintf(weather->wx_speed,
2238 sizeof(weather->wx_speed),
2239 "%03.0f",
2240 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2241 }
2242
2243 /* wind direction */
2244 //
2245 // Note that the first two digits here may be
2246 // 00, or may be FF if a direction calibration
2247 // has been entered. We should zero them.
2248 //
2249 if (data[8]!='-') // '-' signifies invalid data
2250 {
2251 substr(temp_data1,(char *)(data+8),4);
2252 temp_data1[0] = '0';
2253 temp_data1[1] = '0';
2254 xastir_snprintf(weather->wx_course,
2255 sizeof(weather->wx_course),
2256 "%03.0f",
2257 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
2258
2259 // Check for course = 0. Change to 360.
2260 if (strncmp(weather->wx_course,"000",3) == 0)
2261 {
2262 xastir_snprintf(weather->wx_course,
2263 sizeof(weather->wx_course),
2264 "360");
2265 }
2266
2267 }
2268 else
2269 {
2270 xastir_snprintf(weather->wx_course,
2271 sizeof(weather->wx_course),
2272 "000");
2273 weather->wx_course[0]=0;
2274 }
2275 }
2276 else if ( temp2 >= temp1 && temp2 >= temp3 )
2277 {
2278 // fprintf(stderr,"WIND: ***\n");
2279
2280 if (!done_with_wx_speed)
2281 {
2282 /* wind speed */
2283 substr(temp_data1,(char *)(data+136),4);
2284 xastir_snprintf(weather->wx_speed,
2285 sizeof(weather->wx_speed),
2286 "%03.0f",
2287 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2288 }
2289
2290 /* wind direction */
2291 //
2292 // Note that the first two digits here may be
2293 // 00, or may be FF if a direction calibration
2294 // has been entered. We should zero them.
2295 //
2296 if (data[140]!='-') // '-' signifies invalid data
2297 {
2298 substr(temp_data1,(char *)(data+140),4);
2299 temp_data1[0] = '0';
2300 temp_data1[1] = '0';
2301 xastir_snprintf(weather->wx_course,
2302 sizeof(weather->wx_course),
2303 "%03.0f",
2304 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
2305
2306 // Check for course = 0. Change to 360.
2307 if (strncmp(weather->wx_course,"000",3) == 0)
2308 {
2309 xastir_snprintf(weather->wx_course,
2310 sizeof(weather->wx_course),
2311 "360");
2312 }
2313
2314 }
2315 else
2316 {
2317 xastir_snprintf(weather->wx_course,
2318 sizeof(weather->wx_course),
2319 "000");
2320 weather->wx_course[0]=0;
2321 }
2322 }
2323 else if ( temp3 >= temp2 && temp3 >= temp1 )
2324 {
2325 // fprintf(stderr,"WIND: ***\n");
2326
2327 if (!done_with_wx_speed)
2328 {
2329 /* wind speed */
2330 substr(temp_data1,(char *)(data+284),4);
2331 xastir_snprintf(weather->wx_speed,
2332 sizeof(weather->wx_speed),
2333 "%03.0f",
2334 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2335 }
2336
2337 /* wind direction */
2338 //
2339 // Note that the first two digits here may be
2340 // 00, or may be FF if a direction calibration
2341 // has been entered. We should zero them.
2342 //
2343 if (data[288]!='-') // '-' signifies invalid data
2344 {
2345 substr(temp_data1,(char *)(data+288),4);
2346 temp_data1[0] = '0';
2347 temp_data1[1] = '0';
2348 xastir_snprintf(weather->wx_course,
2349 sizeof(weather->wx_course),
2350 "%03.0f",
2351 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
2352
2353 // Check for course = 0. Change to 360.
2354 if (strncmp(weather->wx_course,"000",3) == 0)
2355 {
2356 xastir_snprintf(weather->wx_course,
2357 sizeof(weather->wx_course),
2358 "360");
2359 }
2360
2361 }
2362 else
2363 {
2364 xastir_snprintf(weather->wx_course,
2365 sizeof(weather->wx_course),
2366 "000");
2367 weather->wx_course[0]=0;
2368 }
2369 }
2370 else /* Or default to the first value */
2371 {
2372 // fprintf(stderr,"WIND: DEFAULTING!\n");
2373
2374 if (!done_with_wx_speed)
2375 {
2376 /* wind speed */
2377 substr(temp_data1,(char *)(data+4),4);
2378 xastir_snprintf(weather->wx_speed,
2379 sizeof(weather->wx_speed),
2380 "%03.0f",
2381 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2382 }
2383
2384 /* wind direction */
2385 //
2386 // Note that the first two digits here may be
2387 // 00, or may be FF if a direction calibration
2388 // has been entered. We should zero them.
2389 //
2390 if (data[8]!='-') // '-' signifies invalid data
2391 {
2392 substr(temp_data1,(char *)(data+8),4);
2393 temp_data1[0] = '0';
2394 temp_data1[1] = '0';
2395 xastir_snprintf(weather->wx_course,
2396 sizeof(weather->wx_course),
2397 "%03.0f",
2398 (strtol(temp_data1,&temp_conv,16)/256.0)*360.0);
2399
2400 // Check for course = 0. Change to 360.
2401 if (strncmp(weather->wx_course,"000",3) == 0)
2402 {
2403 xastir_snprintf(weather->wx_course,
2404 sizeof(weather->wx_course),
2405 "360");
2406 }
2407
2408 }
2409 else
2410 {
2411 xastir_snprintf(weather->wx_course,
2412 sizeof(weather->wx_course),
2413 "000");
2414 weather->wx_course[0]=0;
2415 }
2416 }
2417
2418
2419 /* outdoor temp */
2420 if (data[24]!='-') // '-' signifies invalid data
2421 {
2422 int temp4;
2423
2424 substr(temp_data1,(char *)(data+24),4);
2425 temp4 = (int)strtol(temp_data1,&temp_conv,16);
2426
2427 if (temp_data1[0] > '7') // Negative value, convert
2428 {
2429 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
2430 }
2431
2432 xastir_snprintf(weather->wx_temp,
2433 sizeof(weather->wx_temp),
2434 "%03d",
2435 (int)((float)((temp4<<16)/65536)/10.0));
2436 }
2437 else
2438 {
2439 weather->wx_temp[0]=0;
2440 }
2441
2442 // We don't want to parse this here because compute_rain()
2443 // calculates this for us from the accumulating long-term rain
2444 // total. If we were to do it here as well, we'll get conflicting
2445 // results. Since only some units put out today's rain total, we'll
2446 // just rely on our own calculations for it instead. It'll work
2447 // across more units.
2448 /*
2449 // todays rain total (on some units)
2450 if (data[28]!='-') { // '-' signifies invalid data
2451 substr(temp_data1,(char *)(data+28),4);
2452 switch (WX_rain_gauge_type) {
2453 case 1: // 0.1" rain gauge
2454 xastir_snprintf(weather->wx_prec_00,
2455 sizeof(weather->wx_prec_00),
2456 "%0.2f",
2457 (float)strtol(temp_data1,&temp_conv,16)/10.0);
2458 break;
2459 case 3: // 0.1mm rain gauge
2460 xastir_snprintf(weather->wx_prec_00,
2461 sizeof(weather->wx_prec_00),
2462 "%0.2f",
2463 (float)strtol(temp_data1,&temp_conv,16)/254.0);
2464 break;
2465 case 2: // 0.01" rain gauge
2466 case 0: // No conversion
2467 default:
2468 xastir_snprintf(weather->wx_prec_00,
2469 sizeof(weather->wx_prec_00),
2470 "%0.2f",
2471 (float)strtol(temp_data1,&temp_conv,16)/100.0);
2472 break;
2473 }
2474 } else
2475 weather->wx_prec_00[0]=0;
2476 */
2477
2478 /* rain total long term */
2479 if ((char)data[432]!='-') // '-' signifies invalid data
2480 {
2481 substr(temp_data1,(char *)(data+432),4);
2482 switch (WX_rain_gauge_type)
2483 {
2484 case 1: // 0.1" rain gauge
2485 xastir_snprintf(weather->wx_rain_total,
2486 sizeof(weather->wx_rain_total),
2487 "%0.2f",
2488 strtol(temp_data1,&temp_conv,16)*10.0);
2489 break;
2490 case 3: // 0.1mm rain gauge
2491 xastir_snprintf(weather->wx_rain_total,
2492 sizeof(weather->wx_rain_total),
2493 "%0.2f",
2494 strtol(temp_data1,&temp_conv,16)/2.54);
2495 break;
2496 case 2: // 0.01" rain gauge
2497 case 0: // No conversion
2498 default:
2499 xastir_snprintf(weather->wx_rain_total,
2500 sizeof(weather->wx_rain_total),
2501 "%0.2f",
2502 strtol(temp_data1,&temp_conv,16)*1.0);
2503 break;
2504 }
2505 /* Since local station only */
2506 compute_rain((float)atof(weather->wx_rain_total));
2507 weather->wx_compute_rain_rates=1;
2508
2509 /*last hour rain */
2510 xastir_snprintf(weather->wx_rain,
2511 sizeof(weather->wx_rain),
2512 "%0.2f",
2513 rain_minute_total);
2514
2515 /*last 24 hour rain */
2516 xastir_snprintf(weather->wx_prec_24,
2517 sizeof(weather->wx_prec_24),
2518 "%0.2f",
2519 rain_24);
2520
2521 /* rain since midnight */
2522 xastir_snprintf(weather->wx_prec_00,
2523 sizeof(weather->wx_prec_00),
2524 "%0.2f",
2525 rain_00);
2526 }
2527 else
2528 {
2529 weather->wx_rain_total[0]=0;
2530 }
2531
2532 /* baro */
2533 if (data[32]!='-') // '-' signifies invalid data
2534 {
2535 substr(temp_data1,(char *)(data+32),4);
2536 xastir_snprintf(weather->wx_baro,
2537 sizeof(weather->wx_baro),
2538 "%0.1f",
2539 strtol(temp_data1,&temp_conv,16)/10.0);
2540 }
2541 else
2542 {
2543 weather->wx_baro[0]=0;
2544 }
2545
2546 /* outdoor humidity */
2547 if (data[52]!='-') // '-' signifies invalid data
2548 {
2549 substr(temp_data1,(char *)(data+52),4);
2550 xastir_snprintf(weather->wx_hum,
2551 sizeof(weather->wx_hum),
2552 "%03.0f",
2553 strtol(temp_data1,&temp_conv,16)/10.0);
2554 }
2555 else
2556 {
2557 weather->wx_hum[0]=0;
2558 }
2559
2560 /* dew point */
2561 if (data[60]!='-') // '-' signifies invalid data
2562 {
2563 int temp4;
2564
2565 substr(temp_data1,(char *)(data+60),4);
2566 temp4 = (int)strtol(temp_data1,&temp_conv,16);
2567
2568 if (temp_data1[0] > '7') // Negative value, convert
2569 {
2570 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
2571 }
2572
2573 xastir_snprintf(wx_dew_point,
2574 sizeof(wx_dew_point),
2575 "%03d",
2576 (int)((float)((temp4<<16)/65536)/10.0));
2577 wx_dew_point_on = 1;
2578 }
2579
2580 /*high winds for today*/
2581 if (data[248]!='-') // '-' signifies invalid data
2582 {
2583 substr(temp_data1,(char *)(data+248),4);
2584 xastir_snprintf(wx_high_wind,
2585 sizeof(wx_high_wind),
2586 "%03.0f",
2587 0.5 + (strtol(temp_data1,&temp_conv,16)/10.0)*0.62137);
2588 wx_high_wind_on = 1;
2589 }
2590
2591 /*wind chill */
2592 if (data[20]!='-') // '-' signifies invalid data
2593 {
2594 int temp4;
2595
2596 substr(temp_data1,(char *)(data+20),4);
2597 temp4 = (int)strtol(temp_data1,&temp_conv,16);
2598
2599 if (temp_data1[0] > '7') // Negative value, convert
2600 {
2601 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
2602 }
2603
2604 xastir_snprintf(wx_wind_chill,
2605 sizeof(wx_wind_chill),
2606 "%03d",
2607 (int)((float)((temp4<<16)/65536)/10.0));
2608 wx_wind_chill_on = 1;
2609 }
2610
2611 /*3-Hr Barometric Change */
2612 if (data[36]!='-') // '-' signifies invalid data
2613 {
2614 int temp4;
2615
2616 substr(temp_data1,(char *)(data+36),4);
2617 temp4 = (int)strtol(temp_data1,&temp_conv,16);
2618
2619 if (temp_data1[0] > '7') // Negative value, convert
2620 {
2621 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
2622 }
2623
2624 xastir_snprintf(wx_three_hour_baro,
2625 sizeof(wx_three_hour_baro),
2626 "%0.2f",
2627 // Old code
2628 // (float)((strtol(temp_data1,&temp_conv,16)<<16)/65536)/100.0/3.38639);
2629 // New code, fix by Matt Werner, kb0kqa:
2630 (float)((temp4<<16)/65536)/10.0);
2631
2632 wx_three_hour_baro_on = 1;
2633 }
2634
2635 /* High Temp for Today*/
2636 if (data[276]!='-') // '-' signifies invalid data
2637 {
2638 int temp4;
2639
2640 substr(temp_data1,(char *)(data+276),4);
2641 temp4 = (int)strtol(temp_data1,&temp_conv,16);
2642
2643 if (temp_data1[0] > '7') // Negative value, convert
2644 {
2645 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
2646 }
2647
2648 xastir_snprintf(wx_hi_temp,
2649 sizeof(wx_hi_temp),
2650 "%03d",
2651 (int)((float)((temp4<<16)/65536)/10.0));
2652 wx_hi_temp_on = 1;
2653 }
2654 else
2655 {
2656 wx_hi_temp_on = 0;
2657 }
2658
2659 /* Low Temp for Today*/
2660 if (data[100]!='-') // '-' signifies invalid data
2661 {
2662 int temp4;
2663
2664 substr(temp_data1,(char *)(data+100),4);
2665 temp4 = (int)strtol(temp_data1,&temp_conv,16);
2666
2667 if (temp_data1[0] > '7') // Negative value, convert
2668 {
2669 temp4 = (temp4 & (temp4-0x7FFF)) - 0x8000;
2670 }
2671
2672 xastir_snprintf(wx_low_temp,
2673 sizeof(wx_low_temp),
2674 "%03d",
2675 (int)((float)((temp4<<16)/65536)/10.0));
2676 wx_low_temp_on = 1;
2677 }
2678 else
2679 {
2680 wx_low_temp_on = 0;
2681 }
2682
2683 /* Heat Index Calculation*/
2684 hi_hum=atoi(weather->wx_hum);
2685 rh2= atoi(weather->wx_hum);
2686 rh2=(rh2 * rh2);
2687 hidx_temp=atoi(weather->wx_temp);
2688 t2= atoi(weather->wx_temp);
2689 t2=(t2 * t2);
2690
2691 if (hidx_temp >= 70)
2692 {
2693 heat_index=-42.379+2.04901523 * hidx_temp+10.1433127 * hi_hum-0.22475541
2694 * hidx_temp * hi_hum-0.00683783 * t2-0.05481717 * rh2+0.00122874
2695 * t2 * hi_hum+0.00085282 * hidx_temp * rh2-0.00000199 * t2 * rh2;
2696 xastir_snprintf (wx_heat_index,
2697 sizeof(wx_heat_index),
2698 "%03d",
2699 heat_index);
2700 wx_heat_index_on = 1;
2701 }
2702 else
2703 {
2704 wx_heat_index_on = 0;
2705 }
2706 }
2707 break;
2708
2709
2710
2711 ////////////////////////
2712 // Qualimetrics Q-Net //
2713 ////////////////////////
2714 case(QM_WX):
2715 if (debug_level & 1)
2716 {
2717 fprintf(stderr,"Qualimetrics Q-Net %s:<%s>\n",fill->call_sign,data);
2718 }
2719
2720 weather->wx_type=WX_TYPE;
2721 xastir_snprintf(weather->wx_station,
2722 sizeof(weather->wx_station),
2723 "Q-N");
2724
2725 // Can this sscanf overflow the "temp" buffer? I
2726 // changed the length of temp to MAX_DEVICE_BUFFER to
2727 // avoid this problem.
2728 if (6 != sscanf((char *)data,"%19s %d %19s %d %19s %d",temp,&temp1,temp,&temp2,temp,&temp3))
2729 {
2730 fprintf(stderr,"wx_fill_data:sscanf parsing error\n");
2731 }
2732
2733 /* outdoor temp */
2734 xastir_snprintf(weather->wx_temp,
2735 sizeof(weather->wx_temp),
2736 "%03d",
2737 (int)((temp2/10.0)));
2738
2739 /* baro */
2740 xastir_snprintf(weather->wx_baro,
2741 sizeof(weather->wx_baro),
2742 "%0.1f",
2743 ((float)temp3/100.0)*33.864);
2744
2745 /* outdoor humidity */
2746 xastir_snprintf(weather->wx_hum,
2747 sizeof(weather->wx_hum),
2748 "%03d",
2749 temp1);
2750
2751 if (!from) // From local station
2752 {
2753 weather->wx_gust[0]=0;
2754 weather->wx_course[0]=0;
2755 weather->wx_rain[0]=0;
2756 weather->wx_prec_00[0]=0;
2757 weather->wx_prec_24[0]=0;
2758 weather->wx_rain_total[0]=0;
2759 weather->wx_gust[0]=0;
2760 weather->wx_speed[0]=0;
2761 }
2762 break;
2763
2764
2765
2766 ///////////////////////////////////////////////////////////
2767 // Radio Shack WX-200 or Huger/Oregon Scientific WM-918 //
2768 ///////////////////////////////////////////////////////////
2769 case(RSWX200):
2770
2771 // Notes: Many people run the wx200d daemon connected to the weather station,
2772 // with Xastir then connected to wx200d. Note that wx200d changes the protocol
2773 // slightly: It only sends frames that have changed to the clients. This means
2774 // even if the weather station is sending regular packets, wx200d won't send
2775 // them along to Xastir if all the bits are the same as the last packet of that
2776 // type. To fix this I had to tie into the main.c:UpdateTime() function to do
2777 // regular updates at a 30 second rate, to keep the rain and gust queues cycling
2778 // on a regular basis.
2779 //
2780 // 2nd Note: Some WX-200 weather stations send bogus data. I had to add in a
2781 // bunch of filtering to keep the global variables from getting corrupted by
2782 // this data. More filtering may need to be done and/or the limits may need to
2783 // be changed.
2784
2785 if (!from) // From local station
2786 {
2787 if (debug_level & 1)
2788 {
2789 fprintf(stderr,"RSWX200 WX (binary)\n");
2790 }
2791
2792 weather->wx_type=WX_TYPE;
2793 xastir_snprintf(weather->wx_station,
2794 sizeof(weather->wx_station),
2795 "RSW");
2796
2797 switch (data[0])
2798 {
2799 case 0x8f: /* humidity */
2800 if ( (rswnc(data[20]) <= 100) && (rswnc(data[2]) >= 0) )
2801 xastir_snprintf(weather->wx_hum,
2802 sizeof(weather->wx_hum),
2803 "%03d",
2804 rswnc(data[20]));
2805 else
2806 //sprintf(weather->wx_hum,"100");
2807 {
2808 fprintf(stderr,"Humidity out-of-range, ignoring: %03d\n",rswnc(data[20]) );
2809 }
2810 break;
2811
2812 case 0x9f: /* temp */
2813 /* all data in C ?*/
2814 xastir_snprintf(temp_data1,
2815 sizeof(temp_data1),
2816 "%c%d%0.1f",
2817 ((data[17]&0x08) ? '-' : '+'),(data[17]&0x7),rswnc(data[16])/10.0);
2818 /*fprintf(stderr,"temp data: <%s> %d %d %d\n", temp_data1,((data[17]&0x08)==0x08),(data[17]&0x7),rswnc(data[16]));*/
2819 temp_temp = (float)((atof(temp_data1)*1.8)+32);
2820 if ( (temp_temp >= -99.0) && (temp_temp < 200.0) )
2821 {
2822 xastir_snprintf(weather->wx_temp,
2823 sizeof(weather->wx_temp),
2824 "%03d",
2825 (int)((atof(temp_data1)*1.8)+32));
2826 /*fprintf(stderr,"Temp %s C %0.2f %03d\n",temp_data1,atof(temp_data1),(int)atof(temp_data1));
2827 fprintf(stderr,"Temp F %0.2f %03d\n",(atof(temp_data1)*1.8)+32,(int)(atof(temp_data1)*1.8)+32);
2828 */
2829 }
2830 else // We don't want to save this one
2831 {
2832 fprintf(stderr,"Temp out-of-range, ignoring: %0.2f\n", temp_temp);
2833 }
2834 xastir_snprintf(temp_data1,
2835 sizeof(temp_data1),
2836 "%c%d%d.%d",
2837 ((data[18]&0x80) ? '-' : '+'),(data[18]&0x70)>>4,(data[18]&0x0f),(data[17] & 0xf0) >> 4);
2838 xastir_snprintf(wx_hi_temp,
2839 sizeof(wx_hi_temp),
2840 "%03d",
2841 (int)((atof(temp_data1)*1.8)+32));
2842 wx_hi_temp_on=1;
2843 xastir_snprintf(temp_data1,
2844 sizeof(temp_data1),
2845 "%c%d%d.%d",
2846 ((data[23]&0x80) ? '-' : '+'),(data[23]&0x70)>>4,(data[23]&0x0f),(data[22] & 0xf0) >> 4);
2847 xastir_snprintf(wx_low_temp,
2848 sizeof(wx_low_temp),
2849 "%03d",
2850 (int)((atof(temp_data1)*1.8)+32));
2851 wx_low_temp_on=1;
2852 break;
2853
2854 case 0xaf: /* baro/dewpt */
2855 // local baro pressure in mb?
2856 // sprintf(weather->wx_baro,"%02d%02d",rswnc(data[2]),rswnc(data[1]));
2857 // Sea Level Adjusted baro in mb
2858 xastir_snprintf(weather->wx_baro,
2859 sizeof(weather->wx_baro),
2860 "%0d%02d%0.1f",
2861 (data[5]&0x0f),
2862 rswnc(data[4]),
2863 rswnc(data[3])/10.0);
2864
2865 /* dew point in C */
2866 temp_temp = (int)((rswnc(data[18])*1.8)+32);
2867 if ( (temp_temp >= 32.0) && (temp_temp < 150.0) )
2868 xastir_snprintf(wx_dew_point,
2869 sizeof(wx_dew_point),
2870 "%03d",
2871 (int)((rswnc(data[18])*1.8)+32));
2872 else
2873 {
2874 fprintf(stderr,"Dew point out-of-range, ignoring: %0.2f\n", temp_temp);
2875 }
2876 break;
2877
2878 case 0xbf: /* Rain */
2879 // All data in mm. Convert to hundredths of an inch.
2880 xastir_snprintf(temp_data1,
2881 sizeof(temp_data1),
2882 "%02d%02d",
2883 rswnc(data[6]),
2884 rswnc(data[5]));
2885
2886 temp_temp = (float)(atof(temp_data1) * 3.9370079);
2887
2888 if ( (temp_temp >= 0) && (temp_temp < 51200.0) ) // Between 0 and 512 inches
2889 {
2890 xastir_snprintf(weather->wx_rain_total,
2891 sizeof(weather->wx_rain_total),
2892 "%0.2f",
2893 atof(temp_data1) * 3.9370079);
2894
2895 /* Since local station only */
2896 compute_rain((float)atof(weather->wx_rain_total));
2897 weather->wx_compute_rain_rates=1;
2898
2899 /* Last hour rain */
2900 xastir_snprintf(weather->wx_rain,
2901 sizeof(weather->wx_rain),
2902 "%0.2f",
2903 rain_minute_total);
2904
2905 /* Last 24 hour rain */
2906 xastir_snprintf(weather->wx_prec_24,
2907 sizeof(weather->wx_prec_24),
2908 "%0.2f",
2909 rain_24);
2910
2911 /* Rain since midnight */
2912 xastir_snprintf(weather->wx_prec_00,
2913 sizeof(weather->wx_prec_00),
2914 "%0.2f",
2915 rain_00);
2916 }
2917 else
2918 {
2919 fprintf(stderr,"Total Rain out-of-range, ignoring: %0.2f\n", temp_temp);
2920 }
2921 break;
2922
2923 case 0xcf: /* Wind w/chill*/
2924 /* get last gust speed */
2925 if (strlen(weather->wx_gust) > 0)
2926 {
2927 /* get last speed */
2928 last_speed=(float)atof(weather->wx_gust);
2929 last_speed_time=weather->wx_speed_sec_time;
2930 }
2931 /* all data in m/s */
2932 /* average wind speed */
2933 xastir_snprintf(temp_data1,
2934 sizeof(temp_data1),
2935 "%01d%0.1f",
2936 (data[5]&0xf),
2937 (float)( rswnc(data[4]) / 10 ));
2938 // Convert to mph
2939 xastir_snprintf(weather->wx_speed,
2940 sizeof(weather->wx_speed),
2941 "%03d",
2942 (int)(0.5 + (atof(temp_data1)*2.2369)));
2943
2944 /* wind gust */
2945 xastir_snprintf(temp_data1,
2946 sizeof(temp_data1),
2947 "%01d%0.1f",
2948 (data[2]&0xf),
2949 (float)( rswnc(data[1]) / 10 ));
2950 /*sprintf(weather->wx_gust,"%03d",(int)(0.5 + (atof(temp_data1)*2.2369)));*/
2951
2952 /* do computed gust, convert to mph */
2953 computed_gust = compute_gust((int)(0.5 + (atof(temp_data1)*2.2369)),
2954 last_speed,
2955 &last_speed_time);
2956 weather->wx_speed_sec_time = sec_now();
2957 xastir_snprintf(weather->wx_gust,
2958 sizeof(weather->wx_gust),
2959 "%03d",
2960 (int)(0.5 + computed_gust));
2961
2962 /* high wind gust */
2963 xastir_snprintf(temp_data1,
2964 sizeof(temp_data1),
2965 "%01d%0.1f",
2966 (data[8]&0xf),
2967 (float)( rswnc(data[7]) / 10 ));
2968 xastir_snprintf(wx_high_wind,
2969 sizeof(wx_high_wind),
2970 "%03d",
2971 (int)(0.5 + (atof(temp_data1)*2.2369)));
2972 wx_high_wind_on = 1;
2973
2974 xastir_snprintf(weather->wx_course,
2975 sizeof(weather->wx_course),
2976 "%03d",
2977 ( ((rswnc(data[3])*10) + ((data[2]&0xf0)>>4)) %1000 ) );
2978
2979 // Check for course = 0. Change to 360.
2980 if (strncmp(weather->wx_course,"000",3) == 0)
2981 {
2982 xastir_snprintf(weather->wx_course,
2983 sizeof(weather->wx_course),
2984 "360");
2985 }
2986
2987 /* wind chill in C */
2988 xastir_snprintf(temp_data1,
2989 sizeof(temp_data1),
2990 "%c%d",
2991 ((data[21]&0x20) ? '-' : '+'),
2992 rswnc(data[16]));
2993
2994 temp_temp = (float)((atof(temp_data1)*1.8)+32);
2995 if ( (temp_temp > -200.0) && (temp_temp < 200.0) )
2996 xastir_snprintf(wx_wind_chill,
2997 sizeof(wx_wind_chill),
2998 "%03d",
2999 (int)((atof(temp_data1)*1.8)+32));
3000 else
3001 {
3002 fprintf(stderr,"Wind_chill out-of-range, ignoring: %0.2f\n", temp_temp);
3003 }
3004
3005 wx_wind_chill_on = 1;
3006 break;
3007 default:
3008 break;
3009 }
3010
3011 if (strlen(weather->wx_hum) > 0 && strlen(weather->wx_temp) > 0)
3012 {
3013 /* Heat Index Calculation*/
3014 hi_hum=atoi(weather->wx_hum);
3015 rh2= atoi(weather->wx_hum);
3016 rh2=(rh2 * rh2);
3017 hidx_temp=atoi(weather->wx_temp);
3018 t2= atoi(weather->wx_temp);
3019 t2=(t2 * t2);
3020
3021 if (hidx_temp >= 70)
3022 {
3023 heat_index=(-42.379+2.04901523 * hidx_temp+10.1433127 * hi_hum-0.22475541 * hidx_temp *
3024 hi_hum-0.00683783 * t2-0.05481717 * rh2+0.00122874 * t2 * hi_hum+0.00085282 *
3025 hidx_temp * rh2-0.00000199 * t2 * rh2);
3026 xastir_snprintf(wx_heat_index,
3027 sizeof(wx_heat_index),
3028 "%03d",
3029 heat_index);
3030
3031 wx_heat_index_on=1;
3032 }
3033 else
3034 {
3035 wx_heat_index[0] = 0;
3036 }
3037 } // end of heat index calculation
3038 } // end of if (!from)
3039 break; // End of case for RSWX200 weather station
3040
3041
3042 ///////////////////////////////////////////////////////////
3043 // Davis WMII/WWIII/Vantage Pro via meteo & db2APRS //
3044 ///////////////////////////////////////////////////////////
3045
3046 // Note: format is really APRS Spec 'positionless' WX string w/tag for X and Davis
3047
3048 case(DAVISMETEO) :
3049
3050
3051 // todo - need to deal with lack of values, such as c...s...g...t045 string
3052
3053 memset(weather->wx_course,0,4); // Keep out fradulent data...
3054 memset(weather->wx_speed,0,4);
3055 memset(weather->wx_temp,0,5);
3056 memset(weather->wx_rain,0,10);
3057 memset(weather->wx_prec_00,0,10);
3058 memset(weather->wx_prec_24,0,10);
3059 memset(weather->wx_rain_total,0,10);
3060 memset(weather->wx_hum,0,5);
3061 memset(weather->wx_baro,0,10);
3062 memset(weather->wx_station,0,MAX_WXSTATION);
3063
3064 if ((temp_conv=strchr((char *)data,'c'))) // Wind Direction in Degrees
3065 {
3066 xastir_snprintf(weather->wx_course,
3067 sizeof(weather->wx_course),
3068 "%s",
3069 temp_conv+1);
3070 weather->wx_course[3] = '\0';
3071 }
3072
3073 // Check for course = 0. Change to 360.
3074 if (strncmp(weather->wx_course,"000",3) == 0)
3075 {
3076 xastir_snprintf(weather->wx_course,
3077 sizeof(weather->wx_course),
3078 "360");
3079 }
3080
3081 if ((temp_conv=strchr((char *)data,'s'))) // Wind Speed in MPH - not snowfall
3082 {
3083 xastir_snprintf(weather->wx_speed,
3084 sizeof(weather->wx_speed),
3085 "%s",
3086 temp_conv+1);
3087 weather->wx_speed[3] = '\0';
3088 }
3089
3090 if ((temp_conv=strchr((char *)data,'g'))) // Wind Gust in MPH
3091 {
3092 // Don't read if data is "..." (missing gust data)
3093 // If we aren't getting gust data from the station, don't clobber
3094 // the gust data we're computing!
3095 if (strncmp(temp_conv+1,"...",3) != 0)
3096 {
3097 memset(weather->wx_gust,0,4); // keep out fraudulent data
3098 xastir_snprintf(weather->wx_gust,
3099 sizeof(weather->wx_gust),
3100 "%s",
3101 temp_conv+1);
3102 weather->wx_gust[3] = '\0';
3103
3104 // compute high wind
3105 if(wx_high_wind[0] == '\0' || // first time
3106 (get_hours() == 0 && get_minutes() == 0) || // midnite
3107 (atol(weather->wx_gust) > atol(wx_high_wind))) // gust
3108 {
3109 xastir_snprintf(wx_high_wind,
3110 sizeof(wx_high_wind),
3111 "%s",
3112 weather->wx_gust);
3113 }
3114 wx_high_wind_on=1;
3115 }
3116 }
3117
3118 if ((temp_conv=strchr((char *)data,'t'))) // Temperature in Degrees F
3119 {
3120 xastir_snprintf(weather->wx_temp,
3121 sizeof(weather->wx_temp),
3122 "%s",
3123 temp_conv+1);
3124 weather->wx_temp[3] = '\0';
3125
3126 // compute hi temp, since APRS doesn't send that
3127 if(wx_hi_temp[0] == '\0' || // first time
3128 (get_hours() == 0 && get_minutes() == 0) || // midnite
3129 (atol(weather->wx_temp) > atol(wx_hi_temp)))
3130 {
3131 xastir_snprintf(wx_hi_temp,
3132 sizeof(wx_hi_temp),
3133 "%s",
3134 weather->wx_temp);
3135 }
3136 wx_hi_temp_on=1;
3137
3138 // compute low temp, since APRS doesn't send that
3139 if(wx_low_temp[0] == '\0' || // first time
3140 (get_hours() == 0 && get_minutes() == 0) || // midnite
3141 (atol(weather->wx_temp) < atol(wx_low_temp)))
3142 {
3143 xastir_snprintf(wx_low_temp,
3144 sizeof(wx_low_temp),
3145 "%s",
3146 weather->wx_temp);
3147 }
3148 wx_low_temp_on=1;
3149 }
3150
3151 if ((temp_conv=strchr((char *)data,'r'))) // Rain per hour
3152 {
3153 xastir_snprintf(weather->wx_rain,
3154 sizeof(weather->wx_rain),
3155 "%s",
3156 temp_conv+1);
3157 weather->wx_rain[3] = '\0';
3158 }
3159
3160 if ((temp_conv=strchr((char *)data,'p'))) // Rain per 24 hrs/total
3161 {
3162 xastir_snprintf(weather->wx_prec_24,
3163 sizeof(weather->wx_prec_24),
3164 "%s",
3165 temp_conv+1);
3166 weather->wx_prec_24[3] = '\0';
3167 }
3168
3169 if ((temp_conv=strchr((char *)data,'P'))) // Rain since midnight
3170 {
3171 xastir_snprintf(weather->wx_prec_00,
3172 sizeof(weather->wx_prec_00),
3173 "%s",
3174 temp_conv+1);
3175 weather->wx_prec_00[3] = '\0';
3176 }
3177
3178 if ((temp_conv=strchr((char *)data,'T'))) // Total Rain since
3179 {
3180 // wx station reset
3181 xastir_snprintf(weather->wx_rain_total,
3182 sizeof(weather->wx_rain_total),
3183 "%s",
3184 temp_conv+1);
3185 weather->wx_rain_total[4] = '\0';
3186 }
3187
3188 // Ok, here's the deal --- if we got total rain AND we didn't get
3189 // rain-since-midnight, fix it up.
3190 // This is a problem with LaCrosse --- no rain-since-midnight
3191 // provided. Don't do anything at all if we didn't get total rain
3192 // from the station. compute_rain *depends* on "total rain" being
3193 // a strictly increasing number that is never reset to zero during
3194 // Xastir's run.
3195 if (strlen(weather->wx_rain_total) >0 )
3196 {
3197 compute_rain((float)atof(weather->wx_rain_total));
3198 if (weather->wx_prec_00[0] == '\0')
3199 {
3200 /* rain since midnight */
3201 xastir_snprintf(weather->wx_prec_00,
3202 sizeof(weather->wx_prec_00),
3203 "%0.2f",
3204 rain_00);
3205 }
3206 }
3207
3208 // we are should be getting total rain from the station, but
3209 // we are also getting the rates.
3210 // Davis gives 24-hour, since-midnight, and 1-hour rates.
3211 // LaCrosse gives 24 and 1 hour.
3212 // Don't recompute what the station already gave us.
3213 weather->wx_compute_rain_rates=0;
3214
3215 if ((temp_conv=strchr((char *)data,'h'))) // Humidity %
3216 {
3217
3218 if (!strncmp(temp_conv+1,"00",2)) // APRS says 00 is
3219 {
3220 xastir_snprintf(weather->wx_hum, // 100% humidity
3221 sizeof(weather->wx_hum),
3222 "%s",
3223 "100");
3224 weather->wx_hum[3] = '\0';
3225 }
3226 else
3227 {
3228 xastir_snprintf(weather->wx_hum, // humidity less than
3229 sizeof(weather->wx_hum), // 100%
3230 "%s",
3231 temp_conv+1);
3232 weather->wx_hum[2] = '\0';
3233 }
3234 }
3235
3236 if ((temp_conv=strchr((char *)data,'b'))) // Air Pressure in 1/10 hPa
3237 {
3238 memset(temp_data1,0,sizeof(temp_data1));
3239
3240 xastir_snprintf(temp_data1,
3241 sizeof(temp_data1),
3242 "%s",
3243 temp_conv+1);
3244 temp_data1[5] = '\0';
3245
3246 temp_temp = (float)(atof(temp_data1))/10.0;
3247 xastir_snprintf(temp_data1,
3248 sizeof(temp_data1),
3249 "%0.1f",
3250 temp_temp);
3251 xastir_snprintf(weather->wx_baro,
3252 sizeof(weather->wx_baro),
3253 "%s",
3254 temp_data1);
3255 }
3256
3257 if ((temp_conv=strchr((char *)data,'x'))) // WX Station Identifier
3258 {
3259 xastir_snprintf(weather->wx_station,
3260 sizeof(weather->wx_station),
3261 "%s",
3262 temp_conv+1);
3263 weather->wx_station[MAX_WXSTATION-1] = '\0';
3264 }
3265
3266 // now compute wind chill
3267 wind_chill = 35.74 + .6215 * atof(weather->wx_temp) -
3268 35.75 * pow(atof(weather->wx_gust), .16) +
3269 .4275 * atof(weather->wx_temp) *
3270 pow(atof(weather->wx_gust), .16);
3271
3272 if((wind_chill < atof(weather->wx_temp)) &&
3273 (atof(weather->wx_temp) < 50))
3274 {
3275
3276 xastir_snprintf(wx_wind_chill,
3277 sizeof(wx_wind_chill),
3278 "%.0f",
3279 wind_chill);
3280 wx_wind_chill_on = 1;
3281 }
3282 else
3283 {
3284 wx_wind_chill_on = 0;
3285 wx_wind_chill[0] = '\0';
3286 }
3287
3288 // The rest of the optional WX data is not used by
3289 // xastir (Luminosity, etc), except for snow, which
3290 // conflicts with wind speed (both are lower case 's')
3291
3292 if (debug_level & 1)
3293 fprintf(stdout,"Davis Decode: wd-%s,ws-%s,wg-%s,t-%s,rh-%s,r00-%s,r24-%s,rt-%s,h-%s,ap-%s,station-%s\n",
3294
3295 weather->wx_course,weather->wx_speed,weather->wx_gust,
3296 weather->wx_temp,weather->wx_rain,weather->wx_prec_00,
3297 weather->wx_prec_24,weather->wx_rain_total,
3298 weather->wx_hum,weather->wx_baro,weather->wx_station);
3299 break;
3300 // This is the output of the Davis APRS Data Logger. The format
3301 // is in fact exactly the same as a regular APRS weather packet,
3302 // complete with position information. Ignore that.
3303 // @xxxxxxzDDMM.mmN/DDDMM.mmW_CSE/SPDgGGGtTTTrRRRpRRRPRRRhXXbXXXXX.DsVP
3304 case (DAVISAPRSDL):
3305
3306 memset(weather->wx_course,0,4); // Keep out fradulent data...
3307 memset(weather->wx_speed,0,4);
3308 memset(weather->wx_gust,0,4);
3309 memset(weather->wx_temp,0,5);
3310 memset(weather->wx_rain,0,10);
3311 memset(weather->wx_prec_00,0,10);
3312 memset(weather->wx_prec_24,0,10);
3313 memset(weather->wx_rain_total,0,10);
3314 memset(weather->wx_hum,0,5);
3315 memset(weather->wx_baro,0,10);
3316 memset(weather->wx_station,0,MAX_WXSTATION);
3317
3318 if (sscanf((char *)data,
3319 "%*27s%3s/%3sg%3st%3sr%3sp%3sP%3sh%2sb%5s.DsVP",
3320 weather->wx_course,
3321 weather->wx_speed,
3322 weather->wx_gust,
3323 weather->wx_temp,
3324 weather->wx_rain,
3325 weather->wx_prec_24,
3326 weather->wx_prec_00,
3327 weather->wx_hum,
3328 weather->wx_baro) == 9)
3329 {
3330 // then we got all the data out of the packet... now process
3331 // First null-terminate all the strings:
3332 weather->wx_course[3]='\0';
3333 weather->wx_speed[3]='\0';
3334 weather->wx_gust[3]='\0';
3335 weather->wx_temp[3]='\0';
3336 weather->wx_rain[3]='\0';
3337 weather->wx_prec_24[3]='\0';
3338 weather->wx_prec_00[3]='\0';
3339 weather->wx_hum[2]='\0';
3340 weather->wx_baro[6]='\0';
3341
3342
3343 // NOTE: Davis APRS Data Logger does NOT provide total rain,
3344 // and so data from compute_rain (which needs total rain) will
3345 // be wrong. Set this flag to stop that from clobbering our
3346 // good rain rate data.
3347 weather->wx_compute_rain_rates=0;
3348
3349 // Check for course = 0. Change to 360.
3350 if (strncmp(weather->wx_course,"000",3) == 0)
3351 {
3352 xastir_snprintf(weather->wx_course,
3353 sizeof(weather->wx_course),
3354 "360");
3355 }
3356
3357 // compute high wind
3358 if(wx_high_wind[0] == '\0' || // first time
3359 (get_hours() == 0 && get_minutes() == 0) || // midnite
3360 (atol(weather->wx_gust) > atol(wx_high_wind))) // gust
3361 {
3362 xastir_snprintf(wx_high_wind,
3363 sizeof(wx_high_wind),
3364 "%s",
3365 weather->wx_gust);
3366 }
3367 wx_high_wind_on=1;
3368
3369 // compute hi temp, since APRS doesn't send that
3370 if(wx_hi_temp[0] == '\0' || // first time
3371 (get_hours() == 0 && get_minutes() == 0) || // midnite
3372 (atol(weather->wx_temp) > atol(wx_hi_temp)))
3373 {
3374 xastir_snprintf(wx_hi_temp,
3375 sizeof(wx_hi_temp),
3376 "%s",
3377 weather->wx_temp);
3378 }
3379 wx_hi_temp_on=1;
3380
3381 // compute low temp, since APRS doesn't send that
3382 if(wx_low_temp[0] == '\0' || // first time
3383 (get_hours() == 0 && get_minutes() == 0) || // midnite
3384 (atol(weather->wx_temp) < atol(wx_low_temp)))
3385 {
3386 xastir_snprintf(wx_low_temp,
3387 sizeof(wx_low_temp),
3388 "%s",
3389 weather->wx_temp);
3390 }
3391 wx_low_temp_on=1;
3392
3393
3394 // fix up humidity --- 00 in APRS means 100%:
3395 if (strncmp(weather->wx_hum,"00",2)==0)
3396 {
3397 weather->wx_hum[0]='1';
3398 weather->wx_hum[1]=weather->wx_hum[2]='0';
3399 weather->wx_hum[3]='\0';
3400 }
3401
3402 // fix up barometer. APRS sends in 10ths of millibars:
3403 temp_temp=(float)(atof(weather->wx_baro))/10.0;
3404 weather->wx_baro[0]='\0'; // zero out so snprintf doesn't append
3405 xastir_snprintf(weather->wx_baro,
3406 sizeof(weather->wx_baro),
3407 "%0.1f",
3408 temp_temp); // this should terminate Just Fine.
3409
3410 // now compute wind chill
3411 wind_chill = 35.74 + .6215 * atof(weather->wx_temp) -
3412 35.75 * pow(atof(weather->wx_gust), .16) +
3413 .4275 * atof(weather->wx_temp) *
3414 pow(atof(weather->wx_gust), .16);
3415
3416 if((wind_chill < atof(weather->wx_temp)) &&
3417 (atof(weather->wx_temp) < 50))
3418 {
3419
3420 xastir_snprintf(wx_wind_chill,
3421 sizeof(wx_wind_chill),
3422 "%.0f",
3423 wind_chill);
3424 wx_wind_chill_on = 1;
3425 }
3426 else
3427 {
3428 wx_wind_chill_on = 0;
3429 wx_wind_chill[0] = '\0';
3430 }
3431 xastir_snprintf(weather->wx_station,
3432 sizeof(weather->wx_station),
3433 "%s",
3434 (char *) &(data[63]));
3435
3436 /* Heat Index Calculation*/
3437 hi_hum=atoi(weather->wx_hum);
3438 rh2= atoi(weather->wx_hum);
3439 rh2=(rh2 * rh2);
3440 hidx_temp=atoi(weather->wx_temp);
3441 t2= atoi(weather->wx_temp);
3442 t2=(t2 * t2);
3443
3444 if (hidx_temp >= 70)
3445 {
3446 heat_index=-42.379+2.04901523 * hidx_temp+10.1433127 * hi_hum-0.22475541
3447 * hidx_temp * hi_hum-0.00683783 * t2-0.05481717 * rh2+0.00122874
3448 * t2 * hi_hum+0.00085282 * hidx_temp * rh2-0.00000199 * t2 * rh2;
3449 xastir_snprintf (wx_heat_index,
3450 sizeof(wx_heat_index),
3451 "%03d",
3452 heat_index);
3453 wx_heat_index_on = 1;
3454 }
3455 else
3456 {
3457 wx_heat_index_on = 0;
3458 }
3459
3460
3461 if (debug_level & 1)
3462 fprintf(stdout,"Davis APRS DataLogger Decode $Revision$: wd-%s,ws-%s,wg-%s,t-%s,rh-%s,r24-%s,r00-%s,h-%s,ap-%s,station-%s\n",
3463
3464 weather->wx_course,weather->wx_speed,weather->wx_gust,
3465 weather->wx_temp, weather->wx_rain,
3466 weather->wx_prec_24, weather->wx_prec_00,weather->wx_hum,weather->wx_baro,
3467 weather->wx_station);
3468 }
3469 break;
3470
3471 }
3472 // End of big switch
3473 } // End of wx_fill_data()
3474
3475
3476
3477
3478
3479 //**********************************************************
3480 // Decode WX data line
3481 // wx_line: raw wx data to decode
3482 //
3483 // This is called from main.c:UpdateTime() only. It
3484 // decodes data for serially-connected and network-connected
3485 // WX interfaces only. It calls wx_fill_data() to do the
3486 // real work once it figures out what type of data it has.
3487 //**********************************************************
3488 //
3489 // Note that the length of "wx_line" can be up to MAX_DEVICE_BUFFER,
3490 // which is currently set to 4096.
3491 //
wx_decode(unsigned char * wx_line,int data_length,int port)3492 void wx_decode(unsigned char *wx_line, int data_length, int port)
3493 {
3494 DataRow *p_station;
3495 int decoded;
3496 int find;
3497 int i;
3498 int len;
3499 char time_data[MAX_TIME];
3500 unsigned int check_sum;
3501 int max;
3502 WeatherRow *weather;
3503 float t1,t2,t3,t4,t5,t6,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19;
3504 int t7,t8;
3505
3506
3507 //fprintf(stderr,"wx_decode: %s\n",wx_line);
3508 //fprintf(stderr,"\nwx_decode: %d bytes\n", data_length);
3509
3510 find=0;
3511
3512 len = data_length;
3513 if (len == 0)
3514 {
3515 len=strlen((char *)wx_line);
3516 }
3517
3518 if (len>10 || ((int)wx_line[0]!=0 && port_data[port].data_type==1))
3519 {
3520 if (search_station_name(&p_station,my_callsign,1))
3521 {
3522 if (get_weather_record(p_station)) // DK7IN: only add record if we found something...
3523 {
3524 weather = p_station->weather_data;
3525
3526 decoded=0;
3527 /* Ok decode wx data */
3528 if (wx_line[0]=='!'
3529 && wx_line[1]=='!'
3530 && is_xnum_or_dash((char *)(wx_line+2),40)
3531 && port_data[port].data_type==0)
3532 {
3533
3534 /* Found Peet Bros U-2k */
3535 if (debug_level & 1)
3536 {
3537 fprintf(stderr,"Found Peet Bros U-2k WX:%s\n",wx_line+2);
3538 }
3539
3540 xastir_snprintf(wx_station_type,
3541 sizeof(wx_station_type),
3542 "%s",
3543 langcode("WXPUPSI011"));
3544
3545 xastir_snprintf(raw_wx_string,
3546 sizeof(raw_wx_string),
3547 "%s",
3548 wx_line);
3549
3550 raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it
3551
3552 xastir_snprintf(weather->wx_time,
3553 sizeof(weather->wx_time),
3554 "%s",
3555 get_time(time_data));
3556
3557 weather->wx_sec_time=sec_now();
3558 //weather->wx_data=1;
3559 wx_fill_data(0,APRS_WX3,wx_line,p_station);
3560 decoded=1;
3561 }
3562 else if (((wx_line[0]=='#') || (wx_line[0]=='*'))
3563 && is_xnum_or_dash((char *)(wx_line+1),13)
3564 && port_data[port].data_type==0)
3565 {
3566
3567 /* Found Peet Bros raw U2 data */
3568 xastir_snprintf(wx_station_type,
3569 sizeof(wx_station_type),
3570 "%s",
3571 langcode("WXPUPSI012"));
3572
3573 if (debug_level & 1)
3574 {
3575 fprintf(stderr,"Found Peet Bros raw U2 data WX#:%s\n",wx_line+1);
3576 }
3577
3578 xastir_snprintf(raw_wx_string,
3579 sizeof(raw_wx_string),
3580 "%s",
3581 wx_line);
3582
3583 raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it
3584
3585 xastir_snprintf(weather->wx_time,
3586 sizeof(weather->wx_time),
3587 "%s",
3588 get_time(time_data));
3589 weather->wx_sec_time=sec_now();
3590 //weather->wx_data=1;
3591
3592 if (wx_line[0]=='#') // Wind speed in km/h
3593 {
3594 wx_fill_data(0,APRS_WX4,wx_line,p_station);
3595 }
3596 else // '*', Wind speed in mph
3597 {
3598 wx_fill_data(0,APRS_WX6,wx_line,p_station);
3599 }
3600
3601 decoded=1;
3602 }
3603 else if (strncmp("$ULTW",(char *)wx_line,5)==0
3604 && is_xnum_or_dash((char *)(wx_line+5),44)
3605 && port_data[port].data_type==0)
3606 {
3607
3608 /* Found Peet Bros raw U2 data */
3609
3610 xastir_snprintf(wx_station_type,
3611 sizeof(wx_station_type),
3612 "%s",
3613 langcode("WXPUPSI013"));
3614
3615 if (debug_level & 1)
3616 {
3617 fprintf(stderr,"Found Peet Bros Ultimeter Packet data WX#:%s\n",wx_line+5);
3618 }
3619
3620 xastir_snprintf(raw_wx_string,
3621 sizeof(raw_wx_string),
3622 "%s",
3623 wx_line);
3624 raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it
3625
3626 weather->wx_sec_time=sec_now();
3627 //weather->wx_data=1;
3628 wx_fill_data(0,APRS_WX5,wx_line,p_station);
3629 decoded=1;
3630 }
3631 else if (wx_line[2]==' ' && wx_line[5]==' ' && wx_line[8]=='/' && wx_line[11]=='/'
3632 && wx_line[14]==' ' && wx_line[17]==':' && wx_line[20]==':'
3633 && strncmp(" #0:",(char *)wx_line+23,4)==0 && port_data[port].data_type==0)
3634 {
3635 find=0;
3636 for (i=len; i>23 && !find; i--)
3637 {
3638 if ((int)wx_line[i]==0x03)
3639 {
3640 find=1;
3641 wx_line[i] = 0;
3642 }
3643 }
3644 if (find)
3645 {
3646
3647 /* found Qualimetrics Q-Net station */
3648
3649 xastir_snprintf(wx_station_type,
3650 sizeof(wx_station_type),
3651 "%s",
3652 langcode("WXPUPSI016"));
3653
3654 if (debug_level & 1)
3655 {
3656 fprintf(stderr,"Found Qualimetrics Q-Net station data WX#:%s\n",wx_line+23);
3657 }
3658
3659 xastir_snprintf(weather->wx_time,
3660 sizeof(weather->wx_time),
3661 "%s",
3662 get_time(time_data));
3663 weather->wx_sec_time=sec_now();
3664 //weather->wx_data=1;
3665 wx_fill_data(0,QM_WX,wx_line+24,p_station);
3666 decoded=1;
3667 }
3668 }
3669
3670 //WE7U
3671 // else look for ten ASCII decimal point chars in the input, or do an
3672 // sscanf looking for the correct number and types of fields for the
3673 // OWW server in ARNE mode. 6 %f's, 2 %d's, 11 %f's.
3674 else if (sscanf((const char *)wx_line,"%f %f %f %f %f %f %d %d %f %f %f %f %f %f %f %f %f %f %f", &t1,&t2,&t3,&t4,&t5,&t6,&t7,&t8,&t9,&t10,&t11,&t12,&t13,&t14,&t15,&t16,&t17,&t18,&t19) == 19)
3675 {
3676
3677 // Found Dallas One-Wire Weather Station
3678 if (debug_level & 1)
3679 {
3680 fprintf(stderr,"Found OWW ARNE-mode(19) one-wire weather station data\n");
3681 }
3682
3683 weather->wx_sec_time=sec_now();
3684 wx_fill_data(0,DALLAS_ONE_WIRE,wx_line,p_station);
3685 decoded=1;
3686 }
3687
3688 else if (sscanf((const char *)wx_line,"%f %f %f %f %f %f %d %d %f %f %f %f", &t1,&t2,&t3,&t4,&t5,&t6,&t7,&t8,&t9,&t10,&t11,&t12) == 12)
3689 {
3690
3691 // Found Dallas One-Wire Weather Station
3692 if (debug_level & 1)
3693 {
3694 fprintf(stderr,"Found OWW ARNE-mode(12) one-wire weather station data\n");
3695 }
3696
3697 weather->wx_sec_time=sec_now();
3698 wx_fill_data(0,DALLAS_ONE_WIRE,wx_line,p_station);
3699 decoded=1;
3700 }
3701
3702 else if (strncmp("&CR&",(char *)wx_line,4)==0
3703 && is_xnum_or_dash((char *)(wx_line+5),44)
3704 && port_data[port].data_type==0)
3705 {
3706
3707 if (debug_level & 1)
3708 {
3709 fprintf(stderr,"Found Peet Complete station data\n");
3710 }
3711
3712 xastir_snprintf(wx_station_type,
3713 sizeof(wx_station_type),
3714 "%s",
3715 langcode("WXPUPSI017"));
3716
3717 xastir_snprintf(raw_wx_string,
3718 sizeof(raw_wx_string),
3719 "%s",
3720 wx_line);
3721
3722 raw_wx_string[MAX_RAW_WX_STRING] = '\0'; // Terminate it
3723
3724 weather->wx_sec_time=sec_now();
3725 //weather->wx_data=1;
3726 wx_fill_data(0,PEET_COMPLETE,wx_line,p_station);
3727 decoded=1;
3728 }
3729
3730 else if (port_data[port].data_type==1)
3731 {
3732 // int jj;
3733
3734 /* binary data type */
3735 if (debug_level & 1)
3736 {
3737 fprintf(stderr,"Found binary data: %d bytes\n", len);
3738 }
3739
3740 /* clear raw string */
3741 memset(raw_wx_string,0,sizeof(raw_wx_string));
3742 max=0;
3743 switch (wx_line[0])
3744 {
3745 case 0x8f:
3746 max=34;
3747 break;
3748
3749 case 0x9f:
3750 max=33;
3751 break;
3752
3753 case 0xaf:
3754 max=30;
3755 break;
3756
3757 case 0xbf:
3758 max=13;
3759 break;
3760
3761 case 0xcf:
3762 max=26;
3763 break;
3764
3765 default:
3766 break;
3767 }
3768
3769 // fprintf(stderr, "wx_decode binary: ");
3770 // for (jj = 0; jj < len+1; jj++) {
3771 // fprintf(stderr, "%02x ", wx_line[jj]);
3772 // }
3773 // fprintf(stderr, "\n");
3774 // fprintf(stderr, "Integers: ");
3775 // for (jj = 0; jj < max+1; jj++) {
3776 // fprintf(stderr, "%0d ", wx_line[jj]);
3777 // }
3778 // fprintf(stderr, "\n");
3779
3780
3781 if (len < (max+1))
3782 {
3783 fprintf(stderr, " Short NET_WX packet, %d bytes\n", len);
3784 }
3785
3786 if (max > 0 && len >= (max+1) )
3787 {
3788
3789 // Compute the checksum from the data
3790 check_sum = 0;
3791
3792 for (i = 0; i < max; i++)
3793 {
3794 check_sum += wx_line[i];
3795 }
3796 // fprintf(stderr," Checksum: 0x%02x ", 0x0ff & check_sum);
3797
3798 if ( wx_line[max] == (0xff & check_sum) )
3799 {
3800
3801 /* good RS WX-200 data */
3802 //fprintf(stderr,"GOOD RS WX-200 %0X data\n",wx_line[0]);
3803 /* found RS WX-200 */
3804 xastir_snprintf(wx_station_type,
3805 sizeof(wx_station_type),
3806 "%s",
3807 langcode("WXPUPSI025"));
3808 xastir_snprintf(weather->wx_time,
3809 sizeof(weather->wx_time),
3810 "%s",
3811 get_time(time_data));
3812 weather->wx_sec_time=sec_now();
3813 //weather->wx_data=1;
3814 wx_fill_data(0,RSWX200,wx_line,p_station);
3815 decoded=1;
3816 }
3817 else
3818 {
3819 // fprintf(stderr, "bad");
3820 }
3821 }
3822 }
3823 else if (wx_line[0]=='@' && strncmp((char *)&(wx_line[63]),".DsVP",5)==0)
3824 {
3825 // this is a Davis Vantage Pro with APRS Data Logger
3826 if (debug_level & 1)
3827 {
3828 fprintf(stdout,"Davis VP APRS Data Logger data found ... %s\n", wx_line);
3829 }
3830 xastir_snprintf(wx_station_type,
3831 sizeof(wx_station_type),
3832 "%s",
3833 langcode("WXPUPSI028"));
3834
3835 xastir_snprintf(weather->wx_time,
3836 sizeof(weather->wx_time),
3837 "%s",
3838 get_time(time_data));
3839 weather->wx_sec_time=sec_now();
3840 wx_fill_data(0,DAVISAPRSDL,wx_line,p_station);
3841 decoded=1;
3842 }
3843 else // ASCII data of undetermined type
3844 {
3845
3846 // Davis Weather via meteo -> db2APRS -> TCP port
3847
3848 if (strstr((const char *)wx_line, "xDvs")) // APRS 'postionless' WX data w/ Davis & X tag
3849 {
3850
3851 if (debug_level & 1)
3852 {
3853 fprintf(stdout,"Davis Data found... %s\n",wx_line);
3854 }
3855
3856 xastir_snprintf(wx_station_type,
3857 sizeof(wx_station_type),
3858 "%s",
3859 langcode("WXPUPSI026"));
3860 xastir_snprintf(weather->wx_time,
3861 sizeof(weather->wx_time),
3862 "%s",
3863 get_time(time_data));
3864 weather->wx_sec_time=sec_now();
3865 wx_fill_data(0,DAVISMETEO,wx_line,p_station);
3866 decoded=1;
3867 }
3868
3869 else if (debug_level & 1)
3870 {
3871 fprintf(stderr,"Unknown WX DATA:%s\n",wx_line);
3872 }
3873 }
3874
3875 if (decoded)
3876 {
3877 /* save data back */
3878
3879 if (begin_critical_section(&port_data_lock, "wx.c:wx_decode(1)" ) > 0)
3880 {
3881 fprintf(stderr,"port_data_lock, Port = %d\n", port);
3882 }
3883
3884 port_data[port].decode_errors=0;
3885
3886 if (end_critical_section(&port_data_lock, "wx.c:wx_decode(2)" ) > 0)
3887 {
3888 fprintf(stderr,"port_data_lock, Port = %d\n", port);
3889 }
3890
3891 statusline(langcode("BBARSTA032"),1); // Decoded WX Data
3892 /* redraw now */
3893 //redraw_on_new_data=2;
3894 redraw_on_new_data=1;
3895 fill_wx_data();
3896 }
3897 else
3898 {
3899 /* Undecoded packet */
3900 memset(raw_wx_string,0,sizeof(raw_wx_string));
3901
3902 if (begin_critical_section(&port_data_lock, "wx.c:wx_decode(3)" ) > 0)
3903 {
3904 fprintf(stderr,"port_data_lock, Port = %d\n", port);
3905 }
3906
3907 port_data[port].decode_errors++; // We have errors in decoding
3908 if (port_data[port].decode_errors>10)
3909 {
3910 /* wrong data type? */
3911 port_data[port].data_type++; // Try another data type. 0=ascii, 1=wx binary
3912 port_data[port].data_type&=0x01;
3913 /*if (debug_level & 1)*/
3914 fprintf(stderr,"Data type %d\n",port_data[port].data_type);
3915 port_data[port].decode_errors=0;
3916 }
3917
3918 if (end_critical_section(&port_data_lock, "wx.c:wx_decode(4)" ) > 0)
3919 {
3920 fprintf(stderr,"port_data_lock, Port = %d\n", port);
3921 }
3922
3923 }
3924 }
3925 }
3926 }
3927 }
3928
3929
3930
3931
3932
3933 /***********************************************************/
3934 /* fill string with WX data for transmit */
3935 /* */
3936 /***********************************************************/
3937
wx_tx_data1(char * st,int st_size)3938 time_t wx_tx_data1(char *st, int st_size)
3939 {
3940 DataRow *p_station;
3941 time_t wx_time;
3942 char temp[100];
3943 WeatherRow *weather;
3944
3945 st[0] = '\0';
3946 wx_time = 0;
3947 if (search_station_name(&p_station,my_callsign,1))
3948 {
3949 if (get_weather_record(p_station)) // station has wx data
3950 {
3951 weather = p_station->weather_data;
3952
3953
3954 //WE7U: For debugging purposes only
3955 //weather->wx_sec_time=sec_now();
3956 //sprintf(weather->wx_course,"359"); // degrees
3957 //sprintf(weather->wx_speed,"001"); // mph
3958 //sprintf(weather->wx_gust,"010"); // mph
3959 //sprintf(weather->wx_temp,"069"); // Farenheit
3960
3961 //if ( strlen(weather->wx_rain_total) == 0)
3962 // sprintf(weather->wx_rain_total,"1900.40");
3963
3964 //sprintf(weather->wx_rain_total,"1987.6"); // hundredths of an inch
3965 //compute_rain(atof(weather->wx_rain_total));
3966 //sprintf(weather->wx_rain_total,"1988.6");
3967 //compute_rain(atof(weather->wx_rain_total));
3968 //sprintf(weather->wx_rain_total,"1990.6");
3969
3970 //compute_rain(atof(weather->wx_rain_total));
3971 //sprintf(weather->wx_rain,"%0.2f",rain_minute_total);
3972 //sprintf(weather->wx_prec_24,"%0.2f",rain_24);
3973 //sprintf(weather->wx_prec_00,"%0.2f",rain_00);
3974
3975 //sprintf(weather->wx_rain,"0"); // hundredths of an inch
3976 //sprintf(weather->wx_prec_00,"0"); // hundredths of an inch
3977 //sprintf(weather->wx_prec_24,"0"); // hundredths of an inch
3978
3979 //sprintf(weather->wx_hum,"92"); // %
3980 //sprintf(weather->wx_baro,"1013.0"); // hPa
3981 //weather->wx_type = WX_TYPE;
3982 //xastir_snprintf(weather->wx_station,sizeof(weather->wx_station),"RSW");
3983 // 359/000g000t065r010P020p030h92b01000
3984
3985
3986 if (strlen(weather->wx_course) > 0 && strlen(weather->wx_speed) > 0)
3987 {
3988 // We have enough wx_data
3989 wx_time=weather->wx_sec_time;
3990 xastir_snprintf(temp,
3991 sizeof(temp),
3992 "%s",
3993 weather->wx_course);
3994 if (strlen(temp) > 3)
3995 {
3996 if (debug_level & 1)
3997 {
3998 fprintf(stderr,"wx_course too long: %s\n", temp);
3999 }
4000 xastir_snprintf(temp,
4001 sizeof(temp),
4002 "...");
4003 }
4004 if ( (atoi(weather->wx_course) > 360) || (atoi(weather->wx_course) < 0) )
4005 {
4006 if (debug_level & 1)
4007 {
4008 fprintf(stderr,"wx_course out-of-range: %s\n", weather->wx_course);
4009 }
4010 xastir_snprintf(temp,
4011 sizeof(temp),
4012 "...");
4013 }
4014 //sprintf(st,"%s/%s",weather->wx_course,weather->wx_speed);
4015 strncat(st, temp, st_size - 1 - strlen(st));
4016 strncat(st, "/", st_size - 1 - strlen(st));
4017
4018 xastir_snprintf(temp,
4019 sizeof(temp),
4020 "%s",
4021 weather->wx_speed);
4022 if (strlen(temp) > 3)
4023 {
4024 if (debug_level & 1)
4025 {
4026 fprintf(stderr,"wx_speed too long: %s\n", temp);
4027 }
4028 xastir_snprintf(temp,
4029 sizeof(temp),
4030 "...");
4031 }
4032 if ( (atoi(weather->wx_speed) < 0) || (atoi(weather->wx_speed) > 999) )
4033 {
4034 if (debug_level & 1)
4035 {
4036 fprintf(stderr,"wx_speed out-of-range: %s\n", weather->wx_speed);
4037 }
4038 xastir_snprintf(temp,
4039 sizeof(temp),
4040 "...");
4041 }
4042 strncat(st, temp, st_size - 1 - strlen(st));
4043 }
4044 else
4045 {
4046 // We don't have enough wx_data, may be from a Qualimetrics Q-Net?
4047 wx_time=weather->wx_sec_time;
4048 xastir_snprintf(st,
4049 st_size,
4050 ".../...");
4051
4052
4053 if (debug_level & 1)
4054 {
4055 fprintf(stderr,"\n\nAt least one field was empty: Course: %s\tSpeed: %s\n", weather->wx_course, weather->wx_speed);
4056 fprintf(stderr,"Will be sending '.../...' instead of real values.\n\n\n");
4057 }
4058
4059
4060 }
4061 if (strlen(weather->wx_gust) > 0)
4062 {
4063 xastir_snprintf(temp,
4064 sizeof(temp),
4065 "g%s",
4066 weather->wx_gust);
4067 if (strlen(temp) > 4)
4068 {
4069 if (debug_level & 1)
4070 {
4071 fprintf(stderr,"wx_gust too long: %s\n", temp);
4072 }
4073
4074 xastir_snprintf(temp,
4075 sizeof(temp),
4076 "g...");
4077 }
4078 if (atoi(weather->wx_gust) < 0)
4079 {
4080 if (debug_level & 1)
4081 {
4082 fprintf(stderr,"wx_gust out-of-range: %s\n", weather->wx_gust);
4083 }
4084
4085 xastir_snprintf(temp,
4086 sizeof(temp),
4087 "g...");
4088 }
4089 strncat(st, temp, st_size - 1 - strlen(st));
4090 }
4091 else
4092 {
4093 strncat(st, "g...", st_size - 1 - strlen(st));
4094 }
4095
4096 if (strlen(weather->wx_temp) > 0)
4097 {
4098 xastir_snprintf(temp,
4099 sizeof(temp),
4100 "t%s",
4101 weather->wx_temp);
4102 if (strlen(temp) > 4)
4103 {
4104 if (debug_level & 1)
4105 {
4106 fprintf(stderr,"wx_temp too long: %s\n", temp);
4107 }
4108
4109 xastir_snprintf(temp,
4110 sizeof(temp),
4111 "t...");
4112 }
4113 if ( (atoi(weather->wx_temp) > 999) || (atoi(weather->wx_temp) < -99) )
4114 {
4115 if (debug_level & 1)
4116 {
4117 fprintf(stderr,"wx_temp out-of-bounds: %s\n", weather->wx_temp);
4118 }
4119
4120 xastir_snprintf(temp,
4121 sizeof(temp),
4122 "t...");
4123 }
4124 strncat(st, temp, st_size - 1 - strlen(st));
4125 }
4126 else
4127 {
4128 strncat(st, "t...", st_size - 1 - strlen(st));
4129 }
4130
4131 if (strlen(weather->wx_rain) > 0)
4132 {
4133 xastir_snprintf(temp,
4134 sizeof(temp),
4135 "r%03d",
4136 (int)(atof(weather->wx_rain) + 0.5) ); // Cheater's way of rounding
4137 if (strlen(temp)>4)
4138 {
4139 if (debug_level & 1)
4140 {
4141 fprintf(stderr,"wx_rain too long: %s\n", temp);
4142 }
4143
4144 // Don't transmit this field if it's not valid
4145 xastir_snprintf(temp,
4146 sizeof(temp),
4147 "r ");
4148 }
4149 if (atoi(weather->wx_rain) < 0)
4150 {
4151 if (debug_level & 1)
4152 {
4153 fprintf(stderr,"wx_rain out-of-bounds: %s\n", weather->wx_rain);
4154 }
4155
4156 // Don't transmit this field if it's not valid
4157 xastir_snprintf(temp,
4158 sizeof(temp),
4159 "r...");
4160 }
4161 strncat(st, temp, st_size - 1 - strlen(st));
4162 }
4163 else
4164 {
4165 // Don't transmit this field if it's not valid
4166 //strncat(st, "r...", st_size - 1 - strlen(st));
4167 }
4168
4169 if (strlen(weather->wx_prec_00) > 0)
4170 {
4171 xastir_snprintf(temp,
4172 sizeof(temp),
4173 "P%03d",
4174 (int)(atof(weather->wx_prec_00) + 0.5) ); // Cheater's way of rounding
4175 if (strlen(temp)>4)
4176 {
4177 if (debug_level & 1)
4178 {
4179 fprintf(stderr,"wx_prec_00 too long: %s\n", temp);
4180 }
4181
4182 // Don't transmit this field if it's not valid
4183 xastir_snprintf(temp,
4184 sizeof(temp),
4185 "P ");
4186 }
4187 if (atoi(weather->wx_prec_00) < 0)
4188 {
4189 if (debug_level & 1)
4190 {
4191 fprintf(stderr,"wx_prec_00 out-of-bounds: %s\n", weather->wx_prec_00);
4192 }
4193
4194 // Don't transmit this field if it's not valid
4195 xastir_snprintf(temp,
4196 sizeof(temp),
4197 "P...");
4198 }
4199 strncat(st, temp, st_size - 1 - strlen(st));
4200 }
4201 else
4202 {
4203 // Don't transmit this field if it's not valid
4204 //strncat(st, "P...", st_size - 1 - strlen(st));
4205 }
4206
4207 if (strlen(weather->wx_prec_24) > 0)
4208 {
4209 xastir_snprintf(temp,
4210 sizeof(temp),
4211 "p%03d",
4212 (int)(atof(weather->wx_prec_24) + 0.5) ); // Cheater's way of rounding
4213 if (strlen(temp)>4)
4214 {
4215 if (debug_level & 1)
4216 {
4217 fprintf(stderr,"wx_prec_24 too long: %s\n", temp);
4218 }
4219
4220 // Don't transmit this field if it's not valid
4221 xastir_snprintf(temp,
4222 sizeof(temp),
4223 "p ");
4224 }
4225 if (atoi(weather->wx_prec_24) < 0)
4226 {
4227 if (debug_level & 1)
4228 {
4229 fprintf(stderr,"wx_prec_24 out-of-bounds: %s\n", weather->wx_prec_24);
4230 }
4231
4232 // Don't transmit this field if it's not valid
4233 xastir_snprintf(temp,
4234 sizeof(temp),
4235 "p...");
4236 }
4237 strncat(st, temp, st_size - 1 - strlen(st));
4238 }
4239 else
4240 {
4241 // Don't transmit this field if it's not valid
4242 //strncat(st, "p...", st_size - 1 - strlen(st));
4243 }
4244
4245 if (strlen(weather->wx_hum) > 0)
4246 {
4247 if (atoi(weather->wx_hum)>99)
4248 {
4249 xastir_snprintf(temp,
4250 sizeof(temp),
4251 "h00");
4252 }
4253 else
4254 xastir_snprintf(temp,
4255 sizeof(temp),
4256 "h%02d",
4257 atoi(weather->wx_hum));
4258
4259 if (strlen(temp) > 4)
4260 {
4261 if (debug_level & 1)
4262 {
4263 fprintf(stderr,"wx_hum too long: %s\n", temp);
4264 }
4265
4266 // Don't transmit this field if it's not valid
4267 //xastir_snprintf(temp,sizeof(temp),"h..");
4268 }
4269 if (atoi(weather->wx_hum) < 0)
4270 {
4271 if (debug_level & 1)
4272 {
4273 fprintf(stderr,"wx_hum out-of-bounds: %s\n", weather->wx_hum);
4274 }
4275 // Don't transmit this field if it's not valid
4276 //xastir_snprintf(temp,sizeof(temp),"h..");
4277 }
4278 strncat(st, temp, st_size - 1 - strlen(st));
4279 }
4280 else
4281 {
4282 // Don't transmit this field if it's not valid
4283 //strncat(st, "h..", st_size - 1 - strlen(st));
4284 }
4285
4286 if (strlen(weather->wx_baro) > 0)
4287 {
4288 xastir_snprintf(temp,
4289 sizeof(temp),
4290 "b%05d",
4291 (int)((atof(weather->wx_baro) * 10.0)) );
4292 if (strlen(temp)>6)
4293 {
4294 if (debug_level & 1)
4295 {
4296 fprintf(stderr,"wx_baro too long: %s\n", temp);
4297 }
4298 // Don't transmit this field if it's not valid
4299 //xastir_snprintf(temp,sizeof(temp),"b.....");
4300 }
4301 if ((int)((atof(weather->wx_baro) * 10.0) < 0))
4302 {
4303 if (debug_level & 1)
4304 {
4305 fprintf(stderr,"wx_baro out-of-bounds: %s\n", weather->wx_baro);
4306 }
4307 // Don't transmit this field if it's not valid
4308 //xastir_snprintf(temp,sizeof(temp),"b.....");
4309 }
4310 strncat(st, temp, st_size - 1 - strlen(st));
4311 }
4312 else
4313 {
4314 // Don't transmit this field if it's not valid
4315 //strncat(st, "b.....", st_size - 1 - strlen(st));
4316 }
4317
4318 xastir_snprintf(temp,
4319 sizeof(temp),
4320 "%c%s",
4321 weather->wx_type,
4322 weather->wx_station);
4323 strncat(st, temp, st_size - 1 - strlen(st));
4324 }
4325 }
4326
4327
4328 if (debug_level & 1)
4329 {
4330 fprintf(stderr,"Weather String: %s\n", st);
4331 }
4332
4333
4334 return(wx_time);
4335 }
4336
4337
4338
4339
4340
4341 /***********************************************************/
4342 /* transmit raw wx data */
4343 /* */
4344 /***********************************************************/
tx_raw_wx_data(void)4345 void tx_raw_wx_data(void)
4346 {
4347
4348 if (strlen(raw_wx_string)>10)
4349 {
4350 output_my_data(raw_wx_string,-1,0,0,0,NULL);
4351 if (debug_level & 1)
4352 {
4353 fprintf(stderr,"Sending Raw WX data <%s>\n",raw_wx_string);
4354 }
4355 }
4356 }
4357
4358
4359